# that can be used to generate config files or run ad-hoc puppet modules
# inside of a container.
+import glob
import json
import logging
import os
+import sys
import subprocess
import sys
import tempfile
log.debug(cmd_stderr)
+def match_config_volume(prefix, config):
+ # Match the mounted config volume - we can't just use the
+ # key as e.g "novacomute" consumes config-data/nova
+ volumes = config.get('volumes', [])
+ config_volume=None
+ for v in volumes:
+ if v.startswith(prefix):
+ config_volume = os.path.relpath(
+ v.split(":")[0], prefix).split("/")[0]
+ break
+ return config_volume
+
+
+def get_config_hash(prefix, config_volume):
+ hashfile = os.path.join(prefix, "%s.md5sum" % config_volume)
+ hash_data = None
+ if os.path.isfile(hashfile):
+ with open(hashfile) as f:
+ hash_data = f.read().rstrip()
+ return hash_data
+
+
def rm_container(name):
if os.environ.get('SHOW_DIFF', None):
log.info('Diffing container: %s' % name)
log.debug('manifest %s' % manifest)
log.debug('config_image %s' % config_image)
log.debug('volumes %s' % volumes)
- hostname = short_hostname()
- sh_script = '/var/lib/docker-puppet/docker-puppet-%s.sh' % config_volume
+ sh_script = '/var/lib/docker-puppet/docker-puppet.sh'
with open(sh_script, 'w') as script_file:
os.chmod(script_file.name, 0755)
mkdir -p /etc/puppet
cp -a /tmp/puppet-etc/* /etc/puppet
rm -Rf /etc/puppet/ssl # not in use and causes permission errors
- echo '{"step": %(step)s}' > /etc/puppet/hieradata/docker.json
+ echo "{\\"step\\": $STEP}" > /etc/puppet/hieradata/docker.json
TAGS=""
- if [ -n "%(puppet_tags)s" ]; then
- TAGS='--tags "%(puppet_tags)s"'
+ if [ -n "$PUPPET_TAGS" ]; then
+ TAGS="--tags \"$PUPPET_TAGS\""
fi
- FACTER_hostname=%(hostname)s FACTER_uuid=docker /usr/bin/puppet apply --verbose $TAGS /etc/config.pp
+
+ # workaround LP1696283
+ mkdir -p /etc/ssh
+ touch /etc/ssh/ssh_known_hosts
+
+ FACTER_hostname=$HOSTNAME FACTER_uuid=docker /usr/bin/puppet apply --verbose $TAGS /etc/config.pp
# Disables archiving
- if [ -z "%(no_archive)s" ]; then
- rm -Rf /var/lib/config-data/%(name)s
-
- # copying etc should be enough for most services
- mkdir -p /var/lib/config-data/%(name)s/etc
- cp -a /etc/* /var/lib/config-data/%(name)s/etc/
-
- if [ -d /root/ ]; then
- cp -a /root/ /var/lib/config-data/%(name)s/root/
- fi
- if [ -d /var/lib/ironic/tftpboot/ ]; then
- mkdir -p /var/lib/config-data/%(name)s/var/lib/ironic/
- cp -a /var/lib/ironic/tftpboot/ /var/lib/config-data/%(name)s/var/lib/ironic/tftpboot/
- fi
- if [ -d /var/lib/ironic/httpboot/ ]; then
- mkdir -p /var/lib/config-data/%(name)s/var/lib/ironic/
- cp -a /var/lib/ironic/httpboot/ /var/lib/config-data/%(name)s/var/lib/ironic/httpboot/
- fi
-
- # apache services may files placed in /var/www/
- if [ -d /var/www/ ]; then
- mkdir -p /var/lib/config-data/%(name)s/var/www
- cp -a /var/www/* /var/lib/config-data/%(name)s/var/www/
- fi
+ if [ -z "$NO_ARCHIVE" ]; then
+ archivedirs=("/etc" "/root" "/var/lib/ironic/tftpboot" "/var/lib/ironic/httpboot" "/var/www")
+ rsync_srcs=""
+ for d in "${archivedirs[@]}"; do
+ if [ -d "$d" ]; then
+ rsync_srcs+=" $d"
+ fi
+ done
+ rsync -a -R --delay-updates --delete-after $rsync_srcs /var/lib/config-data/${NAME}
+
+ # Also make a copy of files modified during puppet run
+ # This is useful for debugging
+ mkdir -p /var/lib/config-data/puppet-generated/${NAME}
+ rsync -a -R -0 --delay-updates --delete-after \
+ --files-from=<(find $rsync_srcs -newer /etc/ssh/ssh_known_hosts -print0) \
+ / /var/lib/config-data/puppet-generated/${NAME}
+
+ # Write a checksum of the config-data dir, this is used as a
+ # salt to trigger container restart when the config changes
+ tar cf - /var/lib/config-data/${NAME} | md5sum | awk '{print $1}' > /var/lib/config-data/${NAME}.md5sum
fi
- """ % {'puppet_tags': puppet_tags, 'name': config_volume,
- 'hostname': hostname,
- 'no_archive': os.environ.get('NO_ARCHIVE', ''),
- 'step': os.environ.get('STEP', '6')})
+ """)
with tempfile.NamedTemporaryFile() as tmp_man:
with open(tmp_man.name, 'w') as man_file:
dcmd = ['/usr/bin/docker', 'run',
'--user', 'root',
'--name', 'docker-puppet-%s' % config_volume,
+ '--env', 'PUPPET_TAGS=%s' % puppet_tags,
+ '--env', 'NAME=%s' % config_volume,
+ '--env', 'HOSTNAME=%s' % short_hostname(),
+ '--env', 'NO_ARCHIVE=%s' % os.environ.get('NO_ARCHIVE', ''),
+ '--env', 'STEP=%s' % os.environ.get('STEP', '6'),
'--volume', '%s:/etc/config.pp:ro' % tmp_man.name,
'--volume', '/etc/puppet/:/tmp/puppet-etc/:ro',
'--volume', '/usr/share/openstack-puppet/modules/:/usr/share/openstack-puppet/modules/:ro',
log.debug(cmd_stderr)
if subproc.returncode != 0:
log.error('Failed running docker-puppet.py for %s' % config_volume)
- rm_container('docker-puppet-%s' % config_volume)
+ else:
+ # only delete successful runs, for debugging
+ rm_container('docker-puppet-%s' % config_volume)
return subproc.returncode
# Holds all the information for each process to consume.
log.error('ERROR configuring %s' % config_volume)
success = False
+
+# Update the startup configs with the config hash we generated above
+config_volume_prefix = os.environ.get('CONFIG_VOLUME_PREFIX', '/var/lib/config-data')
+log.debug('CONFIG_VOLUME_PREFIX: %s' % config_volume_prefix)
+startup_configs = os.environ.get('STARTUP_CONFIG_PATTERN', '/var/lib/tripleo-config/docker-container-startup-config-step_*.json')
+log.debug('STARTUP_CONFIG_PATTERN: %s' % startup_configs)
+infiles = glob.glob('/var/lib/tripleo-config/docker-container-startup-config-step_*.json')
+for infile in infiles:
+ with open(infile) as f:
+ infile_data = json.load(f)
+
+ for k, v in infile_data.iteritems():
+ config_volume = match_config_volume(config_volume_prefix, v)
+ if config_volume:
+ config_hash = get_config_hash(config_volume_prefix, config_volume)
+ if config_hash:
+ env = v.get('environment', [])
+ env.append("TRIPLEO_CONFIG_HASH=%s" % config_hash)
+ log.debug("Updating config hash for %s, config_volume=%s hash=%s" % (k, config_volume, config_hash))
+ infile_data[k]['environment'] = env
+
+ outfile = os.path.join(os.path.dirname(infile), "hashed-" + os.path.basename(infile))
+ with open(outfile, 'w') as out_f:
+ json.dump(infile_data, out_f)
+
if not success:
sys.exit(1)