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,14 @@
# -*- 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.

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Argcomplete completers for various config related things."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.command_lib.util import completers
from googlecloudsdk.core import module_util
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
def PropertiesCompleter(prefix, **unused_kwargs):
"""An argcomplete completer for property and section names."""
all_sections = properties.VALUES.AllSections()
options = []
if '/' in prefix:
# Section has been specified, only return properties under that section.
parts = prefix.split('/', 1)
section = parts[0]
prefix = parts[1]
if section in all_sections:
section_str = section + '/'
props = properties.VALUES.Section(section).AllProperties()
options.extend([section_str + p for p in props if p.startswith(prefix)])
else:
# No section. Return matching sections and properties in the default
# group.
options.extend([s + '/' for s in all_sections if s.startswith(prefix)])
section = properties.VALUES.default_section.name
props = properties.VALUES.Section(section).AllProperties()
options.extend([p for p in props if p.startswith(prefix)])
return options
def NamedConfigCompleter(prefix, **unused_kwargs):
"""An argcomplete completer for existing named configuration names."""
configs = list(named_configs.ConfigurationStore.AllConfigs().keys())
return [c for c in configs if c.startswith(prefix)]
class PropertyValueCompleter(completers.Converter):
"""A completer for a specific property value.
The property value to be completed is not known until completion time.
"""
def Complete(self, prefix, parameter_info):
properties.VALUES.core.print_completion_tracebacks.Set(True)
prop_name = parameter_info.GetValue('property')
if not prop_name:
# No property specified. This should have been caught by the caller.
return None
prop = properties.FromString(prop_name)
if not prop:
# Property is invalid. This should have been caught by the caller.
return None
if prop.choices:
# Fixed set of possible values - easy.
return [c for c in prop.choices if c.startswith(prefix)]
if prop.completer:
# prop.completer is the module path for the resource value completer.
completer_class = module_util.ImportModule(prop.completer)
completer = completer_class(cache=self.cache)
return completer.Complete(prefix, parameter_info)
# No completer for this property.
return None
def Update(self, parameter_info=None, aggregations=None):
"""No completion cache for properties."""
del parameter_info, aggregations

View File

