diff --git a/CHANGES/1165.feature b/CHANGES/1165.feature new file mode 100644 index 000000000..a66103737 --- /dev/null +++ b/CHANGES/1165.feature @@ -0,0 +1 @@ +As a user I can serve cosign produced signatures, SBOMS and attestations. diff --git a/CHANGES/1166.feature b/CHANGES/1166.feature new file mode 100644 index 000000000..dc3b98943 --- /dev/null +++ b/CHANGES/1166.feature @@ -0,0 +1 @@ +Added ability to mirror cosign signatures, SBOMs and attestations. diff --git a/CHANGES/1167.feature b/CHANGES/1167.feature new file mode 100644 index 000000000..57eb191e9 --- /dev/null +++ b/CHANGES/1167.feature @@ -0,0 +1 @@ +As a user I can push a cosign signature, attestation or SBOM to Pulp Registry. diff --git a/CHANGES/1232.feature b/CHANGES/1232.feature new file mode 100644 index 000000000..bcfdb4eff --- /dev/null +++ b/CHANGES/1232.feature @@ -0,0 +1,3 @@ +Enabled registry to support by default some well-known OCI types as such: +Source containers, Conftest policies, Singularity. + diff --git a/CHANGES/1233.feature b/CHANGES/1233.feature new file mode 100644 index 000000000..89e9629dc --- /dev/null +++ b/CHANGES/1233.feature @@ -0,0 +1,2 @@ +Added ``ADDITIONAL_OCI_ARTIFACT_TYPES`` setting to make the list of supported OCI artifact types +configurable. diff --git a/CHANGES/464.feature b/CHANGES/464.feature new file mode 100644 index 000000000..8aa3f454c --- /dev/null +++ b/CHANGES/464.feature @@ -0,0 +1 @@ +Added OCI artifact support helm charts. diff --git a/docs/workflows/cosign-support.rst b/docs/workflows/cosign-support.rst new file mode 100644 index 000000000..711b1f3b3 --- /dev/null +++ b/docs/workflows/cosign-support.rst @@ -0,0 +1,118 @@ +.. _cosign-support: + +Install cosign +============== + +Check for the file in https://github.com/sigstore/cosign/releases + +Binary:: + + wget "https://github.com/sigstore/cosign/releases/download/v1.6.0/cosign-linux-amd64" + mv cosign-linux-amd64 /usr/local/bin/cosign + chmod +x /usr/local/bin/cosign + +Rpm:: + + wget "https://github.com/sigstore/cosign/releases/download/v1.6.0/cosign-1.6.0.x86_64.rpm" + rpm -ivh cosign-1.6.0.x86_64.rpm + +Mirror +====== + +Being an OCI compliant registry Pulp Container registry can natively mirror cosign signatures +wich are stored as an OCI image:: + + { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": "sha256:f35028aa1563f37ccbaa0b32c57777ffbd8e9e3d81d739fec0022995e58a375a", + "size": 153 + }, + "layers": [ + { + "mediaType": "application/vnd.dev.cosign.simplesigning.v1+json", + "digest": "sha256:d3370bd32b32aba43de2b45bb4a2de2fb5c95fd2edbe738acbc3bc595b80c456", + "size": 305, + "annotations": { + "dev.cosignproject.cosign/signature": "MEUCIBWDnTKhbf5x3mSuEHWkv3ixloIFXeDpfXipF9szqrd5AiEA+UU5J84gQ9JnmT6QZAXiPXqSoDVW0CXQYssGh63e9Ro=" + } + } + ] + } + + +During the syncronization task, Pulp will automatically mirror cosign signatures or atomic +signatures (accessible via signatures extentions API). + + +Sign +==== + +Pulp Container registry can host cosign signature which can be pushed via cosign or podman clients: + +Cosign:: + + + # This command creates an ECDSA-P256 key pair (a private and a public key). + cosign generate-key-pair + cosign sign --key cosign.key pulp-registry/ipanova/cosign-test:latest + +or via Podman:: + + podman push pulp-registry/ipanova/cosign-test:latest --sign-by-sigstore-private-key cosign.key + +.. warning:: + To use this with images hosted on image registries, the relevant registry or repository must have + the use-sigstore-attachments option enabled in containers-registries.d(5). This specifies whether + sigstore image attachments (signatures, attestations and the like) are going to be read/written + along with the image. If disabled, the images are treated as if no attachments exist; attempts to + write attachments fail. + +As a result of this operation ``ipanova/cosign-test:latest`` image is signed and its +cosign signature is stored in the registry as an OCI image. Cosign uses a fixed naming convention +to decide the name for a separate image, at which we can store the signature. The tag name resolved +to a fixed digest of the image/or manifest list which is being signed in a form of ``sha256-12345.sig`` + +The payload of the signature will be store as an image layer:: + + { + "critical": { + "identity": { + "docker-reference": "pulp-registry/ipanova/cosigned:latest" + }, + "image": { + "docker-manifest-digest": "sha256:81cd171c4eda75046c31d6ed26f1241bbfa9326640613430be780ea931b02c24" + }, + "type": "cosign container image signature" + }, + "optional": { + "creator": "containers/image 5.23.1", + "timestamp": 1673006074 + } + } + + +.. note: + Besides cosign signature Pulp Container Registry can mirror and host SBOMs and attestations. + + +The verify +========== + +Signature verification can be done via cosign or podman clients:: + + cosign verify --key cosign.pub pulp-registry/ipanova/cosign-test:latest + +When using podman client the policy.json file should be properly configured per specs. +A new requirement type ``sigstoreSigned`` has been introduced: + +https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md#sigstoresigned + +.. warning:: + To use this with images hosted on image registries, the relevant registry or repository must have + the use-sigstore-attachments option enabled in containers-registries.d(5). This specifies whether + sigstore image attachments (signatures, attestations and the like) are going to be read/written + along with the image. If disabled, the images are treated as if no attachments exist; attempts to + write attachments fail. diff --git a/docs/workflows/helm-support.rst b/docs/workflows/helm-support.rst new file mode 100644 index 000000000..98d321ee7 --- /dev/null +++ b/docs/workflows/helm-support.rst @@ -0,0 +1,98 @@ +.. _helm-support: + +Install helm client +=================== + +Install Helm client: Download your desired version from the Helm releases page. +Unpack it and move the helm binary to its desired destination :: + + +$ tar -zxvf helm-v3.8.2-linux-amd64.tar.gz +$ mv linux-amd64/helm /usr/local/bin/helm + +RPM installation:: + + sudo dnf install helm + + +Using Helm charts with Pulp Container +===================================== + + +Push and Host +------------- + +Use the following example to download and push an etherpad chart from the Red Hat community repository. + +Add a chart repository:: + + $ helm repo add redhat-cop https://redhat-cop.github.io/helm-charts + +Update the information of available charts locally from the chart repository:: + + $ helm repo update + +Download a chart from a repository:: + + $ helm pull redhat-cop/etherpad --version=0.0.4 --untar + +Package the chart into a chart archive:: + + $ helm package ./etherpad + Successfully packaged chart and saved it to: /home/vagrant/devel/pulp_container/etherpad-0.0.4.tgz + +Log in to your Pulp container registry using helm registry login:: + + $ helm registry login pulp3-source-fedora36.puffy.example.com + +Push the chart to your Pulp Container registry using the helm push command:: + + $ helm push etherpad-0.0.4.tgz oci://pulp3-source-fedora36.puffy.example.com + Pushed: pulp3-source-fedora36.puffy.example.com/etherpad:0.0.4 + Digest: sha256:a6667ff2a0e2bd7aa4813db9ac854b5124ff1c458d170b70c2d2375325f2451b + +Ensure that the push worked by deleting the local copy, and then pulling the chart from the repository:: + + $ rm -rf etherpad-0.0.4.tgz + + $ helm pull oci://pulp3-source-fedora36.puffy.example.com/etherpad --version 0.0.4 + Pulled: pulp3-source-fedora36.puffy.example.com/etherpad:0.0.4 + Digest: sha256:4f627399685880daf30cf77b6026dc129034d68c7676c7e07020b70cf7130902 + +The chart can then be installed using the helm install command:: + + $ helm install etherpad-0.0.4.tgz + +Alternatively, charts can be installed directly from the registry without needing to download locally. +Use the helm install command and reference the registry location:: + + $ helm install oci://pulp3-source-fedora36.puffy.example.com/helm/etherpad --version=0.0.4 + + + +Mirror +------ + +Being an OCI compliant registry Pulp Container registry can natively mirror helm charts +wich are stored as an OCI image:: + + { + "schemaVersion": 2, + "config": { + "mediaType": "application/vnd.cncf.helm.config.v1+json", + "digest": "sha256:8ec7c0f2f6860037c19b54c3cfbab48d9b4b21b485a93d87b64690fdb68c2111", + "size": 117 + }, + "layers": [ + { + "mediaType": "application/vnd.cncf.helm.chart.content.v1.tar+gzip", + "digest": "sha256:1b251d38cfe948dfc0a5745b7af5ca574ecb61e52aed10b19039db39af6e1617", + "size": 2487 + }, + { + "mediaType": "application/vnd.cncf.helm.chart.provenance.v1.prov", + "digest": "sha256:3e207b409db364b595ba862cdc12be96dcdad8e36c59a03b7b3b61c946a5741a", + "size": 643 + } + ] +} diff --git a/docs/workflows/index.rst b/docs/workflows/index.rst index 300938ca5..c91a62d0f 100644 --- a/docs/workflows/index.rst +++ b/docs/workflows/index.rst @@ -60,11 +60,21 @@ Basic Workflows push import-export -Managing Signatures -------------------- +Managing Atomic Signatures +-------------------------- .. toctree:: :maxdepth: 2 sign-images verify-images + +OCI artifact support +-------------------- + + .. toctree:: + :maxdepth: 2 + + cosign-support + helm-support + oci-artifacts diff --git a/docs/workflows/oci-artifacts.rst b/docs/workflows/oci-artifacts.rst new file mode 100644 index 000000000..6bcd79bd6 --- /dev/null +++ b/docs/workflows/oci-artifacts.rst @@ -0,0 +1,53 @@ +Adding other OCI media types to Pulp Registry +============================================= + +Helm, cosign, source containers, singuland OCI images media types are built into the registry by default. +By default the following list of media types is enabled in the Container Registry:: + +.. _default-oci-types: + + * OCI images + * Helm + * Cosign + * Source containers + * Singularity + * Conftest policies + +For any other OCI media type that is not supported by default, you can add them to the +ADDITIONAL_OCI_ARTIFACT_TYPES settings using the following format:: + + ADDITIONAL_OCI_ARTIFACT_TYPES = { + "": [ + "", + "", + ], + "": [ + "", + "", + ], + } + + +For example, you can add WebAssembly (WASM) support by adding the following to your ADDITIONAL_OCI_ARTIFACT_TYPES +setting:: + + ADDITIONAL_OCI_ARTIFACT_TYPES = { + "": [ + "", + "", + ], + "": [ + "", + "", + ], + "application/vnd.module.wasm.config.v1+json": [ + "application/vnd.module.wasm.content.layer.v1+wasm" + ], + } + + .. note:: + +When adding OCI media types that are not configured by default, users will also need to manually add +support for the :ref:`default-oci-types`. +The OCI images media types are supported by default and cannot be disabled, so users will not need +to add that to enable support. diff --git a/docs/workflows/sign-images.rst b/docs/workflows/sign-images.rst index 2b09e9cca..5c836ffc8 100644 --- a/docs/workflows/sign-images.rst +++ b/docs/workflows/sign-images.rst @@ -1,7 +1,7 @@ .. _sign-images: -Image Signature Configuration -============================= +Atomic Container Image Signature Configuration via a Pulp signing service +========================================================================= Administrators can add a container manifest signing service to The Pulp Registry using the command line tools. Users may then associate the signing service with container repositories. @@ -12,10 +12,10 @@ The example below demonstrates how a manifest signing service can be created usi hardware cryptographic device. 2. Create a signing script that accepts a manifest path as the only argument. The script invokes - ``skopeo standalone-sign`` command that generates a container signature for the image manifest, - using the key specified via the ``PULP_SIGNING_KEY_FINGERPRINT`` environment variable. The script - should then print out a JSON structure with the following format. The path of the created - signature is a relative path inside the current working directory:: + ``skopeo standalone-sign`` command that generates an atomic container signature for the image + manifest, using the key specified via the ``PULP_SIGNING_KEY_FINGERPRINT`` environment variable. + The script should then print out a JSON structure with the following format. The path of the + created signature is a relative path inside the current working directory:: {"signature_path": "signature"} @@ -30,7 +30,7 @@ The example below demonstrates how a manifest signing service can be created usi IMAGE_REFERENCE="$REFERENCE" SIGNATURE_PATH="$SIG_PATH" - # Create container signature + # Create atomic container signature skopeo standalone-sign $MANIFEST_PATH $IMAGE_REFERENCE $FINGEPRINT --output $SIGNATURE_PATH # Check the exit status STATUS=$? @@ -80,9 +80,12 @@ The example below demonstrates how a manifest signing service can be created usi Afterwards, users are able to sign selected content by the provided script. + .. warning:: + Underlying singing facility with sign anything what represents an OCI container image. -Sign Images Pushed to the Registry -================================== + +Sign Images Pushed to the Registry via Pulp signing service +----------------------------------------------------------- Given that an image is pushed to the Pulp Registry via ``podman/docker push`` or via the standard DockerRegistry v2 push API, a repository is created containing it:: @@ -140,7 +143,7 @@ operation or it can be explictly specified in the following manner:: "worker": "/pulp/api/v3/workers/eb65c2d9-31b2-47dc-847e-dad0e744c539/" } -Upon task complection, a signature is created and added to the repository:: +Upon task complection, an atomic container signature is created and added to the repository:: $ http GET https://pulp.example.com/pulp/api/v3/repositories/container/container-push/3b279a32-b313-44bf-ad41-4359a92cae24/versions/10/ { @@ -201,8 +204,8 @@ Upon task complection, a signature is created and added to the repository:: } -Sign Images Mirrored into the Registry -====================================== +Sign Images Mirrored into the Registry via Pulp signing service +--------------------------------------------------------------- It is possible to sign content that was synchronized from remote registries. If the content was synced together with signatures, upon signing task completion new signatures will be @@ -306,15 +309,15 @@ by proviging ``tags_list`` option to the call. Note that ``manifest lists`` are not signed, instead all the image manifests that manifest lists contain, are signed. -Manage Signatures via the Extensions API -======================================== +Manage Atomic Container Signatures via the Extensions API +========================================================= This API exposes an endpoint for reading and writing image signatures. Users should configure the sigstore section in the `registries.d file `_ accordingly to benefit from the API. -Reading Image Signatures ------------------------- +Reading Atomic Container Image Signatures +----------------------------------------- To read existing signatures, issue the following GET request:: @@ -323,8 +326,8 @@ To read existing signatures, issue the following GET request:: Signatures are retrieved by container clients automatically if the policy requires so. The policy is defined in the file ``/etc/containers/policy.json``. -Writing Image Signatures ------------------------- +Writing Atomic Container Image Signatures +----------------------------------------- To add a new signature to an image, execute the following PUT request:: diff --git a/docs/workflows/sync.rst b/docs/workflows/sync.rst index ce1ac1b05..b86351a40 100644 --- a/docs/workflows/sync.rst +++ b/docs/workflows/sync.rst @@ -72,9 +72,9 @@ the ``sigstore`` field when creating a Remote. .. note:: Some registries provide docker API extensions for ``atomic container signature`` type only, or - have ``cosign`` type signatures that are stored as a separate OCI artifact in a registry. - Pulp will automatically sync signatures provided via the docker API extension. At the moment, - `cosign` signatures are not supported. + have ``cosign`` type signatures that are stored as a separate OCI image in a registry. + Pulp will automatically sync signatures provided via the docker API extension or cosign + signatures stored as an OCI image. Reference: `Container Remote Usage <../restapi.html#tag/Remotes:-Container>`_ diff --git a/pulp_container/app/__init__.py b/pulp_container/app/__init__.py index 2398ffad1..2aab1b3ff 100644 --- a/pulp_container/app/__init__.py +++ b/pulp_container/app/__init__.py @@ -8,3 +8,15 @@ class PulpContainerPluginAppConfig(PulpPluginAppConfig): label = "container" version = "2.15.0.dev" python_package_name = "pulp-container" + + def ready(self): + super().ready() + self.register_registry_types() + + def register_registry_types(self): + # circular import avoidance + from pulp_container import constants + from django.conf import settings + + for media_type, layer_types in settings.ADDITIONAL_OCI_ARTIFACT_TYPES.items(): + constants.register_well_known_types(media_type, layer_types) diff --git a/pulp_container/app/json_schemas.py b/pulp_container/app/json_schemas.py index 7a2c5bbaa..587b166a8 100644 --- a/pulp_container/app/json_schemas.py +++ b/pulp_container/app/json_schemas.py @@ -1,10 +1,18 @@ -from pulp_container.constants import BLOB_CONTENT_TYPE, MEDIA_TYPE, SIGNATURE_TYPE +from pulp_container.constants import ( + ALLOWED_ARTIFACT_TYPES, + ALLOWED_BLOB_CONTENT_TYPES, + BLOB_CONTENT_TYPE, + MEDIA_TYPE, + OCI_BLOB_MEDIA_TYPE, + SIGNATURE_TYPE, +) def get_descriptor_schema( allowed_media_types, additional_properties=None, additional_required=None ): """Return a concrete descriptor schema for manifests.""" + properties = { "mediaType": {"type": "string", "enum": allowed_media_types}, "size": {"type": "number"}, @@ -60,6 +68,7 @@ def get_descriptor_schema( "required": ["schemaVersion", "manifests"], } + OCI_MANIFEST_SCHEMA = { "type": "object", "properties": { @@ -68,24 +77,16 @@ def get_descriptor_schema( "type": "string", "enum": [MEDIA_TYPE.MANIFEST_OCI], }, - "config": get_descriptor_schema([MEDIA_TYPE.CONFIG_BLOB_OCI]), + "config": get_descriptor_schema(ALLOWED_ARTIFACT_TYPES), "layers": { "type": "array", - "items": get_descriptor_schema( - [ - MEDIA_TYPE.REGULAR_BLOB_OCI_TAR, - MEDIA_TYPE.REGULAR_BLOB_OCI_TAR_GZIP, - MEDIA_TYPE.REGULAR_BLOB_OCI_TAR_ZSTD, - MEDIA_TYPE.FOREIGN_BLOB_OCI_TAR, - MEDIA_TYPE.FOREIGN_BLOB_OCI_TAR_GZIP, - MEDIA_TYPE.FOREIGN_BLOB_OCI_TAR_ZSTD, - ] - ), + "items": get_descriptor_schema(ALLOWED_BLOB_CONTENT_TYPES), }, }, "required": ["schemaVersion", "config", "layers"], } + DOCKER_MANIFEST_LIST_V2_SCHEMA = { "type": "object", "properties": { @@ -134,6 +135,7 @@ def get_descriptor_schema( "required": ["schemaVersion", "mediaType", "manifests"], } + DOCKER_MANIFEST_V2_SCHEMA = { "type": "object", "properties": { @@ -176,6 +178,7 @@ def get_descriptor_schema( "required": ["schemaVersion", "mediaType", "config", "layers"], } + DOCKER_MANIFEST_V1_SCHEMA = { "type": "object", "properties": { @@ -217,6 +220,7 @@ def get_descriptor_schema( "required": ["tag", "name", "fsLayers", "history"], } + SIGNATURE_SCHEMA = { "title": "Atomic Container Signature", "description": "JSON Schema Validation for the Signature Payload", diff --git a/pulp_container/app/schema_convert.py b/pulp_container/app/schema_convert.py index 581f5e1c2..c75e2c984 100644 --- a/pulp_container/app/schema_convert.py +++ b/pulp_container/app/schema_convert.py @@ -94,7 +94,7 @@ class Schema2toSchema1Converter: and call convert() to obtain the signed manifest, as a JSON-encoded string. """ - EMPTY_LAYER = "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" + EMPTY_BLOB = "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" def __init__(self, manifest, config_layer, name, tag): """ @@ -171,7 +171,7 @@ def _compute_fs_layers(self): curr_uncompressed_dig = next(diff_ids) for curr_hist in config_layer_history: if curr_hist.get("empty_layer"): - layer_id = self.EMPTY_LAYER + layer_id = self.EMPTY_BLOB uncompressed_dig = None else: layer_id = curr_compressed_dig @@ -180,7 +180,7 @@ def _compute_fs_layers(self): curr_compressed_dig = next(layers)["digest"] curr_uncompressed_dig = next(diff_ids) except StopIteration: - curr_compressed_dig = self.EMPTY_LAYER + curr_compressed_dig = self.EMPTY_BLOB curr_uncompressed_dig = None fs_layers.append(FS_Layer(layer_id, uncompressed_dig, curr_hist)) return fs_layers diff --git a/pulp_container/app/settings.py b/pulp_container/app/settings.py index a1e413b24..ebec01167 100644 --- a/pulp_container/app/settings.py +++ b/pulp_container/app/settings.py @@ -2,3 +2,41 @@ "dynaconf_merge_unique": True, "reusable_conditions": ["pulp_container.app.global_access_conditions"], } + +ADDITIONAL_OCI_ARTIFACT_TYPES = { + "application/vnd.oci.image.config.v1+json": [ + # cosign signing and attestations + "application/vnd.dev.cosign.simplesigning.v1+json", + "application/vnd.dsse.envelope.v1+json", + # cosign SBOMS spdx and cyclonedx + "text/spdx", + "text/spdx+xml", + "text/spdx+json", + "application/vnd.cyclonedx", + "application/vnd.cyclonedx+xml", + "application/vnd.cyclonedx+json", + # syft SBOMS + "application/vnd.syft+json", + # cosign in-toto attestations + "application/vnd.in-toto+json", + ], + # helm + "application/vnd.cncf.helm.config.v1+json": [ + "application/tar+gzip", + "application/vnd.cncf.helm.chart.content.v1.tar+gzip", + "application/vnd.cncf.helm.chart.provenance.v1.prov", + ], + # source containers + "application/vnd.oci.source.image.config.v1+json": [ + "application/vnd.oci.image.layer.v1.tar+gzip" + ], + # conftest policies + "application/vnd.unknown.config.v1+json": [ + "application/vnd.cncf.openpolicyagent.policy.layer.v1+rego", + "application/vnd.cncf.openpolicyagent.data.layer.v1+json", + ], + # singularity + "application/vnd.sylabs.sif.config.v1+json": [ + "application/vnd.sylabs.sif.layer.v1.sif", + ], +} diff --git a/pulp_container/app/tasks/sync_stages.py b/pulp_container/app/tasks/sync_stages.py index 9f406dfa7..5fedd67d9 100644 --- a/pulp_container/app/tasks/sync_stages.py +++ b/pulp_container/app/tasks/sync_stages.py @@ -133,15 +133,9 @@ async def run(self): to_download = [] BATCH_SIZE = 500 + # it can be wether a separate sigstore location or registry with extended signatures API signature_source = await self.get_signature_source() - if signature_source is None and self.signed_only: - raise ValueError( - "It is requested to sync only signed content but no sigstore URL is " - "provided. Please configure a `sigstore` on your Remote or set " - "`signed_only` to `False` for your sync request." - ) - async with ProgressReport( message="Downloading tag list", code="sync.downloading.tag_list", total=1 ) as pb: @@ -175,11 +169,25 @@ async def run(self): saved_artifact, content_data, raw_data, response = await artifact digest = response.artifact_attributes["sha256"] + tag_name = response.url.split("/")[-1] + + # Look for cosign signatures + # cosign signature has a tag convention 'sha256-1234.sig' + if self.signed_only and not signature_source: + if not tag_name.enswith(".sig") and digest + ".sig" not in tag_list: + # skip this tag, there is no corresponding signature + log.info( + "The unsigned image {digest} can't be synced " + "due to a requirement to sync signed content " + "only.".format(digest=digest) + ) + # Count the skipped tagks as parsed too. + await pb_parsed_tags.aincrement() + continue media_type = determine_media_type(content_data, response) validate_manifest(content_data, media_type, digest) - tag_name = response.url.split("/")[-1] tag_dc = DeclarativeContent(Tag(name=tag_name)) if media_type in (MEDIA_TYPE.MANIFEST_LIST, MEDIA_TYPE.INDEX_OCI): diff --git a/pulp_container/app/utils.py b/pulp_container/app/utils.py index e33cc40a6..47ac7e77e 100644 --- a/pulp_container/app/utils.py +++ b/pulp_container/app/utils.py @@ -171,6 +171,7 @@ def determine_media_type_from_json(content_data): else: if config := content_data.get("config"): config_media_type = config.get("mediaType") + # TODO add here additional config layers if config_media_type == MEDIA_TYPE.CONFIG_BLOB_OCI: return MEDIA_TYPE.MANIFEST_OCI else: diff --git a/pulp_container/constants.py b/pulp_container/constants.py index 333a8f669..c67519af2 100644 --- a/pulp_container/constants.py +++ b/pulp_container/constants.py @@ -30,6 +30,18 @@ LIST=[MEDIA_TYPE.MANIFEST_LIST, MEDIA_TYPE.INDEX_OCI], ) +OCI_BLOB_DISTRIBUTABLE_MEDIA_TYPE = [ + MEDIA_TYPE.REGULAR_BLOB_OCI_TAR, + MEDIA_TYPE.REGULAR_BLOB_OCI_TAR_GZIP, + MEDIA_TYPE.REGULAR_BLOB_OCI_TAR_ZSTD, +] +OCI_BLOB_NON_DISTRIBUTABLE_MEDIA_TYPE = [ + MEDIA_TYPE.FOREIGN_BLOB_OCI_TAR, + MEDIA_TYPE.FOREIGN_BLOB_OCI_TAR_GZIP, + MEDIA_TYPE.FOREIGN_BLOB_OCI_TAR_ZSTD, +] +OCI_BLOB_MEDIA_TYPE = OCI_BLOB_DISTRIBUTABLE_MEDIA_TYPE + OCI_BLOB_NON_DISTRIBUTABLE_MEDIA_TYPE + EMPTY_BLOB = "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4" BLOB_CONTENT_TYPE = "application/octet-stream" SIGNATURE_TYPE = SimpleNamespace( @@ -43,3 +55,16 @@ SIGNATURE_PAYLOAD_MAX_SIZE = 4 * MEGABYTE SIGNATURE_API_EXTENSION_VERSION = 2 + + +ALLOWED_ARTIFACT_TYPES = [MEDIA_TYPE.CONFIG_BLOB_OCI] +ALLOWED_BLOB_CONTENT_TYPES = OCI_BLOB_MEDIA_TYPE + + +def register_well_known_types(artifact_config_type, artifact_layer_types): + if artifact_config_type not in ALLOWED_ARTIFACT_TYPES: + ALLOWED_ARTIFACT_TYPES.append(artifact_config_type) + + for layer_type in artifact_layer_types: + if layer_type not in ALLOWED_BLOB_CONTENT_TYPES: + ALLOWED_BLOB_CONTENT_TYPES.append(layer_type) diff --git a/pulp_container/tests/unit/test_convert.py b/pulp_container/tests/unit/test_convert.py index 611e1531d..fa4b4bdd3 100644 --- a/pulp_container/tests/unit/test_convert.py +++ b/pulp_container/tests/unit/test_convert.py @@ -14,13 +14,13 @@ class Test(TestCase): def test_convert(self): """Test schema converter on a known manifest""" cnv = schema_convert.Schema2toSchema1Converter( - MANIFEST, CONFIG_LAYER, "test-repo", "tes-tag" + MANIFEST, CONFIG_BLOB, "test-repo", "tes-tag" ) converted_mf, signed_mf = cnv.convert() compare_manifests(converted_mf, signed_mf) validate_signature(signed_mf) - empty = dict(blobSum=cnv.EMPTY_LAYER) + empty = dict(blobSum=cnv.EMPTY_BLOB) assert [ dict(blobSum="sha256:layer1"), empty, @@ -34,7 +34,7 @@ def test_manifest_with_foreign_layers_conversion(self): """Test if the conversion of a manifest with foreign layers fails gracefully""" try: schema_convert.Schema2toSchema1Converter( - MANIFEST_WITH_FOREIGN_LAYERS, CONFIG_LAYER, "test-repo", "tes-tag" + MANIFEST_WITH_FOREIGN_BLOBS, CONFIG_BLOB, "test-repo", "tes-tag" ) except ValueError: pass @@ -44,10 +44,10 @@ def test_manifest_with_foreign_layers_conversion(self): def test_compute_layers(self): """Test that computing the layers produces the expected data""" cnv = schema_convert.Schema2toSchema1Converter( - MANIFEST, CONFIG_LAYER, "test-repo", "tes-tag" + MANIFEST, CONFIG_BLOB, "test-repo", "tes-tag" ) cnv.compute_layers() - empty = dict(blobSum=cnv.EMPTY_LAYER) + empty = dict(blobSum=cnv.EMPTY_BLOB) assert [ dict(blobSum="sha256:layer1"), empty, @@ -118,7 +118,7 @@ def validate_signature(signed_mf): MANIFEST = dict(schemaVersion=2, layers=[dict(digest="sha256:base"), dict(digest="sha256:layer1")]) -MANIFEST_WITH_FOREIGN_LAYERS = dict( +MANIFEST_WITH_FOREIGN_BLOBS = dict( schemaVersion=2, layers=[ dict( @@ -129,7 +129,7 @@ def validate_signature(signed_mf): ], ) -CONFIG_LAYER = dict( +CONFIG_BLOB = dict( architecture="amd64", author="Mihai Ibanescu ", config=dict(Hostname="decafbad", Cmd=["/bin/bash"]),