Refactor compose1.py to welcome new builders
Refactor compose1.py so we can support more builders (e.g. podman). Change-Id: Ic9ae9ae5b5b1315493903baa7efb23d6c5d37790
This commit is contained in:
parent
e2093fb7fc
commit
3c0d4f51d9
|
@ -117,7 +117,7 @@ def debug(config_id, container_name, action, config, managed_by, labels=None,
|
|||
'--name',
|
||||
r.unique_container_name(container_name)
|
||||
]
|
||||
builder.docker_run_args(cmd, container_name)
|
||||
builder.container_run_args(cmd, container_name)
|
||||
print(' '.join(cmd))
|
||||
elif action == 'run':
|
||||
cmd = [
|
||||
|
@ -126,7 +126,7 @@ def debug(config_id, container_name, action, config, managed_by, labels=None,
|
|||
'--name',
|
||||
r.unique_container_name(container_name)
|
||||
]
|
||||
builder.docker_run_args(cmd, container_name)
|
||||
builder.container_run_args(cmd, container_name)
|
||||
return r.execute_interactive(cmd)
|
||||
elif action == 'dump-yaml':
|
||||
print(yaml.safe_dump(config, default_flow_style=False))
|
||||
|
|
|
@ -19,9 +19,9 @@ import tenacity
|
|||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ComposeV1Builder(object):
|
||||
class BaseBuilder(object):
|
||||
|
||||
def __init__(self, config_id, config, runner, labels=None):
|
||||
def __init__(self, config_id, config, runner, labels):
|
||||
self.config_id = config_id
|
||||
self.config = config
|
||||
self.labels = labels
|
||||
|
@ -60,7 +60,7 @@ class ComposeV1Builder(object):
|
|||
self.runner.unique_container_name(container)
|
||||
]
|
||||
self.label_arguments(cmd, container)
|
||||
self.docker_run_args(cmd, container)
|
||||
self.container_run_args(cmd, container)
|
||||
elif action == 'exec':
|
||||
cmd = [self.runner.docker_cmd, 'exec']
|
||||
self.docker_exec_args(cmd, container)
|
||||
|
@ -161,116 +161,6 @@ class ComposeV1Builder(object):
|
|||
if v:
|
||||
cmd.append('%s=%s' % (arg, v))
|
||||
|
||||
def docker_run_args(self, cmd, container):
|
||||
cconfig = self.config[container]
|
||||
if cconfig.get('detach', True):
|
||||
cmd.append('--detach=true')
|
||||
self.list_or_string_arg(cconfig, cmd, 'env_file', '--env-file')
|
||||
# TODO(sbaker): support the dict layout for this property
|
||||
for v in cconfig.get('environment', []):
|
||||
if v:
|
||||
cmd.append('--env=%s' % v)
|
||||
self.boolean_arg(cconfig, cmd, 'remove', '--rm')
|
||||
self.boolean_arg(cconfig, cmd, 'interactive', '--interactive')
|
||||
self.boolean_arg(cconfig, cmd, 'tty', '--tty')
|
||||
self.string_arg(cconfig, cmd, 'net', '--net')
|
||||
self.string_arg(cconfig, cmd, 'ipc', '--ipc')
|
||||
self.string_arg(cconfig, cmd, 'pid', '--pid')
|
||||
self.string_arg(cconfig, cmd, 'uts', '--uts')
|
||||
# TODO(sbaker): implement ulimits property, deprecate this ulimit
|
||||
# property
|
||||
for u in cconfig.get('ulimit', []):
|
||||
if u:
|
||||
cmd.append('--ulimit=%s' % u)
|
||||
if 'healthcheck' in cconfig:
|
||||
hconfig = cconfig['healthcheck']
|
||||
if 'test' in hconfig:
|
||||
cmd.append('--health-cmd=%s' % hconfig['test'])
|
||||
if 'interval' in hconfig:
|
||||
cmd.append('--health-interval=%s' % hconfig['interval'])
|
||||
if 'timeout' in hconfig:
|
||||
cmd.append('--health-timeout=%s' % hconfig['timeout'])
|
||||
if 'retries' in hconfig:
|
||||
cmd.append('--health-retries=%s' % hconfig['retries'])
|
||||
|
||||
def lower(a):
|
||||
return str(a).lower()
|
||||
|
||||
self.string_arg(cconfig, cmd, 'privileged', '--privileged', lower)
|
||||
self.string_arg(cconfig, cmd, 'restart', '--restart')
|
||||
self.string_arg(cconfig, cmd, 'user', '--user')
|
||||
self.list_arg(cconfig, cmd, 'group_add', '--group-add')
|
||||
self.list_arg(cconfig, cmd, 'volumes', '--volume')
|
||||
self.list_arg(cconfig, cmd, 'volumes_from', '--volumes-from')
|
||||
# TODO(sbaker): deprecate log_tag, implement log_driver, log_opt
|
||||
if 'log_tag' in cconfig:
|
||||
cmd.append('--log-opt=tag=%s' % cconfig['log_tag'])
|
||||
self.string_arg(cconfig, cmd, 'cpu_shares', '--cpu-shares')
|
||||
self.string_arg(cconfig, cmd, 'security_opt', '--security-opt')
|
||||
self.string_arg(cconfig, cmd, 'stop_signal', '--stop-signal')
|
||||
|
||||
def duration(a):
|
||||
if isinstance(a, (int, float)):
|
||||
return a
|
||||
|
||||
# match groups of the format 5h34m56s
|
||||
m = re.match('^'
|
||||
'(([\d\.]+)h)?'
|
||||
'(([\d\.]+)m)?'
|
||||
'(([\d\.]+)s)?'
|
||||
'(([\d\.]+)ms)?'
|
||||
'(([\d\.]+)us)?'
|
||||
'$', a)
|
||||
|
||||
if not m:
|
||||
# fallback to parsing string as a number
|
||||
return float(a)
|
||||
|
||||
n = 0.0
|
||||
if m.group(2):
|
||||
n += 3600 * float(m.group(2))
|
||||
if m.group(4):
|
||||
n += 60 * float(m.group(4))
|
||||
if m.group(6):
|
||||
n += float(m.group(6))
|
||||
if m.group(8):
|
||||
n += float(m.group(8)) / 1000.0
|
||||
if m.group(10):
|
||||
n += float(m.group(10)) / 1000000.0
|
||||
return n
|
||||
|
||||
self.string_arg(cconfig, cmd,
|
||||
'stop_grace_period', '--stop-timeout',
|
||||
duration)
|
||||
|
||||
# TODO(sbaker): add missing compose v1 properties:
|
||||
# cap_add, cap_drop
|
||||
# cgroup_parent
|
||||
# devices
|
||||
# dns, dns_search
|
||||
# entrypoint
|
||||
# expose
|
||||
# extra_hosts
|
||||
# labels
|
||||
# ports
|
||||
# stop_signal
|
||||
# volume_driver
|
||||
# cpu_quota
|
||||
# cpuset
|
||||
# domainname
|
||||
# hostname
|
||||
# mac_address
|
||||
# mem_limit
|
||||
# memswap_limit
|
||||
# mem_swappiness
|
||||
# read_only
|
||||
# shm_size
|
||||
# stdin_open
|
||||
# working_dir
|
||||
|
||||
cmd.append(cconfig.get('image', ''))
|
||||
cmd.extend(self.command_argument(cconfig.get('command')))
|
||||
|
||||
def docker_exec_args(self, cmd, container):
|
||||
cconfig = self.config[container]
|
||||
if 'privileged' in cconfig:
|
||||
|
@ -342,6 +232,123 @@ class ComposeV1Builder(object):
|
|||
return command.split()
|
||||
return command
|
||||
|
||||
def lower(self, a):
|
||||
return str(a).lower()
|
||||
|
||||
def duration(self, a):
|
||||
if isinstance(a, (int, float)):
|
||||
return a
|
||||
|
||||
# match groups of the format 5h34m56s
|
||||
m = re.match('^'
|
||||
'(([\d\.]+)h)?'
|
||||
'(([\d\.]+)m)?'
|
||||
'(([\d\.]+)s)?'
|
||||
'(([\d\.]+)ms)?'
|
||||
'(([\d\.]+)us)?'
|
||||
'$', a)
|
||||
|
||||
if not m:
|
||||
# fallback to parsing string as a number
|
||||
return float(a)
|
||||
|
||||
n = 0.0
|
||||
if m.group(2):
|
||||
n += 3600 * float(m.group(2))
|
||||
if m.group(4):
|
||||
n += 60 * float(m.group(4))
|
||||
if m.group(6):
|
||||
n += float(m.group(6))
|
||||
if m.group(8):
|
||||
n += float(m.group(8)) / 1000.0
|
||||
if m.group(10):
|
||||
n += float(m.group(10)) / 1000000.0
|
||||
return n
|
||||
|
||||
|
||||
class ComposeV1Builder(BaseBuilder):
|
||||
|
||||
def __init__(self, config_id, config, runner, labels=None):
|
||||
super(ComposeV1Builder, self).__init__(config_id, config, runner,
|
||||
labels)
|
||||
|
||||
def container_run_args(self, cmd, container):
|
||||
cconfig = self.config[container]
|
||||
if cconfig.get('detach', True):
|
||||
cmd.append('--detach=true')
|
||||
self.list_or_string_arg(cconfig, cmd, 'env_file', '--env-file')
|
||||
# TODO(sbaker): support the dict layout for this property
|
||||
for v in cconfig.get('environment', []):
|
||||
if v:
|
||||
cmd.append('--env=%s' % v)
|
||||
self.boolean_arg(cconfig, cmd, 'remove', '--rm')
|
||||
self.boolean_arg(cconfig, cmd, 'interactive', '--interactive')
|
||||
self.boolean_arg(cconfig, cmd, 'tty', '--tty')
|
||||
self.string_arg(cconfig, cmd, 'net', '--net')
|
||||
self.string_arg(cconfig, cmd, 'ipc', '--ipc')
|
||||
self.string_arg(cconfig, cmd, 'pid', '--pid')
|
||||
self.string_arg(cconfig, cmd, 'uts', '--uts')
|
||||
# TODO(sbaker): implement ulimits property, deprecate this ulimit
|
||||
# property
|
||||
for u in cconfig.get('ulimit', []):
|
||||
if u:
|
||||
cmd.append('--ulimit=%s' % u)
|
||||
if 'healthcheck' in cconfig:
|
||||
hconfig = cconfig['healthcheck']
|
||||
if 'test' in hconfig:
|
||||
cmd.append('--health-cmd=%s' % hconfig['test'])
|
||||
if 'interval' in hconfig:
|
||||
cmd.append('--health-interval=%s' % hconfig['interval'])
|
||||
if 'timeout' in hconfig:
|
||||
cmd.append('--health-timeout=%s' % hconfig['timeout'])
|
||||
if 'retries' in hconfig:
|
||||
cmd.append('--health-retries=%s' % hconfig['retries'])
|
||||
|
||||
self.string_arg(cconfig, cmd, 'privileged', '--privileged', self.lower)
|
||||
self.string_arg(cconfig, cmd, 'restart', '--restart')
|
||||
self.string_arg(cconfig, cmd, 'user', '--user')
|
||||
self.list_arg(cconfig, cmd, 'group_add', '--group-add')
|
||||
self.list_arg(cconfig, cmd, 'volumes', '--volume')
|
||||
self.list_arg(cconfig, cmd, 'volumes_from', '--volumes-from')
|
||||
# TODO(sbaker): deprecate log_tag, implement log_driver, log_opt
|
||||
if 'log_tag' in cconfig:
|
||||
cmd.append('--log-opt=tag=%s' % cconfig['log_tag'])
|
||||
self.string_arg(cconfig, cmd, 'cpu_shares', '--cpu-shares')
|
||||
self.string_arg(cconfig, cmd, 'security_opt', '--security-opt')
|
||||
self.string_arg(cconfig, cmd, 'stop_signal', '--stop-signal')
|
||||
|
||||
self.string_arg(cconfig, cmd,
|
||||
'stop_grace_period', '--stop-timeout',
|
||||
self.duration)
|
||||
|
||||
# TODO(sbaker): add missing compose v1 properties:
|
||||
# cap_add, cap_drop
|
||||
# cgroup_parent
|
||||
# devices
|
||||
# dns, dns_search
|
||||
# entrypoint
|
||||
# expose
|
||||
# extra_hosts
|
||||
# labels
|
||||
# ports
|
||||
# stop_signal
|
||||
# volume_driver
|
||||
# cpu_quota
|
||||
# cpuset
|
||||
# domainname
|
||||
# hostname
|
||||
# mac_address
|
||||
# mem_limit
|
||||
# memswap_limit
|
||||
# mem_swappiness
|
||||
# read_only
|
||||
# shm_size
|
||||
# stdin_open
|
||||
# working_dir
|
||||
|
||||
cmd.append(cconfig.get('image', ''))
|
||||
cmd.extend(self.command_argument(cconfig.get('command')))
|
||||
|
||||
|
||||
class PullException(Exception):
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ from paunch import runner
|
|||
from paunch.tests import base
|
||||
|
||||
|
||||
class TestComposeV1Builder(base.TestCase):
|
||||
class TestBaseBuilder(base.TestCase):
|
||||
|
||||
def test_apply(self):
|
||||
orig_call = tenacity.wait.wait_random_exponential.__call__
|
||||
|
@ -412,46 +412,6 @@ three-12345678 three''', '', 0),
|
|||
'--label', 'config_data=null'],
|
||||
cmd)
|
||||
|
||||
def test_docker_run_args(self):
|
||||
config = {
|
||||
'one': {
|
||||
'image': 'centos:7',
|
||||
'privileged': True,
|
||||
'user': 'bar',
|
||||
'net': 'host',
|
||||
'ipc': 'host',
|
||||
'pid': 'container:bar',
|
||||
'uts': 'host',
|
||||
'restart': 'always',
|
||||
'healthcheck': {
|
||||
'test': '/bin/true',
|
||||
'interval': '30s',
|
||||
'timeout': '10s',
|
||||
'retries': 3
|
||||
},
|
||||
'env_file': '/tmp/foo.env',
|
||||
'log_tag': '{{.ImageName}}/{{.Name}}/{{.ID}}',
|
||||
'cpu_shares': 600,
|
||||
'security_opt': 'label:disable'
|
||||
}
|
||||
}
|
||||
builder = compose1.ComposeV1Builder('foo', config, None)
|
||||
|
||||
cmd = ['docker', 'run', '--name', 'one']
|
||||
builder.docker_run_args(cmd, 'one')
|
||||
self.assertEqual(
|
||||
['docker', 'run', '--name', 'one',
|
||||
'--detach=true', '--env-file=/tmp/foo.env',
|
||||
'--net=host', '--ipc=host', '--pid=container:bar',
|
||||
'--uts=host', '--health-cmd=/bin/true', '--health-interval=30s',
|
||||
'--health-timeout=10s', '--health-retries=3',
|
||||
'--privileged=true', '--restart=always', '--user=bar',
|
||||
'--log-opt=tag={{.ImageName}}/{{.Name}}/{{.ID}}',
|
||||
'--cpu-shares=600',
|
||||
'--security-opt=label:disable', 'centos:7'],
|
||||
cmd
|
||||
)
|
||||
|
||||
def test_durations(self):
|
||||
config = {
|
||||
'a': {'stop_grace_period': 123},
|
||||
|
@ -480,10 +440,10 @@ three-12345678 three''', '', 0),
|
|||
|
||||
for container, arg in result.items():
|
||||
cmd = []
|
||||
builder.docker_run_args(cmd, container)
|
||||
builder.container_run_args(cmd, container)
|
||||
self.assertIn(arg, cmd)
|
||||
|
||||
def test_docker_run_args_lists(self):
|
||||
def test_container_run_args_lists(self):
|
||||
config = {
|
||||
'one': {
|
||||
'image': 'centos:7',
|
||||
|
@ -503,7 +463,7 @@ three-12345678 three''', '', 0),
|
|||
builder = compose1.ComposeV1Builder('foo', config, None)
|
||||
|
||||
cmd = ['docker', 'run', '--name', 'one']
|
||||
builder.docker_run_args(cmd, 'one')
|
||||
builder.container_run_args(cmd, 'one')
|
||||
self.assertEqual(
|
||||
['docker', 'run', '--name', 'one',
|
||||
'--env-file=/tmp/foo.env', '--env-file=/tmp/bar.env',
|
||||
|
@ -562,3 +522,45 @@ three-12345678 three''', '', 0),
|
|||
['ls', '-l', '"/foo', 'bar"'],
|
||||
b.command_argument('ls -l "/foo bar"')
|
||||
)
|
||||
|
||||
|
||||
class TestComposeV1Builder(TestBaseBuilder):
|
||||
def test_docker_run_args(self):
|
||||
config = {
|
||||
'one': {
|
||||
'image': 'centos:7',
|
||||
'privileged': True,
|
||||
'user': 'bar',
|
||||
'net': 'host',
|
||||
'ipc': 'host',
|
||||
'pid': 'container:bar',
|
||||
'uts': 'host',
|
||||
'restart': 'always',
|
||||
'healthcheck': {
|
||||
'test': '/bin/true',
|
||||
'interval': '30s',
|
||||
'timeout': '10s',
|
||||
'retries': 3
|
||||
},
|
||||
'env_file': '/tmp/foo.env',
|
||||
'log_tag': '{{.ImageName}}/{{.Name}}/{{.ID}}',
|
||||
'cpu_shares': 600,
|
||||
'security_opt': 'label:disable'
|
||||
}
|
||||
}
|
||||
builder = compose1.ComposeV1Builder('foo', config, None)
|
||||
|
||||
cmd = ['docker', 'run', '--name', 'one']
|
||||
builder.container_run_args(cmd, 'one')
|
||||
self.assertEqual(
|
||||
['docker', 'run', '--name', 'one',
|
||||
'--detach=true', '--env-file=/tmp/foo.env',
|
||||
'--net=host', '--ipc=host', '--pid=container:bar',
|
||||
'--uts=host', '--health-cmd=/bin/true', '--health-interval=30s',
|
||||
'--health-timeout=10s', '--health-retries=3',
|
||||
'--privileged=true', '--restart=always', '--user=bar',
|
||||
'--log-opt=tag={{.ImageName}}/{{.Name}}/{{.ID}}',
|
||||
'--cpu-shares=600',
|
||||
'--security-opt=label:disable', 'centos:7'],
|
||||
cmd
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue