265 lines
8.8 KiB
Python
265 lines
8.8 KiB
Python
# -*- 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.'
|
|
)
|