Trigger container update on image id update
If you reuse a container tag for a new image, paunch does not currently rebuild the container to get the new image. This change adds logic to the updated check to also check the image id defined in the container instance against the image id for the image to see if the container image has changed. Closes-Bug: #1895974 Change-Id: I2710497282eb4d6fa8f68dc6d50044c1ce68eb35
This commit is contained in:
parent
6ad9b29f26
commit
4ae7d886db
|
@ -220,9 +220,22 @@ class BaseBuilder(object):
|
|||
list(itertools.chain.from_iterable(container_names))):
|
||||
return False
|
||||
|
||||
ex_data_str = self.runner.inspect(
|
||||
container, '{{index .Config.Labels "config_data"}}')
|
||||
if not ex_data_str:
|
||||
# fetch container inspect info
|
||||
inspect_info = self.runner.inspect(container)
|
||||
if not inspect_info:
|
||||
# we shouldn't get here but you never know
|
||||
self.log.debug("Deleting container (no inspect data): "
|
||||
"%s" % container)
|
||||
self.runner.remove_container(container)
|
||||
return True
|
||||
container_config = inspect_info.get('Config', {})
|
||||
config_data = container_config.get('Labels', {}).get('config_data')
|
||||
try:
|
||||
ex_data = yaml.safe_load(str(config_data))
|
||||
except Exception:
|
||||
ex_data = None
|
||||
|
||||
if not ex_data:
|
||||
if self.cleanup:
|
||||
self.log.debug("Deleting container (no_config_data): "
|
||||
"%s" % container)
|
||||
|
@ -232,17 +245,27 @@ class BaseBuilder(object):
|
|||
self.log.debug("Skipping container (cleanup disabled): "
|
||||
"%s" % container)
|
||||
|
||||
try:
|
||||
ex_data = yaml.safe_load(str(ex_data_str))
|
||||
except Exception:
|
||||
ex_data = None
|
||||
|
||||
# check if config_data has changed
|
||||
new_data = self.config[container]
|
||||
if new_data != ex_data:
|
||||
self.log.debug("Deleting container (changed config_data): %s"
|
||||
% container)
|
||||
self.runner.remove_container(container)
|
||||
return True
|
||||
|
||||
# check if the container image has changed (but tag as not)
|
||||
# e.g. if you use :latest, the name doesn't change but the ID does
|
||||
container_image = container_config.get('Image')
|
||||
|
||||
if container_image:
|
||||
image_id_str = self.runner.inspect(
|
||||
container_image, "{{index .Id}}", o_type='image')
|
||||
if str(image_id_str).strip() != inspect_info.get('Image'):
|
||||
self.log.debug("Deleting container (image updated): "
|
||||
"%s" % container)
|
||||
self.runner.remove_container(container)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def label_arguments(self, cmd, container):
|
||||
|
|
|
@ -262,10 +262,14 @@ three-12345678 three''', '', 0),
|
|||
('', '', 0),
|
||||
('', '', 0), # ps for rename one
|
||||
# inspect one
|
||||
('{"start_order": 0, "image": "centos:7"}', '', 0),
|
||||
('[{"Config": {"Labels": {"config_data": '
|
||||
'"{\\\"start_order\\\": 0, \\\"image\\\": \\\"centos:7\\\"}"'
|
||||
'}}}]', '', 0),
|
||||
('Created two-12345678', '', 0),
|
||||
# inspect three
|
||||
('{"start_order": 42, "image": "centos:7"}', '', 0),
|
||||
('[{"Config": {"Labels": {"config_data": '
|
||||
'"{\\\"start_order\\\": 42, \\\"image\\\": \\\"centos:7\\\"}"'
|
||||
'}}}]', '', 0),
|
||||
# stop three, changed config data
|
||||
('', '', 0),
|
||||
# rm three, changed config data
|
||||
|
@ -305,7 +309,6 @@ three-12345678 three''', '', 0),
|
|||
mock.ANY),
|
||||
# check the renamed one, config hasn't changed
|
||||
mock.call(['docker', 'inspect', '--type', 'container',
|
||||
'--format', '{{index .Config.Labels "config_data"}}',
|
||||
'one'], mock.ANY, False, True),
|
||||
# don't run one, its already running
|
||||
# run two
|
||||
|
@ -320,7 +323,6 @@ three-12345678 three''', '', 0),
|
|||
),
|
||||
# rm three, changed config
|
||||
mock.call(['docker', 'inspect', '--type', 'container',
|
||||
'--format', '{{index .Config.Labels "config_data"}}',
|
||||
'three'], mock.ANY, False, True),
|
||||
mock.call(['docker', 'stop', 'three'], mock.ANY),
|
||||
mock.call(['docker', 'rm', 'three'], mock.ANY),
|
||||
|
@ -606,6 +608,194 @@ three-12345678 three''', '', 0),
|
|||
b.command_argument('ls -l "/foo bar"')
|
||||
)
|
||||
|
||||
@mock.patch('paunch.runner.DockerRunner', autospec=True)
|
||||
def test_delete_updated_no_change(self, runner):
|
||||
mock_inspect = mock.MagicMock()
|
||||
mock_inspect.side_effect = [
|
||||
{
|
||||
"Id": ("d038dccebdb0996ed36ab4ff06e7c424b3816d67664aa11e00642"
|
||||
"be5e00cec55"),
|
||||
"Config": {
|
||||
"Labels": {
|
||||
"config_data": """{
|
||||
\"start_order\": 0,
|
||||
\"image": \"centos:7\"
|
||||
}"""
|
||||
},
|
||||
"Image": "127.0.0.1:8787/centos:7"
|
||||
},
|
||||
"Image": "sha256:1"
|
||||
},
|
||||
"sha256:1"
|
||||
]
|
||||
runner.inspect = mock_inspect
|
||||
mock_remove = mock.MagicMock()
|
||||
runner.remove_container = mock_remove
|
||||
|
||||
config = {
|
||||
'one': {
|
||||
'start_order': 0,
|
||||
'image': 'centos:7',
|
||||
}
|
||||
}
|
||||
|
||||
self.builder = compose1.ComposeV1Builder(
|
||||
'one', config, runner.return_value)
|
||||
|
||||
self.builder.runner = runner
|
||||
self.assertFalse(self.builder.delete_updated('one', [['one']]))
|
||||
calls = [
|
||||
mock.call('one'),
|
||||
mock.call('127.0.0.1:8787/centos:7',
|
||||
'{{index .Id}}',
|
||||
o_type='image')
|
||||
]
|
||||
mock_inspect.has_calls(calls)
|
||||
mock_remove.assert_not_called()
|
||||
|
||||
@mock.patch('paunch.runner.DockerRunner', autospec=True)
|
||||
def test_delete_updated_inspect_empty(self, runner):
|
||||
mock_inspect = mock.MagicMock()
|
||||
mock_inspect.return_value = None
|
||||
runner.inspect = mock_inspect
|
||||
mock_remove = mock.MagicMock()
|
||||
runner.remove_container = mock_remove
|
||||
|
||||
config = {
|
||||
'one': {
|
||||
'start_order': 0,
|
||||
'image': 'centos:7',
|
||||
}
|
||||
}
|
||||
|
||||
self.builder = compose1.ComposeV1Builder(
|
||||
'one', config, runner.return_value)
|
||||
|
||||
self.builder.runner = runner
|
||||
self.assertTrue(self.builder.delete_updated('one', [['one']]))
|
||||
mock_inspect.assert_called_once_with('one')
|
||||
mock_remove.assert_called_once_with('one')
|
||||
|
||||
@mock.patch('paunch.runner.DockerRunner', autospec=True)
|
||||
def test_delete_updated_no_config_data(self, runner):
|
||||
mock_inspect = mock.MagicMock()
|
||||
mock_inspect.side_effect = [
|
||||
{
|
||||
"Id": ("d038dccebdb0996ed36ab4ff06e7c424b3816d67664aa11e00642"
|
||||
"be5e00cec55"),
|
||||
"Config": {
|
||||
"Labels": {},
|
||||
"Image": "127.0.0.1:8787/centos:7"
|
||||
},
|
||||
"Image": "sha256:1"
|
||||
},
|
||||
"sha256:1"
|
||||
]
|
||||
runner.inspect = mock_inspect
|
||||
mock_remove = mock.MagicMock()
|
||||
runner.remove_container = mock_remove
|
||||
|
||||
config = {
|
||||
'one': {
|
||||
'start_order': 0,
|
||||
'image': 'centos:7',
|
||||
}
|
||||
}
|
||||
|
||||
self.builder = compose1.ComposeV1Builder(
|
||||
'one', config, runner.return_value)
|
||||
|
||||
self.builder.runner = runner
|
||||
self.assertTrue(self.builder.delete_updated('one', [['one']]))
|
||||
mock_inspect.assert_called_once_with('one')
|
||||
mock_remove.assert_called_once_with('one')
|
||||
|
||||
@mock.patch('paunch.runner.DockerRunner', autospec=True)
|
||||
def test_delete_updated_update_config(self, runner):
|
||||
mock_inspect = mock.MagicMock()
|
||||
mock_inspect.side_effect = [
|
||||
{
|
||||
"Id": ("d038dccebdb0996ed36ab4ff06e7c424b3816d67664aa11e00642"
|
||||
"be5e00cec55"),
|
||||
"Config": {
|
||||
"Labels": {
|
||||
"config_data": """{
|
||||
\"start_order\": 1,
|
||||
\"image": \"centos:7\"
|
||||
}"""
|
||||
},
|
||||
"Image": "127.0.0.1:8787/centos:7"
|
||||
},
|
||||
"Image": "sha256:1"
|
||||
},
|
||||
"sha256:1"
|
||||
]
|
||||
|
||||
runner.inspect = mock_inspect
|
||||
mock_remove = mock.MagicMock()
|
||||
runner.remove_container = mock_remove
|
||||
|
||||
config = {
|
||||
'one': {
|
||||
'start_order': 0,
|
||||
'image': 'centos:7',
|
||||
}
|
||||
}
|
||||
|
||||
self.builder = compose1.ComposeV1Builder(
|
||||
'one', config, runner.return_value)
|
||||
|
||||
self.builder.runner = runner
|
||||
self.assertTrue(self.builder.delete_updated('one', [['one']]))
|
||||
mock_inspect.assert_called_once_with('one')
|
||||
mock_remove.assert_called_once_with('one')
|
||||
|
||||
@mock.patch('paunch.runner.DockerRunner', autospec=True)
|
||||
def test_delete_updated_update_image(self, runner):
|
||||
mock_inspect = mock.MagicMock()
|
||||
mock_inspect.side_effect = [
|
||||
{
|
||||
"Id": ("d038dccebdb0996ed36ab4ff06e7c424b3816d67664aa11e00642"
|
||||
"be5e00cec55"),
|
||||
"Config": {
|
||||
"Labels": {
|
||||
"config_data": """{
|
||||
\"start_order\": 0,
|
||||
\"image": \"centos:7\"
|
||||
}"""
|
||||
},
|
||||
"Image": "127.0.0.1:8787/centos:7"
|
||||
},
|
||||
"Image": "sha256:1"
|
||||
},
|
||||
"sha256:2"
|
||||
]
|
||||
|
||||
runner.inspect = mock_inspect
|
||||
mock_remove = mock.MagicMock()
|
||||
runner.remove_container = mock_remove
|
||||
|
||||
config = {
|
||||
'one': {
|
||||
'start_order': 0,
|
||||
'image': 'centos:7',
|
||||
}
|
||||
}
|
||||
|
||||
self.builder = compose1.ComposeV1Builder(
|
||||
'one', config, runner.return_value)
|
||||
|
||||
self.builder.runner = runner
|
||||
self.assertTrue(self.builder.delete_updated('one', [['one']]))
|
||||
calls = [
|
||||
mock.call('one'),
|
||||
mock.call('127.0.0.1:8787/centos:7',
|
||||
'{{index .Id}}',
|
||||
o_type='image')
|
||||
]
|
||||
mock_inspect.has_calls(calls)
|
||||
mock_remove.assert_called_once_with('one')
|
||||
|
||||
|
||||
class TestVolumeChecks(base.TestCase):
|
||||
@mock.patch('paunch.runner.PodmanRunner', autospec=True)
|
||||
|
|
Loading…
Reference in New Issue