feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# 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.
"""Helpers for parsing certificates."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import base64
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core.util import files
import six
def GetDerCertificate(certificate_file):
"""Read certificate_file and return the certificate in DER encoding.
Args:
certificate_file: A file handle to the certificate in PEM or DER format.
Returns:
The certificate in DER encoding.
Raises:
BadArgumentException: The provided certificate failed to parse as a PEM.
"""
data = files.ReadBinaryFileContents(certificate_file)
if b'-----BEGIN CERTIFICATE-----' in data:
certb64 = data.replace(b'-----BEGIN CERTIFICATE-----', b'', 1)
certb64 = certb64.replace(b'-----END CERTIFICATE-----', b'', 1)
# If there's another certificate detected afterwards
if b'-----BEGIN CERTIFICATE-----' in certb64:
raise exceptions.BadArgumentException(
'certificate_file',
'Cannot place multiple certificates in the same file : {}'.format(
certificate_file))
try:
certb64 = certb64.replace(b'\r', b'').replace(b'\n', b'')
# Since validate=True isn't supported on Python2's base64.b64decode,
# re-encode output and compare to original.
decoded = base64.b64decode(six.ensure_binary(certb64))
encoded = base64.b64encode(decoded)
if encoded != certb64:
raise ValueError('Non-base64 digit found.')
except Exception as e:
raise exceptions.BadArgumentException(
'certificate_file',
'Recognized {} as a PEM file but failed during parsing : {}'.format(
certificate_file, e))
return decoded
else:
return data

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 Google LLC. All Rights Reserved.
#
# 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.
"""Helpers for calculating CRC32C checksums."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import gcloud_crcmod as crcmod
import six
def Crc32c(data):
"""Calculates the CRC32C checksum of the provided data.
Args:
data: the bytes over which the checksum should be calculated.
Returns:
An int representing the CRC32C checksum of the provided bytes.
"""
crc32c_fun = crcmod.predefined.mkPredefinedCrcFun('crc-32c')
return crc32c_fun(six.ensure_binary(data))
def Crc32cMatches(data, data_crc32c):
"""Checks that the CRC32C checksum of the provided data matches the provided checksum.
Args:
data: bytes over which the checksum should be calculated.
data_crc32c: int checksum against which data's checksum will be compared.
Returns:
True iff both checksums match.
"""
return (
Crc32c(data) == data_crc32c
if data is not None
else Crc32c(b'') == data_crc32c
)

View File

@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 Google LLC. All Rights Reserved.
#
# 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.
"""Helpers for End to End integirty verification."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import re
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core import exceptions as core_exceptions
_ERROR_MESSAGE_SUFFIX = (
"Please try again. This should happen rarely. If the error persists, "
"contact cloudkms-feedback@google.com. To learn more about how Cloud KMS "
"verifies in-transit integrity, visit "
"https://cloud.google.com/kms/docs/data-integrity-guidelines.")
class ServerSideIntegrityVerificationError(core_exceptions.Error):
"""Error raised when server reported integrity verification error."""
class ClientSideIntegrityVerificationError(core_exceptions.Error):
"""Error raised when client identifies integrity verification error."""
class ResourceNameVerificationError(core_exceptions.Error):
"""Error raised when server returned resource name differs from client provided resource name."""
def GetRequestToServerCorruptedErrorMessage():
"""Error message for when the request to server failed an integrity check."""
return ("The request sent to the server was corrupted in-transit. {}".format(
_ERROR_MESSAGE_SUFFIX))
def GetResponseFromServerCorruptedErrorMessage():
"""Error message for when the response from server failed an integrity check."""
return ("The response received from the server was corrupted in-transit. {}"
.format(_ERROR_MESSAGE_SUFFIX))
def GetResourceNameMismatchErrorMessage(request_resource_name,
response_resource_name):
return (
"Found a mismatch between user-requested crypto resource ({})".format(
request_resource_name) +
"and server-reported resource used for the cryptographic operation ({}).\n"
.format(response_resource_name) + _ERROR_MESSAGE_SUFFIX)
# LINT.IfChange(invalid_checksum_err)
def ProcessHttpBadRequestError(error):
"""Intercept INVALID_ARGUMENT errors related to checksum verification.
Intercept INVALID_ARGUMENT errors related to checksum verification, to present
a user-friendly message.
All other errors are surfaced as-is.
Args:
error: apitools_exceptions.ProcessHttpBadRequestError.
Raises:
ServerSideIntegrityVerificationError: if |error| is a result of a failed
server-side request integrity verification.
Else, re-raises |error| as exceptions.HttpException.
"""
exc = exceptions.HttpException(error)
regex = re.compile(
"The checksum in field .* did not match the data in field .*.")
if regex.search(exc.payload.status_message) is not None:
raise ServerSideIntegrityVerificationError(
GetRequestToServerCorruptedErrorMessage())
else:
raise exc
# Code paths are prohibited from being included in this file.
# LINT.ThenChange()

View File

@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google LLC. All Rights Reserved.
#
# 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.
"""This module holds exceptions for secrets sruface."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.core import exceptions
class KmsError(exceptions.Error):
"""Base class for kms failures."""
class UpdateError(KmsError):
"""Failures during update."""
class ArgumentError(KmsError):
"""Command argument error."""

View File

