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,75 @@
# -*- 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.
"""config command group."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.core import properties
@base.UniverseCompatible
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA,
base.ReleaseTrack.BETA,
base.ReleaseTrack.PREVIEW,
base.ReleaseTrack.GA,
)
class Config(base.Group):
"""View and edit Google Cloud CLI properties.
The {command} command group lets you set, view and unset properties used by
Google Cloud CLI.
A configuration is a set of properties that govern the behavior of `gcloud`
and other Google Cloud CLI tools. The initial `default` configuration is set
when `gcloud init` is run. You can create additional named configurations
using `gcloud init` or `{command} configurations create`.
To display the path of the active configuration along with information
about the current `gcloud` environment, run $ gcloud info.
To switch between configurations, use `{command} configurations activate`.
gcloud supports several flags that have the same effect as properties in
a configuration (for example, gcloud supports both the `--project` flag and
`project` property). Properties differ from flags in that flags affect command
behavior on a per-invocation basis. Properties allow you to maintain the same
settings across command executions.
In addition to setting properties in a configuration, and the use of flags, it
is possible to override the value of a property with an environment variable.
The matching environment variable for a property is of the form
'CLOUDSDK_CATEGORY_PROPERTY'. For example, to demonstrate overriding
the ``project'' property in the ``core'' category to ``my-project'', use a
command like:
$ CLOUDSDK_CORE_PROJECT=my-project gcloud config get core/project
For more information on configurations, see `gcloud topic configurations`.
## AVAILABLE PROPERTIES
{properties}
"""
category = base.SDK_TOOLS_CATEGORY
detailed_help = {
'properties': properties.VALUES.GetHelpString(),
}

View File

@@ -0,0 +1,113 @@
# -*- 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 return config and auth context for use by external tools."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.config import config_helper
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
from googlecloudsdk.core.credentials import store
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.GA)
class ConfigurationHelper(base.Command):
"""A helper for providing auth and config data to external tools."""
detailed_help = {
'DESCRIPTION':
"""\
{description}
Tools can call out to this command to get gcloud's current auth and
configuration context when needed. This is appropriate when external
tools want to operate within the context of the user's current
gcloud session.
This command returns a nested data structure with the following
schema:
* credential
* access_token - string, The current OAuth2 access token
* token_expiry - string, The time the token will expire. This
can be empty for some credential types. It is a UTC time
formatted as: '%Y-%m-%dT%H:%M:%SZ'
* configuration
* active_configuration - string, The name of the active gcloud
configuration
* properties - {string: {string: string}}, The full set of
active gcloud properties
""",
'EXAMPLES':
"""\
This command should always be used with the --format flag to get the
output in a structured format.
To get the current gcloud context:
$ {command} --format=json
To get the current gcloud context after forcing a refresh of the
OAuth2 credentials:
$ {command} --format=json --force-auth-refresh
To set MIN_EXPIRY amount of time that if given, refresh the
credentials if they are within MIN_EXPIRY from expiration:
$ {command} --format=json --min-expiry=MIN_EXPIRY
By default, MIN_EXPIRY is set to be 0 second.
""",
}
@staticmethod
def Args(parser):
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--force-auth-refresh',
action='store_true',
help='Force a refresh of the credentials even if they have not yet '
'expired. By default, credentials will only refreshed when '
'necessary.')
group.add_argument(
'--min-expiry',
type=arg_parsers.Duration(lower_bound='0s', upper_bound='1h'),
help='If given, refresh the credentials if they are within MIN_EXPIRY '
'from expiration.',
default='0s')
def Run(self, args):
cred = store.Load(use_google_auth=True)
if args.force_auth_refresh:
store.Refresh(cred)
else:
store.RefreshIfExpireWithinWindow(cred, '{}'.format(args.min_expiry))
config_name = named_configs.ConfigurationStore.ActiveConfig().name
props = properties.VALUES.AllValues()
return config_helper.ConfigHelperResult(
credential=cred,
active_configuration=config_name,
properties=props,
)

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*- #
# Copyright 2015 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.
"""Configurations command group."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Configurations(base.Group):
"""Manage the set of gcloud named configurations."""
detailed_help = {
'DESCRIPTION': """\
{description}
The current configuration can be managed via the
CLOUDSDK_ACTIVE_CONFIG_NAME environment variable or a configuration
property. See `gcloud topic configurations` for an overview of named
configurations.
""",
}

View File

@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*- #
# Copyright 2015 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 activate named configuration."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.command_lib.config import config_validators
from googlecloudsdk.command_lib.projects import util as projects_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
@base.UniverseCompatible
class Activate(base.SilentCommand):
"""Activates an existing named configuration."""
detailed_help = {
'DESCRIPTION': """\
{description}
See `gcloud topic configurations` for an overview of named
configurations.
""",
'EXAMPLES': """\
To activate an existing configuration named `my-config`, run:
$ {command} my-config
To list all properties in the activated configuration, run:
$ gcloud config list --all
""",
}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'configuration_name',
completer=completers.NamedConfigCompleter,
help='Name of the configuration to activate')
def Run(self, args):
named_configs.ConfigurationStore.ActivateConfig(args.configuration_name)
log.status.write('Activated [{0}].\n'.format(args.configuration_name))
project_id = properties.VALUES.core.project.Get()
if project_id:
# Warning if current project does not match the one in ADC
config_validators.WarnIfSettingProjectWhenAdcExists(project_id)
projects_util.PrintEnvironmentTagMessage(project_id)
return args.configuration_name

