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,26 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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.
"""Commands for creating and manipulating roles."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Roles(base.Group):
"""Create and manipulate roles."""

View File

@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 creating a role from an existing role."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.iam import util
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope.exceptions import RequiredArgumentException
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.core import log
DETAILED_HELP = {
'EXAMPLES':
"""\
To create a copy of an existing role ``spanner.databaseAdmin'' into an organization
with ``1234567'', run:
$ {command} --source="roles/spanner.databaseAdmin" --destination=CustomViewer --dest-organization=1234567
To create a copy of an existing role ``spanner.databaseAdmin'' into a project with
``PROJECT_ID'', run:
$ {command} --source="roles/spanner.databaseAdmin" --destination=CustomSpannerDbAdmin --dest-project=PROJECT_ID
To modify the newly created role see the roles update command.
"""
}
@base.UniverseCompatible
class Copy(base.Command):
r"""Create a role from an existing role.
This command creates a role from an existing role.
"""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
parser.add_argument(
'--source',
help='The source role ID. '
'For predefined roles, for example: roles/viewer. '
'For custom roles, for example: myCompanyAdmin.')
parser.add_argument(
'--destination',
help='The destination role ID for the new custom '
'role. For example: viewer.')
parser.add_argument(
'--source-organization',
help='The organization of the source role '
'if it is an custom role.')
parser.add_argument(
'--dest-organization', help='The organization of the destination role.')
parser.add_argument(
'--source-project',
help='The project of the source role '
'if it is an custom role.')
parser.add_argument(
'--dest-project', help='The project of the destination role.')
def Run(self, args):
client, messages = util.GetClientAndMessages()
if args.source is None:
raise RequiredArgumentException('source', 'the source role is required.')
if args.destination is None:
raise RequiredArgumentException('destination',
'the destination role is required.')
source_role_name = iam_util.GetRoleName(
args.source_organization,
args.source_project,
args.source,
attribute='the source custom role',
parameter_name='source')
dest_parent = iam_util.GetParentName(
args.dest_organization,
args.dest_project,
attribute='the destination custom role')
source_role = client.organizations_roles.Get(
messages.IamOrganizationsRolesGetRequest(name=source_role_name))
new_role = messages.Role(
title=source_role.title,
description=source_role.description)
permissions_helper = util.PermissionsHelper(client, messages,
iam_util.GetResourceReference(
args.dest_project,
args.dest_organization),
source_role.includedPermissions)
not_supported_permissions = permissions_helper.GetNotSupportedPermissions()
if not_supported_permissions:
log.warning(
'Permissions don\'t support custom roles and won\'t be added: ['
+ ', '.join(not_supported_permissions) + '] \n')
not_applicable_permissions = permissions_helper.GetNotApplicablePermissions(
)
if not_applicable_permissions:
log.warning(
'Permissions not applicable to the current resource and won\'t'
' be added: [' + ', '.join(not_applicable_permissions) + '] \n')
api_diabled_permissions = permissions_helper.GetApiDisabledPermissons()
iam_util.ApiDisabledPermissionsWarning(api_diabled_permissions)
testing_permissions = permissions_helper.GetTestingPermissions()
iam_util.TestingPermissionsWarning(testing_permissions)
valid_permissions = permissions_helper.GetValidPermissions()
new_role.includedPermissions = valid_permissions
result = client.organizations_roles.Create(
messages.IamOrganizationsRolesCreateRequest(
createRoleRequest=messages.CreateRoleRequest(
role=new_role, roleId=args.destination),
parent=dest_parent))
iam_util.SetRoleStageIfAlpha(result)
return result

View File

@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 create a custom role for a project or an organization."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.iam import util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import flags
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.core import log
DETAILED_HELP = {
'EXAMPLES':
"""\
To create a custom role ``ProjectUpdater'' from a YAML file, run:
$ {command} ProjectUpdater --organization=12345 --file=role_file_path
To create a custom role ``ProjectUpdater'' with flags, run:
$ {command} ProjectUpdater --project=myproject --title=ProjectUpdater --description="Have access to get and update the project" --permissions=resourcemanager.projects.get,resourcemanager.projects.update
"""
}
@base.UniverseCompatible
class Create(base.Command):
r"""Create a custom role for a project or an organization.
This command creates a custom role with the provided information.
"""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
roles_group = parser.add_group(mutex=True)
settings_flags_group = roles_group.add_group('Roles Settings')
settings_flags_group.add_argument(
'--title', help='The title of the role you want to create.')
settings_flags_group.add_argument(
'--description', help='The description of the role you want to create.')
settings_flags_group.add_argument(
'--stage', help='The state of the role you want to create. '
'This represents a role\'s lifecycle phase: `ALPHA`, `BETA`, `GA`, '
'`DEPRECATED`, `DISABLED`, `EAP`.')
settings_flags_group.add_argument(
'--permissions',
help='The permissions of the role you want to create. '
'Use commas to separate them.')
roles_group.add_argument(
'--file',
help='The JSON or YAML file with the IAM Role to create. See '
'https://cloud.google.com/iam/reference/rest/v1/projects.roles.')
flags.AddParentFlags(parser, 'create')
flags.GetCustomRoleFlag('create').AddToParser(parser)
def Run(self, args):
client, messages = util.GetClientAndMessages()
parent_name = iam_util.GetParentName(args.organization, args.project)
if args.file:
role = iam_util.ParseYamlToRole(args.file, messages.Role)
role.name = None
role.etag = None
else:
role = messages.Role(title=args.title, description=args.description)
if args.permissions:
role.includedPermissions = args.permissions.split(',')
if args.stage:
role.stage = iam_util.StageTypeFromString(args.stage)
if not role.title:
role.title = args.role
if not args.quiet:
permissions_helper = util.PermissionsHelper(client, messages,
iam_util.GetResourceReference(
args.project,
args.organization),
role.includedPermissions)
api_diabled_permissions = permissions_helper.GetApiDisabledPermissons()
iam_util.ApiDisabledPermissionsWarning(api_diabled_permissions)
testing_permissions = permissions_helper.GetTestingPermissions()
iam_util.TestingPermissionsWarning(testing_permissions)
result = client.organizations_roles.Create(
messages.IamOrganizationsRolesCreateRequest(
createRoleRequest=messages.CreateRoleRequest(
role=role, roleId=args.role),
parent=parent_name))
log.CreatedResource(args.role, kind='role')
iam_util.SetRoleStageIfAlpha(result)
return result

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 a role."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.iam import util
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.iam import flags
from googlecloudsdk.command_lib.iam import iam_util
DETAILED_HELP = {
'EXAMPLES':
"""\
To delete the role ``ProjectUpdater'' of the organization ``1234567'',
run:
$ {command} ProjectUpdater --organization=1234567
To delete the role ``ProjectUpdater'' of the project ``myproject'',
run:
$ {command} ProjectUpdater --project=myproject
"""
}
@base.UniverseCompatible
class Delete(base.DescribeCommand):
"""Delete a custom role from an organization or a project.
This command deletes a role.
This command can fail for the following reasons:
* The role specified does not exist.
* The active user does not have permission to access the given role.
"""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
flags.AddParentFlags(parser, 'delete')
flags.GetCustomRoleFlag('delete').AddToParser(parser)
def Run(self, args):
role_name = iam_util.GetRoleName(args.organization, args.project, args.role)
client, messages = util.GetClientAndMessages()
if args.organization is None and args.project is None:
raise exceptions.InvalidArgumentException(
'ROLE_ID',
'You can not delete a curated/predefined role.')
return client.organizations_roles.Delete(
messages.IamOrganizationsRolesDeleteRequest(name=role_name))

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 describing a role."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.iam import util
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import flags
from googlecloudsdk.command_lib.iam import iam_util
@base.UniverseCompatible
class Describe(base.DescribeCommand):
"""Show metadata for a role.
This command shows metadata for a role.
This command can fail for the following reasons:
* The role specified does not exist.
* The active user does not have permission to access the given role.
## EXAMPLES
To print metadata for the role ``spanner.databaseAdmin'' of the organization
``1234567'', run:
$ {command} roles/spanner.databaseAdmin --organization=1234567
To print metadata for the role ``spanner.databaseAdmin'' of the project
``myproject'', run:
$ {command} roles/spanner.databaseAdmin --project=myproject
To print metadata for a predefined role, ``spanner.databaseAdmin'', run:
$ {command} roles/spanner.databaseAdmin
"""
@staticmethod
def Args(parser):
flags.AddParentFlags(parser, 'describe', required=False)
flags.GetRoleFlag('describe').AddToParser(parser)
def Run(self, args):
role_name = iam_util.GetRoleName(args.organization, args.project, args.role)
client, messages = util.GetClientAndMessages()
res = client.organizations_roles.Get(
messages.IamOrganizationsRolesGetRequest(name=role_name))
iam_util.SetRoleStageIfAlpha(res)
return res

View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 to list all the roles of a parent organization or a project."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.iam import util
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.iam import flags
from googlecloudsdk.command_lib.iam import iam_util
@base.UniverseCompatible
class List(base.ListCommand):
"""List predefined roles, or the custom roles for an organization or project.
When an organization or project is specified, this command lists the custom
roles that are defined for that organization or project.
Otherwise, this command lists IAM's predefined roles.
## EXAMPLES
To list custom roles for the organization ``12345'', run:
$ {command} --organization=12345
To list custom roles for the project ``myproject'', run:
$ {command} --project=myproject
To list all predefined roles, run:
$ {command}
"""
@staticmethod
def Args(parser):
parser.add_argument(
'--show-deleted',
action='store_true',
help='Show deleted roles by specifying this flag.')
flags.AddParentFlags(parser, 'list', required=False)
base.ASYNC_FLAG.RemoveFromParser(parser)
base.PAGE_SIZE_FLAG.RemoveFromParser(parser)
base.URI_FLAG.RemoveFromParser(parser)
def Run(self, args):
client, messages = util.GetClientAndMessages()
if args.project is None and args.organization is None:
return list_pager.YieldFromList(
client.roles,
messages.IamRolesListRequest(showDeleted=args.show_deleted),
field='roles',
limit=args.limit,
batch_size_attribute='pageSize')
parent_name = iam_util.GetParentName(args.organization, args.project)
if args.limit is not None and (args.limit < 1):
raise exceptions.InvalidArgumentException('Limit size must be >=1')
return list_pager.YieldFromList(
client.organizations_roles,
messages.IamOrganizationsRolesListRequest(
parent=parent_name, showDeleted=args.show_deleted),
field='roles',
limit=args.limit,
batch_size_attribute='pageSize')

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 undeleting a role."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.iam import util
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.iam import flags
from googlecloudsdk.command_lib.iam import iam_util
@base.UniverseCompatible
class Undelete(base.DescribeCommand):
"""Undelete a custom role from an organization or a project.
This command undeletes a role. Roles that have been deleted for certain long
time can't be undeleted.
This command can fail for the following reasons:
* The role specified does not exist.
* The active user does not have permission to access the given role.
## EXAMPLES
To undelete the role ``ProjectUpdater'' of the organization ``1234567'', run:
$ {command} ProjectUpdater --organization=1234567
To undelete the role ``ProjectUpdater'' of the project ``myproject'',
run:
$ {command} ProjectUpdater --project=myproject
"""
@staticmethod
def Args(parser):
flags.AddParentFlags(parser, 'undelete')
flags.GetCustomRoleFlag('undelete').AddToParser(parser)
def Run(self, args):
client, messages = util.GetClientAndMessages()
role_name = iam_util.GetRoleName(args.organization, args.project, args.role)
if args.organization is None and args.project is None:
raise exceptions.InvalidArgumentException(
'ROLE_ID',
'You can not undelete a curated/predefined role.')
return client.organizations_roles.Undelete(
messages.IamOrganizationsRolesUndeleteRequest(name=role_name))

View File

@@ -0,0 +1,202 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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 a custom role."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.iam import util
from googlecloudsdk.api_lib.util import http_retry
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.iam import flags
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.core.console import console_io
import six.moves.http_client
DETAILED_HELP = {
'EXAMPLES':
"""\
To update the role ``ProjectUpdater'' from a YAML file, run:
$ {command} ProjectUpdater --organization=123 --file=role_file_path
To update the role ``ProjectUpdater'' with flags, run:
$ {command} ProjectUpdater --project=myproject --permissions=permission1,permission2
"""
}
@base.UniverseCompatible
class Update(base.Command):
"""Update an IAM custom role.
This command updates an IAM custom role.
"""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
updated = parser.add_argument_group(
'The following flags determine the fields need to be updated. '
'You can update a role by specifying the following flags, or '
'you can update a role from a YAML file by specifying the file flag.')
updated.add_argument(
'--title', help='The title of the role you want to update.')
updated.add_argument(
'--description', help='The description of the role you want to update.')
updated.add_argument(
'--stage', help='The state of the role you want to update.')
updated.add_argument(
'--permissions',
help='The permissions of the role you want to set. '
'Use commas to separate them.')
updated.add_argument(
'--add-permissions',
help='The permissions you want to add to the role. '
'Use commas to separate them.')
updated.add_argument(
'--remove-permissions',
help='The permissions you want to remove from the '
'role. Use commas to separate them.')
parser.add_argument(
'--file',
help='The YAML file you want to use to update a role. '
'Can not be specified with other flags except role-id.')
flags.AddParentFlags(parser, 'update')
flags.GetCustomRoleFlag('update').AddToParser(parser)
def Run(self, args):
client, messages = util.GetClientAndMessages()
role_name = iam_util.GetRoleName(args.organization, args.project, args.role)
role = messages.Role()
if args.file:
if (args.title or args.description or args.stage or args.permissions or
args.add_permissions or args.remove_permissions):
raise exceptions.ConflictingArgumentsException('file', 'others')
role = iam_util.ParseYamlToRole(args.file, messages.Role)
if not role.etag:
msg = ('The specified role does not contain an "etag" field '
'identifying a specific version to replace. Updating a '
'role without an "etag" can overwrite concurrent role '
'changes.')
console_io.PromptContinue(
message=msg,
prompt_string='Replace existing role',
cancel_on_no=True)
if not args.quiet:
self.WarnPermissions(client, messages, role.includedPermissions,
args.project, args.organization)
try:
res = client.organizations_roles.Patch(
messages.IamOrganizationsRolesPatchRequest(
name=role_name, role=role))
iam_util.SetRoleStageIfAlpha(res)
return res
except apitools_exceptions.HttpConflictError as e:
raise exceptions.HttpException(
e, error_format=('Stale "etag": '
'Please use the etag from your latest describe '
'response. Or new changes have been made since '
'your latest describe operation. Please retry '
'the whole describe-update process. Or you can '
'leave the etag blank to overwrite concurrent '
'role changes.'))
except apitools_exceptions.HttpError as e:
raise exceptions.HttpException(e)
res = self.UpdateWithFlags(args, role_name, role, client, messages)
iam_util.SetRoleStageIfAlpha(res)
return res
@http_retry.RetryOnHttpStatus(six.moves.http_client.CONFLICT)
def UpdateWithFlags(self, args, role_name, role, iam_client, messages):
role, changed_fields = self.GetUpdatedRole(args, role_name, role,
iam_client, messages)
return iam_client.organizations_roles.Patch(
messages.IamOrganizationsRolesPatchRequest(
name=role_name, role=role, updateMask=','.join(changed_fields)))
def GetUpdatedRole(self, args, role_name, role, iam_client, messages):
"""Gets the updated role from flags."""
changed_fields = []
if args.description is not None:
changed_fields.append('description')
role.description = args.description
if args.title is not None:
changed_fields.append('title')
role.title = args.title
if args.stage:
changed_fields.append('stage')
role.stage = iam_util.StageTypeFromString(args.stage)
if args.permissions is not None and (args.add_permissions or
args.remove_permissions):
raise exceptions.ConflictingArgumentsException(
'--permissions', '-add-permissions or --remove-permissions')
if args.permissions is not None:
changed_fields.append('includedPermissions')
role.includedPermissions = args.permissions.split(',')
if not args.permissions:
role.includedPermissions = []
if not args.quiet:
self.WarnPermissions(iam_client, messages, role.includedPermissions,
args.project, args.organization)
origin_role = iam_client.organizations_roles.Get(
messages.IamOrganizationsRolesGetRequest(name=role_name))
if args.add_permissions or args.remove_permissions:
permissions = set(origin_role.includedPermissions)
changed = False
newly_added_permissions = set()
if args.add_permissions:
for permission in args.add_permissions.split(','):
if permission not in permissions:
permissions.add(permission)
newly_added_permissions.add(permission)
changed = True
if args.remove_permissions:
for permission in args.remove_permissions.split(','):
if permission in permissions:
permissions.remove(permission)
changed = True
if permission in newly_added_permissions:
newly_added_permissions.remove(permission)
if changed:
changed_fields.append('includedPermissions')
role.includedPermissions = list(sorted(permissions))
if not args.quiet:
self.WarnPermissions(iam_client, messages,
list(newly_added_permissions), args.project,
args.organization)
role.etag = origin_role.etag
return role, changed_fields
def WarnPermissions(self, iam_client, messages, permissions, project,
organization):
permissions_helper = util.PermissionsHelper(iam_client, messages,
iam_util.GetResourceReference(
project, organization),
permissions)
api_disabled_permissions = permissions_helper.GetApiDisabledPermissons()
iam_util.ApiDisabledPermissionsWarning(api_disabled_permissions)
testing_permissions = permissions_helper.GetTestingPermissions()
iam_util.TestingPermissionsWarning(testing_permissions)