# -*- coding: utf-8 -*- # Lint as: python3 # Copyright 2021 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 deploy an Apigee archive deployment to an environment.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib import apigee from googlecloudsdk.api_lib.util import waiter from googlecloudsdk.calliope import base from googlecloudsdk.command_lib.apigee import archives as cmd_lib from googlecloudsdk.command_lib.apigee import defaults from googlecloudsdk.command_lib.apigee import errors from googlecloudsdk.command_lib.apigee import resource_args from googlecloudsdk.command_lib.util.args import labels_util from googlecloudsdk.core import log from googlecloudsdk.core.util import files @base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA) class Deploy(base.DescribeCommand): """Deploy an Apigee archive deployment to an environment.""" detailed_help = { "DESCRIPTION": """\ {description} `{command}` installs an archive deployment in an Apigee environment. By default, the archive deployment will be deployed on the remote management plane for the specified Apigee organization. To deploy on a locally running Apigee emulator, use the `--local` flag. """, "EXAMPLES": """\ To deploy the contents of the current working directory as an archive deployment to an environment named ``my-test'', given that the Cloud Platform project has been set in gcloud settings, run: $ {command} --environment=my-test To deploy an archive deployment from a local directory other than the current working directory, to an environment named ``my-demo'' in an organization belonging to a Cloud Platform project other than the one set in gcloud settings, named ``my-org'', run: $ {command} --organization=my-org --environment=my-demo --source=/apigee/dev To deploy the contents of the current working directory as an archive deployment, with the user-defined labels ``my-label1=foo'' and ``my-label2=bar'', to an environment named ``my-test'', given that the Cloud Platform project has been set in gcloud settings, run: $ {command} --environment=my-test --labels=my-label1=foo,my-label2=bar """ } @staticmethod def Args(parser): fallthroughs = [defaults.GCPProductOrganizationFallthrough()] resource_args.AddSingleResourceArgument( parser, resource_path="organization.environment", help_text=("Apigee environment in which to deploy the archive " "deployment."), fallthroughs=fallthroughs, positional=False, required=True) # Create a argument group to manage that only one of either --source or # --bundle-file flags are provided on the command line. source_input_group = parser.add_group(mutex=True, help="Source input.") source_input_group.add_argument( "--source", required=False, type=files.ExpandHomeDir, help="The source directory of the archive to upload.") source_input_group.add_argument( "--bundle-file", required=False, type=files.ExpandHomeDir, help="The zip file containing an archive to upload.") parser.add_argument( "--async", action="store_true", dest="async_", help=("If set, returns immediately and outputs a description of the " "long running operation that was launched. Else, `{command}` " "will block until the archive deployment has been successfully " "deployed to the specified environment.\n\n" "To monitor the operation once it's been launched, run " "`{grandparent_command} operations describe OPERATION_NAME`.")) # This adds the --labels flag. labels_util.AddCreateLabelsFlags(parser) def _GetUploadUrl(self, identifiers): """Gets the signed URL for uploading the archive deployment. Args: identifiers: A dict of resource identifers. Must contain "organizationsId" and "environmentsId" Returns: A str of the upload URL. Raises: googlecloudsdk.command_lib.apigee.errors.RequestError if the "uploadUri" field is not included in the GetUploadUrl response. """ get_upload_url_resp = apigee.ArchivesClient.GetUploadUrl(identifiers) if "uploadUri" not in get_upload_url_resp: raise errors.RequestError( resource_type="getUploadUrl", resource_identifier=identifiers, body=get_upload_url_resp, user_help="Please try again.") return get_upload_url_resp["uploadUri"] def _UploadArchive(self, upload_url, zip_file_path): """Issues an HTTP PUT call to the upload URL with the zip file payload. Args: upload_url: A str containing the full upload URL. zip_file_path: A str of the local path to the zip file. Raises: googlecloudsdk.command_lib.apigee.errors.HttpRequestError if the response status of the HTTP PUT call is not 200 (OK). """ upload_archive_resp = cmd_lib.UploadArchive(upload_url, zip_file_path) if not upload_archive_resp.ok: raise errors.HttpRequestError(upload_archive_resp.status_code, upload_archive_resp.reason, upload_archive_resp.content) def _DeployArchive(self, identifiers, upload_url, labels): """Creates the archive deployment. Args: identifiers: A dict of resource identifers. Must contain "organizationsId" and "environmentsId" upload_url: A str containing the full upload URL. labels: A dict of the key/value pairs to add as labels. Returns: A dict containing the operation metadata. """ post_data = {} post_data["gcs_uri"] = upload_url if labels: post_data["labels"] = {} for k, v in labels.items(): post_data["labels"][k] = v api_response = apigee.ArchivesClient.CreateArchiveDeployment( identifiers, post_data) operation = apigee.OperationsClient.SplitName(api_response) return operation def Run(self, args): """Run the deploy command.""" identifiers = args.CONCEPTS.environment.Parse().AsDict() labels_arg = labels_util.GetUpdateLabelsDictFromArgs(args) local_dir_archive = None try: local_dir_archive = cmd_lib.LocalDirectoryArchive(args.source) if args.bundle_file: local_dir_archive.ValidateZipFilePath(args.bundle_file) zip_file_path = args.bundle_file else: zip_file_path = local_dir_archive.Zip() upload_url = self._GetUploadUrl(identifiers) self._UploadArchive(upload_url, zip_file_path) operation = self._DeployArchive(identifiers, upload_url, labels_arg) if "organization" not in operation or "uuid" not in operation: raise waiter.OperationError( "Unknown operation response: {}".format(operation)) if "warnings" in operation["metadata"]: for warning in operation["metadata"]["warnings"]: log.warning(warning) log.info("Started archives deploy operation %s", operation["name"]) if args.async_: return operation waiter.WaitFor( apigee.LROPoller(operation["organization"]), operation["uuid"], message="Waiting for operation [{}] to complete".format( operation["uuid"]), wait_ceiling_ms=5000, ) finally: if local_dir_archive and hasattr(local_dir_archive, "Close"): local_dir_archive.Close()