Example #1
0
    def __init__(self, source, image, **kwargs):
        """
        """
        LastLogger.__init__(self)
        BuilderStateMachine.__init__(self)

        print_version_of_tools()

        self.tasker = DockerTasker()

        info, version = self.tasker.get_info(), self.tasker.get_version()
        logger.debug(json.dumps(info, indent=2))
        logger.info(json.dumps(version, indent=2))

        # arguments for build
        self.source = source
        self.base_image_id = None
        self.image_id = None
        self.built_image_info = None
        self.image = ImageName.parse(image)

        # get info about base image from dockerfile
        self.df_path, self.df_dir = self.source.get_dockerfile_path()
        self.base_image = ImageName.parse(DockerfileParser(self.df_path).baseimage)
        logger.debug("base image specified in dockerfile = '%s'", self.base_image)
        if not self.base_image.tag:
            self.base_image.tag = 'latest'
def test_pull_parent_images(organization, reactor_config_map, inspect_only):
    builder_image = 'builder:image'
    parent_images = {BASE_IMAGE_NAME.copy(): None, ImageName.parse(builder_image): None}

    enclosed_base_image = BASE_IMAGE_W_REGISTRY
    enclosed_builder_image = LOCALHOST_REGISTRY + '/' + builder_image
    if organization and reactor_config_map:
        base_image_name = ImageName.parse(enclosed_base_image)
        base_image_name.enclose(organization)
        enclosed_base_image = base_image_name.to_str()
        builder_image_name = ImageName.parse(enclosed_builder_image)
        builder_image_name.enclose(organization)
        enclosed_builder_image = builder_image_name.to_str()

    test_pull_base_image_plugin(
        LOCALHOST_REGISTRY, BASE_IMAGE,
        [   # expected to pull
            enclosed_base_image,
            enclosed_builder_image,
        ],
        [],  # should not be pulled
        reactor_config_map=reactor_config_map,
        inspect_only=inspect_only,
        parent_images=parent_images,
        organization=organization)
Example #3
0
def test_hostdocker_build(caplog, source_params):
    if MOCK:
        mock_docker()

    image_name = ImageName(repo="atomic-reactor-test-ssh-image")
    remote_image = image_name.copy()
    remote_image.registry = LOCALHOST_REGISTRY
    m = DockerhostBuildManager("buildroot-dh-fedora", {
        "source": source_params,
        "image": remote_image.to_str(),
        "parent_registry": LOCALHOST_REGISTRY,  # faster
        "target_registries_insecure": True,
        "parent_registry_insecure": True,
    })
    results = m.build()
    dt = DockerTasker()
    dt.pull_image(remote_image, insecure=True)

    if source_params['provider'] == 'path':
        assert_source_from_path_mounted_ok(caplog, m.temp_dir)

    assert len(results.build_logs) > 0
    #assert re.search(r'build json mounted in container .+"uri": %s' %
    #        os.path.join(dconstants.CONTAINER_SHARE_PATH, 'source'))
    # assert isinstance(results.built_img_inspect, dict)
    # assert len(results.built_img_inspect.items()) > 0
    # assert isinstance(results.built_img_info, dict)
    # assert len(results.built_img_info.items()) > 0
    # assert isinstance(results.base_img_info, dict)
    # assert len(results.base_img_info.items()) > 0
    # assert len(results.base_plugins_output) > 0
    # assert len(results.built_img_plugins_output) > 0
    dt.remove_container(results.container_id)
    dt.remove_image(remote_image)
def test_update_base_image(organization, tmpdir, reactor_config_map, docker_tasker):
    df_content = dedent("""\
        FROM {}
        LABEL horses=coconuts
        CMD whoah
    """)
    dfp = df_parser(str(tmpdir))
    image_str = "base:image"
    dfp.content = df_content.format(image_str)
    base_str = "base@sha256:1234"
    base_image_name = ImageName.parse("base@sha256:1234")

    enclosed_parent = ImageName.parse(image_str)
    if organization and reactor_config_map:
        enclosed_parent.enclose(organization)

    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.parent_images = {enclosed_parent: base_image_name}
    workflow.builder.base_image = base_image_name
    workflow.builder.set_parent_inspection_data(base_str, dict(Id=base_str))
    workflow.builder.tasker.inspect_image = lambda *_: dict(Id=base_str)

    run_plugin(workflow, reactor_config_map, docker_tasker, organization=organization)
    expected_df = df_content.format(base_str)
    assert dfp.content == expected_df
def test_privileged_build(caplog, source_params):
    if MOCK:
        mock_docker()

    image_name = ImageName(repo=TEST_IMAGE)
    remote_image = image_name.copy()
    remote_image.registry = LOCALHOST_REGISTRY
    m = PrivilegedBuildManager(
        "buildroot-fedora",
        {
            "source": source_params,
            "image": remote_image.to_str(),
            "parent_registry": LOCALHOST_REGISTRY,  # faster
            "target_registries_insecure": True,
            "parent_registry_insecure": True,
        },
    )
    results = m.build()
    dt = DockerTasker()
    dt.pull_image(remote_image, insecure=True)

    if source_params["provider"] == "path":
        assert_source_from_path_mounted_ok(caplog, m.temp_dir)

    assert len(results.build_logs) > 0
    # assert isinstance(results.built_img_inspect, dict)
    # assert len(results.built_img_inspect.items()) > 0
    # assert isinstance(results.built_img_info, dict)
    # assert len(results.built_img_info.items()) > 0
    # assert isinstance(results.base_img_info, dict)
    # assert len(results.base_img_info.items()) > 0
    # assert len(results.base_plugins_output) > 0
    # assert len(results.built_img_plugins_output) > 0
    dt.remove_container(results.container_id)
    dt.remove_image(remote_image)
Example #6
0
def test_get_primary_images(tag_conf, tag_annotation, expected):
    template_image = ImageName.parse('registry.example.com/fedora')
    workflow = DockerBuildWorkflow(MOCK_SOURCE, 'test-image')

    for tag in tag_conf:
        image_name = ImageName.parse(str(template_image))
        image_name.tag = tag
        workflow.tag_conf.add_primary_image(str(image_name))

    annotations = {}
    for tag in tag_annotation:
        annotations.setdefault('repositories', {}).setdefault('primary', [])
        image_name = ImageName.parse(str(template_image))
        image_name.tag = tag

        annotations['repositories']['primary'].append(str(image_name))

    build_result = BuildResult(annotations=annotations, image_id='foo')
    workflow.build_result = build_result

    actual = get_primary_images(workflow)
    assert len(actual) == len(expected)
    for index, primary_image in enumerate(actual):
        assert primary_image.registry == template_image.registry
        assert primary_image.namespace == template_image.namespace
        assert primary_image.repo == template_image.repo

        assert primary_image.tag == expected[index]