@@ -0,0 +1,98 @@
# -*- 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.
"""Supporting libraries for the config-helper command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.core import config
from googlecloudsdk.core.credentials import creds as c_creds
class ConfigHelperResult(object):
"""The result of the gcloud config config-helper command that gets serialzied.
Attributes:
credential: Credential, The OAuth2 credential information.
configuration: Configuration, Local Cloud SDK configuration information.
sentinels: Sentinels, Paths to various sentinel files.
"""
def __init__(self, credential, active_configuration, properties):
self.credential = Credential(credential)
self.configuration = Configuration(active_configuration, properties)
self.sentinels = Sentinels(config.Paths().config_sentinel_file)
class Credential(object):
"""Holder for credential data.
Attributes:
access_token: str, The current OAuth2 access token.
token_expiry: str, The expiry time in UTC as an RFC3339 formatted string.
id_token: str, The current OAuth2 identity token, if present.
"""
_EXPIRY_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
def __init__(self, cred):
if c_creds.IsOauth2ClientCredentials(cred):
self.access_token = cred.access_token
expiry = getattr(cred, 'token_expiry', None)
else:
self.access_token = cred.token
expiry = getattr(cred, 'expiry', None)
self.token_expiry = (
expiry.strftime(Credential._EXPIRY_FORMAT) if expiry else None)
# The cache blanks the token_response field, so if it's present that
# indicates there's either no cache entry, or we just refreshed tokens.
# Either way, the response is fresher.
token_response = getattr(cred, 'token_response', None)
if token_response:
id_token = token_response.get('id_token', None)
else:
id_token = getattr(cred, 'id_tokenb64', None)
self.id_token = id_token
class Configuration(object):
"""Holder for configuration data.
Attributes:
active_configuration: str, The name of the active configuration.
properties: {str: {str: str}}, A dict of section names to properties and
values.
"""
def __init__(self, active_configuration, properties):
self.active_configuration = active_configuration
self.properties = properties
class Sentinels(object):
"""Holder for sentinel file locations.
Attributes:
config_sentinel: str, The path to the sentinel that indicates changes were
made to properties or the active configuration.
"""
def __init__(self, config_sentinel):
self.config_sentinel = config_sentinel

View File

@@ -0,0 +1,286 @@
# -*- 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.
"""Helpers to validate config set values."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import os
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.cloudresourcemanager import projects_api
from googlecloudsdk.api_lib.compute import base_classes
from googlecloudsdk.api_lib.util import exceptions as api_lib_util_exceptions
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.projects import util as projects_util
from googlecloudsdk.core import config
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.credentials import creds as c_creds
from googlecloudsdk.core.credentials import store as c_store
from googlecloudsdk.core.universe_descriptor import universe_descriptor
def WarnIfSettingNonExistentRegionZone(value, zonal=True) -> bool:
"""Warn if setting 'compute/region' or 'compute/zone' to wrong value."""
zonal_msg = (
'{} is not a valid zone. Run `gcloud compute zones list` to '
'get all zones.'.format(value)
)
regional_msg = (
'{} is not a valid region. Run `gcloud compute regions list`'
'to get all regions.'.format(value)
)
if not value:
log.warning(zonal_msg if zonal else regional_msg)
return True
holder = base_classes.ComputeApiHolder(base.ReleaseTrack.GA)
client = holder.client
zone_request = [(
client.apitools_client.zones,
'Get',
client.messages.ComputeZonesGetRequest(
project=properties.VALUES.core.project.GetOrFail(), zone=value
),
)]
region_request = [(
client.apitools_client.regions,
'Get',
client.messages.ComputeRegionsGetRequest(
project=properties.VALUES.core.project.GetOrFail(), region=value
),
)]
try:
errors = []
client.MakeRequests(zone_request if zonal else region_request, errors)
if errors and 404 in errors[0]:
log.warning(zonal_msg if zonal else regional_msg)
return True
elif not errors:
return False
except (
calliope_exceptions.ToolException,
apitools_exceptions.HttpError,
c_store.NoCredentialsForAccountException,
api_lib_util_exceptions.HttpException,
):
pass
log.warning(
'Property validation for compute/{} was skipped.'.format(
'zone' if zonal else 'region'
)
)
return False
def WarnIfSettingUniverseDomainWithNoDescriptorData(
universe_domain: str,
) -> bool:
"""Warn if setting 'core/universe_domain' with no cached descriptor data."""
universe_descriptor_data = universe_descriptor.UniverseDescriptor()
try:
cached_descriptor_data = universe_descriptor_data.Get(universe_domain)
if cached_descriptor_data:
return False
except universe_descriptor.UniverseDescriptorError as e:
log.warning(f'Failed to update descriptor data: {e}')
log.warning(
'Using gcloud without universe descriptor data outside the default'
' universe may lead to unexpected behavior.'
)
return True
return False
def WarnIfSettingApiEndpointOverrideOutsideOfConfigUniverse(value, prop):
"""Warn if setting 'api_endpoint_overrides/<api>' outside universe_domain."""
universe_domain = properties.VALUES.core.universe_domain.Get()
if universe_domain not in value:
log.warning(
f'The value set for [{prop}] was found to be associated with a universe'
f' domain outside of the current config universe [{universe_domain}].'
' Please create a new gcloud configuration for each universe domain'
' you make requests to using `gcloud config configurations create`'
' with the `--universe-domain` flag or switch to a configuration'
f' associated with [{value}].'
)
return True
return False
def WarnIfSettingAccountOutsideOfConfigUniverse(
account: str, account_universe_domain: str
) -> bool:
"""Warn if setting an account belonging to a different universe_domain.
This warning should only be displayed if the user sets their active account
to an existing credentialed account which does not match the config
universe_domain. If the user sets their active account to an uncredentialed
account, there is no way to determine what universe the account belongs to so
we do not warn in that case.
Args:
account: The account to set [core/account] property to.
account_universe_domain: The respective account universe domain.
Returns:
(Boolean) True if the account is outside of the configuration universe_domain
and warning is logged. False otherwise.
"""
config_universe_domain = properties.VALUES.core.universe_domain.Get()
if (
account_universe_domain
and account_universe_domain != config_universe_domain
):
log.warning(
f'This account [{account}] is from the universe domain'
f' [{account_universe_domain}] which does not match the current'
f' [core/universe_domain] property [{config_universe_domain}]. Update'
' them to match or create a new gcloud configuration for this universe'
' domain using `gcloud config configurations create` with the'
' `--universe-domain` flag or switch to a configuration associated with'
f' [{account_universe_domain}].'
)
return True
return False
def WarnIfSettingUniverseDomainOutsideOfConfigAccountUniverse(
universe_domain: str,
) -> bool:
"""Warn if setting a universe domain mismatched to config account domain.
This warning should only be displayed if the user sets their universe domain
property to a universe domain not associated with the current credentialed
account. If the user has their config set to an uncredentialed account, there
is no way to determine what universe that account belongs to so we do not warn
in that case.
Args:
universe_domain: The universe domain to set [core/universe_domain] property
to.
Returns:
(Boolean) True if the provided universe_domain is outside of the
configuration universe_domain and warning is logged. False otherwise.
"""
config_account = properties.VALUES.core.account.Get()
all_cred_accounts = c_store.AllAccountsWithUniverseDomains()
cred_universe_domains = []
for cred_account in all_cred_accounts:
if cred_account.account == config_account:
cred_universe_domains.append(cred_account.universe_domain)
if cred_universe_domains and universe_domain not in cred_universe_domains:
cred_universe_domain_list = ', '.join(cred_universe_domains)
log.warning(
f'The config account [{config_account}] is available in the following '
f'universe domain(s): [{cred_universe_domain_list}], but it is not '
f'available in [{universe_domain}] which is specified by the '
'[core/universe_domain] property. Update'
' them to match or create a new gcloud configuration for this universe'
' domain using `gcloud config configurations create` with the'
' `--universe-domain` flag or switch to a configuration associated'
f' with [{cred_universe_domain_list}].'
)
return True
return False
def WarnIfSettingProjectWhenAdcExists(project):
"""Warn to update ADC if ADC file contains a different quota_project.
Args:
project: a new project to compare with quota_project in the ADC file.
Returns:
(Boolean) True if new project does not match the quota_project in the
ADC file and warning is logged. False otherwise.
"""
if not os.path.isfile(config.ADCFilePath()):
return False
credentials, _ = c_creds.GetGoogleAuthDefault().load_credentials_from_file(
config.ADCFilePath()
)
if credentials.quota_project_id == project:
return False
log.warning(
'Your active project does not match the quota project in your local'
' Application Default Credentials file. This might result in unexpected'
' quota issues.\n\nTo update your Application Default Credentials quota'
' project, use the `gcloud auth application-default set-quota-project`'
' command.'
)
return True
def WarnIfSettingProjectWithNoAccess(scope, project):
"""Warn if setting 'core/project' config to inaccessible project."""
# Only display a warning if the following conditions are true:
#
# * The current scope is USER (not occurring in the context of installation).
# * The 'core/account' value is set (a user has authed).
#
# If the above conditions are met, check that the project being set exists
# and is accessible to the current user, otherwise show a warning.
if scope == properties.Scope.USER and properties.VALUES.core.account.Get():
project_ref = projects_util.ParseProject(project)
try:
with base.WithLegacyQuota():
project = projects_api.Get(
project_ref, disable_api_enablement_check=True
)
# Check project environment tag
if project:
projects_util.PrintEnvironmentTagMessage(
project.projectId
)
except (
apitools_exceptions.HttpError,
c_store.NoCredentialsForAccountException,
api_lib_util_exceptions.HttpException,
) as e:
warning_msg = (
'You do not appear to have access to project [{}] or it does not'
' exist.'.format(project)
)
if isinstance(e, apitools_exceptions.HttpError):
wrapped_error = api_lib_util_exceptions.HttpException(
e, error_format='{message}{details?\n{?}}'
)
warning_msg = wrapped_error.message
log.warning(warning_msg)
return True
return False
def WarnIfActivateUseClientCertificate(value):
"""Warn if setting context_aware/use_client_certificate to truthy."""
if value.lower() in ['1', 'true', 'on', 'yes', 'y']:
mtls_not_supported_msg = (
'Some services may not support client certificate authorization in '
'this version of gcloud. When a command sends requests to such '
'services, the requests will be executed without using a client '
'certificate.\n\n'
'Please run $ gcloud topic client-certificate for more information.'
)
log.warning(mtls_not_supported_msg)