View File

@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*- #
# Copyright 2015 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 named configuration."""
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.config import config_validators
from googlecloudsdk.command_lib.projects import util as projects_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.UniverseCompatible
class Create(base.SilentCommand):
"""Creates a new named configuration."""
detailed_help = {
'DESCRIPTION': """\
{description}
Except for special cases (NONE), configuration names start with a
lower case letter and contain only lower case letters a-z, digits 0-9,
and hyphens '-'.
See `gcloud topic configurations` for an overview of named
configurations.
""",
'EXAMPLES': """\
To create a new named configuration, run:
$ {command} my-config
""",
}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'configuration_name', help='Name of the configuration to create'
)
parser.add_argument(
'--activate',
action='store_true',
default=True,
help='If true, activate this configuration upon create.',
)
parser.add_argument(
'--universe-domain',
type=str,
hidden=True,
help=(
'If set, creates the configuration with the specified'
' [core/universe_domain].'
),
)
def Run(self, args):
created_config = named_configs.ConfigurationStore.CreateConfig(
args.configuration_name
)
log.CreatedResource(args.configuration_name)
if args.activate:
named_configs.ConfigurationStore.ActivateConfig(args.configuration_name)
log.status.Print('Activated [{0}].'.format(args.configuration_name))
project_id = properties.VALUES.core.project.Get()
if project_id:
projects_util.PrintEnvironmentTagMessage(
project_id
)
else:
log.status.Print(
'To use this configuration, activate it by running:\n'
' $ gcloud config configurations activate {name}\n\n'.format(
name=args.configuration_name
)
)
if args.universe_domain:
config_validators.WarnIfSettingUniverseDomainWithNoDescriptorData(
args.universe_domain
)
universe_descriptor_obj = universe_descriptor.UniverseDescriptor()
_, is_deprecated_and_switched = (
universe_descriptor_obj.UpdateDescriptorFromUniverseDomain(
args.universe_domain
)
)
# Avoid setting back the universe domain property if args.universe_domain
# is deprecated.
if not is_deprecated_and_switched:
created_config.PersistProperty(
'core', 'universe_domain', args.universe_domain
)
else:
log.status.Print('Domain is switched to primary.')
log.status.Print('Updated property [core/universe_domain].')
return args.configuration_name

View File

@@ -0,0 +1,170 @@
# -*- coding: utf-8 -*- #
# Copyright 2015 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 delete named configuration."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.core import config
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
from googlecloudsdk.core.configurations import properties_file
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.resource import resource_printer
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.UniverseCompatible
class Delete(base.SilentCommand):
"""Deletes a named configuration."""
detailed_help = {
'DESCRIPTION': """\
{description} You cannot delete a configuration that is active, even
when overridden with the --configuration flag. To delete the current
active configuration, first `gcloud config configurations activate`
another one.
See `gcloud topic configurations` for an overview of named
configurations.
""",
'EXAMPLES': """\
To delete an existing configuration named `my-config`, run:
$ {command} my-config
To delete more than one configuration, run:
$ {command} my-config1 my-config2
To list existing configurations, run:
$ gcloud config configurations list
""",
}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'configuration_names',
nargs='+',
completer=completers.NamedConfigCompleter,
help=(
'Name of the configuration to delete. '
'Cannot be currently active configuration.'
),
)
def _UniverseDomainSetInAnyConfig(self, universe_domain: str) -> bool:
"""Determines whether the universe domain is set in any other config.
Args:
universe_domain: The universe domain to check for in any other config.
Returns:
True if the universe domain is set in any other config, False otherwise.
"""
all_configs = named_configs.ConfigurationStore.AllConfigs()
for _, user_config in sorted(all_configs.items()):
props = properties.VALUES.AllValues(
list_unset=True,
include_hidden=True,
properties_file=properties_file.PropertiesFile(
[user_config.file_path]
),
only_file_contents=True,
)
if props['core'].get('universe_domain') == universe_domain:
return True
return False
def _DeleteUniverseDescriptor(self, universe_domain: str) -> None:
"""Deletes the universe descriptor if it is not used in any other config.
Args:
universe_domain: The universe domain of the descriptor to delete.
"""
universe_descriptor_obj = universe_descriptor.UniverseDescriptor()
if not self._UniverseDomainSetInAnyConfig(universe_domain):
universe_descriptor_obj.DeleteDescriptorFromUniverseDomain(
universe_domain
)
def _GetConfigurationUniverseDomain(self, config_name: str) -> str:
"""Returns the universe domain of the given configuration.
Args:
config_name: The name of the configuration to get the universe domain of.
Returns:
The universe domain of the given configuration or the default if not
found.
"""
all_named_configs = named_configs.ConfigurationStore.AllConfigs()
for _, user_config in sorted(all_named_configs.items()):
if user_config.name == config_name:
props = properties.VALUES.AllValues(
list_unset=True,
include_hidden=True,
properties_file=properties_file.PropertiesFile(
[user_config.file_path]
),
only_file_contents=True,
)
return (
props['core'].get('universe_domain')
or properties.VALUES.core.universe_domain.default
)
return properties.VALUES.core.universe_domain.default
def Run(self, args):
# Fail the delete operation when we're attempting to delete the
# active config.
active_config = named_configs.ConfigurationStore.ActiveConfig()
if active_config.name in args.configuration_names:
raise named_configs.NamedConfigError(
'Deleting named configuration failed because configuration '
'[{0}] is set as active. Use `gcloud config configurations '
'activate` to change the active configuration.'.format(
active_config.name
)
)
fmt = 'list[title="The following configurations will be deleted:"]'
resource_printer.Print(args.configuration_names, fmt, out=log.status)
console_io.PromptContinue(default=True, cancel_on_no=True)
for configuration_name in args.configuration_names:
delete_config_universe_domain = self._GetConfigurationUniverseDomain(
configuration_name
)
named_configs.ConfigurationStore.DeleteConfig(configuration_name)
config_store_to_delete = config.GetConfigStore(configuration_name)
config_store_to_delete.DeleteConfig()
try:
self._DeleteUniverseDescriptor(delete_config_universe_domain)
except universe_descriptor.UniverseDescriptorError as e:
log.warning(
'Failed to delete universe descriptor for universe domain %s: %s',
delete_config_universe_domain,
e,
)
log.DeletedResource(configuration_name)

