docker-cmd hook switch to the paunch library
paunch is a library and utility which is now in OpenStack under the TripleO umbrella. It contains the logic currently in the docker-cmd hook exposed as a python library and command utility. This change switches the docker-cmd hook to paunch. Asserting --label arguments has been split out from the other docker run arguments so that paunch can add new --label arguments in future releases without breaking these tests. paunch-1.1.0 has just been release which contains new idempotency behaviour, so the tests have been modified to work with the new docker command behaviour that idempotency requires. Change-Id: I884c38ade06ab0e01432837c43f29b123e65fa3c
This commit is contained in:
parent
3a86f8789c
commit
33241a84c1
@ -1,4 +1,5 @@
|
||||
A hook which uses the `docker` command to deploy containers.
|
||||
A hook which uses the `docker` command via
|
||||
`paunch <https://docs.openstack.org/developer/paunch/>`_ to deploy containers.
|
||||
|
||||
The hook currently supports specifying containers in the `docker-compose v1
|
||||
format <https://docs.docker.com/compose/compose-file/#/version-1>`_. The
|
||||
|
@ -12,15 +12,14 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
import paunch
|
||||
import yaml
|
||||
|
||||
DOCKER_CMD = os.environ.get('HEAT_DOCKER_CMD', 'docker')
|
||||
|
||||
@ -36,135 +35,6 @@ def build_response(deploy_stdout, deploy_stderr, deploy_status_code):
|
||||
}
|
||||
|
||||
|
||||
def docker_run_args(cmd, container, config):
|
||||
cconfig = config[container]
|
||||
if cconfig.get('detach', True):
|
||||
cmd.append('--detach=true')
|
||||
if 'env_file' in cconfig:
|
||||
if isinstance(cconfig['env_file'], list):
|
||||
for f in cconfig.get('env_file', []):
|
||||
if f:
|
||||
cmd.append('--env-file=%s' % f)
|
||||
else:
|
||||
cmd.append('--env-file=%s' % cconfig['env_file'])
|
||||
for v in cconfig.get('environment', []):
|
||||
if v:
|
||||
cmd.append('--env=%s' % v)
|
||||
if 'net' in cconfig:
|
||||
cmd.append('--net=%s' % cconfig['net'])
|
||||
if 'pid' in cconfig:
|
||||
cmd.append('--pid=%s' % cconfig['pid'])
|
||||
if 'privileged' in cconfig:
|
||||
cmd.append('--privileged=%s' % str(cconfig['privileged']).lower())
|
||||
if 'restart' in cconfig:
|
||||
cmd.append('--restart=%s' % cconfig['restart'])
|
||||
if 'user' in cconfig:
|
||||
cmd.append('--user=%s' % cconfig['user'])
|
||||
for v in cconfig.get('volumes', []):
|
||||
if v:
|
||||
cmd.append('--volume=%s' % v)
|
||||
for v in cconfig.get('volumes_from', []):
|
||||
if v:
|
||||
cmd.append('--volumes_from=%s' % v)
|
||||
|
||||
cmd.append(cconfig.get('image', ''))
|
||||
cmd.extend(command_argument(cmd, cconfig.get('command')))
|
||||
|
||||
|
||||
def docker_exec_args(cmd, container, config, cid):
|
||||
cconfig = config[container]
|
||||
if 'privileged' in cconfig:
|
||||
cmd.append('--privileged=%s' % str(cconfig['privileged']).lower())
|
||||
if 'user' in cconfig:
|
||||
cmd.append('--user=%s' % cconfig['user'])
|
||||
command = command_argument(cmd, cconfig.get('command'))
|
||||
# for exec, the first argument is the container name,
|
||||
# make sure the correct one is used
|
||||
command[0] = discover_container_name(command[0], cid)
|
||||
cmd.extend(command)
|
||||
|
||||
|
||||
def command_argument(cmd, command):
|
||||
if not command:
|
||||
return []
|
||||
if not isinstance(command, list):
|
||||
return command.split()
|
||||
return command
|
||||
|
||||
|
||||
def execute(cmd):
|
||||
log.debug(' '.join(cmd))
|
||||
subproc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
cmd_stdout, cmd_stderr = subproc.communicate()
|
||||
log.debug(cmd_stdout)
|
||||
log.debug(cmd_stderr)
|
||||
return cmd_stdout, cmd_stderr, subproc.returncode
|
||||
|
||||
|
||||
def label_arguments(cmd, container, cid, iv):
|
||||
cmd.extend([
|
||||
'--label',
|
||||
'deploy_stack_id=%s' % iv.get('deploy_stack_id'),
|
||||
'--label',
|
||||
'deploy_resource_name=%s' % iv.get('deploy_resource_name'),
|
||||
'--label',
|
||||
'config_id=%s' % cid,
|
||||
'--label',
|
||||
'container_name=%s' % container,
|
||||
'--label',
|
||||
'managed_by=docker-cmd'
|
||||
])
|
||||
|
||||
|
||||
def inspect(container, format=None):
|
||||
cmd = [DOCKER_CMD, 'inspect']
|
||||
if format:
|
||||
cmd.append('--format')
|
||||
cmd.append(format)
|
||||
cmd.append(container)
|
||||
(cmd_stdout, cmd_stderr, returncode) = execute(cmd)
|
||||
if returncode != 0:
|
||||
return
|
||||
try:
|
||||
if format:
|
||||
return cmd_stdout
|
||||
else:
|
||||
return json.loads(cmd_stdout)[0]
|
||||
except Exception as e:
|
||||
log.error('Problem parsing docker inspect: %s' % e)
|
||||
|
||||
|
||||
def unique_container_name(container):
|
||||
container_name = container
|
||||
while inspect(container_name, format='exists'):
|
||||
suffix = ''.join(random.choice(
|
||||
string.ascii_lowercase + string.digits) for i in range(8))
|
||||
container_name = '%s-%s' % (container, suffix)
|
||||
return container_name
|
||||
|
||||
|
||||
def discover_container_name(container, cid):
|
||||
cmd = [
|
||||
DOCKER_CMD,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=container_name=%s' % container,
|
||||
'--filter',
|
||||
'label=config_id=%s' % cid,
|
||||
'--format',
|
||||
'{{.Names}}'
|
||||
]
|
||||
(cmd_stdout, cmd_stderr, returncode) = execute(cmd)
|
||||
if returncode != 0:
|
||||
return container
|
||||
names = cmd_stdout.split()
|
||||
if names:
|
||||
return names[0]
|
||||
return container
|
||||
|
||||
|
||||
def main(argv=sys.argv):
|
||||
global log
|
||||
log = logging.getLogger('heat-config')
|
||||
@ -200,36 +70,16 @@ def main(argv=sys.argv):
|
||||
if not isinstance(config, dict):
|
||||
config = yaml.safe_load(config)
|
||||
|
||||
key_fltr = lambda k: config[k].get('start_order', 0)
|
||||
for container in sorted(config, key=key_fltr):
|
||||
log.debug("Running container: %s" % container)
|
||||
action = config[container].get('action', 'run')
|
||||
exit_codes = config[container].get('exit_codes', [0])
|
||||
|
||||
if action == 'run':
|
||||
cmd = [
|
||||
DOCKER_CMD,
|
||||
'run',
|
||||
'--name',
|
||||
unique_container_name(container)
|
||||
]
|
||||
label_arguments(cmd, container, cid, input_values)
|
||||
docker_run_args(cmd, container, config)
|
||||
elif action == 'exec':
|
||||
cmd = [DOCKER_CMD, 'exec']
|
||||
docker_exec_args(cmd, container, config, cid)
|
||||
|
||||
(cmd_stdout, cmd_stderr, returncode) = execute(cmd)
|
||||
if cmd_stdout:
|
||||
stdout.append(cmd_stdout)
|
||||
if cmd_stderr:
|
||||
stderr.append(cmd_stderr)
|
||||
|
||||
if returncode not in exit_codes:
|
||||
log.error("Error running %s. [%s]\n" % (cmd, returncode))
|
||||
deploy_status_code = returncode
|
||||
else:
|
||||
log.debug('Completed %s' % cmd)
|
||||
labels = collections.OrderedDict()
|
||||
labels['deploy_stack_id'] = input_values.get('deploy_stack_id')
|
||||
labels['deploy_resource_name'] = input_values.get('deploy_resource_name')
|
||||
(stdout, stderr, deploy_status_code) = paunch.apply(
|
||||
cid,
|
||||
config,
|
||||
managed_by='docker-cmd',
|
||||
labels=labels,
|
||||
docker_cmd=DOCKER_CMD
|
||||
)
|
||||
|
||||
json.dump(build_response(
|
||||
'\n'.join(stdout), '\n'.join(stderr), deploy_status_code), sys.stdout)
|
||||
|
@ -15,9 +15,10 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import paunch
|
||||
|
||||
CONF_FILE = os.environ.get('HEAT_SHELL_CONFIG',
|
||||
'/var/run/heat-config/heat-config')
|
||||
|
||||
@ -49,100 +50,12 @@ def main(argv=sys.argv):
|
||||
|
||||
cmd_config_ids = [c['id'] for c in configs
|
||||
if c['group'] == 'docker-cmd']
|
||||
try:
|
||||
delete_missing_configs(cmd_config_ids)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
try:
|
||||
rename_containers()
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
|
||||
|
||||
def delete_missing_configs(config_ids):
|
||||
for conf_id in current_config_ids():
|
||||
if conf_id not in config_ids:
|
||||
log.debug('%s no longer exists, deleting containers' % conf_id)
|
||||
remove_containers(conf_id)
|
||||
|
||||
|
||||
def execute(cmd):
|
||||
log.debug(' '.join(cmd))
|
||||
subproc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
cmd_stdout, cmd_stderr = subproc.communicate()
|
||||
return cmd_stdout, cmd_stderr, subproc.returncode
|
||||
|
||||
|
||||
def current_config_ids():
|
||||
# List all config_id labels for containers managed by docker-cmd
|
||||
cmd = [
|
||||
DOCKER_CMD, 'ps', '-a',
|
||||
'--filter', 'label=managed_by=docker-cmd',
|
||||
'--format', '{{.Label "config_id"}}'
|
||||
]
|
||||
cmd_stdout, cmd_stderr, returncode = execute(cmd)
|
||||
if returncode != 0:
|
||||
return set()
|
||||
return set(cmd_stdout.split())
|
||||
|
||||
|
||||
def remove_containers(conf_id):
|
||||
cmd = [
|
||||
DOCKER_CMD, 'ps', '-q', '-a',
|
||||
'--filter', 'label=managed_by=docker-cmd',
|
||||
'--filter', 'label=config_id=%s' % conf_id
|
||||
]
|
||||
cmd_stdout, cmd_stderr, returncode = execute(cmd)
|
||||
if returncode == 0:
|
||||
for container in cmd_stdout.split():
|
||||
remove_container(container)
|
||||
|
||||
|
||||
def remove_container(container):
|
||||
cmd = [DOCKER_CMD, 'rm', '-f', container]
|
||||
cmd_stdout, cmd_stderr, returncode = execute(cmd)
|
||||
if returncode != 0:
|
||||
log.error('Error removing container: %s' % container)
|
||||
log.error(cmd_stderr)
|
||||
|
||||
|
||||
def rename_containers():
|
||||
# list every container name, and its container_name label
|
||||
cmd = [
|
||||
DOCKER_CMD, 'ps', '-a',
|
||||
'--format', '{{.Names}} {{.Label "container_name"}}'
|
||||
]
|
||||
cmd_stdout, cmd_stderr, returncode = execute(cmd)
|
||||
if returncode != 0:
|
||||
return
|
||||
|
||||
lines = cmd_stdout.split("\n")
|
||||
current_containers = []
|
||||
need_renaming = {}
|
||||
for line in lines:
|
||||
entry = line.split()
|
||||
if not entry:
|
||||
continue
|
||||
current_containers.append(entry[0])
|
||||
|
||||
# ignore if container_name label not set
|
||||
if len(entry) < 2:
|
||||
continue
|
||||
|
||||
# ignore if desired name is already actual name
|
||||
if entry[0] == entry[-1]:
|
||||
continue
|
||||
|
||||
need_renaming[entry[0]] = entry[-1]
|
||||
|
||||
for current, desired in sorted(need_renaming.items()):
|
||||
if desired in current_containers:
|
||||
log.info('Cannot rename "%s" since "%s" still exists' % (
|
||||
current, desired))
|
||||
else:
|
||||
cmd = [DOCKER_CMD, 'rename', current, desired]
|
||||
execute(cmd)
|
||||
paunch.cleanup(
|
||||
cmd_config_ids,
|
||||
managed_by='docker-cmd',
|
||||
docker_cmd=DOCKER_CMD
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The logic for the docker-cmd hook is now provided by the paunch library,
|
||||
where further feature work will occur.
|
@ -5,6 +5,7 @@ fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
# Hacking already pins down pep8, pyflakes and flake8
|
||||
hacking>=0.12.0,!=0.13.0,<0.14 # Apache-2.0
|
||||
mock>=2.0 # BSD
|
||||
paunch>=1.0.0 # Apache-2.0
|
||||
requests>=2.10.0,!=2.12.2,!=2.13.0 # Apache-2.0
|
||||
requests-mock>=1.1 # Apache-2.0
|
||||
salt
|
||||
|
@ -112,28 +112,54 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'TEST_STATE_PATH': self.test_state_path,
|
||||
})
|
||||
|
||||
def assert_args_and_labels(self, expected_args, expected_labels, observed):
|
||||
'''Assert the labels arguments separately to other arguments.
|
||||
|
||||
Tests that each expected_labels label exists, and remaining
|
||||
expected arguments match exactly.
|
||||
|
||||
This allows paunch to add new label arguments without breaking these
|
||||
tests.
|
||||
'''
|
||||
|
||||
args = []
|
||||
labels = []
|
||||
j = 0
|
||||
while j < len(observed):
|
||||
if observed[j] == '--label':
|
||||
j += 1
|
||||
labels.append(observed[j])
|
||||
else:
|
||||
args.append(observed[j])
|
||||
j += 1
|
||||
|
||||
self.assertEqual(expected_args, args)
|
||||
for label in expected_labels:
|
||||
self.assertIn(label, labels)
|
||||
|
||||
def test_hook(self):
|
||||
|
||||
self.env.update({
|
||||
'TEST_RESPONSE': json.dumps([{
|
||||
'stderr': 'Error: No such image, container or task: db',
|
||||
'returncode': 1
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'Creating db...'
|
||||
}, {
|
||||
'stderr': 'Error: No such image, container or task: web',
|
||||
'returncode': 1
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'Creating web...'
|
||||
}, {
|
||||
'stdout': 'web',
|
||||
}, {
|
||||
|
||||
'stdout': '',
|
||||
'stderr': 'one.txt\ntwo.txt\nthree.txt'
|
||||
}])
|
||||
'TEST_RESPONSE': json.dumps([
|
||||
# ps for delete missing
|
||||
{},
|
||||
# ps for renames
|
||||
{},
|
||||
# ps for currently running containers
|
||||
{},
|
||||
# inspect for db unique container name
|
||||
{},
|
||||
# docker run db
|
||||
{'stderr': 'Creating db...'},
|
||||
# inspect for web unique container name
|
||||
{},
|
||||
# docker run web
|
||||
{'stderr': 'Creating web...'},
|
||||
# name lookup for exec web
|
||||
{'stdout': 'web'},
|
||||
# docker exec web
|
||||
{'stderr': 'one.txt\ntwo.txt\nthree.txt'},
|
||||
])
|
||||
})
|
||||
returncode, stdout, stderr = self.run_cmd(
|
||||
[self.hook_path], self.env, json.dumps(self.data))
|
||||
@ -148,58 +174,75 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'deploy_status_code': 0
|
||||
}, json.loads(stdout))
|
||||
|
||||
state = list(self.json_from_files(self.test_state_path, 6))
|
||||
state = list(self.json_from_files(self.test_state_path, 9))
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[0]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[1]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[2]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
'db',
|
||||
], state[0]['args'])
|
||||
self.assertEqual([
|
||||
'db'
|
||||
], state[3]['args'])
|
||||
self.assert_args_and_labels([
|
||||
self.fake_tool_path,
|
||||
'run',
|
||||
'--name',
|
||||
'db',
|
||||
'--label',
|
||||
'deploy_stack_id=the_stack',
|
||||
'--label',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'--label',
|
||||
'config_id=abc123',
|
||||
'--label',
|
||||
'container_name=db',
|
||||
'--label',
|
||||
'managed_by=docker-cmd',
|
||||
'--detach=true',
|
||||
'--env-file=env.file',
|
||||
'--env=foo=bar',
|
||||
'--privileged=false',
|
||||
'xxx'
|
||||
''
|
||||
], state[1]['args'])
|
||||
], [
|
||||
'deploy_stack_id=the_stack',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'config_id=abc123',
|
||||
'container_name=db',
|
||||
'managed_by=docker-cmd',
|
||||
], state[4]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
'web',
|
||||
], state[2]['args'])
|
||||
self.assertEqual([
|
||||
], state[5]['args'])
|
||||
self.assert_args_and_labels([
|
||||
self.fake_tool_path,
|
||||
'run',
|
||||
'--name',
|
||||
'web',
|
||||
'--label',
|
||||
'deploy_stack_id=the_stack',
|
||||
'--label',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'--label',
|
||||
'config_id=abc123',
|
||||
'--label',
|
||||
'container_name=web',
|
||||
'--label',
|
||||
'managed_by=docker-cmd',
|
||||
'--detach=true',
|
||||
'--env-file=foo.env',
|
||||
'--env-file=bar.conf',
|
||||
@ -214,7 +257,13 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'yyy',
|
||||
'/bin/webserver',
|
||||
'start'
|
||||
], state[3]['args'])
|
||||
], [
|
||||
'deploy_stack_id=the_stack',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'config_id=abc123',
|
||||
'container_name=web',
|
||||
'managed_by=docker-cmd',
|
||||
], state[6]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
@ -225,25 +274,32 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}}',
|
||||
], state[4]['args'])
|
||||
], state[7]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'exec',
|
||||
'web',
|
||||
'/bin/ls',
|
||||
'-l'
|
||||
], state[5]['args'])
|
||||
], state[8]['args'])
|
||||
|
||||
def test_hook_exit_codes(self):
|
||||
|
||||
self.env.update({
|
||||
'TEST_RESPONSE': json.dumps([{
|
||||
'stdout': 'web',
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'Warning: custom exit code',
|
||||
'returncode': 1
|
||||
}])
|
||||
'TEST_RESPONSE': json.dumps([
|
||||
# ps for delete missing
|
||||
{},
|
||||
# ps for renames
|
||||
{},
|
||||
# ps for currently running containers
|
||||
{},
|
||||
{'stdout': 'web'},
|
||||
{
|
||||
'stdout': '',
|
||||
'stderr': 'Warning: custom exit code',
|
||||
'returncode': 1
|
||||
}
|
||||
])
|
||||
})
|
||||
returncode, stdout, stderr = self.run_cmd(
|
||||
[self.hook_path], self.env, json.dumps(self.data_exit_code))
|
||||
@ -254,7 +310,38 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'deploy_status_code': 0
|
||||
}, json.loads(stdout))
|
||||
|
||||
state = list(self.json_from_files(self.test_state_path, 2))
|
||||
state = list(self.json_from_files(self.test_state_path, 5))
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[0]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[1]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[2]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
@ -265,37 +352,42 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}}',
|
||||
], state[0]['args'])
|
||||
], state[3]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'exec',
|
||||
'web',
|
||||
'/bin/ls',
|
||||
'-l'
|
||||
], state[1]['args'])
|
||||
], state[4]['args'])
|
||||
|
||||
def test_hook_failed(self):
|
||||
|
||||
self.env.update({
|
||||
'TEST_RESPONSE': json.dumps([{
|
||||
'stderr': 'Error: No such image, container or task: db',
|
||||
'returncode': 1
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'Creating db...'
|
||||
}, {
|
||||
'stderr': 'Error: No such image, container or task: web',
|
||||
'returncode': 1
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'Creating web...'
|
||||
}, {
|
||||
'stdout': 'web',
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'No such file or directory',
|
||||
'returncode': 2
|
||||
}])
|
||||
'TEST_RESPONSE': json.dumps([
|
||||
# ps for delete missing
|
||||
{},
|
||||
# ps for renames
|
||||
{},
|
||||
# ps for currently running containers
|
||||
{},
|
||||
# inspect for db unique container name
|
||||
{},
|
||||
# docker run db
|
||||
{'stderr': 'Creating db...'},
|
||||
# inspect for web unique container name
|
||||
{},
|
||||
# docker run web
|
||||
{'stderr': 'Creating web...'},
|
||||
# name lookup for exec web
|
||||
{'stdout': 'web'},
|
||||
# docker exec web fails
|
||||
{
|
||||
'stdout': '',
|
||||
'stderr': 'No such file or directory',
|
||||
'returncode': 2
|
||||
}
|
||||
])
|
||||
})
|
||||
returncode, stdout, stderr = self.run_cmd(
|
||||
[self.hook_path], self.env, json.dumps(self.data))
|
||||
@ -308,57 +400,75 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'deploy_status_code': 2
|
||||
}, json.loads(stdout))
|
||||
|
||||
state = list(self.json_from_files(self.test_state_path, 6))
|
||||
state = list(self.json_from_files(self.test_state_path, 9))
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[0]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[1]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[2]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
'db',
|
||||
], state[0]['args'])
|
||||
self.assertEqual([
|
||||
'db'
|
||||
], state[3]['args'])
|
||||
self.assert_args_and_labels([
|
||||
self.fake_tool_path,
|
||||
'run',
|
||||
'--name',
|
||||
'db',
|
||||
'--label',
|
||||
'deploy_stack_id=the_stack',
|
||||
'--label',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'--label',
|
||||
'config_id=abc123',
|
||||
'--label',
|
||||
'container_name=db',
|
||||
'--label',
|
||||
'managed_by=docker-cmd',
|
||||
'--detach=true',
|
||||
'--env-file=env.file',
|
||||
'--env=foo=bar',
|
||||
'--privileged=false',
|
||||
'xxx'
|
||||
], state[1]['args'])
|
||||
''
|
||||
], [
|
||||
'deploy_stack_id=the_stack',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'config_id=abc123',
|
||||
'container_name=db',
|
||||
'managed_by=docker-cmd',
|
||||
], state[4]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
'web',
|
||||
], state[2]['args'])
|
||||
self.assertEqual([
|
||||
], state[5]['args'])
|
||||
self.assert_args_and_labels([
|
||||
self.fake_tool_path,
|
||||
'run',
|
||||
'--name',
|
||||
'web',
|
||||
'--label',
|
||||
'deploy_stack_id=the_stack',
|
||||
'--label',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'--label',
|
||||
'config_id=abc123',
|
||||
'--label',
|
||||
'container_name=web',
|
||||
'--label',
|
||||
'managed_by=docker-cmd',
|
||||
'--detach=true',
|
||||
'--env-file=foo.env',
|
||||
'--env-file=bar.conf',
|
||||
@ -373,7 +483,13 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'yyy',
|
||||
'/bin/webserver',
|
||||
'start'
|
||||
], state[3]['args'])
|
||||
], [
|
||||
'deploy_stack_id=the_stack',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'config_id=abc123',
|
||||
'container_name=web',
|
||||
'managed_by=docker-cmd',
|
||||
], state[6]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
@ -384,43 +500,47 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}}',
|
||||
], state[4]['args'])
|
||||
], state[7]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'exec',
|
||||
'web',
|
||||
'/bin/ls',
|
||||
'-l'
|
||||
], state[5]['args'])
|
||||
], state[8]['args'])
|
||||
|
||||
def test_hook_unique_names(self):
|
||||
|
||||
self.env.update({
|
||||
'TEST_RESPONSE': json.dumps([{
|
||||
'stdout': 'exists\n',
|
||||
'returncode': 0
|
||||
}, {
|
||||
'stderr': 'Error: No such image, container or task: db-blah',
|
||||
'returncode': 1
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'Creating db...'
|
||||
}, {
|
||||
'stdout': 'exists\n',
|
||||
'returncode': 0
|
||||
}, {
|
||||
'stderr': 'Error: No such image, container or task: web-blah',
|
||||
'returncode': 1
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'Creating web...'
|
||||
}, {
|
||||
'stdout': 'web-asdf1234',
|
||||
}, {
|
||||
'stdout': '',
|
||||
'stderr': 'one.txt\ntwo.txt\nthree.txt'
|
||||
}])
|
||||
'TEST_RESPONSE': json.dumps([
|
||||
# ps for delete missing in this config id
|
||||
{},
|
||||
# ps for renames
|
||||
{'stdout': 'web web\ndb db\n'},
|
||||
# ps for currently running containers in this config id
|
||||
{},
|
||||
# inspect for db unique container name
|
||||
{'stdout': 'exists'},
|
||||
{
|
||||
'stderr': 'Error: No such container: db-blah',
|
||||
'returncode': 1
|
||||
},
|
||||
# docker run db
|
||||
{'stderr': 'Creating db...'},
|
||||
# # inspect for web unique container name
|
||||
{'stdout': 'exists'},
|
||||
{
|
||||
'stderr': 'Error: No such container: web-blah',
|
||||
'returncode': 1
|
||||
},
|
||||
# # docker run web
|
||||
{'stderr': 'Creating web...'},
|
||||
# name lookup for exec web
|
||||
{'stdout': 'web-asdf1234'},
|
||||
# docker exec web-asdf1234
|
||||
{'stderr': 'one.txt\ntwo.txt\nthree.txt'},
|
||||
])
|
||||
})
|
||||
|
||||
returncode, stdout, stderr = self.run_cmd(
|
||||
[self.hook_path], self.env, json.dumps(self.data))
|
||||
|
||||
@ -434,75 +554,92 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'deploy_status_code': 0
|
||||
}, json.loads(stdout))
|
||||
|
||||
state = list(self.json_from_files(self.test_state_path, 8))
|
||||
db_container_name = state[1]['args'][4]
|
||||
web_container_name = state[4]['args'][4]
|
||||
state = list(self.json_from_files(self.test_state_path, 11))
|
||||
db_container_name = state[4]['args'][4]
|
||||
web_container_name = state[7]['args'][4]
|
||||
self.assertRegex(db_container_name, 'db-[0-9a-z]{8}')
|
||||
self.assertRegex(web_container_name, 'web-[0-9a-z]{8}')
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'exists',
|
||||
'db',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[0]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'exists',
|
||||
db_container_name,
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[1]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'run',
|
||||
'--name',
|
||||
db_container_name,
|
||||
'--label',
|
||||
'deploy_stack_id=the_stack',
|
||||
'--label',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'--label',
|
||||
'config_id=abc123',
|
||||
'--label',
|
||||
'container_name=db',
|
||||
'--label',
|
||||
'managed_by=docker-cmd',
|
||||
'--detach=true',
|
||||
'--env-file=env.file',
|
||||
'--env=foo=bar',
|
||||
'--privileged=false',
|
||||
'xxx'
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--filter',
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[2]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
'web',
|
||||
'db'
|
||||
], state[3]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
web_container_name,
|
||||
db_container_name,
|
||||
], state[4]['args'])
|
||||
self.assert_args_and_labels([
|
||||
self.fake_tool_path,
|
||||
'run',
|
||||
'--name',
|
||||
db_container_name,
|
||||
'--detach=true',
|
||||
'--env-file=env.file',
|
||||
'--env=foo=bar',
|
||||
'--privileged=false',
|
||||
'xxx'
|
||||
], [
|
||||
'deploy_stack_id=the_stack',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'config_id=abc123',
|
||||
'container_name=db',
|
||||
'managed_by=docker-cmd',
|
||||
], state[5]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
'web',
|
||||
], state[6]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'inspect',
|
||||
'--format',
|
||||
'exists',
|
||||
web_container_name,
|
||||
], state[7]['args'])
|
||||
self.assert_args_and_labels([
|
||||
self.fake_tool_path,
|
||||
'run',
|
||||
'--name',
|
||||
web_container_name,
|
||||
'--label',
|
||||
'deploy_stack_id=the_stack',
|
||||
'--label',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'--label',
|
||||
'config_id=abc123',
|
||||
'--label',
|
||||
'container_name=web',
|
||||
'--label',
|
||||
'managed_by=docker-cmd',
|
||||
'--detach=true',
|
||||
'--env-file=foo.env',
|
||||
'--env-file=bar.conf',
|
||||
@ -517,7 +654,13 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'yyy',
|
||||
'/bin/webserver',
|
||||
'start'
|
||||
], state[5]['args'])
|
||||
], [
|
||||
'deploy_stack_id=the_stack',
|
||||
'deploy_resource_name=the_deployment',
|
||||
'config_id=abc123',
|
||||
'container_name=web',
|
||||
'managed_by=docker-cmd',
|
||||
], state[8]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
@ -528,14 +671,14 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
'label=config_id=abc123',
|
||||
'--format',
|
||||
'{{.Names}}',
|
||||
], state[6]['args'])
|
||||
], state[9]['args'])
|
||||
self.assertEqual([
|
||||
self.fake_tool_path,
|
||||
'exec',
|
||||
'web-asdf1234',
|
||||
'/bin/ls',
|
||||
'-l'
|
||||
], state[7]['args'])
|
||||
], state[10]['args'])
|
||||
|
||||
def test_cleanup_deleted(self):
|
||||
self.env.update({
|
||||
@ -571,6 +714,8 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[1]['args'])
|
||||
@ -647,6 +792,8 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[5]['args'])
|
||||
@ -687,6 +834,8 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[1]['args'])
|
||||
@ -765,6 +914,8 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[5]['args'])
|
||||
@ -805,6 +956,8 @@ class HookDockerCmdTest(common.RunScriptTest):
|
||||
self.fake_tool_path,
|
||||
'ps',
|
||||
'-a',
|
||||
'--filter',
|
||||
'label=managed_by=docker-cmd',
|
||||
'--format',
|
||||
'{{.Names}} {{.Label "container_name"}}'
|
||||
], state[1]['args'])
|
||||
|
Loading…
x
Reference in New Issue
Block a user