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,51 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""The command group for the Google Secret Manager API."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA,
base.ReleaseTrack.ALPHA)
class CloudSecrets(base.Group):
"""Manage secrets on Google Cloud.
Google Secret Manager allows users to store and retrieve secrets such as API
keys, certificates, passwords on Google Cloud. Google Secret Manager is
integrated with Cloud IAM and Cloud Audit Logging so users can manage
permissions on individual secrets and monitor how these are used.
To learn more about Google Secret Manager, visit:
https://cloud.google.com/secret-manager/
To read API and usage documentation, visit:
https://cloud.google.com/secret-manager/docs/
"""
category = base.IDENTITY_AND_SECURITY_CATEGORY
def Filter(self, context, args):
del context, args
# This is technically the default, but some gcloud commands like IAM have
# user project override off by default, so we enable it here explicitly.
base.EnableUserProjectQuota()

View File

@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Command to add-iam-policy-binding to a secret resource."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.api_lib.util import exceptions as gcloud_exception
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.command_lib.secrets import args as secrets_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class AddIamPolicyBinding(base.Command):
"""Add IAM policy binding to a secret.
Add an IAM policy binding to the IAM policy of a secret. One binding
consists of a member and a role.
"""
detailed_help = {
'EXAMPLES': """\
To add an IAM policy binding for the role of 'roles/secretmanager.secretAccessor'
for the user 'test-user@gmail.com' on my-secret, run:
$ {command} my-secret --member='user:test-user@gmail.com' --role='roles/secretmanager.secretAccessor'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
""",
}
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser,
purpose='',
positional=True,
required=True,
help_text='Name of the secret for which to add the IAM policy binding.',
)
secrets_args.AddLocation(parser, purpose='to add iam policy', hidden=False)
iam_util.AddArgsForAddIamPolicyBinding(parser, add_condition=True)
@gcloud_exception.CatchHTTPErrorRaiseHTTPException(
'Status code: {status_code}. {status_message}.'
)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
multi_ref = args.CONCEPTS.secret.Parse()
condition = iam_util.ValidateAndExtractConditionMutexRole(args)
result = secrets_api.Secrets(api_version=api_version).AddIamPolicyBinding(
multi_ref,
args.member,
args.role,
condition=condition,
secret_location=args.location,
)
iam_util.LogSetIamPolicy(multi_ref.Name(), 'secret')
return result
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class AddIamPolicyBindingBeta(AddIamPolicyBinding):
"""Add IAM policy binding to a secret resource."""
detailed_help = {
'EXAMPLES': """\
To add an IAM policy binding for the role of 'roles/secretmanager.secretAccessor'
for the user 'test-user@gmail.com' on my-secret, run:
$ {command} my-secret --member='user:test-user@gmail.com' --role='roles/secretmanager.secretAccessor'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
""",
}

View File

