feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""The gcloud run jobs group."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import platforms
from googlecloudsdk.core import properties
@base.UniverseCompatible
class Jobs(base.Group):
"""View and manage your Cloud Run jobs.
This set of commands can be used to view and manage your Cloud Run jobs.
"""
detailed_help = {
'EXAMPLES':
"""
To list your existing jobs, run:
$ {command} list
""",
}
@staticmethod
def Args(parser):
"""Adds --region flag."""
flags.AddRegionArg(parser)
def Filter(self, context, args):
"""Runs before command.Run and validates platform with passed args."""
# Ensures a platform is set on the run/platform property and
# all other passed args are valid for this platform and release track.
flags.GetAndValidatePlatform(args, self.ReleaseTrack(), flags.Product.RUN)
return context

View File

@@ -0,0 +1,33 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding to a Cloud Run job.
description: |
Add an IAM policy binding to the IAM policy of a Cloud Run job. One binding consists of a member,
and a role.
examples: |
To add an IAM policy binding for the role of 'roles/run.invoker' for the user 'test-user@gmail.com'
with job 'my-job' and region 'us-central1', run:
$ {command} my-job --region='us-central1' --member='user:test-user@gmail.com' --role='roles/run.invoker'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: run.projects.locations.jobs
modify_request_hooks:
- googlecloudsdk.command_lib.run.platforms:ValidatePlatformIsManaged
arguments:
resource:
help_text: The job for which to add IAM policy binding to.
spec: !REF googlecloudsdk.command_lib.run.resources:job
# The --region flag is specified at the group level, so don't try to add it here
removed_flags: ['region']
command_level_fallthroughs:
region:
- arg_name: 'region'
iam:
enable_condition: false

View File

@@ -0,0 +1,278 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Deploy a container to Cloud Run that will run to completion."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exceptions
from googlecloudsdk.calliope import parser_errors as c_parser_errors
from googlecloudsdk.command_lib.run import config_changes
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import container_parser
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import messages_util
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run import stages
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import log
from googlecloudsdk.core.console import progress_tracker
EXAMPLE_JOB_IMAGE = 'us-docker.pkg.dev/cloudrun/container/job:latest'
def ContainerArgGroup(release_track=base.ReleaseTrack.GA):
"""Returns an argument group with all per-container deploy args."""
help_text = """
Container Flags
If the --container is specified the following arguments may only be specified after a --container flag.
"""
group = base.ArgumentGroup(help=help_text)
# Verify image flag is specified in Run function for better error message.
group.AddArgument(flags.ImageArg(image=EXAMPLE_JOB_IMAGE, required=False))
group.AddArgument(flags.MutexEnvVarsFlags(release_track=release_track))
group.AddArgument(flags.MemoryFlag())
group.AddArgument(flags.CpuFlag())
group.AddArgument(flags.GpuFlag())
group.AddArgument(flags.ArgsFlag())
group.AddArgument(flags.SecretsFlags())
group.AddArgument(flags.CommandFlag())
group.AddArgument(flags.DependsOnFlag())
group.AddArgument(flags.AddVolumeMountFlag())
group.AddArgument(flags.RemoveVolumeMountFlag())
group.AddArgument(flags.ClearVolumeMountsFlag())
group.AddArgument(flags.StartupProbeFlag())
return group
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Create(base.Command):
"""Create a Cloud Run job."""
detailed_help = {
'DESCRIPTION': """\
Creates a new Cloud Run job.
""",
'EXAMPLES': """\
To deploy a new job `my-data-transformation` on Cloud Run:
$ {command} my-data-transformation --image=us-docker.pkg.dev/project/image
You may also omit the job name. Then a prompt will be displayed
with a suggested default value:
$ {command} --image=us-docker.pkg.dev/project/image
""",
}
@classmethod
def CommonArgs(cls, parser):
# Flags not specific to any platform
job_presentation = presentation_specs.ResourcePresentationSpec(
'JOB',
resource_args.GetJobResourceSpec(prompt=True),
'Job to create.',
required=True,
prefixes=False,
)
flags.AddLabelsFlag(parser)
flags.AddParallelismFlag(parser)
flags.AddTasksFlag(parser)
flags.AddMaxRetriesFlag(parser)
flags.AddTaskTimeoutFlags(parser)
flags.AddServiceAccountFlag(parser)
flags.AddSetCloudSQLFlag(parser)
flags.AddVpcConnectorArg(parser)
flags.AddVpcNetworkGroupFlagsForCreate(parser, resource_kind='job')
flags.AddEgressSettingsFlag(parser)
flags.AddClientNameAndVersionFlags(parser)
flags.AddBinAuthzPolicyFlags(parser, with_clear=False)
flags.AddBinAuthzBreakglassFlag(parser)
flags.AddCmekKeyFlag(parser, with_clear=False)
flags.AddSandboxArg(parser, hidden=True)
flags.AddGeneralAnnotationFlags(parser)
flags.AddVolumesFlags(parser, cls.ReleaseTrack())
flags.AddGpuTypeFlag(parser)
flags.GpuZonalRedundancyFlag(parser)
polling_group = parser.add_mutually_exclusive_group()
flags.AddAsyncFlag(polling_group)
execute_group = polling_group.add_argument_group(
help='--async cannot be used if executing the job after the update.'
)
flags.AddWaitForCompletionFlag(execute_group, implies_execute_now=True)
flags.AddExecuteNowFlag(execute_group)
concept_parsers.ConceptParser([job_presentation]).AddToParser(parser)
# No output by default, can be overridden by --format
parser.display_info.AddFormat('none')
@staticmethod
def Args(parser):
Create.CommonArgs(parser)
container_args = ContainerArgGroup()
container_parser.AddContainerFlags(parser, container_args)
def Run(self, args):
"""Deploy a Job to Cloud Run."""
if flags.FlagIsExplicitlySet(args, 'containers'):
containers = args.containers
if len(containers) > 10:
raise c_exceptions.InvalidArgumentException(
'--container', 'Jobs may include at most 10 containers'
)
needs_image = {
name: container
for name, container in containers.items()
if not container.IsSpecified('image')
}
if needs_image:
raise exceptions.RequiredImageArgumentException(needs_image)
elif not flags.FlagIsExplicitlySet(args, 'image'):
raise c_parser_errors.RequiredError(argument='--image')
job_ref = args.CONCEPTS.job.Parse()
flags.ValidateResource(job_ref)
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack()
)
changes = flags.GetJobConfigurationChanges(
args,
release_track=self.ReleaseTrack())
changes.append(
config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())
)
execute_now = args.execute_now or args.wait
messages_util.MaybeLogDefaultGpuTypeMessage(args, resource=None)
with serverless_operations.Connect(conn_context) as operations:
pretty_print.Info(
messages_util.GetStartDeployMessage(
conn_context, job_ref, 'Creating', 'job'
)
)
if execute_now:
header_msg = 'Creating and running job...'
else:
header_msg = 'Creating job...'
def _CreateJob(changes_):
with progress_tracker.StagedProgressTracker(
header_msg,
stages.JobStages(
execute_now=execute_now, include_completion=args.wait
),
failure_message='Job failed to deploy',
suppress_output=args.async_,
) as tracker:
execution_ = None
job_ = operations.CreateJob(
job_ref, changes_, tracker, asyn=(args.async_ and not execute_now)
)
if execute_now:
execution_ = operations.RunJob(
job_ref, tracker, args.wait, args.async_, self.ReleaseTrack()
)
return job_, execution_
try:
job, execution = _CreateJob(changes)
except exceptions.HttpError as e:
if flags.ShouldRetryNoZonalRedundancy(args, str(e)):
changes.append(
config_changes.GpuZonalRedundancyChange(
gpu_zonal_redundancy=False
)
)
job, execution = _CreateJob(changes)
else:
raise e
if args.async_ and not execute_now:
pretty_print.Success(
'Job [{{bold}}{job}{{reset}}] is being created '
'asynchronously.'.format(job=job.name)
)
else:
operation = 'been created'
if args.wait:
operation += ' and completed execution [{}]'.format(execution.name)
elif execute_now:
operation += ' and started running execution [{}]'.format(
execution.name
)
pretty_print.Success(
'Job [{{bold}}{job}{{reset}}] has successfully {operation}.'.format(
job=job.name, operation=operation
)
)
msg = ''
if execute_now:
msg += messages_util.GetExecutionCreatedMessage(
self.ReleaseTrack(), execution
)
msg += '\n'
msg += messages_util.GetRunJobMessage(
self.ReleaseTrack(), job.name, repeat=execute_now
)
log.status.Print(msg)
return job
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaCreate(Create):
"""Create a Cloud Run job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
container_args = ContainerArgGroup(release_track=base.ReleaseTrack.BETA)
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaCreate(BetaCreate):
"""Create a Cloud Run job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
flags.AddRuntimeFlag(parser)
flags.AddPriorityFlag(parser)
flags.AddDelayExecutionFlag(parser)
container_args = ContainerArgGroup(release_track=base.ReleaseTrack.ALPHA)
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)

