Skip to content

Commit b078413

Browse files
committed
feat: add CMEK support (via synth)
1 parent ede4343 commit b078413

File tree

16 files changed

+533
-45
lines changed

16 files changed

+533
-45
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";
@@ -104,6 +103,11 @@ message Backup {
104103
// restored database from the backup enters the `READY` state, the reference
105104
// to the backup is removed.
106105
repeated string referencing_databases = 7 [(google.api.field_behavior) = OUTPUT_ONLY];
106+
107+
// Output only. The encryption information for the backup.
108+
// If the encryption key protecting this resource is customer managed, then
109+
// kms_key_version will be filled.
110+
EncryptionInfo encryption_info = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
107111
}
108112

109113
// The request for [CreateBackup][google.spanner.admin.database.v1.DatabaseAdmin.CreateBackup].
@@ -128,6 +132,14 @@ message CreateBackupRequest {
128132

129133
// Required. The backup to create.
130134
Backup backup = 3 [(google.api.field_behavior) = REQUIRED];
135+
136+
// Optional. An encryption configuration describing the encryption type and key
137+
// resources in Cloud KMS used to encrypt the backup. If no
138+
// `encryption_config` is specified, the backup will use the same
139+
// encryption configuration as the database by default, namely
140+
// [encryption_type][google.spanner.admin.database.v1.CreateBackupEncryptionConfig.encryption_type] =
141+
// USE_DATABASE_ENCRYPTION.
142+
CreateBackupEncryptionConfig encryption_config = 4 [(google.api.field_behavior) = OPTIONAL];
131143
}
132144

133145
// Metadata type for the operation returned by
@@ -364,3 +376,43 @@ message BackupInfo {
364376
// Name of the database the backup was created from.
365377
string source_database = 3;
366378
}
379+
380+
// Encryption configuration for the backup to create.
381+
message CreateBackupEncryptionConfig {
382+
// Encryption types for the backup.
383+
enum EncryptionType {
384+
// Unspecified. Do not use.
385+
ENCRYPTION_TYPE_UNSPECIFIED = 0;
386+
387+
// Use the same encryption configuration as the database. This is the
388+
// default option when
389+
// [encryption_config][CreateBackupEncryptionConfig.encryption_config] is
390+
// empty. If the database is using customer managed encryption, the
391+
// backup will be using the same KMS key.
392+
USE_DATABASE_ENCRYPTION = 1;
393+
394+
// Enforce google default encryption.
395+
GOOGLE_DEFAULT_ENCRYPTION = 2;
396+
397+
// Enforce customer managed encryption. If specified, the kms_key_name
398+
// must provide a valid Cloud KMS key name.
399+
CUSTOMER_MANAGED_ENCRYPTION = 3;
400+
}
401+
402+
// Required. The encryption type of the backup.
403+
EncryptionType encryption_type = 1 [(google.api.field_behavior) = REQUIRED];
404+
405+
// Optional. The resource name of the Cloud KMS key that will be used to
406+
// protect the backup. Once specified, the backup will enforce customer
407+
// managed encryption, regardless of the database encryption type.
408+
// This field should be set only when
409+
// [encryption_type][google.spanner.admin.database.v1.CreateBackupEncryptionConfig.encryption_type] is
410+
// CUSTOMER_MANAGED_ENCRYPTION. Values are of the form
411+
// `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>`.
412+
string kms_key_name = 2 [
413+
(google.api.field_behavior) = OPTIONAL,
414+
(google.api.resource_reference) = {
415+
type: "cloudkms.googleapis.com/CryptoKey"
416+
}
417+
];
418+
}

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
@@ -368,6 +368,12 @@ message Database {
368368
// Output only. Applicable only for restored databases. Contains information
369369
// about the restore source.
370370
RestoreInfo restore_info = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
371+
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];
371377
}
372378

373379
// The request for [ListDatabases][google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases].
@@ -425,6 +431,10 @@ message CreateDatabaseRequest {
425431
// statements execute atomically with the creation of the database:
426432
// if there is an error in any statement, the database is not created.
427433
repeated string extra_statements = 3 [(google.api.field_behavior) = OPTIONAL];
434+
435+
// Optional.
436+
EncryptionConfig encryption_config = 4
437+
[(google.api.field_behavior) = OPTIONAL];
428438
}
429439

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

665725
// 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
@@ -169,6 +170,53 @@ def parse_backup_path(path: str) -> Dict[str, str]:
169170
)
170171
return m.groupdict() if m else {}
171172

173+
@staticmethod
174+
def crypto_key_path(
175+
project: str, location: str, key_ring: str, crypto_key: str,
176+
) -> str:
177+
"""Return a fully-qualified crypto_key string."""
178+
return "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}".format(
179+
project=project,
180+
location=location,
181+
key_ring=key_ring,
182+
crypto_key=crypto_key,
183+
)
184+
185+
@staticmethod
186+
def parse_crypto_key_path(path: str) -> Dict[str, str]:
187+
"""Parse a crypto_key path into its component segments."""
188+
m = re.match(
189+
r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)/keyRings/(?P<key_ring>.+?)/cryptoKeys/(?P<crypto_key>.+?)$",
190+
path,
191+
)
192+
return m.groupdict() if m else {}
193+
194+
@staticmethod
195+
def crypto_key_version_path(
196+
project: str,
197+
location: str,
198+
key_ring: str,
199+
crypto_key: str,
200+
crypto_key_version: str,
201+
) -> str:
202+
"""Return a fully-qualified crypto_key_version string."""
203+
return "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}/cryptoKeyVersions/{crypto_key_version}".format(
204+
project=project,
205+
location=location,
206+
key_ring=key_ring,
207+
crypto_key=crypto_key,
208+
crypto_key_version=crypto_key_version,
209+
)
210+
211+
@staticmethod
212+
def parse_crypto_key_version_path(path: str) -> Dict[str, str]:
213+
"""Parse a crypto_key_version path into its component segments."""
214+
m = re.match(
215+
r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)/keyRings/(?P<key_ring>.+?)/cryptoKeys/(?P<crypto_key>.+?)/cryptoKeyVersions/(?P<crypto_key_version>.+?)$",
216+
path,
217+
)
218+
return m.groupdict() if m else {}
219+
172220
@staticmethod
173221
def database_path(project: str, instance: str, database: str,) -> str:
174222
"""Return a fully-qualified database string."""

0 commit comments

Comments
 (0)