def test_image_download(tmpdir, docker_tasker, parents, skip_plugin, architecture,
                        architectures, download_filesystem, reactor_config_map, caplog):
    if MOCK:
        mock_docker()

    workflow = mock_workflow(tmpdir)
    if not skip_plugin:
        mock_koji_session(download_filesystem=download_filesystem)
    mock_image_build_file(str(tmpdir))

    workflow.builder.base_image = ImageName.parse(parents[-1])
    workflow.builder.parents_ordered = parents
    workflow.builder.custom_parent_image = 'koji/image-build' in parents
    workflow.builder.custom_base_image = 'koji/image-build' == parents[-1]
    workflow.builder.parent_images = {}
    for image in parents:
        if image == 'scratch':
            continue
        workflow.builder.parent_images[ImageName.parse(image)] = None

    if architectures:
        workflow.prebuild_results[PLUGIN_CHECK_AND_SET_PLATFORMS_KEY] = set(architectures)

    if reactor_config_map:
        make_and_store_reactor_config_map(workflow, {'root_url': '', 'auth': {}})

    runner = PreBuildPluginsRunner(
        docker_tasker,
        workflow,
        [{
            'name': PLUGIN_ADD_FILESYSTEM_KEY,
            'args': {
                'koji_hub': KOJI_HUB,
                'architecture': architecture,
            }
        }]
    )

    results = runner.run()
    plugin_result = results[PLUGIN_ADD_FILESYSTEM_KEY]

    if skip_plugin:
        message = 'Nothing to do for non-custom base images'
        assert message in caplog.text
        assert plugin_result is None
        return

    assert 'base-image-id' in plugin_result
    assert 'filesystem-koji-task-id' in plugin_result

    if download_filesystem:
        assert plugin_result['base-image-id'] == IMPORTED_IMAGE_ID
        assert plugin_result['filesystem-koji-task-id'] == FILESYSTEM_TASK_ID
    else:
        assert plugin_result['base-image-id'] is None
        assert plugin_result['filesystem-koji-task-id'] is None
def test_try_with_library_pull_base_image(library, reactor_config_map):
    if MOCK:
        mock_docker(remember_images=True)

    tasker = DockerTasker(retry_times=0)
    workflow = DockerBuildWorkflow(MOCK_SOURCE, 'test-image')
    workflow.builder = MockBuilder()

    if library:
        base_image = 'library/parent-image'
    else:
        base_image = 'parent-image'
    workflow.builder.base_image = ImageName.parse(base_image)
    workflow.builder.parent_images = {ImageName.parse(base_image): None}

    class MockResponse(object):
        content = ''

    cr = CommandResult()
    cr._error = "cmd_error"
    cr._error_detail = {"message": "error_detail"}

    if library:
        call_wait = 1
    else:
        call_wait = 2

    (flexmock(atomic_reactor.util)
        .should_receive('wait_for_command')
        .times(call_wait)
        .and_return(cr))

    error_message = 'registry.example.com/' + base_image

    if reactor_config_map:
        workflow.plugin_workspace[ReactorConfigPlugin.key] = {}
        workflow.plugin_workspace[ReactorConfigPlugin.key][WORKSPACE_CONF_KEY] =\
            ReactorConfig({'version': 1,
                           'source_registry': {'url': 'registry.example.com',
                                               'insecure': True}})

    runner = PreBuildPluginsRunner(
        tasker,
        workflow,
        [{
            'name': PullBaseImagePlugin.key,
            'args': {'parent_registry': 'registry.example.com',
                     'parent_registry_insecure': True},
        }],
    )

    with pytest.raises(PluginFailedException) as exc:
        runner.run()

    assert error_message in exc.value.args[0]
        def workflow_callback(workflow):
            workflow = self.prepare(workflow)
            release = 'rel1'
            version = 'ver1'
            config_blob = {'config': {'Labels': {'release': release, 'version': version}}}
            (flexmock(atomic_reactor.util)
             .should_receive('get_config_from_registry')
             .and_return(config_blob)
             .times(0 if sha_is_manifest_list else 1))

            manifest_list = {
                'manifests': [
                    {'platform': {'architecture': 'amd64'}, 'digest': 'sha256:123456'},
                    {'platform': {'architecture': 'ppc64le'}, 'digest': 'sha256:654321'},
                ]
            }

            manifest_tag = 'registry.example.com' + '/' + BASE_IMAGE_W_SHA
            base_image_result = ImageName.parse(manifest_tag)
            manifest_image = base_image_result.copy()

            if sha_is_manifest_list:
                (flexmock(atomic_reactor.util)
                 .should_receive('get_manifest_list')
                 .with_args(image=manifest_image, registry=manifest_image.registry, insecure=True,
                            dockercfg_path=None)
                 .and_return(flexmock(json=lambda: manifest_list,
                                      content=json.dumps(manifest_list).encode('utf-8')))
                 .once())
            else:
                (flexmock(atomic_reactor.util)
                 .should_receive('get_manifest_list')
                 .with_args(image=manifest_image, registry=manifest_image.registry, insecure=True,
                            dockercfg_path=None)
                 .and_return(None)
                 .once()
                 .ordered())

                docker_tag = "%s-%s" % (version, release)
                manifest_tag = 'registry.example.com' + '/' +\
                               BASE_IMAGE_W_SHA[:BASE_IMAGE_W_SHA.find('@sha256')] +\
                               ':' + docker_tag
                base_image_result = ImageName.parse(manifest_tag)
                manifest_image = base_image_result.copy()
                (flexmock(atomic_reactor.util)
                 .should_receive('get_manifest_list')
                 .with_args(image=manifest_image, registry=manifest_image.registry, insecure=True,
                            dockercfg_path=None)
                 .and_return(flexmock(json=lambda: manifest_list))
                 .once()
                 .ordered())
            return workflow
def test_parent_images_mismatch_base_image(tmpdir, docker_tasker):
    """test when base_image has been updated differently from parent_images."""
    dfp = df_parser(str(tmpdir))
    dfp.content = "FROM base:image"
    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.base_image = ImageName.parse("base:image")
    workflow.builder.parent_images = {
       ImageName.parse("base:image"): ImageName.parse("different-parent-tag")
    }

    with pytest.raises(BaseImageMismatch):
        ChangeFromPlugin(docker_tasker, workflow).run()
 def __init__(self):
     self.tasker = flexmock()
     self.base_image = ImageName(repo='Fedora', tag='22')
     self.original_base_image = ImageName(repo='Fedora', tag='22')
     self.base_from_scratch = False
     self.custom_base_image = False
     self.parent_images = {ImageName.parse('base'): ImageName.parse('base:stubDigest')}
     base_inspect = {INSPECT_CONFIG: {'Labels': BASE_IMAGE_LABELS.copy()}}
     self._parent_images_inspect = {ImageName.parse('base:stubDigest'): base_inspect}
     self.parent_images_digests = {'base:latest': {V2_LIST: 'stubDigest'}}
     self.image_id = 'image_id'
     self.image = 'image'
     self._df_path = 'df_path'
     self.df_dir = 'df_dir'
def test_pull_parent_wrong_registry(reactor_config_map, inspect_only):  # noqa: F811
    parent_images = {
        ImageName.parse("base:image"): None,
        ImageName.parse("some.registry:8888/builder:image"): None}
    with pytest.raises(PluginFailedException) as exc:
        test_pull_base_image_plugin(
            'different.registry:5000', "base:image", [], [],
            reactor_config_map=reactor_config_map,
            inspect_only=inspect_only,
            parent_images=parent_images
        )
    assert "Dockerfile: 'some.registry:8888/builder:image'" in str(exc.value)
    assert "expected registry: 'different.registry:5000'" in str(exc.value)
    assert "base:image" not in str(exc.value)