View File

@@ -0,0 +1,103 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for deleting jobs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.run import execution
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import deletion
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import log
from googlecloudsdk.core.console import console_io
@base.UniverseCompatible
class Delete(base.Command):
"""Delete a job."""
detailed_help = {
'DESCRIPTION': """
{description}
""",
'EXAMPLES': """
To delete a job:
$ {command} job-name
""",
}
@staticmethod
def CommonArgs(parser):
job_presentation = presentation_specs.ResourcePresentationSpec(
'JOB',
resource_args.GetJobResourceSpec(),
'Job to delete.',
required=True,
prefixes=False,
)
flags.AddAsyncFlag(
parser, default_async_for_cluster=True
)
concept_parsers.ConceptParser([job_presentation]).AddToParser(parser)
@staticmethod
def Args(parser):
Delete.CommonArgs(parser)
def Run(self, args):
"""Delete a job."""
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack()
)
job_ref = args.CONCEPTS.job.Parse()
with serverless_operations.Connect(conn_context) as client:
message = 'Job [{}] will be deleted.'.format(job_ref.jobsId)
if console_io.CanPrompt() and self.HasRunningExecutions(job_ref, client):
message += (
' This job is currently executing; all running executions will be'
' stopped and deleted.'
)
console_io.PromptContinue(
message=message,
throw_if_unattended=True,
cancel_on_no=True,
)
deletion.Delete(job_ref, client.GetJob, client.DeleteJob, args.async_)
if args.async_:
pretty_print.Success('Job [{}] is being deleted.'.format(job_ref.jobsId))
else:
log.DeletedResource(job_ref.jobsId, 'job')
def HasRunningExecutions(self, job_ref, client):
# gcloud-disable-gdu-domain
label_selector = (
'{label} = {name}, run.googleapis.com/servingState = Active'.format(
label=execution.JOB_LABEL, name=job_ref.jobsId
)
)
for _ in client.ListExecutions(
job_ref.Parent(), label_selector, limit=1, page_size=1
):
return True
return False

View File

