-
Notifications
You must be signed in to change notification settings - Fork 803
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add telemetry to auto provisioning #7984
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"wrangler": patch | ||
--- | ||
|
||
feat: add telemetry to experimental auto-provisioning | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
import { http, HttpResponse } from "msw"; | ||
import { logger } from "../logger"; | ||
import { mockAccountId, mockApiToken } from "./helpers/mock-account-id"; | ||
import { mockConsoleMethods } from "./helpers/mock-console"; | ||
import { clearDialogs, mockPrompt, mockSelect } from "./helpers/mock-dialogs"; | ||
|
@@ -30,6 +31,7 @@ describe("--x-provision", () => { | |
const { setIsTTY } = useMockIsTTY(); | ||
|
||
beforeEach(() => { | ||
logger.loggerLevel = "debug"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might need to restore the loggerLevel after the test or it will create a side effect for the rest of the tests. |
||
setIsTTY(true); | ||
msw.use( | ||
...mswSuccessDeploymentScriptMetadata, | ||
|
@@ -103,6 +105,18 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
|
||
expect(std.debug).toContain( | ||
JSON.stringify( | ||
{ | ||
"kv_namespace inherited": 1, | ||
"d1 inherited": 1, | ||
"r2_bucket inherited": 1, | ||
}, | ||
undefined, | ||
2 | ||
) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -208,6 +222,17 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify( | ||
{ | ||
"kv_namespace connected": 1, | ||
"d1 connected": 1, | ||
"r2_bucket connected": 1, | ||
}, | ||
undefined, | ||
2 | ||
) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -327,6 +352,17 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify( | ||
{ | ||
"kv_namespace connected": 1, | ||
"d1 connected": 1, | ||
"r2_bucket connected": 1, | ||
}, | ||
undefined, | ||
2 | ||
) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -459,6 +495,17 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify( | ||
{ | ||
"kv_namespace created": 1, | ||
"d1 created": 1, | ||
"r2_bucket created": 1, | ||
}, | ||
undefined, | ||
2 | ||
) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -524,6 +571,9 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify({ "d1 created": 1 }, undefined, 2) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -566,6 +616,9 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify({ "d1 inherited": 1 }, undefined, 2) | ||
); | ||
}); | ||
|
||
it("will not inherit d1 binding when the database name is provided but has changed", async () => { | ||
|
@@ -643,6 +696,9 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify({ "d1 created": 1 }, undefined, 2) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -716,6 +772,9 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify({ "r2_bucket created": 1 }, undefined, 2) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -770,6 +829,7 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).not.toContain("Provisioning summary"); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -814,6 +874,9 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify({ "d1 connected": 1 }, undefined, 2) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
@@ -898,6 +961,9 @@ describe("--x-provision", () => { | |
https://test-name.test-sub-domain.workers.dev | ||
Current Version ID: Galaxy-Class" | ||
`); | ||
expect(std.debug).toContain( | ||
JSON.stringify({ "r2_bucket created": 1 }, undefined, 2) | ||
); | ||
expect(std.err).toMatchInlineSnapshot(`""`); | ||
expect(std.warn).toMatchInlineSnapshot(`""`); | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import { prompt, select } from "../dialogs"; | |
import { UserError } from "../errors"; | ||
import { createKVNamespace, listKVNamespaces } from "../kv/helpers"; | ||
import { logger } from "../logger"; | ||
import * as metrics from "../metrics"; | ||
import { APIError } from "../parse"; | ||
import { createR2Bucket, getR2Bucket, listR2Buckets } from "../r2/helpers"; | ||
import { isLegacyEnv } from "../utils/isLegacyEnv"; | ||
|
@@ -85,7 +86,9 @@ abstract class ProvisionResourceHandler< | |
public type: T, | ||
public binding: B, | ||
public idField: keyof B, | ||
public accountId: string | ||
public accountId: string, | ||
/** For telemetry */ | ||
public summary: Record<string, number> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An instance of this class represents a single binding—it feels odd to be passing in a record of multiple bindings here? Why does this need to be done insdie the class? Can't we just count the number of items in pendingResources? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah it is odd :/ i wanted access to the inherit/create/connect methods to record what happens to each resource, not just that they get provisioned in some way (connected or created), especially as some resources get connected before Although now that i'm revisiting it, i think that's probably a level of granularity that isn't necessary/important and we can just count pendingResources... yeah i'll simplify this |
||
) {} | ||
|
||
// Does this resource already exist in the currently deployed version of the Worker? | ||
|
@@ -95,10 +98,16 @@ abstract class ProvisionResourceHandler< | |
): boolean | Promise<boolean>; | ||
|
||
inherit(): void { | ||
this.summary[`${this.type} inherited`] = | ||
(this.summary[`${this.type} inherited`] ?? 0) + 1; | ||
// @ts-expect-error idField is a key of this.binding | ||
this.binding[this.idField] = INHERIT_SYMBOL; | ||
} | ||
connect(id: string): void { | ||
connect(id: string, record: boolean = true): void { | ||
if (record) { | ||
this.summary[`${this.type} connected`] = | ||
(this.summary[`${this.type} connected`] ?? 0) + 1; | ||
} | ||
// @ts-expect-error idField is a key of this.binding | ||
this.binding[this.idField] = id; | ||
} | ||
|
@@ -108,8 +117,10 @@ abstract class ProvisionResourceHandler< | |
abstract get name(): string | undefined; | ||
|
||
async provision(name: string): Promise<void> { | ||
this.summary[`${this.type} created`] = | ||
(this.summary[`${this.type} created`] ?? 0) + 1; | ||
const id = await this.create(name); | ||
this.connect(id); | ||
this.connect(id, false); | ||
} | ||
|
||
// This binding is fully specified and can't/shouldn't be provisioned | ||
|
@@ -164,8 +175,12 @@ class R2Handler extends ProvisionResourceHandler<"r2_bucket", CfR2Bucket> { | |
); | ||
return name; | ||
} | ||
constructor(binding: CfR2Bucket, accountId: string) { | ||
super("r2_bucket", binding, "bucket_name", accountId); | ||
constructor( | ||
binding: CfR2Bucket, | ||
accountId: string, | ||
summary: Record<string, number> | ||
) { | ||
super("r2_bucket", binding, "bucket_name", accountId, summary); | ||
} | ||
canInherit(settings: Settings | undefined): boolean { | ||
return !!settings?.bindings.find( | ||
|
@@ -212,8 +227,12 @@ class KVHandler extends ProvisionResourceHandler< | |
async create(name: string) { | ||
return await createKVNamespace(this.accountId, name); | ||
} | ||
constructor(binding: CfKvNamespace, accountId: string) { | ||
super("kv_namespace", binding, "id", accountId); | ||
constructor( | ||
binding: CfKvNamespace, | ||
accountId: string, | ||
summary: Record<string, number> | ||
) { | ||
super("kv_namespace", binding, "id", accountId, summary); | ||
} | ||
canInherit(settings: Settings | undefined): boolean { | ||
return !!settings?.bindings.find( | ||
|
@@ -234,8 +253,12 @@ class D1Handler extends ProvisionResourceHandler<"d1", CfD1Database> { | |
const db = await createD1Database(this.accountId, name); | ||
return db.uuid; | ||
} | ||
constructor(binding: CfD1Database, accountId: string) { | ||
super("d1", binding, "database_id", accountId); | ||
constructor( | ||
binding: CfD1Database, | ||
accountId: string, | ||
summary: Record<string, number> | ||
) { | ||
super("d1", binding, "database_id", accountId, summary); | ||
} | ||
async canInherit(settings: Settings | undefined): Promise<boolean> { | ||
const maybeInherited = settings?.bindings.find( | ||
|
@@ -341,8 +364,13 @@ async function collectPendingResources( | |
accountId: string, | ||
scriptName: string, | ||
bindings: CfWorkerInit["bindings"] | ||
): Promise<PendingResource[]> { | ||
): Promise<{ | ||
pendingResources: PendingResource[]; | ||
summary: Record<string, number>; | ||
}> { | ||
let settings: Settings | undefined; | ||
// summarises what happens to the resources to send as one telemetry event | ||
const summary: Record<string, number> = {}; | ||
|
||
try { | ||
settings = await getSettings(accountId, scriptName); | ||
|
@@ -361,7 +389,11 @@ async function collectPendingResources( | |
HANDLERS | ||
) as (keyof typeof HANDLERS)[]) { | ||
for (const resource of bindings[resourceType] ?? []) { | ||
const h = new HANDLERS[resourceType].Handler(resource, accountId); | ||
const h = new HANDLERS[resourceType].Handler( | ||
resource, | ||
accountId, | ||
summary | ||
); | ||
|
||
if (await h.shouldProvision(settings)) { | ||
pendingResources.push({ | ||
|
@@ -373,18 +405,22 @@ async function collectPendingResources( | |
} | ||
} | ||
|
||
return pendingResources.sort( | ||
(a, b) => HANDLERS[a.resourceType].sort - HANDLERS[b.resourceType].sort | ||
); | ||
return { | ||
pendingResources: pendingResources.sort( | ||
(a, b) => HANDLERS[a.resourceType].sort - HANDLERS[b.resourceType].sort | ||
), | ||
summary, | ||
}; | ||
} | ||
|
||
export async function provisionBindings( | ||
bindings: CfWorkerInit["bindings"], | ||
accountId: string, | ||
scriptName: string, | ||
autoCreate: boolean, | ||
config: Config | ||
): Promise<void> { | ||
const pendingResources = await collectPendingResources( | ||
const { pendingResources, summary } = await collectPendingResources( | ||
accountId, | ||
scriptName, | ||
bindings | ||
|
@@ -422,6 +458,12 @@ export async function provisionBindings( | |
|
||
logger.log(`🎉 All resources provisioned, continuing with deployment...\n`); | ||
} | ||
if (Object.keys(summary).length) { | ||
metrics.sendMetricsEvent("provision resources", summary, { | ||
sendMetrics: config.send_metrics, | ||
}); | ||
logger.debug("Provisioning summary:", JSON.stringify(summary, null, 2)); | ||
} | ||
} | ||
|
||
function getSettings(accountId: string, scriptName: string) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a sanity check: This is a patch because it is an experimental feature, right?