def SetServingStatus(self, service_name, version_id, serving_status, block=True): """Sets the serving status of the specified version. Args: service_name: str, The service name version_id: str, The version to delete. serving_status: The serving status to set. block: bool, whether to block on the completion of the operation Returns: The completed Operation if block is True, or the Operation to wait on otherwise. """ patch_request = self.messages.AppengineAppsServicesVersionsPatchRequest( name=self._FormatVersion(service_name=service_name, version_id=version_id), version=self.messages.Version(servingStatus=serving_status), updateMask='servingStatus') operation = self.client.apps_services_versions.Patch(patch_request) if block: return operations_util.WaitForOperation( self.client.apps_operations, operation) else: return operation
def CreateDomainMapping(self, domain, certificate_id, no_managed_certificate): """Creates a domain mapping for the given application. Args: domain: str, the custom domain string. certificate_id: str, a certificate id for the new domain. no_managed_certificate: bool, don't automatically provision a certificate. Returns: The created DomainMapping object. """ ssl = self.messages.SslSettings(certificateId=certificate_id) domain_mapping = self.messages.DomainMapping(id=domain, sslSettings=ssl) request = self.messages.AppengineAppsDomainMappingsCreateRequest( parent=self._FormatApp(), domainMapping=domain_mapping, noManagedCertificate=no_managed_certificate) operation = requests.MakeRequest( self.client.apps_domainMappings.Create, request) return operations_util.WaitForOperation(self.client.apps_operations, operation).response
def UpdateDispatchRules(self, dispatch_rules): """Updates an application's dispatch rules. Args: dispatch_rules: [{'service': str, 'domain': str, 'path': str}], dispatch- rules to set-and-replace. Returns: Long running operation. """ # Create a configuration update request. update_mask = 'dispatchRules,' application_update = self.messages.Application() application_update.dispatchRules = [ self.messages.UrlDispatchRule(**r) for r in dispatch_rules ] update_request = self.messages.AppengineAppsPatchRequest( name=self._FormatApp(), application=application_update, updateMask=update_mask) operation = self.client.apps.Patch(update_request) log.debug( 'Received operation: [{operation}] with mask [{mask}]'.format( operation=operation.name, mask=update_mask)) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def UpdateDatabaseType(self, database_type): """Updates an application's database_type. Args: database_type: New database type to switch to Returns: Long running operation. """ # Create a configuration update request. update_mask = 'databaseType' application_update = self.messages.Application() application_update.databaseType = database_type update_request = self.messages.AppengineAppsPatchRequest( name=self._FormatApp(), application=application_update, updateMask=update_mask) operation = self.client.apps.Patch(update_request) log.debug( 'Received operation: [{operation}] with mask [{mask}]'.format( operation=operation.name, mask=update_mask)) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def SetTrafficSplit(self, service_name, allocations, shard_by='UNSPECIFIED', migrate=False): """Sets the traffic split of the given services. Args: service_name: str, The service name allocations: A dict mapping version ID to traffic split. shard_by: A ShardByValuesEnum value specifying how to shard the traffic. migrate: Whether or not to migrate traffic. Returns: Long running operation. """ # Create a traffic split where 100% of traffic goes to the specified # version. traffic_split = encoding.PyValueToMessage(self.messages.TrafficSplit, { 'allocations': allocations, 'shardBy': shard_by }) update_service_request = self.messages.AppengineAppsServicesPatchRequest( name=self._GetServiceRelativeName(service_name=service_name), service=self.messages.Service(split=traffic_split), migrateTraffic=migrate, updateMask='split') message = 'Setting traffic split for service [{service}]'.format( service=service_name) operation = self.client.apps_services.Patch(update_service_request) return operations_util.WaitForOperation(self.client.apps_operations, operation, message=message)
def CreateDomainMapping(self, domain, certificate_id, management_type): """Creates a domain mapping for the given application. Args: domain: str, the custom domain string. certificate_id: str, a certificate id for the new domain. management_type: SslSettings.SslManagementTypeValueValuesEnum, AUTOMATIC or MANUAL certificate provisioning. Returns: The created DomainMapping object. """ ssl = self.messages.SslSettings(certificateId=certificate_id, sslManagementType=management_type) domain_mapping = self.messages.DomainMapping(id=domain, sslSettings=ssl) request = self.messages.AppengineAppsDomainMappingsCreateRequest( parent=self._FormatApp(), domainMapping=domain_mapping) operation = self.client.apps_domainMappings.Create(request) return operations_util.WaitForOperation(self.client.apps_operations, operation).response
def UpdateDomainMapping(self, domain, certificate_id, no_certificate_id): """Updates a domain mapping for the given application. Args: domain: str, the custom domain string. certificate_id: str, a certificate id for the domain. no_certificate_id: boolean, remove the certificate id from the domain. Returns: The updated DomainMapping object. """ mask_fields = [] if certificate_id or no_certificate_id: mask_fields.append('sslSettings.certificateId') ssl = self.messages.SslSettings(certificateId=certificate_id) domain_mapping = self.messages.DomainMapping(id=domain, sslSettings=ssl) if not mask_fields: raise exceptions.MinimumArgumentException( 'Please specify at least one attribute to the domain-mapping update.') request = self.messages.AppengineAppsDomainMappingsPatchRequest( name=self._FormatDomainMapping(domain), domainMapping=domain_mapping, updateMask=','.join(mask_fields)) operation = requests.MakeRequest(self.client.apps_domainMappings.Patch, request) return operations_util.WaitForOperation(self.client.apps_operations, operation).response
def PatchApplication(self, split_health_checks=None): """Updates an application. Args: split_health_checks: Boolean, whether to enable split health checks by default. Returns: Long running operation. """ # Create a configuration update request. update_mask = '' if split_health_checks is not None: update_mask += 'featureSettings.splitHealthChecks,' application_update = self.messages.Application() application_update.featureSettings = self.messages.FeatureSettings( splitHealthChecks=split_health_checks) update_request = self.messages.AppengineAppsPatchRequest( name=self._FormatApp(), application=application_update, updateMask=update_mask) operation = self.client.apps.Patch(update_request) log.debug('Received operation: [{operation}] with mask [{mask}]'.format( operation=operation.name, mask=update_mask)) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def CreateApp(self, location): """Creates an App Engine app within the current cloud project. Creates a new singleton app within the currently selected Cloud Project. The action is one-time and irreversible. Args: location: str, The location (region) of the app, i.e. "us-central" Raises: apitools_exceptions.HttpConflictError if app already exists Returns: A long running operation. """ create_request = self.messages.Application(id=self.project, locationId=location) operation = self.client.apps.Create(create_request) log.debug('Received operation: [{operation}]'.format( operation=operation.name)) message = ( 'Creating App Engine application in project [{project}] and ' 'region [{region}].'.format(project=self.project, region=location)) return operations_util.WaitForOperation(self.client.apps_operations, operation, message=message)
def CreateApp(self, location): """Creates an App Engine app within the current cloud project. Creates a new singleton app within the currently selected Cloud Project. The action is one-time and irreversible. Args: location: str, The location (region) of the app, i.e. "us-central" Raises: googlecloudsdk.api_lib.app.exceptions.ConflictError if app already exists Returns: A long running operation. """ create_request = self.messages.Application(id=self.project, locationId=location) operation = requests.MakeRequest(self.client.apps.Create, create_request) log.debug('Received operation: [{operation}]'.format( operation=operation.name)) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def DeployService(self, service_name, version_id, service_config, manifest, build, endpoints_info=None, extra_config_settings=None): """Updates and deploys new app versions based on given config. If the build operation has not yet completed, streams the Google Cloud Builder logs as well. Args: service_name: str, The service to deploy. version_id: str, The version of the service to deploy. service_config: AppInfoExternal, Service info parsed from a service yaml file. manifest: Dictionary mapping source files to Google Cloud Storage locations. build: BuildArtifact, a wrapper which contains either the build ID for an in-progress parallel build, or the name of the container image for a serial build. endpoints_info: EndpointsServiceInfo, Endpoints service info to be added to the AppInfoExternal configuration. Only provided when Endpoints API Management feature is enabled. extra_config_settings: dict, client config settings to pass to the server as beta settings. Returns: A Version resource representing the deployed version. """ version_resource = self._CreateVersionResource(service_config, manifest, version_id, build, endpoints_info, extra_config_settings) create_request = self.messages.AppengineAppsServicesVersionsCreateRequest( parent=self._GetServiceRelativeName(service_name=service_name), version=version_resource) operation = self.client.apps_services_versions.Create(create_request) # If build operation is still in progress, stream build logs and wait for # completion before polling the service deployment operation for completion. # Service deployment can never complete before the build has finished, as it # is dependent on the built image. if build and build.IsBuildId() and build.build_op: cloud_build.CloudBuildClient().WaitAndStreamLogs(build.build_op) metrics.CustomTimedEvent(metric_names.CLOUDBUILD_EXECUTE_ASYNC) log.debug('Received operation: [{operation}]'.format( operation=operation.name)) message = 'Updating service [{service}]'.format(service=service_name) return operations_util.WaitForOperation(self.client.apps_operations, operation, message=message)
def DeleteDomainMapping(self, domain): """Deletes a domain mapping for the given application. Args: domain: str, the domain to delete. """ request = self.messages.AppengineAppsDomainMappingsDeleteRequest( name=self._FormatDomainMapping(domain)) operation = self.client.apps_domainMappings.Delete(request) operations_util.WaitForOperation(self.client.apps_operations, operation)
def Run(self, args): api_client = appengine_api_client.GetApiClient() operation = api_client.GetOperation(args.operation) if operation.done: log.status.Print('Operation [{0}] is already done.'.format( args.operation)) return operation else: with progress_tracker.ProgressTracker( 'Waiting for operation [{0}] to complete.'.format( args.operation)): return operations_util.WaitForOperation( api_client.client.apps_operations, operation)
def _WaitAndStreamLogs(self, build_op, logs_bucket, build_id, logs_uri): """Wait for a Cloud Build to finish, optionally streaming logs.""" log.status.Print( 'Started cloud build [{build_id}].'.format(build_id=build_id)) if logs_bucket: log_object = self.CLOUDBUILD_LOGFILE_FMT_STRING.format( build_id=build_id) log_tailer = cloudbuild_logs.LogTailer(bucket=logs_bucket, obj=log_object) log_loc = None if logs_uri: log.status.Print('To see logs in the Cloud Console: ' + logs_uri) log_loc = 'at ' + logs_uri else: log.status.Print('Logs can be found in the Cloud Console.') log_loc = 'in the Cloud Console.' op = operations_util.WaitForOperation( operation_service=self.client.operations, operation=build_op, retry_interval=self._RETRY_INTERVAL, max_retries=self._MAX_RETRIES, retry_callback=log_tailer.Poll) # Poll the logs one final time to ensure we have everything. We know this # final poll will get the full log contents because GCS is strongly # consistent and Container Builder waits for logs to finish pushing before # marking the build complete. log_tailer.Poll(is_last=True) else: op = operations_util.WaitForOperation( operation_service=self.client.operations, operation=build_op, retry_interval=self._RETRY_INTERVAL, max_retries=self._MAX_RETRIES) final_status = _GetStatusFromOp(op) if final_status != self.CLOUDBUILD_SUCCESS: raise BuildFailedError('Cloud build failed with status ' + final_status + '. Check logs ' + log_loc)
def DeleteInstance(self, res): """Delete a Flexible instance. Args: res: A googlecloudsdk.core.Resource object. Returns: The completed Operation. """ request = self.messages.AppengineAppsServicesVersionsInstancesDeleteRequest( name=res.RelativeName()) operation = self.client.apps_services_versions_instances.Delete(request) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def DeleteService(self, service_name): """Deletes the specified service. Args: service_name: str, Name of the service to delete. Returns: The completed Operation. """ delete_request = self.messages.AppengineAppsServicesDeleteRequest( name=self._GetServiceRelativeName(service_name=service_name)) operation = requests.MakeRequest(self.client.apps_services.Delete, delete_request) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def DeployService(self, service_name, version_id, service_config, manifest, build, endpoints_info=None, extra_config_settings=None): """Updates and deploys new app versions based on given config. Args: service_name: str, The service to deploy. version_id: str, The version of the service to deploy. service_config: AppInfoExternal, Service info parsed from a service yaml file. manifest: Dictionary mapping source files to Google Cloud Storage locations. build: BuildArtifact, a wrapper which contains either the build ID for an in-progress parallel build, or the name of the container image for a serial build. endpoints_info: EndpointsServiceInfo, Endpoints service info to be added to the AppInfoExternal configuration. Only provided when Endpoints API Management feature is enabled. extra_config_settings: dict, client config settings to pass to the server as beta settings. Returns: A Version resource representing the deployed version. """ version_resource = self._CreateVersionResource(service_config, manifest, version_id, build, endpoints_info, extra_config_settings) create_request = self.messages.AppengineAppsServicesVersionsCreateRequest( parent=self._GetServiceRelativeName(service_name=service_name), version=version_resource) operation = requests.MakeRequest( self.client.apps_services_versions.Create, create_request) log.debug('Received operation: [{operation}]'.format( operation=operation.name)) message = 'Updating service [{service}]'.format(service=service_name) return operations_util.WaitForOperation(self.client.apps_operations, operation, message=message)
def DeleteVersion(self, service_name, version_id): """Deletes the specified version of the given service. Args: service_name: str, The service name version_id: str, The version to delete. Returns: The completed Operation. """ delete_request = self.messages.AppengineAppsServicesVersionsDeleteRequest( name=self._FormatVersion(service_name=service_name, version_id=version_id)) operation = requests.MakeRequest( self.client.apps_services_versions.Delete, delete_request) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def RepairApplication(self): """Creates missing app resources. In particular, the Application.code_bucket GCS reference. Returns: A long running operation. """ request = self.messages.AppengineAppsRepairRequest( name=self._FormatApp(), repairApplicationRequest=self.messages.RepairApplicationRequest()) operation = requests.MakeRequest(self.client.apps.Repair, request) log.debug('Received operation: [{operation}]'.format( operation=operation.name)) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def UpdateDomainMapping(self, domain, certificate_id, no_certificate_id, management_type): """Updates a domain mapping for the given application. Args: domain: str, the custom domain string. certificate_id: str, a certificate id for the domain. no_certificate_id: bool, remove the certificate id from the domain. management_type: SslSettings.SslManagementTypeValueValuesEnum, AUTOMATIC or MANUAL certificate provisioning. Returns: The updated DomainMapping object. """ mask_fields = [] if certificate_id or no_certificate_id: mask_fields.append('sslSettings.certificateId') if management_type: mask_fields.append('sslSettings.sslManagementType') ssl = self.messages.SslSettings( certificateId=certificate_id, sslManagementType=management_type) domain_mapping = self.messages.DomainMapping(id=domain, sslSettings=ssl) if not mask_fields: raise exceptions.MinimumArgumentException( ['--[no-]certificate-id', '--no_managed_certificate'], 'Please specify at least one attribute to the domain-mapping update.') request = self.messages.AppengineAppsDomainMappingsPatchRequest( name=self._FormatDomainMapping(domain), domainMapping=domain_mapping, updateMask=','.join(mask_fields)) operation = self.client.apps_domainMappings.Patch(request) return operations_util.WaitForOperation(self.client.apps_operations, operation).response
def _SetAppEngineApplicationIap(self, enabled, oauth2_client_id=None, oauth2_client_secret=None): application = _GetApplication(self.project) api_client = appengine_api_client.AppengineApiClient.GetApiClient() iap_kwargs = _MakeIAPKwargs(False, application.iap, enabled, oauth2_client_id, oauth2_client_secret) application_update = api_client.messages.Application( iap=api_client.messages.IdentityAwareProxy(**iap_kwargs)) application = resources.REGISTRY.Parse( self.project, collection=APPENGINE_APPS_COLLECTION) update_request = api_client.messages.AppengineAppsPatchRequest( name=application.RelativeName(), application=application_update, updateMask='iap,') operation = api_client.client.apps.Patch(update_request) return operations_util.WaitForOperation(api_client.client.apps_operations, operation)
def DebugInstance(self, res, ssh_key=None): """Enable debugging of a Flexible instance. Args: res: A googleclousdk.core.Resource object. ssh_key: str, Public SSH key to add to the instance. Examples: `[USERNAME]:ssh-rsa [KEY_VALUE] [USERNAME]` , `[USERNAME]:ssh-rsa [KEY_VALUE] google-ssh {"userName":"******",` `"expireOn":"[EXPIRE_TIME]"}` For more information, see Adding and Removing SSH Keys (https://cloud.google.com/compute/docs/instances/adding-removing-ssh- keys). Returns: The completed Operation. """ request = self.messages.AppengineAppsServicesVersionsInstancesDebugRequest( name=res.RelativeName(), debugInstanceRequest=self.messages.DebugInstanceRequest(sshKey=ssh_key)) operation = self.client.apps_services_versions_instances.Debug(request) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def DeployService(self, service_name, version_id, service_config, manifest, image, endpoints_info=None): """Updates and deploys new app versions based on given config. Args: service_name: str, The service to deploy. version_id: str, The version of the service to deploy. service_config: AppInfoExternal, Service info parsed from a service yaml file. manifest: Dictionary mapping source files to Google Cloud Storage locations. image: The name of the container image. endpoints_info: EndpointsServiceInfo, Endpoints service info to be added to the AppInfoExternal configuration. Only provided when Endpoints API Management feature is enabled. Returns: A Version resource representing the deployed version. """ version_resource = self._CreateVersionResource(service_config, manifest, version_id, image, endpoints_info) create_request = self.messages.AppengineAppsServicesVersionsCreateRequest( parent=self._GetServiceRelativeName(service_name=service_name), version=version_resource) operation = requests.MakeRequest( self.client.apps_services_versions.Create, create_request) log.debug('Received operation: [{operation}]'.format( operation=operation.name)) return operations_util.WaitForOperation(self.client.apps_operations, operation)
def RepairApplication(self, progress_message=None): """Creates missing app resources. In particular, the Application.code_bucket GCS reference. Args: progress_message: str, the message to use while the operation is polled, if not the default. Returns: A long running operation. """ request = self.messages.AppengineAppsRepairRequest( name=self._FormatApp(), repairApplicationRequest=self.messages.RepairApplicationRequest()) operation = requests.MakeRequest(self.client.apps.Repair, request) log.debug('Received operation: [{operation}]'.format( operation=operation.name)) return operations_util.WaitForOperation( self.client.apps_operations, operation, message=progress_message)
def SetIngressTrafficAllowed(self, service_name, ingress_traffic_allowed): """Sets the ingress traffic allowed for a service. Args: service_name: str, The service name ingress_traffic_allowed: An IngressTrafficAllowed enum. Returns: The completed Operation. The Operation will contain a Service resource. """ network_settings = self.messages.NetworkSettings( ingressTrafficAllowed=ingress_traffic_allowed) update_service_request = self.messages.AppengineAppsServicesPatchRequest( name=self._GetServiceRelativeName(service_name=service_name), service=self.messages.Service(networkSettings=network_settings), updateMask='networkSettings') message = 'Setting ingress settings for service [{service}]'.format( service=service_name) operation = self.client.apps_services.Patch(update_service_request) return operations_util.WaitForOperation(self.client.apps_operations, operation, message=message)
def DeployService(self, service_name, version_id, service_config, manifest, build, extra_config_settings=None): """Updates and deploys new app versions. Args: service_name: str, The service to deploy. version_id: str, The version of the service to deploy. service_config: AppInfoExternal, Service info parsed from a service yaml file. manifest: Dictionary mapping source files to Google Cloud Storage locations. build: BuildArtifact, a wrapper which contains either the build ID for an in-progress parallel build, the name of the container image for a serial build, or the options for creating a build elsewhere. Not present during standard deploys. extra_config_settings: dict, client config settings to pass to the server as beta settings. Returns: The Admin API Operation, unfinished. """ operation = self._CreateVersion(service_name, version_id, service_config, manifest, build, extra_config_settings) message = 'Updating service [{service}]'.format(service=service_name) if util.Environment.IsFlexible(service_config.env): message += ' (this may take several minutes)' operation_metadata_type = self._ResolveMetadataType() # This indicates that a server-side build should be created. if build and build.IsBuildOptions(): if not operation_metadata_type: log.warning( 'Unable to determine build from Operation metadata. ' 'Skipping log streaming') else: # Poll the operation until the build is present. poller = operations_util.AppEngineOperationBuildPoller( self.client.apps_operations, operation_metadata_type) operation = operations_util.WaitForOperation( self.client.apps_operations, operation, message=message, poller=poller) build_id = operations_util.GetBuildFromOperation( operation, operation_metadata_type) if build_id: build = app_cloud_build.BuildArtifact.MakeBuildIdArtifact( build_id) if build and build.IsBuildId(): build_ref = resources.REGISTRY.Parse( build.identifier, params={'projectId': properties.VALUES.core.project.GetOrFail}, collection='cloudbuild.projects.builds') cloudbuild_logs.CloudBuildClient().Stream(build_ref) done_poller = operations_util.AppEngineOperationPoller( self.client.apps_operations, operation_metadata_type) return operations_util.WaitForOperation(self.client.apps_operations, operation, message=message, poller=done_poller)
def DeployService(self, service_name, version_id, service_config, manifest, build, extra_config_settings=None, service_account_email=None): """Updates and deploys new app versions. Args: service_name: str, The service to deploy. version_id: str, The version of the service to deploy. service_config: AppInfoExternal, Service info parsed from a service yaml file. manifest: Dictionary mapping source files to Google Cloud Storage locations. build: BuildArtifact, a wrapper which contains either the build ID for an in-progress parallel build, the name of the container image for a serial build, or the options for creating a build elsewhere. Not present during standard deploys. extra_config_settings: dict, client config settings to pass to the server as beta settings. service_account_email: Identity of this deployed version. If not set, the Admin API will fall back to use the App Engine default appspot service account. Returns: The Admin API Operation, unfinished. Raises: apitools_exceptions.HttpNotFoundError if build ID doesn't exist """ operation = self._CreateVersion(service_name, version_id, service_config, manifest, build, extra_config_settings, service_account_email) message = 'Updating service [{service}]'.format(service=service_name) if service_config.env in [env.FLEX, env.MANAGED_VMS]: message += ' (this may take several minutes)' operation_metadata_type = self._ResolveMetadataType() # This indicates that a server-side build should be created. if build and build.IsBuildOptions(): if not operation_metadata_type: log.warning( 'Unable to determine build from Operation metadata. ' 'Skipping log streaming') else: # Poll the operation until the build is present. poller = operations_util.AppEngineOperationBuildPoller( self.client.apps_operations, operation_metadata_type) operation = operations_util.WaitForOperation( self.client.apps_operations, operation, message=message, poller=poller) build_id = operations_util.GetBuildFromOperation( operation, operation_metadata_type) if build_id: build = app_cloud_build.BuildArtifact.MakeBuildIdArtifact( build_id) if build and build.IsBuildId(): try: build_ref = resources.REGISTRY.Parse( build.identifier, params={ 'projectId': properties.VALUES.core.project.GetOrFail }, collection='cloudbuild.projects.builds') cloudbuild_logs.CloudBuildClient().Stream(build_ref, out=log.status) except apitools_exceptions.HttpNotFoundError: region = util.ConvertToCloudRegion( self.GetApplication().locationId) build_ref = resources.REGISTRY.Create( collection='cloudbuild.projects.locations.builds', projectsId=properties.VALUES.core.project.GetOrFail, locationsId=region, buildsId=build.identifier) cloudbuild_logs.CloudBuildClient().Stream(build_ref, out=log.status) done_poller = operations_util.AppEngineOperationPoller( self.client.apps_operations, operation_metadata_type) return operations_util.WaitForOperation(self.client.apps_operations, operation, message=message, poller=done_poller)