@@ -0,0 +1,26 @@
# -*- 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.
"""Command group for managing Secretmanager secret configurations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Config(base.Group):
"""Manage Secretmanager secret configurations."""

View File

@@ -0,0 +1,38 @@
release_tracks: [ALPHA]
command_type: CONFIG_EXPORT
help_text:
brief: Export the configuration for a Secretmanager secret.
description: |
*{command}* exports the configuration for a Secretmanager secret.
Secret configurations can be exported in
Kubernetes Resource Model (krm) or Terraform HCL formats. The
default format is `krm`.
Specifying `--all` allows you to export the configurations for all
secrets within the project.
Specifying `--path` allows you to export the configuration(s) to
a local directory.
examples: |
To export the configuration for a secret, run:
$ {command} my-secret
To export the configuration for a secret to a file, run:
$ {command} my-secret --path=/path/to/dir/
To export the configuration for a secret in Terraform
HCL format, run:
$ {command} my-secret --resource-format=terraform
To export the configurations for all secrets within a
project, run:
$ {command} --all
arguments:
resource:
help_text: Secret to export the configuration for.
spec: !REF googlecloudsdk.command_lib.secrets.resources:secret

View File

@@ -0,0 +1,706 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Create a new secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
from googlecloudsdk.command_lib.secrets import util as secrets_util
from googlecloudsdk.command_lib.util import crc32c
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.command_lib.util.args import map_util
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.DefaultUniverseOnly
class Create(base.CreateCommand):
# pylint: disable=line-too-long
r"""Create a new secret.
Create a secret with the given name and creates a secret version with the
given data, if any. If a secret already exists with the given name, this
command will return an error.
## EXAMPLES
Create a secret with an automatic replication policy without creating any
versions:
$ {command} my-secret
Create a new secret named 'my-secret' with an automatic replication policy
and data from a file:
$ {command} my-secret --data-file=/tmp/secret
Create a new secret named 'my-secret' in 'us-central1' with data from a file:
$ {command} my-secret --data-file=/tmp/secret
--replication-policy=user-managed \
--locations=us-central1
Create a new secret named 'my-secret' in 'us-central1' and 'us-east1' with
the value "s3cr3t":
$ printf "s3cr3t" | {command} my-secret --data-file=-
--replication-policy=user-managed --locations=us-central1,us-east1
Create a new secret named 'my-secret' in 'us-central1' and 'us-east1' with
the value "s3cr3t" in PowerShell (Note: PowerShell will add a newline to the
resulting secret):
$ Write-Output "s3cr3t" | {command} my-secret --data-file=-
--replication-policy=user-managed --locations=us-central1,us-east1
Create a secret with an automatic replication policy and a next rotation time:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
Create a secret with an automatic replication policy and a rotation period:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
--rotation-period="7200s"
Create a secret with delayed secret version destroy enabled:
$ {command} my-secret --version-destroy-ttl="86400s"
"""
# pylint: enable=line-too-long
EMPTY_DATA_FILE_MESSAGE = (
'The value provided for --data-file is the empty string. This can happen '
'if you pass or pipe a variable that is undefined. Please verify that '
'the --data-file flag is not the empty string. If you are not providing '
'secret data, omit the --data-file flag.')
INVALID_POLICY_MESSAGE = (
'The value provided for --replication-policy is invalid. Valid values '
'are "automatic" and "user-managed".')
INVALID_POLICY_PROP_MESSAGE = (
'Cannot use the secrets/replication-policy property because its value is'
' invalid. Please either set it to a valid value ("automatic" or '
'"user-managed") or override it for this command by using the '
'--replication-policy flag.')
MANAGED_BUT_NO_LOCATIONS_MESSAGE = (
'If --replication-policy is user-managed then --locations must also be '
'provided. Please set the desired storage regions in --locations or the '
'secrets/locations property. For an automatic replication policy, please'
' set --replication-policy or the secrets/replication-policy property to'
' "automatic".')
AUTOMATIC_AND_LOCATIONS_MESSAGE = (
'If --replication-policy is "automatic" then --locations are not '
'allowed. Please remove the --locations flag or set the '
'--replication-policy to "user-managed".')
AUTOMATIC_PROP_AND_LOCATIONS_MESSAGE = (
'The secrets/replication-policy property is "automatic" and not '
'overridden so --locations are not allowed. Please remove the --locations'
' flag or set the replication-policy to "user-managed".')
AUTOMATIC_AND_LOCATIONS_PROP_MESSAGE = (
'Cannot create a secret with an "automatic" replication policy if the '
'secrets/locations property is set. Please either use a "user-managed" '
'replication policy or unset secrets/locations.')
NO_POLICY_AND_LOCATIONS_MESSAGE = (
'Locations are only allowed when creating a secret with a "user-managed" '
'replication policy. Please use the --replication-policy flag to set it '
'or remove --locations to use an automatic replication policy.')
MANAGED_AND_KMS_FLAG_MESSAGE = (
'The --kms-key-name flag can only be used when creating a secret with an'
' "automatic" replication policy. To specify encryption keys for secrets '
'with a "user-managed" replication policy, please use '
'--replication-policy-file.')
POLICY_AND_POLICY_FILE_MESSAGE = (
'A --replication-policy-file and --replication-policy cannot both be '
'specified.')
LOCATIONS_AND_POLICY_FILE_MESSAGE = (
'A --replication-policy-file and --locations cannot both be specified.')
KMS_KEY_AND_POLICY_FILE_MESSAGE = (
'A --replication-policy-file and --kms-key-name cannot both be specified.'
)
REPLICATION_POLICY_FILE_EMPTY_MESSAGE = ('File cannot be empty.')
KMS_KEY_AND_USER_MANAGED_MESSAGE = (
'The --kms-key-name flag can only be set for automatically replicated '
'secrets. To create a user managed secret with customer managed '
'encryption keys, please use --replication-policy-file.')
CONFIRM_EXPIRE_TIME_MESSAGE = (
'This secret and all of its versions will be automatically deleted at '
'the requested expire-time of [{expire_time}].')
CONFIRM_TTL_MESSAGE = (
'This secret and all of its versions will be automatically deleted '
'after the requested ttl of [{ttl}] has elapsed.')
REGIONAL_KMS_FLAG_MESSAGE = (
'The --regional-kms-key-name flag can only be used when creating a'
' regional secret with "--location" and should not be used with'
' "--replication-policy-file" or "--kms-key-name"'
)
REGIONAL_SECRET_MESSAGE = (
'Regional secret created using "--location" should not have flags like '
'"--replication-policy-file" or "--kms-key-name" or "--locations" or '
'--replication-policy'
)
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to the command.
"""
secrets_args.AddSecret(
parser, purpose='to create', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to create secret', hidden=False)
secrets_args.AddDataFile(parser)
secrets_args.AddCreateReplicationPolicyGroup(parser)
labels_util.AddCreateLabelsFlags(parser)
secrets_args.AddCreateExpirationGroup(parser)
secrets_args.AddTopics(parser)
secrets_args.AddCreateRotationGroup(parser)
secrets_args.AddRegionalKmsKeyName(parser)
secrets_args.AddCreateVersionDestroyTTL(parser)
secrets_args.GetTagsArg().AddToParser(parser)
annotations = parser.add_group(mutex=True, help='Annotations')
map_util.AddMapSetFlag(annotations, 'annotations', 'Annotations', str, str)
def Run(self, args):
"""Run is called by calliope to create the secret.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
The response from the create secret API call.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
messages = secrets_api.GetMessages(version=api_version)
secret_ref = args.CONCEPTS.secret.Parse()
is_regional = args.location is not None
data = secrets_util.ReadFileOrStdin(args.data_file)
replication_policy_contents = secrets_util.ReadFileOrStdin(
args.replication_policy_file, is_binary=False)
labels = labels_util.ParseCreateArgs(args, messages.Secret.LabelsValue)
tags = secrets_args.GetTagsFromArgs(args, messages.Secret.TagsValue)
replication_policy = args.replication_policy
locations = args.locations
kms_keys = []
if args.replication_policy_file and args.replication_policy:
raise exceptions.ConflictingArgumentsException(
self.POLICY_AND_POLICY_FILE_MESSAGE)
if args.replication_policy_file and args.locations:
raise exceptions.ConflictingArgumentsException(
self.LOCATIONS_AND_POLICY_FILE_MESSAGE)
if args.replication_policy_file and args.kms_key_name:
raise exceptions.ConflictingArgumentsException(
self.KMS_KEY_AND_POLICY_FILE_MESSAGE)
if not is_regional and args.regional_kms_key_name:
raise exceptions.ConflictingArgumentsException(
self.REGIONAL_KMS_FLAG_MESSAGE
)
if args.regional_kms_key_name and (
args.replication_policy_file or args.kms_key_name
):
raise exceptions.ConflictingArgumentsException(
self.REGIONAL_KMS_FLAG_MESSAGE
)
if is_regional and (
replication_policy
or args.kms_key_name
or args.replication_policy_file
or args.locations
):
raise exceptions.ConflictingArgumentsException(
self.REGIONAL_SECRET_MESSAGE
)
if args.kms_key_name:
kms_keys.append(args.kms_key_name)
if args.replication_policy_file:
if not replication_policy_contents:
raise exceptions.InvalidArgumentException(
'replication-policy', self.REPLICATION_POLICY_FILE_EMPTY_MESSAGE
)
replication_policy, locations, kms_keys = (
secrets_util.ParseReplicationFileContents(replication_policy_contents)
)
else:
if not replication_policy:
replication_policy = properties.VALUES.secrets.replication_policy.Get()
default_to_automatic = replication_policy is None
if default_to_automatic:
replication_policy = 'automatic'
if replication_policy not in {'user-managed', 'automatic'}:
if args.replication_policy:
raise exceptions.InvalidArgumentException('replication-policy',
self.INVALID_POLICY_MESSAGE)
raise exceptions.InvalidArgumentException(
'replication-policy', self.INVALID_POLICY_PROP_MESSAGE)
if replication_policy == 'user-managed' and kms_keys:
raise exceptions.InvalidArgumentException(
'kms-key-name', self.KMS_KEY_AND_USER_MANAGED_MESSAGE)
if not locations:
# if locations weren't given, try to get them from properties
locations = properties.VALUES.secrets.locations.Get()
if locations:
locations = locations.split(',')
if replication_policy == 'user-managed' and not locations:
raise exceptions.RequiredArgumentException(
'locations', self.MANAGED_BUT_NO_LOCATIONS_MESSAGE)
if replication_policy == 'automatic':
if args.locations:
# check args.locations separately from locations because we have
# different error messages depending on whether the user used the
# --locations flag or the secrets/locations property
if args.replication_policy:
raise exceptions.InvalidArgumentException(
'locations', self.AUTOMATIC_AND_LOCATIONS_MESSAGE)
if default_to_automatic:
raise exceptions.InvalidArgumentException(
'locations', self.NO_POLICY_AND_LOCATIONS_MESSAGE)
raise exceptions.InvalidArgumentException(
'locations', self.AUTOMATIC_PROP_AND_LOCATIONS_MESSAGE)
if locations:
raise exceptions.InvalidArgumentException(
'replication-policy', self.AUTOMATIC_AND_LOCATIONS_PROP_MESSAGE)
locations = []
# Differentiate between the flag being provided with an empty value and the
# flag being omitted. See b/138796299 for info.
if args.data_file == '': # pylint: disable=g-explicit-bool-comparison
raise exceptions.BadFileException(self.EMPTY_DATA_FILE_MESSAGE)
if args.expire_time:
msg = self.CONFIRM_EXPIRE_TIME_MESSAGE.format(
expire_time=args.expire_time)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True)
if args.ttl:
msg = self.CONFIRM_TTL_MESSAGE.format(ttl=args.ttl)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True)
annotations = []
if args.IsSpecified('set_annotations'):
annotations = [
messages.Secret.AnnotationsValue.AdditionalProperty(
key=annotation, value=metadata)
for (annotation, metadata) in args.set_annotations.items()
]
if is_regional:
replication_policy = None
if args.version_destroy_ttl:
version_destroy_ttl = f'{args.version_destroy_ttl}s'
else:
version_destroy_ttl = None
# Create the secret
response = secrets_api.Secrets(api_version=api_version).Create(
secret_ref,
labels=labels,
tags=tags,
locations=locations,
policy=replication_policy,
expire_time=args.expire_time,
ttl=args.ttl,
keys=kms_keys,
topics=args.topics,
annotations=annotations,
next_rotation_time=args.next_rotation_time,
rotation_period=args.rotation_period,
regional_kms_key_name=args.regional_kms_key_name,
version_destroy_ttl=version_destroy_ttl,
secret_location=args.location,
)
if data:
data_crc32c = crc32c.get_crc32c(data)
version = secrets_api.Secrets(api_version=api_version).AddVersion(
secret_ref,
data,
crc32c.get_checksum(data_crc32c),
secret_location=args.location,
)
if is_regional:
version_ref = secrets_args.ParseRegionalVersionRef(version.name)
else:
version_ref = secrets_args.ParseVersionRef(version.name)
secrets_log.Versions().Created(version_ref)
else:
secrets_log.Secrets().Created(secret_ref)
return response
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@base.DefaultUniverseOnly
class CreateBeta(base.CreateCommand):
# pylint: disable=line-too-long
r"""Create a new secret.
Create a secret with the given name and creates a secret version with the
given data, if any. Note, the created secret ends with a newline.
If a secret already exists with the given name, this command will return
an error.
## EXAMPLES
Create a secret with an automatic replication policy without creating any
versions:
$ {command} my-secret
Create a new secret named 'my-secret' with an automatic replication policy
and data from a file:
$ {command} my-secret --data-file=/tmp/secret
Create a new secret named 'my-secret' in 'us-central1' with data from a file:
$ {command} my-secret --data-file=/tmp/secret
--replication-policy=user-managed \
--locations=us-central1
Create a new secret named 'my-secret' in 'us-central1' and 'us-east1' with
the value "s3cr3t":
$ printf "s3cr3t" | {command} my-secret --data-file=-
--replication-policy=user-managed --locations=us-central1,us-east1
Create a new secret named 'my-secret' in 'us-central1' and 'us-east1' with
the value "s3cr3t" in PowerShell (Note: PowerShell will add a newline to the
resulting secret):
$ Write-Output "s3cr3t" | {command} my-secret --data-file=-
--replication-policy=user-managed --locations=us-central1,us-east1
Create an expiring secret with an automatic replication policy using a ttl:
$ {command} my-secret --ttl="600s"
Create an expiring secret with an automatic replication policy using an
expire-time:
$ {command} my-secret --expire-time="2030-01-01T08:15:30-05:00"
Create a secret with an automatic replication policy and a next rotation time:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
Create a secret with an automatic replication policy and a rotation period:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
--rotation-period="7200s"
Create a secret with delayed secret version destroy enabled:
$ {command} my-secret --version-destroy-ttl="86400s"
"""
# pylint: enable=line-too-long
EMPTY_DATA_FILE_MESSAGE = (
'The value provided for --data-file is the empty string. This can happen '
'if you pass or pipe a variable that is undefined. Please verify that '
'the --data-file flag is not the empty string. If you are not providing '
'secret data, omit the --data-file flag.')
INVALID_POLICY_MESSAGE = (
'The value provided for --replication-policy is invalid. Valid values '
'are "automatic" and "user-managed".')
INVALID_POLICY_PROP_MESSAGE = (
'Cannot use the secrets/replication-policy property because its value is'
' invalid. Please either set it to a valid value ("automatic" or '
'"user-managed") or override it for this command by using the '
'--replication-policy flag.')
MANAGED_BUT_NO_LOCATIONS_MESSAGE = (
'If --replication-policy is user-managed then --locations must also be '
'provided. Please set the desired storage regions in --locations or the '
'secrets/locations property. For an automatic replication policy, please'
' set --replication-policy or the secrets/replication-policy property to'
' "automatic".')
AUTOMATIC_AND_LOCATIONS_MESSAGE = (
'If --replication-policy is "automatic" then --locations are not '
'allowed. Please remove the --locations flag or set the '
'--replication-policy to "user-managed".')
AUTOMATIC_PROP_AND_LOCATIONS_MESSAGE = (
'The secrets/replication-policy property is "automatic" and not '
'overridden so --locations are not allowed. Please remove the --locations'
' flag or set the replication-policy to "user-managed".')
AUTOMATIC_AND_LOCATIONS_PROP_MESSAGE = (
'Cannot create a secret with an "automatic" replication policy if the '
'secrets/locations property is set. Please either use a "user-managed" '
'replication policy or unset secrets/locations.')
NO_POLICY_AND_LOCATIONS_MESSAGE = (
'Locations are only allowed when creating a secret with a "user-managed" '
'replication policy. Please use the --replication-policy flag to set it '
'or remove --locations to use an automatic replication policy.')
MANAGED_AND_KMS_FLAG_MESSAGE = (
'The --kms-key-name flag can only be used when creating a secret with an'
' "automatic" replication policy. To specify encryption keys for secrets '
'with a "user-managed" replication policy, please use '
'--replication-policy-file.')
POLICY_AND_POLICY_FILE_MESSAGE = (
'A --replication-policy-file and --replication-policy cannot both be '
'specified.')
LOCATIONS_AND_POLICY_FILE_MESSAGE = (
'A --replication-policy-file and --locations cannot both be specified.')
KMS_KEY_AND_POLICY_FILE_MESSAGE = (
'A --replication-policy-file and --kms-key-name cannot both be specified.'
)
REPLICATION_POLICY_FILE_EMPTY_MESSAGE = ('File cannot be empty.')
KMS_KEY_AND_USER_MANAGED_MESSAGE = (
'The --kms-key-name flag can only be set for automatically replicated '
'secrets. To create a user managed secret with customer managed '
'encryption keys, please use --replication-policy-file.')
CONFIRM_EXPIRE_TIME_MESSAGE = (
'This secret and all of its versions will be automatically deleted at '
'the requested expire-time of [{expire_time}].')
CONFIRM_TTL_MESSAGE = (
'This secret and all of its versions will be automatically deleted '
'after the requested ttl of [{ttl}] has elapsed.')
REGIONAL_KMS_FLAG_MESSAGE = (
'The --regional-kms-key-name flag can only be used when creating a'
' regional secret with "--location" and should not be used with'
' "--replication-policy-file" or "--kms-key-name"'
)
REGIONAL_SECRET_MESSAGE = (
'Regional secret created using "--location" should not have flags like '
'"--replication-policy-file" or "--kms-key-name" or "--locations" or '
'--replication-policy'
)
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to create', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to create secret', hidden=False)
secrets_args.AddDataFile(parser)
secrets_args.AddCreateReplicationPolicyGroup(parser)
labels_util.AddCreateLabelsFlags(parser)
secrets_args.AddCreateExpirationGroup(parser)
secrets_args.AddCreateRotationGroup(parser)
secrets_args.AddTopics(parser)
secrets_args.AddRegionalKmsKeyName(parser)
secrets_args.AddCreateVersionDestroyTTL(parser)
secrets_args.GetTagsArg().AddToParser(parser)
annotations = parser.add_group(mutex=True, help='Annotations')
map_util.AddMapSetFlag(annotations, 'annotations', 'Annotations', str, str)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
messages = secrets_api.GetMessages(version=api_version)
secret_ref = args.CONCEPTS.secret.Parse()
is_regional = args.location is not None
data = secrets_util.ReadFileOrStdin(args.data_file)
replication_policy_contents = secrets_util.ReadFileOrStdin(
args.replication_policy_file, is_binary=False)
labels = labels_util.ParseCreateArgs(args, messages.Secret.LabelsValue)
tags = secrets_args.GetTagsFromArgs(args, messages.Secret.TagsValue)
replication_policy = args.replication_policy
locations = args.locations
kms_keys = []
if args.replication_policy_file and args.replication_policy:
raise exceptions.ConflictingArgumentsException(
self.POLICY_AND_POLICY_FILE_MESSAGE)
if args.replication_policy_file and args.locations:
raise exceptions.ConflictingArgumentsException(
self.LOCATIONS_AND_POLICY_FILE_MESSAGE)
if args.replication_policy_file and args.kms_key_name:
raise exceptions.ConflictingArgumentsException(
self.KMS_KEY_AND_POLICY_FILE_MESSAGE)
if not is_regional and args.regional_kms_key_name:
raise exceptions.ConflictingArgumentsException(
self.REGIONAL_KMS_FLAG_MESSAGE
)
if args.regional_kms_key_name and (
args.replication_policy_file or args.kms_key_name
):
raise exceptions.ConflictingArgumentsException(
self.REGIONAL_KMS_FLAG_MESSAGE
)
if is_regional and (
replication_policy
or args.kms_key_name
or args.replication_policy_file
or args.locations
):
raise exceptions.ConflictingArgumentsException(
self.REGIONAL_SECRET_MESSAGE
)
if args.kms_key_name:
kms_keys.append(args.kms_key_name)
if args.replication_policy_file:
if not replication_policy_contents:
raise exceptions.InvalidArgumentException(
'replication-policy', self.REPLICATION_POLICY_FILE_EMPTY_MESSAGE
)
replication_policy, locations, kms_keys = (
secrets_util.ParseReplicationFileContents(replication_policy_contents)
)
else:
if not replication_policy:
replication_policy = properties.VALUES.secrets.replication_policy.Get()
default_to_automatic = replication_policy is None
if default_to_automatic:
replication_policy = 'automatic'
if replication_policy not in {'user-managed', 'automatic'}:
if args.replication_policy:
raise exceptions.InvalidArgumentException('replication-policy',
self.INVALID_POLICY_MESSAGE)
raise exceptions.InvalidArgumentException(
'replication-policy', self.INVALID_POLICY_PROP_MESSAGE)
if replication_policy == 'user-managed' and kms_keys:
raise exceptions.InvalidArgumentException(
'kms-key-name', self.KMS_KEY_AND_USER_MANAGED_MESSAGE)
if not locations:
# if locations weren't given, try to get them from properties
locations = properties.VALUES.secrets.locations.Get()
if locations:
locations = locations.split(',')
if replication_policy == 'user-managed' and not locations:
raise exceptions.RequiredArgumentException(
'locations', self.MANAGED_BUT_NO_LOCATIONS_MESSAGE)
if replication_policy == 'automatic':
if args.locations:
# check args.locations separately from locations because we have
# different error messages depending on whether the user used the
# --locations flag or the secrets/locations property
if args.replication_policy:
raise exceptions.InvalidArgumentException(
'locations', self.AUTOMATIC_AND_LOCATIONS_MESSAGE)
if default_to_automatic:
raise exceptions.InvalidArgumentException(
'locations', self.NO_POLICY_AND_LOCATIONS_MESSAGE)
raise exceptions.InvalidArgumentException(
'locations', self.AUTOMATIC_PROP_AND_LOCATIONS_MESSAGE)
if locations:
raise exceptions.InvalidArgumentException(
'replication-policy', self.AUTOMATIC_AND_LOCATIONS_PROP_MESSAGE)
locations = []
# Differentiate between the flag being provided with an empty value and the
# flag being omitted. See b/138796299 for info.
if args.data_file == '': # pylint: disable=g-explicit-bool-comparison
raise exceptions.BadFileException(self.EMPTY_DATA_FILE_MESSAGE)
if args.expire_time:
msg = self.CONFIRM_EXPIRE_TIME_MESSAGE.format(
expire_time=args.expire_time)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True)
if args.ttl:
msg = self.CONFIRM_TTL_MESSAGE.format(ttl=args.ttl)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True)
annotations = []
if args.IsSpecified('set_annotations'):
annotations = [
messages.Secret.AnnotationsValue.AdditionalProperty(
key=annotation, value=metadata)
for (annotation, metadata) in args.set_annotations.items()
]
if is_regional:
replication_policy = None
if args.version_destroy_ttl:
version_destroy_ttl = f'{args.version_destroy_ttl}s'
else:
version_destroy_ttl = None
# Create the secret
response = secrets_api.Secrets(api_version=api_version).Create(
secret_ref,
labels=labels,
tags=tags,
locations=locations,
policy=replication_policy,
expire_time=args.expire_time,
ttl=args.ttl,
keys=kms_keys,
next_rotation_time=args.next_rotation_time,
rotation_period=args.rotation_period,
topics=args.topics,
annotations=annotations,
regional_kms_key_name=args.regional_kms_key_name,
version_destroy_ttl=version_destroy_ttl,
secret_location=args.location,
)
if data:
data_crc32c = crc32c.get_crc32c(data)
version = secrets_api.Secrets(api_version=api_version).AddVersion(
secret_ref,
data,
crc32c.get_checksum(data_crc32c),
secret_location=args.location,
)
if is_regional:
version_ref = secrets_args.ParseRegionalVersionRef(version.name)
else:
version_ref = secrets_args.ParseVersionRef(version.name)
secrets_log.Versions().Created(version_ref)
else:
secrets_log.Secrets().Created(secret_ref)
return response

View File

@@ -0,0 +1,136 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Delete a secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
from googlecloudsdk.core.console import console_io
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Delete(base.DeleteCommand):
r"""Delete a secret.
Delete a secret and destroy all secret versions. This action is irreversible.
If the given secret does not exist, this command will succeed, but the
operation will be a no-op.
## EXAMPLES
Delete a secret `my-secret`:
$ {command} my-secret
Delete a secret `my-secret` using an etag:
$ {command} my-secret --etag=123
"""
CONFIRM_DELETE_MESSAGE = (
'You are about to destroy the secret [{secret}] and its [{num_versions}] '
'version(s). This action cannot be reversed.')
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to delete', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to delete secret', hidden=False)
secrets_args.AddSecretEtag(parser, action='deleted')
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
messages = secrets_api.GetMessages(version=api_version)
secret_ref = args.CONCEPTS.secret.Parse()
# List all secret versions and parse their refs
versions = secrets_api.Versions(api_version=api_version).ListWithPager(
secret_ref=secret_ref, limit=9999, secret_location=args.location
)
active_version_count = 0
for version in versions:
if version.state != messages.SecretVersion.StateValueValuesEnum.DESTROYED:
active_version_count += 1
msg = self.CONFIRM_DELETE_MESSAGE.format(
secret=secret_ref.Name(), num_versions=active_version_count
)
console_io.PromptContinue(msg, throw_if_unattended=True, cancel_on_no=True)
result = secrets_api.Secrets(api_version=api_version).Delete(
secret_ref, etag=args.etag, secret_location=args.location
)
secrets_log.Secrets().Deleted(secret_ref)
return result
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class DeleteBeta(Delete):
r"""Delete a secret.
Delete a secret and destroy all secret versions. This action is irreversible.
If the given secret does not exist, this command will succeed, but the
operation will be a no-op.
## EXAMPLES
Delete a secret `my-secret`:
$ {command} my-secret
Delete a secret `my-secret` using etag:
$ {command} my-secret --etag=123
"""
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to delete', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to delete secret', hidden=False)
secrets_args.AddSecretEtag(parser, action='deleted')
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
messages = secrets_api.GetMessages(version=api_version)
secret_ref = args.CONCEPTS.secret.Parse()
# List all secret versions and parse their refs
versions = secrets_api.Versions(api_version=api_version).ListWithPager(
secret_ref=secret_ref, limit=9999, secret_location=args.location
)
active_version_count = 0
for version in versions:
if version.state != messages.SecretVersion.StateValueValuesEnum.DESTROYED:
active_version_count += 1
msg = self.CONFIRM_DELETE_MESSAGE.format(
secret=secret_ref.Name(), num_versions=active_version_count
)
console_io.PromptContinue(msg, throw_if_unattended=True, cancel_on_no=True)
result = secrets_api.Secrets(api_version=api_version).Delete(
secret_ref, etag=args.etag, secret_location=args.location
)
secrets_log.Secrets().Deleted(secret_ref)
return result

View File

@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Describe a secret's metadata."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.secrets import args as secrets_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Describe(base.DescribeCommand):
r"""Describe a secret's metadata.
Describe a secret's metadata.
## EXAMPLES
Describe metadata of the secret named 'my-secret':
$ {command} my-secret
"""
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to describe', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to describe', hidden=False)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
secret = secrets_api.Secrets(api_version=api_version).Get(
secret_ref, secret_location=args.location
)
return secret
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class DescribeBeta(Describe):
r"""Describe a secret's metadata.
Describe a secret's metadata.
## EXAMPLES
Describe metadata of the secret named 'my-secret':
$ {command} my-secret
"""
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to describe', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to describe', hidden=False)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
secret = secrets_api.Secrets(api_version=api_version).Get(
secret_ref, secret_location=args.location
)
return secret

View File

@@ -0,0 +1,84 @@
# -*- 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.
"""Fetch the IAM policy for a secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.secrets import args as secrets_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class GetIamPolicy(base.ListCommand):
"""Get the IAM policy for the secret.
Displays the IAM policy associated with the secret. If formatted as JSON,
the output can be edited and used as a policy file for set-iam-policy. The
output includes an "etag" field identifying the version emitted and
allowing detection of concurrent policy updates.
Run gcloud secrets set-iam-policy for additional details.
"""
detailed_help = {
'EXAMPLES': """\
To print the IAM policy for secret named 'my-secret', run:
$ {command} my-secret
""",
}
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser,
purpose='',
positional=True,
required=True,
help_text='Name of the secret from which to get IAM policy.',
)
secrets_args.AddLocation(parser, purpose='to get iam policy', hidden=False)
base.URI_FLAG.RemoveFromParser(parser)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
multi_ref = args.CONCEPTS.secret.Parse()
return secrets_api.Secrets(api_version=api_version).GetIamPolicy(
multi_ref, secret_location=args.location
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class GetIamPolicyBeta(GetIamPolicy):
"""Get the IAM policy of a secret.
Gets the IAM policy for the given secret.
Returns an empty policy if the resource does not have a policy
set.
"""
detailed_help = {
'EXAMPLES': """\
To print the IAM policy for secret named 'my-secret', run:
$ {command} my-secret [--location=]
""",
}

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""List all secret names."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import fmt as secrets_fmt
from googlecloudsdk.core.resource import resource_expr_rewrite
from googlecloudsdk.core.resource import resource_projection_spec
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class List(base.ListCommand):
r"""List all secret names.
List all secret names. This command only returns the secret's names, not
their secret data. To learn about retrieving a secret's data, run
`$ {parent_command} versions access --help`.
## EXAMPLES
List secret names.
$ {command}
"""
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for list secret command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddProject(parser)
secrets_args.AddLocation(
parser, purpose='to list regional secrets', hidden=False
)
base.PAGE_SIZE_FLAG.SetDefault(parser, 100)
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Secrets:
"""Run is called by calliope to implement the list secrets command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke list secrets.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
if args.location:
project_ref = args.CONCEPTS.location.Parse()
secrets_fmt.RegionalSecretTableUsingArgument(
args, api_version=api_version
)
else:
project_ref = args.CONCEPTS.project.Parse()
secrets_fmt.SecretTableUsingArgument(args, api_version=api_version)
if not project_ref:
raise exceptions.RequiredArgumentException(
'project',
'Please set a project with `--project` flag or `gcloud config set'
' project PROJECT_ID`.',
)
server_filter = None
if args.filter:
rewriter = resource_expr_rewrite.Backend()
display_info = args.GetDisplayInfo()
defaults = resource_projection_spec.ProjectionSpec(
symbols=display_info.transforms, aliases=display_info.aliases
)
_, server_filter = rewriter.Rewrite(args.filter, defaults=defaults)
return secrets_api.Secrets(api_version=api_version).ListWithPager(
project_ref=project_ref, limit=args.limit, request_filter=server_filter
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class ListBeta(List):
r"""List all secret names.
List all secret names. This command only returns the secret's names, not
their secret data. To learn about retrieving a secret's data, run
`$ {parent_command} versions access --help`.
## EXAMPLES
List secret names.
$ {command}
"""
@staticmethod
def Args(parser):
secrets_args.AddProject(parser)
secrets_args.AddLocation(
parser, purpose='to list regional secrets', hidden=False
)
base.PAGE_SIZE_FLAG.SetDefault(parser, 100)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
project_ref = args.CONCEPTS.project.Parse()
location_ref = args.CONCEPTS.location.Parse()
if args.location:
project_ref = location_ref
secrets_fmt.RegionalSecretTableUsingArgument(
args, api_version=api_version
)
else:
secrets_fmt.SecretTableUsingArgument(args, api_version=api_version)
if not project_ref:
raise exceptions.RequiredArgumentException(
'project',
'Please set a project with `--project` flag or `gcloud config set'
' project <project_id>`.',
)
server_filter = None
if args.filter:
rewriter = resource_expr_rewrite.Backend()
display_info = args.GetDisplayInfo()
defaults = resource_projection_spec.ProjectionSpec(
symbols=display_info.transforms, aliases=display_info.aliases
)
_, server_filter = rewriter.Rewrite(args.filter, defaults=defaults)
return secrets_api.Secrets(api_version=api_version).ListWithPager(
project_ref=project_ref, limit=args.limit, request_filter=server_filter
)

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""The command group for locations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class CloudSecrets(base.Group):
"""Manage locations of users' secrets.
Manage locations of users' secrets on Google Cloud.
"""
def Filter(self, context, args):
del context, args

View File

@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Describe a location."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.secrets import args as secrets_args
class Describe(base.DescribeCommand):
r"""Describe a location.
Describe a location available for storing secrets.
## EXAMPLES
Describe the location 'us-central1':
$ {command} us-central1
"""
@staticmethod
def Args(parser):
secrets_args.AddLocation(
parser, purpose='to describe', positional=True, required=True)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
location_ref = args.CONCEPTS.location.Parse()
return secrets_api.Locations(api_version=api_version).Get(location_ref)

View File

@@ -0,0 +1,89 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""List all available locations in which secrets can be replicated."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import fmt as secrets_fmt
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class ListBeta(base.ListCommand):
r"""List all available locations.
List all available locations in which secrets can be replicated.
## EXAMPLES
List available secrets locations:
$ {command}
"""
@staticmethod
def Args(parser):
api_version = secrets_api.GetApiFromTrack(base.ReleaseTrack.BETA)
secrets_args.AddProject(parser)
secrets_fmt.UseLocationTable(parser, api_version=api_version)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
project_ref = args.CONCEPTS.project.Parse()
if not project_ref:
raise exceptions.RequiredArgumentException(
'project',
'Please set a project with "--project" flag or "gcloud config set project <project_id>".'
)
return secrets_api.Locations(api_version=api_version).ListWithPager(
project_ref=project_ref, limit=args.limit
)
@base.ReleaseTracks(base.ReleaseTrack.GA)
class List(base.ListCommand):
r"""List all available locations.
List all available locations in which secrets can be replicated.
## EXAMPLES
List available secrets locations:
$ {command}
"""
@staticmethod
def Args(parser):
api_version = secrets_api.GetApiFromTrack(base.ReleaseTrack.GA)
secrets_args.AddProject(parser)
secrets_fmt.UseLocationTable(parser, api_version)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
project_ref = args.CONCEPTS.project.Parse()
if not project_ref:
raise exceptions.RequiredArgumentException(
'project',
'Please set a project with "--project" flag or "gcloud config set project <project_id>".'
)
return secrets_api.Locations(api_version=api_version).ListWithPager(
project_ref=project_ref, limit=args.limit
)

View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Command to remove-iam-policy-binding to a secret resource."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.api_lib.util import exceptions as gcloud_exception
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.command_lib.secrets import args as secrets_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class RemoveIamPolicyBinding(base.Command):
"""Remove IAM policy binding for a secret.
Removes a policy binding from the IAM policy of a secret. One binding
consists of a member and a role.
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
"""
detailed_help = {
'EXAMPLES': """\
To remove an IAM policy binding for the role of 'roles/viewer' for the user
'test-user@gmail.com' on the my-secret, run:
$ {command} my-secret --member='user:test-user@gmail.com' --role='roles/viewer'
""",
}
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser,
purpose='',
positional=True,
required=True,
help_text='Name of the secret from which to remove IAM policy binding.',
)
secrets_args.AddLocation(
parser, purpose='to remove iam policy', hidden=False
)
iam_util.AddArgsForRemoveIamPolicyBinding(parser, add_condition=True)
@gcloud_exception.CatchHTTPErrorRaiseHTTPException(
'Status code: {status_code}. {status_message}.'
)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
condition = iam_util.ValidateAndExtractConditionMutexRole(args)
result = secrets_api.Secrets(
api_version=api_version
).RemoveIamPolicyBinding(
secret_ref,
args.member,
args.role,
condition=condition,
secret_location=args.location,
)
iam_util.LogSetIamPolicy(secret_ref.Name(), 'secret')
return result
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class RemoveIamPolicyBindingBeta(RemoveIamPolicyBinding):
"""Remove IAM policy binding of a secret resource."""
detailed_help = {
'EXAMPLES': """\
To remove an IAM policy binding for the role of 'roles/viewer' for the user
'test-user@gmail.com' on the my-secret, run:
$ {command} my-secret --member='user:test-user@gmail.com' --role='roles/viewer'
""",
}

View File

@@ -0,0 +1,32 @@
# -*- 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.
"""Manage secret replication."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class CloudSecrets(base.Group):
"""Manage secret replication.
Manage secret replication on Google Cloud.
"""
def Filter(self, context, args):
del context, args

View File

@@ -0,0 +1,61 @@
# -*- 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.
"""Describe a secret's metadata."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class Get(base.DescribeCommand):
r"""Describe a secret's replication.
Describe a secret's replication
## EXAMPLES
To describe the replication of a secret named 'my-secret', run:
$ {command} my-secret
"""
SECRET_MISSING_MESSAGE = (
'Cannot get replication for secret [{secret}] because it does not exist. '
'Please use the create command to create a new secret.')
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
secrets_args.AddSecret(
parser, purpose='to describe', positional=True, required=True)
def Run(self, args: parser_extensions.Namespace):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
secret = secrets_api.Secrets(api_version=api_version).GetOrNone(secret_ref)
# Secret does not exist
if secret is None:
raise exceptions.InvalidArgumentException(
'secret',
self.SECRET_MISSING_MESSAGE.format(secret=secret_ref.Name()))
return secret.replication

View File

@@ -0,0 +1,81 @@
# -*- 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.
"""Describe a secret's metadata."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
from googlecloudsdk.command_lib.secrets import util as secrets_util
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class Set(base.UpdateCommand):
r"""Set a secret's replication.
Sets the replication policy for the given secret as defined in a JSON or YAML
file. The locations that a Secret is replicated to cannot be changed.
## EXAMPLES
To set the replication of a secret named 'my-secret' to the contents of
my-file.json, run:
$ {command} my-secret --replication-policy-file=my-file.json
"""
SECRET_MISSING_MESSAGE = (
'Cannot set replication for secret [{secret}] because it does not exist. '
'Please use the create command to create a new secret.')
REPLICATION_POLICY_FILE_EMPTY_MESSAGE = ('File cannot be empty.')
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to update', positional=True, required=True)
secrets_args.AddReplicationPolicyFile(parser, required=True)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
replication_policy_contents = secrets_util.ReadFileOrStdin(
args.replication_policy_file, is_binary=False)
secret_ref = args.CONCEPTS.secret.Parse()
if not replication_policy_contents:
raise exceptions.InvalidArgumentException(
'replication-policy', self.REPLICATION_POLICY_FILE_EMPTY_MESSAGE)
policy, locations, kms_keys = secrets_util.ParseReplicationFileContents(
replication_policy_contents)
# Attempt to get the secret
secret = secrets_api.Secrets(api_version=api_version).GetOrNone(secret_ref)
# Secret does not exist
if secret is None:
raise exceptions.InvalidArgumentException(
'secret',
self.SECRET_MISSING_MESSAGE.format(secret=secret_ref.Name()))
secret = secrets_api.Secrets(api_version=api_version).SetReplication(
secret_ref, policy, locations, kms_keys
)
secrets_log.Secrets().UpdatedReplication(secret_ref)
return secret

View File

@@ -0,0 +1,191 @@
# -*- 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.
"""Update an existing secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import exceptions
from googlecloudsdk.command_lib.secrets import log as secrets_log
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class Update(base.UpdateCommand):
r"""Update a secret replica's metadata.
Update a secret replica's metadata (e.g. cmek policy). This command will
return an error if given a secret that does not exist or if given a
location that the given secret doesn't exist in.
The --remove-kms-key flag is only valid for Secrets that have an
automatic replication policy or exist in a single location. To remove
keys from a Secret with multiple user managed replicas, please use the
set-replication command.
## EXAMPLES
To remove CMEK from a secret called 'my-secret', run:
$ {command} my-secret --remove-cmek
To set the CMEK key on an automatic secret called my-secret to a specified
KMS key, run:
${command} my-secret
--set-kms-key=projects/my-project/locations/global/keyRings/my-keyring/cryptoKeys/my-key
To set the CMEK key on a secret called my-secret to a specified KMS key in
a specified location in its replication, run:
${command} my-secret
--set-kms-key=projects/my-project/locations/us-central1/keyRings/my-keyring/cryptoKeys/my-key
--location=us-central1
"""
NO_CHANGES_MESSAGE = (
'There are no changes to the secret [{secret}] for update.')
SECRET_MISSING_MESSAGE = (
'The secret [{secret}] cannot be updated because it does not exist. '
'Please use the create command to create a new secret.')
LOCATION_REQUIRED_MESSAGE = (
'This secret has a user managed replication polciy. The location in '
'which to set the customer managed encryption key must be set with '
'--location.')
MISCONFIGURED_REPLICATION_MESSAGE = (
'There was a problem updating replication for this secret. Please use '
'the replication set command to perform this update.')
LOCATION_AND_AUTOMATIC_MESSAGE = (
'This secret has an automatic replication policy. To set its customer '
'managed encryption key, please omit --locations.')
LOCATION_NOT_IN_POLICY_MESSAGE = (
'The secret does not have a replica in this location.')
PARTIALLY_CMEK_MESSAGE = (
'Either all replicas must use customer managed encryption or all '
'replicas must use Google managed encryption. To add customer managed '
'encryption to all replicas, please use the replication set command.')
REMOVE_AND_SET_CMEK_MESSAGE = (
'Cannot simultaneously set and remove a customer managed encryption key.')
REMOVE_CMEK_AND_LOCATION_MESSAGE = (
'Cannot remove customer managed encryption keys for just one location. '
'To use Google managed encryption keys for all locations, please remove '
'--locations.')
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to update', positional=True, required=True)
secrets_args.AddUpdateReplicationGroup(parser)
def _RemoveCmek(self, secret_ref, secret):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
if secret.replication.automatic:
updated_secret = secrets_api.Secrets(
api_version=api_version
).SetReplication(secret_ref, 'automatic', [], [])
secrets_log.Secrets().UpdatedReplication(secret_ref)
return updated_secret
if secret.replication.userManaged and secret.replication.userManaged.replicas:
locations = []
for replica in secret.replication.userManaged.replicas:
if not replica.location:
raise exceptions.MisconfiguredReplicationError(
self.MISCONFIGURED_REPLICATION_MESSAGE)
locations.append(replica.location)
updated_secret = secrets_api.Secrets(
api_version=api_version
).SetReplication(secret_ref, 'user-managed', locations, [])
secrets_log.Secrets().UpdatedReplication(secret_ref)
return updated_secret
raise exceptions.MisconfiguredReplicationError(
self.MISCONFIGURED_REPLICATION_MESSAGE)
def _SetKmsKey(self, secret_ref, secret, kms_key, location):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
if secret.replication.automatic:
if location:
raise calliope_exceptions.BadArgumentException(
'location', self.LOCATION_AND_AUTOMATIC_MESSAGE)
updated_secret = secrets_api.Secrets(
api_version=api_version
).SetReplication(secret_ref, 'automatic', [], [kms_key])
secrets_log.Secrets().UpdatedReplication(secret_ref)
return updated_secret
if secret.replication.userManaged and secret.replication.userManaged.replicas:
if not location:
raise calliope_exceptions.RequiredArgumentException(
'location', self.LOCATION_REQUIRED_MESSAGE)
locations = []
keys = []
found_location = False
for replica in secret.replication.userManaged.replicas:
if not replica.location:
raise exceptions.MisconfiguredReplicationError(
self.MISCONFIGURED_REPLICATION_MESSAGE)
locations.append(replica.location)
if location == replica.location:
found_location = True
keys.append(kms_key)
elif replica.customerManagedEncryption and replica.customerManagedEncryption.kmsKeyName:
keys.append(replica.customerManagedEncryption.kmsKeyName)
if not found_location:
raise calliope_exceptions.InvalidArgumentException(
'location', self.LOCATION_NOT_IN_POLICY_MESSAGE)
if len(locations) != len(keys):
raise exceptions.MisconfiguredEncryptionError(
self.PARTIALLY_CMEK_MESSAGE)
updated_secret = secrets_api.Secrets(
api_version=api_version
).SetReplication(secret_ref, 'user-managed', locations, keys)
secrets_log.Secrets().UpdatedReplication(secret_ref)
return updated_secret
raise exceptions.MisconfiguredReplicationError(
self.MISCONFIGURED_REPLICATION_MESSAGE)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
if not args.remove_cmek and not args.set_kms_key:
raise calliope_exceptions.MinimumArgumentException(
['--remove-cmek', '--set-kms-key'])
if args.remove_cmek and args.set_kms_key:
raise calliope_exceptions.ConflictingArgumentsException(
self.REMOVE_AND_SET_CMEK_MESSAGE)
if args.remove_cmek and args.location:
raise calliope_exceptions.ConflictingArgumentsException(
self.REMOVE_CMEK_AND_LOCATION_MESSAGE)
# args.set_kms_key without args.location is allowed only if the secret has
# a single replica.
# Attempt to get the secret
secret = secrets_api.Secrets(api_version=api_version).GetOrNone(secret_ref)
# Secret does not exist
if secret is None:
raise calliope_exceptions.InvalidArgumentException(
'secret',
self.SECRET_MISSING_MESSAGE.format(secret=secret_ref.Name()))
if args.remove_cmek:
return self._RemoveCmek(secret_ref, secret)
return self._SetKmsKey(secret_ref, secret, args.set_kms_key, args.location)

View File

@@ -0,0 +1,95 @@
# -*- 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.
"""Set the IAM policy for a secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.command_lib.secrets import args as secrets_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class SetIamPolicy(base.ListCommand):
"""Set the IAM policy binding for a secret.
Sets the IAM policy for the given secret as defined in a JSON or YAML file.
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
"""
detailed_help = {
'EXAMPLES': """\
The following command will read an IAM policy defined in a JSON file
'policy.json' and set it for the secret 'my-secret':
$ {command} my-secret policy.json
""",
}
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser,
purpose='',
positional=True,
required=True,
help_text='Name of the secret for which to set the IAM policy.',
)
secrets_args.AddLocation(parser, purpose='to set iam policy', hidden=False)
iam_util.AddArgForPolicyFile(parser)
base.URI_FLAG.RemoveFromParser(parser)
base.FILTER_FLAG.RemoveFromParser(parser)
base.LIMIT_FLAG.RemoveFromParser(parser)
base.PAGE_SIZE_FLAG.RemoveFromParser(parser)
base.SORT_BY_FLAG.RemoveFromParser(parser)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
messages = secrets_api.GetMessages(version=api_version)
policy, update_mask = iam_util.ParseYamlOrJsonPolicyFile(
args.policy_file, messages.Policy
)
result = secrets_api.Secrets(api_version=api_version).SetIamPolicy(
secret_ref, policy, update_mask, secret_location=args.location
)
iam_util.LogSetIamPolicy(secret_ref.Name(), 'secret')
return result
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class SetIamPolicyBeta(SetIamPolicy):
"""Set the IAM policy for a secret.
Sets the IAM policy for the given secret.
Returns an empty policy if the resource does not have a policy
set.
"""
detailed_help = {
'EXAMPLES': """\
To print the IAM policy for secret named 'my-secret', run:
$ {command} my-secret [--location=]
""",
}

