Skip to content

Commit e8a2a1d

Browse files
authored
R2: Add Sippy support (#4130)
1 parent 2ddef2a commit e8a2a1d

File tree

4 files changed

+205
-0
lines changed

4 files changed

+205
-0
lines changed

.changeset/fluffy-sheep-raise.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Added support for R2 Sippy incremental migration

packages/wrangler/src/__tests__/r2.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe("r2", () => {
3737
wrangler r2 bucket create <name> Create a new R2 bucket
3838
wrangler r2 bucket list List R2 buckets
3939
wrangler r2 bucket delete <name> Delete an R2 bucket
40+
wrangler r2 bucket sippy Manage Sippy incremental migration on an R2 bucket
4041
4142
Flags:
4243
-j, --experimental-json-config Experimental: Support wrangler.json [boolean]

packages/wrangler/src/r2/helpers.ts

+63
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,66 @@ export async function usingLocalBucket<T>(
222222
await mf.dispose();
223223
}
224224
}
225+
226+
/**
227+
* Retreive the sippy upstream bucket for the bucket with the given name
228+
*/
229+
export async function getR2Sippy(
230+
accountId: string,
231+
bucketName: string,
232+
jurisdiction?: string
233+
): Promise<string> {
234+
const headers: HeadersInit = {};
235+
if (jurisdiction !== undefined) {
236+
headers["cf-r2-jurisdiction"] = jurisdiction;
237+
}
238+
return await fetchResult(
239+
`/accounts/${accountId}/r2/buckets/${bucketName}/sippy`,
240+
{ method: "GET", headers }
241+
);
242+
}
243+
244+
/**
245+
* Disable sippy on the bucket with the given name
246+
*/
247+
export async function deleteR2Sippy(
248+
accountId: string,
249+
bucketName: string,
250+
jurisdiction?: string
251+
): Promise<void> {
252+
const headers: HeadersInit = {};
253+
if (jurisdiction !== undefined) {
254+
headers["cf-r2-jurisdiction"] = jurisdiction;
255+
}
256+
return await fetchResult(
257+
`/accounts/${accountId}/r2/buckets/${bucketName}/sippy`,
258+
{ method: "DELETE", headers }
259+
);
260+
}
261+
262+
/**
263+
* Enable sippy on the bucket with the given name
264+
*/
265+
export async function putR2Sippy(
266+
accountId: string,
267+
bucketName: string,
268+
config: {
269+
provider: "AWS";
270+
zone: string | undefined;
271+
bucket: string;
272+
key_id: string;
273+
access_key: string;
274+
r2_key_id: string;
275+
r2_access_key: string;
276+
},
277+
jurisdiction?: string
278+
): Promise<void> {
279+
const headers: HeadersInit = {};
280+
if (jurisdiction !== undefined) {
281+
headers["cf-r2-jurisdiction"] = jurisdiction;
282+
}
283+
return await fetchResult(
284+
`/accounts/${accountId}/r2/buckets/${bucketName}/sippy`,
285+
{ method: "PUT", body: JSON.stringify(config), headers }
286+
);
287+
}

packages/wrangler/src/r2/index.ts

+136
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ import {
1515
createR2Bucket,
1616
deleteR2Bucket,
1717
deleteR2Object,
18+
deleteR2Sippy,
1819
getR2Object,
20+
getR2Sippy,
1921
listR2Buckets,
2022
putR2Object,
23+
putR2Sippy,
2124
usingLocalBucket,
2225
} from "./helpers";
2326

@@ -503,6 +506,139 @@ export function r2(r2Yargs: CommonYargsArgv) {
503506
});
504507
}
505508
);
509+
510+
r2BucketYargs.command(
511+
"sippy",
512+
"Manage Sippy incremental migration on an R2 bucket",
513+
(sippyYargs) => {
514+
return sippyYargs
515+
.command(
516+
"enable <name>",
517+
"Enable Sippy on an R2 bucket",
518+
(yargs) =>
519+
yargs
520+
.positional("name", {
521+
describe: "The name of the bucket",
522+
type: "string",
523+
demandOption: true,
524+
})
525+
.option("jurisdiction", {
526+
describe: "The jurisdiction where the bucket exists",
527+
alias: "J",
528+
requiresArg: true,
529+
type: "string",
530+
})
531+
.option("provider", {
532+
choices: ["AWS"],
533+
default: "AWS",
534+
implies: [
535+
"bucket",
536+
"key-id",
537+
"secret-access-key",
538+
"r2-key-id",
539+
"r2-secret-access-key",
540+
],
541+
})
542+
.option("bucket", {
543+
description: "The name of the upstream bucket",
544+
string: true,
545+
})
546+
.option("region", {
547+
description: "The region of the upstream bucket",
548+
string: true,
549+
})
550+
.option("key-id", {
551+
description:
552+
"The secret access key id for the upstream bucket",
553+
string: true,
554+
})
555+
.option("secret-access-key", {
556+
description:
557+
"The secret access key for the upstream bucket",
558+
string: true,
559+
})
560+
.option("r2-key-id", {
561+
description: "The secret access key id for this R2 bucket",
562+
string: true,
563+
})
564+
.option("r2-secret-access-key", {
565+
description: "The secret access key for this R2 bucket",
566+
string: true,
567+
}),
568+
569+
async (args) => {
570+
const config = readConfig(args.config, args);
571+
const accountId = await requireAuth(config);
572+
573+
await putR2Sippy(
574+
accountId,
575+
args.name,
576+
{
577+
provider: "AWS",
578+
zone: args["region"],
579+
bucket: args.bucket ?? "",
580+
key_id: args["key-id"] ?? "",
581+
access_key: args["secret-access-key"] ?? "",
582+
r2_key_id: args["r2-key-id"] ?? "",
583+
r2_access_key: args["r2-secret-access-key"] ?? "",
584+
},
585+
args.jurisdiction
586+
);
587+
}
588+
)
589+
.command(
590+
"disable <name>",
591+
"Disable Sippy on an R2 bucket",
592+
(yargs) =>
593+
yargs
594+
.positional("name", {
595+
describe: "The name of the bucket",
596+
type: "string",
597+
demandOption: true,
598+
})
599+
.option("jurisdiction", {
600+
describe: "The jurisdiction where the bucket exists",
601+
alias: "J",
602+
requiresArg: true,
603+
type: "string",
604+
}),
605+
async (args) => {
606+
const config = readConfig(args.config, args);
607+
const accountId = await requireAuth(config);
608+
609+
await deleteR2Sippy(accountId, args.name, args.jurisdiction);
610+
}
611+
)
612+
.command(
613+
"get <name>",
614+
"Check the status of Sippy on an R2 bucket",
615+
(yargs) =>
616+
yargs
617+
.positional("name", {
618+
describe: "The name of the bucket",
619+
type: "string",
620+
demandOption: true,
621+
})
622+
.option("jurisdiction", {
623+
describe: "The jurisdiction where the bucket exists",
624+
alias: "J",
625+
requiresArg: true,
626+
type: "string",
627+
}),
628+
async (args) => {
629+
const config = readConfig(args.config, args);
630+
const accountId = await requireAuth(config);
631+
632+
const sippyBucket = await getR2Sippy(
633+
accountId,
634+
args.name,
635+
args.jurisdiction
636+
);
637+
logger.log(`Sippy upstream bucket: ${sippyBucket}.`);
638+
}
639+
);
640+
}
641+
);
506642
return r2BucketYargs;
507643
});
508644
}

0 commit comments

Comments
 (0)