def _find_image(img, ignore_registry=False):
    global mock_images

    tagged_img = ImageName.parse(img).to_str(explicit_tag=True)
    for im in mock_images:
        im_name = im['RepoTags'][0]
        if im_name == tagged_img:
            return im
        if ignore_registry:
            im_name_wo_reg = ImageName.parse(im_name).to_str(registry=False)
            if im_name_wo_reg == tagged_img:
                return im

    return None
def test_parent_images_unresolved(tmpdir, docker_tasker):
    """test when parent_images hasn't been filled in with unique tags."""
    dfp = df_parser(str(tmpdir))
    dfp.content = "FROM spam"

    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.base_image = ImageName.parse('eggs')
    # we want to fail because some img besides base was not resolved
    workflow.builder.parent_images = {
       ImageName.parse('spam'): ImageName.parse('eggs'),
       ImageName.parse('extra:image'): None
    }

    with pytest.raises(ParentImageUnresolved):
        ChangeFromPlugin(docker_tasker, workflow).run()
    def run(self):
        dockerfile = DockerfileParser(self.workflow.builder.df_path)

        image_name = ImageName.parse(dockerfile.baseimage)
        if image_name.namespace != 'koji' or image_name.repo != 'image-build' :
            self.log.info('Base image not supported: %s', dockerfile.baseimage)
            return
        image_build_conf = image_name.tag or 'image-build.conf'

        self.session = create_koji_session(self.koji_hub, self.koji_auth_info)

        task_id, filesystem_regex = self.build_filesystem(image_build_conf)

        task = TaskWatcher(self.session, task_id, self.poll_interval)
        task.wait()
        if task.failed():
            raise RuntimeError('Create filesystem task failed: {}'
                               .format(task_id))

        filesystem = self.download_filesystem(task_id, filesystem_regex)

        base_image = self.import_base_image(filesystem)
        dockerfile.baseimage = base_image

        return base_image
Example #16
0
    def create_image(self, df_dir_path, image, use_cache=False):
        """
        create image: get atomic-reactor sdist tarball, build image and tag it

        :param df_path:
        :param image:
        :return:
        """
        logger.debug("creating build image: df_dir_path = '%s', image = '%s'", df_dir_path, image)

        if not os.path.isdir(df_dir_path):
            raise RuntimeError("Directory '%s' does not exist.", df_dir_path)

        tmpdir = tempfile.mkdtemp()
        df_tmpdir = os.path.join(tmpdir, 'df-%s' % uuid.uuid4())
        git_tmpdir = os.path.join(tmpdir, 'git-%s' % uuid.uuid4())
        os.mkdir(df_tmpdir)
        logger.debug("tmp dir with dockerfile '%s' created", df_tmpdir)
        os.mkdir(git_tmpdir)
        logger.debug("tmp dir with atomic-reactor '%s' created", git_tmpdir)
        try:
            for f in glob(os.path.join(df_dir_path, '*')):
                shutil.copy(f, df_tmpdir)
                logger.debug("cp '%s' -> '%s'", f, df_tmpdir)
            logger.debug("df dir: %s", os.listdir(df_tmpdir))
            reactor_tarball = self.get_reactor_tarball_path(tmpdir=git_tmpdir)
            reactor_tb_path = os.path.join(df_tmpdir, DOCKERFILE_REACTOR_TARBALL_NAME)
            shutil.copy(reactor_tarball, reactor_tb_path)

            image_name = ImageName.parse(image)
            logs_gen = self.tasker.build_image_from_path(df_tmpdir, image_name, stream=True, use_cache=use_cache)
            wait_for_command(logs_gen)
        finally:
            shutil.rmtree(tmpdir)
    def run(self):
        image_names = self.workflow.tag_conf.images[:]
        # Add in additional image names, if any
        if self.image_names:
            self.log.info("extending image names: %s", self.image_names)
            image_names += [ImageName.parse(x) for x in self.image_names]

        if self.load_exported_image:
            if len(self.workflow.exported_image_sequence) == 0:
                raise RuntimeError('no exported image to push to pulp')
            export_path = self.workflow.exported_image_sequence[-1].get("path")
            top_layer, crane_repos = self.push_tar(export_path, image_names)
        else:
            # Work out image ID
            image = self.workflow.image
            self.log.info("fetching image %s from docker", image)
            with tempfile.NamedTemporaryFile(prefix='docker-image-', suffix='.tar') as image_file:
                image_file.write(self.tasker.d.get_image(image).data)
                # This file will be referenced by its filename, not file
                # descriptor - must ensure contents are written to disk
                image_file.flush()
                top_layer, crane_repos = self.push_tar(image_file.name, image_names)

        if self.publish:
            for image_name in crane_repos:
                self.log.info("image available at %s", str(image_name))

        return top_layer, crane_repos
def mock_environment(tmpdir, primary_images=None,
                     annotations={}):
    if MOCK:
        mock_docker()
    tasker = DockerTasker()
    workflow = DockerBuildWorkflow(SOURCE, "test-image")
    base_image_id = '123456parent-id'
    setattr(workflow, '_base_image_inspect', {'Id': base_image_id})
    setattr(workflow, 'builder', X())
    setattr(workflow.builder, 'image_id', '123456imageid')
    setattr(workflow.builder, 'base_image', ImageName(repo='Fedora', tag='22'))
    setattr(workflow.builder, 'source', X())
    setattr(workflow.builder, 'built_image_info', {'ParentId': base_image_id})
    setattr(workflow.builder.source, 'dockerfile_path', None)
    setattr(workflow.builder.source, 'path', None)
    setattr(workflow, 'tag_conf', TagConf())
    if primary_images:
        for image in primary_images:
            if '-' in ImageName.parse(image).tag:
                workflow.tag_conf.add_primary_image(image)
        workflow.tag_conf.add_unique_image(primary_images[0])

    workflow.build_result = BuildResult(image_id='123456', annotations=annotations)

    return tasker, workflow
Example #19
0
    def tag_image(self, image, target_image, force=False):
        """
        tag provided image with specified image_name, registry and tag

        :param image: str or ImageName, image to tag
        :param target_image: ImageName, new name for the image
        :param force: bool, force tag the image?
        :return: str, image (reg.om/img:v1)
        """
        logger.info("tagging image '%s' as '%s'", image, target_image)
        logger.debug("image = '%s', target_image_name = '%s'", image, target_image)
        if not isinstance(image, ImageName):
            image = ImageName.parse(image)

        if image != target_image:
            response = self.d.tag(
                image.to_str(),
                target_image.to_str(tag=False),
                tag=target_image.tag,
                force=force)  # returns True/False
            if not response:
                logger.error("failed to tag image")
                raise RuntimeError("Failed to tag image '%s': target_image = '%s'" %
                        image.to_str(), target_image)
        else:
            logger.debug('image already tagged correctly, nothing to do')
        return target_image.to_str()  # this will be the proper name, not just repo/img
 def run(self):
     image = self.workflow.builder.image_id
     if not image:
         self.log.error("no built image, nothing to remove")
         return
     try:
         self.tasker.remove_image(image, force=True)
     except APIError as ex:
         if ex.is_client_error():
             self.log.warning("failed to remove built image %s (%s: %s), ignoring",
                              image, ex.response.status_code, ex.response.reason)
         else:
             raise
     if self.remove_base_image and self.workflow.pulled_base_images:
         # FIXME: we may need to add force here, let's try it like this for now
         # FIXME: when ID of pulled img matches an ID of an image already present, don't remove
         for base_image_tag in self.workflow.pulled_base_images:
             try:
                 self.tasker.remove_image(ImageName.parse(base_image_tag))
             except APIError as ex:
                 if ex.is_client_error():
                     self.log.warning("failed to remove base image %s (%s: %s), ignoring",
                                      base_image_tag,
                                      ex.response.status_code,
                                      ex.response.reason)
                 else:
                     raise
