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,137 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 Google Inc. 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.
"""API Keys API helper functions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.services import exceptions
from googlecloudsdk.api_lib.util import apis as core_apis
from googlecloudsdk.calliope import base as calliope_base
_PROJECT_RESOURCE = 'projects/%s'
_PARENT_RESOURCE = 'projects/%s/locations/global'
_API_NAME = 'apikeys'
_RELEASE_TRACK_TO_API_VERSION = {
calliope_base.ReleaseTrack.ALPHA: 'v2',
calliope_base.ReleaseTrack.BETA: 'v2',
calliope_base.ReleaseTrack.GA: 'v2'
}
def ListKeys(project, show_deleted=None, page_size=None, limit=None):
"""List API Keys for a given project.
Args:
project: The project for which to list keys.
show_deleted: Includes deleted keys in the list.
page_size: The page size to list.
limit: The max number of metrics to return.
Raises:
exceptions.PermissionDeniedException: when listing keys fails.
Returns:
The list of keys
"""
client = GetClientInstance(calliope_base.ReleaseTrack.GA)
messages = client.MESSAGES_MODULE
request = messages.ApikeysProjectsLocationsKeysListRequest(
parent=GetParentResourceName(project), showDeleted=show_deleted)
return list_pager.YieldFromList(
client.projects_locations_keys,
request,
limit=limit,
batch_size_attribute='pageSize',
batch_size=page_size,
field='keys')
def GetClientInstance(release_track=calliope_base.ReleaseTrack.ALPHA):
"""Returns an API client for ApiKeys."""
api_version = _RELEASE_TRACK_TO_API_VERSION.get(release_track)
return core_apis.GetClientInstance(_API_NAME, api_version)
def GetOperation(name):
"""Make API call to get an operation.
Args:
name: The name of the operation.
Raises:
exceptions.OperationErrorException: when the getting operation API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the service.
Returns:
The result of the operation
"""
client = GetClientInstance()
messages = client.MESSAGES_MODULE
request = messages.ApikeysOperationsGetRequest(name=name)
try:
return client.operations.Get(request)
except (apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError) as e:
exceptions.ReraiseError(e, exceptions.OperationErrorException)
def GetAllowedAndroidApplications(args, messages):
"""Create list of allowed android applications."""
allowed_applications = []
for application in getattr(args, 'allowed_application', []) or []:
android_application = messages.V2AndroidApplication(
sha1Fingerprint=application['sha1_fingerprint'],
packageName=application['package_name'])
allowed_applications.append(android_application)
return allowed_applications
def GetApiTargets(args, messages):
"""Create list of target apis."""
api_targets = []
for api_target in getattr(args, 'api_target', []) or []:
api_targets.append(
messages.V2ApiTarget(
service=api_target.get('service'),
methods=api_target.get('methods', [])))
return api_targets
def GetAnnotations(args, messages):
"""Create list of annotations."""
annotations = getattr(args, 'annotations', {})
additional_property_messages = []
if not annotations:
return None
for key, value in annotations.items():
additional_property_messages.append(
messages.V2Key.AnnotationsValue.AdditionalProperty(
key=key, value=value))
annotation_value_message = messages.V2Key.AnnotationsValue(
additionalProperties=additional_property_messages)
return annotation_value_message
def GetParentResourceName(project):
return _PARENT_RESOURCE % (project)

View File

@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""services enable helper functions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.services import services_util
from googlecloudsdk.api_lib.services import serviceusage
from googlecloudsdk.core import log
def IsServiceEnabled(project_id, service_name):
"""Return true if the service is enabled.
Args:
project_id: The ID of the project we want to query.
service_name: The name of the service.
Raises:
exceptions.GetServicePermissionDeniedException: if a 403 or 404
error is returned by the Get request.
apitools_exceptions.HttpError: Another miscellaneous error with the listing
service.
Returns:
True if the service is enabled, false otherwise.
"""
service = serviceusage.GetService(project_id, service_name)
return serviceusage.IsServiceEnabled(service)
def EnableService(project_id, service_name, is_async=False):
"""Enable a service without checking if it is already enabled.
Args:
project_id: The ID of the project for which to enable the service.
service_name: The name of the service to enable on the project.
is_async: bool, if True, print the operation ID and return immediately,
without waiting for the op to complete.
Raises:
exceptions.EnableServicePermissionDeniedException: when enabling the API
fails with a 403 or 404 error code.
api_lib_exceptions.HttpException: Another miscellaneous error with the
servicemanagement service.
"""
log.status.Print('Enabling service [{0}] on project [{1}]...'.format(
service_name, project_id))
# Enable the service
op = serviceusage.EnableApiCall(project_id, service_name)
if not is_async and not op.done:
op = services_util.WaitOperation(op.name, serviceusage.GetOperation)
services_util.PrintOperation(op)
def EnableServiceIfDisabled(project_id, service_name, is_async=False):
"""Check to see if the service is enabled, and if it is not, do so.
Args:
project_id: The ID of the project for which to enable the service.
service_name: The name of the service to enable on the project.
is_async: bool, if True, print the operation ID and return immediately,
without waiting for the op to complete.
Raises:
exceptions.ListServicesPermissionDeniedException: if a 403 or 404 error
is returned by the listing service.
exceptions.EnableServicePermissionDeniedException: when enabling the API
fails with a 403 or 404 error code.
api_lib_exceptions.HttpException: Another miscellaneous error with the
servicemanagement service.
"""
# If the service is enabled, we can return
if IsServiceEnabled(project_id, service_name):
log.debug('Service [{0}] is already enabled for project [{1}]'.format(
service_name, project_id))
return
EnableService(project_id, service_name, is_async)

View File

@@ -0,0 +1,244 @@
# -*- 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.
"""Wrapper for user-visible error exceptions to raise in the CLI."""
from googlecloudsdk.api_lib.util import exceptions as api_lib_exceptions
from googlecloudsdk.core import exceptions as core_exceptions
class Error(core_exceptions.Error):
"""Base class for Services errors."""
class EnableServiceException(Error):
"""Exception for enable service command."""
class ListServicesException(Error):
"""List services command."""
class ListMcpServicesException(Error):
"""List MCP services command."""
class GetServicePermissionDeniedException(Error):
"""Permission denied exception for get service command."""
class CreateQuotaOverridePermissionDeniedException(Error):
"""Permission denied exception for create quota override command."""
class UpdateQuotaOverridePermissionDeniedException(Error):
"""Permission denied exception for update quota override command."""
class DeleteQuotaOverridePermissionDeniedException(Error):
"""Permission denied exception for delete quota override command."""
class CreateConnectionsPermissionDeniedException(Error):
"""Permission denied exception for create connection command."""
class DeleteConnectionsPermissionDeniedException(Error):
"""Permission denied exception for create connection command."""
class UpdateConnectionsPermissionDeniedException(Error):
"""Permission denied exception for list connections command."""
class ListConnectionsPermissionDeniedException(Error):
"""Permission denied exception for list connections command."""
class EnableVpcServiceControlsPermissionDeniedException(Error):
"""Permission denied exception for enable vpc service controls command."""
class GetVpcServiceControlsPermissionDeniedException(Error):
"""Permission denied exception for get vpc service controls command."""
class DisableVpcServiceControlsPermissionDeniedException(Error):
"""Permission denied exception for disable vpc service controls command."""
class CreatePeeredDnsDomainPermissionDeniedException(Error):
"""Permission denied exception for create peered dns domain command."""
class DeletePeeredDnsDomainPermissionDeniedException(Error):
"""Permission denied exception for delete peered dns domain command."""
class ListPeeredDnsDomainsPermissionDeniedException(Error):
"""Permission denied exception for list peered dns domains command."""
class GenerateServiceIdentityPermissionDeniedException(Error):
"""Permission denied exception for generate service identitiy command."""
class GetConsumerPolicyException(Error):
"""Exception for get consumer policy."""
class UpdateConsumerPolicyException(Error):
"""Update consumer policy."""
class GetReverseDependencyClosurePermissionDeniedException(Error):
"""Permission denied exception for get reverse dependency closure."""
class ListFlattenedMembersPermissionDeniedException(Error):
"""Permission denied exception for list flattened members."""
class ListGroupMembersException(Error):
"""Exception for list group members."""
class FetchValueInfoPermissionDeniedException(Error):
"""Permission denied exception for fetch value info group members."""
class GetEffectivePolicyException(Error):
"""Exception for get effective policy."""
class FetchPublicValueInfoPermissionDeniedException(Error):
"""Permission denied exception for fetch public value info group members."""
class TestEnabledException(Error):
"""Exception for test enabled."""
class ListDescendantServicesException(Error):
"""Exception for list descendant services."""
class ListExpandedMembersException(Error):
"""Exception for list expanded members."""
class ListAncestorGroupsPermissionDeniedException(Error):
"""Permission denied exception for list ancestor groups."""
class BatchGetServiceException(Error):
"""Batch get service."""
class GetServiceException(Error):
"""Get service."""
class ListCategoryServicesException(Error):
"""List category service."""
class ListPublicServicesException(Error):
"""List public service."""
class ListSharedServicesException(Error):
"""List shared service."""
class AnalyzeConsumerPolicyException(Error):
"""Analyze consumer policy."""
class TestMcpEnabledException(Error):
"""Exception for test MCP enabled."""
class GetMcpPolicyException(Error):
"""Exception for get MCP policy."""
class GetContentSecurityPolicyException(Error):
"""Exception for get content security policy."""
class UpdateContentSecurityPolicyException(Error):
"""Exception for update content security policy."""
class AddContentSecurityProviderException(Error):
"""Exception for add content security provider."""
class RemoveContentSecurityProviderException(Error):
"""Exception for remove content security provider."""
class GetEffectiveMcpPolicyException(Error):
"""Exception for get effective MCP policy."""
class UpdateMcpPolicyException(Error):
"""Exception for update MCP policy."""
class EnableMcpServiceException(Error):
"""Exception for enable MCP service."""
class OperationErrorException(Error):
"""Operation error."""
class TimeoutError(Error):
"""Timeout error."""
def ReraiseError(err, klass):
"""Transform and re-raise error helper."""
core_exceptions.reraise(klass(api_lib_exceptions.HttpException(err)))
class ConfigError(Error):
"""Raised when unable to parse a config file."""
def __init__(self, message=None, **kwargs):
message = message or 'Config Error.'
super(ConfigError, self).__init__(message, **kwargs)
class InvalidGroupNameError(Error):
"""Raised when the group name is invalid."""
def __init__(self, group_name, **kwargs):
error_message = (
'Invalid group name format: {}. It should be '
'services/<service_name>/groups/<group_name>'.format(group_name)
)
super(InvalidGroupNameError, self).__init__(error_message, **kwargs)
class EmptyMembersError(Error):
"""Raised when the members list is empty."""
def __init__(self, group_name, **kwargs):
error_message = (
f'Group {group_name} is not found or access to its members is denied.'
)
super(EmptyMembersError, self).__init__(error_message, **kwargs)

View File

@@ -0,0 +1,448 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 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.
"""services vpc-peering helper functions."""
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.services import exceptions
from googlecloudsdk.api_lib.util import apis
NETWORK_URL_FORMAT = 'projects/%s/global/networks/%s'
def CreateConnection(project_number, service, network, ranges):
"""Make API call to create a connection to a specific service.
Args:
project_number: The number of the project for which to peer the service.
service: The name of the service to peer with.
network: The network in consumer project to peer with.
ranges: The names of IP CIDR ranges for peering service to use.
Raises:
exceptions.CreateConnectionsPermissionDeniedException: when the create
connection API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The result of the create connection operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesConnectionsCreateRequest(
parent='services/' + service,
connection=messages.Connection(
network=NETWORK_URL_FORMAT % (project_number, network),
reservedPeeringRanges=ranges,
),
)
try:
return client.services_connections.Create(request)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e, exceptions.CreateConnectionsPermissionDeniedException
)
def DeleteConnection(project_number, service, network):
"""Make API call to delete an existing connection to a specific service.
Args:
project_number: The number of the project which is peered to the service.
service: The name of the service peered with.
network: The network in consumer project peered with.
Raises:
exceptions.DeleteConnectionsPermissionDeniedException: when the delete
connection API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The result of the delete connection operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = (
messages.ServicenetworkingServicesConnectionsDeleteConnectionRequest(
name='services/%s/connections/-' % service,
deleteConnectionRequest=messages.DeleteConnectionRequest(
consumerNetwork=NETWORK_URL_FORMAT % (project_number, network)
),
)
)
try:
return client.services_connections.DeleteConnection(request)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e, exceptions.DeleteConnectionsPermissionDeniedException
)
def UpdateConnection(project_number, service, network, ranges, force):
"""Make API call to update a connection to a specific service.
Args:
project_number: The number of the project for which to peer the service.
service: The name of the service to peer with.
network: The network in consumer project to peer with.
ranges: The names of IP CIDR ranges for peering service to use.
force: If true, update the connection even if the update can be destructive.
Raises:
exceptions.UpdateConnectionsPermissionDeniedException: when the update
connection API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The result of the update connection operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesConnectionsPatchRequest(
name='services/%s/connections/-' % service,
connection=messages.Connection(
network=NETWORK_URL_FORMAT % (project_number, network),
reservedPeeringRanges=ranges,
),
force=force,
)
try:
return client.services_connections.Patch(request)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e, exceptions.UpdateConnectionsPermissionDeniedException
)
def ListConnections(project_number, service, network):
"""Make API call to list connections of a network for a specific service.
Args:
project_number: The number of the project for which to peer the service.
service: The name of the service to peer with.
network: The network in consumer project to peer with.
Raises:
exceptions.ListConnectionsPermissionDeniedException: when the list
connections API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The list of connections.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# The API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesConnectionsListRequest(
parent='services/' + service,
network='projects/{0}/global/networks/{1}'.format(
project_number, network
),
)
try:
return client.services_connections.List(request).connections
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e, exceptions.ListConnectionsPermissionDeniedException
)
def EnableVpcServiceControls(project_number, service, network):
"""Make API call to enable VPC service controls for a specific service.
Args:
project_number: The number of the project which is peered with the service.
service: The name of the service to enable VPC service controls for.
network: The network in the consumer project peered with the service.
Raises:
exceptions.EnableVpcServiceControlsPermissionDeniedException: when the
enable VPC service controls API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The result of the enable VPC service controls operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesEnableVpcServiceControlsRequest(
enableVpcServiceControlsRequest=messages.EnableVpcServiceControlsRequest(
consumerNetwork=NETWORK_URL_FORMAT % (project_number, network)
),
parent='services/' + service,
)
try:
return client.services.EnableVpcServiceControls(request)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e, exceptions.EnableVpcServiceControlsPermissionDeniedException
)
def GetVpcServiceControls(project_number, service, network):
"""Make API call to get VPC service controls for a specific service.
Args:
project_number: The number of the project which is peered with the service.
service: The name of the service to get VPC service controls for.
network: The network in the consumer project peered with the service.
Raises:
exceptions.GetVpcServiceControlsPermissionDeniedException: when the
get VPC service controls API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The state of the VPC service controls for the peering connection.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
request = messages.ServicenetworkingServicesProjectsGlobalNetworksGetVpcServiceControlsRequest(
name='services/%s/projects/%s/global/networks/%s'
% (service, project_number, network)
)
try:
return client.services_projects_global_networks.GetVpcServiceControls(
request
)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e, exceptions.GetVpcServiceControlsPermissionDeniedException
)
def DisableVpcServiceControls(project_number, service, network):
"""Make API call to disable VPC service controls for a specific service.
Args:
project_number: The number of the project which is peered with the service.
service: The name of the service to disable VPC service controls for.
network: The network in the consumer project peered with the service.
Raises:
exceptions.DisableVpcServiceControlsPermissionDeniedException: when the
disable VPC service controls API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The result of the disable VPC service controls operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesDisableVpcServiceControlsRequest(
disableVpcServiceControlsRequest=messages.DisableVpcServiceControlsRequest(
consumerNetwork=NETWORK_URL_FORMAT % (project_number, network)
),
parent='services/' + service,
)
try:
return client.services.DisableVpcServiceControls(request)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e, exceptions.DisableVpcServiceControlsPermissionDeniedException
)
def CreatePeeredDnsDomain(project_number, service, network, name, dns_suffix):
"""Make API call to create a peered DNS domain for a specific connection.
Args:
project_number: The number of the project which is peered with the service.
service: The name of the service to create a peered DNS domain for.
network: The network in the consumer project peered with the service.
name: The name of the peered DNS domain.
dns_suffix: The DNS domain name suffix of the peered DNS domain.
Raises:
exceptions.CreatePeeredDnsDomainPermissionDeniedException: when the create
peered DNS domain API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The result of the create peered DNS domain operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesProjectsGlobalNetworksPeeredDnsDomainsCreateRequest(
parent='services/%s/projects/%s/global/networks/%s'
% (service, project_number, network),
peeredDnsDomain=messages.PeeredDnsDomain(dnsSuffix=dns_suffix, name=name),
)
try:
return client.services_projects_global_networks_peeredDnsDomains.Create(
request
)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e,
exceptions.CreatePeeredDnsDomainPermissionDeniedException,
)
def DeletePeeredDnsDomain(project_number, service, network, name):
"""Make API call to delete a peered DNS domain for a specific connection.
Args:
project_number: The number of the project which is peered with the service.
service: The name of the service to delete a peered DNS domain for.
network: The network in the consumer project peered with the service.
name: The name of the peered DNS domain.
Raises:
exceptions.DeletePeeredDnsDomainPermissionDeniedException: when the delete
peered DNS domain API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The result of the delete peered DNS domain operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesProjectsGlobalNetworksPeeredDnsDomainsDeleteRequest(
name='services/%s/projects/%s/global/networks/%s/peeredDnsDomains/%s'
% (service, project_number, network, name)
)
try:
return client.services_projects_global_networks_peeredDnsDomains.Delete(
request
)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e,
exceptions.DeletePeeredDnsDomainPermissionDeniedException,
)
def ListPeeredDnsDomains(project_number, service, network):
"""Make API call to list the peered DNS domains for a specific connection.
Args:
project_number: The number of the project which is peered with the service.
service: The name of the service to list the peered DNS domains for.
network: The network in the consumer project peered with the service.
Raises:
exceptions.ListPeeredDnsDomainsPermissionDeniedException: when the delete
peered DNS domain API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The list of peered DNS domains.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
# the API only takes project number, so we cannot use resource parser.
request = messages.ServicenetworkingServicesProjectsGlobalNetworksPeeredDnsDomainsListRequest(
parent='services/%s/projects/%s/global/networks/%s'
% (service, project_number, network)
)
try:
return client.services_projects_global_networks_peeredDnsDomains.List(
request
).peeredDnsDomains
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(
e,
exceptions.ListPeeredDnsDomainsPermissionDeniedException,
)
def GetOperation(name):
"""Make API call to get an operation.
Args:
name: The name of operation.
Raises:
exceptions.OperationErrorException: when the getting operation API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the peering
service.
Returns:
The long running operation.
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
request = messages.ServicenetworkingOperationsGetRequest(name=name)
try:
return client.operations.Get(request)
except (
apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError,
) as e:
exceptions.ReraiseError(e, exceptions.OperationErrorException)
def _GetClientInstance():
return apis.GetClientInstance('servicenetworking', 'v1', no_http=False)