@@ -0,0 +1,402 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Deploy a container to Cloud Run that will run to completion."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import enum
import os.path
from googlecloudsdk.api_lib.run import api_enabler
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exceptions
from googlecloudsdk.command_lib.artifacts import docker_util
from googlecloudsdk.command_lib.run import artifact_registry
from googlecloudsdk.command_lib.run import config_changes
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import container_parser
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import messages_util
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run import stages
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.console import progress_tracker
class BuildType(enum.Enum):
DOCKERFILE = 'Dockerfile'
BUILDPACKS = 'Buildpacks'
def ContainerArgGroup(release_track=base.ReleaseTrack.GA):
"""Returns an argument group with all per-container deploy args."""
help_text = """
Container Flags
If the --container or --remove-containers flag is specified the following
arguments may only be specified after a --container flag.
"""
group = base.ArgumentGroup(help=help_text)
group.AddArgument(
flags.SourceAndImageFlags(
image='us-docker.pkg.dev/cloudrun/container/job:latest',
release_track=release_track,
)
)
group.AddArgument(flags.MutexEnvVarsFlags(release_track=release_track))
group.AddArgument(flags.MemoryFlag())
group.AddArgument(flags.CpuFlag())
group.AddArgument(flags.GpuFlag())
group.AddArgument(flags.ArgsFlag())
group.AddArgument(flags.SecretsFlags())
group.AddArgument(flags.CommandFlag())
group.AddArgument(flags.DependsOnFlag())
group.AddArgument(flags.AddVolumeMountFlag())
group.AddArgument(flags.RemoveVolumeMountFlag())
group.AddArgument(flags.ClearVolumeMountsFlag())
group.AddArgument(flags.StartupProbeFlag())
return group
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Deploy(base.Command):
"""Create or update a Cloud Run job."""
detailed_help = {
'DESCRIPTION': """\
Creates or updates a Cloud Run job.
""",
'EXAMPLES': """\
To deploy a new job `my-data-transformation` to Cloud Run:
$ {command} my-data-transformation --image=us-docker.pkg.dev/project/image
You may also omit the job name. Then a prompt will be displayed
with a suggested default value:
$ {command} --image=us-docker.pkg.dev/project/image
""",
}
@classmethod
def CommonArgs(cls, parser):
job_presentation = presentation_specs.ResourcePresentationSpec(
'JOB',
resource_args.GetJobResourceSpec(prompt=True),
'Job to deploy.',
required=True,
prefixes=False,
)
flags.AddLabelsFlag(parser)
flags.AddParallelismFlag(parser)
flags.AddTasksFlag(parser)
flags.AddMaxRetriesFlag(parser)
flags.AddTaskTimeoutFlags(parser)
flags.AddServiceAccountFlag(parser)
flags.AddSetCloudSQLFlag(parser)
flags.AddVpcConnectorArg(parser)
flags.AddVpcNetworkGroupFlagsForUpdate(parser, resource_kind='job')
flags.AddEgressSettingsFlag(parser)
flags.AddClientNameAndVersionFlags(parser)
flags.AddBinAuthzPolicyFlags(parser, with_clear=False)
flags.AddBinAuthzBreakglassFlag(parser)
flags.AddCmekKeyFlag(parser, with_clear=False)
flags.AddSandboxArg(parser, hidden=True)
flags.AddGeneralAnnotationFlags(parser)
flags.AddVolumesFlags(parser, cls.ReleaseTrack())
flags.AddGpuTypeFlag(parser)
flags.GpuZonalRedundancyFlag(parser)
polling_group = parser.add_mutually_exclusive_group()
flags.AddAsyncFlag(polling_group)
execute_group = polling_group.add_argument_group(
help='--async cannot be used if executing the job after the update.')
flags.AddWaitForCompletionFlag(execute_group, implies_execute_now=True)
flags.AddExecuteNowFlag(execute_group)
concept_parsers.ConceptParser([job_presentation]).AddToParser(parser)
# No output by default, can be overridden by --format
parser.display_info.AddFormat('none')
@staticmethod
def Args(parser):
Deploy.CommonArgs(parser)
container_args = ContainerArgGroup()
container_parser.AddContainerFlags(parser, container_args)
flags.RemoveContainersFlag().AddToParser(parser)
def Run(self, args):
"""Deploy a Job to Cloud Run."""
if flags.FlagIsExplicitlySet(args, 'containers'):
containers = args.containers
else:
containers = {'': args}
if len(containers) > 10:
raise c_exceptions.InvalidArgumentException(
'--container', 'Jobs may include at most 10 containers'
)
build_from_source = {
name: container
for name, container in containers.items()
if not container.IsSpecified('image')
}
if len(build_from_source) > 1:
needs_image = [
name
for name, container in build_from_source.items()
if not flags.FlagIsExplicitlySet(container, 'source')
]
if needs_image:
raise exceptions.RequiredImageArgumentException(needs_image)
raise c_exceptions.InvalidArgumentException(
'--container', 'At most one container can be deployed from source.'
)
for name, container in build_from_source.items():
if not flags.FlagIsExplicitlySet(container, 'source'):
if console_io.CanPrompt():
container.source = flags.PromptForDefaultSource(name)
else:
if name:
message = (
'Container {} requires a container image to deploy (e.g.'
' `us-docker.pkg.dev/cloudrun/container/job:latest`) if no'
' build source is provided.'.format(name)
)
else:
message = (
'Requires a container image to deploy (e.g.'
' `us-docker.pkg.dev/cloudrun/container/job:latest`) if no'
' build source is provided.'
)
raise c_exceptions.RequiredArgumentException(
'--image',
message,
)
required_apis = [api_enabler.get_run_api()]
if build_from_source:
required_apis.append('artifactregistry.googleapis.com')
required_apis.append('cloudbuild.googleapis.com')
already_activated_services = api_enabler.check_and_enable_apis(
properties.VALUES.core.project.Get(), required_apis
)
job_ref = args.CONCEPTS.job.Parse()
flags.ValidateResource(job_ref)
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack()
)
image = None
pack = None
source = None
operation_message = 'Deploying container to'
repo_to_create = None
# Build an image from source if source specified
if build_from_source:
# Only one container can deployed from source
container = next(iter(build_from_source.values()))
source = container.source
ar_repo = docker_util.DockerRepo(
project_id=properties.VALUES.core.project.Get(required=True),
location_id=artifact_registry.RepoRegion(args),
repo_id='cloud-run-source-deploy',
)
if artifact_registry.ShouldCreateRepository(
ar_repo, skip_activation_prompt=already_activated_services
):
repo_to_create = ar_repo
# The image is built with latest tag. After build, the image digest
# from the build result will be added to the image of the job spec.
container.image = '{repo}/{job}'.format(
repo=ar_repo.GetDockerString(), job=job_ref.jobsId
)
# Use GCP Buildpacks if Dockerfile doesn't exist
docker_file = source + '/Dockerfile'
if os.path.exists(docker_file):
build_type = BuildType.DOCKERFILE
else:
pack = [{'image': container.image}]
if self.ReleaseTrack() is base.ReleaseTrack.ALPHA:
command_arg = getattr(container, 'command', None)
if command_arg is not None:
command = ' '.join(command_arg)
pack[0].update(
{'env': 'GOOGLE_ENTRYPOINT="{command}"'.format(command=command)}
)
build_type = BuildType.BUILDPACKS
image = None if pack else container.image
operation_message = (
'Building using {build_type} and deploying container to'
).format(build_type=build_type.value)
pretty_print.Info(
messages_util.GetBuildEquivalentForSourceRunMessage(
job_ref.jobsId, pack, source, subgroup='jobs '
)
)
changes = flags.GetJobConfigurationChanges(
args,
release_track=self.ReleaseTrack())
changes.append(
config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())
)
execute_now = args.execute_now or args.wait
with serverless_operations.Connect(conn_context) as operations:
job_obj = operations.GetJob(job_ref)
messages_util.MaybeLogDefaultGpuTypeMessage(args, job_obj)
pretty_print.Info(
messages_util.GetStartDeployMessage(
conn_context, job_ref, operation_message, 'job'
)
)
operation = 'Creating' if job_obj is None else 'Updating'
if build_from_source and execute_now:
header_msg = 'Building, {} and running job...'.format(operation.lower())
elif build_from_source:
header_msg = 'Building and {} job...'.format(operation.lower())
elif execute_now:
header_msg = '{} and running job...'.format(operation)
else:
header_msg = '{} job...'.format(operation)
def _DeployJob(changes_):
with progress_tracker.StagedProgressTracker(
header_msg,
stages.JobStages(
execute_now=execute_now,
include_completion=args.wait,
include_build=bool(build_from_source),
include_create_repo=repo_to_create is not None,
),
failure_message='Job failed to deploy',
suppress_output=args.async_,
) as tracker:
job_ = operations.DeployJob(
job_ref,
changes_,
self.ReleaseTrack(),
tracker,
asyn=(args.async_ and not execute_now),
build_image=image,
build_pack=pack,
build_source=source,
repo_to_create=repo_to_create,
prefetch=job_obj,
already_activated_services=already_activated_services,
)
execution_ = None
if execute_now:
execution_ = operations.RunJob(
job_ref, tracker, args.wait, args.async_, self.ReleaseTrack()
)
return job_, execution_
try:
job, execution = _DeployJob(changes)
except exceptions.HttpError as e:
if flags.ShouldRetryNoZonalRedundancy(args, str(e)):
changes.append(
config_changes.GpuZonalRedundancyChange(
gpu_zonal_redundancy=False
)
)
job, execution = _DeployJob(changes)
else:
raise e
if args.async_ and not execute_now:
pretty_print.Success(
'Job [{{bold}}{job}{{reset}}] is being deployed '
'asynchronously.'.format(job=job.name)
)
else:
operation = 'been deployed'
if args.wait:
operation += ' and completed execution [{}]'.format(execution.name)
elif execute_now:
operation += ' and started running execution [{}]'.format(
execution.name
)
pretty_print.Success(
'Job [{{bold}}{job}{{reset}}] has successfully {operation}.'.format(
job=job.name, operation=operation
)
)
msg = ''
if execute_now:
msg += messages_util.GetExecutionCreatedMessage(
self.ReleaseTrack(), execution
)
msg += '\n'
msg += messages_util.GetRunJobMessage(
self.ReleaseTrack(), job.name, repeat=execute_now
)
log.status.Print(msg)
return job
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaDeploy(Deploy):
"""Create or update a Cloud Run job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
container_args = ContainerArgGroup(release_track=base.ReleaseTrack.BETA)
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)
flags.RemoveContainersFlag().AddToParser(parser)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaDeploy(BetaDeploy):
"""Create or update a Cloud Run job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
container_args = ContainerArgGroup(release_track=base.ReleaseTrack.ALPHA)
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)
flags.RemoveContainersFlag().AddToParser(parser)
flags.AddRuntimeFlag(parser)
flags.AddPriorityFlag(parser)
flags.AddDelayExecutionFlag(parser)

View File

@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for obtaining details about jobs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run.printers import export_printer
from googlecloudsdk.command_lib.run.printers import job_printer
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core.resource import resource_printer
class Describe(base.DescribeCommand):
"""Obtain details about jobs."""
detailed_help = {
'DESCRIPTION':
"""
{description}
""",
'EXAMPLES':
"""
To describe a job:
$ {command} my-job
""",
}
@staticmethod
def CommonArgs(parser):
job_presentation = presentation_specs.ResourcePresentationSpec(
'JOB',
resource_args.GetJobResourceSpec(),
'Job to describe.',
required=True,
prefixes=False)
concept_parsers.ConceptParser([job_presentation]).AddToParser(parser)
resource_printer.RegisterFormatter(
job_printer.JOB_PRINTER_FORMAT, job_printer.JobPrinter)
parser.display_info.AddFormat(job_printer.JOB_PRINTER_FORMAT)
resource_printer.RegisterFormatter(
export_printer.EXPORT_PRINTER_FORMAT,
export_printer.ExportPrinter)
@staticmethod
def Args(parser):
Describe.CommonArgs(parser)
def Run(self, args):
"""Show details about a job execution."""
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack(), version_override='v1')
job_ref = args.CONCEPTS.job.Parse()
with serverless_operations.Connect(conn_context) as client:
job = client.GetJob(job_ref)
if not job:
raise exceptions.ArgumentError('Cannot find job [{}].'.format(
job_ref.Name()))
return job

View File

