pulp_2_tests.tests.docker.api_v2.test_sync_publish

Location: Pulp 2 TestsTestspulp_2_tests.tests.docker.api_v2.test_sync_publish

Tests for syncing and publishing docker repositories.

class pulp_2_tests.tests.docker.api_v2.test_sync_publish.DockerHeadersTestCase(methodName='runTest')

Bases: pulp_2_tests.tests.docker.api_v2.utils.SyncPublishMixin, unittest.case.TestCase

Synced docker v2 repo has correctly populated Content-Type field.

This test targets the following issue:

Pulp #4535

Sync and publish a docker repo and ensures the Content-Type field is accurately populated.

Steps involved:

  1. Create, sync and publish a docker repository.
  2. Get all tags from the docker repo.
  3. Choose a random tag and get the header information from that tag.
  4. Get the Content-Type field and verify accurate.
test_validate_content_type()

Validates content_type contains correct content.

pulp_2_tests.tests.docker.api_v2.test_sync_publish.MANIFEST_LIST_V2 = {'$schema': 'http://json-schema.org/schema#', 'description': 'Derived from: https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list', 'properties': {'manifests': {'items': {'properties': {'digest': {'type': 'string'}, 'mediaType': {'type': 'string'}, 'platform': {'properties': {'architecture': {'type': 'string'}, 'features': {'items': {'type': 'string'}, 'type': 'array'}, 'os': {'type': 'string'}, 'os.features': {'items': {'type': 'string'}, 'type': 'array'}, 'os.version': {'type': 'string'}, 'variant': {'type': 'string'}}, 'type': 'object'}, 'size': {'type': 'integer'}}, 'type': 'object'}, 'type': 'array'}, 'mediaType': {'type': 'string'}, 'schemaVersion': {'type': 'integer'}}, 'title': 'Image Manifest List', 'type': 'object'}

A schema for docker manifest lists.

pulp_2_tests.tests.docker.api_v2.test_sync_publish.MANIFEST_V1 = {'$schema': 'http://json-schema.org/schema#', 'description': 'Derived from: https://docs.docker.com/registry/spec/manifest-v2-1/', 'properties': {'architecture': {'type': 'string'}, 'fsLayers': {'items': {'properties': {'blobSum': {'type': 'string'}}, 'type': 'object'}, 'type': 'array'}, 'history': {'items': {'properties': {'v1Compatibility': {'type': 'string'}}, 'type': 'object'}, 'type': 'array'}, 'name': {'type': 'string'}, 'schemaVersion': {'type': 'integer'}, 'signatures': {'items': {'properties': {'header': {'properties': {'alg': {'type': 'string'}, 'jwk': {'properties': {'crv': {'type': 'string'}, 'kid': {'type': 'string'}, 'kty': {'type': 'string'}, 'x': {'type': 'string'}, 'y': {'type': 'string'}}, 'type': 'object'}}, 'type': 'object'}, 'protected': {'type': 'string'}, 'signature': {'type': 'string'}}, 'type': 'object'}, 'type': 'array'}, 'tag': {'type': 'string'}}, 'title': 'Image Manifest Version 2, Schema 1', 'type': 'object'}

A schema for docker v2 image manifests, schema 1.

pulp_2_tests.tests.docker.api_v2.test_sync_publish.MANIFEST_V2 = {'$schema': 'http://json-schema.org/schema#', 'description': 'Derived from: https://docs.docker.com/registry/spec/manifest-v2-2/#image-manifest', 'properties': {'config': {'properties': {'digest': {'type': 'string'}, 'mediaType': {'type': 'string'}, 'size': {'type': 'integer'}}, 'type': 'object'}, 'layers': {'items': {'properties': {'digest': {'type': 'string'}, 'mediaType': {'type': 'string'}, 'size': {'type': 'integer'}, 'urls': {'items': {'type': 'string'}, 'type': 'array'}}, 'type': 'object'}, 'type': 'array'}, 'mediaType': {'type': 'string'}, 'schemaVersion': {'type': 'integer'}}, 'title': 'Image Manifest Version 2, Schema 2', 'type': 'object'}

A schema for docker v2 image manifests, schema 2.

class pulp_2_tests.tests.docker.api_v2.test_sync_publish.NoAmd64LinuxTestCase(methodName='runTest')

Bases: pulp_2_tests.tests.docker.api_v2.utils.SyncPublishMixin, unittest.case.TestCase

Sync a Docker image with no amd64/linux build.

A manifest list lets a single Docker repository contain multiple images. This is useful in the case where an image contains platform-specific code, and an image must be built for each each supported architecture, OS, etc.

When a modern Docker client fetches an image, it does the following:

  1. Get a manifest list.
  2. Look through the list of available images.
  3. Pick out an image that functions on the current host’s platform.
  4. Get a manifest for that image.
  5. Use the information in the manifest to get the image layers.

Older Docker clients aren’t aware of manifest lists, and when they go to fetch an image, they just ask for any old manifest from a repository. When a Docker registry receives such a request, it does the following:

  1. Look through the list of available images.
  2. If an image with an architecture of amd64 and an os of linux is available, return its manifest. Otherwise, return an HTTP 404.

This test case verifies Pulp’s behaviour in the case where an upstream Docker repository has content described by a manifest list.

This test case doesn’t verify Pulp’s behaviour in the case where an upstream Docker repository has content described by a v2 manifest or v1 manifest. In these cases, the correct behaviour of a Docker registry is not well defined. See the backward compatibility documentation.

classmethod setUpClass()

Create class-wide variables.

classmethod tearDownClass()

Clean up resources.

test_01_set_up()

