Skip to content

Commit 9132c21

Browse files
authored
samples: adds samples for CMEK (#989)
* samples: adds samples for CMEK Adds samples to create an encrypted database, to create an encrypted backup and to restore to an encrypted database. * samples: fix checkstyle violations * samples: addresses PR comments. * samples: fixes encryption key tests * samples: prints user provided key in backup sample Prints out the user provided key in the encrypted backup sample, instead of printing out the Backup.encryption_info.kms_key_version. This should align with the key that we are printing on the other samples (instead of printing a key version). * tests: verifies the key returned in create backup Verifies that the key used in the create backup is returned in the response correctly. * samples: addresses PR comments
1 parent 22412e0 commit 9132c21

File tree

10 files changed

+531
-11
lines changed

10 files changed

+531
-11
lines changed

google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITBackupTest.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ public void testBackups() throws InterruptedException, ExecutionException {
245245
.build()));
246246

247247
// Verifies that the database encryption has been properly set
248-
testDatabaseEncryption(db1);
248+
testDatabaseEncryption(db1, keyName);
249249

250250
// Create two backups in parallel.
251251
String backupId1 = testHelper.getUniqueBackupId() + "_bck1";
@@ -314,7 +314,7 @@ public void testBackups() throws InterruptedException, ExecutionException {
314314
// Verifies that backup version time is the specified one
315315
testBackupVersionTime(backup1, versionTime);
316316
// Verifies that backup encryption has been properly set
317-
testBackupEncryption(backup1);
317+
testBackupEncryption(backup1, keyName);
318318

319319
// Insert some more data into db2 to get a timestamp from the server.
320320
Timestamp commitTs =
@@ -374,7 +374,7 @@ public void testBackups() throws InterruptedException, ExecutionException {
374374
testGetBackup(db2, backupId2, expireTime);
375375
testUpdateBackup(backup1);
376376
testCreateInvalidExpirationDate(db1);
377-
testRestore(backup1, op1, versionTime);
377+
testRestore(backup1, versionTime, keyName);
378378

379379
testDelete(backupId2);
380380
testCancelBackupOperation(db1);
@@ -447,17 +447,17 @@ private void testBackupVersionTime(Backup backup, Timestamp versionTime) {
447447
logger.info("Done verifying backup version time for " + backup.getId());
448448
}
449449

450-
private void testDatabaseEncryption(Database database) {
450+
private void testDatabaseEncryption(Database database, String expectedKey) {
451451
logger.info("Verifying database encryption for " + database.getId());
452452
assertThat(database.getEncryptionConfig()).isNotNull();
453-
assertThat(database.getEncryptionConfig().getKmsKeyName()).isEqualTo(keyName);
453+
assertThat(database.getEncryptionConfig().getKmsKeyName()).isEqualTo(expectedKey);
454454
logger.info("Done verifying database encryption for " + database.getId());
455455
}
456456

457-
private void testBackupEncryption(Backup backup) {
457+
private void testBackupEncryption(Backup backup, String expectedKey) {
458458
logger.info("Verifying backup encryption for " + backup.getId());
459459
assertThat(backup.getEncryptionInfo()).isNotNull();
460-
assertThat(backup.getEncryptionInfo().getKmsKeyVersion()).isNotNull();
460+
assertThat(backup.getEncryptionInfo().getKmsKeyVersion()).contains(expectedKey);
461461
logger.info("Done verifying backup encryption for " + backup.getId());
462462
}
463463

@@ -620,8 +620,7 @@ private void testDelete(String backupId) throws InterruptedException {
620620
logger.info("Finished delete tests");
621621
}
622622

623-
private void testRestore(
624-
Backup backup, OperationFuture<Backup, CreateBackupMetadata> backupOp, Timestamp versionTime)
623+
private void testRestore(Backup backup, Timestamp versionTime, String expectedKey)
625624
throws InterruptedException, ExecutionException {
626625
// Restore the backup to a new database.
627626
String restoredDb = testHelper.getUniqueDatabaseId();
@@ -636,7 +635,7 @@ private void testRestore(
636635
final Restore restore =
637636
dbAdminClient
638637
.newRestoreBuilder(backup.getId(), DatabaseId.of(projectId, instanceId, restoredDb))
639-
.setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(keyName))
638+
.setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(expectedKey))
640639
.build();
641640
restoreOperation = dbAdminClient.restoreDatabase(restore);
642641
restoreOperationName = restoreOperation.getName();
@@ -687,7 +686,7 @@ private void testRestore(
687686
Timestamp.fromProto(
688687
reloadedDatabase.getProto().getRestoreInfo().getBackupInfo().getVersionTime()))
689688
.isEqualTo(versionTime);
690-
testDatabaseEncryption(reloadedDatabase);
689+
testDatabaseEncryption(reloadedDatabase, expectedKey);
691690

692691
// Restoring the backup to an existing database should fail.
693692
try {

samples/install-without-bom/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@
143143
<configuration>
144144
<systemPropertyVariables>
145145
<spanner.test.instance>spanner-testing</spanner.test.instance>
146+
<spanner.test.key.location>us-central1</spanner.test.key.location>
147+
<spanner.test.key.ring>spanner-test-keyring</spanner.test.key.ring>
148+
<spanner.test.key.name>spanner-test-key</spanner.test.key.name>
146149
<spanner.sample.database>mysample</spanner.sample.database>
147150
<spanner.quickstart.database>quick-db</spanner.quickstart.database>
148151
</systemPropertyVariables>

samples/snapshot/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@
142142
<configuration>
143143
<systemPropertyVariables>
144144
<spanner.test.instance>spanner-testing</spanner.test.instance>
145+
<spanner.test.key.location>us-central1</spanner.test.key.location>
146+
<spanner.test.key.ring>spanner-test-keyring</spanner.test.key.ring>
147+
<spanner.test.key.name>spanner-test-key</spanner.test.key.name>
145148
<spanner.sample.database>mysample</spanner.sample.database>
146149
<spanner.quickstart.database>quick-db</spanner.quickstart.database>
147150
</systemPropertyVariables>

samples/snippets/pom.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@
147147
<configuration>
148148
<systemPropertyVariables>
149149
<spanner.test.instance>spanner-testing</spanner.test.instance>
150+
<spanner.test.key.location>us-central1</spanner.test.key.location>
151+
<spanner.test.key.ring>spanner-test-keyring</spanner.test.key.ring>
152+
<spanner.test.key.name>spanner-test-key</spanner.test.key.name>
150153
<spanner.sample.database>mysample</spanner.sample.database>
151154
<spanner.quickstart.database>quick-db</spanner.quickstart.database>
152155
</systemPropertyVariables>
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.spanner;
18+
19+
// [START spanner_create_backup_with_encryption_key]
20+
21+
import com.google.api.gax.longrunning.OperationFuture;
22+
import com.google.cloud.Timestamp;
23+
import com.google.cloud.spanner.Backup;
24+
import com.google.cloud.spanner.BackupId;
25+
import com.google.cloud.spanner.DatabaseAdminClient;
26+
import com.google.cloud.spanner.DatabaseId;
27+
import com.google.cloud.spanner.Spanner;
28+
import com.google.cloud.spanner.SpannerExceptionFactory;
29+
import com.google.cloud.spanner.SpannerOptions;
30+
import com.google.cloud.spanner.encryption.EncryptionConfigs;
31+
import com.google.spanner.admin.database.v1.CreateBackupMetadata;
32+
import java.util.concurrent.ExecutionException;
33+
import java.util.concurrent.TimeUnit;
34+
import java.util.concurrent.TimeoutException;
35+
import org.threeten.bp.LocalDateTime;
36+
import org.threeten.bp.OffsetDateTime;
37+
38+
public class CreateBackupWithEncryptionKey {
39+
40+
static void createBackupWithEncryptionKey() throws InterruptedException {
41+
// TODO(developer): Replace these variables before running the sample.
42+
String projectId = "my-project";
43+
String instanceId = "my-instance";
44+
String databaseId = "my-database";
45+
String backupId = "my-backup";
46+
String kmsKeyName =
47+
"projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";
48+
49+
try (Spanner spanner =
50+
SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
51+
DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
52+
createBackupWithEncryptionKey(
53+
adminClient,
54+
projectId,
55+
instanceId,
56+
databaseId,
57+
backupId,
58+
kmsKeyName);
59+
}
60+
}
61+
62+
static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient,
63+
String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName)
64+
throws InterruptedException {
65+
// Set expire time to 14 days from now.
66+
final Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert(
67+
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS));
68+
final Backup backupToCreate = adminClient
69+
.newBackupBuilder(BackupId.of(projectId, instanceId, backupId))
70+
.setDatabase(DatabaseId.of(projectId, instanceId, databaseId))
71+
.setExpireTime(expireTime)
72+
.setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName))
73+
.build();
74+
final OperationFuture<Backup, CreateBackupMetadata> operation = adminClient
75+
.createBackup(backupToCreate);
76+
77+
Backup backup;
78+
try {
79+
System.out.println("Waiting for operation to complete...");
80+
backup = operation.get(1200, TimeUnit.SECONDS);
81+
} catch (ExecutionException e) {
82+
// If the operation failed during execution, expose the cause.
83+
throw SpannerExceptionFactory.asSpannerException(e.getCause());
84+
} catch (InterruptedException e) {
85+
// Throw when a thread is waiting, sleeping, or otherwise occupied,
86+
// and the thread is interrupted, either before or during the activity.
87+
throw SpannerExceptionFactory.propagateInterrupt(e);
88+
} catch (TimeoutException e) {
89+
// If the operation timed out propagates the timeout
90+
throw SpannerExceptionFactory.propagateTimeout(e);
91+
}
92+
93+
System.out.printf(
94+
"Backup %s of size %d bytes was created at %s using encryption key %s%n",
95+
backup.getId().getName(),
96+
backup.getSize(),
97+
LocalDateTime.ofEpochSecond(
98+
backup.getProto().getCreateTime().getSeconds(),
99+
backup.getProto().getCreateTime().getNanos(),
100+
OffsetDateTime.now().getOffset()),
101+
kmsKeyName
102+
);
103+
104+
return null;
105+
}
106+
}
107+
// [END spanner_create_backup_with_encryption_key]
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.spanner;
18+
19+
// [START spanner_create_database_with_encryption_key]
20+
21+
import com.google.api.gax.longrunning.OperationFuture;
22+
import com.google.cloud.spanner.Database;
23+
import com.google.cloud.spanner.DatabaseAdminClient;
24+
import com.google.cloud.spanner.DatabaseId;
25+
import com.google.cloud.spanner.Spanner;
26+
import com.google.cloud.spanner.SpannerExceptionFactory;
27+
import com.google.cloud.spanner.SpannerOptions;
28+
import com.google.cloud.spanner.encryption.EncryptionConfigs;
29+
import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
30+
import java.util.Arrays;
31+
import java.util.concurrent.ExecutionException;
32+
import java.util.concurrent.TimeUnit;
33+
import java.util.concurrent.TimeoutException;
34+
35+
public class CreateDatabaseWithEncryptionKey {
36+
37+
static void createDatabaseWithEncryptionKey() {
38+
// TODO(developer): Replace these variables before running the sample.
39+
String projectId = "my-project";
40+
String instanceId = "my-instance";
41+
String databaseId = "my-database";
42+
String kmsKeyName =
43+
"projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";
44+
45+
try (Spanner spanner =
46+
SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
47+
DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
48+
createDatabaseWithEncryptionKey(
49+
adminClient,
50+
projectId,
51+
instanceId,
52+
databaseId,
53+
kmsKeyName);
54+
}
55+
}
56+
57+
static Void createDatabaseWithEncryptionKey(DatabaseAdminClient adminClient,
58+
String projectId, String instanceId, String databaseId, String kmsKeyName) {
59+
final Database databaseToCreate = adminClient
60+
.newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId))
61+
.setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName))
62+
.build();
63+
final OperationFuture<Database, CreateDatabaseMetadata> operation = adminClient
64+
.createDatabase(databaseToCreate, Arrays.asList(
65+
"CREATE TABLE Singers ("
66+
+ " SingerId INT64 NOT NULL,"
67+
+ " FirstName STRING(1024),"
68+
+ " LastName STRING(1024),"
69+
+ " SingerInfo BYTES(MAX)"
70+
+ ") PRIMARY KEY (SingerId)",
71+
"CREATE TABLE Albums ("
72+
+ " SingerId INT64 NOT NULL,"
73+
+ " AlbumId INT64 NOT NULL,"
74+
+ " AlbumTitle STRING(MAX)"
75+
+ ") PRIMARY KEY (SingerId, AlbumId),"
76+
+ " INTERLEAVE IN PARENT Singers ON DELETE CASCADE"
77+
));
78+
try {
79+
System.out.println("Waiting for operation to complete...");
80+
Database createdDatabase = operation.get(120, TimeUnit.SECONDS);
81+
82+
System.out.printf(
83+
"Database %s created with encryption key %s%n",
84+
createdDatabase.getId(),
85+
createdDatabase.getEncryptionConfig().getKmsKeyName()
86+
);
87+
} catch (ExecutionException e) {
88+
// If the operation failed during execution, expose the cause.
89+
throw SpannerExceptionFactory.asSpannerException(e.getCause());
90+
} catch (InterruptedException e) {
91+
// Throw when a thread is waiting, sleeping, or otherwise occupied,
92+
// and the thread is interrupted, either before or during the activity.
93+
throw SpannerExceptionFactory.propagateInterrupt(e);
94+
} catch (TimeoutException e) {
95+
// If the operation timed out propagates the timeout
96+
throw SpannerExceptionFactory.propagateTimeout(e);
97+
}
98+
return null;
99+
}
100+
}
101+
// [END spanner_create_database_with_encryption_key]

0 commit comments

Comments
 (0)