@@ -0,0 +1,221 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for running jobs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import container_parser
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import messages_util
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run import stages
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import log
from googlecloudsdk.core.console import progress_tracker
def ContainerOverridesGroup():
"""Returns an argument group with all per-container args for overrides."""
help_text = """
Container Flags
If the --container is specified the following arguments may only be specified after a --container flag.
"""
group = base.ArgumentGroup(help=help_text)
group.AddArgument(flags.ArgsFlag(for_execution_overrides=True))
group.AddArgument(flags.OverrideEnvVarsFlag())
return group
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Execute(base.Command):
"""Execute a job."""
detailed_help = {
'DESCRIPTION': """
{description}
""",
'EXAMPLES': """
To execute a job:
$ {command} my-job
""",
}
container_flags_text = '`--update-env-vars`, `--args`'
@classmethod
def CommonArgs(cls, parser):
job_presentation = presentation_specs.ResourcePresentationSpec(
'JOB',
resource_args.GetJobResourceSpec(prompt=True),
'Job to execute.',
required=True,
prefixes=False,
)
concept_parsers.ConceptParser([job_presentation]).AddToParser(parser)
polling_group = parser.add_mutually_exclusive_group()
flags.AddAsyncFlag(polling_group)
flags.AddWaitForCompletionFlag(polling_group)
# No output by default, can be overridden by --format
parser.display_info.AddFormat('none')
flags.AddTaskTimeoutFlags(parser, for_execution_overrides=True)
flags.AddTasksFlag(parser, for_execution_overrides=True)
@staticmethod
def Args(parser):
Execute.CommonArgs(parser)
container_args = ContainerOverridesGroup()
container_parser.AddContainerFlags(parser, container_args)
def _MakeContainerOverrde(self, operations, args, container_name=None):
# If args list has been explicitly set as an empty list,
# this is to clear out the existing args list.
clear_args = flags.FlagIsExplicitlySet(args, 'args') and not args.args
return operations.MakeContainerOverride(
name=container_name,
update_env_vars=args.update_env_vars,
args=args.args,
clear_args=clear_args,
)
def _AssertContainerOverrides(self, args):
if flags.FlagIsExplicitlySet(args, 'containers'):
for container_name, container_args in args.containers.items():
if not flags.FlagIsExplicitlySet(
container_args, 'args'
) and not flags.FlagIsExplicitlySet(container_args, 'update_env_vars'):
raise exceptions.NoConfigurationChangeError(
'No container overrides requested to container `{}`. '
'Did you mean to include the flags {} after `--container` flag?'
.format(container_name, self.container_flags_text)
)
def Run(self, args):
"""Execute a Job on Cloud Run."""
job_ref = args.CONCEPTS.job.Parse()
flags.ValidateResource(job_ref)
self._AssertContainerOverrides(args)
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack()
)
with serverless_operations.Connect(conn_context) as operations:
with progress_tracker.StagedProgressTracker(
'Creating execution...',
stages.ExecutionStages(include_completion=args.wait),
failure_message='Executing job failed',
suppress_output=args.async_,
) as tracker:
overrides = None
if flags.HasExecutionOverrides(args):
operations.ValidateConfigOverrides(
job_ref, flags.GetExecutionOverridesChangesForValidation(args)
)
container_overrides = []
if flags.HasContainerOverrides(args):
if flags.HasTopLevelContainerOverride(args):
container_overrides.append(
self._MakeContainerOverrde(operations, args)
)
if flags.FlagIsExplicitlySet(args, 'containers'):
for container_name, container_args in args.containers.items():
container_overrides.append(
self._MakeContainerOverrde(
operations, container_args, container_name
)
)
tier = (
args.priority_tier
if flags.FlagIsExplicitlySet(args, 'priority_tier')
else 'PRIORITY_TIER_UNSPECIFIED'
)
delay_execution = (
args.delay_execution
if flags.FlagIsExplicitlySet(args, 'delay_execution')
else False
)
overrides = operations.GetExecutionOverrides(
args.tasks,
args.task_timeout,
tier,
delay_execution,
container_overrides,
)
e = operations.RunJob(
job_ref,
tracker,
args.wait,
args.async_,
self.ReleaseTrack(),
overrides
)
if args.async_:
pretty_print.Success(
'Execution [{{bold}}{execution}{{reset}}] is being'
' started asynchronously.'.format(execution=e.name)
)
else:
operation = 'completed' if args.wait else 'started running'
pretty_print.Success(
'Execution [{{bold}}{execution}{{reset}}] has '
'successfully {operation}.'.format(
execution=e.name, operation=operation
)
)
log.status.Print(
messages_util.GetExecutionCreatedMessage(self.ReleaseTrack(), e)
)
return e
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaExecute(Execute):
"""Execute a job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
container_args = ContainerOverridesGroup()
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaExecute(BetaExecute):
"""Execute a job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
flags.AddPriorityFlag(parser)
flags.AddDelayExecutionFlag(parser)
container_args = ContainerOverridesGroup()
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""The gcloud run executions group."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import platforms
from googlecloudsdk.core import properties
@base.UniverseCompatible
class Executions(base.Group):
"""View and manage your Cloud Run jobs executions.
This set of commands can be used to view and manage your Cloud Run jobs
executions.
"""
detailed_help = {
'EXAMPLES':
"""
To list your executions for a job, run:
$ {command} list --job=my-job
""",
}

View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for canceling executions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import cancellation
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core.console import console_io
@base.UniverseCompatible
class Cancel(base.Command):
"""Cancel an execution."""
detailed_help = {
'DESCRIPTION': """
{description}
""",
'EXAMPLES': """
To cancel an execution:
$ {command} my-execution
""",
}
@staticmethod
def CommonArgs(parser):
execution_presentation = presentation_specs.ResourcePresentationSpec(
'EXECUTION',
resource_args.GetExecutionResourceSpec(),
'Execution to cancel.',
required=True,
prefixes=False,
)
flags.AddAsyncFlag(
parser, default_async_for_cluster=True
)
concept_parsers.ConceptParser([execution_presentation]).AddToParser(parser)
@staticmethod
def Args(parser):
Cancel.CommonArgs(parser)
def Run(self, args):
"""Cancel an execution."""
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack()
)
ex_ref = args.CONCEPTS.execution.Parse()
console_io.PromptContinue(
message='Execution [{}] will be cancelled.'.format(ex_ref.executionsId),
throw_if_unattended=True,
cancel_on_no=True,
)
with serverless_operations.Connect(conn_context) as client:
cancellation.Cancel(
ex_ref, client.GetExecution, client.CancelExecution, args.async_
)
if args.async_:
pretty_print.Success(
'Execution [{}] is being cancelled.'.format(ex_ref.executionsId)
)
else:
pretty_print.Success(
'Cancelled execution [{}].'.format(ex_ref.executionsId)
)

View File

@@ -0,0 +1,89 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for deleting executions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import deletion
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import log
from googlecloudsdk.core.console import console_io
@base.UniverseCompatible
class Delete(base.Command):
"""Delete an execution."""
detailed_help = {
'DESCRIPTION': """
{description}
""",
'EXAMPLES': """
To delete an execution:
$ {command} my-execution
""",
}
@staticmethod
def CommonArgs(parser):
execution_presentation = presentation_specs.ResourcePresentationSpec(
'EXECUTION',
resource_args.GetExecutionResourceSpec(),
'Execution to delete.',
required=True,
prefixes=False,
)
flags.AddAsyncFlag(
parser, default_async_for_cluster=True
)
concept_parsers.ConceptParser([execution_presentation]).AddToParser(parser)
@staticmethod
def Args(parser):
Delete.CommonArgs(parser)
def Run(self, args):
"""Delete an execution."""
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack()
)
ex_ref = args.CONCEPTS.execution.Parse()
console_io.PromptContinue(
message='Execution [{}] will be deleted.'.format(ex_ref.executionsId),
throw_if_unattended=True,
cancel_on_no=True,
)
with serverless_operations.Connect(conn_context) as client:
deletion.Delete(
ex_ref, client.GetExecution, client.DeleteExecution, args.async_
)
if args.async_:
pretty_print.Success(
'Execution [{}] is being deleted.'.format(ex_ref.executionsId)
)
else:
log.DeletedResource(ex_ref.executionsId, 'execution')

