Skip to content
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

feat: allow multiple KMS keys to create CMEK database/backup #2099

Merged
merged 15 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions samples/backups-copy-with-multiple-kms-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2024 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.

// sample-metadata:
// title: Copies a source backup
// usage: node spannerCopyBackup <INSTANCE_ID> <COPY_BACKUP_ID> <SOURCE_BACKUP_ID> <PROJECT_ID>

'use strict';

function main(
instanceId = 'my-instance',
backupId = 'my-backup',
sourceBackupPath = 'projects/my-project-id/instances/my-source-instance/backups/my-source-backup',
projectId = 'my-project-id',
kmsKeyNames = 'key1,key2'
) {
// [START spanner_copy_backup_with_MR_CMEK]
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
// const instanceId = 'my-instance';
// const backupId = 'my-backup',
// const sourceBackupPath = 'projects/my-project-id/instances/my-source-instance/backups/my-source-backup',
// const projectId = 'my-project-id';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,
// projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';

// Imports the Google Cloud Spanner client library
const {Spanner} = require('@google-cloud/spanner');
const {PreciseDate} = require('@google-cloud/precise-date');

// Creates a client
const spanner = new Spanner({
projectId: projectId,
});

// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();

async function spannerCopyBackupWithMultipleKmsKeys() {
// Expire copy backup 14 days in the future
const expireTime = Spanner.timestamp(
Date.now() + 1000 * 60 * 60 * 24 * 14
).toStruct();

// Copy the source backup
try {
console.log(`Creating copy of the source backup ${sourceBackupPath}.`);
const [operation] = await databaseAdminClient.copyBackup({
parent: databaseAdminClient.instancePath(projectId, instanceId),
sourceBackup: sourceBackupPath,
backupId: backupId,
expireTime: expireTime,
kmsKeyNames: kmsKeyNames.split(','),
});

console.log(
`Waiting for backup copy ${databaseAdminClient.backupPath(
projectId,
instanceId,
backupId
)} to complete...`
);
await operation.promise();

// Verify the copy backup is ready
const [copyBackup] = await databaseAdminClient.getBackup({
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
});

if (copyBackup.state === 'READY') {
console.log(
`Backup copy ${copyBackup.name} of size ` +
`${copyBackup.sizeBytes} bytes was created at ` +
`${new PreciseDate(copyBackup.createTime).toISOString()} ` +
'with version time ' +
`${new PreciseDate(copyBackup.versionTime).toISOString()}`
);
} else {
console.error('ERROR: Copy of backup is not ready.');
}
} catch (err) {
console.error('ERROR:', err);
}
}
spannerCopyBackupWithMultipleKmsKeys();
// [END spanner_copy_backup_with_MR_CMEK]
}
process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
125 changes: 125 additions & 0 deletions samples/backups-create-with-multiple-kms-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sample content is all good but new samples should follow rubric format . Refer sample https://github.com/googleapis/nodejs-spanner/blob/main/samples/instance-partition-create.js

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. PTAL.

* Copyright 2024 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';

function main(
instanceId = 'my-instance',
databaseId = 'my-database',
backupId = 'my-backup',
projectId = 'my-project-id',
kmsKeyNames = 'key1,key2'
) {
// [START spanner_create_backup_with_MR_CMEK]
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';

// Imports the Google Cloud client library
const {Spanner, protos} = require('@google-cloud/spanner');
const {PreciseDate} = require('@google-cloud/precise-date');

// Creates a client
const spanner = new Spanner({
projectId: projectId,
});

// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();
async function createBackupWithMultipleKmsKeys() {
// Creates a new backup of the database
try {
console.log(
`Creating backup of database ${databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId
)}.`
);

// Expire backup 14 days in the future
const expireTime = Date.now() + 1000 * 60 * 60 * 24 * 14;

// Create a backup of the state of the database at the current time.
const [operation] = await databaseAdminClient.createBackup({
parent: databaseAdminClient.instancePath(projectId, instanceId),
backupId: backupId,
backup: (protos.google.spanner.admin.database.v1.Backup = {
database: databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId
),
expireTime: Spanner.timestamp(expireTime).toStruct(),
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
}),
encryptionConfig: {
encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
kmsKeyNames: kmsKeyNames.split(','),
},
});

console.log(
`Waiting for backup ${databaseAdminClient.backupPath(
projectId,
instanceId,
backupId
)} to complete...`
);
await operation.promise();

// Verify backup is ready
const [backupInfo] = await databaseAdminClient.getBackup({
name: databaseAdminClient.backupPath(projectId, instanceId, backupId),
});

const kmsKeyVersions = backupInfo.encryptionInformation
.map(encryptionInfo => encryptionInfo.kmsKeyVersion)
.join(', ');

if (backupInfo.state === 'READY') {
console.log(
`Backup ${backupInfo.name} of size ` +
`${backupInfo.sizeBytes} bytes was created at ` +
`${new PreciseDate(backupInfo.createTime).toISOString()} ` +
`using encryption key ${kmsKeyVersions}`
);
} else {
console.error('ERROR: Backup is not ready.');
}
} catch (err) {
console.error('ERROR:', err);
} finally {
// Close the spanner client when finished.
// The databaseAdminClient does not require explicit closure. The closure of the Spanner client will automatically close the databaseAdminClient.
spanner.close();
}
}
createBackupWithMultipleKmsKeys();
// [END spanner_create_backup_with_MR_CMEK]
}

