File: //snap/google-cloud-cli/current/lib/googlecloudsdk/command_lib/container/fleet/resources.py
# -*- coding: utf-8 -*- #
# Copyright 2025 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.
"""Functions for resource arguments in fleet commands."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import re
from googlecloudsdk.api_lib.container.fleet import util
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base as calliope_base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.calliope.concepts import concepts
from googlecloudsdk.calliope.concepts import deps
from googlecloudsdk.command_lib.container.fleet import api_util
from googlecloudsdk.command_lib.container.fleet import util as cmd_util
from googlecloudsdk.command_lib.util.concepts import concept_parsers
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
_LOCATION_RE = re.compile('/locations/([a-z0-9-]+)/')
def PromptForMembership(memberships=None,
flag='--membership',
message='Please specify a membership:\n',
cancel=True):
"""Prompt the user for a membership from a list of memberships in the fleet.
This method is referenced by fleet and feature commands as a fallthrough
for getting the memberships when required.
Args:
memberships: List of memberships to prompt from
flag: The name of the membership flag, used in the error message
message: The message given to the user describing the prompt.
cancel: Whether to include a "cancel" option.
Returns:
The membership specified by the user (str), or None if unable to prompt.
Raises:
OperationCancelledError if the prompt is cancelled by user
RequiredArgumentException if the console is unable to prompt
"""
if not console_io.CanPrompt():
raise calliope_exceptions.RequiredArgumentException(
flag, ('Cannot prompt a console for membership. Membership is '
'required. Please specify `{}` to select at '
'least one membership.'.format(flag)))
if memberships is None:
memberships, unreachable = api_util.ListMembershipsFull()
if unreachable:
raise exceptions.Error(
('Locations {} are currently unreachable. Please specify '
'memberships using `--location` or the full resource name '
'(projects/*/locations/*/memberships/*)').format(unreachable))
if not memberships:
raise exceptions.Error('No Memberships available in the fleet.')
idx = console_io.PromptChoice(
MembershipPartialNames(memberships),
message=message,
cancel_option=cancel)
return memberships[idx] if idx is not None else None
# For CLI output (e.g. prompts), the LOCATION/ID format
# (e.g. us-central1/my-membership) is more readable than
# the full resource name
def MembershipPartialNames(memberships):
"""Converts a list of full membership names to LOCATION/ID format."""
return [util.MembershipPartialName(m) for m in memberships]
def _LocationAttributeConfig(help_text=''):
"""Create location attributes in resource argument.
Args:
help_text: If set, overrides default help text for `--location`
Returns:
Location resource argument parameter config
"""
fallthroughs = [
deps.ArgFallthrough('--location'),
deps.PropertyFallthrough(properties.VALUES.gkehub.location),
]
return concepts.ResourceParameterAttributeConfig(
name='location',
help_text=help_text if help_text else ('Location for the {resource}.'),
fallthroughs=fallthroughs)
def _BasicAttributeConfig(attr_name, help_text=''):
"""Create basic attributes in resource argument.
Args:
attr_name: Name of the resource
help_text: If set, overrides default help text
Returns:
Resource argument parameter config
"""
return concepts.ResourceParameterAttributeConfig(
name=attr_name,
help_text=help_text if help_text else ('Name of the {resource}.'))
# Note that the membership_help argument does not work.
def AddMembershipResourceArg(parser,
api_version='v1',
positional=False,
plural=False,
membership_required=False,
flag_override='',
membership_help='',
location_help=''):
"""Add resource arg for projects/{}/locations/{}/memberships/{}."""
flag_name = '--membership'
if flag_override:
flag_name = flag_override
elif positional:
# Flags without '--' prefix are automatically positional
flag_name = 'MEMBERSHIP_NAME'
elif plural:
flag_name = '--memberships'
spec = concepts.ResourceSpec(
'gkehub.projects.locations.memberships',
api_version=api_version,
resource_name='membership',
plural_name='memberships',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_LocationAttributeConfig(location_help),
membershipsId=_BasicAttributeConfig(
'memberships' if plural else 'membership', membership_help))
concept_parsers.ConceptParser.ForResource(
flag_name,
spec,
'The group of arguments defining one or more memberships.'
if plural else 'The group of arguments defining a membership.',
plural=plural,
required=membership_required).AddToParser(parser)
def MembershipLocationSpecified(args, flag_override=''):
"""Returns whether a membership location is specified in args."""
if args.IsSpecified('location'):
return True
if args.IsKnownAndSpecified('membership') and _LOCATION_RE.search(
args.membership) is not None:
return True
if args.IsKnownAndSpecified('MEMBERSHIP_NAME') and _LOCATION_RE.search(
args.MEMBERSHIP_NAME) is not None:
return True
if args.IsKnownAndSpecified('memberships') and all(
[_LOCATION_RE.search(m) is not None for m in args.memberships]):
return True
if args.IsKnownAndSpecified(flag_override) and _LOCATION_RE.search(
args.GetValue(flag_override)) is not None:
return True
return False
def SearchMembershipResource(args,
flag_override='',
filter_cluster_missing=False):
"""Searches the fleet for an ambiguous membership provided in args.
Only necessary if location is ambiguous, i.e.
MembershipLocationSpecified(args) is False, or this behavior is necessary for
backwards compatibility. If flag_override is unset, the argument must be
called `MEMBERSHIP_NAME` if positional and `--membership` otherwise. Runs a
ListMemberships API call to verify the membership exists.
Args:
args: arguments provided to a command, including a membership resource arg
flag_override: a custom membership flag
filter_cluster_missing: whether to filter out memberships that are missing
a cluster.
Returns:
A membership resource name string
(e.g. projects/x/locations/y/memberships/z)
Raises:
googlecloudsdk.core.exceptions.Error: unable to find the membership
in the fleet
"""
if MembershipLocationSpecified(args) and api_util.GetMembership(
MembershipResourceName(args)):
return MembershipResourceName(args)
if args.IsKnownAndSpecified(flag_override):
arg_membership = getattr(args, flag_override)
elif args.IsKnownAndSpecified('MEMBERSHIP_NAME'):
arg_membership = args.MEMBERSHIP_NAME
elif args.IsKnownAndSpecified('membership'):
arg_membership = args.membership
else:
return None
all_memberships, unavailable = api_util.ListMembershipsFull(
filter_cluster_missing=filter_cluster_missing)
if unavailable:
raise exceptions.Error(
('Locations {} are currently unreachable. Please specify '
'memberships using `--location` or the full resource name '
'(projects/*/locations/*/memberships/*)').format(unavailable))
if not all_memberships:
raise exceptions.Error('No memberships available in the fleet.')
# Search all memberships for specified membership
found = []
for existing_membership in all_memberships:
if arg_membership == util.MembershipShortname(existing_membership):
found.append(existing_membership)
if not found:
raise exceptions.Error(
'Membership {} not found in the fleet.'.format(arg_membership))
elif len(found) > 1:
raise AmbiguousMembershipError(arg_membership)
return found[0]
def SearchMembershipResourcesPlural(args, filter_cluster_missing=False):
"""Searches the fleet for the membership resources provided in args.
Only necessary if location is ambiguous, i.e.
MembershipLocationSpecified(args) is
False. Assumes the argument is called `--membership`, `--memberships` if
plural, or `MEMBERSHIP_NAME` if positional. Runs ListMemberships API call to
verify the membership exists.
Args:
args: arguments provided to a command, including a membership resource arg
filter_cluster_missing: whether to filter out memberships that are missing
a cluster.
Returns:
A list of membership resource names
(e.g. ["projects/x/locations/y/memberships/z"])
Raises:
googlecloudsdk.core.exceptions.Error: unable to find a membership
in the fleet
"""
if args.IsKnownAndSpecified('memberships'):
arg_memberships = args.memberships
else:
return None
all_memberships, unavailable = api_util.ListMembershipsFull(
filter_cluster_missing=filter_cluster_missing)
if unavailable:
raise exceptions.Error(
('Locations [{}] are currently unreachable. Please specify '
'memberships using `--location` or the full resource name '
'(projects/*/locations/*/memberships/*)').format(unavailable))
if not all_memberships:
raise exceptions.Error('No memberships available in the fleet.')
memberships = []
for arg_membership in arg_memberships:
# Search all memberships for specified membership
found = []
for existing_membership in all_memberships:
if arg_membership == util.MembershipShortname(existing_membership):
found.append(existing_membership)
if not found:
raise exceptions.Error(
'Membership {} not found in the fleet.'.format(arg_membership))
elif len(found) > 1:
raise AmbiguousMembershipError(arg_membership)
memberships.append(found[0])
return memberships
def AmbiguousMembershipError(membership):
return exceptions.Error(
('Multiple memberships named {} found in the fleet. Please use '
'`--location` or full resource name '
'(projects/*/locations/*/memberships/*) to specify.').format(membership))
def MembershipResourceName(args, flag_override=''):
"""Gets a membership resource name from a membership resource argument.
If flag_override is unset, the argument must be `MEMBERSHIP_NAME` if
positional and `--membership` otherwise.
Args:
args: arguments provided to a command, including a membership resource arg
flag_override: a custom membership flag name
Returns:
The membership resource name (e.g. projects/x/locations/y/memberships/z)
"""
if args.IsKnownAndSpecified(flag_override):
return args.CONCEPTS.GetValue(flag_override).Parse().RelativeName()
if args.IsKnownAndSpecified('MEMBERSHIP_NAME'):
return args.CONCEPTS.membership_name.Parse().RelativeName()
return args.CONCEPTS.membership.Parse().RelativeName()
def PluralMembershipsResourceNames(args):
"""Gets a list of membership resource names from a --memberships resource arg.
Args:
args: arguments provided to a command, including a plural memberships
resource arg
Returns:
A list of membership resource names (e.g.
projects/x/locations/y/memberships/z)
"""
return [m.RelativeName() for m in args.CONCEPTS.memberships.Parse()]
def UseRegionalMemberships(track=None):
"""Returns whether regional memberships should be included.
This will be updated as regionalization is released, and eventually deleted
when it is fully rolled out.
Args:
track: The release track of the command
Returns:
A bool, whether regional memberships are supported for the release track in
the active environment
"""
return (track is calliope_base.ReleaseTrack.ALPHA) and (
cmd_util.APIEndpoint() == cmd_util.AUTOPUSH_API)
def InProdRegionalAllowlist(project, track=None):
"""Returns whether project is allowlisted for regional memberships in Prod.
This will be updated as regionalization is released, and eventually deleted
when it is fully rolled out.
Args:
project: The parent project ID of the membership
track: The release track of the command
Returns:
A bool, whether project is allowlisted for regional memberships in Prod
"""
prod_regional_allowlist = [
'gkeconnect-prober',
'gkeconnect-e2e',
'gkehub-cep-test',
'connectgateway-gke-testing',
'xuebinz-gke',
'kolber-anthos-testing',
'anthonytong-hub2',
'wenjuntoy2',
'hub-regionalisation-test', # For Cloud Console UI testing.
'hub-regionalisation-test-2', # For Cloud Console UI testing.
'a4vm-ui-tests-3', # For Cloud Console UI testing.
'm4a-ui-playground-1', # For Cloud Console UI testing.
'anthos-cl-e2e-tests',
'a4vm-ui-playground',
'm4a-ui-playground-1',
]
return track is calliope_base.ReleaseTrack.ALPHA and (
project in prod_regional_allowlist)
def GetMembershipProjects(memberships):
"""Returns all unique project identifiers of the given membership names.
ListMemberships should use the same identifier (all number or all ID) in
membership names. Users can convert their own project identifiers for manually
entering arguments.
Args:
memberships: A list of full membership resource names
Returns:
A list of project identifiers in the parents of the memberships
Raises: googlecloudsdk.core.exceptions.Error if unable to parse any membership
name
"""
projects = set()
for m in memberships:
match = re.match(r'projects\/(.*)\/locations\/(.*)\/memberships\/(.*)', m)
if not match:
raise exceptions.Error('Unable to parse membership {} (expected '
'projects/*/locations/*/memberships/*)'.format(m))
projects.add(match.group(1))
return list(projects)
def ParseMembershipArg(args, membership_flag='MEMBERSHIP_NAME'):
"""Returns a membership on which to run the command, given the arguments.
This function is currently only used by the unregister command. This logic
should be combined with the feature ParseMembership function in a later CL.
Allows for `MEMBERSHIP_NAME` positional flag.
Args:
args: object containing arguments passed as flags with the command
membership_flag: the membership flag used to pass in the memberhip resource
Returns:
membership: A membership resource name string
Raises:
exceptions.Error: no memberships were found or memberships are invalid
calliope_exceptions.RequiredArgumentException: membership was not provided
"""
# If a membership is provided (positional arg)
if args.IsKnownAndSpecified(membership_flag):
if MembershipLocationSpecified(args):
return MembershipResourceName(args)
else:
return SearchMembershipResource(args)
raise calliope_exceptions.RequiredArgumentException(
membership_flag, 'membership is required for this command.')
def _DefaultToGlobalLocationAttributeConfig(help_text=''):
"""Create basic attributes that fallthrough location to global in resource argument.
Args:
help_text: If set, overrides default help text
Returns:
Resource argument parameter config
"""
return concepts.ResourceParameterAttributeConfig(
name='location',
fallthroughs=[
deps.Fallthrough(
function=cmd_util.DefaultToGlobal,
hint='global is the only supported location',
)
],
help_text=help_text if help_text else ('Name of the {resource}.'),
)
def AddScopeResourceArg(
parser,
flag_name='NAME',
api_version='v1',
scope_help='',
required=False,
group=None,
):
"""Add resource arg for projects/{}/locations/{}/scopes/{}."""
spec = concepts.ResourceSpec(
'gkehub.projects.locations.scopes',
api_version=api_version,
resource_name='scope',
plural_name='scopes',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_DefaultToGlobalLocationAttributeConfig(),
scopesId=_BasicAttributeConfig('scope', scope_help),
)
concept_parsers.ConceptParser.ForResource(
flag_name,
spec,
'The group of arguments defining the Fleet Scope.',
plural=False,
required=required,
group=group,
# This hides the location flag as we only allow global scope.
flag_name_overrides={'location': ''},
).AddToParser(parser)
def AddScopeNamespaceResourceArg(
parser,
flag_name='NAMESPACE',
api_version='v1',
namespace_help='',
required=False,
):
"""Add resource arg for projects/{}/locations/{}/scopes/{}/namespaces/{}."""
spec = concepts.ResourceSpec(
'gkehub.projects.locations.scopes.namespaces',
api_version=api_version,
resource_name='namespace',
plural_name='namespaces',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_DefaultToGlobalLocationAttributeConfig(),
scopesId=_BasicAttributeConfig('scope', 'the'),
namespacesId=_BasicAttributeConfig('namespace', namespace_help),
)
concept_parsers.ConceptParser.ForResource(
flag_name,
spec,
'The group of arguments defining the Fleet Namespace.',
plural=False,
required=required,
# This hides the location flag as we only allow global scope.
flag_name_overrides={'location': ''},
).AddToParser(parser)
def AddScopeRBACResourceArg(parser, api_version='v1', rbacrb_help=''):
"""Add resource arg for projects/{}/locations/{}/scopes/{}/rbacrolebindings/{}."""
# Flags without '--' prefix are automatically positional
flag_name = 'NAME'
spec = concepts.ResourceSpec(
'gkehub.projects.locations.scopes.rbacrolebindings',
api_version=api_version,
resource_name='rbacrolebinding',
plural_name='rbacrolebindings',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_LocationAttributeConfig(),
scopesId=_BasicAttributeConfig('scope', ''),
rbacrolebindingsId=_BasicAttributeConfig('rbacrolebinding', rbacrb_help),
)
concept_parsers.ConceptParser.ForResource(
flag_name,
spec,
'The group of arguments defining an RBACRoleBinding.',
plural=False,
required=True,
).AddToParser(parser)
def AddRBACResourceArg(parser, api_version='v1', rbacrb_help=''):
"""Add resource arg for projects/{}/locations/{}/memberships/{}."""
# Flags without '--' prefix are automatically positional
flag_name = 'NAME'
spec = concepts.ResourceSpec(
'gkehub.projects.locations.namespaces.rbacrolebindings',
api_version=api_version,
resource_name='rbacrolebinding',
plural_name='rbacrolebindings',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_LocationAttributeConfig(),
namespacesId=_BasicAttributeConfig('namespace', ''),
rbacrolebindingsId=_BasicAttributeConfig('rbacrolebinding', rbacrb_help))
concept_parsers.ConceptParser.ForResource(
flag_name,
spec,
'The group of arguments defining an RBACRoleBinding.',
plural=False,
required=True).AddToParser(parser)
def AddUpdateNamespaceLabelsFlags(parser):
"""Adds flags to an argparse parser for updating namespace labels.
Args:
parser: The argparse parser to add the flags to.
"""
_GetUpdateNamespaceLabelsFlag('namespace').AddToParser(parser)
remove_group = parser.add_mutually_exclusive_group()
_GetClearNamespaceLabelsFlag('namespace').AddToParser(
remove_group
)
_GetRemoveNamespaceLabelsFlag('namespace').AddToParser(remove_group)
def UpdateScopeLabelsFlags():
remove_group = calliope_base.ArgumentGroup(mutex=True)
remove_group.AddArgument(
_GetClearNamespaceLabelsFlag('scope')
)
remove_group.AddArgument(
_GetRemoveNamespaceLabelsFlag('scope')
)
return [
_GetUpdateNamespaceLabelsFlag('scope'),
remove_group,
]
def AddCreateNamespaceLabelsFlags(parser):
"""Adds flags to an argparse parser for creating namespace labels.
Args:
parser: The argparse parser to add the flags to.
"""
_GetCreateNamespaceLabelsFlag('namespace').AddToParser(parser)
def CreateScopeLabelsFlags():
return [_GetCreateNamespaceLabelsFlag('scope')]
def _GetClearNamespaceLabelsFlag(resource_type):
labels_name = 'namespace-labels'
return calliope_base.Argument(
'--clear-{}'.format(labels_name),
action='store_true',
help="""\
Remove all {resource_type}-level labels from the cluster namespace. If `--update-{labels}` is also specified then
`--clear-{labels}` is applied first.
For example, to remove all labels:
$ {{command}} {resource_type}_name --clear-{labels}
To remove all existing {resource_type}-level labels and create two new labels,
``foo'' and ``baz'':
$ {{command}} {resource_type}_name --clear-{labels} --update-{labels} foo=bar,baz=qux
""".format(labels=labels_name, resource_type=resource_type))
def _GetRemoveNamespaceLabelsFlag(resource_type):
labels_name = 'namespace-labels'
return calliope_base.Argument(
'--remove-{}'.format(labels_name),
metavar='KEY',
type=arg_parsers.ArgList(),
action=arg_parsers.UpdateAction,
help="""\
List of {resource_type}-level label keys to remove in the cluster namespace. If a label does not exist it is
silently ignored. If `--update-{labels}` is also specified then
`--update-{labels}` is applied first.
""".format(labels=labels_name, resource_type=resource_type))
def _GetUpdateNamespaceLabelsFlag(resource_type):
"""Makes a base.Argument for the `--update-namespace-labels` flag."""
labels_name = 'namespace-labels'
return calliope_base.Argument(
'--update-{}'.format(labels_name),
metavar='KEY=VALUE',
type=arg_parsers.ArgDict(),
action=arg_parsers.UpdateAction,
help="""\
List of {resource_type}-level label KEY=VALUE pairs to update in the cluster namespace. If a
label exists, its value is modified. Otherwise, a new label is'
created.""".format(resource_type=resource_type))
def _GetCreateNamespaceLabelsFlag(resource_type):
labels_name = 'namespace-labels'
return calliope_base.Argument(
'--{}'.format(labels_name),
metavar='KEY=VALUE',
type=arg_parsers.ArgDict(),
action=arg_parsers.UpdateAction,
help="""\
List of {resource_type}-level label KEY=VALUE pairs to add.
""".format(resource_type=resource_type))
def RBACResourceName(args):
"""Gets an RBACRoleBinding resource name from a resource argument.
Assumes the argument is called NAME.
Args:
args: arguments provided to a command, including an rbacRB resource arg
Returns:
The rbacRB resource name (e.g.
projects/x/locations/global/namespaces/y/rbacrolebindings/z
projects/x/locations/global/scopes/y/rbacrolebindings/z)
"""
return args.CONCEPTS.name.Parse().RelativeName()
def AddMembershipBindingResourceArg(parser, api_version='v1', binding_help=''):
"""Add resource arg for projects/{}/locations/{}/memberships/{}/bindings/{}."""
# Flags without '--' prefix are automatically positional
flag_name = 'BINDING'
spec = concepts.ResourceSpec(
'gkehub.projects.locations.memberships.bindings',
api_version=api_version,
resource_name='binding',
plural_name='bindings',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_LocationAttributeConfig(),
membershipsId=_BasicAttributeConfig('membership', ''),
bindingsId=_BasicAttributeConfig('binding', binding_help))
concept_parsers.ConceptParser.ForResource(
flag_name,
spec,
'The group of arguments defining a Membership Binding.',
plural=False,
required=True).AddToParser(parser)
def MembershipBindingResourceName(args):
"""Gets a Membership-Binding resource name from a resource argument.
Assumes the argument is called BINDING.
Args:
args: arguments provided to a command, including a Binding resource arg
Returns:
The Binding resource name (e.g.
projects/x/locations/l/memberships/y/bindings/z)
"""
return args.CONCEPTS.binding.Parse().RelativeName()
def AddRolloutResourceArg(parser, api_version='v1'):
"""Add resource arg for projects/{}/locations/{}/rollouts/{}."""
# Flags without '--' prefix are automatically positional
spec = concepts.ResourceSpec(
'gkehub.projects.locations.rollouts',
api_version=api_version,
resource_name='rollout',
plural_name='rollouts',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_DefaultToGlobalLocationAttributeConfig(),
rolloutsId=_BasicAttributeConfig('rollout'),
)
concept_parsers.ConceptParser.ForResource(
name='rollout',
resource_spec=spec,
group_help='The group of arguments defining a Fleet Rollout.',
plural=False,
required=True,
# This hides the location flag as we only allow global scope.
flag_name_overrides={'location': ''},
).AddToParser(parser)
def AddRolloutSequenceResourceArg(parser, api_version='v1'):
"""Add resource arg for projects/{}/locations/{}/rolloutSequences/{}."""
# Flags without '--' prefix are automatically positional
spec = concepts.ResourceSpec(
'gkehub.projects.locations.rolloutSequences',
api_version=api_version,
resource_name='rolloutSequence',
plural_name='rolloutSequences',
disable_auto_completers=True,
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_DefaultToGlobalLocationAttributeConfig(),
rolloutSequencesId=_BasicAttributeConfig('rollout_sequence'),
)
concept_parsers.ConceptParser.ForResource(
name='rolloutSequence',
resource_spec=spec,
group_help='The group of arguments defining a Rollout Sequence.',
plural=False,
required=True,
# This hides the location flag as we only allow global scope.
flag_name_overrides={'location': ''},
).AddToParser(parser)
def RolloutSequenceResourceName(args):
"""Gets a RolloutSequence resource name from a resource argument.
Assumes the argument is called ROLLOUTSEQUENCE.
Args:
args: arguments provided to a command, including a RolloutSequence resource
arg
Returns:
The RolloutSequence resource name (e.g.
projects/x/locations/l/rolloutSequences/z)
"""
return args.CONCEPTS.rolloutsequence.Parse().RelativeName()
def AddWorkloadIdentityPoolResourceArg(parser, api_version='v1'):
"""Add resource arg for projects/{}/locations/{}/workloadidentitypools/{}."""
# Flags without '--' prefix are automatically positional
flag_name = 'WORKLOAD_IDENTITY_POOL'
spec = concepts.ResourceSpec(
'iam.projects.locations.workloadIdentityPools',
api_version=api_version,
resource_name='workloadidentitypool',
plural_name='workloadidentitypools',
projectsId=concepts.DEFAULT_PROJECT_ATTRIBUTE_CONFIG,
locationsId=_LocationAttributeConfig(),
workloadIdentityPoolsId=_BasicAttributeConfig('workloadidentitypool'),
)
concept_parsers.ConceptParser.ForResource(
name=flag_name,
resource_spec=spec,
group_help='The group of arguments defining a Workload Identity Pool.',
plural=False,
required=True,
# This hides the location flag as we only allow global scope.
flag_name_overrides={'location': ''},
).AddToParser(parser)
def WorkloadIdentityPoolResourceName(args):
"""Gets a WorkloadIdentityPool resource name from a resource argument.
Assumes the argument is called WORKLOAD_IDENTITY_POOL.
Args:
args: arguments provided to a command,
including a WorkloadIdentityPool resource arg
Returns:
The WorkloadIdentityPool resource name (e.g.
projects/x/locations/l/workloadidentitypools/z)
"""
return args.CONCEPTS.workload_identity_pool.Parse().RelativeName()