Skip to content

Instantly share code, notes, and snippets.

@mfischer-zd
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mfischer-zd/6b5a8e7d4c8a99f066c3 to your computer and use it in GitHub Desktop.
Save mfischer-zd/6b5a8e7d4c8a99f066c3 to your computer and use it in GitHub Desktop.
consul_tag_helper
#!/usr/bin/env python
# vim: syn=python:ts=4:sw=4:et:ai
# Usage: consul_tag_helper
#
# This program is in the Public Domain.
#
# Place your consul configuration files in /etc/consul.boilerplate.d, instead
# of /etc/consul.d. Boilerplate JSON configuration files can contain an extra
# key under 'service' called 'tag_program'. 'tag_program' is a path to an
# arbitrary program that emits tags, one per line, to standard output.
#
# consul_tag_helper will iterate through each boilerplate configuration file,
# run the tag helper script, insert the emitted tags into the boilerplate
# configuration, save the generated configuration to /etc/consul.d, and
# reload the Consul agent.
import os
import shlex
import subprocess
import tempfile
import json
import hashlib
import time
BOILERPLATE_DIR = '/etc/consul.boilerplate.d'
CONSUL_CONFDIR = '/etc/consul.d'
def service_change_pending(config, filename):
pending_json = json.dumps(config, indent=3, sort_keys=True)
pending_digest = hashlib.md5(pending_json).digest()
current_digest = hashlib.md5(open(filename, 'r').read()).digest()
if pending_digest != current_digest:
return True
else:
return False
def update_service(config, filename, uid=0, gid=0, mode=0644):
print 'Service %s has changed tags. Rewriting configuration file and reloading consul.' % config['service']['name']
try:
tmp_consul_config_file = tempfile.NamedTemporaryFile(prefix=CONSUL_CONFDIR + '/', delete=False)
json.dump(config, tmp_consul_config_file, indent=3, sort_keys=True)
tmp_consul_config_file.close()
os.chown(tmp_consul_config_file.name, uid, gid)
os.chmod(tmp_consul_config_file.name, mode)
os.rename(tmp_consul_config_file.name, filename)
subprocess.check_call(['consul', 'reload'])
finally:
if os.path.exists(tmp_consul_config_file.name):
os.remove(tmp_consul_config_file.name)
while True:
if os.path.exists(BOILERPLATE_DIR):
for basename in os.listdir(BOILERPLATE_DIR):
boilerplate_filename = os.path.join(BOILERPLATE_DIR, basename)
consul_config_filename = os.path.join(CONSUL_CONFDIR, basename)
stat = os.stat(boilerplate_filename)
with open(boilerplate_filename, 'r') as boilerplate_file:
config = json.load(boilerplate_file)
if 'service' in config:
service = config['service']
if 'tag_program' in service:
tag_program = service['tag_program']
del service['tag_program']
tag_program = shlex.split(tag_program.encode('ascii', 'replace')) # Need to encode as shelx.split() in Python 2.6.x only accepts non unicode strings
tags = subprocess.Popen(tag_program, stdout=subprocess.PIPE).communicate()[0].splitlines()
if 'tags' not in service:
service['tags'] = []
service['tags'] += tags
if not os.path.exists(consul_config_filename):
update_service(config, consul_config_filename, uid=stat.st_uid, gid=stat.st_gid, mode=stat.st_mode)
elif service_change_pending(config, consul_config_filename):
update_service(config, consul_config_filename, uid=stat.st_uid, gid=stat.st_gid, mode=stat.st_mode)
time.sleep(5)
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment