File: //snap/google-cloud-cli/396/lib/googlecloudsdk/api_lib/services/services_util.py
# -*- 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))