View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Flags and helpers for the config related commands."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.core import properties
INSTALLATION_FLAG = base.Argument(
'--installation',
required=False,
action='store_true',
help="""\
If set, the property is updated for the entire Google Cloud CLI
installation. Otherwise, by default, the property is updated only in the
currently active configuration."""
)
def RequestedScope(args):
# This hackiness will go away when we rip out args.scope everywhere
install = 'installation' if getattr(args, 'installation', False) else None
scope_arg = getattr(args, 'scope', None)
return properties.Scope.FromId(scope_arg or install)

View File

@@ -0,0 +1,14 @@
# -*- 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.

View File

@@ -0,0 +1,76 @@
# -*- 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.
"""Library of methods for manipulating virtualenv setup."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import os
import platform
from googlecloudsdk.core.util import files
from googlecloudsdk.core.util import platforms
import six
# Python modules to install into virtual env environment
MODULES = [
'crcmod',
'grpcio',
'pyopenssl==24.2.1',
'google_crc32c',
'certifi',
'https://github.com/googleapis/enterprise-certificate-proxy/releases/download/v0.3.6/cryptography-42.0.7-{}.whl'
.format(
'cp39-abi3-macosx_10_12_universal2'
if platform.processor() == 'arm'
else 'cp37-abi3-macosx_10_12_x86_64'
),
'setuptools',
]
# Enable file name.
ENABLE_FILE = 'enabled'
def IsPy2():
"""Wrap six.PY2, needed because mocking six.PY2 breaks test lib things."""
return six.PY2
def IsWindows():
"""Wrapped because mocking directly can break test lib things."""
return platforms.OperatingSystem.IsWindows()
def VirtualEnvExists(ve_dir):
"""Returns True if Virtual Env already exists."""
return os.path.isdir(ve_dir)
def EnableFileExists(ve_dir):
"""Returns True if enable file exists."""
return os.path.exists('{}/{}'.format(ve_dir, ENABLE_FILE))
def CreateEnableFile(ve_dir):
"""Create enable file."""
files.WriteFileContents('{}/{}'.format(ve_dir, ENABLE_FILE), 'enabled')
def RmEnableFile(ve_dir):
"""Remove enable file."""
os.unlink('{}/{}'.format(ve_dir, ENABLE_FILE))