View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for obtaining details about executions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run.printers import export_printer
from googlecloudsdk.command_lib.run.printers import job_printer
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core.resource import resource_printer
class Describe(base.DescribeCommand):
"""Obtain details about executions."""
detailed_help = {
'DESCRIPTION':
"""
{description}
""",
'EXAMPLES':
"""
To describe an execution:
$ {command} my-execution
""",
}
@staticmethod
def CommonArgs(parser):
execution_presentation = presentation_specs.ResourcePresentationSpec(
'EXECUTION',
resource_args.GetExecutionResourceSpec(),
'Execution to describe.',
required=True,
prefixes=False)
concept_parsers.ConceptParser([execution_presentation]).AddToParser(parser)
resource_printer.RegisterFormatter(
job_printer.EXECUTION_PRINTER_FORMAT,
job_printer.ExecutionPrinter,
)
parser.display_info.AddFormat(job_printer.EXECUTION_PRINTER_FORMAT)
resource_printer.RegisterFormatter(
export_printer.EXPORT_PRINTER_FORMAT,
export_printer.ExportPrinter,
)
@staticmethod
def Args(parser):
Describe.CommonArgs(parser)
def Run(self, args):
"""Show details about a job execution."""
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack(), version_override='v1')
execution_ref = args.CONCEPTS.execution.Parse()
with serverless_operations.Connect(conn_context) as client:
execution = client.GetExecution(execution_ref)
if not execution:
raise exceptions.ArgumentError('Cannot find execution [{}].'.format(
execution_ref.Name()))
return execution

View File

@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for listing job executions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.run import execution
from googlecloudsdk.command_lib.run import commands
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
def _SucceededStatus(ex):
return '{} / {}'.format(
ex.get('status', {}).get('succeededCount', 0),
ex.get('spec', {}).get('taskCount', 0),
)
def _ByStartAndCreationTime(ex):
"""Sort key that sorts executions by start time, newest and unstarted first.
All unstarted executions will be first and sorted by their creation timestamp,
all started executions will be second and sorted by their start time.
Args:
ex: googlecloudsdk.api_lib.run.execution.Execution
Returns:
The lastTransitionTime of the Started condition or the creation timestamp of
the execution if the execution is unstarted.
"""
return (
False
if ex.started_condition and ex.started_condition['status'] is not None
else True,
ex.started_condition['lastTransitionTime']
if ex.started_condition and ex.started_condition['lastTransitionTime']
else ex.creation_timestamp,
)
class List(commands.List):
"""List executions."""
detailed_help = {
'DESCRIPTION':
"""
{description}
""",
'EXAMPLES':
"""
To list all executions in all regions:
$ {command}
""",
}
@classmethod
def CommonArgs(cls, parser):
namespace_presentation = presentation_specs.ResourcePresentationSpec(
'--namespace',
resource_args.GetNamespaceResourceSpec(),
'Namespace to list executions in.',
required=True,
prefixes=False,
hidden=True,
)
flags.AddJobFlag(parser)
concept_parsers.ConceptParser([namespace_presentation]).AddToParser(parser)
parser.display_info.AddFormat(
'table('
'{ready_column},'
'job_name:label=JOB,'
'name:label=EXECUTION,'
'region:label=REGION,'
'status.runningCount.yesno(no="0"):label=RUNNING,'
'succeeded_status():label=COMPLETE,'
'creation_timestamp.date("%Y-%m-%d %H:%M:%S %Z"):label=CREATED,'
'author:label="RUN BY"):({alias})'.format(
ready_column=pretty_print.READY_COLUMN,
alias=commands.SATISFIES_PZS_ALIAS,
)
)
parser.display_info.AddUriFunc(cls._GetResourceUri)
parser.display_info.AddTransforms({
'succeeded_status': _SucceededStatus,
})
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
def _SortExecutions(self, executions):
return sorted(
commands.SortByName(executions),
key=_ByStartAndCreationTime,
reverse=True)
def Run(self, args):
"""List executions of a job."""
job_name = args.job
namespace_ref = args.CONCEPTS.namespace.Parse()
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack())
with serverless_operations.Connect(conn_context) as client:
self.SetCompleteApiEndpoint(conn_context.endpoint)
label_selector = None
if job_name:
label_selector = '{label} = {name}'.format(
label=execution.JOB_LABEL, name=job_name
)
return self._SortExecutions(
client.ListExecutions(namespace_ref, label_selector)
)

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Group definition for logs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import platforms
from googlecloudsdk.core import properties
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Logs(base.Group):
"""Read logs for Cloud Run jobs executions."""
detailed_help = {
'EXAMPLES':
"""
To tail logs for a job execution, run:
$ {command} tail my-job-execution
To read logs for a job execution, run:
$ {command} read my-job-execution
""",
}

View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command to read logs for a job execution."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.api_lib.logging import common
from googlecloudsdk.api_lib.logging.formatter import FormatLog
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.logs import read as read_logs_lib
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.core import log
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Read(base.Command):
"""Read logs for Cloud Run job executions."""
detailed_help = {
'DESCRIPTION': """\
{command} reads log entries. Log entries matching *--log-filter* are
returned according to the specified --order.
If the log entries come from multiple logs, then entries from
different logs might be intermingled in the results.
""",
'EXAMPLES': """\
To read log entries from for a Cloud Run job execution, run:
$ {command} my-job-execution
To read log entries with severity ERROR or higher, run:
$ {command} my-job-execution --log-filter="severity>=ERROR"
To read log entries written in a specific time window, run:
$ {command} my-job-execution --log-filter='timestamp<="2015-05-31T23:59:59Z" AND timestamp>="2015-05-31T00:00:00Z"'
To read up to 10 log entries in your job payloads that include the
word `SearchText` and format the output in `JSON` format, run:
$ {command} my-job-execution --log-filter="textPayload:SearchText" --limit=10 --format=json
Detailed information about filters can be found at:
[](https://cloud.google.com/logging/docs/view/advanced_filters)
""",
}
@staticmethod
def Args(parser):
parser.add_argument('execution', help='Name for a Cloud Run job execution.')
read_logs_lib.LogFilterArgs(parser)
read_logs_lib.LoggingReadArgs(parser)
def Run(self, args):
filters = [args.log_filter] if args.IsSpecified('log_filter') else []
filters.append('resource.type = %s \n' % 'cloud_run_job')
filters.append(
'labels."run.googleapis.com/execution_name" = %s \n'
% args.execution
)
filters.append(
'resource.labels.location = %s \n' % flags.GetRegion(args, prompt=True)
)
filters.append('severity >= DEFAULT \n')
filters += read_logs_lib.MakeTimestampFilters(args)
lines = []
logs = common.FetchLogs(
read_logs_lib.JoinFilters(filters),
order_by=args.order,
limit=args.limit,
)
for log_line in logs:
output_log = FormatLog(log_line)
if output_log:
lines.append(output_log)
for line in reversed(lines):
log.out.Print(line)

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Command to tail logs for a job execution."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.logs import read as read_logs_lib
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import streaming
from googlecloudsdk.core import properties
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Tail(base.BinaryBackedCommand):
"""Tail logs for Cloud Run job executions."""
detailed_help = {
'DESCRIPTION': """\
{command} tails log-entries for a particular
Cloud Run job exeuction in real time. The log entries are formatted for
consumption in a terminal.
""",
'EXAMPLES': """\
To tail log entries for a Cloud Run job exeuction, run:
$ {command} my-job-execution
To tail log entries with severity ERROR or higher, run:
$ {command} my-job-execution --log-filter="severity>=ERROR"
Detailed information about filters can be found at:
[](https://cloud.google.com/logging/docs/view/advanced_filters)
""",
}
@staticmethod
def Args(parser):
parser.add_argument('execution', help='Name for a Cloud Run job.')
read_logs_lib.LogFilterArgs(parser)
def Run(self, args):
filters = []
if args.IsSpecified('log_filter'):
filters.append(args.log_filter)
filters.append('resource.type=%s' % 'cloud_run_job')
filters.append(
'labels."run.googleapis.com/execution_name"=%s' % args.execution
)
filters.append(
'resource.labels.location=%s' % flags.GetRegion(args, prompt=True)
)
filters.append('severity>=DEFAULT')
project_id = properties.VALUES.core.project.Get(required=True)
filter_str = ' '.join(filters)
command_executor = streaming.LogStreamingWrapper()
response = command_executor(
project_id=project_id, log_format='run', log_filter=filter_str
)
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""The gcloud run tasks group."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import platforms
from googlecloudsdk.core import properties
@base.UniverseCompatible
class Tasks(base.Group):
"""View and manage your Cloud Run jobs tasks.
This set of commands can be used to view and manage your Cloud Run jobs
tasks.
"""
detailed_help = {
'EXAMPLES':
"""
To list your existing tasks for an execution, run:
$ {command} list --execution my_execution
""",
}

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for obtaining details about tasks."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run.printers import export_printer
from googlecloudsdk.command_lib.run.printers import job_printer
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core.resource import resource_printer
class Describe(base.DescribeCommand):
"""Obtain details about tasks."""
detailed_help = {
'DESCRIPTION':
"""
{description}
""",
'EXAMPLES':
"""
To describe a task:
$ {command} my-task
""",
}
@staticmethod
def CommonArgs(parser):
task_presentation = presentation_specs.ResourcePresentationSpec(
'TASK',
resource_args.GetTaskResourceSpec(),
'Task to describe.',
required=True,
prefixes=False)
concept_parsers.ConceptParser([task_presentation]).AddToParser(parser)
resource_printer.RegisterFormatter(
job_printer.TASK_PRINTER_FORMAT,
job_printer.TaskPrinter)
parser.display_info.AddFormat(job_printer.TASK_PRINTER_FORMAT)
@staticmethod
def Args(parser):
Describe.CommonArgs(parser)
def Run(self, args):
"""Show details about a job task."""
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack(), version_override='v1')
task_ref = args.CONCEPTS.task.Parse()
with serverless_operations.Connect(conn_context) as client:
task = client.GetTask(task_ref)
if not task:
raise exceptions.ArgumentError('Cannot find task [{}].'.format(
task_ref.Name()))
return task

