Skip to content

Commit

Permalink
If image is already in the registry don't overwrite it
Browse files Browse the repository at this point in the history
  • Loading branch information
eranco74 committed Oct 22, 2017
1 parent 9af56b7 commit 847a14d
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 12 deletions.
32 changes: 25 additions & 7 deletions skipper/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ def build(ctx, images_to_build, container_context):

@cli.command()
@click.option('--namespace', help='Namespace to push into')
@click.option('--force', help="Push image even if it's already in the registry", is_flag=True, default=False)
@click.argument('image')
@click.pass_context
def push(ctx, namespace, image):
def push(ctx, namespace, force, image):
'''
Push a container
'''
Expand All @@ -104,12 +105,20 @@ def push(ctx, namespace, image):
utils.logger.error('Failed to tag image: %(tag)s as fqdn', dict(tag=image_name, fqdn=fqdn_image))
sys.exit(ret)

utils.logger.debug("Pushing to registry %(registry)s", dict(registry=ctx.obj['registry']))
command = ['docker', 'push', fqdn_image]
ret = runner.run(command)
if ret != 0:
utils.logger.error('Failed to push image: %(tag)s', dict(tag=fqdn_image))
sys.exit(ret)
repo_name = utils.generate_fqdn_image(None, namespace, image, tag=None)

images_info = utils.get_remote_images_info([repo_name], ctx.obj['registry'])
tags = [info[-1] for info in images_info]
if tag in tags:
if not force:
utils.logger.info("Image %(image)s is already in registry %(registry)s, not pushing",
dict(image=fqdn_image, registry=ctx.obj['registry']))
else:
utils.logger.warning("Image %(image)s is already in registry %(registry)s, pushing anyway",
dict(image=fqdn_image, registry=ctx.obj['registry']))
_push_to_registry(ctx.obj['registry'], fqdn_image)
else:
_push_to_registry(ctx.obj['registry'], fqdn_image)

utils.logger.debug("Removing tag %(tag)s", dict(tag=fqdn_image))
command = ['docker', 'rmi', fqdn_image]
Expand Down Expand Up @@ -250,6 +259,15 @@ def version():
click.echo(get_distribution("strato-skipper").version) # pylint: disable=no-member


def _push_to_registry(registry, fqdn_image):
utils.logger.debug("Pushing to registry %(registry)s", dict(registry=registry))
command = ['docker', 'push', fqdn_image]
ret = runner.run(command)
if ret != 0:
utils.logger.error('Failed to push image: %(tag)s', dict(tag=fqdn_image))
sys.exit(ret)


def _prepare_build_container(registry, image, tag, git_revision=False, container_context=None):