process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
89 changes: 89 additions & 0 deletions samples/backups-restore-with-multiple-kms-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Copyright 2024 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';

function main(
instanceId = 'my-instance',
databaseId = 'my-database',
backupId = 'my-backup',
projectId = 'my-project',
kmsKeyNames = 'key1,key2'
) {
// [START spanner_restore_backup_with_MR_CMEK]
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,
// projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';

// Imports the Google Cloud client library and precise date library
const {Spanner} = require('@google-cloud/spanner');

// Creates a client
const spanner = new Spanner({
projectId: projectId,
});

// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();

async function restoreBackupWithMultipleKmsKeys() {
// Restore the database
console.log(
`Restoring database ${databaseAdminClient.databasePath(
projectId,
instanceId,
databaseId
)} from backup ${backupId}.`
);
const [restoreOperation] = await databaseAdminClient.restoreDatabase({
parent: databaseAdminClient.instancePath(projectId, instanceId),
databaseId: databaseId,
backup: databaseAdminClient.backupPath(projectId, instanceId, backupId),
encryptionConfig: {
encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
kmsKeyNames: kmsKeyNames.split(','),
},
});

// Wait for restore to complete
console.log('Waiting for database restore to complete...');
await restoreOperation.promise();

console.log('Database restored from backup.');
const [metadata] = await databaseAdminClient.getDatabase({
name: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
});
console.log(
`Database ${metadata.restoreInfo.backupInfo.sourceDatabase} was restored ` +
`to ${databaseId} from backup ${metadata.restoreInfo.backupInfo.backup} ` +
`using encryption key ${metadata.encryptionConfig.kmsKeyNames}.`
);
}
restoreBackupWithMultipleKmsKeys();
// [END spanner_restore_backup_with_MR_CMEK]
}

process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
77 changes: 77 additions & 0 deletions samples/database-create-with-multiple-kms-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2024 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';

function main(
instanceId = 'my-instance',
databaseId = 'my-database',
projectId = 'my-project',
kmsKeyNames = 'key1,key2,key3'
) {
// [START spanner_create_database_with_MR_CMEK]
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const kmsKeyNames =
// 'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key1,projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key2';

// Imports the Google Cloud client library
const {Spanner, protos} = require('@google-cloud/spanner');

// creates a client
const spanner = new Spanner({
projectId: projectId,
});

// Gets a reference to a Cloud Spanner Database Admin Client object
const databaseAdminClient = spanner.getDatabaseAdminClient();

async function createDatabaseWithMultipleKmsKeys() {
// Creates a database
const [operation] = await databaseAdminClient.createDatabase({
createStatement: 'CREATE DATABASE `' + databaseId + '`',
parent: databaseAdminClient.instancePath(projectId, instanceId),
encryptionConfig:
(protos.google.spanner.admin.database.v1.EncryptionConfig = {
kmsKeyNames: kmsKeyNames.split(','),
}),
});

console.log(`Waiting for operation on ${databaseId} to complete...`);
await operation.promise();

console.log(`Created database ${databaseId} on instance ${instanceId}.`);

// Get encryption key
const [metadata] = await databaseAdminClient.getDatabase({
name: databaseAdminClient.databasePath(projectId, instanceId, databaseId),
});

console.log(
`Database encrypted with keys ${metadata.encryptionConfig.kmsKeyNames}.`
);
}
createDatabaseWithMultipleKmsKeys();
// [END spanner_create_database_with_MR_CMEK]
}

process.on('unhandledRejection', err => {
console.error(err.message);
process.exitCode = 1;
});
main(...process.argv.slice(2));
Loading
Loading