@@ -0,0 +1,883 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# 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.
"""Helpers for parsing flags and arguments."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import actions
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.kms import maps
from googlecloudsdk.command_lib.kms import resource_args
from googlecloudsdk.command_lib.util import completers
from googlecloudsdk.command_lib.util import parameter_info_lib
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from googlecloudsdk.core.util import times
EKM_CONNECTION_COLLECTION = 'cloudkms.projects.locations.ekmConnections'
KEY_RING_COLLECTION = 'cloudkms.projects.locations.keyRings'
KEY_HANDLE_COLLECTION = 'cloudkms.projects.locations.keyHandles'
LOCATION_COLLECTION = 'cloudkms.projects.locations'
# Collection names.
CRYPTO_KEY_COLLECTION = 'cloudkms.projects.locations.keyRings.cryptoKeys'
CRYPTO_KEY_VERSION_COLLECTION = '%s.cryptoKeyVersions' % CRYPTO_KEY_COLLECTION
IMPORT_JOB_COLLECTION = 'cloudkms.projects.locations.keyRings.importJobs'
SINGLE_TENANT_HSM_INSTANCE_COLLECTION = (
'cloudkms.projects.locations.singleTenantHsmInstances'
)
# list command aggregators
class ListCommandParameterInfo(parameter_info_lib.ParameterInfoByConvention):
def GetFlag(
self,
parameter_name,
parameter_value=None,
check_properties=True,
for_update=False,
):
return super(ListCommandParameterInfo, self).GetFlag(
parameter_name,
parameter_value=parameter_value,
check_properties=check_properties,
for_update=for_update,
)
class ListCommandCompleter(completers.ListCommandCompleter):
def ParameterInfo(self, parsed_args, argument):
return ListCommandParameterInfo(
parsed_args,
argument,
self.collection,
updaters=COMPLETERS_BY_CONVENTION,
)
# kms completers
class LocationCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(LocationCompleter, self).__init__(
collection=LOCATION_COLLECTION,
list_command='kms locations list --uri',
**kwargs
)
class SingleTenantHsmInstanceCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(SingleTenantHsmInstanceCompleter, self).__init__(
collection=SINGLE_TENANT_HSM_INSTANCE_COLLECTION,
list_command='kms single-tenant-hsm-instances list --uri',
**kwargs
)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class EkmConnectionCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(EkmConnectionCompleter, self).__init__(
collection=EKM_CONNECTION_COLLECTION,
list_command='kms ekm-connections list --uri',
flags=['location'],
**kwargs
)
class KeyRingCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(KeyRingCompleter, self).__init__(
collection=KEY_RING_COLLECTION,
list_command='kms keyrings list --uri',
flags=['location'],
**kwargs
)
class KeyCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(KeyCompleter, self).__init__(
collection=CRYPTO_KEY_COLLECTION,
list_command='kms keys list --uri',
flags=['location', 'keyring'],
**kwargs
)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class KeyHandleCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(KeyHandleCompleter, self).__init__(
collection=KEY_HANDLE_COLLECTION,
list_command='kms key-handles list --uri',
flags=['location'],
**kwargs
)
class KeyVersionCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(KeyVersionCompleter, self).__init__(
collection=CRYPTO_KEY_VERSION_COLLECTION,
list_command='kms keys versions list --uri',
flags=['location', 'key', 'keyring'],
**kwargs
)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class ImportJobCompleter(ListCommandCompleter):
def __init__(self, **kwargs):
super(ImportJobCompleter, self).__init__(
collection=IMPORT_JOB_COLLECTION,
list_command='beta kms import-jobs list --uri',
flags=['location', 'keyring'],
**kwargs
)
# completers by parameter name convention
COMPLETERS_BY_CONVENTION = {
'location': (LocationCompleter, False),
'keyring': (KeyRingCompleter, False),
'key-handle': (KeyHandleCompleter, False),
'key': (KeyCompleter, False),
'import-jobs': (ImportJobCompleter, False),
}
# Flags.
def AddLocationFlag(parser, resource='resource'):
parser.add_argument(
'--location',
completer=LocationCompleter,
help='Location of the {0}.'.format(resource),
)
def AddSingleTenantInstance(parser, resource='resource'):
parser.add_argument(
'--single-tenant-hsm-instance',
completer=SingleTenantHsmInstanceCompleter,
help='Single tenant HSM instance of the {0}.'.format(resource),
)
def AddKeyRingFlag(parser, resource='resource'):
parser.add_argument(
'--keyring',
completer=KeyRingCompleter,
help='Key ring of the {0}.'.format(resource),
)
def AddCryptoKeyFlag(parser, help_text=None):
parser.add_argument(
'--key', completer=KeyCompleter, help=help_text or 'The containing key.'
)
def AddKeyResourceFlags(parser, help_text=None):
AddLocationFlag(parser, 'keyring')
AddKeyRingFlag(parser, 'key')
AddCryptoKeyFlag(parser, help_text)
def AddSingleTenantHsmInstanceFlag(parser):
parser.add_argument(
'--single-tenant-hsm-instance',
completer=SingleTenantHsmInstanceCompleter,
help='The single tenant HSM instance to use for the import job.',
)
def AddCryptoKeyVersionFlag(parser, help_action, required=False):
parser.add_argument(
'--version',
required=required,
completer=KeyVersionCompleter,
help='Version {0}.'.format(help_action),
)
def AddCryptoKeyPrimaryVersionFlag(parser, help_action, required=False):
parser.add_argument(
'--primary-version',
required=required,
completer=KeyVersionCompleter,
help='Primary version {0}.'.format(help_action),
)
def AddRotationPeriodFlag(parser):
parser.add_argument(
'--rotation-period',
type=arg_parsers.Duration(lower_bound='1d'),
help=(
'Automatic rotation period of the key. See '
'$ gcloud topic datetimes for information on duration formats.'
),
)
def AddNextRotationTimeFlag(parser):
parser.add_argument(
'--next-rotation-time',
type=arg_parsers.Datetime.Parse,
help=(
'Next automatic rotation time of the key. See '
'$ gcloud topic datetimes for information on time formats.'
),
)
def AddRemoveRotationScheduleFlag(parser):
parser.add_argument(
'--remove-rotation-schedule',
action='store_true',
help='Remove any existing rotation schedule on the key.',
)
def AddSkipInitialVersionCreationFlag(parser):
parser.add_argument(
'--skip-initial-version-creation',
default=None,
action='store_true',
dest='skip_initial_version_creation',
help=(
'Skip creating the first version in a key and setting it as '
'primary during creation.'
),
)
def AddPlaintextFileFlag(parser, help_action):
parser.add_argument(
'--plaintext-file',
help='File path of the plaintext file {0}.'.format(help_action),
required=True,
)
def AddSharedSecretFileFlag(parser, help_action):
parser.add_argument(
'--shared-secret-file',
help='File path of the shared secret file {0}.'.format(help_action),
required=True,
)
def AddCiphertextFileFlag(parser, help_action):
parser.add_argument(
'--ciphertext-file',
help='File path of the ciphertext file {0}.'.format(help_action),
required=True,
)
def AddSignatureFileFlag(parser, help_action):
parser.add_argument(
'--signature-file',
help='Path to the signature file {}.'.format(help_action),
required=True,
)
def AddInputFileFlag(parser, help_action):
parser.add_argument(
'--input-file',
help='Path to the input file {}.'.format(help_action),
required=True,
)
def AddRsaAesWrappedKeyFileFlag(parser, help_action):
parser.add_argument(
'--rsa-aes-wrapped-key-file',
help='Path to the wrapped RSA AES key file {}.'.format(help_action),
hidden=True,
action=actions.DeprecationAction(
'--rsa-aes-wrapped-key-file',
warn=(
'The {flag_name} flag is deprecated but will continue to be '
'supported. Prefer to use the --wrapped-key-file flag instead.'
),
),
)
def AddWrappedKeyFileFlag(parser, help_action):
parser.add_argument(
'--wrapped-key-file',
help='Path to the RSA/RSA+AES wrapped key file {}.'.format(help_action),
)
def AddOutputFileFlag(parser, help_action):
parser.add_argument(
'--output-file', help='Path to the output file {}.'.format(help_action)
)
def AddAadFileFlag(parser):
parser.add_argument(
'--additional-authenticated-data-file',
help=(
'File path to the optional file containing the additional '
'authenticated data.'
),
)
def AddIvFileFlag(parser, help_action):
parser.add_argument(
'--initialization-vector-file',
help=(
'File path to the optional file containing the initialization '
'vector {}.'.format(help_action)
),
)
def AddProtectionLevelFlag(parser):
parser.add_argument(
'--protection-level',
choices=[
'software',
'hsm',
'hsm-single-tenant',
'external',
'external-vpc',
],
default='software',
help='Protection level of the key.',
)
def AddRequiredProtectionLevelFlag(parser):
parser.add_argument(
'--protection-level',
choices=['software', 'hsm', 'hsm-single-tenant'],
help='Protection level of the import job.',
required=True,
)
def AddAttestationFileFlag(parser):
parser.add_argument(
'--attestation-file', help='Path to the output attestation file.'
)
def AddDefaultAlgorithmFlag(parser):
parser.add_argument(
'--default-algorithm',
choices=sorted(maps.ALL_ALGORITHMS),
help=(
'The default algorithm for the crypto key. For more information '
'about choosing an algorithm, see '
'https://cloud.google.com/kms/docs/algorithms.'
),
)
def AddRequiredImportMethodFlag(parser):
parser.add_argument(
'--import-method',
choices=sorted(maps.IMPORT_METHOD_MAPPER.choices)[1:],
help=(
'The wrapping method to be used for incoming key material. For more '
'information about choosing an import method, see '
'https://cloud.google.com/kms/docs/key-wrapping.'
),
required=True,
)
def AddPublicKeyFileFlag(parser):
parser.add_argument(
'--public-key-file',
help=(
'Path to the public key of the ImportJob, used to wrap the key for '
'import. If missing, the public key will be fetched on your behalf.'
),
)
def AddTargetKeyFileFlag(parser):
parser.add_argument(
'--target-key-file',
help=(
'Path to the unwrapped target key to import into a Cloud KMS key'
' version. If specified, the key will be securely wrapped before'
' transmission to Google.'
),
)
def AddDigestAlgorithmFlag(parser, help_action):
parser.add_argument(
'--digest-algorithm', choices=sorted(maps.DIGESTS), help=help_action
)
def AddImportedVersionAlgorithmFlag(parser):
parser.add_argument(
'--algorithm',
choices=sorted(maps.ALGORITHMS_FOR_IMPORT),
help=(
'The algorithm to assign to the new key version. For more '
'information about supported algorithms, see '
'https://cloud.google.com/kms/docs/algorithms.'
),
required=True,
)
def AddExternalKeyUriFlag(parser):
parser.add_argument(
'--external-key-uri',
suggestion_aliases=['--key-uri'],
help=(
'The URI of the external key for keys with protection level'
' "external".'
),
)
def AddEkmConnectionKeyPathFlag(parser):
parser.add_argument(
'--ekm-connection-key-path',
help=(
'The path to the external key material on the EKM for keys with '
'protection level "external-vpc".'
),
)
def AddStateFlag(parser):
parser.add_argument('--state', dest='state', help='State of the key version.')
def AddSkipIntegrityVerification(parser):
parser.add_argument(
'--skip-integrity-verification',
default=None,
action='store_true',
dest='skip_integrity_verification',
help='Skip integrity verification on request and response API fields.',
)
def AddDestroyScheduledDurationFlag(parser):
parser.add_argument(
'--destroy-scheduled-duration',
type=arg_parsers.Duration(upper_bound='120d'),
help=(
'The amount of time that versions of the key should spend in the '
'DESTROY_SCHEDULED state before transitioning to DESTROYED. See '
'$ gcloud topic datetimes for information on duration formats.'
),
)
def AddCryptoKeyBackendFlag(parser):
parser.add_argument(
'--crypto-key-backend',
help=(
'The resource name of the backend environment where the key material'
' for all CryptoKeyVersions associated with this CryptoKey reside and'
' where all related cryptographic operations are performed. Currently'
' only applicable for EXTERNAL_VPC and EkmConnection resource names.'
),
)
def AddImportOnlyFlag(parser):
parser.add_argument(
'--import-only',
default=None,
action='store_true',
dest='import_only',
help='Restrict this key to imported versions only.',
)
def AddAllowedAccessReasonsFlag(parser):
parser.add_argument(
'--allowed-access-reasons',
type=arg_parsers.ArgList(
choices=maps.ACCESS_REASON_MAPPER.choices,
max_length=len(maps.ACCESS_REASON_MAPPER.choices),
),
metavar='ALLOWED_ACCESS_REASONS',
help=(
'The list of allowed Key Access Justifications access reasons on '
'the key. The key must be enrolled in Key Access Justifications to '
'configure this field. By default, this field is absent, and all '
'justification codes are allowed. For more information about '
'justification codes, see '
'https://cloud.google.com/assured-workloads/key-access-justifications/docs/justification-codes.'
),
)
def AddRemoveKeyAccessJustificationsPolicyFlag(parser):
parser.add_argument(
'--remove-key-access-justifications-policy',
default=None,
action='store_true',
help=(
'Removes the Key Access Justifications policy on the key, making '
'all justification codes allowed.'
),
)
def AddPublicKeyFormatFlag(parser):
parser.add_argument(
'--public-key-format',
default=None,
help='The format in which the public key will be returned.',
)
# Arguments
def AddKeyRingArgument(parser, help_action):
parser.add_argument(
'keyring',
completer=KeyRingCompleter,
help='Name of the key ring {0}.'.format(help_action),
)
def AddCryptoKeyArgument(parser, help_action):
parser.add_argument(
'key',
completer=KeyCompleter,
help='Name of the key {0}.'.format(help_action),
)
def AddKeyResourceArgument(parser, help_action):
AddLocationFlag(parser, 'key')
AddKeyRingFlag(parser, 'key')
AddCryptoKeyArgument(parser, help_action)
def AddCryptoKeyVersionArgument(parser, help_action):
parser.add_argument(
'version',
completer=KeyVersionCompleter,
help='Name of the version {0}.'.format(help_action),
)
def AddKeyVersionResourceArgument(parser, help_action):
AddKeyResourceFlags(parser)
AddCryptoKeyVersionArgument(parser, help_action)
def AddPositionalImportJobArgument(parser, help_action):
parser.add_argument(
'import_job',
completer=ImportJobCompleter,
help='Name of the import job {0}.'.format(help_action),
)
def AddRequiredImportJobArgument(parser, help_action):
parser.add_argument(
'--import-job',
completer=ImportJobCompleter,
help='Name of the import job {0}.'.format(help_action),
required=True,
)
def AddCertificateChainFlag(parser):
parser.add_argument(
'--certificate-chain-type',
default='all',
choices=['all', 'cavium', 'google-card', 'google-partition'],
help='Certificate chain to retrieve.',
)
def AddServiceDirectoryServiceFlag(parser, required=False):
parser.add_argument(
'--service-directory-service',
help=(
'The resource name of the Service Directory service pointing to '
'an EKM replica.'
),
required=required,
)
def AddEndpointFilterFlag(parser):
parser.add_argument(
'--endpoint-filter',
help=(
'The filter applied to the endpoints of the resolved service. '
'If no filter is specified, all endpoints will be considered.'
),
)
def AddHostnameFlag(parser, required=False):
parser.add_argument(
'--hostname',
help='The hostname of the EKM replica used at TLS and HTTP layers.',
required=required,
)
def AddServerCertificatesFilesFlag(parser, required=False):
parser.add_argument(
'--server-certificates-files',
type=arg_parsers.ArgList(),
metavar='SERVER_CERTIFICATES',
help=(
'A list of filenames of leaf server certificates used to '
'authenticate HTTPS connections to the EKM replica in PEM format. If '
'files are not in PEM, the assumed format will be DER.'
),
required=required,
)
def AddKeyManagementModeFlags(parser):
"""Adds key-management-mode flags and related flags."""
group = parser.add_group(
help=(
'Specifies the key management mode for the EkmConnection and'
' associated fields.'
)
)
group.add_argument(
'--key-management-mode',
choices=['manual', 'cloud-kms'],
help=(
'Key management mode of the ekm connection. An EkmConnection in'
' `cloud-kms` mode means Cloud KMS will attempt to create and manage'
' the key material that resides on the EKM for crypto keys created'
' with this EkmConnection. An EkmConnection in `manual` mode means'
' the external key material will not be managed by Cloud KMS.'
' Omitting the flag defaults to `manual`.'
),
)
group.add_argument(
'--crypto-space-path',
help=(
'Crypto space path for the EkmConnection. Required during '
'EkmConnection creation if `--key-management-mode=cloud-kms`.'
),
)
def AddDefaultEkmConnectionFlag(parser, required=False):
parser.add_argument(
'--default-ekm-connection',
help=(
'The resource name of the EkmConnection to be used as the default'
' EkmConnection for all `external-vpc` CryptoKeys in a project and'
' location. Can be an empty string to remove the default'
' EkmConnection.'
),
required=required,
)
def AddResourceTypeSelectorFlag(parser, required=False):
parser.add_argument(
'--resource-type',
help=(
'The resource type selector for KeyHandle resources of the form'
' {{SERVICE}}.{{UNIVERSE_DOMAIN}}/{{TYPE}}.'
),
required=required,
)
def AddCreateKeyHandleFlags(parser):
resource_args.AddKmsLocationResourceArgForKMS(parser, True, '--location')
AddResourceTypeSelectorFlag(parser, True)
group = parser.add_group(mutex=True, required=True)
group.add_argument(
'--key-handle-id',
help='The KeyHandle id for the new KeyHandle resource.',
)
group.add_argument(
'--generate-key-handle-id',
help='Generate a KeyHandle id for the new KeyHandle resource.',
action='store_true',
)
def AddFolderIdFlag(parser, required=False):
parser.add_argument(
'--folder',
help='The folder id in which the AutokeyConfig resource exists.',
required=required,
)
def AddAutokeyConfigFileFlag(parser):
parser.add_argument(
'CONFIG_FILE',
help='The file containing the AutokeyConfig resource.',
)
# Parsing.
def ParseLocationName(args):
return resources.REGISTRY.Parse(
args.location,
params={'projectsId': properties.VALUES.core.project.GetOrFail},
collection=LOCATION_COLLECTION,
)
def ParseEkmConnectionName(args):
return resources.REGISTRY.Parse(
args.ekm_connection,
params={
'projectsId': properties.VALUES.core.project.GetOrFail,
'locationsId': args.MakeGetOrRaise('--location'),
},
collection=EKM_CONNECTION_COLLECTION,
)
def ParseKeyRingName(args):
return resources.REGISTRY.Parse(
args.keyring,
params={
'projectsId': properties.VALUES.core.project.GetOrFail,
'locationsId': args.MakeGetOrRaise('--location'),
},
collection=KEY_RING_COLLECTION,
)
def ParseCryptoKeyName(args):
return resources.REGISTRY.Parse(
args.key,
params={
'keyRingsId': args.MakeGetOrRaise('--keyring'),
'locationsId': args.MakeGetOrRaise('--location'),
'projectsId': properties.VALUES.core.project.GetOrFail,
},
collection=CRYPTO_KEY_COLLECTION,
)
def ParseCryptoKeyVersionName(args):
return resources.REGISTRY.Parse(
args.version,
params={
'cryptoKeysId': args.MakeGetOrRaise('--key'),
'keyRingsId': args.MakeGetOrRaise('--keyring'),
'locationsId': args.MakeGetOrRaise('--location'),
'projectsId': properties.VALUES.core.project.GetOrFail,
},
collection=CRYPTO_KEY_VERSION_COLLECTION,
)
def ParseImportJobName(args):
return resources.REGISTRY.Parse(
args.import_job,
params={
'keyRingsId': args.MakeGetOrRaise('--keyring'),
'locationsId': args.MakeGetOrRaise('--location'),
'projectsId': properties.VALUES.core.project.GetOrFail,
},
collection=IMPORT_JOB_COLLECTION,
)
def ParseSingleTenantHsmInstanceName(args):
return resources.REGISTRY.Parse(
args.single_tenant_hsm_instance,
params={
'projectsId': properties.VALUES.core.project.GetOrFail,
'locationsId': args.MakeGetOrRaise('--location'),
},
collection=SINGLE_TENANT_HSM_INSTANCE_COLLECTION,
)
# Get parent type Resource from output of Parse functions above.
def ParseParentFromResource(resource_ref):
collection_list = resource_ref.Collection().split('.')
parent_collection = '.'.join(collection_list[:-1])
params = resource_ref.AsDict()
del params[collection_list[-1] + 'Id']
return resources.REGISTRY.Create(parent_collection, **params)
# Set proto fields from flags.
def SetRotationPeriod(args, crypto_key):
if args.rotation_period is not None:
crypto_key.rotationPeriod = '{0}s'.format(args.rotation_period)
def SetNextRotationTime(args, crypto_key):
if args.next_rotation_time is not None:
crypto_key.nextRotationTime = times.FormatDateTime(args.next_rotation_time)
def SetDestroyScheduledDuration(args, crypto_key):
if args.destroy_scheduled_duration is not None:
crypto_key.destroyScheduledDuration = '{0}s'.format(
args.destroy_scheduled_duration
)
def SetKeyAccessJustificationsPolicy(args, crypto_key):
messages = cloudkms_base.GetMessagesModule()
if args.allowed_access_reasons is not None:
allowed_access_reasons = []
for access_reason in args.allowed_access_reasons:
reason_string = maps.ACCESS_REASON_MAPPER.GetEnumForChoice(access_reason)
if reason_string not in allowed_access_reasons:
allowed_access_reasons.append(reason_string)
crypto_key.keyAccessJustificationsPolicy = (
messages.KeyAccessJustificationsPolicy(
allowedAccessReasons=allowed_access_reasons
)
)

View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# 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.
"""Helpers for digesting a file."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import hashlib
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core.util import files
_READ_SIZE = 4 * 1024 * 1024
def _ChunkReader(file_, chunk_size=_READ_SIZE):
while True:
chunk = file_.read(chunk_size)
if not chunk:
break
yield chunk
_DIGEST_ALGORITHMS = {
'sha256': hashlib.sha256,
'sha384': hashlib.sha384,
'sha512': hashlib.sha512,
}
# TODO(b/77481291) Refactor this to allow reading from stdin.
def GetDigest(digest_algorithm, filename):
"""Digest the file at filename based on digest_algorithm.
Args:
digest_algorithm: The algorithm used to digest the file, can be one of
'sha256', 'sha384', or 'sha512'.
filename: A valid file path over which a digest will be calculated.
Returns:
The digest of the provided file.
Raises:
InvalidArgumentException: The provided digest_algorithm is invalid.
"""
with files.BinaryFileReader(filename) as f:
return GetDigestOfFile(digest_algorithm, f)
def GetDigestOfFile(digest_algorithm, file_to_digest):
"""Digest the file_to_digest based on digest_algorithm.
Args:
digest_algorithm: The algorithm used to digest the file, can be one of
'sha256', 'sha384', or 'sha512'.
file_to_digest: A valid file handle.
Returns:
The digest of the provided file.
Raises:
InvalidArgumentException: The provided digest_algorithm is invalid.
"""
messages = cloudkms_base.GetMessagesModule()
algorithm = _DIGEST_ALGORITHMS.get(digest_algorithm)
if not algorithm:
raise exceptions.InvalidArgumentException('digest',
'digest_algorithm is invalid.')
digest = algorithm()
for chunk in _ChunkReader(file_to_digest):
digest.update(chunk)
kwargs = {digest_algorithm: digest.digest()}
return messages.Digest(**kwargs)