View File

@@ -0,0 +1,568 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Update an existing secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
from googlecloudsdk.command_lib.secrets import util as secrets_util
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.command_lib.util.args import map_util
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.DefaultUniverseOnly
class Update(base.UpdateCommand):
r"""Update a secret's metadata.
Update a secret's metadata (e.g. labels). This command will
return an error if given a secret that does not exist.
## EXAMPLES
Update the label of a secret named `my-secret`.
$ {command} my-secret --update-labels=foo=bar
Update the label of a secret using an etag.
$ {command} my-secret --update-labels=foo=bar --etag=123
Update a secret to have a next-rotation-time:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
Update a secret to have a next-rotation-time and rotation-period:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
--rotation-period="7200s"
Update a secret to remove the next-rotation-time:
$ {command} my-secret --remove-next-rotation-time
Update a secret to clear rotation policy:
$ {command} my-secret --remove-rotation-schedule
Update version destroy ttl of a secret:
$ {command} my-secret --version-destroy-ttl="86400s"
Disable delayed secret version destroy:
$ {command} my-secret --remove-version-destroy-ttl
"""
NO_CHANGES_MESSAGE = (
'There are no changes to the secret [{secret}] for update.')
SECRET_MISSING_MESSAGE = (
'The secret [{secret}] cannot be updated because it does not exist. '
'Please use the create command to create a new secret.')
CONFIRM_EXPIRE_TIME_MESSAGE = (
'This secret and all of its versions will be automatically deleted at '
'the requested expire-time of [{expire_time}].')
CONFIRM_TTL_MESSAGE = (
'This secret and all of its versions will be automatically deleted '
'after the requested ttl of [{ttl}] has elapsed.')
REGIONAL_KMS_FLAG_MESSAGE = (
'The --regional-kms-key-name or --remove-regional-kms-key-name flag can'
' only be used when update a regional secret with "--location".'
)
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddSecret(
parser, purpose='to update', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to update secret', hidden=False)
alias = parser.add_group(mutex=True, help='Version Aliases')
annotations = parser.add_group(mutex=True, help='Annotations')
labels_util.AddUpdateLabelsFlags(parser)
secrets_args.AddSecretEtag(parser, action='updated')
secrets_args.AddUpdateExpirationGroup(parser)
secrets_args.AddUpdateTopicsGroup(parser)
secrets_args.AddUpdateRotationGroup(parser)
secrets_args.AddUpdateVersionDestroyTTL(parser)
secrets_args.AddUpdateRegionalKmsKey(parser)
map_util.AddMapUpdateFlag(alias, 'version-aliases', 'Version Aliases', str,
int)
map_util.AddMapRemoveFlag(alias, 'version-aliases', 'Version Aliases', str)
map_util.AddMapClearFlag(alias, 'version-aliases', 'Version Aliases')
map_util.AddMapUpdateFlag(annotations, 'annotations', 'Annotations', str,
str)
map_util.AddMapRemoveFlag(annotations, 'annotations', 'Annotations', str)
map_util.AddMapClearFlag(annotations, 'annotations', 'Annotations')
def _RunUpdate(self, original, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
messages = secrets_api.GetMessages(version=api_version)
secret_ref = args.CONCEPTS.secret.Parse()
# Collect the list of update masks
update_mask = []
labels_diff = labels_util.Diff.FromUpdateArgs(args)
if labels_diff.MayHaveUpdates():
update_mask.append('labels')
if args.IsSpecified('ttl'):
update_mask.append('ttl')
if args.IsSpecified('expire_time') or args.IsSpecified('remove_expiration'):
update_mask.append('expire_time')
if ((args.IsSpecified('next_rotation_time') or
args.IsSpecified('remove_next_rotation_time')) or
args.IsSpecified('remove_rotation_schedule')):
update_mask.append('rotation.next_rotation_time')
if ((args.IsSpecified('rotation_period') or
args.IsSpecified('remove_rotation_period')) or
args.IsSpecified('remove_rotation_schedule')):
update_mask.append('rotation.rotation_period')
if args.IsSpecified('add_topics') or args.IsSpecified(
'remove_topics') or args.IsSpecified('clear_topics'):
update_mask.append('topics')
if args.IsSpecified('update_version_aliases') or args.IsSpecified(
'remove_version_aliases') or args.IsSpecified('clear_version_aliases'):
update_mask.append('version_aliases')
if args.IsSpecified('update_annotations') or args.IsSpecified(
'remove_annotations') or args.IsSpecified('clear_annotations'):
update_mask.append('annotations')
if args.IsSpecified('version_destroy_ttl') or args.IsSpecified(
'remove_version_destroy_ttl'
):
update_mask.append('version_destroy_ttl')
if args.IsSpecified('regional_kms_key_name') or args.IsSpecified(
'remove_regional_kms_key_name'
):
update_mask.append('customer_managed_encryption')
# Validations
if not update_mask:
raise exceptions.MinimumArgumentException(
[
'--clear-labels',
'--remove-labels',
'--update-labels',
'--ttl',
'--expire-time',
'--remove-expiration',
'--clear-topics',
'--remove-topics',
'--add-topics',
'--update-version-aliases',
'--remove-version-aliases',
'--clear-version-aliases',
'--update-annotations',
'--remove-annotations',
'--clear-annotations',
'--next-rotation-time',
'--remove-next-rotation-time',
'--rotation-period',
'--remove-rotation-period',
'--remove-rotation-schedule',
'--version-destroy-ttl',
'--remove-version-destroy-ttl',
'--remove_regional_kms_key_name',
'--regional-kms-key-name',
],
self.NO_CHANGES_MESSAGE.format(secret=secret_ref.Name()),
)
labels_update = labels_diff.Apply(messages.Secret.LabelsValue,
original.labels)
labels = original.labels
if labels_update.needs_update:
labels = labels_update.labels
if args.expire_time:
msg = self.CONFIRM_EXPIRE_TIME_MESSAGE.format(
expire_time=args.expire_time)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True)
if args.ttl:
msg = self.CONFIRM_TTL_MESSAGE.format(ttl=args.ttl)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True)
if 'topics' in update_mask:
topics = secrets_util.ApplyTopicsUpdate(args, original.topics)
else:
topics = []
version_aliases = []
if 'version_aliases' in update_mask:
version_aliases = []
if original.versionAliases is None:
original.versionAliases = messages.Secret.VersionAliasesValue(
additionalProperties=[])
version_aliases_dict = secrets_util.ApplyAliasUpdate(
args, original.versionAliases.additionalProperties)
for alias, version in version_aliases_dict.items():
version_aliases.append(
messages.Secret.VersionAliasesValue.AdditionalProperty(
key=alias, value=version))
annotations = []
if 'annotations' in update_mask:
if original.annotations is None:
original.annotations = messages.Secret.AnnotationsValue(
additionalProperties=[])
annotations_dict = secrets_util.ApplyAnnotationsUpdate(
args, original.annotations.additionalProperties)
for annotation, metadata in annotations_dict.items():
annotations.append(
messages.Secret.AnnotationsValue.AdditionalProperty(
key=annotation, value=metadata))
if args.version_destroy_ttl:
version_destroy_ttl = f'{args.version_destroy_ttl}s'
else:
version_destroy_ttl = None
if not args.location and (
args.regional_kms_key_name or args.remove_regional_kms_key_name
):
raise exceptions.RequiredArgumentException(
'location', self.REGIONAL_KMS_FLAG_MESSAGE
)
secret = secrets_api.Secrets(api_version=api_version).Update(
secret_ref=secret_ref,
labels=labels,
version_aliases=version_aliases,
annotations=annotations,
update_mask=update_mask,
etag=args.etag,
expire_time=args.expire_time,
ttl=args.ttl,
topics=topics,
next_rotation_time=args.next_rotation_time,
rotation_period=args.rotation_period,
version_destroy_ttl=version_destroy_ttl,
regional_kms_key_name=args.regional_kms_key_name,
secret_location=args.location,
)
secrets_log.Secrets().Updated(secret_ref)
return secret
def Run(self, args):
"""Run is called by calliope to update the secret.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
The API call to service for secret update.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
secret = secrets_api.Secrets(api_version=api_version).GetOrNone(
secret_ref, secret_location=args.location
)
# Secret does not exist
if secret is None:
raise exceptions.InvalidArgumentException(
'secret', self.SECRET_MISSING_MESSAGE.format(secret=secret_ref.Name())
)
# The secret exists, update it
return self._RunUpdate(secret, args)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@base.DefaultUniverseOnly
class UpdateBeta(Update):
r"""Update a secret's metadata.
Update a secret's metadata (e.g. labels). This command will
return an error if given a secret that does not exist.
## EXAMPLES
Update the label of a secret named `my-secret`.
$ {command} my-secret --update-labels=foo=bar
Update the label of a secret using etag.
$ {command} my-secret --update-labels=foo=bar --etag=123
Update the expiration of a secret named 'my-secret' using a ttl.
$ {command} my-secret --ttl="600s"
Update the expiration of a secret named 'my-secret' using an expire-time.
$ {command} my-secret --expire-time="2030-01-01T08:15:30-05:00"
Remove the expiration of a secret named 'my-secret'.
$ {command} my-secret --remove-expiration
Update a secret to have a next-rotation-time:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
Update a secret to have a next-rotation-time and rotation-period:
$ {command} my-secret --next-rotation-time="2030-01-01T15:30:00-05:00"
--rotation-period="7200s"
Update a secret to remove the next-rotation-time:
$ {command} my-secret --remove-next-rotation-time
Update a secret to clear rotation policy:
$ {command} my-secret --remove-rotation-schedule
Update version destroy ttl of a secret:
$ {command} my-secret --version-destroy-ttl="86400s"
Disable delayed secret version destroy:
$ {command} my-secret --remove-version-destroy-ttl
"""
NO_CHANGES_MESSAGE = (
'There are no changes to the secret [{secret}] for update'
)
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to update', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to update secret', hidden=False)
alias = parser.add_group(mutex=True, help='Version Aliases')
annotations = parser.add_group(mutex=True, help='Annotations')
labels_util.AddUpdateLabelsFlags(parser)
secrets_args.AddSecretEtag(parser, action='updated')
secrets_args.AddUpdateExpirationGroup(parser)
secrets_args.AddUpdateRotationGroup(parser)
secrets_args.AddUpdateTopicsGroup(parser)
secrets_args.AddUpdateVersionDestroyTTL(parser)
secrets_args.AddUpdateRegionalKmsKey(parser)
map_util.AddMapUpdateFlag(alias, 'version-aliases', 'Version Aliases', str,
int)
map_util.AddMapRemoveFlag(alias, 'version-aliases', 'Version Aliases', str)
map_util.AddMapClearFlag(alias, 'version-aliases', 'Version Aliases')
map_util.AddMapUpdateFlag(annotations, 'annotations', 'Annotations', str,
str)
map_util.AddMapRemoveFlag(annotations, 'annotations', 'Annotations', str)
map_util.AddMapClearFlag(annotations, 'annotations', 'Annotations')
def _RunUpdate(self, original, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
messages = secrets_api.GetMessages(version=api_version)
secret_ref = args.CONCEPTS.secret.Parse()
# Collect the list of update masks
update_mask = []
labels_diff = labels_util.Diff.FromUpdateArgs(args)
if labels_diff.MayHaveUpdates():
update_mask.append('labels')
if args.IsSpecified('ttl'):
update_mask.append('ttl')
if args.IsSpecified('expire_time') or args.IsSpecified('remove_expiration'):
update_mask.append('expire_time')
if args.IsSpecified('add_topics') or args.IsSpecified(
'remove_topics') or args.IsSpecified('clear_topics'):
update_mask.append('topics')
if ((args.IsSpecified('next_rotation_time') or
args.IsSpecified('remove_next_rotation_time')) or
args.IsSpecified('remove_rotation_schedule')):
update_mask.append('rotation.next_rotation_time')
if ((args.IsSpecified('rotation_period') or
args.IsSpecified('remove_rotation_period')) or
args.IsSpecified('remove_rotation_schedule')):
update_mask.append('rotation.rotation_period')
if args.IsSpecified('update_version_aliases') or args.IsSpecified(
'remove_version_aliases') or args.IsSpecified('clear_version_aliases'):
update_mask.append('version_aliases')
if args.IsSpecified('update_annotations') or args.IsSpecified(
'remove_annotations') or args.IsSpecified('clear_annotations'):
update_mask.append('annotations')
if args.IsSpecified('version_destroy_ttl') or args.IsSpecified(
'remove_version_destroy_ttl'
):
update_mask.append('version_destroy_ttl')
if args.IsSpecified('regional_kms_key_name') or args.IsSpecified(
'remove_regional_kms_key_name'
):
update_mask.append('customer_managed_encryption')
# Validations
if not update_mask:
raise exceptions.MinimumArgumentException(
[
'--clear-labels',
'--remove-labels',
'--update-labels',
'--ttl',
'--expire-time',
'--remove-expiration',
'--clear-topics',
'--remove-topics',
'--add-topics',
'--update-version-aliases',
'--remove-version-aliases',
'--clear-version-aliases',
'--update-annotations',
'--remove-annotations',
'--clear-annotations',
'--next-rotation-time',
'--remove-next-rotation-time',
'--rotation-period',
'--remove-rotation-period',
'--remove-rotation-schedule',
'--version-destroy-ttl',
'--remove-version-destroy-ttl',
'--remove_regional_kms_key_name',
'--regional-kms-key-name',
],
self.NO_CHANGES_MESSAGE.format(secret=secret_ref.Name()),
)
labels_update = labels_diff.Apply(messages.Secret.LabelsValue,
original.labels)
labels = original.labels
if labels_update.needs_update:
labels = labels_update.labels
if 'topics' in update_mask:
topics = secrets_util.ApplyTopicsUpdate(args, original.topics)
else:
topics = []
version_aliases = []
if 'version_aliases' in update_mask:
if original.versionAliases is None:
original.versionAliases = messages.Secret.VersionAliasesValue(
additionalProperties=[]
)
version_aliases_dict = secrets_util.ApplyAliasUpdate(
args, original.versionAliases.additionalProperties
)
for alias, version in version_aliases_dict.items():
version_aliases.append(
messages.Secret.VersionAliasesValue.AdditionalProperty(
key=alias, value=version
)
)
annotations = []
if 'annotations' in update_mask:
annotations = []
if original.annotations is None:
original.annotations = messages.Secret.AnnotationsValue(
additionalProperties=[]
)
annotations_dict = secrets_util.ApplyAnnotationsUpdate(
args, original.annotations.additionalProperties
)
for annotation, metadata in annotations_dict.items():
annotations.append(
messages.Secret.AnnotationsValue.AdditionalProperty(
key=annotation, value=metadata
)
)
if args.expire_time:
msg = self.CONFIRM_EXPIRE_TIME_MESSAGE.format(
expire_time=args.expire_time
)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True
)
if args.ttl:
msg = self.CONFIRM_TTL_MESSAGE.format(ttl=args.ttl)
console_io.PromptContinue(
msg, throw_if_unattended=True, cancel_on_no=True
)
if args.version_destroy_ttl:
version_destroy_ttl = f'{args.version_destroy_ttl}s'
else:
version_destroy_ttl = None
if not args.location and (
args.regional_kms_key_name or args.remove_regional_kms_key_name
):
raise exceptions.RequiredArgumentException(
'location', self.REGIONAL_KMS_FLAG_MESSAGE
)
secret = secrets_api.Secrets(api_version=api_version).Update(
secret_ref=secret_ref,
labels=labels,
update_mask=update_mask,
version_aliases=version_aliases,
annotations=annotations,
etag=args.etag,
expire_time=args.expire_time,
ttl=args.ttl,
topics=topics,
next_rotation_time=args.next_rotation_time,
rotation_period=args.rotation_period,
version_destroy_ttl=version_destroy_ttl,
regional_kms_key_name=args.regional_kms_key_name,
secret_location=args.location,
)
secrets_log.Secrets().Updated(secret_ref)
return secret
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
secret = secrets_api.Secrets(api_version=api_version).GetOrNone(
secret_ref, secret_location=args.location
)
# Secret does not exist
if secret is None:
raise exceptions.InvalidArgumentException(
'secret', self.SECRET_MISSING_MESSAGE.format(secret=secret_ref.Name())
)
# The secret exists, update it
return self._RunUpdate(secret, args)

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Manage secret versions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class CloudSecrets(base.Group):
"""Manage secret versions.
Manage secret versions on Google Cloud.
"""
def Filter(self, context, args):
del context, args

View File

@@ -0,0 +1,171 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Access a secret version's data."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.api_lib.util import exceptions
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import fmt as secrets_fmt
from googlecloudsdk.command_lib.secrets import util as secrets_util
from googlecloudsdk.command_lib.util import crc32c
CHECKSUM_VERIFICATION_FAILURE_MESSAGE = (
'An incorrect data_crc32c was calculated for the provided payload. This '
'might be a transient issue that resolves with a retry. If this is '
'happening repeatedly open an issue with Secret Manager at '
'https://issuetracker.google.com/issues/new?component=784854&template=1380926.'
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Access(base.DescribeCommand):
# pylint: disable=line-too-long
r"""Access a secret version's data.
Access the data for the specified secret version.
## EXAMPLES
Access the data for version 123 of the secret 'my-secret':
$ {command} 123 --secret=my-secret
Note: The output will be formatted as UTF-8 which can corrupt binary secrets.
To write raw bytes to a file use --out-file flag:
$ {command} 123 --secret=my-secret --out-file=/tmp/secret
To get the raw bytes, have Google Cloud CLI print the response as
base64-encoded and decode:
$ {command} 123 --secret=my-secret --format='get(payload.data)' | tr '_-' '/+' | base64 -d
"""
EMPTY_OUT_FILE_MESSAGE = (
'The value provided for --out-file is the empty string. This can happen '
'if you pass or pipe a variable that is undefined. Please verify that '
'the --out-file flag is not the empty string.')
# pylint: enable=line-too-long
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for secrets versions access command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddVersionOrAlias(
parser, purpose='to access', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to access secret', hidden=False)
secrets_args.AddOutFile(parser)
secrets_fmt.UseSecretData(parser)
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Versions:
"""Run is called by calliope to implement the secret versions access command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke secret version access.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
version = secrets_api.Versions(api_version=api_version).Access(
version_ref, secret_location=args.location
)
if version.payload.dataCrc32c is None or crc32c.does_data_match_checksum(
version.payload.data, version.payload.dataCrc32c
):
if args.IsSpecified('out_file'):
if not args.out_file:
raise calliope_exceptions.BadFileException(
self.EMPTY_OUT_FILE_MESSAGE)
# if flag --out-file is provided use the 'disable'
# format from go/gcloud-ref/topic/formats
args.format = 'disable'
secrets_util.WriteBinaryFile(args.out_file, version.payload.data)
return version
raise exceptions.HttpException(CHECKSUM_VERIFICATION_FAILURE_MESSAGE)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class AccessBeta(Access):
# pylint: disable=line-too-long
r"""Access a secret version's data.
Access the data for the specified secret version.
## EXAMPLES
Access the data for version 123 of the secret 'my-secret':
$ {command} 123 --secret=my-secret
Note: The output will be formatted as UTF-8 which can corrupt binary secrets.
To write raw bytes to a file use --out-file flag:
$ {command} 123 --secret=my-secret --out-file=/tmp/secret
To get the raw bytes, have Google Cloud CLI print the response as
base64-encoded and decode:
$ {command} 123 --secret=my-secret --format='get(payload.data)' | tr '_-' '/+' | base64 -d
"""
# pylint: enable=line-too-long
@staticmethod
def Args(parser):
secrets_args.AddVersionOrAlias(
parser, purpose='to access', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to access secret', hidden=False)
secrets_args.AddOutFile(parser)
secrets_fmt.UseSecretData(parser)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
version = secrets_api.Versions(api_version=api_version).Access(
version_ref, secret_location=args.location
)
if version.payload.dataCrc32c is None or crc32c.does_data_match_checksum(
version.payload.data, version.payload.dataCrc32c
):
if args.IsSpecified('out_file'):
if not args.out_file:
raise calliope_exceptions.BadFileException(
self.EMPTY_OUT_FILE_MESSAGE)
# if flag --out-file is provided use the 'disable'
# format from go/gcloud-ref/topic/formats
args.format = 'disable'
secrets_util.WriteBinaryFile(args.out_file, version.payload.data)
return version
raise exceptions.HttpException(CHECKSUM_VERIFICATION_FAILURE_MESSAGE)

View File

@@ -0,0 +1,186 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Enable the version of the provided secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
from googlecloudsdk.command_lib.secrets import util as secrets_util
from googlecloudsdk.command_lib.util import crc32c
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Create(base.CreateCommand):
r"""Create a new version of an existing secret.
Create a new version of an existing secret with the provided data. The
command will return an error if no such secret exists.
## EXAMPLES
Create a new version of an existing secret named 'my-secret' with secret data
"s3cr3t":
$ printf "s3cr3t" | {command} my-secret --data-file=-
Create a new version of an existing secret named 'my-secret' with secret data
"s3cr3t" using PowerShell (Note: PowerShell will add a newline to the
resulting secret):
$ Write-Output "s3cr3t" | {command} my-secret --data-file=-
Create a new version of an existing secret named 'my-secret' with secret data
from a file:
$ {command} my-secret --data-file=/tmp/secret
"""
EMPTY_DATA_FILE_MESSAGE = (
'The value provided for --data-file is the empty string. This can happen '
'if you pass or pipe a variable that is undefined. Please verify that '
'the --data-file flag is not the empty string.')
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for secrets versions add command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddSecret(
parser, purpose='to create', positional=True, required=True
)
secrets_args.AddLocation(
parser, purpose='to create secret version', hidden=False
)
secrets_args.AddDataFile(parser, required=True)
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Versions:
"""Run is called by calliope to implement the secret versions add command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke secret version add.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
is_regional = args.location is not None
data = secrets_util.ReadFileOrStdin(args.data_file)
# Differentiate between the flag being provided with an empty value and the
# flag being omitted. See b/138796299 for info.
if args.data_file == '': # pylint: disable=g-explicit-bool-comparison
raise exceptions.BadFileException(self.EMPTY_DATA_FILE_MESSAGE)
data_crc32c = crc32c.get_crc32c(data)
version = secrets_api.Secrets(api_version=api_version).AddVersion(
secret_ref,
data,
crc32c.get_checksum(data_crc32c),
secret_location=args.location,
)
if is_regional:
version_ref = secrets_args.ParseRegionalVersionRef(version.name)
else:
version_ref = secrets_args.ParseVersionRef(version.name)
secrets_log.Versions().Created(version_ref)
if not version.clientSpecifiedPayloadChecksum:
raise exceptions.HttpException(
'Version created but payload data corruption may have occurred, '
'please destroy the created version, and retry. See also '
'https://cloud.google.com/secret-manager/docs/data-integrity.'
)
return version
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class CreateBeta(Create):
r"""Create a new version of an existing secret.
Create a new version of an existing secret with the provided data. The
command will return an error if no such secret exists.
## EXAMPLES
Create a new version of an existing secret named 'my-secret' with secret data
"s3cr3t":
$ printf "s3cr3t" | {command} my-secret --data-file=-
Create a new version of an existing secret named 'my-secret' with secret data
"s3cr3t" using PowerShell (Note: PowerShell will add a newline to the
resulting secret):
$ Write-Output "s3cr3t" | {command} my-secret --data-file=-
Create a new version of an existing secret named 'my-secret' with secret data
from a file:
$ {command} my-secret --data-file=/tmp/secret
"""
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser, purpose='to create', positional=True, required=True
)
secrets_args.AddLocation(
parser, purpose='to create secret version', hidden=False
)
secrets_args.AddDataFile(parser, required=True)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
is_regional = args.location is not None
data = secrets_util.ReadFileOrStdin(args.data_file)
# Differentiate between the flag being provided with an empty value and the
# flag being omitted. See b/138796299 for info.
if args.data_file == '': # pylint: disable=g-explicit-bool-comparison
raise exceptions.BadFileException(self.EMPTY_DATA_FILE_MESSAGE)
data_crc32c = crc32c.get_crc32c(data)
version = secrets_api.Secrets(api_version=api_version).AddVersion(
secret_ref,
data,
crc32c.get_checksum(data_crc32c),
secret_location=args.location,
)
if is_regional:
version_ref = secrets_args.ParseRegionalVersionRef(version.name)
else:
version_ref = secrets_args.ParseVersionRef(version.name)
secrets_log.Versions().Created(version_ref)
if not version.clientSpecifiedPayloadChecksum:
raise exceptions.HttpException(
'Version created but payload data corruption may have occurred, '
'please destroy the created version, and retry. See also '
'https://cloud.google.com/secret-manager/docs/data-integrity.')
return version

View File

@@ -0,0 +1,26 @@
# -*- 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.
"""Command group for managing Secretmanager version configurations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Config(base.Group):
"""Manage Secretmanager version configurations."""

View File

@@ -0,0 +1,38 @@
release_tracks: [ALPHA]
command_type: CONFIG_EXPORT
help_text:
brief: Export the configuration for a Secretmanager version.
description: |
*{command}* exports the configuration for a Secretmanager version.
Version configurations can be exported in
Kubernetes Resource Model (krm) or Terraform HCL formats. The
default format is `krm`.
Specifying `--all` allows you to export the configurations for all
versions within the project.
Specifying `--path` allows you to export the configuration(s) to
a local directory.
examples: |
To export the configuration for a version, run:
$ {command} my-version
To export the configuration for a version to a file, run:
$ {command} my-version --path=/path/to/dir/
To export the configuration for a version in Terraform
HCL format, run:
$ {command} my-version --resource-format=terraform
To export the configurations for all versions within a
project, run:
$ {command} --all
arguments:
resource:
help_text: Version to export the configuration for.
spec: !REF googlecloudsdk.command_lib.secrets.resources:version

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Describe metadata about the secret version."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Describe(base.DescribeCommand):
r"""Describe metadata about the secret version.
Describe a secret version's metadata. This command does not include the
secret version's secret data.
## EXAMPLES
Describe version '123' of the secret named 'my-secret':
$ {command} 123 --secret=my-secret
"""
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for secrets versions describe command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddVersionOrAlias(
parser, purpose='to describe', positional=True, required=True)
secrets_args.AddLocation(parser, purpose='to describe secret', hidden=False)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
return secrets_api.Versions(api_version=api_version).Get(
version_ref, secret_location=args.location
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class DescribeBeta(base.DescribeCommand):
r"""Describe metadata about the secret version.
Describe a secret version's metadata. This command does not include the
secret version's secret data.
## EXAMPLES
Describe version '123' of the secret named 'my-secret':
$ {command} 123 --secret=my-secret
"""
@staticmethod
def Args(parser):
secrets_args.AddVersionOrAlias(
parser, purpose='to describe', positional=True, required=True)
secrets_args.AddLocation(parser, purpose='to describe secret', hidden=False)
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Versions:
"""Run is called by calliope to implement the secret versions describe command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke secret version describe.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
return secrets_api.Versions(api_version=api_version).Get(
version_ref, secret_location=args.location
)

View File

@@ -0,0 +1,146 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Destroy a secret version's metadata and secret data."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
from googlecloudsdk.core.console import console_io
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Destroy(base.DeleteCommand):
r"""Destroy a secret version's metadata and secret data.
Destroy a secret version's metadata and secret data. This action is
irreversible.
## EXAMPLES
Destroy version `123` of the secret named `my-secret`:
$ {command} 123 --secret=my-secret
Destroy version `123` of the secret named `my-secret` using etag:
$ {command} 123 --secret=my-secret --etag=123
"""
CONFIRM_DESTROY_MESSAGE = (
'You are about to destroy version [{version}] of the secret [{secret}]. '
'This action cannot be reversed.')
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for secrets versions destroy command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddVersion(
parser, purpose='to destroy', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to destroy ', hidden=False)
secrets_args.AddVersionEtag(parser, action='destroyed')
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Versions:
"""Run is called by calliope to implement the secret versions destroy command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke secret version destroy.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
# Destructive action, prompt to continue
console_io.PromptContinue(
self.CONFIRM_DESTROY_MESSAGE.format(
version=version_ref.Name(), secret=version_ref.Parent().Name()
),
throw_if_unattended=True,
cancel_on_no=True,
)
result = secrets_api.Versions(api_version=api_version).Destroy(
version_ref, etag=args.etag, secret_location=args.location
)
if result.scheduledDestroyTime is None:
secrets_log.Versions().Destroyed(version_ref)
else:
secrets_log.Versions().ScheduledDestroy(
result.scheduledDestroyTime, version_ref
)
return result
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class DestroyBeta(Destroy):
r"""Destroy a secret version's metadata and secret data.
Destroy a secret version's metadata and secret data. This action is
irreversible.
## EXAMPLES
Destroy version `123` of the secret named `my-secret`:
$ {command} 123 --secret=my-secret
Destroy version `123` of the secret named `my-secret` using an etag:
$ {command} 123 --secret=my-secret --etag=123
"""
@staticmethod
def Args(parser):
secrets_args.AddVersion(
parser, purpose='to destroy', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to destroy ', hidden=False)
secrets_args.AddVersionEtag(parser, action='destroyed')
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
# Destructive action, prompt to continue
console_io.PromptContinue(
self.CONFIRM_DESTROY_MESSAGE.format(
version=version_ref.Name(), secret=version_ref.Parent().Name()
),
throw_if_unattended=True,
cancel_on_no=True,
)
result = secrets_api.Versions(api_version=api_version).Destroy(
version_ref, etag=args.etag, secret_location=args.location
)
if result.scheduledDestroyTime is None:
secrets_log.Versions().Destroyed(version_ref)
else:
secrets_log.Versions().ScheduledDestroy(
result.scheduledDestroyTime, version_ref
)
return result

View File

@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Disable the version of the provided secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Disable(base.DeleteCommand):
r"""Disable the version of the provided secret.
Disable the version of the provided secret. It can be re-enabled with
`{parent_command} enable`.
## EXAMPLES
Disable version `123` of the secret named `my-secret`:
$ {command} 123 --secret=my-secret
Disable version `123` of the secret named `my-secret` using etag:
$ {command} 123 --secret=my-secret --etag=123
"""
@staticmethod
def Args(parser):
secrets_args.AddVersion(
parser, purpose='to disable', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to disable', hidden=False)
secrets_args.AddVersionEtag(parser, action='disabled')
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
result = secrets_api.Versions(api_version=api_version).Disable(
version_ref, etag=args.etag, secret_location=args.location
)
secrets_log.Versions().Disabled(version_ref)
return result
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class DisableBeta(Disable):
r"""Disable the version of the provided secret.
Disable the version of the provided secret. It can be re-enabled with
`{parent_command} enable`.
## EXAMPLES
Disable version `123` of the secret named `my-secret`:
$ {command} 123 --secret=my-secret
Disable version `123` of the secret named `my-secret` using an etag:
$ {command} 123 --secret=my-secret --etag=123
"""
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for secrets versions disable command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddVersion(
parser, purpose='to disable', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to disable', hidden=False)
secrets_args.AddVersionEtag(parser, action='disabled')
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Versions:
"""Run is called by calliope to implement the secret versions disable command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke secret version disable.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
result = secrets_api.Versions(api_version=api_version).Disable(
version_ref, etag=args.etag, secret_location=args.location
)
secrets_log.Versions().Disabled(version_ref)
return result

View File

@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Enable the version of the provided secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import log as secrets_log
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Create(base.CreateCommand):
r"""Enable the version of the provided secret.
Enable the version of the provided secret. It can be disabled with
`{parent_command} disable`.
## EXAMPLES
Enable version `123` of the secret named `my-secret`:
$ {command} 123 --secret=my-secret
Enable version `123` of the secret named `my-secret` using etag:
$ {command} 123 --secret=my-secret --etag=123
"""
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for secrets versions enable command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddVersion(
parser, purpose='to enable', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to enable', hidden=False)
secrets_args.AddVersionEtag(parser, action='enabled')
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Versions:
"""Run is called by calliope to implement the secret versions enable command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke secret version enable.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
result = secrets_api.Versions(api_version=api_version).Enable(
version_ref, etag=args.etag, secret_location=args.location
)
secrets_log.Versions().Enabled(version_ref)
return result
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class CreateBeta(Create):
r"""Enable the version of the provided secret.
Enable the version of the provided secret. It can be disabled with
`{parent_command} disable`.
## EXAMPLES
Enable version `123` of the secret named `my-secret`:
$ {command} 123 --secret=my-secret
Enable version `123` of the secret named `my-secret` using an etag:
$ {command} 123 --secret=my-secret --etag=123
"""
@staticmethod
def Args(parser):
secrets_args.AddVersion(
parser, purpose='to enable', positional=True, required=True
)
secrets_args.AddLocation(parser, purpose='to enable', hidden=False)
secrets_args.AddVersionEtag(parser, action='enabled')
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
version_ref = args.CONCEPTS.version.Parse()
result = secrets_api.Versions(api_version=api_version).Enable(
version_ref, etag=args.etag, secret_location=args.location
)
secrets_log.Versions().Enabled(version_ref)
return result

View File

@@ -0,0 +1,147 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""List all versions for a secret."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.secrets import api as secrets_api
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_arguments
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.secrets import args as secrets_args
from googlecloudsdk.command_lib.secrets import fmt as secrets_fmt
from googlecloudsdk.core.resource import resource_expr_rewrite
from googlecloudsdk.core.resource import resource_projection_spec
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class List(base.ListCommand):
r"""List all versions for a secret.
List all versions and their status (For example: active/disabled/destroyed)
for a secret.
## EXAMPLES
List all versions for the secret named 'my-secret':
$ {command} my-secret
"""
@staticmethod
def Args(parser: parser_arguments.ArgumentInterceptor):
"""Args is called by calliope to gather arguments for secrets versions list command.
Args:
parser: An argparse parser that you can use to add arguments that will be
available to this command.
"""
secrets_args.AddSecret(
parser,
purpose='from which to list versions',
positional=True,
required=True,
)
secrets_args.AddLocation(parser, purpose='to create secret', hidden=False)
base.PAGE_SIZE_FLAG.SetDefault(parser, 100)
def Run(self, args: parser_extensions.Namespace) -> secrets_api.Versions:
"""Run is called by calliope to implement the secret versions list command.
Args:
args: an argparse namespace, all the arguments that were provided to this
command invocation.
Returns:
API call to invoke secret version list.
"""
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
if args.location:
secrets_fmt.RegionalSecretVersionTableUsingArgument(
args, api_version=api_version
)
else:
secrets_fmt.SecretVersionTableUsingArgument(args, api_version=api_version)
server_filter = None
if args.filter:
rewriter = resource_expr_rewrite.Backend()
display_info = args.GetDisplayInfo()
defaults = resource_projection_spec.ProjectionSpec(
symbols=display_info.transforms, aliases=display_info.aliases
)
_, server_filter = rewriter.Rewrite(args.filter, defaults=defaults)
return secrets_api.Versions(api_version=api_version).ListWithPager(
secret_ref=secret_ref,
limit=args.limit,
request_filter=server_filter,
secret_location=args.location,
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class ListBeta(List):
r"""List all versions for a secret.
List all versions and their status (For example: active/disabled/destroyed)
for a secret.
## EXAMPLES
List all versions for the secret named 'my-secret':
$ {command} my-secret
"""
@staticmethod
def Args(parser):
secrets_args.AddSecret(
parser,
purpose='from which to list versions',
positional=True,
required=True,
)
secrets_args.AddLocation(parser, purpose='to create secret', hidden=False)
base.PAGE_SIZE_FLAG.SetDefault(parser, 100)
def Run(self, args):
api_version = secrets_api.GetApiFromTrack(self.ReleaseTrack())
secret_ref = args.CONCEPTS.secret.Parse()
if args.location:
secrets_fmt.RegionalSecretVersionTableUsingArgument(
args, api_version=api_version
)
else:
secrets_fmt.SecretVersionTableUsingArgument(args, api_version=api_version)
server_filter = None
if args.filter:
rewriter = resource_expr_rewrite.Backend()
display_info = args.GetDisplayInfo()
defaults = resource_projection_spec.ProjectionSpec(
symbols=display_info.transforms, aliases=display_info.aliases
)
_, server_filter = rewriter.Rewrite(args.filter, defaults=defaults)
return secrets_api.Versions(api_version=api_version).ListWithPager(
secret_ref=secret_ref,
limit=args.limit,
request_filter=server_filter,
secret_location=args.location,
)