Skip to content

Commit 4626282

Browse files
committed
feat: add CMEK support (via synth)
1 parent 2bdd21c commit 4626282

File tree

16 files changed

+532
-48
lines changed

16 files changed

+532
-48
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pip-log.txt
4545

4646
# Built documentation
4747
docs/_build
48+
bigquery/docs/generated
4849
docs.metadata
4950

5051
# Virtual environment

docs/conf.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,11 @@
345345

346346
# Example configuration for intersphinx: refer to the Python standard library.
347347
intersphinx_mapping = {
348-
"python": ("http://python.readthedocs.org/en/latest/", None),
349-
"google-auth": ("https://google-auth.readthedocs.io/en/stable", None),
348+
"python": ("https://python.readthedocs.org/en/latest/", None),
349+
"google-auth": ("https://googleapis.dev/python/google-auth/latest/", None),
350350
"google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,),
351-
"grpc": ("https://grpc.io/grpc/python/", None),
351+
"grpc": ("https://grpc.github.io/grpc/python/", None),
352+
"proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None),
352353
}
353354

354355

google/cloud/spanner_admin_database_v1/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from .services.database_admin import DatabaseAdminClient
1919
from .types.backup import Backup
2020
from .types.backup import BackupInfo
21+
from .types.backup import CreateBackupEncryptionConfig
2122
from .types.backup import CreateBackupMetadata
2223
from .types.backup import CreateBackupRequest
2324
from .types.backup import DeleteBackupRequest
@@ -27,6 +28,8 @@
2728
from .types.backup import ListBackupsRequest
2829
from .types.backup import ListBackupsResponse
2930
from .types.backup import UpdateBackupRequest
31+
from .types.common import EncryptionConfig
32+
from .types.common import EncryptionInfo
3033
from .types.common import OperationProgress
3134
from .types.spanner_database_admin import CreateDatabaseMetadata
3235
from .types.spanner_database_admin import CreateDatabaseRequest
@@ -40,6 +43,7 @@
4043
from .types.spanner_database_admin import ListDatabasesRequest
4144
from .types.spanner_database_admin import ListDatabasesResponse
4245
from .types.spanner_database_admin import OptimizeRestoredDatabaseMetadata
46+
from .types.spanner_database_admin import RestoreDatabaseEncryptionConfig
4347
from .types.spanner_database_admin import RestoreDatabaseMetadata
4448
from .types.spanner_database_admin import RestoreDatabaseRequest
4549
from .types.spanner_database_admin import RestoreInfo
@@ -51,13 +55,16 @@
5155
__all__ = (
5256
"Backup",
5357
"BackupInfo",
58+
"CreateBackupEncryptionConfig",
5459
"CreateBackupMetadata",
5560
"CreateBackupRequest",
5661
"CreateDatabaseMetadata",
5762
"CreateDatabaseRequest",
5863
"Database",
5964
"DeleteBackupRequest",
6065
"DropDatabaseRequest",
66+
"EncryptionConfig",
67+
"EncryptionInfo",
6168
"GetBackupRequest",
6269
"GetDatabaseDdlRequest",
6370
"GetDatabaseDdlResponse",
@@ -72,6 +79,7 @@
7279
"ListDatabasesResponse",
7380
"OperationProgress",
7481
"OptimizeRestoredDatabaseMetadata",
82+
"RestoreDatabaseEncryptionConfig",
7583
"RestoreDatabaseMetadata",
7684
"RestoreDatabaseRequest",
7785
"RestoreInfo",

google/cloud/spanner_admin_database_v1/proto/backup.proto

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import "google/longrunning/operations.proto";
2222
import "google/protobuf/field_mask.proto";
2323
import "google/protobuf/timestamp.proto";
2424
import "google/spanner/admin/database/v1/common.proto";
25-
import "google/api/annotations.proto";
2625

2726
option csharp_namespace = "Google.Cloud.Spanner.Admin.Database.V1";
2827
option go_package = "google.golang.org/genproto/googleapis/spanner/admin/database/v1;database";
@@ -109,6 +108,11 @@ message Backup {
109108
// restored database from the backup enters the `READY` state, the reference
110109
// to the backup is removed.
111110
repeated string referencing_databases = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
111+
112+
// Output only. The encryption information for the backup.
113+
// If the encryption key protecting this resource is customer managed, then
114+
// kms_key_version will be filled.
115+
EncryptionInfo encryption_info = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
112116
}
113117

114118
// The request for [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup].
@@ -133,6 +137,14 @@ message CreateBackupRequest {
133137

134138
// Required. The backup to create.
135139
Backup backup = 3 [(google.api.field_behavior) = REQUIRED];
140+
141+
// Optional. An encryption configuration describing the encryption type and key
142+
// resources in Cloud KMS used to encrypt the backup. If no
143+
// `encryption_config` is specified, the backup will use the same
144+
// encryption configuration as the database by default, namely
145+
// [encryption_type][google.spanner.admin.database.v1.CreateBackupEncryptionConfig.encryption_type] =
146+
// USE_DATABASE_ENCRYPTION.
147+
CreateBackupEncryptionConfig encryption_config = 4 [(google.api.field_behavior) = OPTIONAL];
136148
}
137149

138150
// Metadata type for the operation returned by
@@ -384,3 +396,43 @@ message BackupInfo {
384396
type: "spanner.googleapis.com/Database"
385397
}];
386398
}
399+
400+
// Encryption configuration for the backup to create.
401+
message CreateBackupEncryptionConfig {
402+
// Encryption types for the backup.
403+
enum EncryptionType {
404+
// Unspecified. Do not use.
405+
ENCRYPTION_TYPE_UNSPECIFIED = 0;
406+
407+
// Use the same encryption configuration as the database. This is the
408+
// default option when
409+
// [encryption_config][CreateBackupEncryptionConfig.encryption_config] is
410+
// empty. If the database is using customer managed encryption, the
411+
// backup will be using the same KMS key.
412+
USE_DATABASE_ENCRYPTION = 1;
413+
414+
// Enforce google default encryption.
415+
GOOGLE_DEFAULT_ENCRYPTION = 2;
416+
417+
// Enforce customer managed encryption. If specified, the kms_key_name
418+
// must provide a valid Cloud KMS key name.
419+
CUSTOMER_MANAGED_ENCRYPTION = 3;
420+
}
421+
422+
// Required. The encryption type of the backup.
423+
EncryptionType encryption_type = 1 [(google.api.field_behavior) = REQUIRED];
424+
425+
// Optional. The resource name of the Cloud KMS key that will be used to
426+
// protect the backup. Once specified, the backup will enforce customer
427+
// managed encryption, regardless of the database encryption type.
428+
// This field should be set only when
429+
// [encryption_type][google.spanner.admin.database.v1.CreateBackupEncryptionConfig.encryption_type] is
430+
// CUSTOMER_MANAGED_ENCRYPTION. Values are of the form
431+
// `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>`.
432+
string kms_key_name = 2 [
433+
(google.api.field_behavior) = OPTIONAL,
434+
(google.api.resource_reference) = {
435+
type: "cloudkms.googleapis.com/CryptoKey"
436+
}
437+
];
438+
}