Example #21
0
    def build_image_privileged_container(self, build_image, json_args_path):
        """
        Build image inside privileged container: this will run another docker instance inside

        This operation is asynchronous and you should wait for container to finish.

        :param build_image: str, name of image where build is performed
        :param json_args_path: str, this dir is mounted inside build container and used
                               as a way to transport data between host and buildroot; there
                               has to be a file inside this dir with name atomic_reactor.BUILD_JSON which
                               is used to feed build
        :return: dict, keys container_id and stream
        """
        logger.info("building image '%s' inside privileged container", build_image)

        self._check_build_input(build_image, json_args_path)
        self._obtain_source_from_path_if_needed(json_args_path, CONTAINER_SHARE_PATH)

        volume_bindings = {json_args_path: {"bind": CONTAINER_SHARE_PATH}}

        if self._volume_bind_understands_mode():
            volume_bindings[json_args_path]["mode"] = "rw,Z"
        else:
            volume_bindings[json_args_path]["rw"] = True

        logger.debug("build json mounted in container: %s", open(os.path.join(json_args_path, BUILD_JSON)).read())
        container_id = self.tasker.run(
            ImageName.parse(build_image),
            create_kwargs={"volumes": [json_args_path]},
            start_kwargs={"binds": volume_bindings, "privileged": True},
        )

        return container_id
def test_pull_base_image_plugin(df_base, parent_registry, expected_w_reg, expected_wo_reg):
    if MOCK:
        mock_docker(remember_images=True)

    tasker = DockerTasker()
    workflow = DockerBuildWorkflow(MOCK_SOURCE, 'test-image')
    workflow.builder = MockBuilder()
    workflow.builder.base_image = ImageName.parse(df_base)

    assert not tasker.image_exists(BASE_IMAGE)
    assert not tasker.image_exists(BASE_IMAGE_W_REGISTRY)

    runner = PreBuildPluginsRunner(
        tasker,
        workflow,
        [{
            'name': PullBaseImagePlugin.key,
            'args': {'parent_registry': parent_registry, 'parent_registry_insecure': True}
        }]
    )

    runner.run()

    assert tasker.image_exists(BASE_IMAGE) == expected_wo_reg
    assert tasker.image_exists(BASE_IMAGE_W_REGISTRY) == expected_w_reg

    try:
        tasker.remove_image(BASE_IMAGE)
        tasker.remove_image(BASE_IMAGE_W_REGISTRY)
    except:
        pass
def test_retry_pull_base_image(exc, failures, should_succeed):
    if MOCK:
        mock_docker(remember_images=True)

    tasker = DockerTasker()
    workflow = DockerBuildWorkflow(MOCK_SOURCE, 'test-image')
    workflow.builder = MockBuilder()
    workflow.builder.base_image = ImageName.parse('parent-image')

    class MockResponse(object):
        content = ''

    expectation = flexmock(tasker).should_receive('tag_image')
    for _ in range(failures):
        expectation = expectation.and_raise(exc('', MockResponse()))

    expectation.and_return('foo')
    expectation.and_return('parent-image')

    runner = PreBuildPluginsRunner(
        tasker,
        workflow,
        [{
            'name': PullBaseImagePlugin.key,
            'args': {'parent_registry': 'registry.example.com',
                     'parent_registry_insecure': True},
        }],
    )

    if should_succeed:
        runner.run()
    else:
        with pytest.raises(Exception):
            runner.run()
Example #24
0
    def __init__(self, source, image, **kwargs):
        """
        """
        LastLogger.__init__(self)
        BuilderStateMachine.__init__(self)

        print_version_of_tools()

        self.tasker = DockerTasker()

        info, version = self.tasker.get_info(), self.tasker.get_version()
        logger.debug(json.dumps(info, indent=2))
        logger.info(json.dumps(version, indent=2))

        # arguments for build
        self.source = source
        self.base_image = None
        self.image_id = None
        self.built_image_info = None
        self.image = ImageName.parse(image)

        # get info about base image from dockerfile
        build_file_path, build_file_dir = self.source.get_build_file_path()

        self.df_dir = build_file_dir
        self._df_path = None

        # If the build file isn't a Dockerfile, but say, a flatpak.json then a
        # plugin needs to create the Dockerfile and set the base image
        if build_file_path.endswith(DOCKERFILE_FILENAME):
            self.set_df_path(build_file_path)
def test_parent_images_missing(tmpdir, docker_tasker):
    """test when parent_images has been mangled and lacks parents compared to dockerfile."""
    dfp = df_parser(str(tmpdir))
    dfp.content = dedent("""\
        FROM first:parent AS builder1
        FROM second:parent AS builder2
        FROM monty
    """)

    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.parent_images = {ImageName.parse("monty"): ImageName.parse("build-name:3")}
    workflow.builder.base_image = ImageName.parse("build-name:3")

    with pytest.raises(ParentImageMissing):
        ChangeFromPlugin(docker_tasker, workflow).run()
def test_get_image_info_by_name_tag_in_name_library():
    if MOCK:
        mock_docker()

    t = DockerTasker()
    image_name = ImageName.parse("library/busybox")
    response = t.get_image_info_by_image_name(image_name)
    assert len(response) == 1
 def __init__(self):
     self.image_id = "xxx"
     self.base_image = ImageName.parse("koji/image-build")
     self.parent_images = {self.base_image: None}
     self.parents_ordered = "koji/image-build"
     self.custom_base_image = True
     self.custom_parent_image = True
     self.set_base_image = flexmock()
def mock_workflow():
    """
    Provide just enough structure that workflow can be used to run the plugin.
    Defaults below are solely to enable that purpose; tests where those values
    matter should provide their own.
    """

    workflow = DockerBuildWorkflow(SOURCE, "mock:default_built")
    workflow.source = StubSource()
    builder = StubInsideBuilder().for_workflow(workflow)
    builder.set_df_path('/mock-path')
    base_image_name = ImageName.parse("mock:tag")
    builder.parent_images[ImageName.parse("mock:base")] = base_image_name
    builder.base_image = base_image_name
    builder.tasker = flexmock()
    workflow.builder = flexmock(builder)

    return workflow
