blob: 2cafc7ec225652248b20e0ac9453eb0ecdc18012 [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
18
19from structlog import get_logger
alshabib9d222022016-11-10 16:11:09 -080020import yaml
alshabib7941d402016-11-08 00:11:20 +010021
22log = get_logger()
23
24INSTANCE_ID_KEY = 'com.docker.compose.container-number'
25INSTANCE_NAME_KEY = 'name'
26
27
28def check(event):
29 return ('from' in event) and\
30 ('Actor' in event and 'Attributes' in event['Actor'] and\
31 INSTANCE_ID_KEY in event['Actor']['Attributes']) and\
32 ('Actor' in event and 'Attributes' in event['Actor'] and\
33 INSTANCE_NAME_KEY in event['Actor']['Attributes'])
34
35def get_entry(key, dico, mandatory = False, noneval=None):
36 if key in dico:
37 return dico[key]
38 if mandatory:
39 raise Exception('Key {} must be in container config'.format(key))
40 return noneval
41
42def obtain_network_name(data):
43 return data['NetworkSettings']['Networks'].keys()
44
45
46def create_network_config(network, links):
47 if links is None:
48 return None
49 # Assuming only one network exists....
50 return create_networking_config(network[0], { l : l for l in links})
51
alshabib7941d402016-11-08 00:11:20 +010052def process_value(value):
53 if value is None:
54 return None
55 if isinstance(value, dict):
56 return value
57 if isinstance(value, list):
58 retval = {}
59 for item in value:
60 if not isinstance(item, int) and ':' in item:
61 item_split = item.split(':')
62 retval[item_split[0]] = item_split[1]
63 else:
64 retval[item] = None
65 return retval
66 raise Exception('Cannot handle {}'.format(value))
67
68def construct_container_spec(config):
69 container_spec = {}
70 container_spec['image'] = get_entry('image', config, mandatory=True)
alshabib7941d402016-11-08 00:11:20 +010071 container_spec['command'] = get_entry('command', config, mandatory=True)
72 container_spec['environment'] = get_entry('environment', config, noneval={})
73 container_spec['ports'] = get_entry('ports', config)
74 container_spec['volumes'] = get_entry('volumes', config)
75 return container_spec
76
77def service_shutdown(service, instance_name, config):
78 containers = get_all_running_containers()
79 for container in containers:
80 info = inspect_container(container['Id'])
81 envs = info['Config']['Env']
82 for env in envs:
83 for name in env.split('='):
84 if name == instance_name:
85 log.info('Removing container {}'.format(container['Names']))
86 remove_container(container['Id'])
87
alshabib9d222022016-11-10 16:11:09 -080088def start_slaves(service, instance_name, instance_id, data, conf):
89 network = obtain_network_name(data)
90 # still assuming a single network
91 config = yaml.load(conf.render(data=data, network=network[0]))
92
alshabib7941d402016-11-08 00:11:20 +010093 if service not in config['services']:
94 log.debug('Unknown service {}'.format(service))
95 return
96 for slave in config['services'][service]['slaves']:
97 if slave not in config['slaves']:
98 log.debug('Unknown slave service {}'.format(slave))
99 continue
alshabib9d222022016-11-10 16:11:09 -0800100
alshabib7941d402016-11-08 00:11:20 +0100101 netcfg = create_network_config(network, get_entry('links', config['slaves'][slave]))
102 container_spec = construct_container_spec(config['slaves'][slave])
103 container_spec['networking_config'] = netcfg
104 if 'volumes' in container_spec:
105 container_spec['host_config'] = create_host_config(
106 process_value(container_spec['volumes']),
107 process_value(container_spec['ports']))
108 container_spec['name'] = 'podder_%s_%s' % (slave, instance_id)
109
110 container_spec['environment']['PODDER_MASTER'] = instance_name
111
112 container = create_container(container_spec)
alshabib9d222022016-11-10 16:11:09 -0800113 log.info('Starting slaves for {}'.format(instance_name))
alshabib7941d402016-11-08 00:11:20 +0100114 start_container(container)
115
116
alshabib9d222022016-11-10 16:11:09 -0800117def stop_slaves(service, instance_name, instance_id, data, conf):
alshabib7941d402016-11-08 00:11:20 +0100118 log.info('Stopping slaves for {}'.format(instance_name))
alshabib9d222022016-11-10 16:11:09 -0800119 config = yaml.load(conf.render())
alshabib7941d402016-11-08 00:11:20 +0100120 if service in config['services']:
121 service_shutdown(service, instance_name, config)
122 else:
123 # handle slave shutdown; restart him
124 pass
125
126
127def handler_start(event, data, config):
128 if not check(event):
129 log.debug('event {} is invalid'.format(event) )
130 return
131 service = event['from']
132 instance_name = event['Actor']['Attributes'][INSTANCE_NAME_KEY]
133 instance_id = event['Actor']['Attributes'][INSTANCE_ID_KEY]
134 start_slaves(service, instance_name, instance_id, data, config)
135
136def handler_stop(event, data, config):
137 if not check(event):
138 log.debug('event {} is invalid'.format(event) )
139 return
140 service = event['from']
141 instance_name = event['Actor']['Attributes'][INSTANCE_NAME_KEY]
142 instance_id = event['Actor']['Attributes'][INSTANCE_ID_KEY]
143 stop_slaves(service, instance_name, instance_id, data, config)
144