#!/usr/bin/env python # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json import logging import os import subprocess import sys import yaml CONF_FILE = os.environ.get('HEAT_SHELL_CONFIG', '/var/run/heat-config/heat-config') DOCKER_COMPOSE_DIR = os.environ.get( 'HEAT_DOCKER_COMPOSE_WORKING', '/var/lib/heat-config/heat-config-docker-compose') DOCKER_COMPOSE_CMD = os.environ.get('HEAT_DOCKER_COMPOSE_CMD', 'docker-compose') def main(argv=sys.argv): log = logging.getLogger('heat-config') handler = logging.StreamHandler(sys.stderr) handler.setFormatter( logging.Formatter( '[%(asctime)s] (%(name)s) [%(levelname)s] %(message)s')) log.addHandler(handler) log.setLevel('DEBUG') if not os.path.exists(CONF_FILE): log.error('No config file %s' % CONF_FILE) return 1 if not os.path.isdir(DOCKER_COMPOSE_DIR): os.makedirs(DOCKER_COMPOSE_DIR, 0o700) try: configs = json.load(open(CONF_FILE)) except ValueError: pass try: cleanup_stale_projects(configs) for c in configs: write_compose_config(c) except Exception as e: log.exception(e) def cleanup_stale_projects(configs): def deployments(configs): for c in configs: yield c['name'] def compose_projects(compose_dir): for proj in os.listdir(compose_dir): if os.path.isfile( os.path.join(DOCKER_COMPOSE_DIR, '%s/docker-compose.yml' % proj)): yield proj def cleanup_containers(project): cmd = [ DOCKER_COMPOSE_CMD, 'kill' ] subproc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = subproc.communicate() for proj in compose_projects(DOCKER_COMPOSE_DIR): if proj not in deployments(configs): proj_dir = os.path.join(DOCKER_COMPOSE_DIR, proj) os.chdir(proj_dir) cleanup_containers(proj) os.remove('%s/docker-compose.yml' % proj_dir) def write_compose_config(c): group = c.get('group') if group != 'docker-compose': return def prepare_dir(path): if not os.path.isdir(path): os.makedirs(path, 0o700) compose_conf = c.get('config', '') if isinstance(compose_conf, dict): yaml_config = yaml.safe_dump(compose_conf, default_flow_style=False) else: yaml_config = compose_conf proj_dir = os.path.join(DOCKER_COMPOSE_DIR, c['name']) prepare_dir(proj_dir) fn = os.path.join(proj_dir, 'docker-compose.yml') with os.fdopen(os.open(fn, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, 0o600), 'w') as f: f.write(yaml_config.encode('utf-8')) if __name__ == '__main__': sys.exit(main(sys.argv))