From 8788d6afe55a666ff57a6d73564c2829c432bd60 Mon Sep 17 00:00:00 2001 From: "Tobias Schlatter (tos)" Date: Wed, 25 Oct 2023 13:35:42 +0200 Subject: [PATCH 1/2] Allow to start containers with bare image IDs Docker supports starting containers with bare image IDs: ``` docker run aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f ``` However, if a tag or digest is added to the ID, Docker will refuse it: ``` docker run aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f:latest Unable to find image 'aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f:latest' locally docker: Error response from daemon: invalid repository name (aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f), cannot specify 64-byte hexadecimal strings. See 'docker run --help'. ``` We adjust the string conversion of ImageName for this specific case to allow starting containers with bare image IDs. More context: https://github.com/testcontainers/testcontainers-java/discussions/7707 --- .../src/container-runtime/image-name.test.ts | 28 +++++++++++++++++++ .../src/container-runtime/image-name.ts | 10 +++++++ 2 files changed, 38 insertions(+) diff --git a/packages/testcontainers/src/container-runtime/image-name.test.ts b/packages/testcontainers/src/container-runtime/image-name.test.ts index ca88122fd..ba21d3315 100644 --- a/packages/testcontainers/src/container-runtime/image-name.test.ts +++ b/packages/testcontainers/src/container-runtime/image-name.test.ts @@ -30,6 +30,26 @@ describe("ContainerImage", () => { const imageName = new ImageName("registry", "image", "sha256:1234abcd1234abcd1234abcd1234abcd"); expect(imageName.string).toBe("registry/image@sha256:1234abcd1234abcd1234abcd1234abcd"); }); + + it("should not append the `latest` tag to image IDs", () => { + const imageName = new ImageName( + undefined, + "aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f", + "latest" + ); + expect(imageName.string).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + }); + + it("should keep other tags (not `latest`) on image IDs", () => { + // Note that the resulting image ID will not be accepted by docker. + // However, not treating tags other than `latests` specially is probably less surprising. + const imageName = new ImageName( + undefined, + "aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f", + "1" + ); + expect(imageName.string).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f:1"); + }); }); describe("fromString", () => { @@ -96,5 +116,13 @@ describe("ContainerImage", () => { expect(imageName.image).toBe("image"); expect(imageName.tag).toBe("sha256:1234abcd1234abcd1234abcd1234abcd"); }); + + it("should work with image being an image ID", () => { + const imageName = ImageName.fromString("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + + expect(imageName.registry).toBe(undefined); + expect(imageName.image).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + expect(imageName.tag).toBe("latest"); + }); }); }); diff --git a/packages/testcontainers/src/container-runtime/image-name.ts b/packages/testcontainers/src/container-runtime/image-name.ts index c7421c6ed..ea10e1a57 100644 --- a/packages/testcontainers/src/container-runtime/image-name.ts +++ b/packages/testcontainers/src/container-runtime/image-name.ts @@ -1,6 +1,8 @@ export class ImageName { public readonly string: string; + private static readonly hexRE = /^[0-9a-f]{64}$/i; + constructor( public readonly registry: string | undefined, public readonly image: string, @@ -12,6 +14,14 @@ export class ImageName { } else { this.string = `${this.registry}/${this.image}:${this.tag}`; } + } else if (this.tag === "latest" && ImageName.hexRE.test(this.image)) { + // 64 byte hex string. This refers to an image sha256 directly. + // Do not put the tag as the docker does not accept it. + // It will fail with: + // + // invalid repository name (), cannot specify 64-byte hexadecimal strings. + // + this.string = this.image; } else if (this.tag.startsWith("sha256:")) { this.string = `${this.image}@${this.tag}`; } else { From b32f9cb0e73c0c9ffe96ce1143efcdaa5071f54e Mon Sep 17 00:00:00 2001 From: "Tobias Schlatter (tos)" Date: Thu, 26 Oct 2023 13:12:54 +0200 Subject: [PATCH 2/2] Add test case, clarify explanation --- .../src/container-runtime/image-name.test.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/testcontainers/src/container-runtime/image-name.test.ts b/packages/testcontainers/src/container-runtime/image-name.test.ts index ba21d3315..682c9c815 100644 --- a/packages/testcontainers/src/container-runtime/image-name.test.ts +++ b/packages/testcontainers/src/container-runtime/image-name.test.ts @@ -41,7 +41,10 @@ describe("ContainerImage", () => { }); it("should keep other tags (not `latest`) on image IDs", () => { - // Note that the resulting image ID will not be accepted by docker. + // Note that the resulting image ID will not be accepted by Docker: + // + // > "invalid repository name [...], cannot specify 64-byte hexadecimal strings" + // // However, not treating tags other than `latests` specially is probably less surprising. const imageName = new ImageName( undefined, @@ -124,5 +127,18 @@ describe("ContainerImage", () => { expect(imageName.image).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); expect(imageName.tag).toBe("latest"); }); + + it("should work with image being an image ID and an explicit tag", () => { + // Note: Such an ID will not be accepted by docker: + // + // > "invalid repository name [...], cannot specify 64-byte hexadecimal strings" + // + // However, parsing it this way is probably least surprising. + const imageName = ImageName.fromString("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f:1"); + + expect(imageName.registry).toBe(undefined); + expect(imageName.image).toBe("aa285b773a2c042056883845aea893a743d358a5d40f61734fa228fde93dae6f"); + expect(imageName.tag).toBe("1"); + }); }); });