View File

@@ -0,0 +1,85 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for listing job tasks."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.command_lib.run import commands
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
class List(commands.List):
"""List tasks."""
detailed_help = {
'DESCRIPTION':
"""
{description}
""",
'EXAMPLES':
"""
To list all tasks for an execution:
$ {command} --execution=my-execution
""",
}
@classmethod
def CommonArgs(cls, parser):
execution_presentation = presentation_specs.ResourcePresentationSpec(
'--execution',
resource_args.GetExecutionResourceSpec(),
'Execution for which to list tasks.',
required=True,
prefixes=False)
concept_parsers.ConceptParser([execution_presentation]).AddToParser(parser)
flags.AddTaskFilterFlags(parser)
parser.display_info.AddFormat(
'table('
'{ready_column},'
'index,'
'running_state:label=STATE,'
'name:label=TASK,'
'start_time.date("%Y-%m-%d %H:%M:%S %Z"):label=STARTED,'
'completion_time.date("%Y-%m-%d %H:%M:%S %Z"):label=COMPLETED,'
'retries):({alias})'.format(
ready_column=pretty_print.READY_COLUMN,
alias=commands.SATISFIES_PZS_ALIAS,
)
)
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
def Run(self, args):
"""List tasks of a job execution."""
execution_ref = args.CONCEPTS.execution.Parse()
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack())
with serverless_operations.Connect(conn_context) as client:
self.SetCompleteApiEndpoint(conn_context.endpoint)
ret = client.ListTasks(execution_ref.Parent(), execution_ref.Name(),
args.filter_flags or None)
return sorted(ret, key=lambda x: x.index)

View File

@@ -0,0 +1,30 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Get the IAM policy for a Cloud Run job.
description: |
This command gets the IAM policy for a job. If
formatted as JSON, the output can be edited and used as a policy
file for *set-iam-policy*. The output includes an "etag" field
identifying the version emitted and allowing detection of
concurrent policy updates; see
$ gcloud alpha run registries set-iam-policy for additional details.
examples: |
To print the IAM policy for a given job, run:
$ {command} --region=us-central1 my-job
request:
collection: run.projects.locations.jobs
modify_request_hooks:
- googlecloudsdk.command_lib.run.platforms:ValidatePlatformIsManaged
arguments:
resource:
help_text: The job for which to display the IAM policy.
spec: !REF googlecloudsdk.command_lib.run.resources:job
# The --region flag is specified at the group level, so don't try to add it here
removed_flags: ['region']
command_level_fallthroughs:
region:
- arg_name: 'region'

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for listing Jobs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.run import global_methods
from googlecloudsdk.command_lib.run import commands
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
class List(commands.List):
"""List jobs."""
detailed_help = {
'DESCRIPTION':
"""
{description}
""",
'EXAMPLES':
"""
To list all jobs in all regions:
$ {command}
""",
}
@classmethod
def CommonArgs(cls, parser):
namespace_presentation = presentation_specs.ResourcePresentationSpec(
'--namespace',
resource_args.GetNamespaceResourceSpec(),
'Namespace to list jobs in.',
required=True,
prefixes=False,
hidden=True,
)
concept_parsers.ConceptParser([namespace_presentation]).AddToParser(parser)
parser.display_info.AddFormat(
'table('
'{ready_column},'
'name:label=JOB,'
'region:label=REGION,'
'status.latestCreatedExecution.creationTimestamp'
'.date("%Y-%m-%d %H:%M:%S %Z"):label="LAST RUN AT",'
'creation_timestamp.date("%Y-%m-%d %H:%M:%S %Z"):label=CREATED,'
'author:label="CREATED BY"):({alias})'.format(
ready_column=pretty_print.READY_COLUMN,
alias=commands.SATISFIES_PZS_ALIAS,
)
)
parser.display_info.AddUriFunc(cls._GetResourceUri)
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
def Run(self, args):
"""List jobs."""
# Use the mixer for global request if there's no --region flag.
namespace_ref = args.CONCEPTS.namespace.Parse()
if not args.IsSpecified('region'):
client = global_methods.GetServerlessClientInstance(api_version='v1')
self.SetPartialApiEndpoint(client.url)
# Don't consider region property here, we'll default to all regions
return commands.SortByName(global_methods.ListJobs(client, namespace_ref))
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack())
with serverless_operations.Connect(conn_context) as client:
self.SetCompleteApiEndpoint(conn_context.endpoint)
return commands.SortByName(client.ListJobs(namespace_ref))

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Group definition for logs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import platforms
from googlecloudsdk.core import properties
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA
)
class Logs(base.Group):
"""Read logs for Cloud Run jobs."""
detailed_help = {
'EXAMPLES': """
To tail logs executions for a job, run:
$ {command} tail my-job
To read logs executions for a job, run:
$ {command} read my-job
""",
}
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class GaLogs(base.Group):
"""Read logs for Cloud Run jobs."""
detailed_help = {
'EXAMPLES': """
To read logs executions for a job, run:
$ {command} read my-job
""",
}

View File