if tag is not None:
Expand Down
106 changes: 101 additions & 5 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,70 @@ def test_build_with_defaults_from_config_file_including_containers(self, skipper
skipper_runner_run_mock.assert_called_once_with(expected_command)

@mock.patch('skipper.git.get_hash', autospec=True, return_value='1234567')
@mock.patch('requests.get', autospec=True)
@mock.patch('skipper.runner.run', autospec=True)
def test_push(self, skipper_runner_run_mock, requests_get_mock, *args):
skipper_runner_run_mock.side_effect = [0, 0]
push_params = ['my_image']
with mock.patch('requests.Response', autospec=True) as requests_response_class_mock:
requests_response_mock = requests_response_class_mock.return_value
requests_response_mock.json.return_value = {
'name': 'my_image',
'tags': ['latest', 'aaaaaaa', 'bbbbbbb']
}
requests_get_mock.return_value = requests_response_mock

self._invoke_cli(
global_params=self.global_params,
subcmd='push',
subcmd_params=push_params
)
expected_commands = [
mock.call(['docker', 'tag', 'my_image:1234567', 'registry.io:5000/my_image:1234567']),
mock.call(['docker', 'push', 'registry.io:5000/my_image:1234567']),
mock.call(['docker', 'rmi', 'registry.io:5000/my_image:1234567']),
]
skipper_runner_run_mock.assert_has_calls(expected_commands)

@mock.patch('skipper.git.get_hash', autospec=True, return_value='1234567')
@mock.patch('requests.get', autospec=True)
@mock.patch('skipper.runner.run', autospec=True)
def test_push(self, skipper_runner_run_mock, *args):
def test_push_already_in_registry(self, skipper_runner_run_mock, requests_get_mock, *args):
skipper_runner_run_mock.side_effect = [0, 0]
push_params = ['my_image']
with mock.patch('requests.Response', autospec=True) as requests_response_class_mock:
requests_response_mock = requests_response_class_mock.return_value
requests_response_mock.json.return_value = {
'name': 'my_image',
'tags': ['latest', 'aaaaaaa', 'bbbbbbb', "1234567"]
}
requests_get_mock.return_value = requests_response_mock

self._invoke_cli(
global_params=self.global_params,
subcmd='push',
subcmd_params=push_params
)
expected_commands = [
mock.call(['docker', 'tag', 'my_image:1234567', 'registry.io:5000/my_image:1234567']),
mock.call(['docker', 'rmi', 'registry.io:5000/my_image:1234567']),
]
skipper_runner_run_mock.assert_has_calls(expected_commands)

@mock.patch('skipper.git.get_hash', autospec=True, return_value='1234567')
@mock.patch('requests.get', autospec=True)
@mock.patch('skipper.runner.run', autospec=True)
def test_push_already_in_registry_with_force(self, skipper_runner_run_mock, requests_get_mock, *args):
skipper_runner_run_mock.side_effect = [0, 0]
push_params = ['my_image', "--force"]
with mock.patch('requests.Response', autospec=True) as requests_response_class_mock:
requests_response_mock = requests_response_class_mock.return_value
requests_response_mock.json.return_value = {
'name': 'my_image',
'tags': ['latest', 'aaaaaaa', 'bbbbbbb']
}
requests_get_mock.return_value = requests_response_mock

self._invoke_cli(
global_params=self.global_params,
subcmd='push',
Expand All @@ -420,10 +480,19 @@ def test_push(self, skipper_runner_run_mock, *args):
skipper_runner_run_mock.assert_has_calls(expected_commands)

@mock.patch('skipper.git.get_hash', autospec=True, return_value='1234567')
@mock.patch('requests.get', autospec=True)
@mock.patch('skipper.runner.run', autospec=True)
def test_push_fail(self, skipper_runner_run_mock, *args):
def test_push_fail(self, skipper_runner_run_mock, requests_get_mock, *args):
skipper_runner_run_mock.side_effect = [0, 1]
push_params = ['my_image']
with mock.patch('requests.Response', autospec=True) as requests_response_class_mock:
requests_response_mock = requests_response_class_mock.return_value
requests_response_mock.json.return_value = {
'name': 'my_image',
'tags': ['latest', 'aaaaaaa', 'bbbbbbb']
}
requests_get_mock.return_value = requests_response_mock

result = self._invoke_cli(
global_params=self.global_params,
subcmd='push',
Expand Down Expand Up @@ -453,10 +522,19 @@ def test_push_tag_fail(self, skipper_runner_run_mock, *args):
skipper_runner_run_mock.assert_has_calls(expected_commands)

@mock.patch('skipper.git.get_hash', autospec=True, return_value='1234567')
@mock.patch('requests.get', autospec=True)
@mock.patch('skipper.runner.run', autospec=True)
def test_push_rmi_fail(self, skipper_runner_run_mock, *args):
def test_push_rmi_fail(self, skipper_runner_run_mock, requests_get_mock, *args):
skipper_runner_run_mock.side_effect = [0, 0, 1]
push_params = ['my_image']
with mock.patch('requests.Response', autospec=True) as requests_response_class_mock:
requests_response_mock = requests_response_class_mock.return_value
requests_response_mock.json.return_value = {
'name': 'my_image',
'tags': ['latest', 'aaaaaaa', 'bbbbbbb']
}
requests_get_mock.return_value = requests_response_mock

result = self._invoke_cli(
global_params=self.global_params,
subcmd='push',
Expand All @@ -471,10 +549,19 @@ def test_push_rmi_fail(self, skipper_runner_run_mock, *args):
skipper_runner_run_mock.assert_has_calls(expected_commands)

@mock.patch('skipper.git.get_hash', autospec=True, return_value='1234567')
@mock.patch('requests.get', autospec=True)
@mock.patch('skipper.runner.run', autospec=True)
def test_push_to_namespace(self, skipper_runner_run_mock, *args):
def test_push_to_namespace(self, skipper_runner_run_mock, requests_get_mock, *args):
skipper_runner_run_mock.side_effect = [0, 0]
push_params = ['--namespace', 'my_namespace', 'my_image']
with mock.patch('requests.Response', autospec=True) as requests_response_class_mock:
requests_response_mock = requests_response_class_mock.return_value
requests_response_mock.json.return_value = {
'name': 'my_image',
'tags': ['latest', 'aaaaaaa', 'bbbbbbb']
}
requests_get_mock.return_value = requests_response_mock

self._invoke_cli(
global_params=self.global_params,
subcmd='push',
Expand All @@ -491,10 +578,19 @@ def test_push_to_namespace(self, skipper_runner_run_mock, *args):
@mock.patch('os.path.exists', autospec=True, return_value=True)
@mock.patch('yaml.load', autospec=True, return_value=SKIPPER_CONF)
@mock.patch('skipper.git.get_hash', autospec=True, return_value='1234567')
@mock.patch('requests.get', autospec=True)
@mock.patch('skipper.runner.run', autospec=True)
def test_push_with_defaults_from_config_file(self, skipper_runner_run_mock, *args):
def test_push_with_defaults_from_config_file(self, skipper_runner_run_mock, requests_get_mock, *args):
skipper_runner_run_mock.side_effect = [0, 0]
push_params = ['my_image']
with mock.patch('requests.Response', autospec=True) as requests_response_class_mock:
requests_response_mock = requests_response_class_mock.return_value
requests_response_mock.json.return_value = {
'name': 'my_image',
'tags': ['latest', 'aaaaaaa', 'bbbbbbb']
}
requests_get_mock.return_value = requests_response_mock

self._invoke_cli(
defaults=config.load_defaults(),
subcmd='push',
Expand Down

0 comments on commit 847a14d

Please sign in to comment.