google/cloud/spanner_admin_database_v1/proto/common.proto

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ syntax = "proto3";
1717
package google.spanner.admin.database.v1;
1818

1919
import "google/api/field_behavior.proto";
20+
import "google/api/resource.proto";
2021
import "google/protobuf/timestamp.proto";
21-
import "google/api/annotations.proto";
22+
import "google/rpc/status.proto";
2223

2324
option csharp_namespace = "Google.Cloud.Spanner.Admin.Database.V1";
2425
option go_package = "google.golang.org/genproto/googleapis/spanner/admin/database/v1;database";
@@ -27,6 +28,14 @@ option java_outer_classname = "CommonProto";
2728
option java_package = "com.google.spanner.admin.database.v1";
2829
option php_namespace = "Google\\Cloud\\Spanner\\Admin\\Database\\V1";
2930
option ruby_package = "Google::Cloud::Spanner::Admin::Database::V1";
31+
option (google.api.resource_definition) = {
32+
type: "cloudkms.googleapis.com/CryptoKey"
33+
pattern: "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}"
34+
};
35+
option (google.api.resource_definition) = {
36+
type: "cloudkms.googleapis.com/CryptoKeyVersion"
37+
pattern: "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}/cryptoKeyVersions/{crypto_key_version}"
38+
};
3039

