def test_does_not_bump_tag_expiration_when_manifest_is_cached_and_upstream_errors(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            UBI8_8_4_DIGEST, UBI8_8_4_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        first_tag = Tag.get(manifest_id=manifest.id)

        proxy_mock = proxy_manifest_response("not-existing-ref", "", "")
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        tag = Tag.get(manifest_id=manifest.id)
        assert tag.id == first_tag.id
        assert tag.lifetime_end_ms == first_tag.lifetime_end_ms
    def test_return_None_when_manifest_is_placeholder_and_upstream_is_down(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            "latest", UBI8_LATEST_MANIFEST_LIST,
            DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, "latest")
        assert tag is not None
        assert tag.manifest is not None

        proxy_mock = proxy_manifest_response("does-not-exist", "", "")
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_LATEST_DIGEST)
        assert manifest is None
    def test_returns_cached_manifest_when_upstream_errors_and_cache_is_not_expired(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            UBI8_8_4_DIGEST, UBI8_8_4_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        assert manifest.digest == UBI8_8_4_DIGEST
        first_manifest = manifest

        proxy_mock = proxy_manifest_response("not-existing-ref", "", "")
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        assert manifest.id == first_manifest.id
        assert manifest.digest == first_manifest.digest
        assert proxy_mock.manifest_exists.call_count == 1
        assert proxy_mock.get_manifest.call_count == 0
    def test_update_relevant_manifest_fields_when_manifest_is_placeholder(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            "latest", UBI8_LATEST_MANIFEST_LIST,
            DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, "latest")
        assert tag is not None
        assert tag.manifest.digest == UBI8_LATEST_MANIFEST_LIST_DIGEST
        assert tag.manifest.is_manifest_list

        proxy_mock = proxy_manifest_response(
            UBI8_LATEST_DIGEST, UBI8_LATEST_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_LATEST_DIGEST)
        mbytes = manifest.internal_manifest_bytes.as_unicode()
        assert mbytes != ""
        assert manifest.digest == UBI8_LATEST_DIGEST
        assert manifest.layers_compressed_size == 772795
    def test_doesnt_bump_tag_expiration_when_upstream_is_dead(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            self.tag, UBI8_8_5_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)
        assert tag is not None
        first_tag = tag

        proxy_mock = proxy_manifest_response("not-existing-ref", "", "")
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)
        assert tag is not None
        assert tag.lifetime_end_ms == first_tag.lifetime_end_ms
    def test_returns_None_when_manifest_no_longer_exists_upstream_and_local_cache_is_expired(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            self.tag, UBI8_8_5_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)
        assert tag is not None

        # expire the tag by setting start and end time to the past
        before_ms = get_epoch_timestamp_ms() - timedelta(
            hours=24).total_seconds() * 1000
        Tag.update(
            lifetime_start_ms=before_ms,
            lifetime_end_ms=before_ms + 5,
        ).where(Tag.id == tag.id).execute()

        proxy_mock = proxy_manifest_response("not-existing-ref", "", "")
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)
        assert tag is None
    def test_renew_tag_when_manifest_is_cached_and_exists_upstream(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            UBI8_8_4_DIGEST, UBI8_8_4_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        first_tag = oci.tag.get_tag_by_manifest_id(repo_ref.id, manifest.id)

        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        tag = oci.tag.get_tag_by_manifest_id(repo_ref.id, manifest.id)
        assert tag.lifetime_end_ms > first_tag.lifetime_end_ms
    def test_returns_cached_manifest_when_it_exists_upstream(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            UBI8_8_4_DIGEST, UBI8_8_4_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        assert manifest.digest == UBI8_8_4_DIGEST
        first_manifest = manifest

        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)

        assert manifest is not None
        assert manifest.id == first_manifest.id
        assert manifest.digest == first_manifest.digest

        # one for each lookup_manifest_by_digest call
        assert proxy_mock.manifest_exists.call_count == 2

        # single call from first call to lookup_manifest_by_digest
        assert proxy_mock.get_manifest.call_count == 1
    def test_bumps_tag_expiration_when_upstream_is_alive_and_cache_is_up_to_date(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            self.tag, UBI8_8_5_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)

        assert tag is not None
        assert tag.name == self.tag

        first_tag = tag
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)

        assert tag is not None
        assert tag.lifetime_end_ms > first_tag.lifetime_end_ms
 def test_passes_through_upstream_error_when_image_isnt_cached(
         self, create_repo, proxy_manifest_response):
     repo_ref = create_repo(self.orgname, self.upstream_repository,
                            self.user)
     proxy_mock = proxy_manifest_response("not-existing-ref", "", "")
     with patch("data.registry_model.registry_proxy_model.Proxy",
                MagicMock(return_value=proxy_mock)):
         proxy_model = ProxyModel(
             self.orgname,
             self.upstream_repository,
             self.user,
         )
         with pytest.raises(TagDoesNotExist):
             proxy_model.get_repo_tag(repo_ref, self.tag)
    def test_updates_manifest_and_bumps_tag_expiration_when_upstream_manifest_changed(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            self.tag, UBI8_8_4_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)

        assert tag is not None
        assert tag.name == self.tag

        first_manifest = tag.manifest

        proxy_mock = proxy_manifest_response(
            self.tag, UBI8_8_5_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)

        assert tag is not None
        assert tag.name == self.tag
        assert tag.manifest.id != first_manifest.id
        assert tag.manifest.digest == UBI8_8_5_DIGEST
    def test_renew_manifest_and_parent_tag_when_manifest_is_child_of_manifest_list(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        input_list = parse_manifest_from_bytes(
            Bytes.for_string_or_unicode(UBI8_LATEST_MANIFEST_LIST),
            DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE,
            sparse_manifest_support=True,
        )
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock()):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest_list, tag = proxy_model._create_manifest_and_retarget_tag(
                repo_ref, input_list, "latest")

        assert manifest_list is not None
        child = (ManifestChild.select(ManifestChild.child_manifest_id).join(
            Manifest,
            on=(ManifestChild.child_manifest_id == Manifest.id
                )).where((ManifestChild.manifest_id == manifest_list.id)
                         & (Manifest.digest == UBI8_LATEST_DIGEST)))
        manifest_tag = Tag.select().where(Tag.manifest == child).get()
        manifest_list_tag = tag

        proxy_mock = proxy_manifest_response(
            UBI8_LATEST_DIGEST, UBI8_LATEST_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_LATEST_DIGEST)

        updated_tag = oci.tag.get_tag_by_manifest_id(repo_ref.id, manifest.id)
        updated_list_tag = oci.tag.get_tag_by_manifest_id(
            repo_ref.id, manifest_list.id)

        assert updated_tag.id == manifest_tag.id
        assert updated_list_tag.id == manifest_list_tag.id
        assert updated_tag.lifetime_end_ms > manifest_tag.lifetime_end_ms
        assert updated_list_tag.lifetime_end_ms > manifest_list_tag.lifetime_end_ms
    def test_caches_manifest_on_first_pull(self, create_repo,
                                           proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            self.tag, UBI8_8_4_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)

        assert tag is not None
        assert tag.manifest is not None
        assert tag.manifest.internal_manifest_bytes.as_unicode(
        ) == UBI8_8_4_MANIFEST_SCHEMA2
    def test_renews_expired_tag_when_manifest_is_up_to_date_with_upstream(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            self.tag, UBI8_8_5_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)

        assert tag is not None
        assert tag.name == self.tag

        # expire the tag by setting start and end time to the past
        before_ms = get_epoch_timestamp_ms() - timedelta(
            hours=24).total_seconds() * 1000
        Tag.update(
            lifetime_start_ms=before_ms,
            lifetime_end_ms=before_ms + 5,
        ).where(Tag.id == tag.id).execute()

        expired_tag = tag

        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            tag = proxy_model.get_repo_tag(repo_ref, self.tag)

        assert tag is not None
        assert expired_tag.id == tag.id
        assert expired_tag.manifest.id == tag.manifest.id
        assert not tag.expired
        new_expiration_ms = get_epoch_timestamp_ms(
        ) + self.config.expiration_s * 1000
        # subtract a some milliseconds so the test doesn't flake
        assert tag.lifetime_end_ms >= new_expiration_ms - 500
    def test_renew_tag_when_cache_is_expired_and_manifest_is_up_to_date_with_upstream(
            self, create_repo, proxy_manifest_response):
        repo_ref = create_repo(self.orgname, self.upstream_repository,
                               self.user)
        proxy_mock = proxy_manifest_response(
            UBI8_8_4_DIGEST, UBI8_8_4_MANIFEST_SCHEMA2,
            DOCKER_SCHEMA2_MANIFEST_CONTENT_TYPE)
        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None

        before_ms = get_epoch_timestamp_ms() - timedelta(
            hours=24).total_seconds() * 1000
        Tag.update(
            lifetime_start_ms=before_ms,
            lifetime_end_ms=before_ms + 5,
        ).where(Tag.manifest == manifest.id).execute()

        with patch("data.registry_model.registry_proxy_model.Proxy",
                   MagicMock(return_value=proxy_mock)):
            proxy_model = ProxyModel(
                self.orgname,
                self.upstream_repository,
                self.user,
            )
            manifest = proxy_model.lookup_manifest_by_digest(
                repo_ref, UBI8_8_4_DIGEST)
        assert manifest is not None
        tag = Tag.get(manifest_id=manifest.id)
        now_ms = get_epoch_timestamp_ms()
        assert tag.lifetime_end_ms > now_ms