From 491181ec3181c14175450192d8e5cf0126550278 Mon Sep 17 00:00:00 2001 From: Tyler Rivera Date: Wed, 22 Oct 2014 10:57:43 -0400 Subject: [PATCH] Allow dependent image pull from insecure registry PR #490 Provides the ability to pull from an insecure registry by passing --allow-insecure-ssl. This commit extends the work done in #490 and adds the ability to pass --allow-insecure-ssl to the up and run commands which will attempt to pull dependent images if they do not exist. Signed-off-by: Tyler Rivera --- fig/cli/main.py | 43 ++++++++++++++++++++++++-------------- fig/project.py | 4 ++-- fig/service.py | 12 +++++++---- tests/unit/service_test.py | 17 +++++++++++++++ 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/fig/cli/main.py b/fig/cli/main.py index 9a47771f4..e00e0a6ef 100644 --- a/fig/cli/main.py +++ b/fig/cli/main.py @@ -264,17 +264,21 @@ class TopLevelCommand(Command): Usage: run [options] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...] Options: - -d Detached mode: Run container in the background, print - new container name. - --entrypoint CMD Override the entrypoint of the image. - -e KEY=VAL Set an environment variable (can be used multiple times) - --no-deps Don't start linked services. - --rm Remove container after run. Ignored in detached mode. - -T Disable pseudo-tty allocation. By default `fig run` - allocates a TTY. + --allow-insecure-ssl Allow insecure connections to the docker + registry + -d Detached mode: Run container in the background, print + new container name. + --entrypoint CMD Override the entrypoint of the image. + -e KEY=VAL Set an environment variable (can be used multiple times) + --no-deps Don't start linked services. + --rm Remove container after run. Ignored in detached mode. + -T Disable pseudo-tty allocation. By default `fig run` + allocates a TTY. """ service = project.get_service(options['SERVICE']) + insecure_registry = options['--allow-insecure-ssl'] + if not options['--no-deps']: deps = service.get_linked_names() @@ -309,8 +313,11 @@ class TopLevelCommand(Command): if options['--entrypoint']: container_options['entrypoint'] = options.get('--entrypoint') - - container = service.create_container(one_off=True, **container_options) + container = service.create_container( + one_off=True, + insecure_registry=insecure_registry, + **container_options + ) if options['-d']: service.start_container(container, ports=None, one_off=True) print(container.name) @@ -396,12 +403,15 @@ class TopLevelCommand(Command): Usage: up [options] [SERVICE...] Options: - -d Detached mode: Run containers in the background, - print new container names. - --no-color Produce monochrome output. - --no-deps Don't start linked services. - --no-recreate If containers already exist, don't recreate them. + --allow-insecure-ssl Allow insecure connections to the docker + registry + -d Detached mode: Run containers in the background, + print new container names. + --no-color Produce monochrome output. + --no-deps Don't start linked services. + --no-recreate If containers already exist, don't recreate them. """ + insecure_registry = options['--allow-insecure-ssl'] detached = options['-d'] monochrome = options['--no-color'] @@ -413,7 +423,8 @@ class TopLevelCommand(Command): project.up( service_names=service_names, start_links=start_links, - recreate=recreate + recreate=recreate, + insecure_registry=insecure_registry, ) to_attach = [c for s in project.get_services(service_names) for c in s.containers()] diff --git a/fig/project.py b/fig/project.py index 38b9f46fc..b30513dd9 100644 --- a/fig/project.py +++ b/fig/project.py @@ -167,7 +167,7 @@ class Project(object): else: log.info('%s uses an image, skipping' % service.name) - def up(self, service_names=None, start_links=True, recreate=True): + def up(self, service_names=None, start_links=True, recreate=True, insecure_registry=False): running_containers = [] for service in self.get_services(service_names, include_links=start_links): @@ -175,7 +175,7 @@ class Project(object): for (_, container) in service.recreate_containers(): running_containers.append(container) else: - for container in service.start_or_create_containers(): + for container in service.start_or_create_containers(insecure_registry=insecure_registry): running_containers.append(container) return running_containers diff --git a/fig/service.py b/fig/service.py index 6c635dee0..558ada2b2 100644 --- a/fig/service.py +++ b/fig/service.py @@ -168,7 +168,7 @@ class Service(object): log.info("Removing %s..." % c.name) c.remove(**options) - def create_container(self, one_off=False, **override_options): + def create_container(self, one_off=False, insecure_registry=False, **override_options): """ Create a container for this service. If the image doesn't exist, attempt to pull it. @@ -179,7 +179,11 @@ class Service(object): except APIError as e: if e.response.status_code == 404 and e.explanation and 'No such image' in str(e.explanation): log.info('Pulling image %s...' % container_options['image']) - output = self.client.pull(container_options['image'], stream=True) + output = self.client.pull( + container_options['image'], + stream=True, + insecure_registry=insecure_registry + ) stream_output(output, sys.stdout) return Container.create(self.client, **container_options) raise @@ -270,12 +274,12 @@ class Service(object): ) return container - def start_or_create_containers(self): + def start_or_create_containers(self, insecure_registry=False): containers = self.containers(stopped=True) if not containers: log.info("Creating %s..." % self._next_container_name(containers)) - new_container = self.create_container() + new_container = self.create_container(insecure_registry=insecure_registry) return [self.start_container(new_container)] else: return [self.start_container_if_stopped(c) for c in containers] diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index bfe53a94e..f1d1c79d9 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -6,6 +6,7 @@ from .. import unittest import mock import docker +from requests import Response from fig import Service from fig.container import Container @@ -14,6 +15,7 @@ from fig.service import ( split_port, parse_volume_spec, build_volume_binding, + APIError, ) @@ -174,6 +176,21 @@ class ServiceTest(unittest.TestCase): self.mock_client.pull.assert_called_once_with('someimage:sometag', insecure_registry=True) mock_log.info.assert_called_once_with('Pulling foo (someimage:sometag)...') + @mock.patch('fig.service.log', autospec=True) + def test_create_container_from_insecure_registry(self, mock_log): + service = Service('foo', client=self.mock_client, image='someimage:sometag') + mock_response = mock.Mock(Response) + mock_response.status_code = 404 + mock_response.reason = "Not Found" + Container.create = mock.Mock() + Container.create.side_effect = APIError('Mock error', mock_response, "No such image") + try: + service.create_container(insecure_registry=True) + except APIError: # We expect the APIError because our service requires a non-existent image. + pass + self.mock_client.pull.assert_called_once_with('someimage:sometag', insecure_registry=True, stream=True) + mock_log.info.assert_called_once_with('Pulling image someimage:sometag...') + class ServiceVolumesTest(unittest.TestCase):