Create, sync and publish a Docker repository.

Specifically, do the following:

  1. Create, sync and publish a Docker repository. Let the repository’s upstream name reference a repository that has an image with a manifest list and no amd64/linux build.
  2. Make Crane immediately re-read the metadata files published by Pulp. (Restart Apache.)
test_02_get_manifest_list()

Get a manifest list.

Assert that:

  • The response headers include a content-type of accept:application/vnd.docker.distribution.manifest.list.v2+json. (See test_02_get_manifest_list().
  • The response body matches MANIFEST_LIST_V2.
  • The returned manifest list doesn’t include any entry where architecture of amd64 and os is linux.
test_02_get_manifest_v1()

Get a v1 manifest. Assert that an HTTP 404 is returned.

test_02_get_manifest_v2()

Get a v2 manifest. Assert that an HTTP 404 is returned.

class pulp_2_tests.tests.docker.api_v2.test_sync_publish.NonNamespacedImageTestCase(methodName='runTest')

Bases: pulp_2_tests.tests.docker.api_v2.utils.SyncPublishMixin, unittest.case.TestCase

Work with an image whose name has no namespace.

test_all()

Work with an image whose name has no namespace.

Create, sync and publish a Docker repository whose UPSTREAM_NAME doesn’t include a namespace. A typical Docker image has a name like “library/busybox.” When a non-namespaced image name like “busybox” is given, a prefix of “library” is assumed.

class pulp_2_tests.tests.docker.api_v2.test_sync_publish.RepoRegistryIdTestCase(methodName='runTest')

Bases: pulp_2_tests.tests.docker.api_v2.utils.SyncPublishMixin, unittest.case.TestCase

Show Pulp can publish repos with varying repo_registry_id values.

do_test(cfg, repo_registry_id)

Execute the test with the given repo_registry_id.

test_all()

Show Pulp can publish repos with varying repo_registry_id values.

The repo_registry_id setting defines a Docker repository’s name as seen by clients such as the Docker CLI. It’s traditionally a two-part name such as docker/busybox, but according to Pulp #2368, it can contain an arbitrary number of slashes. This test case verifies that the repo_registry_id can be set to values containing one, two and three slashes.

Also see: Pulp #2723.

class pulp_2_tests.tests.docker.api_v2.test_sync_publish.V2RegistryTestCase(methodName='runTest')

Bases: pulp_2_tests.tests.docker.api_v2.utils.SyncPublishMixin, unittest.case.TestCase

Create, sync, publish and interact with a v2 Docker registry.

setUp()

Create docker repository.

classmethod setUpClass()

Create class-wide variables.

test_docker_pull_v2()

Emulate docker pull to verify crane is working as expected.

This test emulates docker pull command that hits the pulp crane repositories. First the details about manifests in the repo are collected by hitting the manifests url. Then the blobs checksum that are returned as a part of the previous request are iterated and are hit separately. The actual docker pull, gets all these blobs and stitches them together to form the docker image.This testcase is in reference to Pulp #3638.

test_get_crane_repositories_v2()

Issue an HTTP GET request to /crane/repositories/v2.

Assert that the response is as described by Crane Admin.

test_get_manifest_list()

Issue an HTTP GET request to /v2/{repo_id}/manifests/latest.

Pass a header of accept:application/vnd.docker.distribution.manifest.list.v2+json Assert that:

  • The response body matches MANIFEST_LIST_V2.
  • The response has a content-type equal to what was requested. (According to Docker’s backward compatiblity specification, if a registry is asked for a manifest list but doesn’t have a manifest list, it may return a manifest instead. But this test targets manifest lists, and it will fail if that happens.)
test_get_manifest_v1()

Issue an HTTP GET request to /v2/{repo_id}/manifests/latest.

Pass each of the followng headers in turn:

  • (none)
  • accept:application/json
  • accept:application/vnd.docker.distribution.manifest.v1+json

Assert the response matches MANIFEST_V1.

This test targets Pulp #2336.

test_get_manifest_v2()

Issue an HTTP GET request to /v2/{repo_id}/manifests/latest.

Pass a header of accept:application/vnd.docker.distribution.manifest.v2+json. Assert that the response body matches MANIFEST_V2.

This test targets Pulp #2336.

pulp_2_tests.tests.docker.api_v2.test_sync_publish.skip_if(func, var_name, result, *, exc=<class 'unittest.case.SkipTest'>)

Optionally skip a test method, based on a condition.

This decorator checks to see if func(getattr(self, var_name)) equals result. If so, an exception of type exc is raised. Otherwise, nothing happens, and the decorated test method continues as normal. Here’s an example of how to use this method:

>>> import unittest
>>> from pulp_smash.selectors import skip_if
>>> class MyTestCase(unittest.TestCase):
...
...     @classmethod
...     def setUpClass(cls):
...         cls.my_var = False
...
...     @skip_if(bool, 'my_var', False, unittest.SkipTest)
...     def test_01_skips(self):
...         pass
...
...     def test_02_runs(self):
...         type(self).my_var = True
...
...     @skip_if(bool, 'my_var', False, unittest.SkipTest)
...     def test_03_runs(self):
...         pass

If the same exception should be passed each time this method is called, consider using functools.partial:

>>> from functools import partial
>>> from unittest import SkipTest
>>> from pulp_smash.selectors import skip_if
>>> unittest_skip_if = partial(skip_if, exc=SkipTest)
Parameters:
  • var_name – A valid variable name.
  • result – A value to compare to func(getattr(self, var_name)).
  • exc – A class to instantiate and raise as an exception. Its constructor must accept one string argument.