Skip to content

Commit 1aa04e3

Browse files
Build container without account ID
This allows us to build containers in dry run mode. When we push the container to the managed registry, we re-tag it to include the account ID. We only ever push when not in dry run mode though, so when pushing we can safely grab the account information. Co-authored-by: Hasip Timurtaş <[email protected]>
1 parent a727db3 commit 1aa04e3

File tree

10 files changed

+280
-119
lines changed

10 files changed

+280
-119
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
Build container images without the user's account ID. This allows containers to be built and verified in dry run mode (where we do not necessarily have the user's account info).
6+
7+
When we push the image to the managed registry, we first re-tag the image to include the user's account ID so that the image has the full resolved image name.

packages/containers-shared/src/images.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { buildImage } from "./build";
2-
import { isCloudflareRegistryLink } from "./knobs";
2+
import { getCloudflareContainerRegistry, isCloudflareRegistryLink } from "./knobs";
33
import { dockerLoginManagedRegistry } from "./login";
44
import { ContainerDevOptions } from "./types";
55
import {
@@ -61,3 +61,32 @@ export async function prepareContainerImagesForDev(
6161
await checkExposedPorts(dockerPath, options);
6262
}
6363
}
64+
65+
/**
66+
* Resolve an image name to the full unambiguous name.
67+
*
68+
* For now, this only converts images stored in the managed registry to contain
69+
* the user's account ID in the path.
70+
*/
71+
export async function resolveImageName(
72+
accountId: string,
73+
image: string
74+
): Promise<string> {
75+
let url: URL;
76+
try {
77+
url = new URL(`http://${image}`);
78+
} catch (_) {
79+
return image;
80+
}
81+
82+
if (url.hostname !== getCloudflareContainerRegistry()) {
83+
return image;
84+
}
85+
86+
if (url.pathname.startsWith(`/${accountId}`)) {
87+
return image;
88+
}
89+
90+
return `${url.hostname}/${accountId}${url.pathname}`;
91+
}
92+

