# -*- coding: utf-8 -*- # # Copyright 2020 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Code for making shared messages between commands.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals from googlecloudsdk.api_lib.run import k8s_object from googlecloudsdk.core import log def GetSuccessMessageForMultiRegionSynchronousDeploy(service, regions): """Returns a user message for a successful synchronous deploy. Args: service: googlecloudsdk.api_lib.run.service.Service, Deployed service for which to build a success message. regions: list of regions that we deployed to. """ msg = ( 'Multi-Region Service [{{bold}}{s}{{reset}}] ' 'has been deployed to regions {{bold}}{r}{{reset}}.' '\nRegional URLs:' ).format( s=service.name, r=regions, ) for region in regions: condition = 'MultiRegionReady/' + region url = ( service.conditions[condition].get('message') if condition in service.conditions else '' ) msg += '\n{{bold}}{url}{{reset}} ({{bold}}{r}{{reset}})'.format( r=region, url=url ) return msg def GetSuccessMessageForSynchronousDeploy(service, no_traffic): """Returns a user message for a successful synchronous deploy. Args: service: googlecloudsdk.api_lib.run.service.Service, Deployed service for which to build a success message. no_traffic: bool, whether the service was deployed with --no-traffic flag. """ latest_ready = service.status.latestReadyRevisionName # Use lastCreatedRevisionName if --no-traffic is set. This was due to a bug # where the latestReadyRevisionName was not updated in time when traffic # update was not needed in reconciliation steps. latest_created = service.status.latestCreatedRevisionName latest_percent_traffic = 0 if no_traffic else service.latest_percent_traffic msg = ( 'Service [{{bold}}{serv}{{reset}}] ' 'revision [{{bold}}{rev}{{reset}}] ' 'has been deployed and is serving ' '{{bold}}{latest_percent_traffic}{{reset}} percent of traffic.' ) if latest_percent_traffic: msg += '\nService URL: {{bold}}{url}{{reset}}' latest_url = service.latest_url tag_url_message = '' if latest_url: tag_url_message = '\nThe revision can be reached directly at {}'.format( latest_url ) return ( msg.format( serv=service.name, rev=latest_created if no_traffic else latest_ready, url=service.domain, latest_percent_traffic=latest_percent_traffic, ) + tag_url_message ) def GetStartDeployMessage( conn_context, resource_ref, operation='Deploying container to', resource_kind_lower='service', ): """Returns a user mesage for starting a deploy. Args: conn_context: connection_context.ConnectionInfo, Metadata for the run API client. resource_ref: protorpc.messages.Message, A resource reference object for the resource. See googlecloudsdk.core.resources.Registry.ParseResourceId for details. operation: str, what deploy action is being done. resource_kind_lower: str, resource kind being deployed, e.g. "service" """ msg = ( '{operation} {operator} {resource_kind} ' '[{{bold}}{resource}{{reset}}] in {ns_label} [{{bold}}{ns}{{reset}}]' ) msg += conn_context.location_label # For WorkerPools case resource_ref.Parent().Name() returns the region name # which is not what we want. ns = ( resource_ref.projectsId if resource_kind_lower == 'worker pool' else resource_ref.Parent().Name() ) return msg.format( operation=operation, operator=conn_context.operator, resource_kind=resource_kind_lower, ns_label=conn_context.ns_label, resource=resource_ref.Name(), ns=ns, ) def GetNotFoundMessage(conn_context, resource_ref, resource_kind='Service'): """Returns a user mesage for resource not found. Args: conn_context: connection_context.ConnectionInfo, Metadata for the run API client. resource_ref: protorpc.messages.Message, A resource reference object for the resource. See googlecloudsdk.core.resources.Registry.ParseResourceId for details. resource_kind: str, resource kind, e.g. "Service" """ msg = ( '{resource_kind} [{resource}] could not be found' ' in {ns_label} [{ns}] region [{region}].' ) return msg.format( resource_kind=resource_kind, resource=resource_ref.Name(), ns_label=conn_context.ns_label, ns=resource_ref.Parent().Name(), region=conn_context.region, ) def GetRunJobMessage(release_track, job_name, repeat=False): """Returns a user message for how to run a job.""" return ( '\nTo execute this job{repeat}, use:\n' 'gcloud{release_track} run jobs execute {job_name}'.format( repeat=' again' if repeat else '', release_track=( ' {}'.format(release_track.prefix) if release_track.prefix is not None else '' ), job_name=job_name, ) ) def GetExecutionCreatedMessage(release_track, execution): """Returns a user message with execution details when running a job.""" msg = ( '\nView details about this execution by running:\n' 'gcloud{release_track} run jobs executions describe {execution_name}' ).format( release_track=( ' {}'.format(release_track.prefix) if release_track.prefix is not None else '' ), execution_name=execution.name, ) if execution.status and execution.status.logUri: msg += '\n\nOr visit ' + _GetExecutionUiLink(execution) return msg def _GetExecutionUiLink(execution): return ( 'https://console.cloud.google.com/run/jobs/executions/' 'details/{region}/{execution_name}?project={project}' ).format( region=execution.region, execution_name=execution.name, project=execution.namespace, ) def GetBuildEquivalentForSourceRunMessage(name, pack, source, subgroup=''): """Returns a user message for equivalent gcloud commands for source deploy. Args: name: name of the source target, which is either a service, a job or a worker pack: the pack arguments used to build the container image source: the location of the source subgroup: subgroup name for this command. Either 'jobs ', 'workers ' or empty for services """ build_flag = '--pack image=[IMAGE]' if pack else '--tag [IMAGE]' msg = ( 'This command is equivalent to running ' '`gcloud builds submit {build_flag} {source}` and ' '`gcloud run {subgroup}deploy {name} --image [IMAGE]`\n' ) return msg.format( name=name, build_flag=build_flag, source=source, subgroup=subgroup ) def MaybeLogDefaultGpuTypeMessage(args, resource): """Logs a user message for GPU type default value if it is not provided.""" gpu_set = 'gpu' in args and args.gpu gpu_type_set = 'gpu_type' in args and args.gpu_type if 'containers' in args and args.containers: for _, container_args in args.containers.items(): if 'gpu' in container_args and container_args.gpu: gpu_set = True has_gpu_type = ( resource and resource.template and resource.template.node_selector and resource.template.node_selector.get(k8s_object.GPU_TYPE_NODE_SELECTOR) ) if gpu_set and not gpu_type_set and not has_gpu_type: log.status.Print( 'No GPU type is provided, defaulting to nvidia-l4. To specify the' ' GPU type use --gpu-type.' ) def MaybeLogDefaultGpuTypeMessageForV2Resource(args, resource): """Logs a user message for GPU type default value if it is not provided.""" gpu_set = 'gpu' in args and args.gpu gpu_type_set = 'gpu_type' in args and args.gpu_type if 'containers' in args and args.containers: for _, container_args in args.containers.items(): if 'gpu' in container_args and container_args.gpu: gpu_set = True has_gpu_type = ( resource and resource.template and resource.template.node_selector and resource.template.node_selector.accelerator ) if gpu_set and not gpu_type_set and not has_gpu_type: log.status.Print( 'No GPU type is provided, defaulting to nvidia-l4. To specify the' ' GPU type use --gpu-type.' )