View File

@@ -0,0 +1,229 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""Service Consumer Management API helper functions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from apitools.base.py import list_pager
from googlecloudsdk.api_lib.services import exceptions
from googlecloudsdk.api_lib.util import apis
_SERVICE_CONSUMER_RESOURCE = 'services/%s/%s'
_LIMIT_OVERRIDE_RESOURCE = '%s/producerOverrides/%s'
_VALID_CONSUMER_PREFIX = {'projects/', 'folders/', 'organizations/'}
def ListQuotaMetrics(service, consumer, page_size=None, limit=None):
"""List service quota metrics for a consumer.
Args:
service: The service to list metrics for.
consumer: The consumer to list metrics for, e.g. "projects/123".
page_size: The page size to list.
limit: The max number of metrics to return.
Raises:
exceptions.PermissionDeniedException: when listing metrics fails.
apitools_exceptions.HttpError: Another miscellaneous error with the service.
Returns:
The list of quota metrics
"""
_ValidateConsumer(consumer)
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
request = messages.ServiceconsumermanagementServicesConsumerQuotaMetricsListRequest(
parent=_SERVICE_CONSUMER_RESOURCE % (service, consumer))
return list_pager.YieldFromList(
client.services_consumerQuotaMetrics,
request,
limit=limit,
batch_size_attribute='pageSize',
batch_size=page_size,
field='metrics')
def UpdateQuotaOverrideCall(service,
consumer,
metric,
unit,
dimensions,
value,
force=False):
"""Update a quota override.
Args:
service: The service to update a quota override for.
consumer: The consumer to update a quota override for, e.g. "projects/123".
metric: The quota metric name.
unit: The unit of quota metric.
dimensions: The dimensions of the override in dictionary format. It can be
None.
value: The override integer value.
force: Force override update even if the change results in a substantial
decrease in available quota.
Raises:
exceptions.UpdateQuotaOverridePermissionDeniedException: when updating an
override fails.
apitools_exceptions.HttpError: Another miscellaneous error with the service.
Returns:
The quota override operation.
"""
_ValidateConsumer(consumer)
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
dimensions_message = _GetDimensions(messages, dimensions)
request = messages.ServiceconsumermanagementServicesConsumerQuotaMetricsImportProducerOverridesRequest(
parent=_SERVICE_CONSUMER_RESOURCE % (service, consumer),
v1Beta1ImportProducerOverridesRequest=messages
.V1Beta1ImportProducerOverridesRequest(
inlineSource=messages.V1Beta1OverrideInlineSource(
overrides=[
messages.V1Beta1QuotaOverride(
metric=metric,
unit=unit,
overrideValue=value,
dimensions=dimensions_message)
],),
force=force),
)
try:
return client.services_consumerQuotaMetrics.ImportProducerOverrides(request)
except (apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError) as e:
exceptions.ReraiseError(
e, exceptions.UpdateQuotaOverridePermissionDeniedException)
def DeleteQuotaOverrideCall(service,
consumer,
metric,
unit,
override_id,
force=False):
"""Delete a quota override.
Args:
service: The service to delete a quota aoverride for.
consumer: The consumer to delete a quota override for, e.g. "projects/123".
metric: The quota metric name.
unit: The unit of quota metric.
override_id: The override ID.
force: Force override deletion even if the change results in a substantial
decrease in available quota.
Raises:
exceptions.DeleteQuotaOverridePermissionDeniedException: when deleting an
override fails.
apitools_exceptions.HttpError: Another miscellaneous error with the service.
Returns:
The quota override operation.
"""
_ValidateConsumer(consumer)
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
parent = _GetMetricResourceName(service, consumer, metric, unit)
name = _LIMIT_OVERRIDE_RESOURCE % (parent, override_id)
request = messages.ServiceconsumermanagementServicesConsumerQuotaMetricsLimitsProducerOverridesDeleteRequest(
name=name,
force=force,
)
try:
return client.services_consumerQuotaMetrics_limits_producerOverrides.Delete(
request)
except (apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError) as e:
exceptions.ReraiseError(
e, exceptions.DeleteQuotaOverridePermissionDeniedException)
def _GetDimensions(messages, dimensions):
if dimensions is None:
return None
dt = messages.V1Beta1QuotaOverride.DimensionsValue
# sorted by key strings to maintain the unit test behavior consistency.
return dt(
additionalProperties=[
dt.AdditionalProperty(key=k, value=dimensions[k])
for k in sorted(dimensions.keys())
],)
def _GetMetricResourceName(service, consumer, metric, unit):
"""Get the metric resource name from metric name and unit.
Args:
service: The service to manage an override for.
consumer: The consumer to manage an override for, e.g. "projects/123".
metric: The quota metric name.
unit: The unit of quota metric.
Raises:
exceptions.Error: when the limit with given metric and unit is not found.
Returns:
The quota override operation.
"""
metrics = ListQuotaMetrics(service, consumer)
for m in metrics:
if m.metric == metric:
for q in m.consumerQuotaLimits:
if q.unit == unit:
return q.name
raise exceptions.Error('limit not found with name "%s" and unit "%s".' %
(metric, unit))
def GetOperation(name):
"""Make API call to get an operation.
Args:
name: The name of the operation.
Raises:
exceptions.OperationErrorException: when the getting operation API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the service.
Returns:
The result of the operation
"""
client = _GetClientInstance()
messages = client.MESSAGES_MODULE
request = messages.ServiceconsumermanagementOperationsGetRequest(name=name)
try:
return client.operations.Get(request)
except (apitools_exceptions.HttpForbiddenError,
apitools_exceptions.HttpNotFoundError) as e:
exceptions.ReraiseError(e, exceptions.OperationErrorException)
def _ValidateConsumer(consumer):
for prefix in _VALID_CONSUMER_PREFIX:
if consumer.startswith(prefix):
return
raise exceptions.Error('invalid consumer format "%s".' % consumer)
def _GetClientInstance():
return apis.GetClientInstance('serviceconsumermanagement', 'v1beta1')