@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command to read logs for a job."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.api_lib.logging import common
from googlecloudsdk.api_lib.logging.formatter import FormatLog
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.logs import read as read_logs_lib
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.core import log
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Read(base.Command):
"""Read logs for Cloud Run jobs."""
detailed_help = {
'DESCRIPTION': """\
{command} reads log entries. Log entries matching *--log-filter* are
returned according to the specified --order.
If the log entries come from multiple logs, then entries from
different logs might be intermingled in the results.
""",
'EXAMPLES': """\
To read log entries from for a Cloud Run job, run:
$ {command} my-job
To read log entries with severity ERROR or higher, run:
$ {command} my-job --log-filter="severity>=ERROR"
To read log entries written in a specific time window, run:
$ {command} my-job --log-filter='timestamp<="2015-05-31T23:59:59Z" AND timestamp>="2015-05-31T00:00:00Z"'
To read up to 10 log entries in your job payloads that include the
word `SearchText` and format the output in `JSON` format, run:
$ {command} my-job --log-filter="textPayload:SearchText" --limit=10 --format=json
Detailed information about filters can be found at:
[](https://cloud.google.com/logging/docs/view/advanced_filters)
""",
}
@staticmethod
def Args(parser):
parser.add_argument('job', help='Name for a Cloud Run job.')
read_logs_lib.LogFilterArgs(parser)
read_logs_lib.LoggingReadArgs(parser)
def Run(self, args):
filters = [args.log_filter] if args.IsSpecified('log_filter') else []
filters.append('resource.type = %s \n' % 'cloud_run_job')
filters.append('resource.labels.job_name = %s \n' % args.job)
filters.append('resource.labels.location = %s \n' %
flags.GetRegion(args, prompt=True))
filters.append('severity >= DEFAULT \n')
filters += read_logs_lib.MakeTimestampFilters(args)
lines = []
logs = common.FetchLogs(
read_logs_lib.JoinFilters(filters),
order_by=args.order,
limit=args.limit)
for log_line in logs:
output_log = FormatLog(log_line)
if output_log:
lines.append(output_log)
for line in reversed(lines):
log.out.Print(line)

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command to tail logs for a job."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.logs import read as read_logs_lib
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import streaming
from googlecloudsdk.core import properties
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class Tail(base.BinaryBackedCommand):
"""Tail logs for Cloud Run jobs."""
detailed_help = {
'DESCRIPTION': """\
{command} tails log-entries for a particular
Cloud Run job in real time. The log entries are formatted for
consumption in a terminal.
""",
'EXAMPLES': """\
To tail log entries for a Cloud Run job, run:
$ {command} my-job
To tail log entries with severity ERROR or higher, run:
$ {command} my-job --log-filter="severity>=ERROR"
Detailed information about filters can be found at:
[](https://cloud.google.com/logging/docs/view/advanced_filters)
""",
}
@staticmethod
def Args(parser):
parser.add_argument('job', help='Name for a Cloud Run job.')
read_logs_lib.LogFilterArgs(parser)
def Run(self, args):
filters = []
if args.IsSpecified('log_filter'):
filters.append(args.log_filter)
filters.append('resource.type=%s' % 'cloud_run_job')
filters.append('resource.labels.job_name=%s' % args.job)
filters.append('resource.labels.location=%s' %
flags.GetRegion(args, prompt=True))
filters.append('severity>=DEFAULT')
project_id = properties.VALUES.core.project.Get(required=True)
filter_str = ' '.join(filters)
command_executor = streaming.LogStreamingWrapper()
response = command_executor(
project_id=project_id, log_format='run', log_filter=filter_str)
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,33 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding of a Cloud Run job.
description: |
Remove an IAM policy binding from the IAM policy of a job. One binding consists of a member,
and a role.
examples: |
To remove an IAM policy binding for the role of 'roles/run.invoker' for the user 'test-user@gmail.com'
with servcie 'my-job' and region 'us-central1', run:
$ {command} my-job --region='us-central1' --member='user:test-user@gmail.com' --role='roles/run.invoker'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: run.projects.locations.jobs
modify_request_hooks:
- googlecloudsdk.command_lib.run.platforms:ValidatePlatformIsManaged
arguments:
resource:
help_text: The job for which to remove the IAM policy binding.
spec: !REF googlecloudsdk.command_lib.run.resources:job
# The --region flag is specified at the group level, so don't try to add it here
removed_flags: ['region']
command_level_fallthroughs:
region:
- arg_name: 'region'
iam:
enable_condition: false

View File