def test_update_base_image_inspect_broken(tmpdir, caplog, docker_tasker):
    """exercise code branch where the base image inspect comes back without an Id"""
    df_content = "FROM base:image"
    dfp = df_parser(str(tmpdir))
    dfp.content = df_content
    image_str = "base@sha256:1234"
    image_name = ImageName.parse(image_str)

    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.parent_images = {ImageName.parse("base:image"): image_name}
    workflow.builder.base_image = image_name
    workflow.builder.set_parent_inspection_data(image_str, dict(no_id="here"))

    with pytest.raises(NoIdInspection):
        ChangeFromPlugin(docker_tasker, workflow).run()
    assert dfp.content == df_content  # nothing changed
    assert "missing in inspection" in caplog.text
 def set_base_image(self, base_image, parents_pulled=True, insecure=False, dockercfg_path=None):
     self.base_from_scratch = base_image_is_scratch(base_image)
     if not self.custom_base_image:
         self.custom_base_image = base_image_is_custom(base_image)
     self.base_image = ImageName.parse(base_image)
     self.original_base_image = self.original_base_image or self.base_image
     self.recreate_parent_images()
     if not self.base_from_scratch:
         self.parent_images[self.original_base_image] = self.base_image
def test_get_manifest_digests_connection_error(tmpdir):
    # Test that our code to handle falling back from https to http
    # doesn't do anything unexpected when a connection can't be
    # made at all.
    kwargs = {}
    kwargs['image'] = ImageName.parse('example.com/spam:latest')
    kwargs['registry'] = 'https://example.com'

    url = 'https://example.com/v2/spam/manifests/latest'
    responses.add(responses.GET, url, body=ConnectionError())

    with pytest.raises(ConnectionError):
        get_manifest_digests(**kwargs)
Example #32
0
def _find_image(img, ignore_registry=False):
    global mock_images

    for im in mock_images:
        im_name = im['RepoTags'][0]
        if im_name == img:
            return im
        if ignore_registry:
            im_name_wo_reg = ImageName.parse(im_name).to_str(registry=False)
            if im_name_wo_reg == img:
                return im

    return None
Example #33
0
    def __init__(self, failed=False):
        self.tasker = MockDockerTasker()
        self.base_image = ImageName(repo='Fedora', tag='22')
        self.image_id = 'asd'
        self.image = 'image'
        self.failed = failed
        self.df_path = 'some'
        self.df_dir = 'some'

        def simplegen(x, y):
            yield "some\u2018".encode('utf-8')

        flexmock(self.tasker, build_image_from_path=simplegen)
    def __init__(self):
        mock_docker()
        self.tasker = DockerTasker()
        self.base_image = ImageName(repo='fedora', tag='25')
        self.image_id = 'image_id'
        self.image = INPUT_IMAGE
        self.df_path = 'df_path'
        self.df_dir = 'df_dir'

        def simplegen(x, y):
            yield "some\u2018".encode('utf-8')

        flexmock(self.tasker, build_image_from_path=simplegen)
Example #35
0
def test_parent_images(parents_pulled, tmpdir, source_params):
    if MOCK:
        mock_docker()
    s = get_source_instance_for(source_params)
    b = InsideBuilder(s, '')

    orig_base = b.base_image
    if not b.base_from_scratch:
        assert orig_base in b.parent_images
        assert b.parent_images[orig_base] is None
        b.set_base_image("spam:eggs", parents_pulled=parents_pulled)
        assert b.parent_images[orig_base] == ImageName.parse("spam:eggs")
        assert b.parents_pulled == parents_pulled
def test_yuminject_multiline_wrapped_with_chown(tmpdir, docker_tasker):  # noqa
    df_content = """\
FROM fedora
RUN yum install -y --setopt=tsflags=nodocs bind-utils gettext iproute v8314 mongodb24-mongodb mongodb24 && \
    yum clean all && \
    mkdir -p /var/lib/mongodb/data && chown -R mongodb:mongodb /var/lib/mongodb/ && \
    test "$(id mongodb)" = "uid=184(mongodb) gid=998(mongodb) groups=998(mongodb)" && \
    chmod o+w -R /var/lib/mongodb && chmod o+w -R /opt/rh/mongodb24/root/var/lib/mongodb
CMD blabla"""  # noqa
    df = df_parser(str(tmpdir))
    df.content = df_content

    workflow = DockerBuildWorkflow(SOURCE, "test-image")
    setattr(workflow, 'builder', X())

    metalink = r'https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch'  # noqa

    workflow.files[os.path.join(YUM_REPOS_DIR, DEFAULT_YUM_REPOFILE_NAME)] = \
        render_yum_repo(OrderedDict((('name', 'my-repo'),
                                     ('metalink', metalink),
                                     ('enabled', 1),
                                     ('gpgcheck', 0)), ))
    setattr(workflow.builder, 'image_id', "asd123")
    setattr(workflow.builder, 'df_path', df.dockerfile_path)
    setattr(workflow.builder, 'df_dir', str(tmpdir))
    setattr(workflow.builder, 'base_image', ImageName(repo='Fedora', tag='21'))
    setattr(workflow.builder, 'git_dockerfile_path', None)
    setattr(workflow.builder, 'git_path', None)
    setattr(workflow.builder, 'source', X())
    setattr(workflow.builder.source, 'dockerfile_path', None)
    setattr(workflow.builder.source, 'path', '')
    runner = PreBuildPluginsRunner(docker_tasker, workflow,
                                   [{
                                       'name': InjectYumRepoPlugin.key,
                                       'args': {
                                           "wrap_commands": True
                                       }
                                   }])
    runner.run()
    assert InjectYumRepoPlugin.key is not None

    expected_output = """FROM fedora
RUN printf "[my-repo]\nname=my-repo\nmetalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-\\$releasever&arch=\
\\$basearch\nenabled=1\ngpgcheck=0\n" >/etc/yum.repos.d/atomic-reactor-injected.repo && \
yum install -y --setopt=tsflags=nodocs bind-utils gettext iproute v8314 mongodb24-mongodb mongodb24 &&     \
yum clean all &&     mkdir -p /var/lib/mongodb/data && chown -R mongodb:mongodb /var/lib/mongodb/ &&     \
test "$(id mongodb)" = "uid=184(mongodb) gid=998(mongodb) groups=998(mongodb)" &&     \
chmod o+w -R /var/lib/mongodb && chmod o+w -R /opt/rh/mongodb24/root/var/lib/mongodb && \
yum clean all && rm -f /etc/yum.repos.d/atomic-reactor-injected.repo
CMD blabla"""  # noqa
    assert df.content == expected_output
    def run(self):
        pulp = dockpulp.Pulp(env=self.pulp_registry_name)
        self.set_auth(pulp)

        # We only want the hostname[:port]
        hostname_and_port = re.compile(r'^https?://([^/]*)/?.*')
        pulp_registry = hostname_and_port.sub(lambda m: m.groups()[0],
                                              pulp.registry)

        # Store the registry URI in the push configuration
        self.workflow.push_conf.add_pulp_registry(self.pulp_registry_name,
                                                  pulp_registry)

        self.log.info("syncing from docker V2 registry %s",
                      self.docker_registry)

        docker_registry = hostname_and_port.sub(lambda m: m.groups()[0],
                                                self.docker_registry)

        kwargs = self.get_dockercfg_credentials(docker_registry)
        if self.insecure_registry is not None:
            kwargs['ssl_validation'] = not self.insecure_registry

        images = []
        repos = {}  # pulp repo -> repo id
        for image in self.workflow.tag_conf.images:
            if image.pulp_repo not in repos:
                repo_id = self.create_repo_if_missing(
                    pulp, image.pulp_repo,
                    image.to_str(registry=False, tag=False))
                self.log.info("syncing %s", repo_id)
                pulp.syncRepo(repo=repo_id,
                              feed=self.docker_registry,
                              **kwargs)
                repos[image.pulp_repo] = repo_id

            images.append(
                ImageName(registry=pulp_registry,
                          repo=image.repo,
                          namespace=image.namespace,
                          tag=image.tag))

        self.log.info("publishing to crane")
        pulp.crane(list(repos.values()), wait=True)

        if self.publish:
            for image_name in images:
                self.log.info("image available at %s", image_name.to_str())

        # Return the set of qualified repo names for this image
        return images