View File

@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*- #
# Copyright 2017 Google LLC. All Rights Reserved.
#
# 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.
"""Maps that match gcloud enum values to api enum ones."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base
from googlecloudsdk.command_lib.util.apis import arg_utils
MESSAGES = cloudkms_base.GetMessagesModule()
DIGESTS = {'sha256', 'sha384', 'sha512'}
ALGORITHM_ENUM = MESSAGES.CryptoKeyVersionTemplate.AlgorithmValueValuesEnum
ALGORITHM_MAPPER = arg_utils.ChoiceEnumMapper('algorithm_enum', ALGORITHM_ENUM)
ALGORITHM_ENUM_FOR_IMPORT = (
MESSAGES.ImportCryptoKeyVersionRequest.AlgorithmValueValuesEnum
)
ALGORITHM_MAPPER_FOR_IMPORT = arg_utils.ChoiceEnumMapper(
'algorithm_enum_for_import', ALGORITHM_ENUM_FOR_IMPORT
)
IMPORT_METHOD_ENUM = MESSAGES.ImportJob.ImportMethodValueValuesEnum
IMPORT_METHOD_MAPPER = arg_utils.ChoiceEnumMapper(
'import_method_enum', IMPORT_METHOD_ENUM
)
PURPOSE_ENUM = MESSAGES.CryptoKey.PurposeValueValuesEnum
PURPOSE_MAP = {
'encryption': PURPOSE_ENUM.ENCRYPT_DECRYPT,
'raw-encryption': PURPOSE_ENUM.RAW_ENCRYPT_DECRYPT,
'asymmetric-signing': PURPOSE_ENUM.ASYMMETRIC_SIGN,
'asymmetric-encryption': PURPOSE_ENUM.ASYMMETRIC_DECRYPT,
'mac': PURPOSE_ENUM.MAC,
'key-encapsulation': PURPOSE_ENUM.KEY_ENCAPSULATION,
}
PROTECTION_LEVEL_ENUM = (
MESSAGES.CryptoKeyVersionTemplate.ProtectionLevelValueValuesEnum
)
PROTECTION_LEVEL_MAPPER = arg_utils.ChoiceEnumMapper(
'protection_level_enum', PROTECTION_LEVEL_ENUM
)
IMPORT_PROTECTION_LEVEL_ENUM = MESSAGES.ImportJob.ProtectionLevelValueValuesEnum
IMPORT_PROTECTION_LEVEL_MAPPER = arg_utils.ChoiceEnumMapper(
'protection_level_enum', IMPORT_PROTECTION_LEVEL_ENUM
)
# Add new algorithms according to their purposes here.
VALID_ALGORITHMS_MAP = {
PURPOSE_ENUM.ENCRYPT_DECRYPT: [
'google-symmetric-encryption',
'external-symmetric-encryption',
],
PURPOSE_ENUM.RAW_ENCRYPT_DECRYPT: [
'aes-128-gcm',
'aes-256-gcm',
'aes-128-cbc',
'aes-256-cbc',
'aes-128-ctr',
'aes-256-ctr',
],
PURPOSE_ENUM.ASYMMETRIC_SIGN: [
'ec-sign-ed25519',
'ec-sign-p256-sha256',
'ec-sign-p384-sha384',
'ec-sign-secp256k1-sha256',
'rsa-sign-pss-2048-sha256',
'rsa-sign-pss-3072-sha256',
'rsa-sign-pss-4096-sha256',
'rsa-sign-pss-4096-sha512',
'rsa-sign-pkcs1-2048-sha256',
'rsa-sign-pkcs1-3072-sha256',
'rsa-sign-pkcs1-4096-sha256',
'rsa-sign-pkcs1-4096-sha512',
'rsa-sign-raw-pkcs1-2048',
'rsa-sign-raw-pkcs1-3072',
'rsa-sign-raw-pkcs1-4096',
'pq-sign-ml-dsa-65',
'pq-sign-slh-dsa-sha2-128s',
'pq-sign-hash-slh-dsa-sha2-128s-sha256',
],
PURPOSE_ENUM.ASYMMETRIC_DECRYPT: [
'rsa-decrypt-oaep-2048-sha1',
'rsa-decrypt-oaep-2048-sha256',
'rsa-decrypt-oaep-3072-sha1',
'rsa-decrypt-oaep-3072-sha256',
'rsa-decrypt-oaep-4096-sha1',
'rsa-decrypt-oaep-4096-sha256',
'rsa-decrypt-oaep-4096-sha512',
],
PURPOSE_ENUM.MAC: [
'hmac-sha1',
'hmac-sha224',
'hmac-sha256',
'hmac-sha384',
'hmac-sha512',
],
PURPOSE_ENUM.KEY_ENCAPSULATION: [
'ml-kem-768',
'ml-kem-1024',
'kem-xwing',
],
}
# Derive available algorithms from VALID_ALGORITHMS_MAP.
ALL_ALGORITHMS = frozenset({
# pylint: disable=g-complex-comprehension
algorithm
for algorithms in VALID_ALGORITHMS_MAP.values()
for algorithm in algorithms
})
ALGORITHMS_FOR_IMPORT = ALL_ALGORITHMS - {'external-symmetric-encryption'}
CRYPTO_KEY_VERSION_STATE_ENUM = MESSAGES.CryptoKeyVersion.StateValueValuesEnum
CRYPTO_KEY_VERSION_STATE_MAPPER = arg_utils.ChoiceEnumMapper(
'crypto_key_version_state_enum', CRYPTO_KEY_VERSION_STATE_ENUM
)
KEY_MANAGEMENT_MODE_ENUM = (
MESSAGES.EkmConnection.KeyManagementModeValueValuesEnum
)
KEY_MANAGEMENT_MODE_MAPPER = arg_utils.ChoiceEnumMapper(
'key_management_mode', KEY_MANAGEMENT_MODE_ENUM
)
ACCESS_REASON_ENUM = (
MESSAGES.KeyAccessJustificationsPolicy.AllowedAccessReasonsValueListEntryValuesEnum
)
ACCESS_REASON_MAPPER = arg_utils.ChoiceEnumMapper(
'access_reason_enum', ACCESS_REASON_ENUM
)
PUBLIC_KEY_FORMAT_ENUM = (
MESSAGES.CloudkmsProjectsLocationsKeyRingsCryptoKeysCryptoKeyVersionsGetPublicKeyRequest.PublicKeyFormatValueValuesEnum
)
PUBLIC_KEY_FORMAT_MAPPER = arg_utils.ChoiceEnumMapper(
'public_key_format_enum', PUBLIC_KEY_FORMAT_ENUM
)

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# 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.
"""Helpers for parsing config files."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import yaml
def ReadAutokeyConfigFromConfigFile(file_path):
"""Fetches the AutokeyConfig from the config file."""
try:
parsed_yaml = yaml.load_path(file_path)
etag = parsed_yaml['etag'] if 'etag' in parsed_yaml else ''
except yaml.Error as error:
raise exceptions.Error('unable to load kubeconfig for {0}: {1}'.format(
file_path, error))
if 'name' not in parsed_yaml:
raise exceptions.Error('AutokeyConfig file must contain a name.')
if 'keyProject' not in parsed_yaml:
raise exceptions.Error('AutokeyConfig file must contain a keyProject.')
return parsed_yaml['name'], parsed_yaml['keyProject'], etag

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 Google LLC. All Rights Reserved.
#
# 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.
"""Helpers for parsing key files in pem format."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import base64
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core.util import files
import six
def GetPemPublicKey(public_key_file):
"""Reads, validates, and returns the public key in PEM encoding within a file.
Args:
public_key_file: A public key file handle that contains a PEM encoded public
key.
Returns:
The public key in PEM encoding.
"""
data = files.ReadBinaryFileContents(public_key_file)
if b'-----BEGIN PUBLIC KEY-----' in data:
publickeyb64 = data.replace(b'-----BEGIN PUBLIC KEY-----', b'', 1)
publickeyb64 = publickeyb64.replace(b'-----END PUBLIC KEY-----', b'', 1)
# If there's another public key detected afterwards
if b'-----BEGIN PUBLIC KEY-----' in publickeyb64:
raise exceptions.BadArgumentException(
'public_key_file',
'Cannot place multiple public keys in the same file : {}'.format(
public_key_file
),
)
try:
publickeyb64 = publickeyb64.replace(b'\r', b'').replace(b'\n', b'')
decoded = base64.b64decode(six.ensure_binary(publickeyb64))
encoded = base64.b64encode(decoded)
if encoded != publickeyb64:
raise ValueError('Non-base64 digit found.')
except Exception as e:
raise exceptions.BadArgumentException(
'public_key_file',
'Recognized {} as a PEM encoded public key, but failed during'
' parsing : {}'.format(public_key_file, e),
)
return '\n'.join(data.decode('ascii').splitlines()) + '\n'
else:
raise exceptions.BadArgumentException(
'public_key_file', 'Missing PEM public key header in file'
)

View File

@@ -0,0 +1,422 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# 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.
"""Shared resource flags for kms resources."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from typing import cast
from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.calliope.concepts import deps
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import properties
def KeyVersionAttributeConfig(kms_prefix=True):
name = 'kms-key-version' if kms_prefix else 'key-version'
return concepts.ResourceParameterAttributeConfig(
name=name, help_text='The KMS key version of the {resource}.'
)
def KeyAttributeConfig(kms_prefix=True):
name = 'kms-key' if kms_prefix else 'key'
return concepts.ResourceParameterAttributeConfig(
name=name, help_text='The KMS key of the {resource}.'
)
def KeyringAttributeConfig(kms_prefix=True):
name = 'kms-keyring' if kms_prefix else 'keyring'
return concepts.ResourceParameterAttributeConfig(
name=name, help_text='The KMS keyring of the {resource}.'
)
def KeyHandleAttributeConfig(kms_prefix=True):
name = 'kms-key-handle' if kms_prefix else 'key-handle'
return concepts.ResourceParameterAttributeConfig(
name=name, help_text='The KMS key-handle of the {resource}.'
)
def EkmConnectionAttributeConfig(kms_prefix=True):
name = 'kms-ekmconnection' if kms_prefix else 'ekmconnection'
return concepts.ResourceParameterAttributeConfig(
name=name, help_text='The KMS ekm connection of the {resource}.'
)
def SingleTenantHsmInstanceAttributeConfig(kms_prefix=True):
name = (
'kms-single_tenant_hsm_instance'
if kms_prefix
else 'single_tenant_hsm_instance'
)
return concepts.ResourceParameterAttributeConfig(
name=name,
help_text='The KMS single tenant HSM instance of the {resource}.',
)
def SingleTenantHsmInstanceProposalAttributeConfig(kms_prefix=True):
name = 'kms-proposal' if kms_prefix else 'proposal'
return concepts.ResourceParameterAttributeConfig(
name=name,
help_text=(
'The KMS single tenant HSM instance proposal of the {resource}.'
),
)
def OperationAttributeConfig(kms_prefix=True):
name = 'kms-operation' if kms_prefix else 'operation'
return concepts.ResourceParameterAttributeConfig(
name=name,
help_text='The KMS operation of the {resource}.',
)
def LocationAttributeConfig(kms_prefix=True, region_fallthrough=False):
name = 'kms-location' if kms_prefix else 'location'
fallthroughs = []
if region_fallthrough:
fallthroughs.append(deps.ArgFallthrough('--region'))
return concepts.ResourceParameterAttributeConfig(
name=name,
help_text='The Google Cloud location for the {resource}.',
fallthroughs=fallthroughs,
)
def ProjectAttributeConfig(kms_prefix=True):
name = 'kms-project' if kms_prefix else 'project'
return concepts.ResourceParameterAttributeConfig(
name=name,
help_text='The Google Cloud project for the {resource}.',
fallthroughs=[deps.PropertyFallthrough(properties.VALUES.core.project)],
)
def GetKmsKeyVersionResourceSpec(kms_prefix=True):
return concepts.ResourceSpec(
'cloudkms.projects.locations.keyRings.cryptoKeys.cryptoKeyVersions',
resource_name='key version',
cryptoKeyVersionsId=KeyVersionAttributeConfig(kms_prefix),
cryptoKeysId=KeyAttributeConfig(kms_prefix),
keyRingsId=KeyringAttributeConfig(kms_prefix),
locationsId=LocationAttributeConfig(kms_prefix=kms_prefix),
projectsId=ProjectAttributeConfig(kms_prefix=kms_prefix),
disable_auto_completers=False,
)
def GetKmsKeyResourceSpec(kms_prefix=True, region_fallthrough=False):
return concepts.ResourceSpec(
'cloudkms.projects.locations.keyRings.cryptoKeys',
resource_name='key',
cryptoKeysId=KeyAttributeConfig(kms_prefix),
keyRingsId=KeyringAttributeConfig(kms_prefix),
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsKeyRingResourceSpec(kms_prefix=True, region_fallthrough=False):
return concepts.ResourceSpec(
'cloudkms.projects.locations.keyRings',
resource_name='keyring',
keyRingsId=KeyringAttributeConfig(kms_prefix),
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsKeyHandleResourceSpec(kms_prefix=True, region_fallthrough=False):
return concepts.ResourceSpec(
'cloudkms.projects.locations.keyHandles',
resource_name='key-handle',
keyHandlesId=KeyHandleAttributeConfig(kms_prefix),
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsEkmConnectionResourceSpec(kms_prefix=True, region_fallthrough=False):
return concepts.ResourceSpec(
'cloudkms.projects.locations.ekmConnections',
resource_name='ekmconnection',
ekmConnectionsId=EkmConnectionAttributeConfig(kms_prefix),
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsSingleTenantHsmInstanceResourceSpec(
kms_prefix=True, region_fallthrough=False
):
return concepts.ResourceSpec(
'cloudkms.projects.locations.singleTenantHsmInstances',
resource_name='singleTenantHsmInstance',
singleTenantHsmInstancesId=SingleTenantHsmInstanceAttributeConfig(
kms_prefix
),
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsSingleTenantHsmInstanceProposalResourceSpec(
kms_prefix=True, region_fallthrough=False
):
return concepts.ResourceSpec(
'cloudkms.projects.locations.singleTenantHsmInstances.proposals',
resource_name='singleTenantHsmInstanceProposal',
proposalsId=SingleTenantHsmInstanceProposalAttributeConfig(kms_prefix),
singleTenantHsmInstancesId=SingleTenantHsmInstanceAttributeConfig(
kms_prefix
),
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsOperationResourceSpec(kms_prefix=True, region_fallthrough=False):
return concepts.ResourceSpec(
'cloudkms.projects.locations.operations',
resource_name='operation',
operationsId=OperationAttributeConfig(kms_prefix),
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsLocationResourceSpec(kms_prefix=True, region_fallthrough=False):
return concepts.ResourceSpec(
'cloudkms.projects.locations',
resource_name='location',
locationsId=LocationAttributeConfig(
kms_prefix=kms_prefix, region_fallthrough=region_fallthrough
),
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsProjectResourceSpec(kms_prefix=True):
return concepts.ResourceSpec(
'cloudkms.projects',
resource_name='project',
projectsId=ProjectAttributeConfig(kms_prefix),
disable_auto_completers=False,
)
def GetKmsKeyPresentationSpec(
resource,
region_fallthrough=False,
flag_overrides=None,
permission_info=None,
):
"""Return a Presentation Spec for kms key resource argument.
Args:
resource: str, the name of the resource that the cryptokey will be used to
protect.
region_fallthrough: bool, True if the command has a region flag that should
be used as a fallthrough for the kms location.
flag_overrides: dict, The default flag names are 'kms-key', 'kms-keyring',
'kms-location' and 'kms-project'. You can pass a dict of overrides where
the keys of the dict are the default flag names, and the values are the
override names.
permission_info: str, optional permission info that overrides default
permission info group help.
Returns:
Presentation spec suitable for adding to concept parser.
"""
if not permission_info:
permission_info = '{} must hold permission {}'.format(
"The 'Compute Engine Service Agent' service account",
"'Cloud KMS CryptoKey Encrypter/Decrypter'",
)
group_help = (
'The Cloud KMS (Key Management Service) cryptokey that will be'
' used to protect the {}. {}.'.format(resource, permission_info)
)
presentation_spec = presentation_specs.ResourcePresentationSpec(
'--kms-key',
GetKmsKeyResourceSpec(region_fallthrough=region_fallthrough),
group_help,
flag_name_overrides=flag_overrides or {},
)
return presentation_spec
def AddKmsKeyResourceArg(
parser,
resource,
region_fallthrough=False,
flag_overrides=None,
permission_info=None,
required=False,
name='--kms-key',
hidden=False,
):
"""Add a resource argument for a KMS key to protect other resources.
Args:
parser: the parser for the command.
resource: str, the name of the resource that the cryptokey will be used to
protect.
region_fallthrough: bool, True if the command has a region flag that should
be used as a fallthrough for the kms location.
flag_overrides: dict, The default flag names are 'kms-key', 'kms-keyring',
'kms-location' and 'kms-project'. You can pass a dict of overrides where
the keys of the dict are the default flag names, and the values are the
override names.
permission_info: str, optional permission info that overrides default
permission info group help.
required: bool, optional. True if the flag must be parsable by the parser.
The default value is False.
name: str, optional name of the arg for the KMS resource. Defaults to
'--kms-key'.
hidden: bool, optional to hide the field. True if the flag must be parsable
by the parser. The default value is False.
"""
if not permission_info:
permission_info = '{} must hold permission {}'.format(
"The 'Compute Engine Service Agent' service account",
"'Cloud KMS CryptoKey Encrypter/Decrypter'",
)
concept_parsers.ConceptParser.ForResource(
name,
GetKmsKeyResourceSpec(region_fallthrough=region_fallthrough),
'The Cloud KMS (Key Management Service) cryptokey that will be used to '
'protect the {}. {}.'.format(resource, permission_info),
flag_name_overrides=flag_overrides,
required=required,
hidden=hidden,
).AddToParser(parser)
def AddKmsKeyResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsKeyResourceSpec(kms_prefix=False),
'The KMS key resource.',
required=required,
).AddToParser(parser)
def AddKmsKeyringResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsKeyRingResourceSpec(kms_prefix=False),
'The KMS keyring resource.',
required=required,
).AddToParser(parser)
def AddKmsKeyHandleResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsKeyHandleResourceSpec(kms_prefix=False),
'The KMS key-handle resource.',
required=required,
).AddToParser(parser)
def AddKmsEkmConnectionResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsEkmConnectionResourceSpec(kms_prefix=False),
'The KMS ekm connection resource.',
required=required,
).AddToParser(parser)
def AddKmsSingleTenantHsmInstanceResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsSingleTenantHsmInstanceResourceSpec(kms_prefix=False),
'The KMS single tenant HSM instance resource.',
required=required,
).AddToParser(parser)
def AddKmsSingleTenantHsmInstanceProposalResourceArgForKMS(
parser, required, name
):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsSingleTenantHsmInstanceProposalResourceSpec(kms_prefix=False),
'The KMS single tenant HSM instance proposal resource.',
required=required,
).AddToParser(parser)
def AddKmsOperationResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsOperationResourceSpec(kms_prefix=False),
'The KMS operation resource.',
required=required,
).AddToParser(parser)
def AddKmsLocationResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsLocationResourceSpec(kms_prefix=False),
'The KMS location resource.',
required=required,
).AddToParser(parser)
def AddKmsProjectResourceArgForKMS(parser, required, name):
concept_parsers.ConceptParser.ForResource(
name,
GetKmsProjectResourceSpec(kms_prefix=False),
'The KMS project resource.',
required=required,
).AddToParser(parser)

