Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

Commit

Permalink
Refactor the runner to support more than docker
Browse files Browse the repository at this point in the history
Refactor the runner.py to support more than docker, like we did for
compose1.py.

Change-Id: I9ca29573570b14f204d464598408f316bb71401c
  • Loading branch information
EmilienM committed Aug 27, 2018
1 parent 3c0d4f5 commit c19a46b
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 165 deletions.
136 changes: 71 additions & 65 deletions paunch/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@
LOG = logging.getLogger(__name__)


class DockerRunner(object):

def __init__(self, managed_by, docker_cmd=None):
class BaseRunner(object):
def __init__(self, managed_by, docker_cmd):
self.managed_by = managed_by
self.docker_cmd = docker_cmd or 'docker'
self.docker_cmd = docker_cmd

@staticmethod
def execute(cmd):
Expand Down Expand Up @@ -69,6 +68,74 @@ def containers_in_config(self, conf_id):

return [c for c in cmd_stdout.split()]

def inspect(self, name, format=None, type='container'):
cmd = [self.docker_cmd, 'inspect', '--type', type]
if format:
cmd.append('--format')
cmd.append(format)
cmd.append(name)
(cmd_stdout, cmd_stderr, returncode) = self.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(self, container):
container_name = container
while self.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(self, container, cid):
cmd = [
self.docker_cmd,
'ps',
'-a',
'--filter',
'label=container_name=%s' % container,
'--filter',
'label=config_id=%s' % cid,
'--format',
'{{.Names}}'
]
(cmd_stdout, cmd_stderr, returncode) = self.execute(cmd)
if returncode != 0:
return container
names = cmd_stdout.split()
if names:
return names[0]
return container

def delete_missing_configs(self, config_ids):
if not config_ids:
config_ids = []

for conf_id in self.current_config_ids():
if conf_id not in config_ids:
LOG.debug('%s no longer exists, deleting containers' % conf_id)
self.remove_containers(conf_id)

def list_configs(self):
configs = collections.defaultdict(list)
for conf_id in self.current_config_ids():
for container in self.containers_in_config(conf_id):
configs[conf_id].append(self.inspect(container))
return configs


class DockerRunner(BaseRunner):

def __init__(self, managed_by, docker_cmd='docker'):
docker_cmd = docker_cmd or 'docker'
super(DockerRunner, self).__init__(managed_by, docker_cmd)

def remove_containers(self, conf_id):
for container in self.containers_in_config(conf_id):
self.remove_container(container)
Expand Down Expand Up @@ -131,64 +198,3 @@ def rename_container(self, container, name):
if returncode != 0:
LOG.error('Error renaming container: %s' % container)
LOG.error(cmd_stderr)

def inspect(self, name, format=None, type='container'):
cmd = [self.docker_cmd, 'inspect', '--type', type]
if format:
cmd.append('--format')
cmd.append(format)
cmd.append(name)
(cmd_stdout, cmd_stderr, returncode) = self.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(self, container):
container_name = container
while self.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(self, container, cid):
cmd = [
self.docker_cmd,
'ps',
'-a',
'--filter',
'label=container_name=%s' % container,
'--filter',
'label=config_id=%s' % cid,
'--format',
'{{.Names}}'
]
(cmd_stdout, cmd_stderr, returncode) = self.execute(cmd)
if returncode != 0:
return container
names = cmd_stdout.split()
if names:
return names[0]
return container

def delete_missing_configs(self, config_ids):
if not config_ids:
config_ids = []

for conf_id in self.current_config_ids():
if conf_id not in config_ids:
LOG.debug('%s no longer exists, deleting containers' % conf_id)
self.remove_containers(conf_id)

def list_configs(self):
configs = collections.defaultdict(list)
for conf_id in self.current_config_ids():
for container in self.containers_in_config(conf_id):
configs[conf_id].append(self.inspect(container))
return configs
202 changes: 102 additions & 100 deletions paunch/tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@
from paunch.tests import base


class TestDockerRunner(base.TestCase):

class TestBaseRunner(base.TestCase):
def setUp(self):
super(TestDockerRunner, self).setUp()
super(TestBaseRunner, self).setUp()
self.runner = runner.DockerRunner('tester')

def mock_execute(self, popen, stdout, stderr, returncode):
Expand Down Expand Up @@ -73,103 +72,6 @@ def test_containers_in_config(self, popen):
)
self.assertEqual(['one', 'two', 'three'], result)

@mock.patch('subprocess.Popen')
def test_remove_containers(self, popen):
self.mock_execute(popen, 'one\ntwo\nthree', '', 0)
self.runner.remove_container = mock.Mock()

self.runner.remove_containers('foo')

self.assert_execute(
popen, ['docker', 'ps', '-q', '-a',
'--filter', 'label=managed_by=tester',
'--filter', 'label=config_id=foo']
)
self.runner.remove_container.assert_has_calls([
mock.call('one'), mock.call('two'), mock.call('three')
])

@mock.patch('subprocess.Popen')
def test_remove_container(self, popen):
self.mock_execute(popen, '', '', 0)

self.runner.remove_container('one')
self.assert_execute(
popen, ['docker', 'rm', '-f', 'one']
)