Example #38
0
    def build_image_dockerhost(self, build_image, json_args_path):
        """
        Build docker image inside privileged container using docker from host
        (mount docker socket inside container).
        There are possible races here. Use wisely.

        This operation is asynchronous and you should wait for container to finish.

        :param build_image: str, name of image where build is performed
        :param json_args_path: str, this dir is mounted inside build container and used
                               as a way to transport data between host and buildroot; there
                               has to be a file inside this dir with name
                               atomic_reactor.BUILD_JSON which is used to feed build
        :return: str, container id
        """
        logger.info("building image '%s' in container using docker from host",
                    build_image)

        self._check_build_input(build_image, json_args_path)
        self._obtain_source_from_path_if_needed(json_args_path,
                                                CONTAINER_SHARE_PATH)

        if not os.path.exists(DOCKER_SOCKET_PATH):
            logger.error(
                "looks like docker is not running because there is no socket at: %s",
                DOCKER_SOCKET_PATH)
            raise RuntimeError("docker socket not found: %s" %
                               DOCKER_SOCKET_PATH)

        volume_bindings = {
            DOCKER_SOCKET_PATH: {
                'bind': DOCKER_SOCKET_PATH,
                'mode': 'ro',
            },
            json_args_path: {
                'bind': CONTAINER_SHARE_PATH,
                'mode': 'rw,Z',
            },
        }

        with open(os.path.join(json_args_path, BUILD_JSON)) as fp:
            logger.debug('build json mounted in container: %s', fp.read())

        container_id = self.tasker.run(
            ImageName.parse(build_image),
            create_kwargs={'volumes': [DOCKER_SOCKET_PATH, json_args_path]},
            volume_bindings=volume_bindings,
            privileged=True,
        )

        return container_id
 def test_base_image_missing_labels(self, workflow, koji_session,
                                    remove_labels, exp_result,
                                    reactor_config_map, external, caplog):
     base_tag = ImageName.parse('base:stubDigest')
     workflow.builder.base_image_inspect[INSPECT_CONFIG]['Labels'] =\
         BASE_IMAGE_LABELS_W_ALIASES.copy()
     workflow.builder._parent_images_inspect[base_tag][INSPECT_CONFIG]['Labels'] =\
         BASE_IMAGE_LABELS_W_ALIASES.copy()
     for label in remove_labels:
         del workflow.builder.base_image_inspect[INSPECT_CONFIG]['Labels'][
             label]
         del workflow.builder._parent_images_inspect[base_tag][
             INSPECT_CONFIG]['Labels'][label]
     if not exp_result:
         if not (external and reactor_config_map):
             with pytest.raises(PluginFailedException) as exc:
                 self.run_plugin_with_args(
                     workflow,
                     expect_result=exp_result,
                     reactor_config_map=reactor_config_map,
                     external_base=external)
             assert 'Was this image built in OSBS?' in str(exc.value)
         else:
             result = {
                 PARENT_IMAGES_KOJI_BUILDS: {
                     ImageName.parse('base'): None
                 }
             }
             self.run_plugin_with_args(
                 workflow,
                 expect_result=result,
                 reactor_config_map=reactor_config_map,
                 external_base=external)
             assert 'Was this image built in OSBS?' in caplog.text
     else:
         self.run_plugin_with_args(workflow,
                                   expect_result=exp_result,
                                   reactor_config_map=reactor_config_map)
    def workflow(docker_repos, registry=None):
        images = []
        for tag in ['1.0-1', '1.0', 'latest', 'unique-timestamp']:
            images.extend([
                ImageName.parse('{0}/{1}:{2}'.format(registry, repo,
                                                     tag).lstrip('/'))
                for repo in docker_repos
            ])

        tag_conf = flexmock(images=images)
        push_conf = PushConf()
        return flexmock(tag_conf=tag_conf,
                        push_conf=push_conf,
                        postbuild_plugins_conf=[])
Example #41
0
    def set_base_image(self, base_image, parents_pulled=True, insecure=False):
        self.base_from_scratch = base_image_is_scratch(base_image)
        if not self.custom_base_image:
            self.custom_base_image = base_image_is_custom(base_image)
        self.base_image = ImageName.parse(base_image)
        self.original_base_image = self.original_base_image or self.base_image
        self.recreate_parent_images()

        if not self.base_from_scratch:
            self.parent_images[self.original_base_image] = self.base_image
        self.parents_pulled = parents_pulled
        self.base_image_insecure = insecure
        logger.info("set base image to '%s' with original base '%s'",
                    self.base_image, self.original_base_image)
Example #42
0
def test_hostdocker_build(caplog, source_params):
    if MOCK:
        mock_docker()

    image_name = ImageName(repo="atomic-reactor-test-ssh-image")
    remote_image = image_name.copy()
    remote_image.registry = LOCALHOST_REGISTRY
    m = DockerhostBuildManager(
        "buildroot-dh-fedora",
        {
            "source": source_params,
            "image": remote_image.to_str(),
            "parent_registry": LOCALHOST_REGISTRY,  # faster
            "target_registries_insecure": True,
            "parent_registry_insecure": True,
        })
    results = m.build()
    dt = DockerTasker()
    dt.pull_image(remote_image, insecure=True)

    assert len(results.build_logs) > 0
    dt.remove_container(results.container_id)
    dt.remove_image(remote_image)
    def test_replace_repo_schema_validation(self, site_replacements, exc_msg):
        image = ImageName.parse('a/x/foo')

        mock_package_mapping_files(site_replacements)
        mock_inspect_query(image, {}, times=0)

        site_config = get_site_config(repo_replacements=site_replacements)

        replacer = PullspecReplacer(user_config={}, site_config=site_config)

        with pytest.raises(jsonschema.ValidationError) as exc_info:
            replacer.replace_repo(image)

        assert exc_msg in str(exc_info.value)