View File

@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*- #
# Copyright 2015 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 describe named configuration."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
from googlecloudsdk.core.configurations import properties_file
class Describe(base.DescribeCommand):
"""Describes a named configuration by listing its properties."""
detailed_help = {
'DESCRIPTION': """\
{description}
See `gcloud topic configurations` for an overview of named
configurations.
""",
'EXAMPLES': """\
To describe an existing configuration named `my-config`, run:
$ {command} my-config
This is similar to:
$ gcloud config configurations activate my-config
$ gcloud config list
""",
}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'configuration_name',
completer=completers.NamedConfigCompleter,
help='Name of the configuration to describe')
parser.add_argument(
'--all', action='store_true',
help='Include unset properties in output.')
def Run(self, args):
all_configs = named_configs.ConfigurationStore.AllConfigs(
include_none_config=True)
config = all_configs.get(args.configuration_name, None)
if not config:
raise named_configs.NamedConfigError(
'The configuration [{0}] does not exist.'
.format(args.configuration_name))
return {
'name': config.name,
'is_active': config.is_active,
'properties': properties.VALUES.AllValues(
list_unset=args.all,
properties_file=properties_file.PropertiesFile([config.file_path]),
only_file_contents=True),
}

View File

@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*- #
# Copyright 2015 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 list named configuration."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
from googlecloudsdk.core.configurations import properties_file
import six
class List(base.ListCommand):
"""Lists existing named configurations."""
detailed_help = {
'DESCRIPTION': """\
{description}
Run `$ gcloud topic configurations` for an overview of named
configurations.
""",
'EXAMPLES': """\
To list all available configurations, run:
$ {command}
""",
}
@staticmethod
def Args(parser):
base.PAGE_SIZE_FLAG.RemoveFromParser(parser)
base.URI_FLAG.RemoveFromParser(parser)
configs = named_configs.ConfigurationStore.AllConfigs()
table_format = """table(
name,
is_active,
properties.core.account,
properties.core.project,
properties.compute.zone:label=COMPUTE_DEFAULT_ZONE,
properties.compute.region:label=COMPUTE_DEFAULT_REGION)
"""
for _, config in sorted(six.iteritems(configs)):
props = properties.VALUES.AllValues(
list_unset=True,
include_hidden=True,
properties_file=properties_file.PropertiesFile([config.file_path]),
)
config_universe = props['core'].get('universe_domain')
if (
config_universe
and config_universe != properties.VALUES.core.universe_domain.default
):
table_format = """table(
name,
is_active,
properties.core.account,
properties.core.project,
properties.core.universe_domain,
properties.compute.zone:label=COMPUTE_DEFAULT_ZONE,
properties.compute.region:label=COMPUTE_DEFAULT_REGION)
"""
break
parser.display_info.AddFormat(table_format)
def Run(self, args):
configs = named_configs.ConfigurationStore.AllConfigs()
for _, config in sorted(six.iteritems(configs)):
props = properties.VALUES.AllValues(
list_unset=True,
properties_file=properties_file.PropertiesFile([config.file_path]),
only_file_contents=True)
yield {
'name': config.name,
'is_active': config.is_active,
'properties': props,
}

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 rename named configuration."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.core import log
from googlecloudsdk.core.configurations import named_configs
class Rename(base.SilentCommand):
"""Renames a named configuration."""
detailed_help = {
'DESCRIPTION': """\
{description}
See `gcloud topic configurations` for an overview of named
configurations.
""",
'EXAMPLES': """\
To rename an existing configuration named `my-config`, run:
$ {command} my-config --new-name=new-config
""",
}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'configuration_name',
completer=completers.NamedConfigCompleter,
help='Name of the configuration to rename')
parser.add_argument(
'--new-name',
required=True,
help='Specifies the new name of the configuration.')
def Run(self, args):
named_configs.ConfigurationStore.RenameConfig(args.configuration_name,
args.new_name)
log.status.Print('Renamed [{0}] to be [{1}].'.format(
args.configuration_name, args.new_name))
return args.new_name

