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,61 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""Deployment Manager deployments sub-group."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Deployments(base.Group):
"""Commands for Deployment Manager deployments.
Commands to create, update, delete, and examine deployments of resources.
"""
detailed_help = {
'EXAMPLES': """\
To create a deployment, run:
$ {command} create my-deployment --config config.yaml
To update a deployment, run:
$ {command} update my-deployment --config new_config.yaml
To stop a deployment create or update in progress, run:
$ {command} stop my-deployment
To cancel a previewed create or update, run:
$ {command} cancel-preview my-deployment
To delete a deployment, run:
$ {command} delete my-deployment
To view the details of a deployment, run:
$ {command} describe my-deployment
To see the list of all deployments, run:
$ {command} list
""",
}

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""deployments cancel command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.deployment_manager import dm_api_util
from googlecloudsdk.api_lib.deployment_manager import dm_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.deployment_manager import dm_util
from googlecloudsdk.command_lib.deployment_manager import dm_write
from googlecloudsdk.command_lib.deployment_manager import flags
# Number of seconds (approximately) to wait for cancel operation to complete.
OPERATION_TIMEOUT = 20 * 60 # 20 mins
@dm_base.UseDmApi(dm_base.DmApiVersion.V2)
class CancelPreview(base.Command, dm_base.DmCommand):
"""Cancel a pending or running deployment preview.
This command will cancel a currently running or pending preview operation on
a deployment.
"""
detailed_help = {
'EXAMPLES': """
To cancel a running operation on a deployment, run:
$ {command} my-deployment
To issue a cancel preview command without waiting for the operation to complete, run:
$ {command} my-deployment --async
To cancel a preview command providing a fingerprint:
$ {command} my-deployment --fingerprint=deployment-fingerprint
When a deployment preview is cancelled, the deployment itself is not
deleted.
""",
}
@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 go
on the command line after this command. Positional arguments are
allowed.
"""
flags.AddDeploymentNameFlag(parser)
flags.AddAsyncFlag(parser)
flags.AddFingerprintFlag(parser)
def Run(self, args):
"""Run 'deployments cancel-preview'.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
If --async=true, returns Operation to poll.
Else, returns boolean indicating whether cancel preview operation
succeeded.
Raises:
HttpException: An http error response was received while executing api
request.
"""
if args.fingerprint:
fingerprint = dm_util.DecodeFingerprint(args.fingerprint)
else:
# If no fingerprint is present, default to an empty fingerprint.
# TODO(b/34966984): Remove the empty default after cleaning up all
# deployments that has no fingerprint
fingerprint = dm_api_util.FetchDeploymentFingerprint(
self.client,
self.messages,
dm_base.GetProject(),
args.deployment_name,) or b''
try:
operation = self.client.deployments.CancelPreview(
self.messages.
DeploymentmanagerDeploymentsCancelPreviewRequest(
project=dm_base.GetProject(),
deployment=args.deployment_name,
deploymentsCancelPreviewRequest=
self.messages.DeploymentsCancelPreviewRequest(
fingerprint=fingerprint,
),
)
)
# Fetch and print the latest fingerprint of the deployment.
new_fingerprint = dm_api_util.FetchDeploymentFingerprint(
self.client,
self.messages,
dm_base.GetProject(),
args.deployment_name)
dm_util.PrintFingerprint(new_fingerprint)
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
if args.async_:
return operation
else:
op_name = operation.name
try:
operation = dm_write.WaitForOperation(
self.client,
self.messages,
op_name,
'cancel-preview',
dm_base.GetProject(),
timeout=OPERATION_TIMEOUT)
dm_util.LogOperationStatus(operation, 'Cancel preview')
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
try:
# Fetch a list of the canceled resources.
response = self.client.resources.List(
self.messages.DeploymentmanagerResourcesListRequest(
project=dm_base.GetProject(),
deployment=args.deployment_name,
)
)
# TODO(b/36052523): Pagination
return response.resources if response.resources else []
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)

View File

@@ -0,0 +1,348 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""deployments create command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.deployment_manager import dm_api_util
from googlecloudsdk.api_lib.deployment_manager import dm_base
from googlecloudsdk.api_lib.deployment_manager import exceptions as dm_exceptions
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.deployment_manager import alpha_flags
from googlecloudsdk.command_lib.deployment_manager import dm_util
from googlecloudsdk.command_lib.deployment_manager import dm_write
from googlecloudsdk.command_lib.deployment_manager import flags
from googlecloudsdk.command_lib.deployment_manager import importer
from googlecloudsdk.command_lib.util.apis import arg_utils
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
import six
# Number of seconds (approximately) to wait for create operation to complete.
OPERATION_TIMEOUT = 20 * 60 # 20 mins
@base.UnicodeIsSupported
@base.ReleaseTracks(base.ReleaseTrack.GA)
@dm_base.UseDmApi(dm_base.DmApiVersion.V2)
class Create(base.CreateCommand, dm_base.DmCommand):
"""Create a deployment.
This command inserts (creates) a new deployment based on a provided config
file.
"""
detailed_help = {
'EXAMPLES': """
To create a new deployment from a top-level YAML file, run:
$ {command} my-deployment --config=config.yaml --description="My deployment"
To create a new deployment from a top-level template file, run:
$ gcloud deployment-manager deployments create my-deployment \
--template=template.{jinja|py} \
--properties="string-key:'string-value',integer-key:12345"
To create a new deployment directly from a composite type, run:
$ gcloud deployment-manager deployments create my-deployment \
--composite-type=<project-id>/composite:<type-name> \
--properties="string-key:'string-value',integer-key:12345"
To preview a deployment without actually creating resources, run:
$ {command} my-new-deployment --config=config.yaml --preview
To instantiate a deployment that has been previewed, issue an update command for that deployment without specifying a config file.
More information is available at https://cloud.google.com/deployment-manager/docs/configuration/.
""",
}
_create_policy_flag_map = arg_utils.ChoiceEnumMapper(
'--create-policy',
(apis.GetMessagesModule('deploymentmanager', 'v2beta')
.DeploymentmanagerDeploymentsUpdateRequest.CreatePolicyValueValuesEnum),
help_str='Create policy for resources that have changed in the update',
default='create-or-acquire')
@staticmethod
def Args(parser, version=base.ReleaseTrack.GA):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
version: The version this tool is running as. base.ReleaseTrack.GA
is the default.
"""
group = parser.add_mutually_exclusive_group()
config_group = parser.add_mutually_exclusive_group(required=True)
flags.AddConfigFlags(config_group)
flags.AddAsyncFlag(group)
flags.AddDeploymentNameFlag(parser)
flags.AddPropertiesFlag(parser)
labels_util.AddCreateLabelsFlags(parser)
group.add_argument(
'--automatic-rollback-on-error',
help='If the create request results in a deployment with resource '
'errors, delete that deployment immediately after creation. '
'(default=False)',
dest='automatic_rollback',
default=False,
action='store_true')
parser.add_argument(
'--description',
help='Optional description of the deployment to insert.',
dest='description')
parser.add_argument(
'--preview',
help='Preview the requested create without actually instantiating the '
'underlying resources. (default=False)',
dest='preview',
default=False,
action='store_true')
parser.display_info.AddFormat(flags.RESOURCES_AND_OUTPUTS_FORMAT)
def Epilog(self, resources_were_displayed):
"""Called after resources are displayed if the default format was used.
Args:
resources_were_displayed: True if resources were displayed.
"""
if not resources_were_displayed:
log.status.Print('No resources or outputs found in your deployment.')
def Run(self, args):
"""Run 'deployments create'.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
If --async=true, returns Operation to poll.
Else, returns a struct containing the list of resources and list of
outputs in the deployment.
Raises:
HttpException: An http error response was received while executing api
request.
ConfigError: Config file could not be read or parsed, or the
deployment creation operation encountered an error.
"""
deployment_ref = self.resources.Parse(
args.deployment_name,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='deploymentmanager.deployments')
if (not args.IsSpecified('format')) and (args.async_):
args.format = flags.OPERATION_FORMAT
deployment = self.messages.Deployment(
name=deployment_ref.deployment,
target=importer.BuildTargetConfig(self.messages,
config=args.config,
template=args.template,
composite_type=args.composite_type,
properties=args.properties)
)
self._SetMetadata(args, deployment)
try:
operation = self.client.deployments.Insert(
self._BuildRequest(
args=args, project=dm_base.GetProject(), deployment=deployment))
# Fetch and print the latest fingerprint of the deployment.
fingerprint = dm_api_util.FetchDeploymentFingerprint(
self.client,
self.messages,
dm_base.GetProject(),
deployment_ref.deployment)
dm_util.PrintFingerprint(fingerprint)
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
if args.async_:
return operation
else:
op_name = operation.name
try:
operation = dm_write.WaitForOperation(
self.client,
self.messages,
op_name,
operation_description='create',
project=dm_base.GetProject(),
timeout=OPERATION_TIMEOUT)
dm_util.LogOperationStatus(operation, 'Create')
except apitools_exceptions.HttpError as error:
# TODO(b/37911296): Use gcloud default error handling.
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
except dm_exceptions.OperationError as error:
response = self._HandleOperationError(error,
args,
operation,
dm_base.GetProject(),
deployment_ref)
if getattr(args, 'automatic_rollback', False):
args.format = flags.OPERATION_FORMAT
return response
return dm_api_util.FetchResourcesAndOutputs(self.client, self.messages,
dm_base.GetProject(),
deployment_ref.deployment)
def _BuildRequest(self,
args,
project,
deployment,
supports_create_policy=False):
request = self.messages.DeploymentmanagerDeploymentsInsertRequest(
project=project, deployment=deployment, preview=args.preview)
if supports_create_policy and args.create_policy:
parsed_create_flag = Create._create_policy_flag_map.GetEnumForChoice(
args.create_policy).name
request.createPolicy = (
self.messages.DeploymentmanagerDeploymentsInsertRequest.
CreatePolicyValueValuesEnum(parsed_create_flag))
return request
def _HandleOperationError(
self, error, args, operation, project, deployment_ref):
if args.automatic_rollback:
delete_operation = self._PerformRollback(deployment_ref.deployment,
six.text_type(error))
create_operation = dm_api_util.GetOperation(self.client, self.messages,
operation, project)
return [create_operation, delete_operation]
raise error
def _SetMetadata(self, args, deployment):
if args.description:
deployment.description = args.description
label_dict = labels_util.GetUpdateLabelsDictFromArgs(args)
if label_dict:
deployment.labels = [
self.messages.DeploymentLabelEntry(key=k, value=v)
for k, v in sorted(six.iteritems(label_dict))
]
def _PerformRollback(self, deployment_name, error_message):
# Print information about the failure.
log.warning('There was an error deploying '
+ deployment_name + ':\n' + error_message)
log.status.Print('`--automatic-rollback-on-error` flag was supplied; '
'deleting failed deployment...')
# Delete the deployment.
try:
delete_operation = self.client.deployments.Delete(
self.messages.DeploymentmanagerDeploymentsDeleteRequest(
project=dm_base.GetProject(),
deployment=deployment_name,
)
)
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
# TODO(b/37481635): Use gcloud default operation polling.
dm_write.WaitForOperation(self.client,
self.messages,
delete_operation.name,
'delete',
dm_base.GetProject(),
timeout=OPERATION_TIMEOUT)
completed_operation = dm_api_util.GetOperation(self.client,
self.messages,
delete_operation,
dm_base.GetProject())
return completed_operation
@base.UnicodeIsSupported
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@dm_base.UseDmApi(dm_base.DmApiVersion.ALPHA)
class CreateAlpha(Create):
"""Create a deployment.
This command inserts (creates) a new deployment based on a provided config
file.
"""
@staticmethod
def Args(parser):
Create.Args(parser, version=base.ReleaseTrack.ALPHA)
alpha_flags.AddCredentialFlag(parser)
parser.display_info.AddFormat(alpha_flags.RESOURCES_AND_OUTPUTS_FORMAT)
Create._create_policy_flag_map.choice_arg.AddToParser(parser)
def _SetMetadata(self, args, deployment):
if args.credential:
deployment.credential = dm_util.CredentialFrom(self.messages,
args.credential)
super(CreateAlpha, self)._SetMetadata(args, deployment)
def _BuildRequest(self, args, project, deployment):
return super(CreateAlpha, self)._BuildRequest(
args=args,
project=project,
deployment=deployment,
supports_create_policy=True)
@base.UnicodeIsSupported
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@dm_base.UseDmApi(dm_base.DmApiVersion.V2BETA)
class CreateBeta(Create):
"""Create a deployment.
This command inserts (creates) a new deployment based on a provided config
file.
"""
@staticmethod
def Args(parser):
Create.Args(parser, version=base.ReleaseTrack.BETA)
Create._create_policy_flag_map.choice_arg.AddToParser(parser)
def _BuildRequest(self, args, project, deployment):
return super(CreateBeta, self)._BuildRequest(
args=args,
project=project,
deployment=deployment,
supports_create_policy=True)

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""deployments delete command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.deployment_manager import dm_api_util
from googlecloudsdk.api_lib.deployment_manager import dm_base
from googlecloudsdk.api_lib.deployment_manager import exceptions
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.api_lib.util import exceptions as api_exceptions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.deployment_manager import dm_util
from googlecloudsdk.command_lib.deployment_manager import dm_write
from googlecloudsdk.command_lib.deployment_manager import flags
from googlecloudsdk.core import exceptions as core_exceptions
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
# Number of seconds (approximately) to wait for each delete operation to
# complete.
OPERATION_TIMEOUT = 20 * 60 # 20 mins
@dm_base.UseDmApi(dm_base.DmApiVersion.V2)
class Delete(base.DeleteCommand, dm_base.DmCommand):
"""Delete a deployment.
This command deletes a deployment and deletes all associated resources.
"""
detailed_help = {
'EXAMPLES': """
To delete a deployment, run:
$ {command} my-deployment
To issue a delete command without waiting for the operation to complete, run:
$ {command} my-deployment --async
To delete several deployments, run:
$ {command} my-deployment-one my-deployment-two my-deployment-three
To disable the confirmation prompt on delete, run:
$ {command} my-deployment -q
""",
}
_delete_policy_flag_map = flags.GetDeleteFlagEnumMap(
(apis.GetMessagesModule('deploymentmanager', 'v2')
.DeploymentmanagerDeploymentsDeleteRequest.DeletePolicyValueValuesEnum))
@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 go
on the command line after this command. Positional arguments are
allowed.
"""
parser.add_argument('deployment_name', nargs='+', help='Deployment name.')
Delete._delete_policy_flag_map.choice_arg.AddToParser(parser)
flags.AddAsyncFlag(parser)
def Run(self, args):
"""Run 'deployments delete'.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
If --async=true, returns Operation to poll.
Else, returns boolean indicating whether insert operation succeeded.
Raises:
HttpException: An http error response was received while executing api
request.
"""
prompt_message = ('The following deployments will be deleted:\n- '
+ '\n- '.join(args.deployment_name))
if not args.quiet:
if not console_io.PromptContinue(message=prompt_message, default=False):
raise exceptions.OperationError('Deletion aborted by user.')
operations = []
errors = []
for deployment_name in args.deployment_name:
deployment_ref = self.resources.Parse(
deployment_name,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='deploymentmanager.deployments')
try:
operation = self.client.deployments.Delete(
self.messages.DeploymentmanagerDeploymentsDeleteRequest(
project=dm_base.GetProject(),
deployment=deployment_ref.deployment,
deletePolicy=(Delete._delete_policy_flag_map.
GetEnumForChoice(args.delete_policy)),
)
)
if args.async_:
operations.append(operation)
else:
op_name = operation.name
try:
# TODO(b/62720778): Refactor to use waiter.CloudOperationPoller
operation = dm_write.WaitForOperation(
self.client,
self.messages,
op_name,
'delete',
dm_base.GetProject(),
timeout=OPERATION_TIMEOUT)
dm_util.LogOperationStatus(operation, 'Delete')
except exceptions.OperationError as e:
errors.append(exceptions.OperationError(
'Delete operation {0} failed.\n{1}'.format(op_name, e)))
completed_operation = self.client.operations.Get(
self.messages.DeploymentmanagerOperationsGetRequest(
project=dm_base.GetProject(),
operation=op_name,
)
)
operations.append(completed_operation)
except apitools_exceptions.HttpError as error:
errors.append(api_exceptions.HttpException(
error, dm_api_util.HTTP_ERROR_FORMAT))
if errors:
raise core_exceptions.MultiError(errors)
return operations

View File

@@ -0,0 +1,170 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""deployments describe command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.deployment_manager import dm_api_util
from googlecloudsdk.api_lib.deployment_manager import dm_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.deployment_manager import alpha_flags
from googlecloudsdk.command_lib.deployment_manager import flags
from googlecloudsdk.core import properties
class _Results(object):
"""Encapsulate results into a single object to fit the Run() model."""
def __init__(self, deployment, resources, outputs):
self.deployment = deployment
self.resources = resources
self.outputs = outputs
@base.ReleaseTracks(base.ReleaseTrack.GA)
@dm_base.UseDmApi(dm_base.DmApiVersion.V2)
class Describe(base.DescribeCommand, dm_base.DmCommand):
"""Provide information about a deployment.
This command prints out all available details about a deployment.
"""
detailed_help = {
'EXAMPLES': """\
To display information about a deployment, run:
$ {command} my-deployment
""",
}
@staticmethod
def Args(parser, version=base.ReleaseTrack.GA):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
version: The version this tool is running as. base.ReleaseTrack.GA
is the default.
"""
flags.AddDeploymentNameFlag(parser)
parser.display_info.AddFormat("""
table(
deployment:format='default(name, id, description, fingerprint,
insertTime, manifest.basename(), labels, operation.operationType,
operation.name, operation.progress, operation.status,
operation.user, operation.endTime, operation.startTime,
operation.error, operation.warnings, update)',
resources:format='table(
name:label=NAME,
type:wrap:label=TYPE,
update.state.yesno(no="COMPLETED"),
update.intent)',
outputs:format='table(
name:label=OUTPUTS,
finalValue:label=VALUE)'
)
""")
def Run(self, args):
"""Run 'deployments describe'.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
The requested Deployment.
Raises:
HttpException: An http error response was received while executing api
request.
"""
deployment_ref = self.resources.Parse(
args.deployment_name,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='deploymentmanager.deployments')
try:
deployment = self.client.deployments.Get(
self.messages.DeploymentmanagerDeploymentsGetRequest(
project=dm_base.GetProject(),
deployment=deployment_ref.deployment))
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
try:
response = self.client.resources.List(
self.messages.DeploymentmanagerResourcesListRequest(
project=dm_base.GetProject(), deployment=deployment.name))
resources = response.resources
if self.ReleaseTrack() is base.ReleaseTrack.ALPHA:
if (not args.IsSpecified('format')) and (deployment.update):
args.format = (
alpha_flags.PREVIEWED_DEPLOYMENT_AND_RESOURCES_AND_OUTPUTS_FORMAT)
except apitools_exceptions.HttpError:
# Couldn't get resources, skip adding them to the table.
resources = None
outputs = []
manifest = dm_api_util.ExtractManifestName(deployment)
if manifest:
manifest_response = self.client.manifests.Get(
self.messages.DeploymentmanagerManifestsGetRequest(
project=dm_base.GetProject(),
deployment=deployment_ref.deployment,
manifest=manifest,
)
)
# We might be lacking a layout if the manifest failed expansion.
if manifest_response.layout:
outputs = dm_api_util.FlattenLayoutOutputs(manifest_response.layout)
return _Results(deployment, resources, outputs)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@dm_base.UseDmApi(dm_base.DmApiVersion.ALPHA)
class DescribeAlpha(Describe):
"""Provide information about a deployment.
This command prints out all available details about a deployment.
"""
@staticmethod
def Args(parser):
Describe.Args(parser, version=base.ReleaseTrack.ALPHA)
parser.display_info.AddFormat(
alpha_flags.DEPLOYMENT_AND_RESOURCES_AND_OUTPUTS_FORMAT)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class DescribeBeta(Describe):
"""Provide information about a deployment.
This command prints out all available details about a deployment.
"""
@staticmethod
def Args(parser):
Describe.Args(parser, version=base.ReleaseTrack.BETA)

