Skip to content

Commit

Permalink
Draft Worker for Bulk Secrets (#4028)
Browse files Browse the repository at this point in the history
When Secrets are uploaded if there is no Worker in place already it will fail, this allows for the same draft worker logic to retry adding the secrets
  • Loading branch information
JacobMGEvans authored Sep 26, 2023
1 parent 3cd7286 commit d538973
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 91 deletions.
9 changes: 9 additions & 0 deletions .changeset/pretty-avocados-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"wrangler": patch
---

fix: Bulk Secret Draft Worker

Fixes the issue of a upload of a Secret when a Worker doesn't exist yet, the draft worker is created and the secret is uploaded to it.

Fixes https://github.com/cloudflare/wrangler-action/issues/162
12 changes: 6 additions & 6 deletions packages/wrangler/src/__tests__/secret.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,28 +678,28 @@ describe("wrangler secret", () => {
expect(std.err).toMatchInlineSnapshot(`
"X [ERROR] uploading secret for key: secret-name-1:
request to
request to
https://api.cloudflare.com/client/v4/accounts/some-account-id/workers/scripts/script-name/secrets
failed, reason: Failed to create secret 1
X [ERROR] uploading secret for key: secret-name-3:
request to
request to
https://api.cloudflare.com/client/v4/accounts/some-account-id/workers/scripts/script-name/secrets
failed, reason: Failed to create secret 3
X [ERROR] uploading secret for key: secret-name-5:
request to
request to
https://api.cloudflare.com/client/v4/accounts/some-account-id/workers/scripts/script-name/secrets
failed, reason: Failed to create secret 5
X [ERROR] uploading secret for key: secret-name-7:
request to
request to
https://api.cloudflare.com/client/v4/accounts/some-account-id/workers/scripts/script-name/secrets
failed, reason: Failed to create secret 7
Expand Down Expand Up @@ -750,14 +750,14 @@ describe("wrangler secret", () => {
expect(std.err).toMatchInlineSnapshot(`
"X [ERROR] uploading secret for key: secret-name-1:
request to
request to
https://api.cloudflare.com/client/v4/accounts/some-account-id/workers/scripts/script-name/secrets
failed, reason: Failed to create secret 1
X [ERROR] uploading secret for key: secret-name-2:
request to
request to
https://api.cloudflare.com/client/v4/accounts/some-account-id/workers/scripts/script-name/secrets
failed, reason: Failed to create secret 2
Expand Down
202 changes: 117 additions & 85 deletions packages/wrangler/src/secret/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,86 @@ import * as metrics from "../metrics";
import { parseJSON, readFileSync } from "../parse";
import { requireAuth } from "../user";

import type { Config } from "../config";
import type {
CommonYargsArgv,
StrictYargsOptionsToInterface,
} from "../yargs-types";

function isMissingWorkerError(e: unknown): e is { code: 10007 } {
return (
typeof e === "object" &&
e !== null &&
(e as { code: number }).code === 10007
);
}

async function createDraftWorker({
config,
args,
accountId,
scriptName,
}: {
config: Config;
args: { env?: string; name?: string };
accountId: string;
scriptName: string;
}) {
// TODO: log a warning
await fetchResult(
!isLegacyEnv(config) && args.env
? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
: `/accounts/${accountId}/workers/scripts/${scriptName}`,
{
method: "PUT",
body: createWorkerUploadForm({
name: scriptName,
main: {
name: scriptName,
filePath: undefined,
content: `export default { fetch() {} }`,
type: "esm",
},
bindings: {
kv_namespaces: [],
send_email: [],
vars: {},
durable_objects: { bindings: [] },
queues: [],
r2_buckets: [],
d1_databases: [],
vectorize: [],
constellation: [],
services: [],
analytics_engine_datasets: [],
wasm_modules: {},
browser: undefined,
ai: undefined,
text_blobs: {},
data_blobs: {},
dispatch_namespaces: [],
mtls_certificates: [],
logfwdr: { bindings: [] },
unsafe: {
bindings: undefined,
metadata: undefined,
capnp: undefined,
},
},
modules: [],
migrations: undefined,
compatibility_date: undefined,
compatibility_flags: undefined,
usage_model: undefined,
keepVars: false, // this doesn't matter since it's a new script anyway
logpush: false,
placement: undefined,
tail_consumers: undefined,
}),
}
);
}

export const secret = (secretYargs: CommonYargsArgv) => {
return secretYargs
.option("legacy-env", {
Expand Down Expand Up @@ -84,70 +159,6 @@ export const secret = (secretYargs: CommonYargsArgv) => {
});
}

const createDraftWorker = async () => {
// TODO: log a warning
await fetchResult(
!isLegacyEnv(config) && args.env
? `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}`
: `/accounts/${accountId}/workers/scripts/${scriptName}`,
{
method: "PUT",
body: createWorkerUploadForm({
name: scriptName,
main: {
name: scriptName,
filePath: undefined,
content: `export default { fetch() {} }`,
type: "esm",
},
bindings: {
kv_namespaces: [],
send_email: [],
vars: {},
durable_objects: { bindings: [] },
queues: [],
r2_buckets: [],
d1_databases: [],
vectorize: [],
constellation: [],
services: [],
analytics_engine_datasets: [],
wasm_modules: {},
browser: undefined,
ai: undefined,
text_blobs: {},
data_blobs: {},
dispatch_namespaces: [],
mtls_certificates: [],
logfwdr: { bindings: [] },
unsafe: {
bindings: undefined,
metadata: undefined,
capnp: undefined,
},
},
modules: [],
migrations: undefined,
compatibility_date: undefined,
compatibility_flags: undefined,
usage_model: undefined,
keepVars: false, // this doesn't matter since it's a new script anyway
logpush: false,
placement: undefined,
tail_consumers: undefined,
}),
}
);
};

function isMissingWorkerError(e: unknown): e is { code: 10007 } {
return (
typeof e === "object" &&
e !== null &&
(e as { code: number }).code === 10007
);
}

try {
await submitSecret();
await metrics.sendMetricsEvent("create encrypted variable", {
Expand All @@ -156,7 +167,12 @@ export const secret = (secretYargs: CommonYargsArgv) => {
} catch (e) {
if (isMissingWorkerError(e)) {
// create a draft worker and try again
await createDraftWorker();
await createDraftWorker({
config,
args,
accountId,
scriptName,
});
await submitSecret();
// TODO: delete the draft worker if this failed too?
} else {
Expand Down Expand Up @@ -363,33 +379,49 @@ export const secretBulkHandler = async (secretBulkArgs: SecretBulkArgs) => {
return logger.error(`🚨 No content found in JSON file or piped input.`);
}

const url =
!secretBulkArgs.env || isLegacyEnv(config)
? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
: `/accounts/${accountId}/workers/services/${scriptName}/environments/${secretBulkArgs.env}/secrets`;
function submitSecrets(key: string, value: string) {
const url =
!secretBulkArgs.env || isLegacyEnv(config)
? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
: `/accounts/${accountId}/workers/services/${scriptName}/environments/${secretBulkArgs.env}/secrets`;

return fetchResult(url, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: key,
text: value,
type: "secret_text",
}),
});
}
// Until we have a bulk route for secrets, we need to make a request for each key/value pair
const bulkOutcomes = await Promise.all(
Object.entries(content).map(async ([key, value]) => {
return fetchResult(url, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: key,
text: value,
type: "secret_text",
}),
})
.then(() => {
logger.log(`✨ Successfully created secret for key: ${key}`);
return true;
})
.catch((e) => {
try {
await submitSecrets(key, value);
logger.log(`✨ Successfully created secret for key: ${key}`);
return true;
} catch (e) {
if (e instanceof Error) {
logger.error(
`uploading secret for key: ${key}:
${e.message}`
${e.message}`
);
if (isMissingWorkerError(e)) {
// create a draft worker and try again
await createDraftWorker({
config,
args: secretBulkArgs,
accountId,
scriptName,
});
await submitSecrets(key, value);
// TODO: delete the draft worker if this failed too?
}
return false;
});
}
}
})
);

Expand Down

0 comments on commit d538973

Please sign in to comment.