├── .dockercfg.enc ├── .sync.yml ├── docker-entrypoint.d ├── 01-wait.sh ├── 09-csr_attributes.yaml.sh └── 10-setup-jobber.sh ├── .gitignore ├── Gemfile ├── .travis.yml ├── Dockerfile ├── Dockerfile.octocatalog-diff ├── docker-entrypoint.sh ├── scripts ├── generate_reportlist.py └── catalog_diff ├── known_hosts ├── .build.sh ├── .publish.sh ├── spec └── Dockerfile_spec.rb └── README.md /.dockercfg.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voxpupuli/docker-puppet-catalog-diff/master/.dockercfg.enc -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :global: 3 | docker_image_name: 'puppet-catalog-diff' 4 | travis_key_id: '90e969290c29' 5 | -------------------------------------------------------------------------------- /docker-entrypoint.d/01-wait.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Wait 3 second to give time to Docker to update /etc/resolv.conf 4 | sleep 3 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/ 2 | Gemfile.lock 3 | vendor/ 4 | spec/fixtures/ 5 | .vagrant/ 6 | .bundle/ 7 | coverage/ 8 | log/ 9 | .*.swp 10 | *~ 11 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV['GEM_SOURCE'] || "https://rubygems.org" 2 | 3 | gem 'serverspec', :require => false 4 | gem 'docker-api', :require => false 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | sudo: required 4 | services: docker 5 | cache: bundler 6 | script: 7 | - ./.build.sh 8 | - bundle exec rspec -fd 9 | after_success: 10 | - ./.publish.sh 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM puppet/puppet-agent 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y --force-yes git puppetdb-termini \ 5 | && rm -rf /var/lib/apt/lists/* 6 | 7 | # Install catalog-diff module 8 | RUN git clone https://github.com/camptocamp/puppet-catalog-diff.git /etc/puppetlabs/code/environments/production/modules/catalog_diff 9 | -------------------------------------------------------------------------------- /Dockerfile.octocatalog-diff: -------------------------------------------------------------------------------- 1 | FROM puppet/puppet-agent 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y --force-yes git puppetdb-termini \ 5 | && rm -rf /var/lib/apt/lists/* 6 | 7 | RUN apt-get update \ 8 | && apt-get install -y ruby ruby-dev cmake pkg-config zlib1g-dev libssh2-1-dev libssl-dev \ 9 | && /usr/bin/gem install --no-ri --no-rdoc specific_install \ 10 | && /usr/bin/gem specific_install -l https://github.com/raphink/octocatalog-diff.git -b catalog_compile \ 11 | && apt-get clean 12 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | DIR=/docker-entrypoint.d 6 | 7 | if [[ -d "$DIR" ]] 8 | then 9 | /bin/run-parts --verbose --regex '\.sh$' "$DIR" 10 | fi 11 | 12 | function pipeEnvironmentVariables() { 13 | local environmentfile="/etc/profile.d/jobber.sh" 14 | cat > ${environmentfile} <<_EOF_ 15 | #!/bin/sh 16 | _EOF_ 17 | sh -c export >> ${environmentfile} 18 | } 19 | 20 | if [ "$1" = 'jobberd' ]; then 21 | pipeEnvironmentVariables 22 | exec /usr/local/libexec/jobberd 23 | fi 24 | 25 | exec "$@" 26 | -------------------------------------------------------------------------------- /scripts/generate_reportlist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import json 3 | from os import listdir, path 4 | maindir = path.dirname(path.abspath(__file__)) 5 | datadir = path.join(maindir,'data') 6 | datafiles = [f for f in listdir(datadir) if path.isfile(path.join(datadir, f))] 7 | datadict = {} 8 | for f in datafiles: 9 | if f.endswith('.json'): 10 | f = f[:-5] 11 | datadict.update({f:f}) 12 | with open(path.join(datadir,'reportlist.json'), 'w') as reportlist: 13 | json.dump(datadict, reportlist, indent=4, sort_keys=True) 14 | -------------------------------------------------------------------------------- /known_hosts: -------------------------------------------------------------------------------- 1 | github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== 2 | [puppetmaster.infra.internal]:2222,[10.27.70.203]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPtIpS7hgAmMIMa2/BckxFvWJl+iSkgbQiS647oPCK6+s7yDmggDncNqQ24zAWMd9CEoNKnHiER3J+w+A+P5G+4= 3 | -------------------------------------------------------------------------------- /.build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | if [ "$TRAVIS_BRANCH" == "master" ]; then 3 | echo "Building image with tag latest" 4 | docker build -t camptocamp/puppet-catalog-diff:latest . 5 | elif [ ! -z "$TRAVIS_TAG" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 6 | echo "Building image with tag ${TRAVIS_TAG}" 7 | docker build -t camptocamp/puppet-catalog-diff:$TRAVIS_TAG . 8 | elif [ ! -z "$TRAVIS_BRANCH" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 9 | echo "Building image with tag ${TRAVIS_BRANCH}" 10 | docker build -t camptocamp/puppet-catalog-diff:$TRAVIS_BRANCH . 11 | else 12 | echo "Don't know how to build image" 13 | exit 1 14 | fi 15 | -------------------------------------------------------------------------------- /docker-entrypoint.d/09-csr_attributes.yaml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if getent hosts rancher-metadata > /dev/null; then 4 | # Generate csr_attributes.yaml 5 | cat << EOF > /etc/puppetlabs/puppet/csr_attributes.yaml 6 | --- 7 | custom_attributes: 8 | 1.2.840.113549.1.9.7: '$(curl http://rancher-metadata/latest/self/service/name 2> /dev/null):$(curl http://rancher-metadata/latest/self/service/uuid 2> /dev/null)' 9 | EOF 10 | 11 | # Get certificate 12 | rc=1 13 | while test $rc -ne 0; do 14 | if getent hosts puppetca > /dev/null; then 15 | puppet agent -t --noop --server puppetca 16 | else 17 | puppet agent -t --noop 18 | fi 19 | rc=$? 20 | done 21 | fi 22 | -------------------------------------------------------------------------------- /.publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Setup login 3 | openssl aes-256-cbc -K $encrypted_90e969290c29_key -iv $encrypted_90e969290c29_iv -in .dockercfg.enc -out ~/.dockercfg -d 4 | if [ "$TRAVIS_BRANCH" == "master" ]; then 5 | echo "Deploying image to docker hub for master (latest)" 6 | docker push "camptocamp/puppet-catalog-diff:latest" 7 | elif [ ! -z "$TRAVIS_TAG" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 8 | echo "Deploying image to docker hub for tag ${TRAVIS_TAG}" 9 | docker push "camptocamp/puppet-catalog-diff:${TRAVIS_TAG}" 10 | elif [ ! -z "$TRAVIS_BRANCH" ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then 11 | echo "Deploying image to docker hub for branch ${TRAVIS_BRANCH}" 12 | docker push "camptocamp/puppet-catalog-diff:${TRAVIS_BRANCH}" 13 | else 14 | echo "Not deploying image" 15 | fi 16 | -------------------------------------------------------------------------------- /spec/Dockerfile_spec.rb: -------------------------------------------------------------------------------- 1 | require "serverspec" 2 | require "docker" 3 | 4 | describe "Dockerfile" do 5 | before(:all) do 6 | # See https://github.com/swipely/docker-api/issues/106 7 | Excon.defaults[:write_timeout] = 1000 8 | Excon.defaults[:read_timeout] = 1000 9 | image = Docker::Image.build_from_dir('.') 10 | 11 | set :os, family: :debian 12 | set :backend, :docker 13 | set :docker_image, image.id 14 | set :docker_container_create_options, { "Privileged" => true } 15 | end 16 | 17 | describe file('/etc/puppetlabs/code/environments/production/modules/catalog_diff/lib/puppet/face/catalog/diff.rb') do 18 | it { is_expected.to be_file } 19 | end 20 | 21 | describe file('/usr/local/bin/catalog_diff') do 22 | it { is_expected.to be_file } 23 | it { is_expected.to be_executable } 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /docker-entrypoint.d/10-setup-jobber.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | configfile="/root/.jobber" 4 | 5 | if [ ! -f "${configfile}" ]; then 6 | touch ${configfile} 7 | cat > ${configfile} <<_EOF_ 8 | --- 9 | _EOF_ 10 | for (( i = 1; ; i++ )) 11 | do 12 | VAR_JOB_ON_ERROR="JOB_ON_ERROR$i" 13 | VAR_JOB_NAME="JOB_NAME$i" 14 | VAR_JOB_COMMAND="JOB_COMMAND$i" 15 | VAR_JOB_TIME="JOB_TIME$i" 16 | 17 | if [ ! -n "${!VAR_JOB_NAME}" ]; then 18 | break 19 | fi 20 | 21 | it_job_on_error="Continue" 22 | if [ -n "${!VAR_JOB_ON_ERROR}" ]; then 23 | it_job_on_error=${!VAR_JOB_ON_ERROR} 24 | fi 25 | it_job_name=${!VAR_JOB_NAME} 26 | it_job_time=${!VAR_JOB_TIME} 27 | it_job_command=${!VAR_JOB_COMMAND} 28 | 29 | cat >> ${configfile} <<_EOF_ 30 | - name: ${it_job_name} 31 | cmd: ${it_job_command} 32 | time: '${it_job_time}' 33 | onError: ${it_job_on_error} 34 | notifyOnError: false 35 | notifyOnFailure: false 36 | 37 | _EOF_ 38 | done 39 | fi 40 | 41 | cat ${configfile} 42 | -------------------------------------------------------------------------------- /scripts/catalog_diff: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | THREADS=${THREADS:-1} 4 | COMPRESS=${COMPRESS:-no} 5 | MASTER1=${MASTER1:-puppet} 6 | MASTER2=${MASTER2:-$MASTER1} 7 | ENV1=${ENV1:-production} 8 | ENV2=${ENV2:-$ENV1} 9 | 10 | DATADIR="/data" 11 | mkdir -p $DATADIR 12 | 13 | DEFAULT_REPORT="${DATADIR}/${MASTER1}_${ENV1}_${MASTER2}_${ENV2}.json" 14 | REPORT="${REPORT:-$DEFAULT_REPORT}" 15 | 16 | if [ "x$USE_PUPPETDB" = "xyes" ]; then 17 | PUPPETDB_FLAG="--use_puppetdb" 18 | cat < /etc/puppetlabs/puppet/puppetdb.conf 19 | [main] 20 | port = 8080 21 | server = puppetdb 22 | soft_write_failure = false 23 | EOF 24 | fi 25 | 26 | puppet catalog diff $MASTER1/$ENV1 $MASTER2/$ENV2 \ 27 | --show_resource_diff \ 28 | --content_diff \ 29 | --changed_depth 1000 \ 30 | --configtimeout 3000 \ 31 | --output_report "${REPORT}" \ 32 | --debug $PUPPETDB_FLAG \ 33 | --threads $THREADS $@ 34 | 35 | if [ "x${COMPRESS}" = "xyes" -o "x${COMPRESS}" = "xtrue" ]; then 36 | gzip -c $REPORT > "${REPORT}.gz" 37 | REPORT="${REPORT}.gz" 38 | fi 39 | 40 | if [ "x${SYNC}" = "xyes" -o "x${SYNC}" = "xtrue" ]; then 41 | s3cmd put "${REPORT}" "s3://${S3_BUCKET}/${S3_DIR}/" 42 | fi 43 | 44 | /generate_reportlist.py 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Puppet Catalog Diff Docker image 2 | ================================= 3 | 4 | [![Docker Pulls](https://img.shields.io/docker/pulls/camptocamp/puppet-catalog-diff.svg)](https://hub.docker.com/r/camptocamp/puppet-catalog-diff/) 5 | [![Build Status](https://img.shields.io/travis/camptocamp/docker-puppet-catalog-diff/master.svg)](https://travis-ci.org/camptocamp/docker-puppet-catalog-diff) 6 | [![By Camptocamp](https://img.shields.io/badge/by-camptocamp-fb7047.svg)](http://www.camptocamp.com) 7 | 8 | 9 | # Usage 10 | 11 | ```shell 12 | docker run -ti camptocamp/puppet-catalog-diff puppet catalog diff --help 13 | ``` 14 | 15 | 16 | ## `catalog_diff` wrapper 17 | 18 | The image comes with a `catalog_diff` wrapper taking environment variables. 19 | 20 | ```shell 21 | $ docker run -ti -e "MASTER1=puppet.dev" \ # defaults to "puppet" 22 | -e "MASTER2=puppet.prod" \ # defaults to $MASTER1 23 | -e "ENV1=dev" \ # defaults to "production" 24 | -e "ENV2=production" \ # defaults to $ENV1 25 | -e "THREADS=6" \ # defaults to 1 26 | -e "COMPRESS=yes" \ # optional 27 | -e "SYNC=yes" \ # optional 28 | -e "USE_PUPPETDB=yes" \ 29 | --link puppetdb \ 30 | camptocamp/puppet-catalog-diff catalog_diff 31 | ``` 32 | 33 | ## Using jobber 34 | 35 | This image is based on [jobber](https://hub.docker.com/r/blacklabelops/jobber/~/dockerfile/) for executing tasks. 36 | 37 | This means you can pass environment variables to create cron jobs. For example: 38 | 39 | ```shell 40 | $ docker run -ti -e ENV2=dev \ 41 | -e JOB_NAME1=prod_dev_report \ 42 | -e JOB_COMMAND1="REPORT=/data/my_report.json catalog_diff" \ 43 | camptocamp/puppet-catalog-diff 44 | ``` 45 | 46 | ## Report compression 47 | 48 | If `COMPRESS=yes` is passed to the `catalog_diff` script, the report will be compressed using `gzip`. 49 | 50 | ## S3 synchronization 51 | 52 | This image comes with s3cmd installed. If you pass `SYNC=yes` to the `catalog_diff` command, it will send the generated report to S3. The following environment variables are recognized: 53 | 54 | * Standard AWS environment variables for access and secret keys 55 | * `S3_BUCKET` 56 | * `S3_DIR` 57 | 58 | ## Certificates 59 | 60 | The `puppet catalog` command uses the standard Puppet certificates in the container. No certificate is included in the container, so Puppet will create one when you run it. 61 | 62 | For this reason, it is recommended to mount a certificate signed by your CA in the container when starting it. 63 | 64 | Be sure to set the container hostname to the same as the certificate. 65 | 66 | 67 | ## Reports 68 | 69 | When using the `catalog_diff` wrapper, generated JSON reports are stored in `/data`. It is recommended to mount a volume there to retrieve the reports. 70 | 71 | --------------------------------------------------------------------------------