View File

@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""deployments list command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.deployment_manager import dm_api_util
from googlecloudsdk.api_lib.deployment_manager import dm_base
from googlecloudsdk.calliope import base
@dm_base.UseDmApi(dm_base.DmApiVersion.V2)
class List(base.ListCommand, dm_base.DmCommand):
"""List deployments in a project.
Prints a table with summary information on all deployments in the project.
"""
detailed_help = {
'EXAMPLES': """\
To print out a list of deployments with some summary information about each, run:
$ {command}
To print only the name of each deployment, run:
$ {command} --simple-list
""",
}
@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 go
on the command line after this command. Positional arguments are
allowed.
"""
dm_api_util.SIMPLE_LIST_FLAG.AddToParser(parser)
parser.display_info.AddFormat("""
table(
name,
operation.operationType:label=LAST_OPERATION_TYPE,
operation.status,
description,
manifest.basename(),
operation.error.errors.group(code)
)
""")
def Run(self, args):
"""Run 'deployments list'.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
The list of deployments for this project.
Raises:
HttpException: An http error response was received while executing api
request.
"""
request = self.messages.DeploymentmanagerDeploymentsListRequest(
project=dm_base.GetProject(),
)
return dm_api_util.YieldWithHttpExceptions(list_pager.YieldFromList(
self.client.deployments, request, field='deployments',
limit=args.limit, batch_size=args.page_size))

View File

@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""deployments stop command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.deployment_manager import dm_api_util
from googlecloudsdk.api_lib.deployment_manager import dm_base
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.deployment_manager import dm_util
from googlecloudsdk.command_lib.deployment_manager import dm_write
from googlecloudsdk.command_lib.deployment_manager import flags
# Number of seconds (approximately) to wait for stop operation to complete.
OPERATION_TIMEOUT = 20 * 60 # 20 mins
@dm_base.UseDmApi(dm_base.DmApiVersion.V2)
class Stop(base.Command, dm_base.DmCommand):
"""Stop a pending or running deployment update or creation.
This command will stop a currently running or pending operation on
a deployment.
"""
detailed_help = {
'EXAMPLES': """
To stop a running operation on a deployment, run:
$ {command} my-deployment
To issue a stop command without waiting for the operation to complete, run:
$ {command} my-deployment --async
To stop a running operation on a deployment providing a fingerprint, run:
$ {command} my-deployment --fingerprint=deployment-fingerprint
""",
}
@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 go
on the command line after this command. Positional arguments are
allowed.
"""
flags.AddAsyncFlag(parser)
flags.AddDeploymentNameFlag(parser)
flags.AddFingerprintFlag(parser)
def Run(self, args):
"""Run 'deployments stop'.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
If --async=true, returns Operation to poll.
Else, returns boolean indicating whether stop operation succeeded.
Raises:
HttpException: An http error response was received while executing api
request.
"""
if args.fingerprint:
fingerprint = dm_util.DecodeFingerprint(args.fingerprint)
else:
# If no fingerprint is present, default to an empty fingerprint.
# TODO(b/34966984): Remove the empty default after cleaning up all
# deployments that has no fingerprint
fingerprint = dm_api_util.FetchDeploymentFingerprint(
self.client,
self.messages,
dm_base.GetProject(),
args.deployment_name) or b''
try:
operation = self.client.deployments.Stop(
self.messages.DeploymentmanagerDeploymentsStopRequest(
project=dm_base.GetProject(),
deployment=args.deployment_name,
deploymentsStopRequest=(
self.messages.DeploymentsStopRequest(
fingerprint=fingerprint)
),
)
)
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
if args.async_:
return operation
else:
op_name = operation.name
try:
operation = dm_write.WaitForOperation(
self.client,
self.messages,
op_name,
'stop',
dm_base.GetProject(),
timeout=OPERATION_TIMEOUT)
dm_util.LogOperationStatus(operation, 'Stop')
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
try:
# Fetch a list of the stopped resources.
response = self.client.resources.List(
self.messages.DeploymentmanagerResourcesListRequest(
project=dm_base.GetProject(),
deployment=args.deployment_name,
)
)
# TODO(b/36055861): Pagination
return response.resources if response.resources else []
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)