View File

@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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 set properties."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exc
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
import six
class Get(base.Command):
"""Print the value of a Google Cloud CLI property.
{command} prints the property value from your active client side configuration
only.
## AVAILABLE PROPERTIES
{properties}
## EXAMPLES
To print the project property in the core section, run:
$ {command} project
To print the zone property in the compute section, run:
$ {command} compute/zone
"""
detailed_help = {'properties': properties.VALUES.GetHelpString()}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'property',
metavar='SECTION/PROPERTY',
completer=completers.PropertiesCompleter,
help='The property to be fetched. Note that `SECTION/` is optional'
' while referring to properties in the core section.')
parser.display_info.AddFormat('value(.)')
def Run(self, args):
config_name = named_configs.ConfigurationStore.ActiveConfig().name
if config_name != 'default':
log.status.write('Your active configuration is: [{0}]\n'.format(
config_name))
section, prop = properties.ParsePropertyString(args.property)
if not prop:
if section:
err_msg = ('You cannot call get on a SECTION/. '
'Did you mean `gcloud config list SECTION`?')
raise c_exc.InvalidArgumentException('property', err_msg)
raise c_exc.InvalidArgumentException(
'property', 'Must be in the form: [SECTION/]PROPERTY')
try:
value = properties.VALUES.Section(section).Property(prop).Get(
validate=True)
if not value:
# Writing message to stderr but returning any potentially empty
# value to caller as is
log.err.Print('(unset)')
if section == properties.VALUES.api_endpoint_overrides.name:
api_version = apis.ResolveVersion(prop)
default_endpoint = apis.GetEffectiveApiEndpoint(prop, api_version)
log.status.Print('Defaults to ', default_endpoint)
except properties.InvalidValueError as e:
# Writing warning to stderr but returning invalid value as is
log.warning(six.text_type(e))
value = properties.VALUES.Section(section).Property(prop).Get(
validate=False)
return value

View File

@@ -0,0 +1,27 @@
# -*- 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 set properties."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from surface.config import get
@base.Hidden
class GetValue(get.Get):
"""Alias for `gcloud config get`, for backwards compatibility."""

View File