3140
// Encapsulates progress related information for a Cloud Spanner long
3241
// running operation.
@@ -42,3 +51,56 @@ message OperationProgress {
4251
// successfully.
4352
google.protobuf.Timestamp end_time = 3;
4453
}
54+
55+
// Encryption configuration describing key resources in Cloud KMS used to
56+
// encrypt/decrypt a Cloud Spanner database.
57+
message EncryptionConfig {
58+
// The resource name of the Cloud KMS key that was used to encrypt and decrypt
59+
// the database. The form of the kms_key_name is
60+
// `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys\
61+
// /<kms_key_name>`.
62+
// api-linter: core::0122::name-suffix=disabled
63+
// aip.dev/not-precedent: crypto key identifiers like this are listed as a
64+
// canonical example of when field names would be ambiguous without the
65+
// _name suffix and should therefore include it.
66+
string kms_key_name = 2 [(google.api.resource_reference) = {
67+
type: "cloudkms.googleapis.com/CryptoKey"
68+
}];
69+
}
70+
71+
// Encryption information for a given resource.
72+
// If this resource is protected with customer managed encryption, the in-use
73+
// Cloud KMS key versions will be specified along with their status.
74+
// CMEK is not currently available to end users.
75+
message EncryptionInfo {
76+
// Possible encryption types for a resource.
77+
enum Type {
78+
// Encryption type was not specified, though data at rest remains encrypted.
79+
TYPE_UNSPECIFIED = 0;
80+
81+
// The data backing this resource is encrypted at rest with a key that is
82+
// fully managed by Google. No key version or status will be populated.
83+
// This is the default state.
84+
GOOGLE_DEFAULT_ENCRYPTION = 1;
85+
86+
// The data backing this resource is encrypted at rest with a key that is
87+
// managed by the customer. The active version of the key. 'kms_key_version'
88+
// will be populated, and 'encryption_status' may be populated.
89+
CUSTOMER_MANAGED_ENCRYPTION = 2;
90+
}
91+
92+
// Output only. The type of encryption used to protect this resource.
93+
Type encryption_type = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
94+
95+
// Output only. If present, the status of a recent encrypt/decrypt calls on underlying data
96+
// for this resource. Regardless of status, data is always encrypted at rest.
97+
google.rpc.Status encryption_status = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
98+
99+
// Output only. The Cloud KMS key versions used for a CMEK-protected Spanner resource.
100+
string kms_key_version = 2 [
101+
(google.api.field_behavior) = OUTPUT_ONLY,
102+
(google.api.resource_reference) = {
103+
type: "cloudkms.googleapis.com/CryptoKeyVersion"
104+
}
105+
];
106+
}

google/cloud/spanner_admin_database_v1/proto/spanner_database_admin.proto

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ message Database {
369369
// about the restore source.
370370
RestoreInfo restore_info = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
371371

372+
// Output only. Custom encryption configuration (Cloud KMS keys).
373+
// Applicable only for databases using the Customer Managed Encryption Keys
374+
// feature.
375+
EncryptionConfig encryption_config = 5
376+
[(google.api.field_behavior) = OUTPUT_ONLY];
377+
372378
// Output only. The period in which Cloud Spanner retains all versions of data
373379
// for the database. This is the same as the value of version_retention_period
374380
// database option set using
@@ -436,6 +442,10 @@ message CreateDatabaseRequest {
436442
// statements execute atomically with the creation of the database:
437443
// if there is an error in any statement, the database is not created.
438444
repeated string extra_statements = 3 [(google.api.field_behavior) = OPTIONAL];
445+
446+
// Optional.
447+
EncryptionConfig encryption_config = 4
448+
[(google.api.field_behavior) = OPTIONAL];
439449
}
440450

441451
// Metadata type for the operation returned by
@@ -673,6 +683,56 @@ message RestoreDatabaseRequest {
673683
type: "spanner.googleapis.com/Backup"
674684
}];
675685
}
686+
687+
// Optional. An encryption configuration describing the encryption type and key
688+
// resources in Cloud KMS used to encrypt/decrypt the database to restore to.
689+
// If no `encryption_config` is specified, the restored database will use
690+
// the config default (if set) or the same encryption configuration as
691+
// the backup by default, namely
692+
// [encryption_type][google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.encryption_type] =
693+
// USE_CONFIG_DEFAULT_OR_DATABASE_ENCRYPTION.
694+
RestoreDatabaseEncryptionConfig encryption_config = 4 [(google.api.field_behavior) = OPTIONAL];
695+
}
696+
697+
// Encryption configuration for the database to restore to.
698+
message RestoreDatabaseEncryptionConfig {
699+
// Encryption types for the database to be restored.
700+
enum EncryptionType {
701+
// Unspecified. Do not use.
702+
ENCRYPTION_TYPE_UNSPECIFIED = 0;
703+
704+
// This is the default option when
705+
// [encryption_config][RestoreDatabaseEncryptionConfig.encryption_config] is
706+
// empty. It will first check whether there is a config default and use
707+
// it if set. if not set, it will use the backup encryption setting. Note
708+
// that the config default feature is a new feature that may not be
709+
// available at the beginning.
710+
USE_CONFIG_DEFAULT_OR_BACKUP_ENCRYPTION = 1;
711+
712+
// Enforce google default encryption.
713+
GOOGLE_DEFAULT_ENCRYPTION = 2;
714+
715+
// Enforce customer managed encryption. If specified, the kms_key_name
716+
// must provide a valid Cloud KMS key name.
717+
CUSTOMER_MANAGED_ENCRYPTION = 3;
718+
}
719+
720+
// Required. The encryption type of the restored database.
721+
EncryptionType encryption_type = 1 [(google.api.field_behavior) = REQUIRED];
722+
723+
// Optional. The resource name of the Cloud KMS key that will be used to
724+
// encrypt/decrypt the database to restore to. Once specified, the database
725+
// will enforce customer managed encryption, regardless of the backup
726+
// encryption type. This field should be set only when
727+
// [encryption_type][google.spanner.admin.database.v1.RestoreDatabaseEncryptionConfig.encryption_type] is
728+
// CUSTOMER_MANAGED_ENCRYPTION. Values are of the form
729+
// `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>`.
730+
string kms_key_name = 2 [
731+
(google.api.field_behavior) = OPTIONAL,
732+
(google.api.resource_reference) = {
733+
type: "cloudkms.googleapis.com/CryptoKey"
734+
}
735+
];
676736
}
677737