View File

@@ -0,0 +1,362 @@
# -*- coding: utf-8 -*- #
# Copyright 2014 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.
"""deployments update command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.deployment_manager import dm_api_util
from googlecloudsdk.api_lib.deployment_manager import dm_base
from googlecloudsdk.api_lib.deployment_manager import dm_labels
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.deployment_manager import alpha_flags
from googlecloudsdk.command_lib.deployment_manager import dm_util
from googlecloudsdk.command_lib.deployment_manager import dm_write
from googlecloudsdk.command_lib.deployment_manager import flags
from googlecloudsdk.command_lib.deployment_manager import importer
from googlecloudsdk.command_lib.util.apis import arg_utils
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
import six
# Number of seconds (approximately) to wait for update operation to complete.
OPERATION_TIMEOUT = 20 * 60 # 20 mins
@base.UnicodeIsSupported
@base.ReleaseTracks(base.ReleaseTrack.GA)
@dm_base.UseDmApi(dm_base.DmApiVersion.V2)
class Update(base.UpdateCommand, dm_base.DmCommand):
"""Update a deployment based on a provided config file.
This command will update a deployment with the new config file provided.
Different policies for create, update, and delete policies can be specified.
"""
detailed_help = {
'EXAMPLES': """
To update an existing deployment with a new config YAML file, run:
$ {command} my-deployment --config=new_config.yaml
To update an existing deployment with a new config template file, run:
$ {command} my-deployment --template=new_config.{jinja|py}
To update an existing deployment with a composite type as a new config, run:
$ {command} my-deployment --composite-type=<project-id>/composite:<new-config>
To preview an update to an existing deployment without actually modifying the resources, run:
$ {command} my-deployment --config=new_config.yaml --preview
To apply an update that has been previewed, provide the name of the previewed deployment, and no config file:
$ {command} my-deployment
To specify different create, update, or delete policies, include any subset of the following flags:
$ {command} my-deployment --config=new_config.yaml --create-policy=acquire --delete-policy=abandon
To perform an update without waiting for the operation to complete, run:
$ {command} my-deployment --config=new_config.yaml --async
To update an existing deployment with a new config file and a fingerprint, run:
$ {command} my-deployment --config=new_config.yaml --fingerprint=deployment-fingerprint
Either the `--config`, `--template`, or `--composite-type` flag is required unless launching an already-previewed update to a deployment. If you want to update a deployment's metadata, such as the labels or description, you must run a separate command with `--update-labels`, `--remove-labels`, or `--description`, as applicable.
More information is available at https://cloud.google.com/deployment-manager/docs/deployments/updating-deployments.
""",
}
_delete_policy_flag_map = flags.GetDeleteFlagEnumMap(
(apis.GetMessagesModule('deploymentmanager', 'v2')
.DeploymentmanagerDeploymentsUpdateRequest.DeletePolicyValueValuesEnum))
_create_policy_flag_map = arg_utils.ChoiceEnumMapper(
'--create-policy',
(apis.GetMessagesModule('deploymentmanager', 'v2')
.DeploymentmanagerDeploymentsUpdateRequest.CreatePolicyValueValuesEnum),
help_str='Create policy for resources that have changed in the update',
default='create-or-acquire')
_create_policy_v2beta_flag_map = arg_utils.ChoiceEnumMapper(
'--create-policy',
(apis.GetMessagesModule('deploymentmanager', 'v2beta')
.DeploymentmanagerDeploymentsUpdateRequest.CreatePolicyValueValuesEnum),
help_str='Create policy for resources that have changed in the update',
default='create-or-acquire')
@staticmethod
def Args(parser, version=base.ReleaseTrack.GA):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
version: The version this tool is running as. base.ReleaseTrack.GA
is the default.
"""
flags.AddDeploymentNameFlag(parser)
flags.AddPropertiesFlag(parser)
flags.AddAsyncFlag(parser)
parser.add_argument(
'--description',
help='The new description of the deployment.',
dest='description'
)
group = parser.add_mutually_exclusive_group()
flags.AddConfigFlags(group)
if version in [base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA]:
group.add_argument(
'--manifest-id',
help='Manifest Id of a previous deployment. '
'This flag cannot be used with --config.',
dest='manifest_id')
labels_util.AddUpdateLabelsFlags(parser, enable_clear=False)
parser.add_argument(
'--preview',
help='Preview the requested update without making any changes to the '
'underlying resources. (default=False)',
dest='preview',
default=False,
action='store_true')
if version in [base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA]:
Update._create_policy_v2beta_flag_map.choice_arg.AddToParser(parser)
else:
Update._create_policy_flag_map.choice_arg.AddToParser(parser)
Update._delete_policy_flag_map.choice_arg.AddToParser(parser)
flags.AddFingerprintFlag(parser)
parser.display_info.AddFormat(flags.RESOURCES_AND_OUTPUTS_FORMAT)
def Epilog(self, resources_were_displayed):
"""Called after resources are displayed if the default format was used.
Args:
resources_were_displayed: True if resources were displayed.
"""
if not resources_were_displayed:
log.status.Print('No resources or outputs found in your deployment.')
def Run(self, args):
"""Run 'deployments update'.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
If --async=true, returns Operation to poll.
Else, returns a struct containing the list of resources and list of
outputs in the deployment.
Raises:
HttpException: An http error response was received while executing api
request.
"""
deployment_ref = self.resources.Parse(
args.deployment_name,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='deploymentmanager.deployments')
if not args.IsSpecified('format') and args.async_:
args.format = flags.OPERATION_FORMAT
patch_request = False
deployment = self.messages.Deployment(
name=deployment_ref.deployment,
)
if not (args.config is None and args.template is None
and args.composite_type is None):
deployment.target = importer.BuildTargetConfig(
self.messages,
config=args.config,
template=args.template,
composite_type=args.composite_type,
properties=args.properties)
elif (self.ReleaseTrack() in [base.ReleaseTrack.ALPHA,
base.ReleaseTrack.BETA]
and args.manifest_id):
deployment.target = importer.BuildTargetConfigFromManifest(
self.client, self.messages,
dm_base.GetProject(),
deployment_ref.deployment, args.manifest_id, args.properties)
# Get the fingerprint from the deployment to update.
try:
current_deployment = self.client.deployments.Get(
self.messages.DeploymentmanagerDeploymentsGetRequest(
project=dm_base.GetProject(),
deployment=deployment_ref.deployment
)
)
if args.fingerprint:
deployment.fingerprint = dm_util.DecodeFingerprint(args.fingerprint)
else:
# If no fingerprint is present, default to an empty fingerprint.
# TODO(b/34966984): Remove the empty default after cleaning up all
# deployments that has no fingerprint
deployment.fingerprint = current_deployment.fingerprint or b''
# Get the credential from the deployment to update.
if self.ReleaseTrack() in [base.ReleaseTrack.ALPHA] and args.credential:
deployment.credential = dm_util.CredentialFrom(self.messages,
args.credential)
# Update the labels of the deployment
deployment.labels = self._GetUpdatedDeploymentLabels(
args, current_deployment)
# If no config or manifest_id are specified, but try to update labels,
# only add patch_request header when directly updating a non-previewed
# deployment
no_manifest = (self.ReleaseTrack() is
base.ReleaseTrack.GA) or not args.manifest_id
patch_request = not args.config and no_manifest and (
bool(args.update_labels) or bool(args.remove_labels))
if args.description is None:
deployment.description = current_deployment.description
elif not args.description or args.description.isspace():
deployment.description = None
else:
deployment.description = args.description
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
if patch_request:
args.format = flags.DEPLOYMENT_FORMAT
try:
# Necessary to handle API Version abstraction below
parsed_delete_flag = Update._delete_policy_flag_map.GetEnumForChoice(
args.delete_policy).name
if self.ReleaseTrack() in [
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA
]:
parsed_create_flag = (
Update._create_policy_v2beta_flag_map.GetEnumForChoice(
args.create_policy).name)
else:
parsed_create_flag = (
Update._create_policy_flag_map.GetEnumForChoice(
args.create_policy).name)
request = self.messages.DeploymentmanagerDeploymentsUpdateRequest(
deploymentResource=deployment,
project=dm_base.GetProject(),
deployment=deployment_ref.deployment,
preview=args.preview,
createPolicy=(self.messages.DeploymentmanagerDeploymentsUpdateRequest.
CreatePolicyValueValuesEnum(parsed_create_flag)),
deletePolicy=(self.messages.DeploymentmanagerDeploymentsUpdateRequest.
DeletePolicyValueValuesEnum(parsed_delete_flag)))
client = self.client
client.additional_http_headers['X-Cloud-DM-Patch'] = six.text_type(
patch_request)
operation = client.deployments.Update(request)
# Fetch and print the latest fingerprint of the deployment.
updated_deployment = dm_api_util.FetchDeployment(
self.client, self.messages, dm_base.GetProject(),
deployment_ref.deployment)
if patch_request:
if args.async_:
log.warning(
'Updating Deployment metadata is synchronous, --async flag '
'is ignored.')
log.status.Print('Update deployment metadata completed successfully.')
return updated_deployment
dm_util.PrintFingerprint(updated_deployment.fingerprint)
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
if args.async_:
return operation
else:
op_name = operation.name
try:
operation = dm_write.WaitForOperation(
self.client,
self.messages,
op_name,
'update',
dm_base.GetProject(),
timeout=OPERATION_TIMEOUT)
dm_util.LogOperationStatus(operation, 'Update')
except apitools_exceptions.HttpError as error:
raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT)
return dm_api_util.FetchResourcesAndOutputs(self.client, self.messages,
dm_base.GetProject(),
deployment_ref.deployment)
def _GetUpdatedDeploymentLabels(self, args, deployment):
update_labels = labels_util.GetUpdateLabelsDictFromArgs(args)
remove_labels = labels_util.GetRemoveLabelsListFromArgs(args)
return dm_labels.UpdateLabels(deployment.labels,
self.messages.DeploymentLabelEntry,
update_labels, remove_labels)
@base.UnicodeIsSupported
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@dm_base.UseDmApi(dm_base.DmApiVersion.ALPHA)
class UpdateAlpha(Update):
"""Update a deployment based on a provided config file.
This command will update a deployment with the new config file provided.
Different policies for create, update, and delete policies can be specified.
"""
@staticmethod
def Args(parser):
Update.Args(parser, version=base.ReleaseTrack.ALPHA)
alpha_flags.AddCredentialFlag(parser)
parser.display_info.AddFormat(alpha_flags.RESOURCES_AND_OUTPUTS_FORMAT)
@base.UnicodeIsSupported
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@dm_base.UseDmApi(dm_base.DmApiVersion.V2BETA)
class UpdateBeta(Update):
"""Update a deployment based on a provided config file.
This command will update a deployment with the new config file provided.
Different policies for create, update, and delete policies can be specified.
"""
@staticmethod
def Args(parser):
Update.Args(parser, version=base.ReleaseTrack.BETA)