blob: 825cd60c7fcc846405b7a64326d1bcd0cbdf5bac [file] [log] [blame]
alshabib7941d402016-11-08 00:11:20 +01001#
2# Copyright 2016 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16from common.utils.dockerhelpers import create_host_config, create_container, start_container, create_networking_config, \
17 get_all_running_containers, inspect_container, remove_container
alshabib05fb71f2016-12-04 16:08:29 -080018from docker import errors
alshabib7941d402016-11-08 00:11:20 +010019
20from structlog import get_logger
alshabib9d222022016-11-10 16:11:09 -080021import yaml
alshabib7941d402016-11-08 00:11:20 +010022
23log = get_logger()
24
25INSTANCE_ID_KEY = 'com.docker.compose.container-number'
26INSTANCE_NAME_KEY = 'name'
27
28
29def check(event):
30 return ('from' in event) and\
31 ('Actor' in event and 'Attributes' in event['Actor'] and\
32 INSTANCE_ID_KEY in event['Actor']['Attributes']) and\
33 ('Actor' in event and 'Attributes' in event['Actor'] and\
34 INSTANCE_NAME_KEY in event['Actor']['Attributes'])
35
36def get_entry(key, dico, mandatory = False, noneval=None):
37 if key in dico:
38 return dico[key]
39 if mandatory:
40 raise Exception('Key {} must be in container config'.format(key))
41 return noneval
42
43def obtain_network_name(data):
44 return data['NetworkSettings']['Networks'].keys()
45
46
47def create_network_config(network, links):
48 if links is None:
49 return None
50 # Assuming only one network exists....
51 return create_networking_config(network[0], { l : l for l in links})
52
alshabib7941d402016-11-08 00:11:20 +010053def process_value(value):
54 if value is None:
55 return None
56 if isinstance(value, dict):
57 return value
58 if isinstance(value, list):
59 retval = {}
60 for item in value:
61 if not isinstance(item, int) and ':' in item:
62 item_split = item.split(':')
63 retval[item_split[0]] = item_split[1]
64 else:
65 retval[item] = None
66 return retval
67 raise Exception('Cannot handle {}'.format(value))
68
69def construct_container_spec(config):
70 container_spec = {}
71 container_spec['image'] = get_entry('image', config, mandatory=True)
alshabib7941d402016-11-08 00:11:20 +010072 container_spec['command'] = get_entry('command', config, mandatory=True)
73 container_spec['environment'] = get_entry('environment', config, noneval={})
74 container_spec['ports'] = get_entry('ports', config)
75 container_spec['volumes'] = get_entry('volumes', config)
76 return container_spec
77
78def service_shutdown(service, instance_name, config):
79 containers = get_all_running_containers()
80 for container in containers:
alshabib05fb71f2016-12-04 16:08:29 -080081 try:
82 info = inspect_container(container['Id'])
83 except errors.NotFound, e:
84 continue
alshabib7941d402016-11-08 00:11:20 +010085 envs = info['Config']['Env']
86 for env in envs:
87 for name in env.split('='):
88 if name == instance_name:
89 log.info('Removing container {}'.format(container['Names']))
90 remove_container(container['Id'])
91
alshabib9d222022016-11-10 16:11:09 -080092def start_slaves(service, instance_name, instance_id, data, conf):
93 network = obtain_network_name(data)
94 # still assuming a single network
95 config = yaml.load(conf.render(data=data, network=network[0]))
96
alshabib7941d402016-11-08 00:11:20 +010097 if service not in config['services']:
98 log.debug('Unknown service {}'.format(service))
99 return
100 for slave in config['services'][service]['slaves']:
101 if slave not in config['slaves']:
102 log.debug('Unknown slave service {}'.format(slave))
103 continue
alshabib9d222022016-11-10 16:11:09 -0800104
alshabib7941d402016-11-08 00:11:20 +0100105 netcfg = create_network_config(network, get_entry('links', config['slaves'][slave]))
106 container_spec = construct_container_spec(config['slaves'][slave])
107 container_spec['networking_config'] = netcfg
108 if 'volumes' in container_spec:
109 container_spec['host_config'] = create_host_config(
110 process_value(container_spec['volumes']),
111 process_value(container_spec['ports']))
112 container_spec['name'] = 'podder_%s_%s' % (slave, instance_id)
113
114 container_spec['environment']['PODDER_MASTER'] = instance_name
115
116 container = create_container(container_spec)
alshabib9d222022016-11-10 16:11:09 -0800117 log.info('Starting slaves for {}'.format(instance_name))
alshabib7941d402016-11-08 00:11:20 +0100118 start_container(container)
119
120
alshabib9d222022016-11-10 16:11:09 -0800121def stop_slaves(service, instance_name, instance_id, data, conf):
alshabib7941d402016-11-08 00:11:20 +0100122 log.info('Stopping slaves for {}'.format(instance_name))
alshabib9d222022016-11-10 16:11:09 -0800123 config = yaml.load(conf.render())
alshabib7941d402016-11-08 00:11:20 +0100124 if service in config['services']:
125 service_shutdown(service, instance_name, config)
126 else:
127 # handle slave shutdown; restart him
128 pass
129
130
131def handler_start(event, data, config):
132 if not check(event):
133 log.debug('event {} is invalid'.format(event) )
134 return
135 service = event['from']
136 instance_name = event['Actor']['Attributes'][INSTANCE_NAME_KEY]
137 instance_id = event['Actor']['Attributes'][INSTANCE_ID_KEY]
138 start_slaves(service, instance_name, instance_id, data, config)
139
140def handler_stop(event, data, config):
141 if not check(event):
142 log.debug('event {} is invalid'.format(event) )
143 return
144 service = event['from']
145 instance_name = event['Actor']['Attributes'][INSTANCE_NAME_KEY]
146 instance_id = event['Actor']['Attributes'][INSTANCE_ID_KEY]
147 stop_slaves(service, instance_name, instance_id, data, config)
148