Last active
August 29, 2015 14:18
-
-
Save mfischer-zd/6b5a8e7d4c8a99f066c3 to your computer and use it in GitHub Desktop.
consul_tag_helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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