diff --git a/.changeset/container-dry-run-fix.md b/.changeset/container-dry-run-fix.md new file mode 100644 index 000000000000..bce4fc72107d --- /dev/null +++ b/.changeset/container-dry-run-fix.md @@ -0,0 +1,7 @@ +--- +"wrangler": patch +--- + +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). + +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. diff --git a/packages/containers-shared/src/images.ts b/packages/containers-shared/src/images.ts index 6da5d43ce17f..183756f073ec 100644 --- a/packages/containers-shared/src/images.ts +++ b/packages/containers-shared/src/images.ts @@ -1,5 +1,8 @@ import { buildImage } from "./build"; -import { isCloudflareRegistryLink } from "./knobs"; +import { + getCloudflareContainerRegistry, + isCloudflareRegistryLink, +} from "./knobs"; import { dockerLoginManagedRegistry } from "./login"; import { ContainerDevOptions } from "./types"; import { @@ -61,3 +64,31 @@ export async function prepareContainerImagesForDev( await checkExposedPorts(dockerPath, options); } } + +/** + * Resolve an image name to the full unambiguous name. + * + * For now, this only converts images stored in the managed registry to contain + * the user's account ID in the path. + */ +export async function resolveImageName( + accountId: string, + image: string +): Promise { + let url: URL; + try { + url = new URL(`http://${image}`); + } catch (_) { + return image; + } + + if (url.hostname !== getCloudflareContainerRegistry()) { + return image; + } + + if (url.pathname.startsWith(`/${accountId}`)) { + return image; + } + + return `${url.hostname}/${accountId}${url.pathname}`; +} diff --git a/packages/wrangler/src/__tests__/cloudchamber/build.test.ts b/packages/wrangler/src/__tests__/cloudchamber/build.test.ts index 76ea7c25e692..a528ca531e90 100644 --- a/packages/wrangler/src/__tests__/cloudchamber/build.test.ts +++ b/packages/wrangler/src/__tests__/cloudchamber/build.test.ts @@ -62,7 +62,7 @@ describe("buildAndMaybePush", () => { buildCmd: [ "build", "-t", - `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + `${getCloudflareContainerRegistry()}/test-app:tag`, "--platform", "linux/amd64", "--provenance=false", @@ -73,7 +73,7 @@ describe("buildAndMaybePush", () => { dockerfile, }); expect(dockerImageInspect).toHaveBeenCalledWith("/custom/docker/path", { - imageTag: `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + imageTag: `${getCloudflareContainerRegistry()}/test-app:tag`, formatString: "{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}", }); @@ -94,7 +94,7 @@ describe("buildAndMaybePush", () => { buildCmd: [ "build", "-t", - `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + `${getCloudflareContainerRegistry()}/test-app:tag`, "--platform", "linux/amd64", "--provenance=false", @@ -104,14 +104,26 @@ describe("buildAndMaybePush", () => { ], dockerfile, }); - expect(runDockerCmd).toHaveBeenCalledTimes(1); - expect(runDockerCmd).toHaveBeenCalledWith("docker", [ + + // 3 calls: docker tag + docker push + docker rm + expect(runDockerCmd).toHaveBeenCalledTimes(3); + expect(runDockerCmd).toHaveBeenNthCalledWith(1, "docker", [ + "tag", + `${getCloudflareContainerRegistry()}/test-app:tag`, + `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + ]); + expect(runDockerCmd).toHaveBeenNthCalledWith(2, "docker", [ "push", `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, ]); + expect(runDockerCmd).toHaveBeenNthCalledWith(3, "docker", [ + "image", + "rm", + `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + ]); expect(dockerImageInspect).toHaveBeenCalledOnce(); expect(dockerImageInspect).toHaveBeenCalledWith("docker", { - imageTag: `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + imageTag: `${getCloudflareContainerRegistry()}/test-app:tag`, formatString: "{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}", }); @@ -121,7 +133,7 @@ describe("buildAndMaybePush", () => { it("should be able to build image and not push if it already exists in remote", async () => { vi.mocked(runDockerCmd).mockResolvedValueOnce(); vi.mocked(dockerImageInspect).mockResolvedValue( - '53387881 2 ["registry.cloudflare.com/some-account-id/test-app@sha256:three"]' + '53387881 2 ["registry.cloudflare.com/test-app@sha256:three"]' ); await runWrangler( "containers build ./container-context -t test-app:tag -p" @@ -130,7 +142,7 @@ describe("buildAndMaybePush", () => { buildCmd: [ "build", "-t", - `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + `${getCloudflareContainerRegistry()}/test-app:tag`, "--platform", "linux/amd64", "--provenance=false", @@ -154,11 +166,11 @@ describe("buildAndMaybePush", () => { expect(runDockerCmd).toHaveBeenNthCalledWith(2, "docker", [ "image", "rm", - `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + `${getCloudflareContainerRegistry()}/test-app:tag`, ]); expect(dockerImageInspect).toHaveBeenCalledOnce(); expect(dockerImageInspect).toHaveBeenCalledWith("docker", { - imageTag: `${getCloudflareContainerRegistry()}/some-account-id/test-app:tag`, + imageTag: `${getCloudflareContainerRegistry()}/test-app:tag`, formatString: "{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}", }); @@ -172,7 +184,7 @@ describe("buildAndMaybePush", () => { buildCmd: [ "build", "-t", - `${getCloudflareContainerRegistry()}/some-account-id/test-app`, + `${getCloudflareContainerRegistry()}/test-app`, "--platform", "linux/amd64", "--provenance=false", @@ -194,7 +206,7 @@ describe("buildAndMaybePush", () => { buildCmd: [ "build", "-t", - `${getCloudflareContainerRegistry()}/some-account-id/test-app`, + `${getCloudflareContainerRegistry()}/test-app`, "--platform", "linux/amd64", "--provenance=false", @@ -270,11 +282,8 @@ describe("buildAndMaybePush", () => { }); describe("resolveAppDiskSize", () => { - const accountBase = { - limits: { disk_mb_per_deployment: 2000 }, - } as CompleteAccountCustomer; it("should return parsed app disk size", () => { - const result = resolveAppDiskSize(accountBase, { + const result = resolveAppDiskSize({ ...defaultConfiguration, configuration: { image: "", disk: { size: "500MB" } }, }); @@ -282,7 +291,7 @@ describe("buildAndMaybePush", () => { }); it("should return default size when disk size not set", () => { - const result = resolveAppDiskSize(accountBase, { + const result = resolveAppDiskSize({ ...defaultConfiguration, configuration: { image: "" }, }); @@ -290,7 +299,7 @@ describe("buildAndMaybePush", () => { }); it("should return undefined if app is not passed", () => { - expect(resolveAppDiskSize(accountBase, undefined)).toBeUndefined(); + expect(resolveAppDiskSize(undefined)).toBeUndefined(); }); }); }); diff --git a/packages/wrangler/src/__tests__/cloudchamber/deploy.test.ts b/packages/wrangler/src/__tests__/cloudchamber/deploy.test.ts index 7c41e56018d0..0229f045f5ac 100644 --- a/packages/wrangler/src/__tests__/cloudchamber/deploy.test.ts +++ b/packages/wrangler/src/__tests__/cloudchamber/deploy.test.ts @@ -7,6 +7,7 @@ import { } from "@cloudflare/containers-shared"; import { http, HttpResponse } from "msw"; import { maybeBuildContainer } from "../../cloudchamber/deploy"; +import { clearCachedAccount } from "../../cloudchamber/locations"; import { mockAccountId, mockApiToken } from "../helpers/mock-account-id"; import { mockConsoleMethods } from "../helpers/mock-console"; import { mockLegacyScriptData } from "../helpers/mock-legacy-script"; @@ -33,7 +34,7 @@ vi.mock("node:child_process"); describe("maybeBuildContainer", () => { it("Should return imageUpdate: true if using an image URI", async () => { const config = { - image: "registry.cloudflare.com/some-account-id/some-image:uri", + image: "registry.cloudflare.com/some-image:uri", class_name: "Test", }; const result = await maybeBuildContainer( @@ -121,8 +122,18 @@ describe("wrangler deploy with containers", () => { ) .mockImplementationOnce(mockDockerImageInspect("my-container", "Galaxy")) .mockImplementationOnce(mockDockerLogin("mockpassword")) - .mockImplementationOnce(mockDockerManifestInspect("my-container", true)) - .mockImplementationOnce(mockDockerPush("my-container", "Galaxy")); + .mockImplementationOnce( + mockDockerManifestInspect("some-account-id/my-container", true) + ) + .mockImplementationOnce( + mockDockerTag("my-container", "some-account-id/my-container", "Galaxy") + ) + .mockImplementationOnce( + mockDockerPush("some-account-id/my-container", "Galaxy") + ) + .mockImplementationOnce( + mockDockerImageDelete("some-account-id/my-container", "Galaxy") + ); writeWranglerConfig({ durable_objects: { @@ -247,8 +258,18 @@ describe("wrangler deploy with containers", () => { ) .mockImplementationOnce(mockDockerImageInspect("my-container", "Galaxy")) .mockImplementationOnce(mockDockerLogin("mockpassword")) - .mockImplementationOnce(mockDockerManifestInspect("my-container", true)) - .mockImplementationOnce(mockDockerPush("my-container", "Galaxy")); + .mockImplementationOnce( + mockDockerManifestInspect("some-account-id/my-container", true) + ) + .mockImplementationOnce( + mockDockerTag("my-container", "some-account-id/my-container", "Galaxy") + ) + .mockImplementationOnce( + mockDockerPush("some-account-id/my-container", "Galaxy") + ) + .mockImplementationOnce( + mockDockerImageDelete("some-account-id/my-container", "Galaxy") + ); mockContainersAccount(); @@ -336,8 +357,18 @@ describe("wrangler deploy with containers", () => { ) .mockImplementationOnce(mockDockerImageInspect("my-container", "Galaxy")) .mockImplementationOnce(mockDockerLogin("mockpassword")) - .mockImplementationOnce(mockDockerManifestInspect("my-container", true)) - .mockImplementationOnce(mockDockerPush("my-container", "Galaxy")); + .mockImplementationOnce( + mockDockerManifestInspect("some-account-id/my-container", true) + ) + .mockImplementationOnce( + mockDockerTag("my-container", "some-account-id/my-container", "Galaxy") + ) + .mockImplementationOnce( + mockDockerPush("some-account-id/my-container", "Galaxy") + ) + .mockImplementationOnce( + mockDockerImageDelete("some-account-id/my-container", "Galaxy") + ); fs.mkdirSync("nested/src", { recursive: true }); fs.writeFileSync("Dockerfile", "FROM alpine"); @@ -437,6 +468,73 @@ describe("wrangler deploy with containers", () => { }); }); +// This is a separate describe block because we intentionally do not mock any +// API tokens, account ID, or authorization. The purpose of these tests is to +// ensure that --dry-run mode works without requiring any API access. +describe("wrangler deploy with containers dry run", () => { + runInTempDir(); + const std = mockConsoleMethods(); + + beforeEach(() => { + clearCachedAccount(); + }); + + afterEach(() => { + vi.unstubAllEnvs(); + }); + + it("builds the image without pushing", async () => { + vi.mocked(spawn) + .mockImplementationOnce(mockDockerInfo()) + .mockImplementationOnce( + mockDockerBuild("my-container", "worker", "FROM scratch", process.cwd()) + ) + .mockImplementationOnce(mockDockerImageInspect("my-container", "worker")) + .mockImplementationOnce(mockDockerLogin("mockpassword")) + .mockImplementationOnce(mockDockerManifestInspect("my-container", true)) + .mockImplementationOnce(mockDockerPush("my-container", "worker")); + + vi.stubEnv("WRANGLER_DOCKER_BIN", "/usr/bin/docker"); + + fs.writeFileSync("Dockerfile", "FROM scratch"); + fs.writeFileSync( + "index.js", + `export class ExampleDurableObject {}; export default{};` + ); + + writeWranglerConfig({ + durable_objects: { + bindings: [ + { + name: "EXAMPLE_DO_BINDING", + class_name: "ExampleDurableObject", + }, + ], + }, + containers: [ + { + image: "./Dockerfile", + name: "my-container", + instances: 10, + class_name: "ExampleDurableObject", + }, + ], + migrations: [{ tag: "v1", new_sqlite_classes: ["ExampleDurableObject"] }], + }); + + await runWrangler("deploy --dry-run index.js"); + expect(std.out).toMatchInlineSnapshot(` + "Total Upload: xx KiB / gzip: xx KiB + Building image my-container:worker + Your Worker has access to the following bindings: + Binding Resource + env.EXAMPLE_DO_BINDING (ExampleDurableObject) Durable Object + + --dry-run: exiting now." + `); + }); +}); + function mockGetVersion(versionId: string) { msw.use( http.get( @@ -549,7 +647,7 @@ function mockDockerBuild( expect(args).toEqual([ "build", "-t", - `${getCloudflareContainerRegistry()}/some-account-id/${containerName}:${tag}`, + `${getCloudflareContainerRegistry()}/${containerName}:${tag}`, "--platform", "linux/amd64", "--provenance=false", @@ -590,7 +688,7 @@ function mockDockerImageInspect(containerName: string, tag: string) { expect(args).toEqual([ "image", "inspect", - `${getCloudflareContainerRegistry()}/some-account-id/${containerName}:${tag}`, + `${getCloudflareContainerRegistry()}/${containerName}:${tag}`, "--format", "{{ .Size }} {{ len .RootFS.Layers }} {{json .RepoDigests}}", ]); @@ -612,7 +710,7 @@ function mockDockerImageInspect(containerName: string, tag: string) { setImmediate(() => { stdout.emit( "data", - `123456 4 ["${getCloudflareContainerRegistry()}/some-account-id/${containerName}@sha256:three"]` + `123456 4 ["${getCloudflareContainerRegistry()}/${containerName}@sha256:three"]` ); }); @@ -620,6 +718,18 @@ function mockDockerImageInspect(containerName: string, tag: string) { }; } +function mockDockerImageDelete(containerName: string, tag: string) { + return (cmd: string, args: readonly string[]) => { + expect(cmd).toBe("/usr/bin/docker"); + expect(args).toEqual([ + "image", + "rm", + `${getCloudflareContainerRegistry()}/${containerName}:${tag}`, + ]); + return defaultChildProcess(); + }; +} + function mockDockerLogin(expectedPassword: string) { return (cmd: string, _args: readonly string[]) => { expect(cmd).toBe("/usr/bin/docker"); @@ -653,7 +763,7 @@ function mockDockerManifestInspect(containerName: string, shouldFail = true) { expect(args).toEqual([ "manifest", "inspect", - `${getCloudflareContainerRegistry()}/some-account-id/${containerName}@three`, + `${getCloudflareContainerRegistry()}/${containerName}@three`, ]); const readable = new Writable({ write() {}, @@ -679,7 +789,19 @@ function mockDockerPush(containerName: string, tag: string) { expect(cmd).toBe("/usr/bin/docker"); expect(args).toEqual([ "push", - `${getCloudflareContainerRegistry()}/some-account-id/${containerName}:${tag}`, + `${getCloudflareContainerRegistry()}/${containerName}:${tag}`, + ]); + return defaultChildProcess(); + }; +} + +function mockDockerTag(from: string, to: string, tag: string) { + return (cmd: string, args: readonly string[]) => { + expect(cmd).toBe("/usr/bin/docker"); + expect(args).toEqual([ + "tag", + `${getCloudflareContainerRegistry()}/${from}:${tag}`, + `${getCloudflareContainerRegistry()}/${to}:${tag}`, ]); return defaultChildProcess(); }; diff --git a/packages/wrangler/src/cloudchamber/apply.ts b/packages/wrangler/src/cloudchamber/apply.ts index 0ff1804eedd0..459d8f39c041 100644 --- a/packages/wrangler/src/cloudchamber/apply.ts +++ b/packages/wrangler/src/cloudchamber/apply.ts @@ -13,8 +13,8 @@ import { ApplicationsService, CreateApplicationRolloutRequest, DeploymentMutationError, - getCloudflareContainerRegistry, InstanceType, + resolveImageName, RolloutsService, SchedulingPolicy, } from "@cloudflare/containers-shared"; @@ -417,33 +417,6 @@ function sortObjectRecursive>( return sortObjectKeys(objectCopy) as T; } -// Resolve an image name to the full unambiguous name. -// -// For now, this only converts images stored in the managed registry to contain -// the user's account ID in the path. -async function resolveImageName( - config: Config, - image: string -): Promise { - let url: URL; - try { - url = new URL(`http://${image}`); - } catch (_) { - return image; - } - - if (url.hostname !== getCloudflareContainerRegistry()) { - return image; - } - - const accountId = config.account_id || (await getAccountId(config)); - if (url.pathname.startsWith(`/${accountId}`)) { - return image; - } - - return `${url.hostname}/${accountId}${url.pathname}`; -} - export async function apply( args: { skipDefaults: boolean | undefined; @@ -715,6 +688,7 @@ export async function apply( printLine(el, " "); }); + const accountId = config.account_id || (await getAccountId(config)); const configToPush = { ...appConfig, @@ -723,7 +697,7 @@ export async function apply( // De-sugar image name. We do it here so that the user // sees the simplified image name in diffs. - image: await resolveImageName(config, appConfig.configuration.image), + image: await resolveImageName(accountId, appConfig.configuration.image), }, }; diff --git a/packages/wrangler/src/cloudchamber/build.ts b/packages/wrangler/src/cloudchamber/build.ts index 6f4fab0ad15a..43d69e346c05 100644 --- a/packages/wrangler/src/cloudchamber/build.ts +++ b/packages/wrangler/src/cloudchamber/build.ts @@ -5,8 +5,10 @@ import { dockerBuild, dockerImageInspect, dockerLoginManagedRegistry, + getCloudflareContainerRegistry, getCloudflareRegistryWithAccountNamespace, isDir, + resolveImageName, runDockerCmd, } from "@cloudflare/containers-shared"; import { @@ -15,6 +17,7 @@ import { } from "../environment-variables/misc-variables"; import { UserError } from "../errors"; import { logger } from "../logger"; +import { getAccountId } from "../user"; import { resolveAppDiskSize } from "./common"; import { loadAccount } from "./locations"; import type { Config } from "../config"; @@ -81,14 +84,7 @@ export async function buildAndMaybePush( containerConfig?: ContainerApp ): Promise<{ image: string; pushed: boolean }> { try { - // account is also used to check limits below, so it is better to just pull the entire - // account information here - const account = await loadAccount(); - const cloudflareAccountID = account.external_account_id; - const imageTag = getCloudflareRegistryWithAccountNamespace( - cloudflareAccountID, - args.tag - ); + const imageTag = `${getCloudflareContainerRegistry()}/${args.tag}`; const { buildCmd, dockerfile } = await constructBuildCommand( { tag: imageTag, @@ -107,8 +103,6 @@ export async function buildAndMaybePush( dockerfile, }); - // ensure the account is not allowed to build anything that exceeds the current - // account's disk size limits const inspectOutput = await dockerImageInspect(pathToDocker, { imageTag, formatString: @@ -116,32 +110,35 @@ export async function buildAndMaybePush( }); const [sizeStr, layerStr, repoDigests] = inspectOutput.split(" "); - const size = parseInt(sizeStr, 10); - const layers = parseInt(layerStr, 10); - // 16MiB is the layer size adjustments we use in devmapper - const MiB = 1024 * 1024; - const requiredSize = Math.ceil(size * 1.1 + layers * 16 * MiB); - // TODO: do more config merging and earlier - await ensureDiskLimits({ - requiredSize, - account: account, - containerApp: containerConfig, - }); let pushed = false; if (push) { + const account = await loadAccount(); + + const size = parseInt(sizeStr, 10); + const layers = parseInt(layerStr, 10); + + // 16MiB is the layer size adjustments we use in devmapper + const MiB = 1024 * 1024; + const requiredSize = Math.ceil(size * 1.1 + layers * 16 * MiB); + await ensureDiskLimits({ + requiredSize, + account, + containerApp: containerConfig, + }); + await dockerLoginManagedRegistry(pathToDocker); try { // We don't try to parse repoDigests until this point // because we don't want to fail on parse errors if we // won't be pushing the image anyways. // - // A Docker image digest is a unique, cryptographic identifier (SHA-256 hash) - // representing the content of a Docker image. Unlike tags, which can be reused - // or changed, a digest is immutable and ensures that the exact same image is - // pulled every time. This guarantees consistency across different environments - // and deployments. - // From: https://docs.docker.com/dhi/core-concepts/digests/ + // A Docker image digest is a unique, cryptographic identifier (SHA-256 hash) + // representing the content of a Docker image. Unlike tags, which can be reused + // or changed, a digest is immutable and ensures that the exact same image is + // pulled every time. This guarantees consistency across different environments + // and deployments. + // From: https://docs.docker.com/dhi/core-concepts/digests/ const parsedDigests = JSON.parse(repoDigests); if (!Array.isArray(parsedDigests)) { @@ -155,37 +152,57 @@ export async function buildAndMaybePush( const repositoryOnly = imageTag.split(":")[0]; // if this succeeds it means this image already exists remotely // if it fails it means it doesn't exist remotely and should be pushed. - const digests = parsedDigests.filter( + const [digest, ...rest] = parsedDigests.filter( (d): d is string => typeof d === "string" && d.split("@")[0] === repositoryOnly ); - if (digests.length !== 1) { + if (rest.length > 0) { throw new Error( - `Expected there to only be 1 valid digests for this repository: ${repositoryOnly} but there were ${digests.length}` + `Expected there to only be 1 valid digests for this repository: ${repositoryOnly} but there were ${rest.length + 1}` ); } + // Resolve the image name to include the user's + // account ID before checking if it exists in + // the managed registry. + const [image, hash] = digest.split("@"); + const resolvedImage = await resolveImageName( + account.external_account_id, + image + ); + const remoteDigest = `${resolvedImage}@${hash}`; + await runDockerCmd( pathToDocker, - ["manifest", "inspect", digests[0]], + ["manifest", "inspect", remoteDigest], "ignore" ); logger.log("Image already exists remotely, skipping push"); logger.debug( - `Untagging built image: ${imageTag} since there was no change.` + `Untagging built image: ${args.tag} since there was no change.` ); await runDockerCmd(pathToDocker, ["image", "rm", imageTag]); - return { image: digests[0], pushed: false }; + return { image: remoteDigest, pushed: false }; } catch (error) { - logger.log(`Image does not exist remotely, pushing: ${imageTag}`); if (error instanceof Error) { logger.debug( - `Checking for local image ${imageTag} failed with error: ${error.message}` + `Checking for local image ${args.tag} failed with error: ${error.message}` ); } - await runDockerCmd(pathToDocker, ["push", imageTag]); + // Re-tag the image to include the account ID + const namespacedImageTag = getCloudflareRegistryWithAccountNamespace( + account.external_account_id, + args.tag + ); + + logger.log( + `Image does not exist remotely, pushing: ${namespacedImageTag}` + ); + await runDockerCmd(pathToDocker, ["tag", imageTag, namespacedImageTag]); + await runDockerCmd(pathToDocker, ["push", namespacedImageTag]); + await runDockerCmd(pathToDocker, ["image", "rm", namespacedImageTag]); pushed = true; } } @@ -230,13 +247,13 @@ export async function buildCommand( export async function pushCommand( args: StrictYargsOptionsToInterface, - _: Config + config: Config ) { try { await dockerLoginManagedRegistry(args.pathToDocker); - const account = await loadAccount(); + const accountId = config.account_id || (await getAccountId(config)); const newTag = getCloudflareRegistryWithAccountNamespace( - account.external_account_id, + accountId, args.TAG ); const dockerPath = args.pathToDocker ?? getDockerPath(); @@ -259,7 +276,7 @@ export async function ensureDiskLimits(options: { }): Promise { const MB = 1000 * 1000; const MiB = 1024 * 1024; - const appDiskSize = resolveAppDiskSize(options.account, options.containerApp); + const appDiskSize = resolveAppDiskSize(options.containerApp); const accountDiskSize = (options.account.limits.disk_mb_per_deployment ?? 2000) * MB; // if appDiskSize is defined and configured to be more than the accountDiskSize, error diff --git a/packages/wrangler/src/cloudchamber/common.ts b/packages/wrangler/src/cloudchamber/common.ts index 0ec1ead840fa..1deb9b44f175 100644 --- a/packages/wrangler/src/cloudchamber/common.ts +++ b/packages/wrangler/src/cloudchamber/common.ts @@ -619,7 +619,6 @@ export function resolveMemory( // app.configuration is not set // ordering: app.configuration.disk.size -> account.limits.disk_mb_per_deployment -> default fallback to 2GB in bytes export function resolveAppDiskSize( - account: CompleteAccountCustomer, app: ContainerApp | undefined ): number | undefined { if (app === undefined) { diff --git a/packages/wrangler/src/cloudchamber/deploy.ts b/packages/wrangler/src/cloudchamber/deploy.ts index 6176c2d6fc16..01a36cb0f76f 100644 --- a/packages/wrangler/src/cloudchamber/deploy.ts +++ b/packages/wrangler/src/cloudchamber/deploy.ts @@ -17,7 +17,7 @@ export async function maybeBuildContainer( imageTag: string, dryRun: boolean, pathToDocker: string, - configPath: string | undefined + configPath?: string ): Promise<{ image: string; imageUpdated: boolean }> { try { if ( diff --git a/packages/wrangler/src/cloudchamber/locations.ts b/packages/wrangler/src/cloudchamber/locations.ts index cde005249950..0a2776a5068d 100644 --- a/packages/wrangler/src/cloudchamber/locations.ts +++ b/packages/wrangler/src/cloudchamber/locations.ts @@ -6,6 +6,10 @@ import type { let cachedAccount: CompleteAccountCustomer | undefined; +export function clearCachedAccount() { + cachedAccount = undefined; +} + export async function loadAccount(): Promise { if (cachedAccount !== undefined) { return cachedAccount; diff --git a/packages/wrangler/src/deploy/deploy.ts b/packages/wrangler/src/deploy/deploy.ts index e627b004c649..33b4907d06d6 100644 --- a/packages/wrangler/src/deploy/deploy.ts +++ b/packages/wrangler/src/deploy/deploy.ts @@ -776,7 +776,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m // fails. if (config.containers) { // if you have a registry url specified, you don't need docker - const hasDockerfiles = config.containers?.some((container) => + const hasDockerfiles = config.containers.some((container) => isDockerfile( container.image ?? container.configuration?.image, config.configPath