Example #44
0
def test_prebuild_plugin_failure(docker_tasker):  # noqa
    workflow = DockerBuildWorkflow("test-image", source=SOURCE)
    setattr(workflow, 'builder', X())
    setattr(workflow.builder, 'image_id', "asd123")
    setattr(workflow.builder, 'base_image', ImageName(repo='fedora', tag='21'))
    setattr(workflow.builder, "source", X())
    setattr(workflow.builder.source, 'dockerfile_path', "/non/existent")
    setattr(workflow.builder.source, 'path', "/non/existent")
    runner = PreBuildPluginsRunner(docker_tasker, workflow,
                                   [{"name": AddYumRepoByUrlPlugin.key,
                                     "args": {'repourls': True}}])
    with pytest.raises(PluginFailedException):
        runner.run()
    assert workflow.build_process_failed is True
def prepare(tmpdir):
    if MOCK:
        mock_docker()
    tasker = DockerTasker()
    workflow = DockerBuildWorkflow("test-image", source=SOURCE)
    setattr(workflow, 'builder', X())
    source = MockSource(tmpdir)

    setattr(workflow.builder, 'image_id', "asd123")
    setattr(workflow.builder, 'base_image', ImageName(repo='Fedora', tag='21'))
    setattr(workflow.builder, 'source', source)
    setattr(workflow, 'source', source)

    return tasker, workflow
Example #46
0
def prepare():
    tasker = DockerTasker()
    workflow = DockerBuildWorkflow(SOURCE, "test-image")
    setattr(workflow, 'builder', X())

    setattr(workflow.builder, 'image_id', "asd123")
    setattr(workflow.builder, 'base_image', ImageName(repo='Fedora', tag='21'))
    setattr(workflow.builder, 'source', X())
    setattr(workflow.builder.source, 'dockerfile_path', None)
    setattr(workflow.builder.source, 'path', None)

    flexmock(koji, ClientSession=MockedClientSession, PathInfo=MockedPathInfo)

    return tasker, workflow
Example #47
0
def test_parent_images_unresolved(tmpdir):
    """test when parent_images hasn't been filled in with unique tags."""
    dfp = df_parser(str(tmpdir))
    dfp.content = "FROM spam"

    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.base_image = ImageName.parse('eggs')

    # we want to fail because some img besides base was not resolved
    workflow.builder.parent_images = {'spam': 'eggs', 'extra:image': None}

    with pytest.raises(ParentImageUnresolved):
        ChangeFromPlugin(docker_tasker(), workflow).run()
def test_hostdocker_build(caplog, source_params):
    if MOCK:
        mock_docker()

    image_name = ImageName(repo="atomic-reactor-test-ssh-image")
    remote_image = image_name.copy()
    remote_image.registry = LOCALHOST_REGISTRY
    m = DockerhostBuildManager(
        "buildroot-dh-fedora",
        {
            "source": source_params,
            "image": remote_image.to_str(),
            "parent_registry": LOCALHOST_REGISTRY,  # faster
            "target_registries_insecure": True,
            "parent_registry_insecure": True,
        })
    results = m.build()
    dt = DockerTasker()
    dt.pull_image(remote_image, insecure=True)

    if source_params['provider'] == 'path':
        assert_source_from_path_mounted_ok(caplog, m.temp_dir)

    assert len(results.build_logs) > 0
    #assert re.search(r'build json mounted in container .+"uri": %s' %
    #        os.path.join(dconstants.CONTAINER_SHARE_PATH, 'source'))
    # assert isinstance(results.built_img_inspect, dict)
    # assert len(results.built_img_inspect.items()) > 0
    # assert isinstance(results.built_img_info, dict)
    # assert len(results.built_img_info.items()) > 0
    # assert isinstance(results.base_img_info, dict)
    # assert len(results.base_img_info.items()) > 0
    # assert len(results.base_plugins_output) > 0
    # assert len(results.built_img_plugins_output) > 0
    dt.remove_container(results.container_id)
    dt.remove_image(remote_image)
Example #49
0
    def get_component_name(self):
        try:
            labels = Labels(self.labels)
            _, name = labels.get_name_and_value(Labels.LABEL_TYPE_NAME)
        except KeyError:
            self.log.error('Unable to determine component from "Labels"')
            raise

        organization = get_registries_organization(self.workflow)
        if organization:
            image = ImageName.parse(name)
            image.enclose(organization)
            name = image.get_repo()

        return name
Example #50
0
def test_rpmqa_plugin_exception(docker_tasker):  # noqa
    workflow = DockerBuildWorkflow(SOURCE, "test-image")
    setattr(workflow, 'builder', X())
    setattr(workflow.builder, 'image_id', "asd123")
    setattr(workflow.builder, 'base_image', ImageName(repo='fedora', tag='21'))
    setattr(workflow.builder, "source", X())
    setattr(workflow.builder.source, 'dockerfile_path', "/non/existent")
    setattr(workflow.builder.source, 'path', "/non/existent")

    flexmock(docker.Client, logs=mock_logs_raise)
    runner = PostBuildPluginsRunner(docker_tasker, workflow,
                                    [{"name": PostBuildRPMqaPlugin.key,
                                      "args": {'image_id': TEST_IMAGE}}])
    with pytest.raises(PluginFailedException):
        runner.run()
    def _resolve_base_image(self, build_json):
        """If this is an auto-rebuild, adjust the base image to use the triggering build"""
        spec = build_json.get("spec")
        try:
            image_id = spec['triggeredBy'][0]['imageChangeBuild']['imageID']
        except (TypeError, KeyError, IndexError):
            # build not marked for auto-rebuilds; use regular base image
            base_image = self.workflow.builder.base_image
            self.log.info("using %s as base image.", base_image)
        else:
            # build has auto-rebuilds enabled
            self.log.info("using %s from build spec[triggeredBy] as base image.", image_id)
            base_image = ImageName.parse(image_id)  # any exceptions will propagate

        return base_image
Example #52
0
        def workflow_callback(workflow):
            workflow = self.prepare(workflow)
            (flexmock(atomic_reactor.util).should_receive(
                'get_config_from_registry').and_raise(
                    exception('', response=MockResponse())).once())

            manifest_tag = 'registry.example.com' + '/' + BASE_IMAGE_W_SHA
            base_image_result = ImageName.parse(manifest_tag)
            manifest_image = base_image_result.copy()
            (flexmock(atomic_reactor.util).should_receive(
                'get_manifest_list').with_args(
                    image=manifest_image,
                    registry=manifest_image.registry,
                    insecure=True).and_return(None).once())
            return workflow
Example #53
0
def _mock_pull(repo, tag='latest', **kwargs):
    im = ImageName.parse(repo)
    if im.repo == 'library-only' and im.namespace != 'library':
        return iter(mock_pull_logs_failed)

    if tag and 'sha256' in tag:
        repotag = '%s@%s' % (repo, tag)
    else:
        repotag = '%s:%s' % (repo, tag)
    if _find_image(repotag) is None:
        new_image = mock_image.copy()
        new_image['RepoTags'] = [repotag]
        mock_images.append(new_image)

    return iter(mock_pull_logs)
 def _replace(self,
              image,
              registry=_KEEP,
              namespace=_KEEP,
              repo=_KEEP,
              tag=_KEEP):
     """
     Replace specified parts of image pullspec, keep the rest
     """
     return ImageName(
         registry=image.registry if registry is _KEEP else registry,
         namespace=image.namespace if namespace is _KEEP else namespace,
         repo=image.repo if repo is _KEEP else repo,
         tag=image.tag if tag is _KEEP else tag,
     )
