From 1b290f0c054cf5bd037c858f62ad58f2eda8cdf9 Mon Sep 17 00:00:00 2001 From: Yuichi Tanaka Date: Tue, 30 Apr 2024 03:02:14 +0000 Subject: [PATCH 1/5] Change the config layer of the feature manifest to a empty descriptor This commit changes the config layer of the feature manifest to a empty descriptor according to the OCI image manifest specification to to increase the available feature registry. Some registries that implement the OCI image manifest specification, such as AWS ECR, do not support pushing empty content (size 0). However, the manifest may contain layers with no content. Therefore, the OCI Image Manifest specification specifies an empty descriptor of non-zero size to represent a layer with no content. Currently, the devcontainer feature manifest specifies the config layer as an empty content (size 0) layer. If the devcontainer feature manifest conforms to this specification, it can also available registries that cannot publish empty content (size 0). Specification: https://github.com/opencontainers/image-spec/blob/8f3820ccf8f65db8744e626df17fe8a64462aece/manifest.md#guidelines-for-artifact-usage Note: If the config is set to an empty descriptor, the manifest media type must be defined in the artifactType. --- .../containerCollectionsOCI.ts | 16 ++++++++++++++-- .../containerCollectionsOCIPush.ts | 15 ++++++++------- .../containerFeaturesOCIPush.test.ts | 2 +- .../container-features/featureAdvisories.test.ts | 1 + .../container-features/featureHelpers.test.ts | 3 +++ src/test/imageMetadata.test.ts | 1 + 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/spec-configuration/containerCollectionsOCI.ts b/src/spec-configuration/containerCollectionsOCI.ts index 737789059..dc2cd8897 100644 --- a/src/spec-configuration/containerCollectionsOCI.ts +++ b/src/spec-configuration/containerCollectionsOCI.ts @@ -13,6 +13,14 @@ export const DEVCONTAINER_MANIFEST_MEDIATYPE = 'application/vnd.devcontainers'; export const DEVCONTAINER_TAR_LAYER_MEDIATYPE = 'application/vnd.devcontainers.layer.v1+tar'; export const DEVCONTAINER_COLLECTION_LAYER_MEDIATYPE = 'application/vnd.devcontainers.collection.layer.v1+json'; +// Empty Descriptor specified in OCI Image Specification. +// Following Spec: https://github.com/opencontainers/image-spec/blob/v1.1.0/manifest.md#guidance-for-an-empty-descriptor +export const OCI_EMPTY_DESCRIPTOR = { + mediaType: 'application/vnd.oci.empty.v1+json', + digest: 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a', + size: 2, + dataBytes: Buffer.from('{}') +}; export interface CommonParams { env: NodeJS.ProcessEnv; @@ -60,6 +68,7 @@ export interface OCILayer { export interface OCIManifest { digest?: string; schemaVersion: number; + artifactType?: string; mediaType: string; config: { digest: string; @@ -295,10 +304,13 @@ export async function fetchOCIManifestIfExists(params: CommonParams, ref: OCIRef return; } + // If the config layer is an empty descriptor, the media type of the manifest must be defined in the artifactType. + // Specification: https://github.com/opencontainers/image-spec/blob/8f3820ccf8f65db8744e626df17fe8a64462aece/manifest.md#guidelines-for-artifact-usage const { manifestObj } = manifestContainer; + const manifestMediaType = manifestObj.config.mediaType === OCI_EMPTY_DESCRIPTOR.mediaType ? manifestObj.artifactType : manifestObj.config.mediaType; - if (manifestObj.config.mediaType !== DEVCONTAINER_MANIFEST_MEDIATYPE) { - output.write(`(!) Unexpected manifest media type: ${manifestObj.config.mediaType}`, LogLevel.Error); + if (manifestMediaType !== DEVCONTAINER_MANIFEST_MEDIATYPE) { + output.write(`(!) Unexpected manifest media type: ${manifestMediaType}`, LogLevel.Error); return undefined; } diff --git a/src/spec-configuration/containerCollectionsOCIPush.ts b/src/spec-configuration/containerCollectionsOCIPush.ts index adc267558..88350a6a5 100644 --- a/src/spec-configuration/containerCollectionsOCIPush.ts +++ b/src/spec-configuration/containerCollectionsOCIPush.ts @@ -4,7 +4,7 @@ import * as crypto from 'crypto'; import { delay } from '../spec-common/async'; import { Log, LogLevel } from '../spec-utils/log'; import { isLocalFile } from '../spec-utils/pfs'; -import { DEVCONTAINER_COLLECTION_LAYER_MEDIATYPE, DEVCONTAINER_TAR_LAYER_MEDIATYPE, fetchOCIManifestIfExists, OCICollectionRef, OCILayer, OCIManifest, OCIRef, CommonParams, ManifestContainer } from './containerCollectionsOCI'; +import { OCI_EMPTY_DESCRIPTOR, DEVCONTAINER_MANIFEST_MEDIATYPE, DEVCONTAINER_COLLECTION_LAYER_MEDIATYPE, DEVCONTAINER_TAR_LAYER_MEDIATYPE, fetchOCIManifestIfExists, OCICollectionRef, OCILayer, OCIManifest, OCIRef, CommonParams, ManifestContainer } from './containerCollectionsOCI'; import { requestEnsureAuthenticated } from './httpOCIRegistry'; // (!) Entrypoint function to push a single feature/template to a registry. @@ -44,8 +44,8 @@ export async function pushOCIFeatureOrTemplate(params: CommonParams, ociRef: OCI { name: 'configLayer', digest: manifest.manifestObj.config.digest, - contents: Buffer.alloc(0), size: manifest.manifestObj.config.size, + contents: OCI_EMPTY_DESCRIPTOR.dataBytes, }, { name: 'tgzLayer', @@ -119,7 +119,7 @@ export async function pushCollectionMetadata(params: CommonParams, collectionRef name: 'configLayer', digest: manifest.manifestObj.config.digest, size: manifest.manifestObj.config.size, - contents: Buffer.alloc(0), + contents: OCI_EMPTY_DESCRIPTOR.dataBytes, }, { name: 'collectionLayer', @@ -383,15 +383,16 @@ export async function calculateManifestAndContentDigest(output: Log, ociRef: OCI // A canonical manifest digest is the sha256 hash of the JSON representation of the manifest, without the signature content. // See: https://docs.docker.com/registry/spec/api/#content-digests // Below is an example of a serialized manifest that should resolve to '9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3' - // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]} + // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.devcontainers","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]} let manifest: OCIManifest = { schemaVersion: 2, mediaType: 'application/vnd.oci.image.manifest.v1+json', + artifactType: DEVCONTAINER_MANIFEST_MEDIATYPE, config: { - mediaType: 'application/vnd.devcontainers', - digest: 'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', // A zero byte digest for the devcontainer mediaType. - size: 0 + mediaType: OCI_EMPTY_DESCRIPTOR.mediaType, + digest: OCI_EMPTY_DESCRIPTOR.digest, + size: OCI_EMPTY_DESCRIPTOR.size, }, layers: [ dataLayer diff --git a/src/test/container-features/containerFeaturesOCIPush.test.ts b/src/test/container-features/containerFeaturesOCIPush.test.ts index 3a648cac2..6aa465bcd 100644 --- a/src/test/container-features/containerFeaturesOCIPush.test.ts +++ b/src/test/container-features/containerFeaturesOCIPush.test.ts @@ -341,7 +341,7 @@ describe('Test OCI Push Helper Functions', function () { const { contentDigest, manifestBuffer } = manifestContainer; // 'Expected' is taken from intermediate value in oras reference implementation, before hash calculation - assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); + assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.devcontainers","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); // This is the canonical digest of the manifest assert.strictEqual('sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3', contentDigest); diff --git a/src/test/container-features/featureAdvisories.test.ts b/src/test/container-features/featureAdvisories.test.ts index 21dd37527..d27a5e19b 100644 --- a/src/test/container-features/featureAdvisories.test.ts +++ b/src/test/container-features/featureAdvisories.test.ts @@ -88,6 +88,7 @@ function getFeaturesConfig(featureId: string, featureConfigVersion: string, feat featureRef: getRef(output, `${featureId}:${featureConfigVersion}`)!, manifest: { schemaVersion: 1, + artifactType: '', mediaType: '', config: { digest: '', diff --git a/src/test/container-features/featureHelpers.test.ts b/src/test/container-features/featureHelpers.test.ts index fb3c079ed..e6091c933 100644 --- a/src/test/container-features/featureHelpers.test.ts +++ b/src/test/container-features/featureHelpers.test.ts @@ -638,6 +638,7 @@ chmod +x ./install.sh }, manifest: { schemaVersion: 1, + artifactType: '', mediaType: '', config: { digest: '', @@ -722,6 +723,7 @@ chmod +x ./install.sh }, manifest: { schemaVersion: 1, + artifactType: '', mediaType: '', config: { digest: '', @@ -809,6 +811,7 @@ chmod +x ./install.sh }, manifest: { schemaVersion: 1, + artifactType: '', mediaType: '', config: { digest: '', diff --git a/src/test/imageMetadata.test.ts b/src/test/imageMetadata.test.ts index 24045ac4e..ed2469c4f 100644 --- a/src/test/imageMetadata.test.ts +++ b/src/test/imageMetadata.test.ts @@ -563,6 +563,7 @@ function getFeaturesConfig(features: Feature[]): FeaturesConfig { }, manifest: { schemaVersion: 1, + artifactType: '', mediaType: '', config: { digest: '', From fa5761c956f8fda56cbb02dc16609a32f5090043 Mon Sep 17 00:00:00 2001 From: Yuichi Tanaka Date: Fri, 10 May 2024 16:20:04 +0000 Subject: [PATCH 2/5] Revert "Change the config layer of the feature manifest to a empty descriptor" This reverts commit 1b290f0c054cf5bd037c858f62ad58f2eda8cdf9. --- .../containerCollectionsOCI.ts | 16 ++-------------- .../containerCollectionsOCIPush.ts | 15 +++++++-------- .../containerFeaturesOCIPush.test.ts | 2 +- .../container-features/featureAdvisories.test.ts | 1 - .../container-features/featureHelpers.test.ts | 3 --- src/test/imageMetadata.test.ts | 1 - 6 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/spec-configuration/containerCollectionsOCI.ts b/src/spec-configuration/containerCollectionsOCI.ts index dc2cd8897..737789059 100644 --- a/src/spec-configuration/containerCollectionsOCI.ts +++ b/src/spec-configuration/containerCollectionsOCI.ts @@ -13,14 +13,6 @@ export const DEVCONTAINER_MANIFEST_MEDIATYPE = 'application/vnd.devcontainers'; export const DEVCONTAINER_TAR_LAYER_MEDIATYPE = 'application/vnd.devcontainers.layer.v1+tar'; export const DEVCONTAINER_COLLECTION_LAYER_MEDIATYPE = 'application/vnd.devcontainers.collection.layer.v1+json'; -// Empty Descriptor specified in OCI Image Specification. -// Following Spec: https://github.com/opencontainers/image-spec/blob/v1.1.0/manifest.md#guidance-for-an-empty-descriptor -export const OCI_EMPTY_DESCRIPTOR = { - mediaType: 'application/vnd.oci.empty.v1+json', - digest: 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a', - size: 2, - dataBytes: Buffer.from('{}') -}; export interface CommonParams { env: NodeJS.ProcessEnv; @@ -68,7 +60,6 @@ export interface OCILayer { export interface OCIManifest { digest?: string; schemaVersion: number; - artifactType?: string; mediaType: string; config: { digest: string; @@ -304,13 +295,10 @@ export async function fetchOCIManifestIfExists(params: CommonParams, ref: OCIRef return; } - // If the config layer is an empty descriptor, the media type of the manifest must be defined in the artifactType. - // Specification: https://github.com/opencontainers/image-spec/blob/8f3820ccf8f65db8744e626df17fe8a64462aece/manifest.md#guidelines-for-artifact-usage const { manifestObj } = manifestContainer; - const manifestMediaType = manifestObj.config.mediaType === OCI_EMPTY_DESCRIPTOR.mediaType ? manifestObj.artifactType : manifestObj.config.mediaType; - if (manifestMediaType !== DEVCONTAINER_MANIFEST_MEDIATYPE) { - output.write(`(!) Unexpected manifest media type: ${manifestMediaType}`, LogLevel.Error); + if (manifestObj.config.mediaType !== DEVCONTAINER_MANIFEST_MEDIATYPE) { + output.write(`(!) Unexpected manifest media type: ${manifestObj.config.mediaType}`, LogLevel.Error); return undefined; } diff --git a/src/spec-configuration/containerCollectionsOCIPush.ts b/src/spec-configuration/containerCollectionsOCIPush.ts index 88350a6a5..adc267558 100644 --- a/src/spec-configuration/containerCollectionsOCIPush.ts +++ b/src/spec-configuration/containerCollectionsOCIPush.ts @@ -4,7 +4,7 @@ import * as crypto from 'crypto'; import { delay } from '../spec-common/async'; import { Log, LogLevel } from '../spec-utils/log'; import { isLocalFile } from '../spec-utils/pfs'; -import { OCI_EMPTY_DESCRIPTOR, DEVCONTAINER_MANIFEST_MEDIATYPE, DEVCONTAINER_COLLECTION_LAYER_MEDIATYPE, DEVCONTAINER_TAR_LAYER_MEDIATYPE, fetchOCIManifestIfExists, OCICollectionRef, OCILayer, OCIManifest, OCIRef, CommonParams, ManifestContainer } from './containerCollectionsOCI'; +import { DEVCONTAINER_COLLECTION_LAYER_MEDIATYPE, DEVCONTAINER_TAR_LAYER_MEDIATYPE, fetchOCIManifestIfExists, OCICollectionRef, OCILayer, OCIManifest, OCIRef, CommonParams, ManifestContainer } from './containerCollectionsOCI'; import { requestEnsureAuthenticated } from './httpOCIRegistry'; // (!) Entrypoint function to push a single feature/template to a registry. @@ -44,8 +44,8 @@ export async function pushOCIFeatureOrTemplate(params: CommonParams, ociRef: OCI { name: 'configLayer', digest: manifest.manifestObj.config.digest, + contents: Buffer.alloc(0), size: manifest.manifestObj.config.size, - contents: OCI_EMPTY_DESCRIPTOR.dataBytes, }, { name: 'tgzLayer', @@ -119,7 +119,7 @@ export async function pushCollectionMetadata(params: CommonParams, collectionRef name: 'configLayer', digest: manifest.manifestObj.config.digest, size: manifest.manifestObj.config.size, - contents: OCI_EMPTY_DESCRIPTOR.dataBytes, + contents: Buffer.alloc(0), }, { name: 'collectionLayer', @@ -383,16 +383,15 @@ export async function calculateManifestAndContentDigest(output: Log, ociRef: OCI // A canonical manifest digest is the sha256 hash of the JSON representation of the manifest, without the signature content. // See: https://docs.docker.com/registry/spec/api/#content-digests // Below is an example of a serialized manifest that should resolve to '9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3' - // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.devcontainers","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]} + // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]} let manifest: OCIManifest = { schemaVersion: 2, mediaType: 'application/vnd.oci.image.manifest.v1+json', - artifactType: DEVCONTAINER_MANIFEST_MEDIATYPE, config: { - mediaType: OCI_EMPTY_DESCRIPTOR.mediaType, - digest: OCI_EMPTY_DESCRIPTOR.digest, - size: OCI_EMPTY_DESCRIPTOR.size, + mediaType: 'application/vnd.devcontainers', + digest: 'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', // A zero byte digest for the devcontainer mediaType. + size: 0 }, layers: [ dataLayer diff --git a/src/test/container-features/containerFeaturesOCIPush.test.ts b/src/test/container-features/containerFeaturesOCIPush.test.ts index 6aa465bcd..3a648cac2 100644 --- a/src/test/container-features/containerFeaturesOCIPush.test.ts +++ b/src/test/container-features/containerFeaturesOCIPush.test.ts @@ -341,7 +341,7 @@ describe('Test OCI Push Helper Functions', function () { const { contentDigest, manifestBuffer } = manifestContainer; // 'Expected' is taken from intermediate value in oras reference implementation, before hash calculation - assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.devcontainers","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); + assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); // This is the canonical digest of the manifest assert.strictEqual('sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3', contentDigest); diff --git a/src/test/container-features/featureAdvisories.test.ts b/src/test/container-features/featureAdvisories.test.ts index d27a5e19b..21dd37527 100644 --- a/src/test/container-features/featureAdvisories.test.ts +++ b/src/test/container-features/featureAdvisories.test.ts @@ -88,7 +88,6 @@ function getFeaturesConfig(featureId: string, featureConfigVersion: string, feat featureRef: getRef(output, `${featureId}:${featureConfigVersion}`)!, manifest: { schemaVersion: 1, - artifactType: '', mediaType: '', config: { digest: '', diff --git a/src/test/container-features/featureHelpers.test.ts b/src/test/container-features/featureHelpers.test.ts index e6091c933..fb3c079ed 100644 --- a/src/test/container-features/featureHelpers.test.ts +++ b/src/test/container-features/featureHelpers.test.ts @@ -638,7 +638,6 @@ chmod +x ./install.sh }, manifest: { schemaVersion: 1, - artifactType: '', mediaType: '', config: { digest: '', @@ -723,7 +722,6 @@ chmod +x ./install.sh }, manifest: { schemaVersion: 1, - artifactType: '', mediaType: '', config: { digest: '', @@ -811,7 +809,6 @@ chmod +x ./install.sh }, manifest: { schemaVersion: 1, - artifactType: '', mediaType: '', config: { digest: '', diff --git a/src/test/imageMetadata.test.ts b/src/test/imageMetadata.test.ts index ed2469c4f..24045ac4e 100644 --- a/src/test/imageMetadata.test.ts +++ b/src/test/imageMetadata.test.ts @@ -563,7 +563,6 @@ function getFeaturesConfig(features: Feature[]): FeaturesConfig { }, manifest: { schemaVersion: 1, - artifactType: '', mediaType: '', config: { digest: '', From a83bb7ec2896ba078fba3ae0fd96806e2c9b849b Mon Sep 17 00:00:00 2001 From: Yuichi Tanaka Date: Fri, 10 May 2024 16:56:14 +0000 Subject: [PATCH 3/5] Changing the content of the config layer to an empty object The content of the configuration layer of the feature manifest is specified as empty content (size 0). This is because there is no content to reference in the config layer content of the feature manifest. However, some registries that implement the OCI image manifest specification, such as AWS ECR, do not support pushing empty content (size 0). Therefore, this commit changes the content of the config layer of the feature manifestto indicate that empty objects have no content to reference. --- .../containerCollectionsOCIPush.ts | 12 ++++++------ .../containerFeaturesOCIPush.test.ts | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/spec-configuration/containerCollectionsOCIPush.ts b/src/spec-configuration/containerCollectionsOCIPush.ts index adc267558..dbdf620dc 100644 --- a/src/spec-configuration/containerCollectionsOCIPush.ts +++ b/src/spec-configuration/containerCollectionsOCIPush.ts @@ -44,8 +44,8 @@ export async function pushOCIFeatureOrTemplate(params: CommonParams, ociRef: OCI { name: 'configLayer', digest: manifest.manifestObj.config.digest, - contents: Buffer.alloc(0), size: manifest.manifestObj.config.size, + contents: Buffer.from('{}'), }, { name: 'tgzLayer', @@ -119,7 +119,7 @@ export async function pushCollectionMetadata(params: CommonParams, collectionRef name: 'configLayer', digest: manifest.manifestObj.config.digest, size: manifest.manifestObj.config.size, - contents: Buffer.alloc(0), + contents: Buffer.from('{}'), }, { name: 'collectionLayer', @@ -382,16 +382,16 @@ async function postUploadSessionId(params: CommonParams, ociRef: OCIRef | OCICol export async function calculateManifestAndContentDigest(output: Log, ociRef: OCIRef | OCICollectionRef, dataLayer: OCILayer, annotations: { [key: string]: string } | undefined): Promise { // A canonical manifest digest is the sha256 hash of the JSON representation of the manifest, without the signature content. // See: https://docs.docker.com/registry/spec/api/#content-digests - // Below is an example of a serialized manifest that should resolve to '9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3' - // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]} + // Below is an example of a serialized manifest that should resolve to '92612b601337f2aa01bba176a13991b03b156c3edf2c18d3d9776e73ccc273a4' + // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]} let manifest: OCIManifest = { schemaVersion: 2, mediaType: 'application/vnd.oci.image.manifest.v1+json', config: { mediaType: 'application/vnd.devcontainers', - digest: 'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', // A zero byte digest for the devcontainer mediaType. - size: 0 + digest: 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a', // A empty json byte digest for the devcontainer mediaType. + size: 2 }, layers: [ dataLayer diff --git a/src/test/container-features/containerFeaturesOCIPush.test.ts b/src/test/container-features/containerFeaturesOCIPush.test.ts index 3a648cac2..d80d99bc1 100644 --- a/src/test/container-features/containerFeaturesOCIPush.test.ts +++ b/src/test/container-features/containerFeaturesOCIPush.test.ts @@ -341,14 +341,14 @@ describe('Test OCI Push Helper Functions', function () { const { contentDigest, manifestBuffer } = manifestContainer; // 'Expected' is taken from intermediate value in oras reference implementation, before hash calculation - assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); + assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); // This is the canonical digest of the manifest - assert.strictEqual('sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3', contentDigest); + assert.strictEqual('sha256:92612b601337f2aa01bba176a13991b03b156c3edf2c18d3d9776e73ccc273a4', contentDigest); }); it('Can fetch an artifact from a digest reference', async () => { - const manifest = await fetchOCIFeatureManifestIfExistsFromUserIdentifier({ output, env: process.env }, 'ghcr.io/codspace/features/go', 'sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3'); + const manifest = await fetchOCIFeatureManifestIfExistsFromUserIdentifier({ output, env: process.env }, 'ghcr.io/codspace/features/go', 'sha256:92612b601337f2aa01bba176a13991b03b156c3edf2c18d3d9776e73ccc273a4'); assert.strictEqual(manifest?.manifestObj.layers[0].annotations['org.opencontainers.image.title'], 'go.tgz'); }); @@ -362,7 +362,7 @@ describe('Test OCI Push Helper Functions', function () { const tarLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5'); assert.isTrue(tarLayerBlobExists); - const configLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); + const configLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'); assert.isTrue(configLayerBlobExists); const randomStringDoesNotExist = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:41af286dc0b172ed2f1ca934fd2278de4a1192302ffa07087cea2682e7d372e3'); From 192abc4e3d90f821600b9bb3afc5a94f167dedd7 Mon Sep 17 00:00:00 2001 From: Yuichi Tanaka Date: Sat, 1 Jun 2024 02:11:13 +0000 Subject: [PATCH 4/5] Revert the test of push against the GitHub registry and fix/add the comments --- .../containerFeaturesOCIPush.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/test/container-features/containerFeaturesOCIPush.test.ts b/src/test/container-features/containerFeaturesOCIPush.test.ts index d80d99bc1..91d4a8a57 100644 --- a/src/test/container-features/containerFeaturesOCIPush.test.ts +++ b/src/test/container-features/containerFeaturesOCIPush.test.ts @@ -305,7 +305,10 @@ registry`; }); // NOTE: -// Test depends on https://github.com/codspace/features/pkgs/container/features%2Fgo/29819216?tag=1 +// Test depends on https://github.com/codspace/features/pkgs/container/features%2Fgo/29819216?tag=1.0.10 +// TODO: +// This test does not take into account the changes in #815. +// Therefore, this test needs to be changed to depend on the feature that was pushed after the #815 change was merged. describe('Test OCI Push Helper Functions', function () { this.timeout('10s'); it('Generates the correct tgz manifest layer', async () => { @@ -341,14 +344,14 @@ describe('Test OCI Push Helper Functions', function () { const { contentDigest, manifestBuffer } = manifestContainer; // 'Expected' is taken from intermediate value in oras reference implementation, before hash calculation - assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); + assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); // This is the canonical digest of the manifest - assert.strictEqual('sha256:92612b601337f2aa01bba176a13991b03b156c3edf2c18d3d9776e73ccc273a4', contentDigest); + assert.strictEqual('sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3', contentDigest); }); it('Can fetch an artifact from a digest reference', async () => { - const manifest = await fetchOCIFeatureManifestIfExistsFromUserIdentifier({ output, env: process.env }, 'ghcr.io/codspace/features/go', 'sha256:92612b601337f2aa01bba176a13991b03b156c3edf2c18d3d9776e73ccc273a4'); + const manifest = await fetchOCIFeatureManifestIfExistsFromUserIdentifier({ output, env: process.env }, 'ghcr.io/codspace/features/go', 'sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3'); assert.strictEqual(manifest?.manifestObj.layers[0].annotations['org.opencontainers.image.title'], 'go.tgz'); }); @@ -362,7 +365,7 @@ describe('Test OCI Push Helper Functions', function () { const tarLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5'); assert.isTrue(tarLayerBlobExists); - const configLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'); + const configLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); assert.isTrue(configLayerBlobExists); const randomStringDoesNotExist = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:41af286dc0b172ed2f1ca934fd2278de4a1192302ffa07087cea2682e7d372e3'); From d134da7ef564098ac4b22756d06c0ea9f6d68bf3 Mon Sep 17 00:00:00 2001 From: Yuichi Tanaka Date: Tue, 4 Jun 2024 20:57:23 +0000 Subject: [PATCH 5/5] Change the feature that test depends on --- .gitignore | 1 + .../containerCollectionsOCIPush.ts | 4 +- .../assets/devcontainer-feature-color.tgz | Bin 0 -> 3584 bytes src/test/container-features/assets/go.tgz | Bin 15872 -> 0 bytes .../containerFeaturesOCIPush.test.ts | 37 +++++++++--------- 5 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 src/test/container-features/assets/devcontainer-feature-color.tgz delete mode 100644 src/test/container-features/assets/go.tgz diff --git a/.gitignore b/.gitignore index bc02994ef..aac8d0b78 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ output src/test/container-features/configs/temp_lifecycle-hooks-alternative-order test-secrets-temp.json src/test/container-*/**/src/**/README.md +!src/test/container-features/assets/*.tgz diff --git a/src/spec-configuration/containerCollectionsOCIPush.ts b/src/spec-configuration/containerCollectionsOCIPush.ts index dbdf620dc..4175babc7 100644 --- a/src/spec-configuration/containerCollectionsOCIPush.ts +++ b/src/spec-configuration/containerCollectionsOCIPush.ts @@ -382,8 +382,8 @@ async function postUploadSessionId(params: CommonParams, ociRef: OCIRef | OCICol export async function calculateManifestAndContentDigest(output: Log, ociRef: OCIRef | OCICollectionRef, dataLayer: OCILayer, annotations: { [key: string]: string } | undefined): Promise { // A canonical manifest digest is the sha256 hash of the JSON representation of the manifest, without the signature content. // See: https://docs.docker.com/registry/spec/api/#content-digests - // Below is an example of a serialized manifest that should resolve to '92612b601337f2aa01bba176a13991b03b156c3edf2c18d3d9776e73ccc273a4' - // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]} + // Below is an example of a serialized manifest that should resolve to 'dd328c25cc7382aaf4e9ee10104425d9a2561b47fe238407f6c0f77b3f8409fc' + // {"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:0bb92d2da46d760c599d0a41ed88d52521209408b529761417090b62ee16dfd1","size":3584,"annotations":{"org.opencontainers.image.title":"devcontainer-feature-color.tgz"}}],"annotations":{"dev.containers.metadata":"{\"id\":\"color\",\"version\":\"1.0.0\",\"name\":\"A feature to remind you of your favorite color\",\"options\":{\"favorite\":{\"type\":\"string\",\"enum\":[\"red\",\"gold\",\"green\"],\"default\":\"red\",\"description\":\"Choose your favorite color.\"}}}","com.github.package.type":"devcontainer_feature"}} let manifest: OCIManifest = { schemaVersion: 2, diff --git a/src/test/container-features/assets/devcontainer-feature-color.tgz b/src/test/container-features/assets/devcontainer-feature-color.tgz new file mode 100644 index 0000000000000000000000000000000000000000..6908c2e9b7ddb11bd886d6a7fa9f4405a39bc787 GIT binary patch literal 3584 zcmeHHQES^U5cacw#Z4RZVT&y%jt!*@%4nfag;Dyl?LoCKwT3MPNe&Ao|9!F?FG?HQ z(5w$*(Ss%K?)&ca>CRCcy!oP(=F=PkN|Ss_{oQ)pf*?t=ah{FS=_C(mGM;1!1k*RK z%qD2HVak>!WQm958!j^Jhj9HRtgoqm_UWUz#6=;6VO*dZ&5)T^Vf0f=;fEb`wvuFN z=D+*U2Tm;K^Xs#1GRadLe^Ayv!T$q3Q>?P znjgJT11l+U#@gC+H-AcQy}D4`-K~rGQc0%y=$NAq&+-HJ(P5rYEdH*iH<095osL{X(!w`!~+Sx4(Uoc}w z>dz3LxG_B;>xi2QVXoxD{u9baqYPZ1!iR^;^V{pMS2q_c5{3mc@EPJ(tGJd0t6f${ z2;alAGq||E3^%p(H8jhoyxX)LDlDo-mhkb{PIl{_?{w@mj=Z9u=k+6fJ&)(30!Iao J3j8Yy{09CQAMF4D literal 0 HcmV?d00001 diff --git a/src/test/container-features/assets/go.tgz b/src/test/container-features/assets/go.tgz deleted file mode 100644 index 5a831a7ae2e531584cbb523036228c615ebbba2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15872 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 BN-_Wd diff --git a/src/test/container-features/containerFeaturesOCIPush.test.ts b/src/test/container-features/containerFeaturesOCIPush.test.ts index 91d4a8a57..76d743e65 100644 --- a/src/test/container-features/containerFeaturesOCIPush.test.ts +++ b/src/test/container-features/containerFeaturesOCIPush.test.ts @@ -305,29 +305,26 @@ registry`; }); // NOTE: -// Test depends on https://github.com/codspace/features/pkgs/container/features%2Fgo/29819216?tag=1.0.10 -// TODO: -// This test does not take into account the changes in #815. -// Therefore, this test needs to be changed to depend on the feature that was pushed after the #815 change was merged. +// Test depends on https://github.com/orgs/codspace/packages/container/non-empty-config-layer%2Fcolor/225254837?tag=1.0.0 describe('Test OCI Push Helper Functions', function () { this.timeout('10s'); it('Generates the correct tgz manifest layer', async () => { - const dataBytes = fs.readFileSync(`${testAssetsDir}/go.tgz`); + const dataBytes = fs.readFileSync(`${testAssetsDir}/devcontainer-feature-color.tgz`); - const featureRef = getRef(output, 'ghcr.io/devcontainers/features/go'); + const featureRef = getRef(output, 'ghcr.io/codspace/non-empty-config-layer/color'); if (!featureRef) { assert.fail(); } // Calculate the tgz layer and digest - const res = await calculateDataLayer(output, dataBytes, 'go.tgz', DEVCONTAINER_TAR_LAYER_MEDIATYPE); + const res = await calculateDataLayer(output, dataBytes, 'devcontainer-feature-color.tgz', DEVCONTAINER_TAR_LAYER_MEDIATYPE); const expected = { - digest: 'sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5', + digest: 'sha256:0bb92d2da46d760c599d0a41ed88d52521209408b529761417090b62ee16dfd1', mediaType: 'application/vnd.devcontainers.layer.v1+tar', - size: 15872, + size: 3584, annotations: { - 'org.opencontainers.image.title': 'go.tgz' + 'org.opencontainers.image.title': 'devcontainer-feature-color.tgz' } }; @@ -337,35 +334,39 @@ describe('Test OCI Push Helper Functions', function () { assert.deepEqual(res, expected); // Generate entire manifest to be able to calculate content digest - const manifestContainer = await calculateManifestAndContentDigest(output, featureRef, res, undefined); + const annotations = { + 'dev.containers.metadata': '{\"id\":\"color\",\"version\":\"1.0.0\",\"name\":\"A feature to remind you of your favorite color\",\"options\":{\"favorite\":{\"type\":\"string\",\"enum\":[\"red\",\"gold\",\"green\"],\"default\":\"red\",\"description\":\"Choose your favorite color.\"}}}', + 'com.github.package.type': 'devcontainer_feature' + }; + const manifestContainer = await calculateManifestAndContentDigest(output, featureRef, res, annotations); if (!manifestContainer) { assert.fail(); } const { contentDigest, manifestBuffer } = manifestContainer; // 'Expected' is taken from intermediate value in oras reference implementation, before hash calculation - assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5","size":15872,"annotations":{"org.opencontainers.image.title":"go.tgz"}}]}', manifestBuffer.toString()); + assert.strictEqual('{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.devcontainers","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.devcontainers.layer.v1+tar","digest":"sha256:0bb92d2da46d760c599d0a41ed88d52521209408b529761417090b62ee16dfd1","size":3584,"annotations":{"org.opencontainers.image.title":"devcontainer-feature-color.tgz"}}],"annotations":{"dev.containers.metadata":"{\\"id\\":\\"color\\",\\"version\\":\\"1.0.0\\",\\"name\\":\\"A feature to remind you of your favorite color\\",\\"options\\":{\\"favorite\\":{\\"type\\":\\"string\\",\\"enum\\":[\\"red\\",\\"gold\\",\\"green\\"],\\"default\\":\\"red\\",\\"description\\":\\"Choose your favorite color.\\"}}}","com.github.package.type":"devcontainer_feature"}}', manifestBuffer.toString()); // This is the canonical digest of the manifest - assert.strictEqual('sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3', contentDigest); + assert.strictEqual('sha256:dd328c25cc7382aaf4e9ee10104425d9a2561b47fe238407f6c0f77b3f8409fc', contentDigest); }); it('Can fetch an artifact from a digest reference', async () => { - const manifest = await fetchOCIFeatureManifestIfExistsFromUserIdentifier({ output, env: process.env }, 'ghcr.io/codspace/features/go', 'sha256:9726054859c13377c4c3c3c73d15065de59d0c25d61d5652576c0125f2ea8ed3'); - assert.strictEqual(manifest?.manifestObj.layers[0].annotations['org.opencontainers.image.title'], 'go.tgz'); + const manifest = await fetchOCIFeatureManifestIfExistsFromUserIdentifier({ output, env: process.env }, 'ghcr.io/codspace/non-empty-config-layer/color', 'sha256:dd328c25cc7382aaf4e9ee10104425d9a2561b47fe238407f6c0f77b3f8409fc'); + assert.strictEqual(manifest?.manifestObj.layers[0].annotations['org.opencontainers.image.title'], 'devcontainer-feature-color.tgz'); }); it('Can check whether a blob exists', async () => { - const ociFeatureRef = getRef(output, 'ghcr.io/codspace/features/go:1'); + const ociFeatureRef = getRef(output, 'ghcr.io/codspace/non-empty-config-layer/color:1.0.0'); if (!ociFeatureRef) { assert.fail('getRef() for the Feature should not be undefined'); } - const tarLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:b2006e7647191f7b47222ae48df049c6e21a4c5a04acfad0c4ef614d819de4c5'); + const tarLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:0bb92d2da46d760c599d0a41ed88d52521209408b529761417090b62ee16dfd1'); assert.isTrue(tarLayerBlobExists); - const configLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); + const configLayerBlobExists = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'); assert.isTrue(configLayerBlobExists); const randomStringDoesNotExist = await checkIfBlobExists({ output, env: process.env }, ociFeatureRef, 'sha256:41af286dc0b172ed2f1ca934fd2278de4a1192302ffa07087cea2682e7d372e3');