packages/wrangler/src/__tests__/cloudchamber/build.test.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe("buildAndMaybePush", () => {
6262
buildCmd: [
6363
"build",
6464
"-t",
65-
`${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
65+
`${getCloudflareContainerRegistry()}/test-app:tag`,
6666
"--platform",
6767
"linux/amd64",
6868
"--provenance=false",
@@ -73,7 +73,7 @@ describe("buildAndMaybePush", () => {
7373
dockerfile,
7474
});
7575
expect(dockerImageInspect).toHaveBeenCalledWith("/custom/docker/path", {
76-
imageTag: `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
76+
imageTag: `${getCloudflareContainerRegistry()}/test-app:tag`,
7777
formatString:
7878
"{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}",
7979
});
@@ -94,7 +94,7 @@ describe("buildAndMaybePush", () => {
9494
buildCmd: [
9595
"build",
9696
"-t",
97-
`${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
97+
`${getCloudflareContainerRegistry()}/test-app:tag`,
9898
"--platform",
9999
"linux/amd64",
100100
"--provenance=false",
@@ -104,14 +104,26 @@ describe("buildAndMaybePush", () => {
104104
],
105105
dockerfile,
106106
});
107-
expect(runDockerCmd).toHaveBeenCalledTimes(1);
108-
expect(runDockerCmd).toHaveBeenCalledWith("docker", [
107+
108+
// 3 calls: docker tag + docker push + docker rm
109+
expect(runDockerCmd).toHaveBeenCalledTimes(3);
110+
expect(runDockerCmd).toHaveBeenNthCalledWith(1, "docker", [
111+
"tag",
112+
`${getCloudflareContainerRegistry()}/test-app:tag`,
113+
`${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
114+
]);
115+
expect(runDockerCmd).toHaveBeenNthCalledWith(2, "docker", [
109116
"push",
110117
`${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
111118
]);
119+
expect(runDockerCmd).toHaveBeenNthCalledWith(3, "docker", [
120+
"image",
121+
"rm",
122+
`${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
123+
]);
112124
expect(dockerImageInspect).toHaveBeenCalledOnce();
113125
expect(dockerImageInspect).toHaveBeenCalledWith("docker", {
114-
imageTag: `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
126+
imageTag: `${getCloudflareContainerRegistry()}/test-app:tag`,
115127
formatString:
116128
"{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}",
117129
});
@@ -121,7 +133,7 @@ describe("buildAndMaybePush", () => {
121133
it("should be able to build image and not push if it already exists in remote", async () => {
122134
vi.mocked(runDockerCmd).mockResolvedValueOnce();
123135
vi.mocked(dockerImageInspect).mockResolvedValue(
124-
'53387881 2 ["registry.cloudflare.com/some-account-id/test-app@sha256:three"]'
136+
'53387881 2 ["registry.cloudflare.com/test-app@sha256:three"]'
125137
);
126138
await runWrangler(
127139
"containers build ./container-context -t test-app:tag -p"
@@ -130,7 +142,7 @@ describe("buildAndMaybePush", () => {
130142
buildCmd: [
131143
"build",
132144
"-t",
133-
`${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
145+
`${getCloudflareContainerRegistry()}/test-app:tag`,
134146
"--platform",
135147
"linux/amd64",
136148
"--provenance=false",
@@ -154,11 +166,11 @@ describe("buildAndMaybePush", () => {
154166
expect(runDockerCmd).toHaveBeenNthCalledWith(2, "docker", [
155167
"image",
156168
"rm",
157-
`${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
169+
`${getCloudflareContainerRegistry()}/test-app:tag`,
158170
]);
159171
expect(dockerImageInspect).toHaveBeenCalledOnce();
160172
expect(dockerImageInspect).toHaveBeenCalledWith("docker", {
161-
imageTag: `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`,
173+
imageTag: `${getCloudflareContainerRegistry()}/test-app:tag`,
162174
formatString:
163175
"{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}",
164176
});
@@ -172,7 +184,7 @@ describe("buildAndMaybePush", () => {
172184
buildCmd: [
173185
"build",
174186
"-t",
175-
`${getCloudflareContainerRegistry()}/some-account-id/test-app`,
187+
`${getCloudflareContainerRegistry()}/test-app`,
176188
"--platform",
177189
"linux/amd64",
178190
"--provenance=false",
@@ -194,7 +206,7 @@ describe("buildAndMaybePush", () => {
194206
buildCmd: [
195207
"build",
196208
"-t",
197-
`${getCloudflareContainerRegistry()}/some-account-id/test-app`,
209+
`${getCloudflareContainerRegistry()}/test-app`,
198210
"--platform",
199211
"linux/amd64",
200212
"--provenance=false",
@@ -270,27 +282,24 @@ describe("buildAndMaybePush", () => {
270282
});
271283

272284
describe("resolveAppDiskSize", () => {
273-
const accountBase = {
274-
limits: { disk_mb_per_deployment: 2000 },
275-
} as CompleteAccountCustomer;
276285
it("should return parsed app disk size", () => {
277-
const result = resolveAppDiskSize(accountBase, {
286+
const result = resolveAppDiskSize({
278287
...defaultConfiguration,
279288
configuration: { image: "", disk: { size: "500MB" } },
280289
});
281290
expect(result).toBeCloseTo(500 * 1000 * 1000, -5);
282291
});
283292

284293
it("should return default size when disk size not set", () => {
285-
const result = resolveAppDiskSize(accountBase, {
294+
const result = resolveAppDiskSize({
286295
...defaultConfiguration,
287296
configuration: { image: "" },
288297
});
289298
expect(result).toBeCloseTo(2 * 1000 * 1000 * 1000, -5);
290299
});
291300

292301
it("should return undefined if app is not passed", () => {
293-
expect(resolveAppDiskSize(accountBase, undefined)).toBeUndefined();
302+
expect(resolveAppDiskSize(undefined)).toBeUndefined();
294303
});
295304
});
296305
});

0 commit comments

Comments
 (0)