bf603c75cc
A scenario for self-healing and auto-scaling with Heat, Mistral, Zaqar, and Aodh. Change-Id: I652c3b0e0caa433bfd08c0cb35b21507f0897889
117 lines
3.4 KiB
Python
Executable File
117 lines
3.4 KiB
Python
Executable File
#!/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))
|