@@ -0,0 +1,133 @@
# -*- 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.
"""Command to list properties."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.command_lib.projects import util as projects_util
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.configurations import named_configs
class BadConfigListInvocation(exceptions.Error):
"""Exception for incorrect invocations of `config list`."""
@base.UniverseCompatible
class List(base.ListCommand):
"""List Google Cloud CLI properties for the currently active configuration.
{command} lists the properties of the specified section using the
active configuration. These include the account used to authorize access to
Google Cloud, the current Google Cloud project, and the default Compute Engine
region and zone, if set. See `gcloud topic configurations` for more about
configurations.
## AVAILABLE PROPERTIES
{properties}
## EXAMPLES
To list the set project property in the core section, run:
$ {command} project
To list the set zone property in the compute section, run:
$ {command} compute/zone
To list all the set properties in the compute section, run:
$ {command} compute/
To list all the properties in the compute section, run:
$ {command} compute/ --all
To list all the properties, run:
$ {command} --all
Note, you cannot specify both `--all` and a property name. Only a section name
and the `--all` flag can be used together in the format `gcloud config list
<SECTION>/ --all`.
"""
detailed_help = {'properties': properties.VALUES.GetHelpString()}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'--all',
action='store_true',
help='List all set and unset properties that match the arguments.')
parser.add_argument(
'property',
metavar='SECTION/PROPERTY',
nargs='?',
completer=completers.PropertiesCompleter,
help='Property to be listed. Note that SECTION/ is optional while '
'referring to properties in the core section.')
base.PAGE_SIZE_FLAG.RemoveFromParser(parser)
base.URI_FLAG.RemoveFromParser(parser)
parser.display_info.AddFormat('config')
parser.display_info.AddCacheUpdater(None)
def _GetPropertiesToDisplay(self, args):
"""List available regular properties."""
section, prop = properties.ParsePropertyString(args.property)
if prop:
return {
section: {
prop: properties.VALUES.Section(section).Property(prop).Get()
}
}
if section:
return {
section:
properties.VALUES.Section(section).AllValues(list_unset=args.all)
}
return properties.VALUES.AllValues(list_unset=args.all)
def Run(self, args):
_, prop = properties.ParsePropertyString(args.property)
if args.all and prop:
raise BadConfigListInvocation(
'Commands with the `--all` flag must be in the format `gcloud '
'config list <SECTION>/` without a property specified.'
)
project_id = properties.VALUES.core.project.Get()
if project_id:
projects_util.PrintEnvironmentTagMessage(
project_id
)
return self._GetPropertiesToDisplay(args)
def Epilog(self, resources_were_displayed):
config_name = named_configs.ConfigurationStore.ActiveConfig().name
log.status.write(
'\nYour active configuration is: [{0}]\n'.format(config_name)
)

View File

@@ -0,0 +1,191 @@
# -*- 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.
"""Command to set properties."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exc
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.command_lib.config import config_validators
from googlecloudsdk.command_lib.config import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.credentials import store as c_store
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.UniverseCompatible
class Set(base.Command):
"""Set a Google Cloud CLI property.
{command} sets the specified property in your active configuration only. A
property governs the behavior of a specific aspect of Google Cloud CLI such as
the service account to use or the verbosity level of logs. To
set the property across all configurations, use the `--installation` flag. For
more information regarding creating and using configurations, see
`gcloud topic configurations`.
To view a list of properties currently in use, run `gcloud config list`.
To unset properties, use `gcloud config unset`.
Google Cloud CLI comes with a `default` configuration. To create multiple
configurations, use `gcloud config configurations create`, and
`gcloud config configurations activate` to switch between them.
Note: If you are using Cloud Shell, your gcloud command-line tool preferences
are stored in a temporary `tmp` folder, set for your current tab only, and do
not persist across sessions. For details on how to make these configurations
persist, refer to the Cloud Shell
guide on setting gcloud command-line tool preferences:
https://cloud.google.com/shell/docs/configuring-cloud-shell#gcloud_command-line_tool_preferences.
## AVAILABLE PROPERTIES
{properties}
## EXAMPLES
To set the `project` property in the core section, run:
$ {command} project PROJECT_ID
To set the `zone` property in the `compute` section, run:
$ {command} compute/zone ZONE_NAME
To disable prompting for scripting, run:
$ {command} disable_prompts true
To set a proxy with the appropriate type, and specify the address and port on
which to reach it, run:
$ {command} proxy/type http
$ {command} proxy/address 1.234.56.78
$ {command} proxy/port 8080
For a full list of accepted values, see
https://cloud.google.com/sdk/gcloud/reference/topic/configurations#AVAILABLE-PROPERTIES.
"""
detailed_help = {'properties': properties.VALUES.GetHelpString()}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'property',
metavar='SECTION/PROPERTY',
completer=completers.PropertiesCompleter,
help='Property to be set. Note that SECTION/ is optional while '
'referring to properties in the core section, i.e., using either '
'`core/project` or `project` is a valid way of setting a project. '
'Using section names is required for setting other properties like '
'`compute/region`. Consult the Available Properties section below '
'for a comprehensive list of properties.')
parser.add_argument(
'value',
completer=completers.PropertyValueCompleter,
help='Value to be set.')
flags.INSTALLATION_FLAG.AddToParser(parser)
def Run(self, args):
scope = (properties.Scope.INSTALLATION if args.installation
else properties.Scope.USER)
prop = properties.FromString(args.property)
if not prop:
raise c_exc.InvalidArgumentException(
'property', 'Must be in the form: [SECTION/]PROPERTY')
scope_msg = ''
if args.installation:
scope_msg = 'installation '
if prop == properties.VALUES.context_aware.use_client_certificate:
config_validators.WarnIfActivateUseClientCertificate(args.value)
showed_warning = False
if prop == properties.VALUES.core.project:
# This warning is informational and should not ask for confirmation
config_validators.WarnIfSettingProjectWhenAdcExists(args.value)
showed_warning = config_validators.WarnIfSettingProjectWithNoAccess(
scope, args.value)
if prop == properties.VALUES.compute.zone:
showed_warning = config_validators.WarnIfSettingNonExistentRegionZone(
args.value, zonal=True)
if prop == properties.VALUES.compute.region:
showed_warning = config_validators.WarnIfSettingNonExistentRegionZone(
args.value, zonal=False)
if prop.section == properties.VALUES.api_endpoint_overrides.name:
showed_warning = config_validators.WarnIfSettingApiEndpointOverrideOutsideOfConfigUniverse(
args.value, prop
)
cred_account_universe_domain = None
if prop == properties.VALUES.core.account:
cred_account_universe_domain = (
c_store.GetCredentialedAccountUniverseDomain(args.value)
)
showed_warning = (
config_validators.WarnIfSettingAccountOutsideOfConfigUniverse(
args.value, cred_account_universe_domain
)
)
is_deprecated_and_switched = False
if prop == properties.VALUES.core.universe_domain:
showed_warning = config_validators.WarnIfSettingUniverseDomainOutsideOfConfigAccountUniverse(
args.value
)
showed_warning = (
config_validators.WarnIfSettingUniverseDomainWithNoDescriptorData(
args.value
)
) or showed_warning
universe_descriptor_obj = universe_descriptor.UniverseDescriptor()
is_deprecated_and_switched = (
universe_descriptor_obj.IsDomainUpdatedFromDeprecatedToPrimary(
args.value
)
)
if showed_warning and not args.quiet and console_io.CanPrompt():
if not console_io.PromptContinue(
'Are you sure you wish to set {0}property [{1}] to {2}?'.format(
scope_msg, prop, args.value
)
):
return
# Avoid setting back the universe domain property if args.value is
# deprecated.
if not is_deprecated_and_switched:
properties.PersistProperty(prop, args.value, scope=scope)
else:
log.status.Print('Domain is switched to primary.')
log.status.Print('Updated {0}property [{1}].'.format(scope_msg, prop))
if cred_account_universe_domain and showed_warning:
properties.PersistProperty(
properties.VALUES.core.universe_domain,
cred_account_universe_domain,
scope=scope,
)
log.status.Print('Updated [core/universe_domain] to match.')

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Configurations command group."""
from googlecloudsdk.calliope import base
@base.Hidden
@base.UniverseCompatible
@base.ReleaseTracks(base.ReleaseTrack.GA)
class UniverseDescriptors(base.Group):
"""Manage the universe descriptor data."""

View File

@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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 universe descriptor data entry in the cache."""
from googlecloudsdk.calliope import base
from googlecloudsdk.core import log
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.UniverseCompatible
class Create(base.Command):
"""Create a new universe descriptor data entry."""
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'universe_domain',
help='Universe domain of the universe descriptor to add to the cache.',
)
def Run(self, args):
del self
universe_descriptor_obj = universe_descriptor.UniverseDescriptor()
try:
universe_descriptor_obj.Get(
args.universe_domain, fetch_if_not_cached=False
)
except universe_descriptor.UniverseDescriptorError:
pass
else:
log.error(
'Universe descriptor with universe domain [%s] already cached.',
args.universe_domain,
)
return
universe_descriptor_obj.UpdateDescriptorFromUniverseDomain(
args.universe_domain
)
log.status.Print(
'Universe descriptor with universe domain [%s] cached.'
% args.universe_domain,
)

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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 delete universe descriptor data."""
from googlecloudsdk.calliope import base
from googlecloudsdk.core import log
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.Hidden
@base.UniverseCompatible
class Delete(base.Command):
"""Delete universe descriptor data."""
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'universe_domain',
help='Universe domain of the descriptor to delete.',
)
def Run(self, args):
del self
universe_descriptor_obj = universe_descriptor.UniverseDescriptor()
log.warning(
'The universe descriptor with universe domain [%s] will be deleted:',
args.universe_domain,
)
console_io.PromptContinue(default=True, cancel_on_no=True)
try:
universe_descriptor_obj.DeleteDescriptorFromUniverseDomain(
args.universe_domain
)
log.DeletedResource(
'Universe descriptor with universe domain [%s]' % args.universe_domain
)
except universe_descriptor.UniverseDescriptorError:
log.warning(
'No descriptor found for universe domain [%s].', args.universe_domain
)
return

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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 describe universe descriptor data."""
from cloudsdk.google.protobuf import json_format
from googlecloudsdk.calliope import base
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.Hidden
@base.UniverseCompatible
class Describe(base.Command):
"""Describe universe descriptor data dict in the cache."""
detailed_help = {
'EXAMPLES': """\
To describe an existing universe descriptor with domain `my-universe-domain.com`, run:
$ {command} my-universe-domain.com
""",
}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'universe_domain',
help='Universe domain of the universe descriptor to describe.',
)
def Run(self, args):
del self
universe_descriptor_obj = universe_descriptor.UniverseDescriptor()
descriptor_json = universe_descriptor_obj.Get(
args.universe_domain, fetch_if_not_cached=False
)
return json_format.MessageToDict(
descriptor_json, always_print_fields_with_no_presence=True
)

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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 list cached universe descriptors."""
import sqlite3
from googlecloudsdk.calliope import base
from googlecloudsdk.core import config
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.UniverseCompatible
class List(base.ListCommand):
"""List cached universe descriptors."""
@staticmethod
def Args(parser):
base.PAGE_SIZE_FLAG.RemoveFromParser(parser)
base.URI_FLAG.RemoveFromParser(parser)
table_format = """table(
universe_domain,
universe_short_name,
project_prefix,
authentication_domain,
cloud_web_domain,
version)
"""
parser.display_info.AddFormat(table_format)
def Run(self, unused_args):
config_universe_domains = universe_descriptor.GetAllConfigUniverseDomains()
config_store = config.GetConfigStore(
universe_descriptor.CONFIG_CACHE_DESCRIPTOR_DATA_TABLE_NAME
)
for universe_domain in config_universe_domains:
try:
yield config_store.GetJSON(universe_domain)
except sqlite3.Error:
pass

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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 update a universe descriptor data entry in the cache."""
from googlecloudsdk.calliope import base
from googlecloudsdk.core import log
from googlecloudsdk.core.universe_descriptor import universe_descriptor
@base.UniverseCompatible
class Update(base.Command):
"""Update universe descriptor data in the cache."""
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'universe_domain',
help=(
'Universe domain of the universe descriptor to update in the cache.'
),
)
def Run(self, args):
"""Run the update command."""
del self
universe_descriptor_obj = universe_descriptor.UniverseDescriptor()
try:
universe_descriptor_obj.Get(
args.universe_domain, fetch_if_not_cached=False
)
except universe_descriptor.UniverseDescriptorError:
log.error(
'Universe descriptor with universe domain [%s] is not cached.',
args.universe_domain,
)
else:
universe_descriptor_obj.UpdateDescriptorFromUniverseDomain(
args.universe_domain
)
log.status.Print(
'Universe descriptor with universe domain [%s] updated.'
% args.universe_domain,
)