Example #55
0
def test_get_manifest_digests_missing(tmpdir, v1_digest, v2_digest):
    kwargs = {}

    image = ImageName.parse('example.com/spam:latest')
    kwargs['image'] = image

    kwargs['registry'] = 'https://example.com'

    url = 'https://example.com/v2/spam/manifests/latest'

    def request_callback(request):
        media_type = request.headers['Accept']
        media_type_prefix = media_type.split('+')[0]
        # If requested schema version is not available, attempt to
        # fallback to other version if possible to simulate how
        # a docker registry behaves
        if media_type.endswith('v2+json') and v2_digest:
            digest = 'v2-digest'
        elif media_type.endswith('v2+json') and v1_digest:
            digest = 'not-used'
            media_type_prefix = media_type_prefix.replace('v2', 'v1', 1)
        elif media_type.endswith('v1+json') and v1_digest:
            digest = 'v1-digest'
        elif media_type.endswith('v1+json') and v2_digest:
            digest = 'not-used'
            media_type_prefix = media_type_prefix.replace('v1', 'v2', 1)
        else:
            raise ValueError('Unexpected media type {}'.format(media_type))

        headers = {
            'Content-Type': '{}+jsonish'.format(media_type_prefix),
            'Docker-Content-Digest': digest
        }
        return (200, headers, '')

    responses.add_callback(responses.GET, url, callback=request_callback)

    actual_digests = get_manifest_digests(**kwargs)

    if v1_digest:
        assert actual_digests.v1 == 'v1-digest'
    else:
        assert actual_digests.v1 is None

    if v2_digest:
        assert actual_digests.v2 == 'v2-digest'
    else:
        assert actual_digests.v2 is None
Example #56
0
def test_ensure_primary(tmpdir, monkeypatch, osbs_error, tag_conf, annotations,
                        tag_prefix, reactor_config_map):
    """
    Test that primary image tags are ensured
    """

    runner = prepare(tmpdir,
                     primary_images_annotations=annotations,
                     primary_images_tag_conf=tag_conf,
                     reactor_config_map=reactor_config_map)

    monkeypatch.setenv("BUILD", json.dumps({"metadata": {}}))
    tags = []
    primary_images = runner.workflow.tag_conf.primary_images
    if not primary_images:
        primary_images = [
            ImageName.parse(primary) for primary in
            runner.workflow.build_result.annotations['repositories']['primary']
        ]

    for primary_image in primary_images:
        tag = primary_image.tag
        if '-' in tag:
            continue
        tags.append(tag)

    (flexmock(OSBS).should_receive('get_image_stream').once().with_args(
        TEST_IMAGESTREAM).and_return(ImageStreamResponse()))

    # By using a combination of ordered and once, we verify that
    # ensure_image_stream_tag is not called with version-release tag
    for x in range(DEFAULT_TAGS_AMOUNT):
        expectation = (
            flexmock(OSBS).should_receive('ensure_image_stream_tag').with_args(
                dict, tag_prefix + str(x)).once().ordered())
        if osbs_error:
            expectation.and_raise(OsbsResponseException('None', 500))
    (flexmock(OSBS).should_receive('import_image_tags').once().and_raise(
        AttributeError))
    (flexmock(OSBS).should_receive('import_image').with_args(
        TEST_IMAGESTREAM,
        tags=tags).times(0 if osbs_error else 1).and_return(True))

    if osbs_error:
        with pytest.raises(PluginFailedException):
            runner.run()
    else:
        runner.run()
Example #57
0
def test_update_parent_images(organization, df_content, expected_df_content, base_from_scratch,
                              tmpdir, reactor_config_map, docker_tasker):
    """test the happy path for updating multiple parents"""
    dfp = df_parser(str(tmpdir))
    dfp.content = df_content

    # maps from dockerfile image to unique tag and then to ID
    first = ImageName.parse("first:parent")
    second = ImageName.parse("second:parent")
    monty = ImageName.parse("monty")
    custom = ImageName.parse("koji/image-build")
    build1 = ImageName.parse('build-name:1')
    build2 = ImageName.parse('build-name:2')
    build3 = ImageName.parse('build-name:3')
    if organization and reactor_config_map:
        first.enclose(organization)
        second.enclose(organization)
        monty.enclose(organization)
    pimgs = {
        first: build1,
        second: build2,
        monty: build3,
        custom: build3,
    }
    img_ids = {
        'build-name:1': 'id:1',
        'build-name:2': 'id:2',
        'build-name:3': 'id:3',
    }

    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.set_base_from_scratch(base_from_scratch)
    workflow.builder.base_image = ImageName.parse('build-name:3')
    workflow.builder.parent_images = pimgs
    workflow.builder.tasker.inspect_image = lambda img: dict(Id=img_ids[img])
    for image_name, image_id in img_ids.items():
        workflow.builder.set_parent_inspection_data(image_name, dict(Id=image_id))

    original_base = workflow.builder.base_image
    run_plugin(workflow, reactor_config_map, docker_tasker, organization=organization)
    assert dfp.content == expected_df_content
    assert workflow.builder.original_df == df_content
    if base_from_scratch:
        assert original_base == workflow.builder.base_image
Example #58
0
    def get_pullspecs(self):
        """
        Find pullspecs in predefined locations.

        :return: set of ImageName pullspecs
        """
        named_pullspecs = self._named_pullspecs()
        pullspecs = set()

        for p in named_pullspecs:
            image = ImageName.parse(p.image)
            log.debug("%s - Found pullspec for %s: %s", self.path,
                      p.description, image)
            pullspecs.add(image)

        return pullspecs
Example #59
0
    def replace_pullspecs(self, replacement_pullspecs):
        """
        Replace pullspecs in predefined locations.

        :param replacement_pullspecs: mapping of pullspec -> replacement
        """
        named_pullspecs = self._named_pullspecs()

        for p in named_pullspecs:
            old = ImageName.parse(p.image)
            new = replacement_pullspecs.get(old)

            if new is not None and old != new:
                log.debug("%s - Replaced pullspec for %s: %s -> %s", self.path,
                          p.description, old, new)
                p.image = new.to_str()  # `new` is an ImageName
Example #60
0
def test_update_base_image_inspect_broken(tmpdir, caplog):
    """exercise code branch where the base image inspect comes back without an Id"""
    df_content = "FROM base:image"
    dfp = df_parser(str(tmpdir))
    dfp.content = df_content

    workflow = mock_workflow()
    workflow.builder.set_df_path(dfp.dockerfile_path)
    workflow.builder.parent_images = {"base:image": "base@sha256:1234"}
    workflow.builder.base_image = ImageName.parse("base@sha256:1234")
    workflow.builder.tasker.inspect_image = lambda img: dict(no_id="here")

    with pytest.raises(NoIdInspection):
        ChangeFromPlugin(docker_tasker(), workflow).run()
    assert dfp.content == df_content  # nothing changed
    assert "missing in inspection" in caplog.text()