View File

@@ -0,0 +1,72 @@
project:
name: project
collection: cloudkms.projects
attributes:
- &project
parameter_name: projectsId
attribute_name: project
help: The project name.
location:
name: location
collection: cloudkms.projects.locations
attributes:
- &location
parameter_name: locationsId
attribute_name: location
help: The location of the resource.
ekm_connection:
name: ekm connection
collection: cloudkms.projects.locations.ekmConnections
attributes:
- *project
- *location
- &ekm_connection
parameter_name: ekmConnectionsId
attribute_name: ekm_connection
help: The name of the ekm connection.
key_ring:
name: keyring
collection: cloudkms.projects.locations.keyRings
attributes:
- *project
- *location
- &keyring
parameter_name: keyRingsId
attribute_name: keyring
help: The containing keyring.
import_job:
name: import job
collection: cloudkms.projects.locations.keyRings.importJobs
attributes:
- *project
- *location
- *keyring
- parameter_name: importJobsId
attribute_name: import_job
help: The name of the import job.
key:
name: key
collection: cloudkms.projects.locations.keyRings.cryptoKeys
attributes:
- *project
- *location
- *keyring
- parameter_name: cryptoKeysId
attribute_name: key
help: The name of the key.
key_handle:
name: key handle
collection: cloudkms.projects.locations.keyHandles
attributes:
- *project
- *location
- &keyhandle
parameter_name: keyHandlesId
attribute_name: key_handle
help: The name of the key handle.