View File

@@ -0,0 +1,80 @@
# -*- 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.
"""Command to unset properties."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exc
from googlecloudsdk.command_lib.config import completers
from googlecloudsdk.command_lib.config import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
class Unset(base.Command):
"""Unset a Google Cloud CLI property.
By default, unsets the property in your active configuration only. Use the
`--installation` flag to unset the property across all configurations. See
`gcloud topic configurations` for more information.
## AVAILABLE PROPERTIES
{properties}
## EXAMPLES
To unset the project property in the core section, run:
$ {command} project
To unset the zone property in the compute section, run:
$ {command} compute/zone
"""
detailed_help = {'properties': properties.VALUES.GetHelpString()}
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'property',
metavar='SECTION/PROPERTY',
completer=completers.PropertiesCompleter,
help='The property to be unset. Note that SECTION/ is optional while '
'referring to properties in the core section.')
flags.INSTALLATION_FLAG.AddToParser(parser)
def Run(self, args):
"""Runs this command."""
scope = (properties.Scope.INSTALLATION if args.installation
else properties.Scope.USER)
prop = properties.FromString(args.property)
if not prop:
raise c_exc.InvalidArgumentException(
'property', 'Must be in the form: [SECTION/]PROPERTY')
properties.PersistProperty(prop, None, scope=scope)
scope_msg = ''
if args.installation:
scope_msg = 'installation '
log.status.Print('Unset {0}property [{1}].'.format(scope_msg, prop))

View File

@@ -0,0 +1,29 @@
# -*- 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.
"""virtualenv command group."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.core import properties
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.GA)
class VirtualEnv(base.Group):
"""Manage Cloud SDK virtual env setup."""

