Ejemplo n.º 1
0
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.

          More information is available at https://cloud.google.com/deployment-manager/docs/configuration/.
          """,
    }

    _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'] = 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,
                self.ReleaseTrack() is base.ReleaseTrack.ALPHA)

    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)
Ejemplo n.º 2
0
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(
                u'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