View File

@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 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.
"""Common helper methods for Services commands."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import json
from apitools.base.py import encoding
from googlecloudsdk.api_lib.services import exceptions
from googlecloudsdk.api_lib.util import apis_internal
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import transport
from googlecloudsdk.core.util import retry
OP_BASE_CMD = 'gcloud services operations '
OP_DESCRIBE_CMD = OP_BASE_CMD + 'describe {0}'
OP_WAIT_CMD = OP_BASE_CMD + 'wait {0}'
SERVICES_COLLECTION = 'servicemanagement.services'
def GetMessagesModule():
# pylint:disable=protected-access
return apis_internal._GetMessagesModule('servicemanagement', 'v1')
def GetClientInstance():
"""Get a client instance for service management without resource quota."""
# pylint:disable=protected-access
# Specifically disable resource quota in all cases for service management.
# We need to use this API to turn on APIs and sometimes the user doesn't have
# this API turned on. We should always used the shared project to do this
# so we can bootstrap users getting the appropriate APIs enabled. If the user
# has explicitly set the quota project, then respect that.
# pylint: disable=g-import-not-at-top
from googlecloudsdk.core.credentials import transports
# pylint: enable=g-import-not-at-top
enable_resource_quota = (
properties.VALUES.billing.quota_project.IsExplicitlySet())
http_client = transports.GetApitoolsTransport(
response_encoding=transport.ENCODING,
enable_resource_quota=enable_resource_quota)
return apis_internal._GetClientInstance(
'servicemanagement', 'v1', http_client=http_client)
def GetValidatedProject(project_id):
"""Validate the project ID, if supplied, otherwise return the default project.
Args:
project_id: The ID of the project to validate. If None, gcloud's default
project's ID will be returned.
Returns:
The validated project ID.
"""
if project_id:
properties.VALUES.core.project.Validate(project_id)
else:
project_id = properties.VALUES.core.project.Get(required=True)
return project_id
def WaitOperation(name, get_op_func):
"""Wait till the operation is done.
Args:
name: The name of operation.
get_op_func: The function that gets the operation.
Raises:
exceptions.OperationErrorException: when the getting operation API fails.
apitools_exceptions.HttpError: Another miscellaneous error with the service.
Returns:
The result of the operation
"""
def _CheckOp(name, result):
op = get_op_func(name)
if op.done:
result.append(op)
return not op.done
# Wait for no more than 30 minutes while retrying the Operation retrieval
result = []
try:
retry.Retryer(
exponential_sleep_multiplier=1.1,
wait_ceiling_ms=10000,
max_wait_ms=30 * 60 * 1000).RetryOnResult(
_CheckOp, [name, result], should_retry_if=True, sleep_ms=2000)
except retry.MaxRetrialsException:
raise exceptions.TimeoutError('Timed out while waiting for '
'operation {0}. Note that the operation '
'is still pending.'.format(name))
return result[0] if result else None
def PrintOperation(op):
"""Print the operation.
Args:
op: The long running operation.
Raises:
OperationErrorException: if the operation fails.
Returns:
Nothing.
"""
if not op.done:
log.status.Print('Operation "{0}" is still in progress.'.format(op.name))
return
if op.error:
raise exceptions.OperationErrorException(
'The operation "{0}" resulted in a failure "{1}".\nDetails: "{2}".'.
format(op.name, op.error.message, op.error.details))
log.status.Print('Operation "{0}" finished successfully.'.format(op.name))
def PrintOperationWithResponse(op):
"""Print the operation with response.
Args:
op: The long running operation.
Raises:
OperationErrorException: if the operation fails.
Returns:
Nothing.
"""
if not op.done:
log.status.Print('Operation "{0}" is still in progress.'.format(op.name))
return
if op.error:
raise exceptions.OperationErrorException(
'The operation "{0}" resulted in a failure "{1}".\nDetails: "{2}".'.
format(op.name, op.error.message, op.error.details))
if op.response:
log.status.Print('Operation [{0}] complete. Result: {1}'.format(
op.name,
json.dumps(
encoding.MessageToDict(op.response),
sort_keys=True,
indent=4,
separators=(',', ':'))))
else:
log.status.Print('Operation "{0}" finished successfully.'.format(op.name))
def PrintOperationWithResponseForUpdateConsumerPolicy(op):
"""Print the operation with response for update consumer policy.
Args:
op: The long running operation.
Raises:
OperationErrorException: if the operation fails.
Returns:
Nothing.
"""
if not op.done:
log.status.Print('Operation "{0}" is still in progress.'.format(op.name))
return
if op.error:
error_message = op.error.message
# only apply the replacement if the error message contains the string
# 'Please specify force' to avoid replacing other messages.
modified_message = error_message.replace(
'Please specify force', 'Please specify --bypass-api-usage-check flag'
)
raise exceptions.OperationErrorException(
'The operation "{0}" resulted in a failure "{1}"'.format(
op.name, modified_message
)
)
if op.response:
log.status.Print(
'Operation [{0}] complete. Result: {1}'.format(
op.name,
json.dumps(
encoding.MessageToDict(op.response),
sort_keys=True,
indent=4,
separators=(',', ':'),
),
)
)
else:
log.status.Print('Operation "{0}" finished successfully.'.format(op.name))