View File

@@ -0,0 +1,113 @@
# -*- 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.
"""Command to create virtualenv environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.config.virtualenv import util
from googlecloudsdk.core import config
from googlecloudsdk.core import execution_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.util import files
@base.Hidden
@base.DefaultUniverseOnly
class Create(base.Command):
"""Create a virtualenv environment.
Create a virtual env context for gcloud to run in. Installs several
python modules into the virtual environment. The virtual env environment
can be inspected via the `{parent_command} describe` command. Note this
command does not enable the virtualenv environment, you must run
`{parent_command} enable` to do so.
"""
@staticmethod
def Args(parser):
"""Adds args for this command."""
parser.add_argument(
'--python-to-use',
help='Absolute path to python to use to create virtual env.')
def Run(self, args):
if util.IsPy2() and not args.IsSpecified('python_to_use'):
log.error('Virtual env support requires Python 3.')
raise exceptions.ExitCodeNoError(exit_code=3)
if util.IsWindows():
log.error('Virtual env support not enabled on Windows.')
raise exceptions.ExitCodeNoError(exit_code=4)
if args.IsSpecified('python_to_use'):
python = args.python_to_use
else:
try:
python = execution_utils.GetPythonExecutable()
except ValueError:
log.error('Failed to resolve python to use for virtual env.')
raise exceptions.ExitCodeNoError(exit_code=5)
ve_dir = config.Paths().virtualenv_dir
if util.VirtualEnvExists(ve_dir):
log.error('Virtual env setup {} already exists.'.format(ve_dir))
raise exceptions.ExitCodeNoError(exit_code=5)
succeeded_making_venv = False
try:
log.status.Print('Creating virtualenv...')
# python -m venv is preferred as it aligns the python used with
# the current in used Python.
ec = execution_utils.Exec([python, '-m', 'venv', ve_dir],
no_exit=True,
err_func=log.file_only_logger.debug,
out_func=log.file_only_logger.debug)
if ec != 0:
# Many linux vendors havea a history of having a broken python-venv
# package that will not work correctly, debian for example. If -m venv
# failed above we will attempt to use the virtualenv tool if it is
# installed and exists in $PATH.
ec = execution_utils.Exec(['virtualenv', '-q', '-p', python, ve_dir],
no_exit=True)
if ec != 0:
log.error('Virtual env setup failed.')
raise exceptions.ExitCodeNoError(exit_code=ec)
log.status.Print('Installing modules...')
install_modules = [
'{}/bin/pip3'.format(ve_dir), 'install', '--log',
'{}/install_module.log'.format(ve_dir),
'--disable-pip-version-check'
]
install_modules.extend(util.MODULES)
ec = execution_utils.Exec(install_modules, no_exit=True)
if ec == 0:
# prevent the cleanup that occurs in finally block
succeeded_making_venv = True
else:
log.error('Virtual env setup failed.')
if properties.IsInternalUserCheck():
log.error(
'You might need further authentication. See more at '
'go/gcloud-internal-auth.'
)
raise exceptions.ExitCodeNoError(exit_code=ec)
finally:
# If something went wrong we clean up any partial created ve_dir
if not succeeded_making_venv:
if util.VirtualEnvExists(ve_dir):
files.RmTree(ve_dir)

View File

@@ -0,0 +1,43 @@
# -*- 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.
"""Command to delete virtualenv environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.config.virtualenv import util
from googlecloudsdk.core import config
from googlecloudsdk.core import log
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.util import files
class Delete(base.Command):
"""Delete a virtualenv environment."""
def Run(self, args):
ve_dir = config.Paths().virtualenv_dir
if not util.VirtualEnvExists(ve_dir):
log.status.Print('Virtual env does not exist at {}.'.format(ve_dir))
raise exceptions.ExitCodeNoError(exit_code=1)
console_io.PromptContinue(
message='Delete virtual env setup at {}'.format(ve_dir),
cancel_on_no=True)
files.RmTree(ve_dir)