@mock.patch('subprocess.Popen')
def test_container_names(self, popen):
ps_result = '''one one
two-12345678 two
two two
three-12345678 three
four-12345678 four
'''

self.mock_execute(popen, ps_result, '', 0)

names = list(self.runner.container_names())

self.assert_execute(
popen, ['docker', 'ps', '-a',
'--filter', 'label=managed_by=tester',
'--format', '{{.Names}} {{.Label "container_name"}}']
)
self.assertEqual([
['one', 'one'],
['two-12345678', 'two'],
['two', 'two'],
['three-12345678', 'three'],
['four-12345678', 'four']
], names)

@mock.patch('subprocess.Popen')
def test_container_names_by_conf_id(self, popen):
ps_result = '''one one
two-12345678 two
'''

self.mock_execute(popen, ps_result, '', 0)

names = list(self.runner.container_names('abc'))

self.assert_execute(
popen, ['docker', 'ps', '-a',
'--filter', 'label=managed_by=tester',
'--filter', 'label=config_id=abc',
'--format', '{{.Names}} {{.Label "container_name"}}']
)
self.assertEqual([
['one', 'one'],
['two-12345678', 'two']
], names)

@mock.patch('subprocess.Popen')
def test_rename_containers(self, popen):
ps_result = '''one one
two-12345678 two
two two
three-12345678 three
four-12345678 four
'''

self.mock_execute(popen, ps_result, '', 0)
self.runner.rename_container = mock.Mock()

self.runner.rename_containers()

self.assert_execute(
popen, ['docker', 'ps', '-a',
'--filter', 'label=managed_by=tester',
'--format', '{{.Names}} {{.Label "container_name"}}']
)
# only containers three-12345678 and four-12345678 four will be renamed
self.runner.rename_container.assert_has_calls([
mock.call('three-12345678', 'three'),
mock.call('four-12345678', 'four')
], any_order=True)

@mock.patch('subprocess.Popen')
def test_inspect(self, popen):
self.mock_execute(popen, '[{"foo": "bar"}]', '', 0)
Expand Down Expand Up @@ -284,3 +186,103 @@ def test_list_configs(self, popen):
'two': [{'e': 'f'}, {'e': 'f'}, {'e': 'f'}],
'three': [{'e': 'f'}, {'e': 'f'}, {'e': 'f'}]
}, result)


class TestDockerRunner(TestBaseRunner):

@mock.patch('subprocess.Popen')
def test_remove_containers(self, popen):
self.mock_execute(popen, 'one\ntwo\nthree', '', 0)
self.runner.remove_container = mock.Mock()

self.runner.remove_containers('foo')

self.assert_execute(
popen, ['docker', 'ps', '-q', '-a',
'--filter', 'label=managed_by=tester',
'--filter', 'label=config_id=foo']
)
self.runner.remove_container.assert_has_calls([
mock.call('one'), mock.call('two'), mock.call('three')
])

@mock.patch('subprocess.Popen')
def test_remove_container(self, popen):
self.mock_execute(popen, '', '', 0)

self.runner.remove_container('one')
self.assert_execute(
popen, ['docker', 'rm', '-f', 'one']
)

@mock.patch('subprocess.Popen')
def test_container_names(self, popen):
ps_result = '''one one
two-12345678 two
two two
three-12345678 three
four-12345678 four
'''

self.mock_execute(popen, ps_result, '', 0)

names = list(self.runner.container_names())

self.assert_execute(
popen, ['docker', 'ps', '-a',
'--filter', 'label=managed_by=tester',
'--format', '{{.Names}} {{.Label "container_name"}}']
)
self.assertEqual([
['one', 'one'],
['two-12345678', 'two'],
['two', 'two'],
['three-12345678', 'three'],
['four-12345678', 'four']
], names)

@mock.patch('subprocess.Popen')
def test_container_names_by_conf_id(self, popen):
ps_result = '''one one
two-12345678 two
'''

self.mock_execute(popen, ps_result, '', 0)

names = list(self.runner.container_names('abc'))

self.assert_execute(
popen, ['docker', 'ps', '-a',
'--filter', 'label=managed_by=tester',
'--filter', 'label=config_id=abc',
'--format', '{{.Names}} {{.Label "container_name"}}']
)
self.assertEqual([
['one', 'one'],
['two-12345678', 'two']
], names)

@mock.patch('subprocess.Popen')
def test_rename_containers(self, popen):
ps_result = '''one one
two-12345678 two
two two
three-12345678 three
four-12345678 four
'''

self.mock_execute(popen, ps_result, '', 0)
self.runner.rename_container = mock.Mock()

self.runner.rename_containers()

self.assert_execute(
popen, ['docker', 'ps', '-a',
'--filter', 'label=managed_by=tester',
'--format', '{{.Names}} {{.Label "container_name"}}']
)
# only containers three-12345678 and four-12345678 four will be renamed
self.runner.rename_container.assert_has_calls([
mock.call('three-12345678', 'three'),
mock.call('four-12345678', 'four')
], any_order=True)

0 comments on commit c19a46b

Please sign in to comment.