diff --git a/README.md b/README.md index 5beb571fc..c3b232821 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-storage/tre | Configure Retries | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/configureRetries.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/configureRetries.js,samples/README.md) | | Copy File | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/copyFile.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/copyFile.js,samples/README.md) | | Copy Old Version Of File. | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/copyOldVersionOfFile.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/copyOldVersionOfFile.js,samples/README.md) | +| Create a Dual-Region Bucket | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/createBucketWithDualRegion.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/createBucketWithDualRegion.js,samples/README.md) | | Create Bucket With Storage Class and Location. | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/createBucketWithStorageClassAndLocation.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/createBucketWithStorageClassAndLocation.js,samples/README.md) | | Create Bucket With Turbo Replication | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/createBucketWithTurboReplication.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/createBucketWithTurboReplication.js,samples/README.md) | | Create New Bucket | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/createNewBucket.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/createNewBucket.js,samples/README.md) | diff --git a/samples/README.md b/samples/README.md index f26291d8e..d22d3a693 100644 --- a/samples/README.md +++ b/samples/README.md @@ -33,6 +33,7 @@ objects to users via direct download. * [Configure Retries](#configure-retries) * [Copy File](#copy-file) * [Copy Old Version Of File.](#copy-old-version-of-file.) + * [Create a Dual-Region Bucket](#create-a-dual-region-bucket) * [Create Bucket With Storage Class and Location.](#create-bucket-with-storage-class-and-location.) * [Create Bucket With Turbo Replication](#create-bucket-with-turbo-replication) * [Create New Bucket](#create-new-bucket) @@ -413,6 +414,25 @@ __Usage:__ +### Create a Dual-Region Bucket + +Create a Dual-Region Bucket with provided locations. + +View the [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/createBucketWithDualRegion.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/createBucketWithDualRegion.js,samples/README.md) + +__Usage:__ + + +`node createBucketWithDualRegion.js ` + + +----- + + + + ### Create Bucket With Storage Class and Location. Create Bucket With Storage Class and Location. diff --git a/samples/createBucketWithDualRegion.js b/samples/createBucketWithDualRegion.js new file mode 100644 index 000000000..e4fa86afa --- /dev/null +++ b/samples/createBucketWithDualRegion.js @@ -0,0 +1,67 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +// sample-metadata: +// title: Create a Dual-Region Bucket +// description: Create a Dual-Region Bucket with provided locations. +// usage: node createBucketWithDualRegion.js + +function main( + bucketName = 'my-bucket', + region1 = 'US-EAST1', + region2 = 'US-WEST1' +) { + // [START storage_create_bucket_dual_region] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // The ID of your GCS bucket + // const bucketName = 'your-unique-bucket-name'; + + // The bucket's pair of regions. Case-insensitive. + // See this documentation for other valid locations: + // https://cloud.google.com/storage/docs/locations + // const region1 = 'US-EAST1'; + // const region2 = 'US-WEST1'; + + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + // The bucket in the sample below will be created in the project associated with this client. + // For more information, please see https://cloud.google.com/docs/authentication/production or https://googleapis.dev/nodejs/storage/latest/Storage.html + const storage = new Storage(); + + async function createDualRegionBucket() { + // For regions supporting dual-regions see: https://cloud.google.com/storage/docs/locations + const [bucket] = await storage.createBucket(bucketName, { + location: `${region1}+${region2}`, // e.g. `US-EAST1+US-WEST1` + }); + + console.log(`${bucket.name} created in '${region1}+${region2}'`); + } + + createDualRegionBucket().catch(console.error); + // [END storage_create_bucket_dual_region] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/samples/createBucketWithStorageClassAndLocation.js b/samples/createBucketWithStorageClassAndLocation.js index 9a6875e0b..27ee7e70a 100644 --- a/samples/createBucketWithStorageClassAndLocation.js +++ b/samples/createBucketWithStorageClassAndLocation.js @@ -40,7 +40,7 @@ function main( // The name of a location // See this documentation for other valid locations: - // http://g.co/cloud/storage/docs/bucket-locations#location-mr + // http://g.co/cloud/storage/docs/locations#location-mr // const location = 'ASIA'; // Imports the Google Cloud client library diff --git a/samples/system-test/buckets.test.js b/samples/system-test/buckets.test.js index daa98f10b..d44d8186a 100644 --- a/samples/system-test/buckets.test.js +++ b/samples/system-test/buckets.test.js @@ -26,17 +26,20 @@ const storage = new Storage(); const samplesTestBucketPrefix = `nodejs-storage-samples-${uuid.v4()}`; const bucketName = `${samplesTestBucketPrefix}-a`; const bucketNameDualRegion = `${samplesTestBucketPrefix}-b`; -const bucketNameWithClassAndLocation = `${samplesTestBucketPrefix}-c`; +const bucketNameDualRegionTurbo = `${samplesTestBucketPrefix}-c`; +const bucketNameWithClassAndLocation = `${samplesTestBucketPrefix}-d`; const defaultKmsKeyName = process.env.GOOGLE_CLOUD_KMS_KEY_ASIA; const bucket = storage.bucket(bucketName); const bucketWithClassAndLocation = storage.bucket( bucketNameWithClassAndLocation ); const dualRegionBucket = storage.bucket(bucketNameDualRegion); +const dualRegionBucketTurbo = storage.bucket(bucketNameDualRegionTurbo); const PUBLIC_ACCESS_PREVENTION_INHERITED = 'inherited'; const PUBLIC_ACCESS_PREVENTION_ENFORCED = 'enforced'; +const DUAL_REGION = ['US-EAST1', 'US-WEST1']; const RPO_ASYNC_TURBO = 'ASYNC_TURBO'; const RPO_DEFAULT = 'DEFAULT'; @@ -212,54 +215,73 @@ it('should set public access prevention to inherited', async () => { ); }); +it('should create a dual-region bucket', async () => { + const dualRegion = `${DUAL_REGION[0]}+${DUAL_REGION[1]}`; + + const output = execSync( + `node createBucketWithDualRegion.js ${bucketNameDualRegion} ${DUAL_REGION[0]} ${DUAL_REGION[1]}` + ); + + assert.include(output, `${bucketNameDualRegion} created in '${dualRegion}'`); + + const [exists] = await dualRegionBucket.exists(); + assert.strictEqual(exists, true); + + const [metadata] = await dualRegionBucket.getMetadata(); + assert.strictEqual(metadata.location, dualRegion); + assert.strictEqual(metadata.locationType, 'dual-region'); +}); + it('should create a dual-region bucket with turbo replication enabled', async () => { const output = execSync( - `node createBucketWithTurboReplication.js ${bucketNameDualRegion}` + `node createBucketWithTurboReplication.js ${bucketNameDualRegionTurbo}` ); assert.match( output, new RegExp( - `${bucketNameDualRegion} created with the recovery point objective \\(RPO\\) set to ASYNC_TURBO in NAM4.` + `${bucketNameDualRegionTurbo} created with the recovery point objective \\(RPO\\) set to ASYNC_TURBO in NAM4.` ) ); - const [exists] = await dualRegionBucket.exists(); + const [exists] = await dualRegionBucketTurbo.exists(); assert.strictEqual(exists, true); }); it("should get a bucket's RPO metadata", async () => { - await storage.bucket(bucketNameDualRegion).setMetadata({ + await storage.bucket(bucketNameDualRegionTurbo).setMetadata({ rpo: RPO_ASYNC_TURBO, }); - const output = execSync(`node getRPO.js ${bucketNameDualRegion}`); + const output = execSync(`node getRPO.js ${bucketNameDualRegionTurbo}`); assert.match( output, - new RegExp(`RPO is ASYNC_TURBO for ${bucketNameDualRegion}.`) + new RegExp(`RPO is ASYNC_TURBO for ${bucketNameDualRegionTurbo}.`) ); - const metadata = await dualRegionBucket.getMetadata(); + const metadata = await dualRegionBucketTurbo.getMetadata(); assert.strictEqual(metadata[0].rpo, RPO_ASYNC_TURBO); }); it("should set a bucket's RPO to ASYNC_TURBO", async () => { - const output = execSync(`node setRPOAsyncTurbo.js ${bucketNameDualRegion}`); + const output = execSync( + `node setRPOAsyncTurbo.js ${bucketNameDualRegionTurbo}` + ); assert.match( output, - new RegExp(`Turbo replication enabled for ${bucketNameDualRegion}.`) + new RegExp(`Turbo replication enabled for ${bucketNameDualRegionTurbo}.`) ); - const metadata = await dualRegionBucket.getMetadata(); + const metadata = await dualRegionBucketTurbo.getMetadata(); assert.strictEqual(metadata[0].rpo, RPO_ASYNC_TURBO); }); it("should set a bucket's RPO to DEFAULT", async () => { - const output = execSync(`node setRPODefault.js ${bucketNameDualRegion}`); + const output = execSync(`node setRPODefault.js ${bucketNameDualRegionTurbo}`); assert.match( output, - new RegExp(`Turbo replication disabled for ${bucketNameDualRegion}.`) + new RegExp(`Turbo replication disabled for ${bucketNameDualRegionTurbo}.`) ); - const metadata = await dualRegionBucket.getMetadata(); + const metadata = await dualRegionBucketTurbo.getMetadata(); assert.strictEqual(metadata[0].rpo, RPO_DEFAULT); }); diff --git a/src/storage.ts b/src/storage.ts index cf2418ef5..2298544b6 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -753,7 +753,9 @@ export class Storage extends Service { * @property {Cors[]} [cors=[]] Specify the CORS configuration to use. * @property {boolean} [dra=false] Specify the storage class as Durable Reduced * Availability. - * @property {string} [location] Specify the location / region in which to create the bucket. + * @property {string} [location] Specify the bucket's location(s). If specifying + * a dual-region, can be specified as a string `"US-CENTRAL1+US-WEST1"`. + * For more information, see {@link https://cloud.google.com/storage/docs/locations| Bucket Locations}. * @property {boolean} [multiRegional=false] Specify the storage class as * Multi-Regional. * @property {boolean} [nearline=false] Specify the storage class as Nearline. @@ -761,7 +763,7 @@ export class Storage extends Service { * @property {boolean} [requesterPays=false] **Early Access Testers Only** * Force the use of the User Project metadata field to assign operational * costs when an operation is made on a Bucket and its objects. - * @property {string} [rpo] For dual region buckets, controls whether turbo + * @property {string} [rpo] For dual-region buckets, controls whether turbo * replication is enabled (`ASYNC_TURBO`) or disabled (`DEFAULT`). * @property {boolean} [standard=true] Specify the storage class as Standard. * @property {string} [storageClass] The new storage class. (`standard`, diff --git a/system-test/storage.ts b/system-test/storage.ts index 7c3e2cc86..4fd29c23c 100644 --- a/system-test/storage.ts +++ b/system-test/storage.ts @@ -929,6 +929,29 @@ describe('storage', () => { }); }); + describe('dual-region', () => { + let bucket: Bucket; + + const REGION1 = 'US-EAST1'; + const REGION2 = 'US-WEST1'; + + beforeEach(() => { + bucket = storage.bucket(generateName()); + }); + + it('creates a dual-region bucket', async () => { + const dualRegion = `${REGION1}+${REGION2}`; + await bucket.create({location: dualRegion}); + + const [exists] = await bucket.exists(); + assert.strictEqual(exists, true); + + const [bucketMetadata] = await bucket.getMetadata(); + assert.strictEqual(bucketMetadata.location, dualRegion); + assert.strictEqual(bucketMetadata.locationType, 'dual-region'); + }); + }); + describe('uniform bucket-level access', () => { let bucket: Bucket;