678738
// Metadata type for the long-running operation returned by

google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from google.cloud.spanner_admin_database_v1.services.database_admin import pagers
3434
from google.cloud.spanner_admin_database_v1.types import backup
3535
from google.cloud.spanner_admin_database_v1.types import backup as gsad_backup
36+
from google.cloud.spanner_admin_database_v1.types import common
3637
from google.cloud.spanner_admin_database_v1.types import spanner_database_admin
3738
from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore
3839
from google.iam.v1 import policy_pb2 as policy # type: ignore
@@ -62,6 +63,12 @@ class DatabaseAdminAsyncClient:
6263

6364
backup_path = staticmethod(DatabaseAdminClient.backup_path)
6465
parse_backup_path = staticmethod(DatabaseAdminClient.parse_backup_path)
66+
crypto_key_path = staticmethod(DatabaseAdminClient.crypto_key_path)
67+
parse_crypto_key_path = staticmethod(DatabaseAdminClient.parse_crypto_key_path)
68+
crypto_key_version_path = staticmethod(DatabaseAdminClient.crypto_key_version_path)
69+
parse_crypto_key_version_path = staticmethod(
70+
DatabaseAdminClient.parse_crypto_key_version_path
71+
)
6572
database_path = staticmethod(DatabaseAdminClient.database_path)
6673
parse_database_path = staticmethod(DatabaseAdminClient.parse_database_path)
6774
instance_path = staticmethod(DatabaseAdminClient.instance_path)

google/cloud/spanner_admin_database_v1/services/database_admin/client.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from google.cloud.spanner_admin_database_v1.services.database_admin import pagers
3838
from google.cloud.spanner_admin_database_v1.types import backup
3939
from google.cloud.spanner_admin_database_v1.types import backup as gsad_backup
40+
from google.cloud.spanner_admin_database_v1.types import common
4041
from google.cloud.spanner_admin_database_v1.types import spanner_database_admin
4142
from google.iam.v1 import iam_policy_pb2 as iam_policy # type: ignore
4243
from google.iam.v1 import policy_pb2 as policy # type: ignore
@@ -185,6 +186,53 @@ def parse_backup_path(path: str) -> Dict[str, str]:
185186
)
186187
return m.groupdict() if m else {}
187188

189+
@staticmethod
190+
def crypto_key_path(
191+
project: str, location: str, key_ring: str, crypto_key: str,
192+
) -> str:
193+
"""Return a fully-qualified crypto_key string."""
194+
return "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}".format(
195+
project=project,
196+
location=location,
197+
key_ring=key_ring,
198+
crypto_key=crypto_key,
199+
)
200+
201+
@staticmethod
202+
def parse_crypto_key_path(path: str) -> Dict[str, str]:
203+
"""Parse a crypto_key path into its component segments."""
204+
m = re.match(
205+
r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)/keyRings/(?P<key_ring>.+?)/cryptoKeys/(?P<crypto_key>.+?)$",
206+
path,
207+
)
208+
return m.groupdict() if m else {}
209+
210+
@staticmethod
211+
def crypto_key_version_path(
212+
project: str,
213+
location: str,
214+
key_ring: str,
215+
crypto_key: str,
216+
crypto_key_version: str,
217+
) -> str:
218+
"""Return a fully-qualified crypto_key_version string."""
219+
return "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}/cryptoKeyVersions/{crypto_key_version}".format(
220+
project=project,
221+
location=location,
222+
key_ring=key_ring,
223+
crypto_key=crypto_key,
224+
crypto_key_version=crypto_key_version,
225+
)
226+
227+
@staticmethod
228+
def parse_crypto_key_version_path(path: str) -> Dict[str, str]:
229+
"""Parse a crypto_key_version path into its component segments."""
230+
m = re.match(
231+
r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)/keyRings/(?P<key_ring>.+?)/cryptoKeys/(?P<crypto_key>.+?)/cryptoKeyVersions/(?P<crypto_key_version>.+?)$",
232+
path,
233+
)
234+
return m.groupdict() if m else {}
235+
188236
@staticmethod
189237
def database_path(project: str, instance: str, database: str,) -> str:
190238
"""Return a fully-qualified database string."""

0 commit comments

Comments
 (0)