View File

@@ -0,0 +1,82 @@
# -*- 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.
"""Command to describe virtualenv environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.config.virtualenv import util
from googlecloudsdk.core import config
from googlecloudsdk.core import execution_utils
from googlecloudsdk.core import log
class VirtualEnvInfo(object):
def __init__(self, python_version, modules, enabled):
self.python_version = python_version
self.modules = modules
self.enabled = enabled
class Module(object):
def __init__(self, module_name, module_version):
self.module_name = module_name
self.module_version = module_version
@base.Hidden
class Describe(base.Command):
"""Describe a virtualenv environment."""
def Run(self, args):
ve_dir = config.Paths().virtualenv_dir
if not util.VirtualEnvExists(ve_dir):
log.error('Virtual env does not exist at {}.'.format(ve_dir))
raise exceptions.ExitCodeNoError(exit_code=1)
# The Python version being used.
python_version = 'NOT AVAILABLE'
def _ver(output):
self._version_output = output
ec = execution_utils.Exec(['{}/bin/python3'.format(ve_dir), '--version'],
no_exit=True, out_func=_ver)
if ec == 0:
version_parts = self._version_output.split(' ')
if len(version_parts) == 2:
python_version = version_parts[1]
# The modules installed in the environment.
modules = []
def _mod_output(output):
self._modules_stdout = output
execution_utils.Exec(['{}/bin/pip3'.format(ve_dir), 'freeze'],
no_exit=True, out_func=_mod_output)
for l in self._modules_stdout.split('\n'):
if '==' in l:
mn, mv = l.split('==')
modules.append(Module(mn, mv))
# The enable|disable state of the virtual env environment.
ve_enabled = False
if util.EnableFileExists(ve_dir):
ve_enabled = True
return VirtualEnvInfo(python_version, modules, ve_enabled)

View File

@@ -0,0 +1,42 @@
# -*- 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.
"""Command to disable virtualenv environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.config.virtualenv import util
from googlecloudsdk.core import config
from googlecloudsdk.core import log
@base.Hidden
class Disable(base.Command):
"""Disable a virtualenv environment."""
def Run(self, args):
ve_dir = config.Paths().virtualenv_dir
if util.VirtualEnvExists(ve_dir):
if util.EnableFileExists(ve_dir):
util.RmEnableFile(ve_dir)
log.status.Print('Virtual env disabled.')
else:
log.error('Virtual env does not exist at {}.'.format(ve_dir))
raise exceptions.ExitCodeNoError(exit_code=1)

View File

@@ -0,0 +1,44 @@
# -*- 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.
"""Command to enable virtualenv environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.config.virtualenv import util
from googlecloudsdk.core import config
from googlecloudsdk.core import log
@base.Hidden
class Enable(base.Command):
"""Enable a virtualenv environment."""
def Run(self, args):
ve_dir = config.Paths().virtualenv_dir
if util.VirtualEnvExists(ve_dir):
if not util.EnableFileExists(ve_dir):
util.CreateEnableFile(ve_dir)
log.status.Print('Virtual env enabled.')
else:
log.error('Virtual env does not exist at {}.'.format(ve_dir))
raise exceptions.ExitCodeNoError(exit_code=1)

View File

@@ -0,0 +1,51 @@
# -*- 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.
"""Command to update virtualenv environment."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.config.virtualenv import util
from googlecloudsdk.core import config
from googlecloudsdk.core import execution_utils
from googlecloudsdk.core import log
@base.Hidden
class Update(base.Command):
"""Update modules installed in a virtualenv environment."""
def Run(self, args):
ve_dir = config.Paths().virtualenv_dir
if not util.VirtualEnvExists(ve_dir):
log.error('Virtual env does not exist at {}.'.format(ve_dir))
raise exceptions.ExitCodeNoError(exit_code=1)
log.status.Print('Updating modules...')
update_modules = [
'{}/bin/pip3'.format(ve_dir), 'install', '--log',
'{}/update_module.log'.format(ve_dir), '-q',
'--disable-pip-version-check'
]
update_modules.extend(util.MODULES)
ec = execution_utils.Exec(update_modules, no_exit=True)
if ec != 0:
log.error('Failed to update modules.')
raise exceptions.ExitCodeNoError(exit_code=1)
log.status.Print('Modules updated.')