@@ -0,0 +1,176 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Command for updating env vars and other configuration info."""
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 global_methods
from googlecloudsdk.api_lib.run import job
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.projects import util as projects_util
from googlecloudsdk.command_lib.run import config_changes
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import messages_util as run_messages_util
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run import stages
from googlecloudsdk.core import config
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from googlecloudsdk.core.console import progress_tracker
class Replace(base.Command):
"""Create or replace a job from a YAML job specification."""
detailed_help = {
'DESCRIPTION':
"""\
Creates or replaces a job from a YAML job specification.
""",
'EXAMPLES':
"""\
To replace the specification for a job defined in myjob.yaml
$ {command} myjob.yaml
""",
}
@staticmethod
def Args(parser):
flags.AddAsyncFlag(parser)
flags.AddClientNameAndVersionFlags(parser)
parser.add_argument(
'FILE',
action='store',
type=arg_parsers.YAMLFileContents(),
help='The absolute path to the YAML file with a Cloud Run '
'job definition for the job to update or create.')
# No output by default, can be overridden by --format
parser.display_info.AddFormat('none')
def Run(self, args):
"""Create or Update job from YAML."""
run_messages = apis.GetMessagesModule(global_methods.SERVERLESS_API_NAME,
global_methods.SERVERLESS_API_VERSION)
job_dict = dict(args.FILE)
# Clear the status field since it is ignored by Cloud Run APIs and can cause
# issues trying to convert to a message.
if 'status' in job_dict:
del job_dict['status']
if ('spec' not in job_dict or 'template' not in job_dict['spec']):
raise exceptions.ConfigurationError(
'spec.template is required but missing. '
'Please check the content in your yaml file.')
# If spec.template.metadata is not set, add an empty one so that client
# annotations can be added.
if 'metadata' not in job_dict['spec']['template']:
job_dict['spec']['template']['metadata'] = {}
# For cases where YAML contains the project number as metadata.namespace,
# preemptively convert them to a string to avoid validation failures.
namespace = job_dict.get('metadata', {}).get('namespace', None)
if namespace is not None and not isinstance(namespace, str):
job_dict['metadata']['namespace'] = str(namespace)
try:
raw_job = messages_util.DictToMessageWithErrorCheck(
job_dict, run_messages.Job)
new_job = job.Job(raw_job, run_messages)
except messages_util.ScalarTypeMismatchError as e:
exceptions.MaybeRaiseCustomFieldMismatch(
e,
help_text='Please make sure that the YAML file matches the Cloud Run '
'job definition spec in https://cloud.google.com/run/docs/reference'
'/rest/v1/namespaces.jobs#Job')
# Namespace must match project (or will default to project if
# not specified).
namespace = properties.VALUES.core.project.Get()
if new_job.metadata.namespace is not None:
project = namespace
project_number = projects_util.GetProjectNumber(namespace)
namespace = new_job.metadata.namespace
if namespace != project and namespace != str(project_number):
raise exceptions.ConfigurationError(
'Namespace must be project ID [{}] or quoted number [{}] for '
'Cloud Run (fully managed).'.format(project, project_number))
new_job.metadata.namespace = namespace
is_either_specified = (
args.IsSpecified('client_name') or args.IsSpecified('client_version'))
changes = [
config_changes.ReplaceJobChange(new_job),
config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack()),
config_changes.SetClientNameAndVersionAnnotationChange(
args.client_name if is_either_specified else 'gcloud',
args.client_version
if is_either_specified else config.CLOUD_SDK_VERSION,
set_on_template=True)
]
job_ref = resources.REGISTRY.Parse(
new_job.metadata.name,
params={'namespacesId': new_job.metadata.namespace},
collection='run.namespaces.jobs')
region_label = new_job.region if new_job.is_managed else None
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack(), region_label=region_label)
with serverless_operations.Connect(conn_context) as client:
job_obj = client.GetJob(job_ref)
is_create = not job_obj
operation = ('Creating' if is_create else 'Updating')
pretty_print.Info(
run_messages_util.GetStartDeployMessage(conn_context, job_ref,
operation, 'job'))
header = operation + ' job...'
with progress_tracker.StagedProgressTracker(
header,
stages.JobStages(),
failure_message=('Job failed to deploy'),
suppress_output=args.async_) as tracker:
if job_obj:
job_obj = client.UpdateJob(
job_ref, changes, tracker, asyn=args.async_)
else:
job_obj = client.CreateJob(
job_ref, changes, tracker, asyn=args.async_)
operation = ('created' if is_create else 'updated')
if args.async_:
pretty_print.Success(
'Job [{{bold}}{job}{{reset}}] is being {operation} '
'asynchronously.'.format(job=job_obj.name, operation=operation))
else:
pretty_print.Success('Job [{{bold}}{job}{{reset}}] has been '
'successfully {operation}.'.format(
job=job_obj.name, operation=operation))
log.status.Print(
run_messages_util.GetRunJobMessage(self.ReleaseTrack(), job_obj.name))
return job_obj

View File

@@ -0,0 +1,36 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Set the IAM policy for a job.
description: |
This command replaces the existing IAM policy for a job, given a job
and a file encoded in JSON or YAML that contains the IAM policy. If the
given policy file specifies an "etag" value, then the replacement will
succeed only if the policy already in place matches that etag. (An etag
obtain via `get-iam-policy` will prevent the replacement if the policy
for the job has been subsequently updated.) A policy file that does not
contain an etag value will replace any existing policy for the job.
examples: |
The following command will read an IAM policy defined in a JSON file
'policy.json' and set it for a job with identifier
'my-job'
$ {command} --region=us-central1 my-job policy.json
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
request:
collection: run.projects.locations.jobs
modify_request_hooks:
- googlecloudsdk.command_lib.run.platforms:ValidatePlatformIsManaged
arguments:
resource:
help_text: The job for which to set the IAM policy.
spec: !REF googlecloudsdk.command_lib.run.resources:job
# The --region flag is specified at the group level, so don't try to add it here
removed_flags: ['region']
command_level_fallthroughs:
region:
- arg_name: 'region'

View File

@@ -0,0 +1,256 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Update a Cloud Run Job."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.run import config_changes
from googlecloudsdk.command_lib.run import connection_context
from googlecloudsdk.command_lib.run import container_parser
from googlecloudsdk.command_lib.run import exceptions
from googlecloudsdk.command_lib.run import flags
from googlecloudsdk.command_lib.run import messages_util
from googlecloudsdk.command_lib.run import pretty_print
from googlecloudsdk.command_lib.run import resource_args
from googlecloudsdk.command_lib.run import serverless_operations
from googlecloudsdk.command_lib.run import stages
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.command_lib.util.concepts import presentation_specs
from googlecloudsdk.core import log
from googlecloudsdk.core.console import progress_tracker
def ContainerArgGroup(release_track=base.ReleaseTrack.GA):
"""Returns an argument group with all per-container update args."""
help_text = """
Container Flags
If the --container or --remove-containers flag is specified the following
arguments may only be specified after a --container flag.
"""
group = base.ArgumentGroup(help=help_text)
group.AddArgument(
flags.ImageArg(
image='us-docker.pkg.dev/cloudrun/container/job:latest',
required=False,
)
)
group.AddArgument(flags.MutexEnvVarsFlags(release_track=release_track))
group.AddArgument(flags.MemoryFlag())
group.AddArgument(flags.CpuFlag())
group.AddArgument(flags.GpuFlag())
group.AddArgument(flags.ArgsFlag())
group.AddArgument(flags.SecretsFlags())
group.AddArgument(flags.CommandFlag())
group.AddArgument(flags.DependsOnFlag())
group.AddArgument(flags.AddVolumeMountFlag())
group.AddArgument(flags.RemoveVolumeMountFlag())
group.AddArgument(flags.ClearVolumeMountsFlag())
group.AddArgument(flags.StartupProbeFlag())
return group
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Update(base.Command):
"""Update a Cloud Run Job."""
detailed_help = {
'DESCRIPTION': """\
Updates a Cloud Run job.
""",
'EXAMPLES': """\
To update the container image of Cloud Run job `my-job`:
$ {command} my-job --image=us-docker.pkg.dev/project/image
""",
}
@classmethod
def CommonArgs(cls, parser):
# Flags not specific to any platform
job_presentation = presentation_specs.ResourcePresentationSpec(
'JOB',
resource_args.GetJobResourceSpec(prompt=True),
'Job to update.',
required=True,
prefixes=False,
)
flags.AddLabelsFlags(parser)
flags.AddParallelismFlag(parser)
flags.AddTasksFlag(parser)
flags.AddMaxRetriesFlag(parser)
flags.AddTaskTimeoutFlags(parser)
flags.AddServiceAccountFlag(parser)
flags.AddCloudSQLFlags(parser)
flags.AddVpcConnectorArgs(parser)
flags.AddVpcNetworkGroupFlagsForUpdate(parser, resource_kind='job')
flags.AddEgressSettingsFlag(parser)
flags.AddClientNameAndVersionFlags(parser)
flags.AddBinAuthzPolicyFlags(parser, with_clear=True)
flags.AddBinAuthzBreakglassFlag(parser)
flags.AddCmekKeyFlag(parser, with_clear=False)
flags.AddSandboxArg(parser, hidden=True)
flags.AddGeneralAnnotationFlags(parser)
flags.AddVolumesFlags(parser, cls.ReleaseTrack())
flags.AddGpuTypeFlag(parser)
flags.GpuZonalRedundancyFlag(parser)
polling_group = parser.add_mutually_exclusive_group()
flags.AddAsyncFlag(polling_group)
execute_group = polling_group.add_argument_group(
help='--async cannot be used if executing the job after the update.')
flags.AddWaitForCompletionFlag(execute_group, implies_execute_now=True)
flags.AddExecuteNowFlag(execute_group)
concept_parsers.ConceptParser([job_presentation]).AddToParser(parser)
# No output by default, can be overridden by --format
parser.display_info.AddFormat('none')
@staticmethod
def Args(parser):
Update.CommonArgs(parser)
container_args = ContainerArgGroup()
container_parser.AddContainerFlags(parser, container_args)
flags.RemoveContainersFlag().AddToParser(parser)
def Run(self, args):
"""Update a Job on Cloud Run."""
job_ref = args.CONCEPTS.job.Parse()
flags.ValidateResource(job_ref)
conn_context = connection_context.GetConnectionContext(
args, flags.Product.RUN, self.ReleaseTrack()
)
changes = flags.GetJobConfigurationChanges(
args,
release_track=self.ReleaseTrack())
changes.append(
config_changes.SetLaunchStageAnnotationChange(self.ReleaseTrack())
)
execute_now = args.execute_now or args.wait
with serverless_operations.Connect(conn_context) as operations:
pretty_print.Info(
messages_util.GetStartDeployMessage(
conn_context, job_ref, 'Updating', 'job'
)
)
if execute_now:
header_msg = 'Updating and running job...'
else:
header_msg = 'Updating job...'
def _UpdateJob(changes_):
with progress_tracker.StagedProgressTracker(
header_msg,
stages.JobStages(
execute_now=execute_now, include_completion=args.wait
),
failure_message='Job failed to deploy',
suppress_output=args.async_,
) as tracker:
execution_ = None
job_ = operations.UpdateJob(
job_ref, changes_, tracker, asyn=args.async_
)
if execute_now:
execution_ = operations.RunJob(
job_ref, tracker, args.wait, args.async_, self.ReleaseTrack()
)
return job_, execution_
try:
job, execution = _UpdateJob(changes)
except exceptions.HttpError as e:
if flags.ShouldRetryNoZonalRedundancy(args, str(e)):
changes.append(
config_changes.GpuZonalRedundancyChange(
gpu_zonal_redundancy=False
)
)
job, execution = _UpdateJob(changes)
else:
raise e
if args.async_ and not execute_now:
pretty_print.Success(
'Job [{{bold}}{job}{{reset}}] is being updated '
'asynchronously.'.format(job=job.name)
)
else:
operation = 'been updated'
if args.wait:
operation += ' and completed execution [{}]'.format(execution.name)
elif execute_now:
operation += ' and started running execution [{}]'.format(
execution.name
)
pretty_print.Success(
'Job [{{bold}}{job}{{reset}}] has successfully {operation}.'.format(
job=job.name, operation=operation
)
)
msg = ''
if execute_now:
msg += messages_util.GetExecutionCreatedMessage(
self.ReleaseTrack(), execution
)
msg += '\n'
msg += messages_util.GetRunJobMessage(
self.ReleaseTrack(), job.name, repeat=execute_now
)
log.status.Print(msg)
return job
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaUpdate(Update):
"""Update a Cloud Run Job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
container_args = ContainerArgGroup(release_track=base.ReleaseTrack.BETA)
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)
flags.RemoveContainersFlag().AddToParser(parser)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaUpdate(BetaUpdate):
"""Update a Cloud Run Job."""
@classmethod
def Args(cls, parser):
cls.CommonArgs(parser)
container_args = ContainerArgGroup(release_track=base.ReleaseTrack.ALPHA)
container_parser.AddContainerFlags(
parser, container_args, cls.ReleaseTrack()
)
flags.RemoveContainersFlag().AddToParser(parser)
flags.AddRuntimeFlag(parser)
flags.AddPriorityFlag(parser)
flags.AddDelayExecutionFlag(parser)