# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Read and write properties for the CloudSDK."""


import enum
import functools
import os
import re
import sys
import textwrap

from googlecloudsdk.core import argv_utils
from googlecloudsdk.core import config
from googlecloudsdk.core import exceptions
from googlecloudsdk.core.configurations import named_configs
from googlecloudsdk.core.configurations import properties_file as prop_files_lib
from googlecloudsdk.core.docker import constants as const_lib
from googlecloudsdk.core.resource import resource_printer_types as formats
from googlecloudsdk.core.util import encoding
from googlecloudsdk.core.util import http_proxy_types
from googlecloudsdk.core.util import scaled_integer
from googlecloudsdk.generated_clients.apis import apis_map
import six

# Try to parse the command line flags at import time to see if someone provided
# the --configuration flag.  If they did, this could affect the value of the
# properties defined in that configuration.  Since some libraries (like logging)
# use properties at startup, we want to use the correct configuration for that.
named_configs.FLAG_OVERRIDE_STACK.PushFromArgs(argv_utils.GetDecodedArgv())

_SET_PROJECT_HELP = """\
To set your project, run:

  $ gcloud config set project PROJECT_ID

or to unset it, run:

  $ gcloud config unset project"""

_VALID_PROJECT_REGEX = re.compile(
    r'^'
    # An optional domain-like component, ending with a colon, e.g.,
    # google.com:
    r'(?:(?:[-a-z0-9]{1,63}\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?'
    # Followed by a required identifier-like component, for example:
    #   waffle-house    match
    #   -foozle        no match
    #   Foozle         no match
    # We specifically disallow project number, even though some GCP backends
    # could accept them.
    # We also allow a leading digit as some legacy project ids can have
    # a leading digit.
    r'(?:(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))'
    r'$')

_VALID_ENDPOINT_OVERRIDE_REGEX = re.compile(
    r'^'
    # require http or https for scheme
    r'(?:https?)://'
    # netlocation portion of address. can be any of
    # - domain name
    # - 'localhost'
    # - ipv4 addr
    # - ipv6 addr
    r'(?:'  # begin netlocation
    # - domain name, e.g. 'test-foo.sandbox.googleapis.com', or 'localhost'
    r'(?:[A-Z0-9](?:[A-Z0-9-.])+)|'
    # - ipv4
    r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
    # - ipv6
    r'\[?[A-F0-9]*:[A-F0-9:]+\]?'
    r')'  # end netlocation
    # optional port
    r'(?::\d+)?'
    # require trailing slash, fragment optional
    r'(?:/|[/?]\S+/)'
    r'$',
    re.IGNORECASE)

_GRPC_DIRECTPATH = (
    r'(?:(?:dns|google-c2p):///'
    r'(?:(?:[A-Z0-9](?:[A-Z0-9-.])+)|'
    r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
    r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'
    r'(?::\d+)?(?:/|[/?]\S+/)?'
    r')'
)

_PURE_GRPC = (
    r'(?:'
    r'(?:(?:[A-Z0-9](?:[A-Z0-9-.])+)|'
    r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
    r'\[?[A-F0-9]*:[A-F0-9:]+\]?)'
    r'(?::\d+)'
    r')'
)

_HTTP_PATTERN = _VALID_ENDPOINT_OVERRIDE_REGEX.pattern[1:-1]  # strip ^ and $

_VALID_STORAGE_GRPC_ENDPOINT_OVERRIDE_REGEX = re.compile(
    r'^(?:' + _HTTP_PATTERN + r'|' + _GRPC_DIRECTPATH + r'|'
    + _PURE_GRPC + r')$',
    re.IGNORECASE,
)

_PUBSUB_NOTICE_URL = (
    'https://cloud.google.com/functions/docs/writing/background#event_parameter'
)

_B_468088880_FIXED = True


def Stringize(value):
  if isinstance(value, six.string_types):
    return value
  return str(value)


def ExistingAbsoluteFilepathValidator(file_path):
  """Checks to see if the file path exists and is an absolute path."""
  if file_path is None:
    return
  if not os.path.isfile(file_path):
    raise InvalidValueError('The provided path must exist.')
  if not os.path.isabs(file_path):
    raise InvalidValueError('The provided path must be absolute.')


def _LooksLikeAProjectName(project):
  """Heuristics testing if a string looks like a project name, but an id."""

  if re.match(r'[-0-9A-Z]', project[0]):
    return True

  return any(c in project for c in ' !"\'')


def _BooleanValidator(property_name, property_value):
  """Validates boolean properties.

  Args:
    property_name: str, the name of the property
    property_value: PropertyValue | str | bool, the value to validate

  Raises:
    InvalidValueError: if value is not boolean
  """
  accepted_strings = [
      'true', '1', 'on', 'yes', 'y', 'false', '0', 'off', 'no', 'n', '', 'none'
  ]
  if isinstance(property_value, PropertyValue):
    value = property_value.value
  else:
    value = property_value
  if Stringize(value).lower() not in accepted_strings:
    raise InvalidValueError(
        'The [{0}] value [{1}] is not valid. Possible values: [{2}]. '
        '(See http://yaml.org/type/bool.html)'.format(
            property_name, value,
            ', '.join([x if x else "''" for x in accepted_strings])))


def _IntegerValidator(property_name=None, property_value=None):
  """Validates integer properties.

  Args:
    property_name: str, the name of the property
    property_value: PropertyValue | int | str, the value to validate

  Raises:
    InvalidValueError: if value is not integer
  """
  if property_value is None:
    return
  if property_name is None:
    return

  if isinstance(property_value, PropertyValue):
    value = property_value.value
  else:
    value = property_value

  try:
    # Attempt to convert the value to an integer.
    int_value = int(value)

  except (ValueError, TypeError):

    raise InvalidValueError(
        f'The [{property_name}] value [{value}] is not valid. Only integer'
        ' values are allowed for this property.'
    )

  # Check if the converted value is an integer.

  if not isinstance(int_value, int):
    raise InvalidValueError(
        f'The [{property_name}] value [{value}] is not valid. Only integer'
        ' values are allowed for this property.'
    )

  # Ensure the integer value is positive.
  if int_value <= 0:

    raise InvalidValueError(
        f'The [{property_name}] value [{value}] is not valid. Only positive'
        ' integer values are allowed.'
    )


def _BuildTimeoutValidator(timeout):
  """Validates build timeouts."""
  # pylint: disable=g-import-not-at-top
  from googlecloudsdk.core.util import times
  # pylint: enable=g-import-not-at-top

  if timeout is None:
    return
  seconds = times.ParseDuration(timeout, default_suffix='s').total_seconds
  if seconds <= 0:
    raise InvalidValueError('Timeout must be a positive time duration.')


def _HumanReadableByteAmountValidator(size_string):
  """Validates human readable byte amounts, e.g. 1KiB."""
  if size_string is None:
    return
  try:
    scaled_integer.ParseInteger(size_string)
  except ValueError as e:
    raise InvalidValueError(str(e))


def IsInternalUserCheck():
  """Checks if the current user is an internal Google user.

  Checks the 'CLOUDSDK_INTERNAL_USER' environment variable first to decide
  whther the current user is an internal Google user.
  If the variable is not set, falls back to checking if the user's email
  domain is 'google.com'.

  Returns:
    bool: True if the user is an internal user, False otherwise.
  """
  if 'CLOUDSDK_INTERNAL_USER' in os.environ:
    return (
        encoding.GetEncodedValue(os.environ, 'CLOUDSDK_INTERNAL_USER') == 'true'
    )
  user_email = VALUES.core.account.Get()
  if user_email:
    parts = user_email.split('@')
    if len(parts) == 2:
      return parts[1] == 'google.com'
  return False


class Error(exceptions.Error):
  """Exceptions for the properties module."""


class PropertiesParseError(Error):
  """An exception to be raised when a properties file is invalid."""


class NoSuchPropertyError(Error):
  """An exception to be raised when the desired property does not exist."""


class MissingInstallationConfig(Error):
  """An exception to be raised when the sdk root does not exist."""

  def __init__(self):
    super(MissingInstallationConfig, self).__init__(
        'Installation properties could not be set because the installation '
        'root of the Cloud SDK could not be found.')


class InvalidScopeValueError(Error):
  """Exception for when a string could not be parsed to a valid scope value."""

  def __init__(self, given):
    """Constructs a new exception.

    Args:
      given: str, The given string that could not be parsed.
    """
    super(InvalidScopeValueError, self).__init__(
        'Could not parse [{0}] into a valid configuration scope.  '
        'Valid values are [{1}]'.format(given,
                                        ', '.join(Scope.AllScopeNames())))


class InvalidValueError(Error):
  """An exception to be raised when the set value of a property is invalid."""


class InvalidProjectError(Error):
  """An exception for bad project names, with a little user help."""

  def __init__(self, given):
    super(InvalidProjectError, self).__init__(given + '\n' + _SET_PROJECT_HELP)


class RequiredPropertyError(Error):
  """Generic exception for when a required property was not set."""
  FLAG_STRING = ('It can be set on a per-command basis by re-running your '
                 'command with the [{flag}] flag.\n\n')

  def __init__(self, prop, flag=None, extra_msg=None):
    if prop.section != VALUES.default_section.name:
      section = prop.section + '/'
    else:
      section = ''

    flag = flag or prop.default_flag
    if flag:
      flag_msg = RequiredPropertyError.FLAG_STRING.format(flag=flag)
    else:
      flag_msg = ''

    msg = ("""\
The required property [{property_name}] is not currently set.
{flag_msg}You may set it for your current workspace by running:

  $ gcloud config set {section}{property_name} VALUE

or it can be set temporarily by the environment variable [{env_var}]""".format(
    property_name=prop.name,
    flag_msg=flag_msg,
    section=section,
    env_var=prop.EnvironmentName()))
    if extra_msg:
      msg += '\n\n' + extra_msg
    super(RequiredPropertyError, self).__init__(msg)
    self.property = prop


class UnknownFormatError(exceptions.Error):
  """Unknown format name exception."""

  def __init__(self, printer_name, supported_formats):
    """Constructs a new exception.

    Args:
      printer_name: str, The unknown printer format.
      supported_formats: [str], Supported printer formats.
    """
    super(UnknownFormatError, self).__init__("""\
Format must be one of {0}; received [{1}].

For information on output formats:
  $ gcloud topic formats
""".format(', '.join(supported_formats), printer_name))


class PropertyValue(object):
  """Represents a value and source for a property.

  Attributes:
    value: any, the value of the property.
    source: enum, where the value was sourced from, or UNKNOWN.
  """

  class PropertySource(enum.Enum):
    UNKNOWN = 'unknown'
    PROPERTY_FILE = 'property file'
    ENVIRONMENT = 'environment'
    FLAG = 'flag'
    CALLBACK = 'callback'
    DEFAULT = 'default'

  def __init__(self, value, source=PropertySource.UNKNOWN):
    self.value = value
    self.source = source

  def __str__(self):
    return '{0} ({1})'.format(six.text_type(self.value), self.source.value)


class _Section(object):
  """Represents a section of the properties file that has related properties.

  Attributes:
    name: str, The name of the section.
    is_hidden: bool, True if the section is hidden, False otherwise.
  """

  def __init__(self, name, hidden=False):
    self.__name = name
    self.__is_hidden = hidden
    self.__properties = {}

  @property
  def name(self):
    return self.__name

  @property
  def is_hidden(self):
    return self.__is_hidden

  def __iter__(self):
    return iter(self.__properties.values())

  def __hash__(self):
    return hash(self.name)

  def __eq__(self, other):
    return self.name == other.name

  def __ne__(self, other):
    return self.name != other.name

  def __gt__(self, other):
    return self.name > other.name

  def __ge__(self, other):
    return self.name >= other.name

  def __lt__(self, other):
    return self.name < other.name

  def __le__(self, other):
    return self.name <= other.name

  #  pylint: disable=missing-docstring
  def _Add(self,
           name,
           help_text=None,
           internal=False,
           hidden=False,
           callbacks=None,
           default=None,
           validator=None,
           choices=None,
           completer=None,
           default_flag=None):
    prop = _Property(
        section=self.__name,
        name=name,
        help_text=help_text,
        internal=internal,
        hidden=(self.is_hidden or hidden),
        callbacks=callbacks,
        default=default,
        validator=validator,
        choices=choices,
        completer=completer,
        default_flag=default_flag)
    self.__properties[name] = prop
    return prop

  def _AddBool(self,
               name,
               help_text=None,
               internal=False,
               hidden=False,
               callbacks=None,
               default=None):
    return self._Add(
        name=name,
        help_text=help_text,
        internal=internal,
        hidden=hidden,
        callbacks=callbacks,
        default=default,
        validator=functools.partial(_BooleanValidator, name),
        choices=('true', 'false'))

  def Property(self, property_name):
    """Gets a property from this section, given its name.

    Args:
      property_name: str, The name of the desired property.

    Returns:
      Property, The property corresponding to the given name.


    Raises:
      NoSuchPropertyError: If the property is not known for this section.
    """
    try:
      return self.__properties[property_name]
    except KeyError:
      raise NoSuchPropertyError('Section [{s}] has no property [{p}].'.format(
          s=self.__name, p=property_name))

  def HasProperty(self, property_name):
    """True iff section has given property.

    Args:
      property_name: str, The name of the property to check for membership.

    Returns:
      a boolean. True iff this section contains property_name.
    """
    return property_name in self.__properties

  def AllProperties(self, include_hidden=False):
    """Gets a list of all registered property names in this section.

    Args:
      include_hidden: bool, True to include hidden properties in the result.

    Returns:
      [str], The property names.
    """
    return [
        name for name, prop in six.iteritems(self.__properties)
        if include_hidden or not prop.is_hidden
    ]

  def AllValues(self,
                list_unset=False,
                include_hidden=False,
                properties_file=None,
                only_file_contents=False):
    """Gets all the properties and their values for this section.

    Args:
      list_unset: bool, If True, include unset properties in the result.
      include_hidden: bool, True to include hidden properties in the result. If
        a property has a value set but is hidden, it will be included regardless
        of this setting.
      properties_file: properties_file.PropertiesFile, the file to read settings
        from.  If None the active property file will be used.
      only_file_contents: bool, True if values should be taken only from the
        properties file, false if flags, env vars, etc. should be consulted too.
        Mostly useful for listing file contents.

    Returns:
      {str:str}, The dict of {property:value} for this section.
    """
    properties_file = (
        properties_file or named_configs.ActivePropertiesFile.Load())

    result = {}
    for prop in self:
      if prop.is_internal:
        # Never show internal properties, ever.
        continue
      if (prop.is_hidden and not include_hidden and
          _GetPropertyWithoutCallback(prop, properties_file) is None):
        continue

      if only_file_contents:
        value = properties_file.Get(prop.section, prop.name)
      else:
        property_value = _GetPropertyWithoutDefault(prop, properties_file)
        if property_value is None:
          value = None
        else:
          value = property_value.value

      if value is None:
        if not list_unset:
          # Never include if not set and not including unset values.
          continue
        if prop.is_hidden and not include_hidden:
          # If including unset values, exclude if hidden and not including
          # hidden properties.
          continue

      # Always include if value is set (even if hidden)
      result[prop.name] = value
    return result

  def AllPropertyValues(self,
                        list_unset=False,
                        include_hidden=False,
                        properties_file=None,
                        only_file_contents=False):
    """Gets all the properties and their values for this section.

    Args:
      list_unset: bool, If True, include unset properties in the result.
      include_hidden: bool, True to include hidden properties in the result. If
        a property has a value set but is hidden, it will be included regardless
        of this setting.
      properties_file: properties_file.PropertiesFile, the file to read settings
        from.  If None the active property file will be used.
      only_file_contents: bool, True if values should be taken only from the
        properties file, false if flags, env vars, etc. should be consulted too.
        Mostly useful for listing file contents.

    Returns:
      {str:PropertyValue}, The dict of {property:value} for this section.
    """
    properties_file = (
        properties_file or named_configs.ActivePropertiesFile.Load())

    result = {}
    for prop in self:
      if prop.is_internal:
        # Never show internal properties, ever.
        continue
      if (prop.is_hidden and not include_hidden and
          _GetPropertyWithoutCallback(prop, properties_file) is None):
        continue

      if only_file_contents:
        property_value = PropertyValue(
            properties_file.Get(prop.section, prop.name),
            PropertyValue.PropertySource.PROPERTY_FILE)
      else:
        property_value = _GetPropertyWithoutDefault(prop, properties_file)

      if (property_value is None) or (property_value.value is None):
        if not list_unset:
          # Never include if not set and not including unset property_values.
          continue
        if prop.is_hidden and not include_hidden:
          # If including unset property_values, exclude if hidden and not
          # including hidden properties.
          continue

      # Always include if value is set (even if hidden)
      result[prop.name] = property_value
    return result


def AccessPolicyValidator(policy):
  """Checks to see if the Access Policy string is valid."""
  if policy is None:
    return
  if not policy.isdigit():
    raise InvalidValueError(
        'The access_context_manager.policy property must be set '
        'to the policy number, not a string.')


class _SectionAccessContextManager(_Section):
  """Contains the properties for the 'access_context_manager' section."""

  def OrganizationValidator(self, org):
    """Checks to see if the Organization string is valid."""
    if org is None:
      return
    if not org.isdigit():
      raise InvalidValueError(
          'The access_context_manager.organization property must be set '
          'to the organization ID number, not a string.')

  def __init__(self):
    super(_SectionAccessContextManager, self).__init__(
        'access_context_manager', hidden=True)

    self.policy = self._Add(
        'policy',
        validator=AccessPolicyValidator,
        help_text=('ID of the policy resource to operate on. Can be found '
                   'by running the `access-context-manager policies list` '
                   'command.'))
    self.organization = self._Add(
        'organization',
        validator=self.OrganizationValidator,
        help_text=('Default organization cloud-bindings command group will '
                   'operate on.'))
    self.enable_gcsl = self._AddBool(
        'enable_gcsl',
        default=False,
        help_text=(
            'If True, enable Google Cloud Session Length features for Access'
            ' Context Manager.'
        ),
        hidden=True,
    )


class _SectionAccessibility(_Section):
  """Contains the properties for the 'accessibility' section."""

  def __init__(self):
    super(_SectionAccessibility, self).__init__('accessibility')
    self.screen_reader = self._AddBool(
        'screen_reader',
        default=False,
        help_text='Make gcloud more screen reader friendly.')


class _SectionAi(_Section):
  """Contains the properties for the command group 'ai' section."""

  def __init__(self):
    super(_SectionAi, self).__init__('ai')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with '
        'AI Platform resources. When a `--region` flag is required '
        'but not provided, the command will fall back to this value, if set.')


class _SectionAiPlatform(_Section):
  """Contains the properties for the command group 'ai_platform' section."""

  def __init__(self):
    super(_SectionAiPlatform, self).__init__('ai_platform')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with AI Platform '
        'Training and Prediction resources (currently for Prediction only). '
        'It is ignored for training resources for now. The value should be '
        'either `global` or one of the supported regions. When a `--region` '
        'flag is required but not provided, the command will fall back to this '
        'value, if set.')


class _SectionApiClientOverrides(_Section):
  """Contains the properties for the 'api-client-overrides' section.

  This overrides the API client version to use when talking to this API.
  """

  def __init__(self):
    super(_SectionApiClientOverrides, self).__init__(
        'api_client_overrides', hidden=True)
    self.alloydb = self._Add('alloydb')
    self.appengine = self._Add('appengine')
    self.backupdr = self._Add('backupdr')
    self.baremetalsolution = self._Add('baremetalsolution')
    self.cloudidentity = self._Add('cloudidentity')
    self.compute = self._Add('compute')
    self.compute_alpha = self._Add('compute/alpha')
    self.compute_beta = self._Add('compute/beta')
    self.compute_v1 = self._Add('compute/v1')
    self.container = self._Add('container')
    self.speech = self._Add('speech')
    self.sql = self._Add('sql')
    self.storage = self._Add('storage')
    self.run = self._Add('run')
    self.scc = self._Add('securitycenter')
    self.cloudresourcemanager = self._Add('cloudresourcemanager')
    self.workstations = self._Add('workstations')


class _SectionApiEndpointOverrides(_Section):
  """Contains the properties for the 'api-endpoint-overrides' section.

  This overrides what endpoint to use when talking to the given API.
  """

  def __init__(self):
    super(_SectionApiEndpointOverrides, self).__init__('api_endpoint_overrides')
    self.default_universe_domain = 'googleapis.com'
    try:
      active_config = named_configs.ConfigurationStore.ActiveConfig()
      active_config_properties = active_config.GetProperties()
    except Exception:  # pylint: disable=broad-except
      self._universe_domain = self.default_universe_domain
    else:
      self._universe_domain = active_config_properties.get('core', {}).get(
          'universe_domain', self.default_universe_domain
      )

    self.accessapproval = self._Add(
        'accessapproval', command='gcloud access-approval')
    self.accesscontextmanager = self._Add(
        'accesscontextmanager', command='gcloud access-context-manager')
    self.agentidentity = self._Add(
        'agentidentity', command='gcloud agent-identity')
    self.agentregistry = self._Add(
        'agentregistry', command='gcloud agent-registry', hidden=True)
    self.ai = self._Add('ai', command='gcloud ai')
    self.aiplatform = self._Add('aiplatform', command='gcloud ai-platform')
    self.alloydb = self._Add('alloydb', command='gcloud alloydb', hidden=True)
    self.anthosevents = self._Add('anthosevents', command='gcloud anthos')
    self.anthospolicycontrollerstatus_pa = self._Add(
        'anthospolicycontrollerstatus_pa',
        command='gcloud container fleet policycontroller')
    self.apigateway = self._Add('apigateway', command='gcloud api-gateway')
    self.apigee = self._Add('apigee', command='gcloud apigee')
    self.apihub = self._Add(
        'apihub', command='gcloud apigeeregistry', hidden=True)
    self.apikeys = self._Add(
        'apikeys', command='gcloud services api-keys', hidden=True
    )
    self.appengine = self._Add('appengine', command='gcloud app')
    self.apphub = self._Add('apphub', command='gcloud apphub')
    self.appoptimize = self._Add('appoptimize', command='gcloud app-optimize')
    self.artifactregistry = self._Add(
        'artifactregistry', command='gcloud artifacts')
    self.assuredworkloads = self._Add(
        'assuredworkloads', command='gcloud assured')
    self.auditmanager = self._Add(
        'auditmanager', command='gcloud audit-manager')
    self.authztoolkit = self._Add(
        'authztoolkit', command='gcloud authz-toolkit', hidden=True)
    self.backupdr = self._Add(
        'backupdr', command='gcloud backup-dr', hidden=True)
    self.baremetalsolution = self._Add(
        'baremetalsolution', command='gcloud bms')
    self.batch = self._Add('batch', command='gcloud batch', hidden=True)
    self.beyondcorp = self._Add('beyondcorp', hidden=True)
    self.biglake = self._Add(
        'biglake', command='gcloud biglake', hidden=True)
    self.bigquery = self._Add('bigquery', hidden=True)
    self.bigquerymigration = self._Add('bigquerymigration', hidden=True)
    self.bigtableadmin = self._Add('bigtableadmin', command='gcloud bigtable')
    self.binaryauthorization = self._Add(
        'binaryauthorization', command='gcloud container binauthz', hidden=True)
    self.categorymanager = self._Add('categorymanager', hidden=True)
    self.certificatemanager = self._Add(
        'certificatemanager', command='gcloud certificate-manager')
    self.cloudaicompanion = self._Add('cloudaicompanion', hidden=True)
    self.cloudapiregistry = self._Add(
        'cloudapiregistry', command='gcloud api-registry')
    self.cloudasset = self._Add('cloudasset', command='gcloud asset')
    self.cloudbilling = self._Add('cloudbilling', command='gcloud billing')
    self.cloudbuild = self._Add('cloudbuild', command='gcloud builds')
    self.cloudcommerceconsumerprocurement = self._Add(
        'cloudcommerceconsumerprocurement',
        command='gcloud commerce-procurement')
    self.clouddeploy = self._Add('clouddeploy', command='gcloud deploy')
    self.clouderrorreporting = self._Add(
        'clouderrorreporting', command='gcloud error-reporting')
    self.cloudfunctions = self._Add(
        'cloudfunctions', command='gcloud functions')
    self.cloudidentity = self._Add('cloudidentity', command='gcloud identity')
    self.cloudkms = self._Add('cloudkms', command='gcloud kms')
    self.cloudlocationfinder = self._Add(
        'cloudlocationfinder', command='gcloud cloudlocationfinder')
    self.cloudnumberregistry = self._Add(
        'cloudnumberregistry',
        command='gcloud cloudnumberregistry', hidden=True)
    self.cloudquotas = self._Add(
        'cloudquotas', command='gcloud quotas', hidden=True)
    self.cloudresourcemanager = self._Add(
        'cloudresourcemanager', command='gcloud projects')
    self.cloudresourcesearch = self._Add('cloudresourcesearch', hidden=True)
    self.cloudscheduler = self._Add(
        'cloudscheduler', command='gcloud scheduler')
    self.cloudsecuritycompliance = self._Add(
        'cloudsecuritycompliance',
        command='gcloud compliance-manager')
    self.cloudshell = self._Add(
        'cloudshell', command='gcloud cloud-shell', hidden=True)
    self.cloudsupport = self._Add('cloudsupport', command='gcloud support')
    self.cloudtasks = self._Add('cloudtasks', command='gcloud tasks')
    self.cloudtrace = self._Add('cloudtrace', command='gcloud trace')
    self.composer = self._Add('composer', command='gcloud composer')
    self.compute = self._Add(
        'compute',
        help_text='Overrides API endpoint for `gcloud compute` command group. '
        'For Private Service Connect usage, see '
        'https://cloud.google.com/vpc/docs/configure-private-service-connect-apis#using-endpoints'
    )
    self.configdelivery = self._Add(
        'configdelivery', command='gcloud container fleet packages', hidden=True
    )
    self.connectgateway = self._Add('connectgateway', hidden=True)
    self.container = self._Add('container', command='gcloud container')
    self.containeranalysis = self._Add('containeranalysis', hidden=True)
    self.datacatalog = self._Add('datacatalog', command='gcloud data-catalog')
    self.dataflow = self._Add('dataflow', command='gcloud dataflow')
    self.datafusion = self._Add('datafusion', command='gcloud data-fusion')
    self.datalineage = self._Add('datalineage', command='gcloud datalineage')
    self.datamigration = self._Add(
        'datamigration', command='gcloud database-migration')
    self.datapol = self._Add('datapol', hidden=True)
    self.datapipelines = self._Add(
        'datapipelines', command='gcloud datapipelines')
    self.dataplex = self._Add('dataplex', command='gcloud dataplex')
    self.dataproc = self._Add('dataproc', command='gcloud dataproc')
    self.dataprocgdc = self._Add('dataprocgdc', hidden=True)
    self.datastore = self._Add('datastore', command='gcloud datastore')
    self.datastream = self._Add('datastream', command='gcloud datastream')
    self.deploymentmanager = self._Add(
        'deploymentmanager', command='gcloud deployment-manager')
    # TODO(b/420912196): Unhide after gcloud client releases to GA.
    self.designcenter = self._Add(
        'designcenter', command='gcloud design-center', hidden=True)
    self.developerconnect = self._Add(
        'developerconnect', command='gcloud developer-connect')
    self.developerknowledge = self._Add(
        'developerknowledge', command='gcloud developerknowledge', hidden=True
    )
    self.devicerun = self._Add('devicerun', command='gcloud device-run')
    self.discovery = self._Add('discovery', hidden=True)
    self.dns = self._Add('dns', command='gcloud dns')
    self.domains = self._Add('domains', command='gcloud domains')
    self.edgecontainer = self._Add(
        'edgecontainer', command='gcloud edge-container')
    self.edgenetwork = self._Add(
        'edgenetwork', command='gcloud edge-cloud networking', hidden=True)
    self.eventarc = self._Add('eventarc', command='gcloud eventarc')
    self.eventarcpublishing = self._Add(
        'eventarcpublishing', command='gcloud eventarc publish')
    self.externalexposure = self._Add(
        'externalexposure', command='gcloud scc external-exposure', hidden=True)
    self.faultinjectiontesting = self._Add(
        'faultinjectiontesting', command='gcloud fault-injection')
    self.faulttesting = self._Add(
        'faulttesting', command='gcloud fault-testing')
    self.file = self._Add('file', command='gcloud filestore')
    self.firebasedataconnect = self._Add(
        'firebasedataconnect', command='gcloud firebase-data-connect')
    self.firestore = self._Add('firestore', command='gcloud firestore')
    self.ftp = self._Add('ftp', command='gcloud storage ftp')
    self.geminicloudassist = self._Add(
        'geminicloudassist', command='gcloud geminicloudassist', hidden=True
    )
    self.genomics = self._Add('genomics', command='gcloud genomics')
    self.gkebackup = self._Add('gkebackup', hidden=True)
    self.gkehub = self._Add('gkehub', hidden=True)
    self.gkerecommender = self._Add('gkerecommender', hidden=True)
    self.hypercomputecluster = self._Add(
        'hypercomputecluster', command='gcloud cluster-director')
    self.iamconnectors = self._Add(
        'iamconnectors', command='gcloud agent-identity')
    self.observability = self._Add(
        'observability', command='gcloud observability')
    self.servicehealth = self._Add(
        'servicehealth', command='gcloud servicehealth')
    self.transcoder = self._Add(
        'transcoder', command='gcloud transcoder', hidden=True
    )
    self.gkemulticloud = self._Add(
        'gkemulticloud',
        help_text='Overrides API endpoint for `gcloud container aws`, '
        '`gcloud container azure` and `gcloud container attached` '
        'command groups.'
    )
    # TODO(b/236427906): Unhide after gcloud client releases to GA.
    self.gkeonprem = self._Add('gkeonprem', hidden=True)
    self.healthcare = self._Add('healthcare', command='gcloud healthcare')
    self.iam = self._Add('iam', command='gcloud iam')
    self.iamcredentials = self._Add('iamcredentials', command='gcloud iam')
    self.iap = self._Add('iap', command='gcloud iap')
    self.ids = self._Add('ids', command='gcloud ids')
    self.kmsinventory = self._Add(
        'kmsinventory', command='gcloud kms inventory'
    )
    self.krmapihosting = self._Add(
        'krmapihosting', command='gcloud anthos config controller')
    self.kubernetespolicy = self._Add('kubernetespolicy', hidden=True)
    self.inframanager = self._Add(
        'config', command='gcloud infra-manager')
    self.language = self._Add('language', command='gcloud ml language')
    self.logging = self._Add('logging', command='gcloud logging')
    self.looker = self._Add('looker', command='gcloud looker')
    self.lustre = self._Add('lustre', command='gcloud lustre', hidden=True)
    self.managedflink = self._Add(
        'managedflink', command='gcloud managedflink', hidden=True
    )
    self.managedidentities = self._Add(
        'managedidentities', command='gcloud active-directory')
    self.managedkafka = self._Add(
        'managedkafka', command='gcloud managed-kafka', hidden=True)
    self.manager = self._Add('manager', hidden=True)
    self.marketplacesolutions = self._Add(
        'marketplacesolutions', command='gcloud mps')
    self.mediaasset = self._Add('mediaasset', command='gcloud media')
    self.memcache = self._Add('memcache', command='gcloud memcache')
    self.memorystore = self._Add(
        'memorystore', command='gcloud memorystore', hidden=True)
    self.metastore = self._Add('metastore', command='gcloud metastore')
    self.ml = self._Add('ml', hidden=True)
    self.modelarmor = self._Add(
        'modelarmor', command='gcloud modelarmor', hidden=True)
    self.monitoring = self._Add('monitoring', command='gcloud monitoring')
    self.netapp = self._Add('netapp', command='gcloud netapp')
    self.networkconnectivity = self._Add(
        'networkconnectivity', command='gcloud network-connectivity')
    self.networkmanagement = self._Add(
        'networkmanagement', command='gcloud network-management')
    self.networksecurity = self._Add(
        'networksecurity', command='gcloud network-security')
    self.networkservices = self._Add(
        'networkservices', command='gcloud network-services')
    self.notebooks = self._Add('notebooks', command='gcloud notebooks')
    self.ondemandscanning = self._Add('ondemandscanning', hidden=True)
    self.oracledatabase = self._Add(
        'oracledatabase', command='gcloud oracle-database', hidden=True)
    self.orglifecycle = self._Add(
        'orglifecycle', command='gcloud orglifecycle', hidden=True)
    self.orgpolicy = self._Add('orgpolicy', command='gcloud org-policies')
    self.osconfig = self._Add('osconfig', hidden=True)
    self.oslogin = self._Add('oslogin', hidden=True)
    self.parallelstore = self._Add('parallelstore', hidden=True)
    self.parametermanager = self._Add(
        'parametermanager', command='gcloud parameter-manager', hidden=True)
    self.policyanalyzer = self._Add(
        'policyanalyzer', command='policy-intelligence')
    self.policysimulator = self._Add('policysimulator', hidden=True)
    self.policytroubleshooter = self._Add('policytroubleshooter', hidden=True)
    self.privateca = self._Add('privateca', command='gcloud privateca')
    self.privilegedaccessmanager = self._Add(
        'privilegedaccessmanager', command='gcloud pam'
    )
    self.publicca = self._Add('publicca', command='gcloud publicca')
    self.pubsub = self._Add('pubsub', command='gcloud pubsub')
    self.pubsublite = self._Add('pubsublite', hidden=True)
    self.recaptcha = self._Add(
        'recaptchaenterprise', command='gcloud recaptcha')
    self.recommender = self._Add('recommender', command='gcloud recommender')
    self.redis = self._Add('redis', command='gcloud redis')
    self.remotebuildexecution = self._Add('remotebuildexecution', hidden=True)
    self.replicapoolupdater = self._Add('replicapoolupdater', hidden=True)
    self.run = self._Add('run', command='gcloud run')
    self.runtimeconfig = self._Add(
        'runtimeconfig', command='gcloud runtime-config')
    self.saasservicemgmt = self._Add(
        'saasservicemgmt', command='gcloud saas', hidden=True)
    self.sasportal = self._Add('sasportal', hidden=True)
    self.scc = self._Add('securitycenter', command='gcloud scc')
    self.sddc = self._Add('sddc', command='gcloud vmware sddc')
    self.seclm = self._Add(
        'seclm', command='gcloud seclm', hidden=True)
    self.secrets = self._Add('secretmanager', command='gcloud secrets')
    self.securesourcemanager = self._Add('securesourcemanager', hidden=True)
    self.securitycentermanagement = self._Add(
        'securitycentermanagement', command='gcloud scc manage', hidden=True
    )
    self.securityposture = self._Add('securityposture', hidden=True)
    self.servicedirectory = self._Add(
        'servicedirectory', command='gcloud service-directory')
    self.servicemanagement = self._Add(
        'servicemanagement', command='gcloud endpoints')
    self.serviceregistry = self._Add('serviceregistry', hidden=True)
    self.serviceusage = self._Add('serviceusage', hidden=True)
    self.source = self._Add('source', hidden=True)
    self.sourcerepo = self._Add('sourcerepo', command='gcloud source')
    self.spanner = self._Add(
        'spanner',
        help_text='Overrides API endpoint for `gcloud spanner` command group. '
        'For spanner emulator usage, see '
        'https://cloud.google.com/spanner/docs/emulator#using_the_gcloud_cli_with_the_emulator'
    )
    self.speech = self._Add('speech', command='gcloud ml speech')
    self.sql = self._Add('sql', command='gcloud sql')
    self.storage = self._Add('storage', command='gcloud storage')
    self.storage_grpc = self._Add(
        'storage_grpc',
        help_text=(
            'Overrides gRPC API endpoint for `gcloud storage` command group.'
            'Note: This is an internal property and should not be used by '
            'external callers.'
        ),
        hidden=True,
        validator=self.StorageGrpcEndpointValidator,
    )
    self.storagebatchoperations = self._Add(
        'storagebatchoperations',
        command='gcloud storage batch-actions',
        hidden=True,
    )
    self.storageinsights = self._Add(
        'storageinsights', command='gcloud storage insights', hidden=True)
    self.stream = self._Add('stream', hidden=True)
    self.telcoautomation = self._Add('telcoautomation', hidden=True)
    self.testing = self._Add('testing', command='gcloud firebase test')
    self.toolresults = self._Add('toolresults', hidden=True)
    self.tpu = self._Add('tpu', hidden=True)
    # Aliased to `storagetransfer` in `api_lib/apis/apis_util.py`.
    self.transfer = self._Add('transfer', command='gcloud transfer')
    self.transferappliance = self._Add(
        'transferappliance', command='gcloud transfer appliances')
    self.vectorsearch = self._Add(
        'vectorsearch', command='gcloud vector-search')
    self.vision = self._Add('vision', command='gcloud ml vision')
    self.vmmigration = self._Add('vmmigration', command='gcloud migration vms')
    self.vmwareengine = self._Add('vmwareengine', command='gcloud vmware')
    self.vpcaccess = self._Add('vpcaccess', hidden=True)
    self.workflowexecutions = self._Add(
        'workflowexecutions', command='gcloud workflows executions')
    self.workflows = self._Add('workflows', command='gcloud workflows')
    self.workloadcertificate = self._Add('workloadcertificate', hidden=True)
    self.workloadidentity = self._Add(
        'workloadidentity', hidden=True)
    self.workstations = self._Add('workstations', command='gcloud workstations')

  def StorageGrpcEndpointValidator(self, value):
    """Checks to see if the gRPC endpoint override string is valid."""
    if value is None:
      return
    if not _VALID_STORAGE_GRPC_ENDPOINT_OVERRIDE_REGEX.match(value):
      raise InvalidValueError(
          'The endpoint_overrides property must be an absolute URI (http:// '
          "or https:// ending with '/'), a gRPC Target (dns:///, "
          'google-c2p:///), or a valid hostname:port. '
          '[{value}] is not a valid endpoint override.'.format(value=value)
      )

  def EndpointValidator(self, value):
    """Checks to see if the endpoint override string is valid."""
    if value is None:
      return
    if not _VALID_ENDPOINT_OVERRIDE_REGEX.match(value):
      raise InvalidValueError(
          'The endpoint_overrides property must be an absolute URI beginning '
          'with http:// or https:// and ending with a trailing \'/\'. '
          '[{value}] is not a valid endpoint override.'.format(value=value)
      )

  def _Add(
      self, name, help_text=None, hidden=False, command=None, validator=None
  ):
    if not help_text and command:
      help_text = (
          'Overrides API endpoint for `{}` command group.').format(command)

    default_endpoint = self.GetDefaultEndpoint(name)
    if command and default_endpoint:
      help_text = f'{help_text} Defaults to `{default_endpoint}`'

    return super(_SectionApiEndpointOverrides, self)._Add(
        name,
        help_text=help_text,
        hidden=hidden,
        validator=validator or self.EndpointValidator)

  def _UniversifyHelpTextEndpoint(self, address: str) -> str:
    """Update a URL based on the current universe domain."""
    if (
        address is not None
        and self.default_universe_domain != self._universe_domain
    ):
      address = address.replace(
          self.default_universe_domain, self._universe_domain, 1
      )
    return address

  def GetDefaultEndpoint(self, api_name):
    """Returns the BASE_URL for the respective api and version."""
    api = apis_map.MAP.get(api_name)
    if api:
      for api_version in api:
        api_def = api.get(api_version)
        if api_def.default_version and api_def.apitools:
          return self._UniversifyHelpTextEndpoint(api_def.apitools.base_url)


class _SectionApp(_Section):
  """Contains the properties for the 'app' section."""

  def __init__(self):
    super(_SectionApp, self).__init__('app')
    self.promote_by_default = self._AddBool(
        'promote_by_default',
        help_text='If True, when deploying a new version of a service, that '
        'version will be promoted to receive all traffic for the service. '
        'This property can be overridden with the `--promote-by-default` or '
        '`--no-promote-by-default` flags.',
        default=True)
    self.stop_previous_version = self._AddBool(
        'stop_previous_version',
        help_text='If True, when deploying a new version of a service, the '
        'previously deployed version is stopped. If False, older versions must '
        'be stopped manually.',
        default=True)
    self.trigger_build_server_side = self._AddBool(
        'trigger_build_server_side', hidden=True, default=None)
    self.use_flex_with_buildpacks = self._AddBool(
        'use_flex_with_buildpacks', hidden=True, default=None)
    self.cloud_build_timeout = self._Add(
        'cloud_build_timeout',
        validator=_BuildTimeoutValidator,
        help_text='Timeout, in seconds, to wait for Docker builds to '
        'complete during deployments. All Docker builds now use the '
        'Cloud Build API.')
    self.container_builder_image = self._Add(
        'container_builder_image',
        default='gcr.io/cloud-builders/docker',
        hidden=True)
    self.use_appengine_api = self._AddBool(
        'use_appengine_api', default=True, hidden=True)
    # This property is currently ignored except on OS X Sierra or beta
    # deployments.
    # There's a theoretical benefit to exceeding the number of cores available,
    # since the task is bound by network/API latency among other factors, and
    # mini-benchmarks validated this (I got speedup from 4 threads to 8 on a
    # 4-core machine).
    self.num_file_upload_threads = self._Add(
        'num_file_upload_threads', default=None, hidden=True)

    def GetRuntimeRoot():
      sdk_root = config.Paths().sdk_root
      if sdk_root is None:
        return None
      else:
        return os.path.join(config.Paths().sdk_root, 'platform', 'ext-runtime')

    self.runtime_root = self._Add(
        'runtime_root', callbacks=[GetRuntimeRoot], hidden=True)

    # Whether or not to use the (currently under-development) Flex Runtime
    # Builders, as opposed to Externalized Runtimes.
    #   True  => ALWAYS
    #   False => NEVER
    #   Unset => default behavior, which varies between beta/GA commands
    self.use_runtime_builders = self._Add(
        'use_runtime_builders',
        default=None,
        help_text=('If set, opt in/out to a new code path for building '
                   'applications using pre-fabricated runtimes that can be '
                   'updated independently of client tooling. If not set, '
                   'the default path for each runtime is used.'))
    # The Cloud Storage path prefix for the Flex Runtime Builder configuration
    # files. The configuration files will live at
    # "<PREFIX>/<runtime>-<version>.yaml", with an additional
    # "<PREFIX>/runtime.version" indicating the latest version.
    self.runtime_builders_root = self._Add(
        'runtime_builders_root', default='gs://runtime-builders/', hidden=True)


class _SectionArtifacts(_Section):
  """Contains the properties for the 'artifacts' section."""

  def __init__(self):
    super(_SectionArtifacts, self).__init__('artifacts')

    self.repository = self._Add(
        'repository',
        help_text='Default repository to use when working with Artifact '
        'Registry resources. When a `repository` value is required but not '
        'provided, the command will fall back to this value, if set.')

    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Artifact Registry '
        'resources. When a `location` value is required but not provided, the '
        'command will fall back to this value, if set. If this value is unset, '
        'the default location is `global` when `location` value is optional.')

    self.registry_endpoint_prefix = self._Add(
        'registry_endpoint_prefix',
        default='',
        hidden=True,
        help_text='Default prefix to use while interacting with Artifact '
        'Registry resources.')

    self.domain = self._Add(
        'domain',
        default='pkg.dev',
        hidden=True,
        help_text='Default domain endpoint to use while interacting with '
        'Artifact Registry Docker resources.')

    self.gcr_host = self._Add(
        'gcr_host',
        default='gcr.io',
        hidden=True,
        help_text=(
            'Default host to use while interacting with Container Registry '
            'Docker resources.'
        ),
    )

    self.max_notes_per_batch_request = self._Add(
        'max_notes_per_batch_request',
        default=1000,
        hidden=True,
        help_text='Default batching size for BatchCreateNotes requests.',
    )

    self.allow_unrecognized_registry = self._AddBool(
        'allow_unrecognized_registry',
        default=False,
        hidden=True,
        help_text=(
            'If set to true, bypass the check against the list of known'
            ' registries'
        ),
    )


class _SectionAuth(_Section):
  """Contains the properties for the 'auth' section."""

  DEFAULT_AUTH_HOST = 'https://accounts.google.com/o/oauth2/auth'
  DEFAULT_TOKEN_HOST = 'https://oauth2.googleapis.com/token'
  DEFAULT_MTLS_TOKEN_HOST = 'https://oauth2.mtls.googleapis.com/token'

  def __init__(self):
    super(_SectionAuth, self).__init__('auth')
    self.auth_host = self._Add(
        'auth_host', hidden=True, default=self.DEFAULT_AUTH_HOST)
    self.disable_credentials = self._AddBool(
        'disable_credentials',
        default=False,
        help_text='If True, `gcloud` will not attempt to load any credentials '
        'or authenticate any requests. This is useful when behind a proxy '
        'that adds authentication to requests.')
    self.token_host = self._Add(
        'token_host',
        default=self.DEFAULT_TOKEN_HOST,
        help_text='Overrides the token endpoint to provision access tokens. '
        'It can be used with Private Service Connect.')
    self.mtls_token_host = self._Add(
        'mtls_token_host',
        default=self.DEFAULT_MTLS_TOKEN_HOST,
        help_text='Overrides the mtls token endpoint to provision access tokens.',
        hidden=True)
    self.disable_ssl_validation = self._AddBool(
        'disable_ssl_validation', hidden=True)
    self.client_id = self._Add(
        'client_id', hidden=True, default=config.CLOUDSDK_CLIENT_ID)
    self.client_secret = self._Add(
        'client_secret',
        hidden=True,
        default=config.CLOUDSDK_CLIENT_NOTSOSECRET)
    self.authority_selector = self._Add('authority_selector', hidden=True)
    self.authorization_token_file = self._Add(
        'authorization_token_file', hidden=True)
    self.credential_file_override = self._Add(
        'credential_file_override', hidden=True)
    self.access_token_file = self._Add(
        'access_token_file',
        help_text='A file path to read the access token. Use this property to '
        'authenticate gcloud with an access token. The credentials '
        'of the active account (if it exists) will be ignored. '
        'The file should contain an access token with no other '
        'information.')
    self.impersonate_service_account = self._Add(
        'impersonate_service_account',
        help_text=textwrap.dedent("""\
        While set, all API requests will be
        made as the given service account or target service account in an
        impersonation delegation chain instead of the currently selected
        account. You can specify either a single service account as the
        impersonator, or a comma-separated list of service accounts to
        create an impersonation delegation chain. This is done without
        needing to create, download, or activate a key for the service
        account or accounts.
        +
        In order to make API requests as a service account, your
        currently selected account must have an IAM role that includes
        the `iam.serviceAccounts.getAccessToken` permission for the
        service account or accounts.
        +
        The `roles/iam.serviceAccountTokenCreator` role has
        the `iam.serviceAccounts.getAccessToken permission`. You can
        also create a custom role.
        +
        You can specify a list of service accounts, separated with
        commas. This creates an impersonation delegation chain in which
        each service account delegates its permissions to the next
        service account in the chain. Each service account in the list
        must have the `roles/iam.serviceAccountTokenCreator` role on the
        next service account in the list. For example, when the property is set
        through `gcloud config set auth/impersonate_service_account=`
        ``SERVICE_ACCOUNT_1'',``SERVICE_ACCOUNT_2'',
        the active account must have the
        `roles/iam.serviceAccountTokenCreator` role on
        ``SERVICE_ACCOUNT_1'', which must have the
        `roles/iam.serviceAccountTokenCreator` role on
        ``SERVICE_ACCOUNT_2''.
        ``SERVICE_ACCOUNT_1'' is the impersonated service
        account and ``SERVICE_ACCOUNT_2'' is the delegate.
        """))
    self.disable_code_verifier = self._AddBool(
        'disable_code_verifier',
        default=False,
        hidden=True,
        help_text='Disable code verifier in 3LO auth flow. See '
        'https://tools.ietf.org/html/rfc7636 for more information '
        'about code verifier.')
    self.token_introspection_endpoint = self._Add(
        'token_introspection_endpoint',
        hidden=True,
        help_text='Overrides the endpoint used for token introspection with '
        'Workload and Workforce Identity Federation. It can be used with '
        'Private Service Connect.'
    )
    self.login_config_file = self._Add(
        'login_config_file',
        help_text='Sets the created login configuration file in '
        'auth/login_config_file. Calling `gcloud auth login` will automatically '
        'use this login configuration unless it is explicitly unset.')
    self.service_account_use_self_signed_jwt = self._Add(
        'service_account_use_self_signed_jwt',
        default=False,
        help_text=(
            'If True, use self signed jwt flow to get service account'
            ' credentials access token. This only applies to service account'
            ' json file and not to the legacy .p12 file.'
        ),
        validator=functools.partial(
            _BooleanValidator, 'service_account_use_self_signed_jwt'
        ),
        choices=('true', 'false'),
    )
    self.service_account_disable_id_token_refresh = self._AddBool(
        'service_account_disable_id_token_refresh',
        default=False,
        help_text='If True, disable ID token refresh for service account.',
    )
    self.git_helper_use_adc = self._AddBool(
        'git_helper_use_adc',
        default=False,
        hidden=True,
        help_text=(
            'If True, `gcloud auth git-helper` will use Application Default'
            ' Credentials.'
        ),
    )
    self.enable_sts_revoke_workforce_token = self._AddBool(
        'enable_sts_revoke_workforce_token',
        hidden=True,
        default=False,
        help_text='If True, use STS revoke endpoint to revoke workforce token.',
    )


class _SectionBatch(_Section):
  """Contains the properties for the 'batch' section."""

  def __init__(self):
    super(_SectionBatch, self).__init__('batch')

    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Batch '
        'resources. When a `location` value is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionBilling(_Section):
  """Contains the properties for the 'auth' section."""

  LEGACY = 'LEGACY'
  CURRENT_PROJECT = 'CURRENT_PROJECT'
  CURRENT_PROJECT_WITH_FALLBACK = 'CURRENT_PROJECT_WITH_FALLBACK'

  def __init__(self):
    super(_SectionBilling, self).__init__('billing')

    self.quota_project = self._Add(
        'quota_project',
        default=_SectionBilling.CURRENT_PROJECT,
        help_text=textwrap.dedent("""\
             The Google Cloud project that is billed and charged quota for
             operations performed in `gcloud`. When unset, the default is
             [CURRENT_PROJECT]. This default bills and charges quota against the
             current project. If you need to operate on one project, but need to
             bill your usage against or use quota from a different project, you
             can use this flag to specify the billing project. If both
             `billing/quota_project` and `--billing-project` are specified,
             `--billing-project` takes precedence.
             """))


class _SectionBuilds(_Section):
  """Contains the properties for the 'builds' section."""

  def __init__(self):
    super(_SectionBuilds, self).__init__('builds')

    self.region = self._Add(
        'region',
        help_text='Default region to use when working with Cloud Build '
        'resources. When a `--region` flag is required but not provided, the '
        'command will fall back to this value, if set.')
    self.timeout = self._Add(
        'timeout',
        validator=_BuildTimeoutValidator,
        help_text='Timeout, in seconds, to wait for builds to complete. If '
        'unset, defaults to 10 minutes.')
    self.check_tag = self._AddBool(
        'check_tag',
        default=True,
        hidden=True,
        help_text='If True, validate that the --tag value to builds '
        'submit is in the gcr.io, *.gcr.io, or *.pkg.dev namespace.')
    # TODO(b/118509363): Remove this after its default is True.
    self.use_kaniko = self._AddBool(
        'use_kaniko',
        default=False,
        help_text='If True, kaniko will be used to build images described by '
        'a Dockerfile, instead of `docker build`.')
    self.kaniko_cache_ttl = self._Add(
        'kaniko_cache_ttl',
        default=6,
        help_text='TTL, in hours, of cached layers when using Kaniko. If zero, '
        'layer caching is disabled.')
    self.kaniko_image = self._Add(
        'kaniko_image',
        default='gcr.io/kaniko-project/executor:latest',
        hidden=True,
        help_text='Kaniko builder image to use when use_kaniko=True. Defaults '
        'to gcr.io/kaniko-project/executor:latest')


class _SectionCode(_Section):
  """Contains the properties for the 'code' section."""

  def __init__(self):
    super(_SectionCode, self).__init__('code', hidden=True)

    self.minikube_event_timeout = self._Add(
        'minikube_event_timeout',
        default='90s',
        hidden=True,
        help_text='Terminate the cluster start process if this amount of time '
        'has passed since the last minikube event.')

    self.minikube_path_override = self._Add(
        'minikube_path_override',
        hidden=True,
        help_text='Location of minikube binary.')

    self.skaffold_path_override = self._Add(
        'skaffold_path_override',
        hidden=True,
        help_text='Location of skaffold binary.')


class _SectionColab(_Section):
  """Contains the properties for the 'colab' section."""

  def __init__(self):
    super(_SectionColab, self).__init__('colab')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with Colab Enterprise '
        'resources. When a `--region` flag is required but not provided, the '
        'command will fall back to this value, if set. Please see '
        'https://cloud.google.com/colab/docs/locations for a list of supported '
        'regions.')


class _SectionComponentManager(_Section):
  """Contains the properties for the 'component_manager' section."""

  def __init__(self):
    super(_SectionComponentManager, self).__init__('component_manager')
    self.additional_repositories = self._Add(
        'additional_repositories',
        help_text='Comma separated list of additional repositories to check '
        'for components.  This property is automatically managed by the '
        '`gcloud components repositories` commands.')
    self.disable_update_check = self._AddBool(
        'disable_update_check',
        help_text='If True, Google Cloud CLI will not automatically check for '
        'updates.')
    self.disable_warning = self._AddBool(
        'disable_warning',
        hidden=True,
        help_text='If True, Google Cloud CLI will not display warning messages '
        'about overridden configurations.')
    self.fixed_sdk_version = self._Add('fixed_sdk_version', hidden=True)
    self.snapshot_url = self._Add('snapshot_url', hidden=True)
    # We need the original snapshot_url because snapshot_url may be
    # overwritten by users. Without original_snapshot_url, users can be trapped
    # to the overwritten snapshot_url even after it is unset.
    self.original_snapshot_url = self._Add(
        'original_snapshot_url',
        internal=True,
        hidden=True,
        help_text='Snapshot URL when this installation is firstly installed.',
        default='https://dl.google.com/dl/cloudsdk/channels/rapid/components-2.json'
    )


class _SectionComposer(_Section):
  """Contains the properties for the 'composer' section."""

  def __init__(self):
    super(_SectionComposer, self).__init__('composer')
    self.location = self._Add(
        'location',
        help_text=(
            'Composer location to use. Each Composer location '
            'constitutes an independent resource namespace constrained to '
            'deploying environments into Compute Engine regions inside this '
            'location. This parameter corresponds to the '
            '/locations/<location> segment of the Composer resource URIs being '
            'referenced.'))


class _SectionCompute(_Section):
  """Contains the properties for the 'compute' section."""

  def __init__(self):
    super(_SectionCompute, self).__init__('compute')
    self.zone = self._Add(
        'zone',
        help_text='Default zone to use when working with zonal Compute '
        'Engine resources. When a `--zone` flag is required but not provided, '
        'the command will fall back to this value, if set. To see valid '
        'choices, run `gcloud compute zones list`.',
        completer=('googlecloudsdk.command_lib.compute.completers:'
                   'ZonesCompleter'))
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with regional Compute'
        ' Engine resources. When a `--region` flag is required but not '
        'provided, the command will fall back to this value, if set. To see '
        'valid choices, run `gcloud compute regions list`.',
        completer=('googlecloudsdk.command_lib.compute.completers:'
                   'RegionsCompleter'))
    self.gce_metadata_read_timeout_sec = self._Add(
        'gce_metadata_read_timeout_sec',
        default=20,
        help_text='Timeout of requesting data from gce metadata endpoints.',
        hidden=True)
    self.gce_metadata_check_timeout_sec = self._Add(
        'gce_metadata_check_timeout_sec',
        default=3,
        help_text='Timeout of checking if it is on gce environment.',
        hidden=True)
    self.use_new_list_usable_subnets_api = self._AddBool(
        'use_new_list_usable_subnets_api',
        default=False,
        help_text=(
            'If True, use the new API for listing usable subnets which only '
            'returns subnets in the current project.'))
    self.image_family_scope = self._Add(
        'image_family_scope',
        help_text='Sets how images are selected with image families for '
        'disk and instance creation. By default, zonal image resources '
        'are used when using an image family in a public image project, '
        'and global image resources are used for all other projects. '
        'To override the default behavior, set this property to `zonal` '
        'or `global`. ')
    self.iap_tunnel_use_new_websocket = self._AddBool(
        'iap_tunnel_use_new_websocket',
        default=False,
        help_text='Bool that indicates if we should use new websocket.',
        hidden=True)
    self.force_batch_request = self._AddBool(
        'force_batch_request',
        default=False,
        help_text='Bool that force all requests are sent as batch request',
        hidden=True)
    self.disable_batch_request = self._AddBool(
        'disable_batch_request',
        default=False,
        help_text=(
            'If True, all requests are sent as individual requests instead '
            'of batch requests.'
        ),
        hidden=True,
    )
    self.allow_partial_error = self._AddBool(
        'allow_partial_error',
        default=True,
        help_text=(
            'Allow AggregatedList to return partial response when there are'
            ' partial server down'
        ),
        hidden=True,
    )


class _SectionConfigDelivery(_Section):
  """Contains the properties for the 'config_delivery' section.

  Attributes:
    location: str, The GCP location to use by default e.g., us-central1.
  """

  def __init__(self):
    super(_SectionConfigDelivery, self).__init__('config_delivery', hidden=True)

    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Fleet Packages '
        'resources. When a `location` value is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionContainer(_Section):
  """Contains the properties for the 'container' section."""

  def __init__(self):
    super(_SectionContainer, self).__init__('container')
    self.cluster = self._Add(
        'cluster',
        help_text='Name of the cluster to use by default when '
        'working with Kubernetes Engine.')
    self.use_client_certificate = self._AddBool(
        'use_client_certificate',
        default=False,
        help_text='If True, use the cluster\'s client certificate to '
        'authenticate to the cluster API server.')
    self.use_app_default_credentials = self._AddBool(
        'use_application_default_credentials',
        default=False,
        help_text='If True, use application default credentials to authenticate'
        ' to the cluster API server.')

    self.build_timeout = self._Add(
        'build_timeout',
        validator=_BuildTimeoutValidator,
        help_text='Timeout, in seconds, to wait for container builds to '
        'complete.')
    self.build_check_tag = self._AddBool(
        'build_check_tag',
        default=True,
        hidden=True,
        help_text='If True, validate that the --tag value to container builds '
        'submit is in the gcr.io or *.gcr.io namespace.')


class _SectionContainerAttached(_Section):
  """Contains the properties for the 'container_attached' section."""

  def __init__(self):
    super(_SectionContainerAttached, self).__init__('container_attached')
    self.location = self._Add(
        'location',
        help_text=('Default Google Cloud location to use for Attached '
                   'clusters.'))


class _SectionContainerAws(_Section):
  """Contains the properties for the 'container_aws' section."""

  def __init__(self):
    super(_SectionContainerAws, self).__init__('container_aws')
    self.location = self._Add(
        'location',
        help_text=('Default Google Cloud location to use for Anthos clusters '
                   'on AWS.'))


class _SectionContainerAzure(_Section):
  """Contains the properties for the 'container_azure' section."""

  def __init__(self):
    super(_SectionContainerAzure, self).__init__('container_azure')
    self.location = self._Add(
        'location',
        help_text=('Default Google Cloud location to use for Anthos clusters '
                   'on Azure.'))


class _SectionContainerBareMetal(_Section):
  """Contains the properties for the 'container_bare_metal' section."""

  def __init__(self):
    super(_SectionContainerBareMetal, self).__init__('container_bare_metal')
    self.location = self._Add(
        'location',
        help_text=('Default Google Cloud location to use for Anthos clusters '
                   'on Bare Metal.'))


class _SectionContainerVmware(_Section):
  """Contains the properties for the 'container_vmware' section."""

  def __init__(self):
    super(_SectionContainerVmware, self).__init__('container_vmware')
    self.location = self._Add(
        'location',
        help_text=('Default Google Cloud location to use for Anthos clusters '
                   'on VMware.'))


class _SectionContextAware(_Section):
  """Contains the properties for the 'context_aware' section."""

  def __init__(self):
    super(_SectionContextAware, self).__init__('context_aware')
    self.use_client_certificate = self._AddBool(
        'use_client_certificate',
        help_text=(
            'If True, use client certificate to authorize user '
            'device using Context-aware access. This includes user login '
            'as well. Some services may not support client certificate '
            'authorization. If a command sends requests to such services, the '
            'client certificate will not be validated. '
            'Run `gcloud topic client-certificate` for list of services '
            'supporting this feature.'),
        default=False)
    # Only for tests. It is valuable to test that the mTLS endpoints are serving
    # without involving the policy enforcement. The mTLS endpoints are expected
    # to behave identically to the regular endpoints without policy enforcement.
    self.always_use_mtls_endpoint = self._AddBool(
        'always_use_mtls_endpoint',
        help_text='If True, use the mTLS endpoints regardless of the value of '
        'context_aware/use_client_certificate.',
        default=False,
        hidden=True)
    self.auto_discovery_file_path = self._Add(
        'auto_discovery_file_path',
        validator=ExistingAbsoluteFilepathValidator,
        help_text='File path for auto discovery configuration file.',
        hidden=True)
    self.certificate_config_file_path = self._Add(
        'certificate_config_file_path',
        validator=ExistingAbsoluteFilepathValidator,
        help_text='File path for certificate configuration file.',
        hidden=True)
    self.use_ecp_http_proxy = self._AddBool(
        'use_ecp_http_proxy',
        help_text=(
            'If True, use the ECP HTTP proxy for requests. This property is'
            ' used to determine if the ECP HTTP proxy should be used. If set to'
            ' True, the ECP HTTP proxy will be used for all ECP requests. If'
            ' set to False, the ECP HTTP proxy will not be used.'
        ),
        default=True,
        hidden=True,
    )
    self.use_mtls_for_grpc = self._AddBool(
        'use_mtls_for_grpc',
        help_text=(
            'If True, use mTLS for gRPC connections when Enterprise Certificate'
            ' is configured.'
        ),
        default=False,
        hidden=True,
    )


class _SectionCore(_Section):
  """Contains the properties for the 'core' section."""

  class InteractiveUXStyles(enum.Enum):
    NORMAL = 0
    OFF = 1
    TESTING = 2

  def __init__(self):
    super(_SectionCore, self).__init__('core')
    self.account = self._Add(
        'account',
        help_text='Account `gcloud` should use for authentication. '
        'Run `gcloud auth list` to see your currently available accounts.')
    self.disable_collection_path_deprecation_warning = self._AddBool(
        'disable_collection_path_deprecation_warning',
        hidden=True,
        help_text='If False, any usage of collection paths will result in '
        'deprecation warning. Set it to False to disable it.')
    self.default_regional_backend_service = self._AddBool(
        'default_regional_backend_service',
        help_text='If True, backend services in `gcloud compute '
        'backend-services` will be regional by default. Setting the `--global` '
        'flag is required for global backend services.')
    self.disable_color = self._AddBool(
        'disable_color',
        help_text='If True, color will not be used when printing messages in '
        'the terminal.')
    self.disable_command_lazy_loading = self._AddBool(
        'disable_command_lazy_loading', hidden=True)
    self.disable_prompts = self._AddBool(
        'disable_prompts',
        help_text='If True, the default answer will be assumed for all user '
        'prompts. However, for any prompts that require user input, an error '
        'will be raised. This is equivalent to either using the global '
        '`--quiet` flag or setting the environment variable '
        '`CLOUDSDK_CORE_DISABLE_PROMPTS` to 1. Setting this property is '
        'useful when scripting with `gcloud`.')
    self.display_hints = self._AddBool(
        'display_hints',
        default=False,
        hidden=True,
        help_text='If True, the `--agent-hints` flag will be active.')
    self.disable_usage_reporting = self._AddBool(
        'disable_usage_reporting',
        help_text='If True, anonymous statistics on SDK usage will not be '
        'collected. This value is set by your choices during installation, but '
        'can be changed at any time.  For more information, see '
        '[Usage statistics](/sdk/docs/usage-statistics).')
    self.enable_gri = self._AddBool(
        'enable_gri',
        default=False,
        hidden=True,
        help_text='If True, the parser for gcloud Resource Identifiers will be '
        'enabled when interpreting resource arguments.')
    self.resource_completion_style = self._Add(
        'resource_completion_style',
        choices=('flags', 'gri'),
        default='flags',
        hidden=True,
        help_text='The resource completion style controls how resource strings '
        'are represented in command argument completions.  All styles, '
        'including uri, are handled on input.')
    self.lint = self._Add(
        'lint',
        # Current runtime lint patterns. Delete from this comment when the
        # pattern usage has been deleted.
        #
        #   AddCacheUpdaters: Throws an exception for each command that needs
        #     a parser.display_info.AddCacheUpdater() call.
        #
        # When running tests set default=PATTERN[,PATTERN...] here to weed out
        # all occurrences of the patterns. Patterns are checked using substring
        # matching on the lint property string value:
        #
        #   if 'AddCacheUpdaters' in properties.VALUES.core.lint.Get():
        #     # AddCacheUpdaters lint check enabled.
        default='none',
        hidden=True,
        help_text='Enable the runtime linter for specific patterns. '
        'Each occurrence of a runtime pattern raises an exception. '
        'The pattern names are source specific. Consult the source for '
        'details.')
    self.verbosity = self._Add(
        'verbosity',
        help_text='Default logging verbosity for `gcloud` commands.  This is '
        'the equivalent of using the global `--verbosity` flag. Supported '
        'verbosity levels: `debug`, `info`, `warning`, `error`, `critical`, '
        'and `none`.')
    self.user_output_enabled = self._AddBool(
        'user_output_enabled',
        help_text='True, by default. If False, messages to the user and command'
        ' output on both standard output and standard error will be'
        ' suppressed.',
        default=True)
    self.interactive_ux_style = self._Add(
        'interactive_ux_style',
        help_text='How to display interactive UX elements like progress bars '
        'and trackers.',
        hidden=True,
        default=_SectionCore.InteractiveUXStyles.NORMAL,
        choices=[x.name for x in list(_SectionCore.InteractiveUXStyles)])
    self.log_http = self._AddBool(
        'log_http',
        help_text='If True, log HTTP requests and responses to the logs.  '
        'To see logs in the terminal, adjust `verbosity` settings. '
        'Otherwise, logs are available in their respective log files.',
        default=False)
    self.log_http_redact_token = self._AddBool(
        'log_http_redact_token',
        help_text='If true, this prevents log_http from printing access tokens.'
        ' This property does not have effect unless log_http is true.',
        default=True,
        hidden=True)
    self.log_http_show_request_body = self._AddBool(
        'log_http_show_request_body',
        help_text='If true, this allows log_http to print the request body'
        ' for debugging purposes on requests with the'
        ' "redact_request_body_reason" parameter set on '
        ' core.credentials.transports.GetApitoolsTransports.'
        ' Note: this property does not have any effect unless'
        ' log_http is true.',
        default=False,
        hidden=True)
    self.log_http_streaming_body = self._AddBool(
        'log_http_streaming_body',
        help_text='If True, log the streaming body instead of logging'
        ' the "<streaming body>" text. This flag results in reading the entire'
        ' response body in memory.'
        ' This property does not have effect unless log_http is true.',
        default=False,
        hidden=True)
    self.http_timeout = self._Add('http_timeout', hidden=True)
    self.check_gce_metadata = self._AddBool(
        'check_gce_metadata', hidden=True, default=True)
    self.print_completion_tracebacks = self._AddBool(
        'print_completion_tracebacks',
        hidden=True,
        help_text='If True, print actual completion exceptions with traceback '
        'instead of the nice UX scrubbed exceptions.')
    self.print_unhandled_tracebacks = self._AddBool(
        'print_unhandled_tracebacks', hidden=True)
    self.print_handled_tracebacks = self._AddBool(
        'print_handled_tracebacks', hidden=True)
    self.trace_token = self._Add(
        'trace_token',
        help_text='Token used to route traces of service requests for '
        'investigation of issues. This token will be provided by Google '
        'support.')
    self.request_reason = self._Add('request_reason', hidden=True)
    self.pass_credentials_to_gsutil = self._AddBool(
        'pass_credentials_to_gsutil',
        default=True,
        help_text='If True, pass the configured Google Cloud CLI authentication '
        'to gsutil.')
    self.api_key = self._Add(
        'api_key',
        hidden=True,
        help_text='If provided, this API key is attached to all outgoing '
        'API calls.')
    self.should_prompt_to_enable_api = self._AddBool(
        'should_prompt_to_enable_api',
        default=True,
        hidden=True,
        help_text='If true, will prompt to enable an API if a command fails due'
        ' to the API not being enabled.')
    self.color_theme = self._Add(
        'color_theme',
        help_text='Color palette for output.',
        hidden=True,
        default='off',
        choices=['off', 'normal', 'testing'])
    self.use_legacy_flattened_format = self._AddBool(
        'use_legacy_flattened_format',
        hidden=True,
        default=False,
        help_text='If True, use legacy format for flattened() and text().'
        'Please note that this option will not be supported indefinitely.')

    # Only formats that accept empty projections can be used globally
    supported_global_formats = sorted([
        formats.CONFIG, formats.DEFAULT, formats.DISABLE, formats.FLATTENED,
        formats.JSON, formats.LIST, formats.NONE, formats.OBJECT, formats.TEXT
    ])

    def FormatValidator(print_format):
      if print_format and print_format not in supported_global_formats:
        raise UnknownFormatError(print_format, supported_global_formats)

    self.format = self._Add(
        'format',
        validator=FormatValidator,
        help_text=textwrap.dedent("""\
        Sets the format for printing all command resources. This overrides the
        default command-specific human-friendly output format. Use
        `--verbosity=debug` flag to view the command-specific format. If both
        `core/default_format` and `core/format` are specified, `core/format`
        takes precedence. If both `core/format` and `--format` are specified,
        `--format` takes precedence. The supported formats are limited to:
        `{0}`. For more details run $ gcloud topic formats. Run `$ gcloud config
        set --help` to see more information about `core/format`""".format(
            '`, `'.join(supported_global_formats))))

    self.default_format = self._Add(
        'default_format',
        default='default',
        validator=FormatValidator,
        help_text=textwrap.dedent("""\
        Sets the default format for printing command resources.
        `core/default_format` overrides the default yaml format. If the command
        contains a command-specific output format, it takes precedence over the
        `core/default_format` value. Use `--verbosity=debug` flag to view the
        command-specific format. Both `core/format` and `--format` also take
        precedence over `core/default_format`. The supported formats are limited
        to: `{0}`. For more details run $ gcloud topic formats. Run `$ gcloud
        config set --help` to see more information about
        `core/default_format`""".format(
            '`, `'.join(supported_global_formats))))

    def ShowStructuredLogsValidator(show_structured_logs):
      if show_structured_logs is None:
        return
      if show_structured_logs not in ['always', 'log', 'terminal', 'never']:
        raise InvalidValueError(('show_structured_logs must be one of: '
                                 '[always, log, terminal, never]'))

    self.show_structured_logs = self._Add(
        'show_structured_logs',
        choices=['always', 'log', 'terminal', 'never'],
        default='never',
        hidden=False,
        validator=ShowStructuredLogsValidator,
        help_text=textwrap.dedent("""\
        Control when JSON-structured log messages for the current verbosity
        level (and above) will be written to standard error. If this property
        is disabled, logs are formatted as `text` by default.
        +
        Valid values are:
            *   `never` - Log messages as text
            *   `always` - Always log messages as JSON
            *   `log` - Only log messages as JSON if stderr is a file
            *   `terminal` - Only log messages as JSON if stderr is a terminal
        +
        If unset, default is `never`."""))

    def MaxLogDaysValidator(max_log_days):
      if max_log_days is None:
        return
      try:
        if int(max_log_days) < 0:
          raise InvalidValueError('Max number of days must be at least 0')
      except ValueError:
        raise InvalidValueError('Max number of days must be an integer')

    self.max_log_days = self._Add(
        'max_log_days',
        validator=MaxLogDaysValidator,
        help_text='Maximum number of days to retain log files before deleting.'
        ' If set to 0, turns off log garbage collection and does not delete log'
        ' files. If unset, the default is 30 days.',
        default='30')

    self.disable_file_logging = self._AddBool(
        'disable_file_logging',
        default=False,
        help_text='If True, `gcloud` will not store logs to a file. This may '
        'be useful if disk space is limited.')

    self.parse_error_details = self._Add(
        'parse_error_details',
        help_text=(
            'If True, `gcloud` will attempt to parse and interpret '
            'error details in API originating errors. If False, `gcloud` will '
            ' write flush error details as is to stderr/log.'
        ),
        default=True,
    )

    self.custom_ca_certs_file = self._Add(
        'custom_ca_certs_file',
        validator=ExistingAbsoluteFilepathValidator,
        help_text='Absolute path to a custom CA cert file.')

    def ProjectValidator(project):
      """Checks to see if the project string is valid."""
      if project is None:
        return

      if not isinstance(project, six.string_types):
        raise InvalidValueError('project must be a string')
      if project == '':  # pylint: disable=g-explicit-bool-comparison
        raise InvalidProjectError('The project property is set to the '
                                  'empty string, which is invalid.')
      if _VALID_PROJECT_REGEX.match(project):
        return

      if _LooksLikeAProjectName(project):
        raise InvalidProjectError(
            'The project property must be set to a valid project ID, not the '
            'project name [{value}]'.format(value=project))
      # Non heuristics for a better error message.
      raise InvalidProjectError(
          'The project property must be set to a valid project ID, '
          '[{value}] is not a valid project ID.'.format(value=project))

    self.project = self._Add(
        'project',
        help_text='Project ID of the Cloud Platform project to operate on '
        'by default.  This can be overridden by using the global `--project` '
        'flag.',
        validator=ProjectValidator,
        completer=('googlecloudsdk.command_lib.resource_manager.completers:'
                   'ProjectCompleter'),
        default_flag='--project')
    self.project_number = self._Add(
        'project_number',
        help_text='This property is for tests only. It should be kept in sync '
        'with core/project.',
        internal=True,
        hidden=True)

    self.universe_domain = self._Add(
        'universe_domain',
        help_text='Sets the domain of the universe to use for API requests.',
        default='googleapis.com',
    )

    self.credentialed_hosted_repo_domains = self._Add(
        'credentialed_hosted_repo_domains', hidden=True
    )

    def ConsoleLogFormatValidator(console_log_format):
      if console_log_format is None:
        return
      if console_log_format not in ['standard', 'detailed']:
        raise InvalidValueError(('console_log_format must be one of: '
                                 '[standard, detailed]'))

    self.console_log_format = self._Add(
        'console_log_format',
        choices=['standard', 'detailed'],
        default='standard',
        validator=ConsoleLogFormatValidator,
        help_text=textwrap.dedent("""\
        Control the format used to display log messages to the console.
        +
        Valid values are:
            *   `standard` - Simplified log messages are displayed on the console.
            *   `detailed` - More detailed messages are displayed on the console.
        +
        If unset, default is `standard`."""))


class _SectionDataPipelines(_Section):
  """Contains the properties for the 'datapipelines' section."""

  def __init__(self):
    super(_SectionDataPipelines, self).__init__('datapipelines')
    self.disable_public_ips = self._AddBool(
        'disable_public_ips',
        help_text='Specifies that Cloud Dataflow workers '
        'must not use public IP addresses.',
        default=False)
    self.enable_streaming_engine = self._AddBool(
        'enable_streaming_engine',
        help_text='Set this to true to enable Streaming Engine for the job.',
        default=False)


class _SectionDataflow(_Section):
  """Contains the properties for the 'dataflow' section."""

  def __init__(self):
    super(_SectionDataflow, self).__init__('dataflow')
    self.disable_public_ips = self._AddBool(
        'disable_public_ips',
        help_text='Specifies that Cloud Dataflow workers '
        'must not use public IP addresses.',
        default=False)
    self.print_only = self._AddBool(
        'print_only',
        help_text='Prints the container spec to stdout. Does not save in '
        'Google Cloud Storage.',
        default=False)
    self.enable_streaming_engine = self._AddBool(
        'enable_streaming_engine',
        help_text='Set this to true to enable Streaming Engine for the job.',
        default=False)


class _SectionDatafusion(_Section):
  """Contains the properties for the 'datafusion' section."""

  def __init__(self):
    super(_SectionDatafusion, self).__init__('datafusion')
    self.location = self._Add(
        'location',
        help_text=(
            'Datafusion location to use. Each Datafusion location '
            'constitutes an independent resource namespace constrained to '
            'deploying environments into Compute Engine regions inside this '
            'location. This parameter corresponds to the '
            '/locations/<location> segment of the Datafusion resource URIs being '
            'referenced.'))


class _SectionDataplex(_Section):
  """Contains the properties for the 'dataplex' section."""

  def __init__(self):
    super(_SectionDataplex, self).__init__('dataplex')
    self.location = self._Add(
        'location',
        help_text=(
            'Dataplex location to use. When a `location` is required but not '
            'provided by a flag, the command will fall back to this value, if '
            'set.'))
    self.lake = self._Add(
        'lake',
        help_text=(
            'Dataplex lake to use. When a `lake` is required but not '
            'provided by a flag, the command will fall back to this value, if '
            'set.'))
    self.zone = self._Add(
        'zone',
        help_text=(
            'Dataplex zone to use. When a `zone` is required but not '
            'provided by a flag, the command will fall back to this value, if '
            'set.'))
    self.asset = self._Add(
        'asset',
        help_text=(
            'Dataplex asset to use. When an `asset` is required but not '
            'provided by a flag, the command will fall back to this value, if '
            'set.'))


class _SectionDataproc(_Section):
  """Contains the properties for the 'dataproc' section."""

  def __init__(self):
    super(_SectionDataproc, self).__init__('dataproc')
    self.region = self._Add(
        'region',
        help_text=(
            'Dataproc region to use. Each Dataproc region constitutes an '
            'independent resource namespace constrained to deploying instances '
            'into Compute Engine zones inside the region.'))
    self.location = self._Add(
        'location',
        help_text=(
            'Dataproc location to use. Each Dataproc location constitutes an '
            'independent resource namespace constrained to deploying instances '
            'into Compute Engine zones inside the location.'))


class _SectionDeclarative(_Section):
  """Contains the properties for the 'declarative' section."""

  def __init__(self):
    super(_SectionDeclarative, self).__init__('declarative', hidden=True)
    self.client = self._Add(
        'client_type',
        choices=['dcl', 'kcc'],
        help_text='Underlying declarative client library to use for declarative commands.',
        default='kcc')
    self.format = self._Add(
        'format',
        choices=['krm', 'terraform'],
        help_text='Declarative format to use for declarative commands.',
        default='krm')


class _SectionDeploy(_Section):
  """Contains the properties for the 'deploy' section."""

  def __init__(self):
    super(_SectionDeploy, self).__init__('deploy')
    self.region = self._Add(
        'region',
        help_text=(
            'Cloud Deploy region to use. Each Cloud Deploy '
            'region constitutes an independent resource namespace constrained '
            'to deploying instances into Compute Engine zones inside '
            'the region.'))
    self.delivery_pipeline = self._Add(
        'delivery_pipeline',
        help_text=('Delivery Pipeline being managed by Cloud Deploy.'))


class _SectionDeploymentManager(_Section):
  """Contains the properties for the 'deployment_manager' section."""

  def __init__(self):
    super(_SectionDeploymentManager, self).__init__('deployment_manager')
    self.glob_imports = self._AddBool(
        'glob_imports',
        default=False,
        help_text=(
            'Enable import path globbing. Uses glob patterns to match multiple '
            'imports in a config file.'))


class _SectionDevshell(_Section):
  """Contains the properties for the 'devshell' section."""

  def __init__(self):
    super(_SectionDevshell, self).__init__('devshell')
    self.image = self._Add(
        'image', hidden=True, default=const_lib.DEFAULT_DEVSHELL_IMAGE)
    self.metadata_image = self._Add(
        'metadata_image', hidden=True, default=const_lib.METADATA_IMAGE)


class _SectionDiagnostics(_Section):
  """Contains the properties for the 'diagnostics' section."""

  def __init__(self):
    super(_SectionDiagnostics, self).__init__('diagnostics', hidden=True)
    self.hidden_property_allowlist = self._Add(
        'hidden_property_allowlist',
        internal=True,
        help_text=('Comma separated list of hidden properties that should be '
                   'allowed by the hidden properties diagnostic.'))


class _SectionEdgeContainer(_Section):
  """Contains the properties for the 'edge_container' section."""

  def __init__(self):
    super(_SectionEdgeContainer, self).__init__('edge_container', hidden=True)
    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Private CA '
        'resources. When a `--location` flag is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionEmulator(_Section):
  """Contains the properties for the 'emulator' section.

  This is used to configure emulator properties for pubsub and datastore, such
  as host_port and data_dir.
  """

  def __init__(self):
    super(_SectionEmulator, self).__init__('emulator', hidden=True)
    self.datastore_data_dir = self._Add('datastore_data_dir')
    self.pubsub_data_dir = self._Add('pubsub_data_dir')
    self.datastore_host_port = self._Add(
        'datastore_host_port', default='localhost:8081')
    self.pubsub_host_port = self._Add(
        'pubsub_host_port', default='localhost:8085')
    self.bigtable_host_port = self._Add(
        'bigtable_host_port', default='localhost:8086')


class _SectionEventarc(_Section):
  """Contains the properties for the 'eventarc' section."""

  def __init__(self):
    super(_SectionEventarc, self).__init__('eventarc')
    self.location = self._Add(
        'location',
        help_text='The default location to use when working with Eventarc '
        "resources. This should be either ``global'' or one of the supported "
        'regions. When a `--location` flag is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionExperimental(_Section):
  """Contains the properties for gcloud experiments."""

  def __init__(self):
    super(_SectionExperimental, self).__init__('experimental', hidden=True)


class _SectionFilestore(_Section):
  """Contains the properties for the 'filestore' section."""

  def __init__(self):
    super(_SectionFilestore, self).__init__('filestore')
    self.location = self._Add(
        'location',
        help_text='Please use the `--location` flag or set the '
        'filestore/zone or filestore/region property.')
    self.zone = self._Add(
        'zone',
        help_text='Default zone to use when working with Cloud Filestore '
        'zones. When a `--zone` flag is required but not '
        'provided, the command will fall back to this value, if set.')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with Cloud Filestore '
        'regions. When a `--region` flag is required but not '
        'provided, the command will fall back to this value, if set.')


class _SectionFunctions(_Section):
  """Contains the properties for the 'functions' section."""

  def __init__(self):
    super(_SectionFunctions, self).__init__('functions')
    self.region = self._Add(
        'region',
        default='us-central1',
        help_text='Default region to use when working with Cloud '
        'Functions resources. When a `--region` flag is required but not '
        'provided, the command will fall back to this value, if set. To see '
        'valid choices, run `gcloud beta functions regions list`.',
        completer=('googlecloudsdk.command_lib.functions.flags:'
                   'LocationsCompleter'))
    self.gen2 = self._AddBool(
        'gen2',
        default=False,
        help_text=(
            'Default environment to use when working with Cloud Functions'
            ' resources. When neither `--gen2` nor `--no-gen2` is provided, the'
            ' decision of whether to use Generation 2 falls back to this value'
            ' if set.'
        ),
    )
    self.v2 = self._AddBool(
        'v2',
        default=False,
        hidden=True,
        help_text='DEPRECATED. Use `functions/gen2` instead. '
        'This property will be removed in a future release.')


class _SectionGcloudignore(_Section):
  """Contains the properties for the 'gcloudignore' section."""

  def __init__(self):
    super(_SectionGcloudignore, self).__init__('gcloudignore')
    self.enabled = self._AddBool(
        'enabled',
        default=True,
        help_text=(
            'If True, do not upload `.gcloudignore` files (see `$ gcloud topic '
            'gcloudignore`). If False, turn off the gcloudignore mechanism '
            'entirely and upload all files.'))


class _SectionGkeHub(_Section):
  """Contains the properties for the 'gkehub' section."""

  def __init__(self):
    super(_SectionGkeHub, self).__init__('gkehub', hidden=True)
    self.location = self._Add(
        'location',
        default='global',
        help_text='Please use the `--location` flag to set membership location.'
    )


class _SectionGkebackup(_Section):
  """Contains the properties for 'gkebackup' section."""

  def __init__(self):
    super(_SectionGkebackup, self).__init__('gkebackup')
    self.location = self._Add(
        'location',
        default='-',
        help_text=(
            'Default location to use when working with Backup for GKE Services '
            'resources. When a `--location` flag is required but not provided, '
            'the command will fall back to this value.'))
    self.backup_plan = self._Add(
        'backup_plan',
        default='-',
        help_text=(
            'Default backup plan ID to use when working with Backup for GKE '
            'Services resources. When a `--backup-plan` flag is required but '
            'not provided, the command will fall back to this value.'))
    self.backup = self._Add(
        'backup',
        default='-',
        help_text=(
            'Default backup ID to use when working with Backup for GKE '
            'Services resources. When a `--backup` flag is required but not '
            'provided, the command will fall back to this value.'))
    self.restore = self._Add(
        'restore_plan',
        default='-',
        help_text=(
            'Default restore plan ID to use when working with Backup for GKE '
            'Services resources. When a `--restore-plan` flag is required but '
            'not provided, the command will fall back to this value.'))
    self.restore = self._Add(
        'restore',
        default='-',
        help_text=(
            'Default restore ID to use when working with Backup for GKE '
            'Services resources. When a `--restore` flag is required but not '
            'provided, the command will fall back to this value.'))


class _SectionGrpc(_Section):
  """Contains the properties for gRPC clients."""

  def __init__(self):
    super(_SectionGrpc, self).__init__('grpc', hidden=True)
    self.enable_http2_bdp_probe = self._AddBool(
        'enable_http2_bdp_probe',
        default=False,
        hidden=True,
        help_text='If True, gRPC will send BDP probes to estimate bandwidth.',
    )
    self.http2_lookahead_bytes = self._Add(
        'http2_lookahead_bytes',
        default=0,
        hidden=True,
        validator=_IntegerValidator,
        help_text='The number of bytes gRPC will look ahead in the TCP stream.',
    )
    self.max_metadata_size = self._Add(
        'max_metadata_size',
        hidden=True,
        validator=functools.partial(_IntegerValidator, 'max_metadata_size'),
        help_text='Maximum metadata size (soft limit), in bytes.',
    )


class _SectionHealthcare(_Section):
  """Contains the properties for the 'healthcare' section."""

  def __init__(self):
    super(_SectionHealthcare, self).__init__('healthcare')
    self.location = self._Add(
        'location',
        default='us-central1',
        help_text='Default location to use when working with Cloud Healthcare  '
        'resources. When a `--location` flag is required but not provided, the  '
        'command will fall back to this value.')
    self.dataset = self._Add(
        'dataset',
        help_text='Default dataset to use when working with Cloud Healthcare '
        'resources. When a `--dataset` flag is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionInfraManager(_Section):
  """Contains the properties for the 'infra-manager' section."""

  def __init__(self):
    super(_SectionInfraManager, self).__init__('infra-manager', hidden=True)
    self.location = self._Add(
        'location',
        default=None,
        help_text='The default region to use when working with Infra Manager '
        'resources. When a `--location` flag is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionInteractive(_Section):
  """Contains the properties for the 'interactive' section."""

  def __init__(self):
    super(_SectionInteractive, self).__init__('interactive')
    self.bottom_bindings_line = self._AddBool(
        'bottom_bindings_line',
        default=True,
        help_text='If True, display the bottom key bindings line.')
    self.bottom_status_line = self._AddBool(
        'bottom_status_line',
        default=False,
        help_text='If True, display the bottom status line.')
    self.completion_menu_lines = self._Add(
        'completion_menu_lines',
        default=4,
        help_text='Number of lines in the completion menu.')
    self.context = self._Add(
        'context', default='', help_text='Command context string.')
    self.debug = self._AddBool(
        'debug',
        default=False,
        hidden=True,
        help_text='If True, enable the debugging display.')
    self.fixed_prompt_position = self._Add(
        'fixed_prompt_position',
        default=False,
        help_text='If True, display the prompt at the same position.')
    self.help_lines = self._Add(
        'help_lines',
        default=10,
        help_text='Maximum number of help snippet lines.')
    self.hidden = self._AddBool(
        'hidden',
        default=False,
        help_text='If True, expose hidden commands/flags.')
    self.justify_bottom_lines = self._AddBool(
        'justify_bottom_lines',
        default=False,
        help_text='If True, left- and right-justify bottom toolbar lines.')
    self.manpage_generator = self._Add(
        'manpage_generator',
        default=True,
        help_text=('If True, use the manpage CLI tree generator for '
                   'unsupported commands.'))
    self.multi_column_completion_menu = self._AddBool(
        'multi_column_completion_menu',
        default=False,
        help_text='If True, display the completions as a multi-column menu.')
    self.obfuscate = self._AddBool(
        'obfuscate',
        default=False,
        hidden=True,
        help_text='If True, obfuscate status PII.')
    self.prompt = self._Add(
        'prompt', default='$ ', help_text='Command prompt string.')
    self.show_help = self._AddBool(
        'show_help',
        default=True,
        help_text='If True, show help as command args are being entered.')
    self.suggest = self._AddBool(
        'suggest',
        default=False,
        help_text='If True, add command line suggestions based on history.')


class _SectionKubeRun(_Section):
  """Contains the properties for the 'kuberun' section."""

  def __init__(self):
    super(_SectionKubeRun, self).__init__('kuberun')
    self.enable_experimental_commands = self._AddBool(
        'enable_experimental_commands',
        help_text='If True, experimental KubeRun commands will not prompt to '
        'continue.',
        hidden=True)

    self.environment = self._Add(
        'environment',
        help_text='If set, this environment will be used as the deployment'
        'target in all KubeRun commands.',
        hidden=True)

    self.cluster = self._Add(
        'cluster',
        help_text='ID of the cluster or fully qualified identifier '
        'for the cluster',
        hidden=True)

    self.cluster_location = self._Add(
        'cluster_location',
        help_text='Zone or region in which the cluster is located.',
        hidden=True)

    self.use_kubeconfig = self._AddBool(
        'use_kubeconfig',
        help_text='Use the default or provided kubectl config file.',
        hidden=True)

    self.kubeconfig = self._Add(
        'kubeconfig',
        help_text='Absolute path to your kubectl config file.',
        hidden=True)

    self.context = self._Add(
        'context',
        help_text='Name of the context in your kubectl config file to use.',
        hidden=True)


class _SectionLooker(_Section):
  """Contains the properties for the 'looker' section."""

  def __init__(self):
    super(_SectionLooker, self).__init__('looker')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with Cloud '
        'Looker resources. When a `region` is required but not '
        'provided by a flag, the command will fall back to this value, if set.')


class _SectionLustre(_Section):
  """Contains the properties for the 'lustre' section."""

  def __init__(self):
    super(_SectionLustre, self).__init__('lustre')
    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Cloud Lustre'
                  ' resources. When a `location` value is required but not '
                  'provided, the command will fall back to this value, if set.')


class _SectionMediaAsset(_Section):
  """Contains the properties for the 'media_asset' section."""

  def __init__(self):
    super(_SectionMediaAsset, self).__init__('media_asset')
    self.location = self._Add(
        'location',
        default='us-central1',
        help_text=(
            'Default location to use when working with Cloud Media Asset '
            'resources. When a `--location` flag is required but not provided, '
            'the command will fall back to this value.'))


class _SectionMemcache(_Section):
  """Contains the properties for the 'memcache' section."""

  def __init__(self):
    super(_SectionMemcache, self).__init__('memcache')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with Cloud Memorystore '
        'for Memcached resources. When a `region` is required but not provided '
        'by a flag, the command will fall back to this value, if set.')


class _SectionMetastore(_Section):
  """Contains the properties for the 'metastore' section."""

  class Tier(enum.Enum):
    developer = 1
    enterprise = 3

  def TierValidator(self, tier):
    if tier is None:
      return

    if tier not in [x.name for x in list(_SectionMetastore.Tier)]:
      raise InvalidValueError(
          ('tier `{0}` must be one of: [developer, enterprise]'.format(tier)))

  def __init__(self):
    super(_SectionMetastore, self).__init__('metastore')
    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Dataproc '
        'Metastore. When a `location` is required but not provided by a flag, '
        'the command will fall back to this value, if set.')
    self.tier = self._Add(
        'tier',
        validator=self.TierValidator,
        help_text=textwrap.dedent("""\
        Default tier to use when creating Dataproc Metastore services.
        When a `tier` is required but not provided by a flag,
        the command will fall back to this value, if set.
        +
        Valid values are:
            *   `developer` - The developer tier provides limited scalability
            and no fault tolerance. Good for low-cost proof-of-concept.
            *   `enterprise` - The enterprise tier provides multi-zone high
            availability, and sufficient scalability for enterprise-level
            Dataproc Metastore workloads."""),
        choices=[x.name for x in list(_SectionMetastore.Tier)])


class _SectionMetrics(_Section):
  """Contains the properties for the 'metrics' section."""

  def __init__(self):
    super(_SectionMetrics, self).__init__('metrics', hidden=True)
    self.environment = self._Add('environment', hidden=True)
    self.environment_version = self._Add('environment_version', hidden=True)
    self.command_name = self._Add('command_name', internal=True)

    def GetAgentName():
      from googlecloudsdk.core.util import agents  # pylint: disable=g-import-not-at-top

      return agents.DetectAIAgent()

    self.agent_name = self._Add(
        'agent_name', hidden=True, callbacks=[GetAgentName]
    )


class _SectionMlEngine(_Section):
  """Contains the properties for the 'ml_engine' section."""

  def __init__(self):
    super(_SectionMlEngine, self).__init__('ml_engine')
    self.polling_interval = self._Add(
        'polling_interval',
        default=60,
        help_text=('Interval (in seconds) at which to poll logs from your '
                   'Cloud ML Engine jobs. Note that making it much faster than '
                   'the default (60) will quickly use all of your quota.'))
    self.local_python = self._Add(
        'local_python',
        default=None,
        help_text=('Full path to the Python interpreter to use for '
                   'Cloud ML Engine local predict/train jobs. If not '
                   'specified, the default path is the one to the Python '
                   'interpreter found on system `PATH`.'))


class _SectionMps(_Section):
  """Contains the properties for the 'mps' section."""

  def __init__(self):
    super(_SectionMps, self).__init__('mps')
    self.product = self._Add(
        'product',
        default=None,
        help_text='Id for Marketplace Solutions Product. '
        )


class _SectionNetapp(_Section):
  """Contains the properties for the 'netapp' section."""

  def __init__(self):
    super(_SectionNetapp, self).__init__('netapp')

    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Cloud NetApp Files'
                  ' resources. When a `location` value is required but not '
                  'provided, the command will fall back to this value, if set.')


class _SectionNotebooks(_Section):
  """Contains the properties for the 'notebooks' section."""

  def __init__(self):
    super(_SectionNotebooks, self).__init__('notebooks')

    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Notebook '
        'resources. When a `location` value is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionPrivateCa(_Section):
  """Contains the properties for the 'privateca' section."""

  def __init__(self):
    super(_SectionPrivateCa, self).__init__('privateca')
    self.location = self._Add(
        'location',
        help_text='Default location to use when working with Private CA '
        'resources. When a `--location` flag is required but not provided, the '
        'command will fall back to this value, if set.',
        completer=('googlecloudsdk.command_lib.privateca.completers:'
                   'LocationsCompleter'))


class _SectionProxy(_Section):
  """Contains the properties for the 'proxy' section."""

  def __init__(self):
    super(_SectionProxy, self).__init__('proxy')
    self.address = self._Add(
        'address', help_text='Hostname or IP address of proxy server.')
    self.port = self._Add(
        'port', help_text='Port to use when connected to the proxy server.')
    self.rdns = self._Add(
        'rdns',
        default=True,
        help_text='If True, DNS queries will not be performed '
        'locally, and instead, handed to the proxy to resolve. This is default'
        ' behavior.')
    self.username = self._Add(
        'username',
        help_text='Username to use when connecting, if the proxy '
        'requires authentication.')
    self.password = self._Add(
        'password',
        help_text='Password to use when connecting, if the proxy '
        'requires authentication.')

    valid_proxy_types = sorted(http_proxy_types.PROXY_TYPE_MAP.keys())

    def ProxyTypeValidator(proxy_type):
      if proxy_type is not None and proxy_type not in valid_proxy_types:
        raise InvalidValueError(
            'The proxy type property value [{0}] is not valid. '
            'Possible values: [{1}].'.format(proxy_type,
                                             ', '.join(valid_proxy_types)))

    self.proxy_type = self._Add(
        'type',
        help_text='Type of proxy being used.  Supported proxy types are:'
        ' [{0}].'.format(', '.join(valid_proxy_types)),
        validator=ProxyTypeValidator,
        choices=valid_proxy_types)


class _SectionPubsub(_Section):
  """Contains the properties for the 'pubsub' section."""

  def __init__(self):
    super(_SectionPubsub, self).__init__('pubsub')
    self.legacy_output = self._AddBool(
        'legacy_output',
        default=False,
        internal=True,
        hidden=True,
        help_text=('Use the legacy output for beta pubsub commands. The legacy '
                   'output from beta is being deprecated. This property will '
                   'eventually be removed.'))


class _SectionRecaptcha(_Section):
  """Contains the properties for the 'recaptcha' section."""

  def __init__(self):
    super(_SectionRecaptcha, self).__init__('recaptcha')


class _SectionRedis(_Section):
  """Contains the properties for the 'redis' section."""

  def __init__(self):
    super(_SectionRedis, self).__init__('redis')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with Cloud '
        'Memorystore for Redis resources. When a `region` is required but not '
        'provided by a flag, the command will fall back to this value, if set.')


class _SectionRegional(_Section):
  """Contains the properties for the 'regional' section."""

  # User properties.
  GLOBAL = 'global'
  REGIONAL = 'regional'
  REGIONAL_PREFERRED = 'regional-preferred'
  # Command / group support level annotations.
  SUPPORTED = 'supported'
  REQUIRED = 'required'

  def __init__(self):
    super(_SectionRegional, self).__init__('regional')
    self.endpoint_mode = self._Add(
        'endpoint_mode',
        choices=[self.GLOBAL, self.REGIONAL, self.REGIONAL_PREFERRED],
        help_text="""\
        Specifies endpoint mode for a given command. Regional endpoints
        provide enhanced data residency and reliability by ensuring your
        request is handled entirely within the specified Google Cloud region.
        This differs from global endpoints, which may process parts of the
        request outside the target region.

        Benefits of Regional Endpoints:
        (1) Data Residency: Keeps data in-transit within the chosen region.
        (2) Improved Isolation: Reduces the risk of cross-region impact
        from outages.

        The choices are:
        *   `global` - (Default) Use global rather than regional endpoints.
        *   `regional` - Only use regional endpoints. An error will be raised
            if a regional endpoint is not available for a given command.
        *   `regional-preferred` - Use regional endpoints when available,
            otherwise use global endpoints. Recommended for most users.
        """,
    )
    self.endpoint_compatibility = self._Add(
        'endpoint_compatibility',
        choices=[self.SUPPORTED, self.REQUIRED],
        hidden=True,
        help_text=(
            'Hidden property set by @base.RegionalEndpoints[Supported|Required]'
            ' annotation that indicates the level of regional endpoint'
            ' compatibility for the command/group in question.'
        ),
    )


class _SectionResourcePolicy(_Section):
  """Contains the properties for the 'resource_policy' section."""

  def __init__(self):
    super(_SectionResourcePolicy, self).__init__('resource_policy', hidden=True)
    self.org_restriction_header = self._Add(
        'org_restriction_header',
        default=None,
        help_text='Default organization restriction header to use when '
        'working with GCP resources. If set, the value '
        'must be in JSON format and must contain a comma separated list '
        'of authorized GCP organization IDs. The JSON must then be encoded '
        'by following the RFC 4648, section 5, specifications. '
        'See https://www.rfc-editor.org/rfc/rfc4648#section-5 '
        'for more information about base 64 encoding. And visit '
        'https://cloud.google.com/resource-manager/docs/organization-restrictions/overview '
        'for more information about organization restrictions.')


class _SectionRun(_Section):
  """Contains the properties for the 'run' section."""

  def __init__(self):
    super(_SectionRun, self).__init__('run')
    self.region = self._Add(
        'region',
        help_text='Default region to use when working with Cloud '
        'Run resources. When a `--region` flag is required '
        'but not provided, the command will fall back to this value, if set.')

    self.namespace = self._Add(
        'namespace',
        help_text='Specific to working with Cloud on GKE or '
        'a Kubernetes cluster: Kubernetes namespace for the resource.',
        hidden=True)

    self.cluster = self._Add(
        'cluster',
        help_text='ID of the cluster or fully qualified identifier '
        'for the cluster')

    self.cluster_location = self._Add(
        'cluster_location',
        help_text='Zone or region in which the cluster is located.')

    self.platform = self._Add(
        'platform',
        choices=['gke', 'managed', 'kubernetes'],
        default='managed',
        help_text='Target platform for running commands.')

    self.enable_unified_build = self._Add(
        'enable_unified_build',
        default=False,
        hidden=True,
        help_text='Enables using Cloud Run API to orchestrate builds.',)


class _SectionScc(_Section):
  """Contains the properties for the 'scc' section."""

  def __init__(self):
    super(_SectionScc, self).__init__('scc')
    self.organization = self._Add(
        'organization',
        help_text='Default organization `gcloud` should use for scc surface.')
    self.parent = self._Add(
        'parent',
        help_text='Default parent `gcloud` should use for scc surface.')


class _SectionSecrets(_Section):
  """Contains the properties for the 'secrets' section."""

  def __init__(self):
    super(_SectionSecrets, self).__init__('secrets')
    self.replication_policy = self._Add(
        'replication-policy',
        choices=['automatic', 'user-managed'],
        help_text='The type of replication policy to apply to secrets. Allowed '
        'values are "automatic" and "user-managed". If user-managed then '
        'locations must also be provided.',
    )
    self.locations = self._Add(
        'locations',
        help_text='A comma separated list of the locations to replicate '
        'secrets to. Only applies to secrets with a user-managed policy.')


class _SectionServiceHealth(_Section):
  """Contains the properties for the 'servicehealth' section."""

  def __init__(self):
    super(_SectionServiceHealth, self).__init__('servicehealth')
    self.location = self._Add(
        'location',
        default='global',
        help_text='Default location to use when working with Service Health '
        'resources. When a `--location` flag is required but not provided, the '
        'command will fall back to this value, if set.')


class _SectionSpanner(_Section):
  """Contains the properties for the 'spanner' section."""

  def __init__(self):
    super(_SectionSpanner, self).__init__('spanner')
    self.instance = self._Add(
        'instance',
        help_text='Default instance to use when working with Cloud Spanner '
        'resources. When an instance is required but not provided by a flag, '
        'the command will fall back to this value, if set.',
        completer='googlecloudsdk.command_lib.spanner.flags:InstanceCompleter')


class _SectionSsh(_Section):
  """Contains SSH-related properties."""

  def __init__(self):
    super(_SectionSsh, self).__init__('ssh')
    self.putty_force_connect = self._AddBool(
        'putty_force_connect',
        default=True,  # For backwards compatibility only.
        help_text='Whether or not `gcloud` should automatically accept new or '
        'changed host keys when executing plink/pscp commands on Windows. '
        'Defaults to True, but can be set to False to present these '
        'interactive prompts to the user for host key checking.')
    self.verify_internal_ip = self._AddBool(
        'verify_internal_ip',
        default=True,
        help_text='Whether or not `gcloud` should perform an initial SSH '
        'connection to verify an instance ID is correct when connecting via '
        'its internal IP. Without this check, `gcloud` will simply connect to '
        'the internal IP of the desired instance, which may be wrong if the '
        'desired instance is in a different subnet but happens to share the '
        'same internal IP as an instance in the current subnet. Defaults to '
        'True.')


class CheckHashes(enum.Enum):
  """Different settings for hashing throughout gcloud storage.

  More details in _CHECK_HASHES_HELP_TEXT.
  """

  IF_FAST_ELSE_FAIL = 'if_fast_else_fail'
  IF_FAST_ELSE_SKIP = 'if_fast_else_skip'
  ALWAYS = 'always'
  NEVER = 'never'


class StoragePreferredApi(enum.Enum):
  """Preferred API for gcloud storage."""

  JSON = 'json'
  GRPC_WITH_JSON_FALLBACK = 'grpc_with_json_fallback'


class _SectionStorage(_Section):
  """Contains the properties for the 'storage' section."""

  _CHECK_HASHES_HELP_TEXT = textwrap.dedent("""\
      'check_hashes' specifies how strictly to require integrity checking for
      downloaded data. Legal values are:
      +
      * 'if_fast_else_fail' - (default) Only integrity check if the digest
      will run efficiently (using compiled code), else fail the download.
      +
      * 'if_fast_else_skip' - Only integrity check if the server supplies a hash
      and the local digest computation will run quickly, else skip the check.
      +
      * 'always' - Always check download integrity regardless of possible
      performance costs.
      +
      * 'never' - Don't perform download integrity checks. This setting is
      not recommended except for special cases such as measuring download
      performance excluding time for integrity checking.
      +
      This option exists to assist users who wish to download a composite
      object and are unable to install crcmod with the C-extension. CRC32c is
      the only available integrity check for composite objects, and without the
      C-extension, download performance can be significantly degraded by the
      digest computation. This option is ignored for daisy-chain copies, which
      don't compute hashes but instead (inexpensively) compare the cloud source
      and destination hashes.""")

  DEFAULT_COPY_CHUNK_SIZE = '100Mi'
  DEFAULT_DOWNLOAD_CHUNK_SIZE = '256Ki'
  DEFAULT_UPLOAD_CHUNK_SIZE = '100Mi'
  DEFAULT_MULTIPART_THRESHOLD = '8Mi'
  DEFAULT_MULTIPART_CHUNKSIZE = '8Mi'
  DEFAULT_RESUMABLE_THRESHOLD = '8Mi'
  DEFAULT_BIDI_STREAMING_FLUSH_SIZE = '100Mi'
  DEFAULT_RSYNC_LIST_CHUNK_SIZE = 32000

  def __init__(self):
    super(_SectionStorage, self).__init__('storage')
    self.additional_headers = self._Add(
        'additional_headers',
        help_text=(
            'Includes arbitrary headers in storage API calls.'
            ' Accepts a comma separated list of key=value pairs, e.g.'
            ' `header1=value1,header2=value2`.'
        ),
    )

    self.run_by_gsutil_shim = self._AddBool(
        'run_by_gsutil_shim',
        help_text=(
            'Indicates command was launched by gsutil-to-gcloud-storage shim.'
        ),
        hidden=True,
    )

    self.check_hashes = self._Add(
        'check_hashes',
        default=CheckHashes.IF_FAST_ELSE_FAIL.value,
        help_text=self._CHECK_HASHES_HELP_TEXT,
        choices=([setting.value for setting in CheckHashes]),
    )

    self.enable_server_side_hash_validation = self._AddBool(
        'enable_server_side_hash_validation',
        default=False,
        hidden=True,
        help_text=(
            'If True, hashes will be sent to the server in the last request for'
            ' server-side hash validation during uploads.'
        ),
    )

    self.check_mv_early_deletion_fee = self._AddBool(
        'check_mv_early_deletion_fee',
        default=True,
        help_text=(
            'Block mv commands that may incur an early deletion fee'
            ' (the source object in a mv is deleted).'
        ),
    )

    self.convert_incompatible_windows_path_characters = self._AddBool(
        'convert_incompatible_windows_path_characters',
        default=True,
        help_text=(
            'Allows automatic conversion of invalid path characters on Windows.'
            ' If not enabled, Windows will raise an OSError if an invalid'
            ' character is encountered.'
        ),
    )

    self.copy_chunk_size = self._Add(
        'copy_chunk_size',
        default=self.DEFAULT_COPY_CHUNK_SIZE,
        validator=_HumanReadableByteAmountValidator,
        help_text='Chunk size used for copying to in clouds or on disk.',
    )

    self.download_chunk_size = self._Add(
        'download_chunk_size',
        default=self.DEFAULT_DOWNLOAD_CHUNK_SIZE,
        validator=_HumanReadableByteAmountValidator,
        help_text='Chunk size used for downloading to clouds.',
    )

    self.enable_task_graph_debugging = self._AddBool(
        'enable_task_graph_debugging',
        default=False,
        hidden=True,
        help_text='Enables task graph debugging for gcloud storage commands.',
    )

    self.enable_zonal_buckets_bidi_streaming = self._AddBool(
        'enable_zonal_buckets_bidi_streaming',
        default=True,
        hidden=True,
        help_text=(
            'Enables zonal buckets bidi streaming for gcloud storage commands.'
        ),
    )

    self.use_mrd_bidi_downloads = self._AddBool(
        'use_mrd_bidi_downloads',
        default=False,
        hidden=True,
        help_text=(
            'If True, gcloud storage will use the MRD-based gRPC bidi'
            ' downloads.'
        ),
    )

    self.attempt_grpc_direct_path = self._AddBool(
        'attempt_grpc_direct_path',
        default=True,
        hidden=True,
        help_text=(
            'If True, gRPC will attempt to use DirectPath for Zonal Buckets.'
            'To disable DirectPath, set this property to False.'
        ),
    )

    self.use_nic_isolation = self._AddBool(
        'use_nic_isolation',
        default=False,
        hidden=True,
        help_text=(
            'If True, gcloud storage will pin worker processes to specific'
            ' CPUs to isolate them from NIC interrupts.'
        ),
    )

    self.preallocate_disk_space = self._AddBool(
        'preallocate_disk_space',
        default=False,
        hidden=True,
        help_text=(
            'If True, gcloud storage will reserve disk space for bidi '
            'streaming downloads before downloading the data. This can '
            'improve download speeds but may leave empty space if a '
            'download crashes.'
        ),
    )
    self.use_move_object_api = self._AddBool(
        'use_move_object_api',
        default=True,
        hidden=True,
        help_text=(
            'If True, gcloud storage will use the MoveObject API for'
            ' intra-bucket moves when possible.'
        ),
    )

    self.task_graph_debugging_snapshot_duration = self._Add(
        'task_graph_debugging_snapshot_duration',
        default=1,
        hidden=True,
        validator=_IntegerValidator,
        help_text=(
            'The duration in seconds for which the task graph debugging'
            'framework will take a snapshot'
            'of the task graph, task buffer ,'
            'management threads and worker threads'
            'and then display them and provide a'
            'visual representation of the contents'
            'and help track down the issue.'
        ),
    )

    self.upload_chunk_size = self._Add(
        'upload_chunk_size',
        default=self.DEFAULT_UPLOAD_CHUNK_SIZE,
        validator=_HumanReadableByteAmountValidator,
        help_text='Chunk size used for uploading to clouds.')

    self.max_retries = self._Add(
        'max_retries',
        default=23,
        help_text='Max number of retries for operations like copy.')

    self.base_retry_delay = self._Add(
        'base_retry_delay',
        default=1,
        help_text='Second delay between retrying operations. May be multiplied'
        ' by exponential_sleep_multiplier.')

    self.exponential_sleep_multiplier = self._Add(
        'exponential_sleep_multiplier',
        default=2,
        help_text='Used in exponential backoff for retrying operations.')

    self.gs_xml_endpoint_url = self._Add(
        'gs_xml_endpoint_url',
        default='https://storage.googleapis.com',
        hidden=True,
        help_text='The endpoint used to Google Cloud Storage when HMAC '
        'authentication through Boto3.')

    self.gs_xml_access_key_id = self._Add(
        'gs_xml_access_key_id',
        default=None,
        hidden=True,
        help_text='Legacy Cloud Storage HMAC credential access key ID.'
        'WARNING: This in conjunction with storage/gs_xml_secret_access_key '
        'forces gcloud storage to use the XML API to call Cloud Storage, '
        'which means not all commands will work as expected.')

    self.gs_xml_secret_access_key = self._Add(
        'gs_xml_secret_access_key',
        default=None,
        hidden=True,
        help_text='Legacy Cloud Storage HMAC credential secret access key.'
        'WARNING: This in conjunction with storage/gs_xml_access_key_id '
        'forces gcloud storage to use the XML API to call Cloud Storage, '
        'which means not all commands will work as expected.')

    self.json_api_version = self._Add(
        'json_api_version',
        default='v1',
        hidden=True,
        help_text=(
            'The version "v1" is hardcoded in the generated client for upload'
            ' operations, e.g.'
            ' /resumable/upload/storage/v1/b/{bucket}/o. Setting this property'
            ' will replace "v1" in the above path with the specified value.'
        ),
    )

    self.key_store_path = self._Add(
        'key_store_path',
        help_text=textwrap.dedent("""\
        Path to a yaml file containing an encryption key, and multiple
        decryption keys for use in storage commands. The file must be formatted
        as follows:
        +
            encryption_key: {A customer-supplied or customer-managed key.}
            decryption_keys:
            - {A customer-supplied key}
            ...
        +
        Customer-supplied encryption keys must be RFC 4648 section 4
        base64-encoded AES256 strings. Customer-managed encryption keys must be
        of the form
        `projects/{project}/locations/{location}/keyRings/{key-ring}/cryptoKeys/{crypto-key}`.
        """))

    self.max_retry_delay = self._Add(
        'max_retry_delay',
        default=32,
        help_text='Max second delay between retriable operations.')

    self.multipart_chunksize = self._Add(
        'multipart_chunksize',
        default=self.DEFAULT_MULTIPART_CHUNKSIZE,
        validator=_HumanReadableByteAmountValidator,
        help_text='Specifies partition size in bytes of each part of a '
        'multipart upload made by the Boto3 client. To calculate the maximum '
        'size of a Boto3 client multipart upload, multiply the multipart_chunk '
        'value by the maximum number of parts the API allows. For AWS S3 this '
        'limit is 10000. Values can be provided either in bytes or as '
        'human-readable values (e.g., "150M" to represent 150 mebibytes).')

    self.multipart_threshold = self._Add(
        'multipart_threshold',
        default=self.DEFAULT_MULTIPART_THRESHOLD,
        validator=_HumanReadableByteAmountValidator,
        help_text='Files larger than this threshold will be partitioned into '
        'parts, uploaded separately by the Boto3 client, and then combined '
        'into a single object. Otherwise, files smaller than this threshold '
        'will be uploaded by the Boto3 client in a single stream.')

    self.multiprocessing_default_method = self._Add(
        'multiprocessing_default_method',
        default=None,
        hidden=True,
        help_text='Specifies the default method to use for multiprocessing in'
        ' storage commands. If not set, the default method is determined by the'
        ' operating system and python version.',
        choices=['fork', 'spawn', 'forkserver'],
    )

    self.process_count = self._Add(
        'process_count',
        help_text='The maximum number of processes parallel execution should '
        'use. When process_count and thread_count are both 1, commands use '
        'sequential execution.')

    self.resumable_threshold = self._Add(
        'resumable_threshold',
        default=self.DEFAULT_RESUMABLE_THRESHOLD,
        validator=_HumanReadableByteAmountValidator,
        help_text='File operations above this size in bytes will use resumable'
        ' instead of one-shot strategies. For example, a resumable download.')

    self.sliced_object_download_component_size = self._Add(
        'sliced_object_download_component_size',
        validator=_HumanReadableByteAmountValidator,
        help_text='Target size and upper bound for files to be sliced into.'
        ' Analogous to parallel_composite_upload_component_size.')

    self.sliced_object_download_max_components = self._Add(
        'sliced_object_download_max_components',
        help_text='Specifies the maximum number of slices to be used when'
        ' performing a sliced object download. Set None for automatic'
        ' optimization based on system resources.')

    self.sliced_object_download_threshold = self._Add(
        'sliced_object_download_threshold',
        validator=_HumanReadableByteAmountValidator,
        help_text='Slice files larger than this value. Zero will block sliced'
        ' downloads. Analogous to parallel_composite_upload_threshold.')

    self.thread_count = self._Add(
        'thread_count',
        help_text='The number of threads parallel execution should use per '
        'process. When process_count and thread_count are both 1, commands use '
        'sequential execution.')

    self.parallel_composite_upload_component_prefix = self._Add(
        'parallel_composite_upload_component_prefix',
        default=(
            '/gcloud/tmp/parallel_composite_uploads/'
            'see_gcloud_storage_cp_help_for_details/'
        ),
        help_text=(
            'The prefix used when naming temporary components created by'
            ' composite uploads. If the prefix begins with a `/`, the temporary'
            ' components are uploaded relative to the bucket name. If the'
            ' prefix does not begin with a `/`, the temporary components are'
            ' uploaded relative to the prefix portion of the destination object'
            ' name. For example, consider an upload that will create a final'
            ' object named `gs://bucket/dir1/dir2/object`. Using a prefix of'
            ' `/prefix` means temporary components use names like'
            ' `gs://bucket/prefix/COMPONENT_NAME`. Using a prefix of `prefix`'
            ' means temporary components use names like'
            ' `gs://bucket/dir1/dir2/prefix/COMPONENT_NAME`. Note that this can'
            ' complicate cleaning up temporary components, as they will not all'
            ' share a common prefix. If this property is not specified, gcloud'
            ' storage uses the prefix'
            ' `/gcloud/tmp/parallel_composite_uploads/see_gcloud_storage_cp_help_for_details/`.'
            ' If a chosen prefix results in temporary component names longer'
            ' than the maximum length Cloud Storage allows, gcloud storage'
            ' performs a non-composite upload.'
        ),
    )

    self.parallel_composite_upload_component_size = self._Add(
        'parallel_composite_upload_component_size',
        default='50M',
        validator=_HumanReadableByteAmountValidator,
        help_text='Specifies the ideal size of a component in bytes, which '
        'will act as an upper bound to the size of the components if '
        'ceil(file_size / parallel_composite_upload_component_size) is less '
        'than the maximum number of objects the API allows composing at once. '
        'Values can be provided either in bytes or as human-readable values '
        '(e.g., "150M" to represent 150 mebibytes).')

    self.parallel_composite_upload_compatibility_check = self._AddBool(
        'parallel_composite_upload_compatibility_check',
        default=True,
        help_text='Determines if the GET bucket call should be performed to '
        'check if the default storage class and retention period for the '
        'destination bucket meet the criteria for parallel composite upload.')

    self.delete_source_objects_in_compose = self._AddBool(
        'delete_source_objects_in_compose',
        default=True,
        hidden=True,
        help_text=(
            'If True, parallel composite uploads will use the delete source '
            'objects feature of the compose API to remove temporary '
            'components, avoiding soft-delete costs. Defaults to True.'
        ),
    )

    self.parallel_composite_upload_enabled = self._Add(
        'parallel_composite_upload_enabled',
        default=None,
        help_text='Determines whether parallel composite upload should be '
        'used. Default value is None which will use parallel composite upload '
        'and log an appropriate warning for the user explaining that parallel '
        'composite upload is being used by default.',
        choices=[True, False, None])

    self.parallel_composite_upload_threshold = self._Add(
        'parallel_composite_upload_threshold',
        default='150M',
        validator=_HumanReadableByteAmountValidator,
        help_text='Specifies the maximum size of a file to upload in a single '
        'stream. Files larger than this threshold will be partitioned into '
        'component parts, uploaded in parallel, then composed into a single '
        'object. The number of components will be the smaller of '
        'ceil(file_size / parallel_composite_upload_component_size) and '
        'the maximum number of objects the API allows composing at once. For '
        'Cloud Storage this limit is 32. This property has no effect if '
        'parallel_composite_upload_enabled is set to False.')

    self.bidi_streaming_flush_size = self._Add(
        'bidi_streaming_flush_size',
        default=self.DEFAULT_BIDI_STREAMING_FLUSH_SIZE,
        hidden=True,
        validator=_HumanReadableByteAmountValidator,
        help_text=(
            'The size of the in-memory buffer that is flushed during bidi'
            ' streaming uploads. Ensure this is within your machine\'s RAM'
            ' limits.'
        ),
    )

    self.bidi_streaming_finalize_writes = self._AddBool(
        'bidi_streaming_finalize_writes',
        default=False,
        hidden=True,
        help_text=(
            'If True, the upload will be finalized when the last chunk is'
            ' written. If False, objects will be left in the default'
            ' unfinalized state.'
        ),
    )

    self.rsync_files_directory = self._Add(
        'rsync_files_directory',
        default=os.path.join(
            config.Paths().global_config_dir,
            'surface_data',
            'storage',
            'rsync_files',
        ),
        help_text='Directory path to intermediary files created by rsync.',
    )

    self.rsync_list_chunk_size = self._Add(
        'rsync_list_chunk_size',
        default=self.DEFAULT_RSYNC_LIST_CHUNK_SIZE,
        help_text=(
            'Number of files processed at a time by the rsync command when'
            ' it builds and compares the list of files at the source'
            ' and destination.'
        ),
    )

    self.use_url_based_rsync_sorting = self._AddBool(
        'use_url_based_rsync_sorting',
        default=False,
        hidden=True,
        help_text=(
            'Enables url based sorting for tracker files used by gcloud storage'
            ' rsync .'
        ),
    )

    self.use_gsutil_rsync_delete_unmatched_destination_objects_behavior = self._AddBool(
        'use_gsutil_rsync_delete_unmatched_destination_objects_behavior',
        default=True,
        help_text=(
            'If True, rsync will preserve unmatched destination objects while'
            ' using --delete-unmatched-destination-objects flag along with'
            ' other flags such as --no-clobber, --skip-unsupported and'
            ' --skip-if-dest-has-newer-mtime just as it would with gsutil.'
        ),
    )

    self.use_rsync_unmatched_gstmp_handling = self._AddBool(
        'use_rsync_unmatched_gstmp_handling',
        default=False,
        hidden=True,
        help_text=(
            'If True, rsync with --delete-unmatched-destination-objects will'
            ' delete unmatched destination gstmp files after the rsync is'
            ' successfully complete. In case of a failed rsync, the unmatched'
            ' destination gstmp files will be deleted only if the customer has'
            ' specified --continue-on-error.'
        ),
    )

    self.s3_endpoint_url = self._Add(
        's3_endpoint_url',
        default=None,
        help_text='If set, boto3 client will connect to this endpoint.'
        ' Otherwise, boto3 selects a default endpoint based on the AWS service'
        ' used.')

    self.suggest_transfer = self._AddBool(
        'suggest_transfer',
        default=True,
        help_text='If True, logs messages about when Storage Transfer Service'
        ' might be a better tool than gcloud storage.')

    self.symlink_placeholder_directory = self._Add(
        'symlink_placeholder_directory',
        default=os.path.join(
            config.Paths().global_config_dir,
            'surface_data',
            'storage',
            'symlink_placeholders',
        ),
        help_text='Directory path to temporary symlink placeholder files.',
        hidden=True,
    )

    self.tracker_files_directory = self._Add(
        'tracker_files_directory',
        default=os.path.join(
            config.Paths().global_config_dir,
            'surface_data',
            'storage',
            'tracker_files',
        ),
        help_text='Directory path to tracker files for resumable operations.',
    )

    self.use_gcloud_crc32c = self._AddBool(
        'use_gcloud_crc32c',
        default=None,
        help_text=(
            'If True, data integrity checks use a binary subprocess to '
            ' calculate CRC32C hashes with the included gcloud-crc32c tool'
            ' rather than the google-crc32c Python library. This behavior is '
            ' also triggered when the google-crc32c Python library is'
            ' unavailable even if this property is False.'))

    # TODO(b/109938541): Remove this after implementation seems stable.
    self.use_gsutil = self._AddBool(
        'use_gsutil',
        default=False,
        help_text='If True, use the deprecated upload implementation which '
        'uses gsutil.')

    self.use_magicfile = self._AddBool(
        'use_magicfile',
        default=False,
        help_text=(
            'If True, uses the `file --mime <filename>` command to guess'
            ' content types instead of the default filename extension-based'
            ' mechanism. Available on UNIX and macOS (and possibly on Windows, '
            ' if you\'re running Cygwin or some other package that provides '
            ' implementations of UNIX-like commands). When available and '
            ' enabled use_magicfile should be more robust because it analyzes '
            ' file contents in addition to extensions.'))

    self.use_threading_local = self._AddBool(
        'use_threading_local',
        default=True,
        help_text='If True, reuses some resource if they are already declared on'
        ' a thread. If False, creates duplicates of resources like API clients'
        ' on the same thread. Turning off can help with some bugs but will'
        ' hurt performance.')

    self.preferred_api = self._Add(
        'preferred_api',
        default=StoragePreferredApi.JSON.value,
        hidden=True,
        help_text='Specifies the API to be used for performing'
        ' `gcloud storage` operations. If `grpc_with_json_fallback` is set,'
        ' the gRPC API will be used if the operations is supported by'
        ' `gcloud storage`, else it will fallback to using the JSON API.',
        choices=([api.value for api in StoragePreferredApi]))

    self.use_grpc_if_available = self._AddBool(
        'use_grpc_if_available',
        default=False,
        hidden=True,
        help_text=(
            'If True, uses gRPC when possible. If False, uses existing'
            ' implementation.'
        ),
    )


class _SectionSurvey(_Section):
  """Contains the properties for the 'survey' section."""

  def __init__(self):
    super(_SectionSurvey, self).__init__('survey')
    self.disable_prompts = self._AddBool(
        'disable_prompts',
        default=False,
        help_text='If True, gcloud will not prompt you to take periodic usage '
        'experience surveys.')


class _SectionTest(_Section):
  """Contains the properties for the 'test' section."""

  def __init__(self):
    super(_SectionTest, self).__init__('test')
    self.results_base_url = self._Add('results_base_url', hidden=True)
    self.matrix_status_interval = self._Add(
        'matrix_status_interval', hidden=True)


class _SectionTranscoder(_Section):
  """Contains the properties for the 'transcoder' section."""

  def __init__(self):
    super(_SectionTranscoder, self).__init__('transcoder', hidden=True)
    self.location = self._Add(
        'location',
        help_text=(
            'Transcoder location to use. This parameter corresponds to the '
            '/locations/<location> segment of the Transcoder resource URIs '
            'being referenced.'))


class _SectionTransfer(_Section):
  """Contains the properties for the 'transfer' section."""

  def __init__(self):
    super(_SectionTransfer, self).__init__('transfer', hidden=True)
    self.no_async_polling_interval_ms = self._Add(
        'no_async_polling_interval_ms',
        default=3000,
        hidden=True,
        help_text='Frequency for polling a transfer operation to see if'
        ' it is done.')


class _SectionTransport(_Section):
  """Contains the properties for the 'transport' section."""

  def __init__(self):
    super(_SectionTransport, self).__init__('transport', hidden=True)


class _SectionVectorSearch(_Section):
  """Contains the properties for the command group 'vector-search' section."""

  def __init__(self):
    super(_SectionVectorSearch, self).__init__('vector_search')
    self.location = self._Add(
        'location',
        help_text='Default location to use when working with '
        'Vector Search resources. When a `--location` flag is required '
        'but not provided, the command will fall back to this value, if set.')


class _SectionVmware(_Section):
  """Contains the properties for the 'vmware' section."""

  def __init__(self):
    super(_SectionVmware, self).__init__('vmware')

    self.region = self._Add(
        'region',
        default='us-central1',
        help_text='Default region to use when working with VMware '
        'Engine resources.  When a `--region` '
        'flag is required but not provided, the command will fall back to '
        'this value, if set.')

    self.node_type = self._Add(
        'node-type',
        default='c1-highmem-72-metal',
        hidden=True,
        help_text='Node type to use when creating a new cluster.')


class _SectionWeb3(_Section):
  """Contains the properties for the 'web3' section."""

  def __init__(self):
    super(_SectionWeb3, self).__init__('web3', hidden=True)
    self.location = self._Add(
        'location',
        default='us-central1',
        help_text='The default region to use when working with Cloud '
        'Web3 resources. When a `--location` flag is required '
        'but not provided, the command will fall back to this value, if set.')


class _SectionWorkflows(_Section):
  """Contains the properties for the 'workflows' section."""

  def __init__(self):
    super(_SectionWorkflows, self).__init__('workflows', hidden=True)
    self.location = self._Add(
        'location',
        default='us-central1',
        help_text='The default region to use when working with Cloud '
        'Workflows resources. When a `--location` flag is required '
        'but not provided, the command will fall back to this value, if set.')


class _SectionWorkstations(_Section):
  """Contains the properties for the 'workstations' section."""

  def __init__(self):
    super(_SectionWorkstations, self).__init__('workstations')
    self.region = self._Add(
        'region',
        help_text=(
            'Default region to use when working with Workstations resources.'
            ' When a `--region` flag is required but not provided, the command'
            ' will fall back to this value, if set.'
        ),
    )
    self.cluster = self._Add(
        'cluster',
        help_text=(
            'Default cluster to use when working with Workstations resources.'
            ' When a `--cluster` flag is required but not provided, the command'
            ' will fall back to this value, if set.'
        ),
    )
    self.config = self._Add(
        'config',
        help_text=(
            'Default configuration to use when working with Workstations '
            'resources. When a `--config` flag is required but not '
            'provided, the command will fall back to this value, if set.'
        ),
    )


class _Property(object):
  """An individual property that can be gotten from the properties file.

  Attributes:
    section: str, The name of the section the property appears in, within the
      file.
    name: str, The name of the property.
    help_text: str, The man page help for what this property does.
    is_hidden: bool, True to hide this property from display for users that
      don't know about them.
    is_internal: bool, True to hide this property from display even if it is
      set. Internal properties are implementation details not meant to be set by
      users.
    callbacks: [func], A list of functions to be called, in order, if no value
      is found elsewhere.  The result of a callback will be shown in when
      listing properties (if the property is not hidden).
    completer: [func], a completer function
    default: str, A final value to use if no value is found after the callbacks.
      The default value is never shown when listing properties regardless of
      whether the property is hidden or not.
    default_flag: default_flag name to include in RequiredPropertyError if
      property fails on Get. This can be used for flags that are tightly coupled
      with a property.
    validator: func(str), A function that is called on the value when .Set()'d
      or .Get()'d. For valid values, the function should do nothing. For invalid
      values, it should raise InvalidValueError with an explanation of why it
      was invalid.
    choices: [str], The allowable values for this property.  This is included in
      the help text and used in tab completion.
  """

  def __init__(self,
               section,
               name,
               help_text=None,
               hidden=False,
               internal=False,
               callbacks=None,
               default=None,
               validator=None,
               choices=None,
               completer=None,
               default_flag=None):
    self.__section = section
    self.__name = name
    self.__help_text = help_text
    self.__hidden = hidden
    self.__internal = internal
    self.__callbacks = callbacks or []
    self.__default = default
    self.__validator = validator
    self.__choices = choices
    self.__completer = completer
    self.__default_flag = default_flag

  @property
  def section(self):
    return self.__section

  @property
  def name(self):
    return self.__name

  @property
  def help_text(self):
    return self.__help_text

  @property
  def is_hidden(self):
    return self.__hidden

  @property
  def is_internal(self):
    return self.__internal

  @property
  def default(self):
    return self.__default

  @property
  def callbacks(self):
    return self.__callbacks

  @property
  def choices(self):
    return self.__choices

  @property
  def completer(self):
    return self.__completer

  @property
  def default_flag(self):
    return self.__default_flag

  def __hash__(self):
    return hash(self.section) + hash(self.name)

  def __eq__(self, other):
    return self.section == other.section and self.name == other.name

  def __ne__(self, other):
    return not self == other

  def __gt__(self, other):
    return self.name > other.name

  def __ge__(self, other):
    return self.name >= other.name

  def __lt__(self, other):
    return self.name < other.name

  def __le__(self, other):
    return self.name <= other.name

  def GetOrFail(self):
    """Shortcut for Get(required=True).

    Convenient as a callback function.

    Returns:
      str, The value for this property.
    Raises:
      RequiredPropertyError if property is not set.
    """

    return self.Get(required=True)

  def Get(self, required=False, validate=True):
    """Gets the value for this property.

    Looks first in the environment, then in the workspace config, then in the
    global config, and finally at callbacks.

    Args:
      required: bool, True to raise an exception if the property is not set.
      validate: bool, Whether or not to run the fetched value through the
        validation function.

    Returns:
      str, The value for this property.
    """
    property_value = self.GetPropertyValue(required, validate)
    if property_value is None:
      return None
    return Stringize(property_value.value)

  def GetPropertyValue(self, required=False, validate=True):
    """Gets the value for this property.

    Looks first in the environment, then in the workspace config, then in the
    global config, and finally at callbacks.

    Args:
      required: bool, True to raise an exception if the property is not set.
      validate: bool, Whether or not to run the fetched value through the
        validation function.

    Returns:
      PropertyValue, The value for this property.
    """
    property_value = _GetProperty(self,
                                  named_configs.ActivePropertiesFile.Load(),
                                  required)
    if validate:
      self.Validate(property_value)
    return property_value

  def IsExplicitlySet(self):
    """Determines if this property has been explicitly set by the user.

    Properties with defaults or callbacks don't count as explicitly set.

    Returns:
      True, if the value was explicitly set, False otherwise.
    """
    property_value = _GetPropertyWithoutCallback(
        self, named_configs.ActivePropertiesFile.Load())
    if property_value is None:
      return False
    return property_value.value is not None

  def Validate(self, property_value):
    """Test to see if the value is valid for this property.

    Args:
      property_value: str | PropertyValue, The value of the property to be
        validated.

    Raises:
      InvalidValueError: If the value was invalid according to the property's
          validator.
    """
    if self.__validator:
      if isinstance(property_value, PropertyValue):
        value = property_value.value
      else:
        value = property_value
      try:
        self.__validator(value)
      except InvalidValueError as e:
        prop = '{}/{}'.format(self.section, self.name)
        error = 'Invalid value for property [{}]: {}'.format(prop, e)
        raise InvalidValueError(error)

  def GetBool(self, required=False, validate=True):
    """Gets the boolean value for this property.

    Looks first in the environment, then in the workspace config, then in the
    global config, and finally at callbacks.

    Does not validate by default because boolean properties were not previously
    validated, and startup functions rely on boolean properties that may have
    invalid values from previous installations

    Args:
      required: bool, True to raise an exception if the property is not set.
      validate: bool, Whether or not to run the fetched value through the
        validation function.

    Returns:
      bool, The boolean value for this property, or None if it is not set.

    Raises:
      InvalidValueError: if value is not boolean
    """
    value = _GetBoolProperty(
        self,
        named_configs.ActivePropertiesFile.Load(),
        required,
        validate=validate)
    return value

  def GetInt(self, required=False, validate=True):
    """Gets the integer value for this property.

    Looks first in the environment, then in the workspace config, then in the
    global config, and finally at callbacks.

    Args:
      required: bool, True to raise an exception if the property is not set.
      validate: bool, Whether or not to run the fetched value through the
        validation function.

    Returns:
      int, The integer value for this property.
    """
    value = _GetIntProperty(self, named_configs.ActivePropertiesFile.Load(),
                            required)
    if validate:
      self.Validate(value)
    return value

  def Set(self, property_value):
    """Sets the value for this property as an environment variable.

    Args:
      property_value: PropertyValue | str | bool, The proposed value for this
        property.  If None, it is removed from the environment.
    """
    self.Validate(property_value)
    if isinstance(property_value, PropertyValue):
      value = property_value.value
    else:
      value = property_value
    if value is not None:
      value = Stringize(value)
    encoding.SetEncodedValue(os.environ, self.EnvironmentName(), value)

  def AddCallback(self, callback):
    """Adds another callback for this property."""
    self.__callbacks.append(callback)

  def RemoveCallback(self, callback):
    """Removes given callback for this property."""
    self.__callbacks.remove(callback)

  def ClearCallback(self):
    """Removes all callbacks for this property."""
    self.__callbacks[:] = []

  def EnvironmentName(self):
    """Get the name of the environment variable for this property.

    Returns:
      str, The name of the correct environment variable.
    """
    return 'CLOUDSDK_{section}_{name}'.format(
        section=self.__section.upper(),
        name=self.__name.upper(),
    )

  def __str__(self):
    return '{section}/{name}'.format(section=self.__section, name=self.__name)


class _Sections(object):
  """Represents the available sections in the properties file."""

  _MAPPING = {
      'access_context_manager': '_SectionAccessContextManager',
      'accessibility': '_SectionAccessibility',
      'ai': '_SectionAi',
      'ai_platform': '_SectionAiPlatform',
      'api_client_overrides': '_SectionApiClientOverrides',
      'api_endpoint_overrides': '_SectionApiEndpointOverrides',
      'app': '_SectionApp',
      'artifacts': '_SectionArtifacts',
      'auth': '_SectionAuth',
      'batch': '_SectionBatch',
      'billing': '_SectionBilling',
      'builds': '_SectionBuilds',
      'code': '_SectionCode',
      'colab': '_SectionColab',
      'component_manager': '_SectionComponentManager',
      'composer': '_SectionComposer',
      'compute': '_SectionCompute',
      'config_delivery': '_SectionConfigDelivery',
      'container': '_SectionContainer',
      'container_attached': '_SectionContainerAttached',
      'container_aws': '_SectionContainerAws',
      'container_azure': '_SectionContainerAzure',
      'container_vmware': '_SectionContainerVmware',
      'container_bare_metal': '_SectionContainerBareMetal',
      'context_aware': '_SectionContextAware',
      'core': '_SectionCore',
      'ssh': '_SectionSsh',
      'scc': '_SectionScc',
      'deploy': '_SectionDeploy',
      'dataproc': '_SectionDataproc',
      'dataflow': '_SectionDataflow',
      'datafusion': '_SectionDatafusion',
      'datapipelines': '_SectionDataPipelines',
      'dataplex': '_SectionDataplex',
      'declarative': '_SectionDeclarative',
      'deployment_manager': '_SectionDeploymentManager',
      'devshell': '_SectionDevshell',
      'diagnostics': '_SectionDiagnostics',
      'edge_container': '_SectionEdgeContainer',
      'emulator': '_SectionEmulator',
      'eventarc': '_SectionEventarc',
      'experimental': '_SectionExperimental',
      'filestore': '_SectionFilestore',
      'functions': '_SectionFunctions',
      'gcloudignore': '_SectionGcloudignore',
      'gkehub': '_SectionGkeHub',
      'grpc': '_SectionGrpc',
      'gkebackup': '_SectionGkebackup',
      'healthcare': '_SectionHealthcare',
      # Bit of a hack here - we add a second mapping as this is the only
      # property section named with hyphens (all the others are consistent about
      # using underscores).
      'inframanager': '_SectionInfraManager',
      'infra-manager': '_SectionInfraManager',
      'interactive': '_SectionInteractive',
      'kuberun': '_SectionKubeRun',
      'lustre': '_SectionLustre',
      'looker': '_SectionLooker',
      'media_asset': '_SectionMediaAsset',
      'memcache': '_SectionMemcache',
      'metastore': '_SectionMetastore',
      'metrics': '_SectionMetrics',
      'ml_engine': '_SectionMlEngine',
      'mps': '_SectionMps',
      'netapp': '_SectionNetapp',
      'notebooks': '_SectionNotebooks',
      'privateca': '_SectionPrivateCa',
      'proxy': '_SectionProxy',
      'pubsub': '_SectionPubsub',
      'recaptcha': '_SectionRecaptcha',
      'redis': '_SectionRedis',
      'regional': '_SectionRegional',
      'resource_policy': '_SectionResourcePolicy',
      'run': '_SectionRun',
      'secrets': '_SectionSecrets',
      'servicehealth': '_SectionServiceHealth',
      'spanner': '_SectionSpanner',
      'storage': '_SectionStorage',
      'survey': '_SectionSurvey',
      'test': '_SectionTest',
      'transfer': '_SectionTransfer',
      'transport': '_SectionTransport',
      'transcoder': '_SectionTranscoder',
      'vector_search': '_SectionVectorSearch',
      'vmware': '_SectionVmware',
      'web3': '_SectionWeb3',
      'workflows': '_SectionWorkflows',
      'workstations': '_SectionWorkstations',
  }

  class _ValueFlag(object):

    def __init__(self, value, flag):
      self.value = value
      self.flag = flag

  def __init__(self):
    self.__sections = {}
    self.__invocation_value_stack = [{}]

  def __getattr__(self, name):
    if name in self._MAPPING:
      cls_name = self._MAPPING[name]
      cls = globals()[cls_name]
      section = cls()
      self.__sections[section.name] = section
      setattr(self, name, section)
      return section
    raise AttributeError(
        "'_Sections' object has no attribute '{0}'".format(name))

  @property
  def default_section(self):
    return self.core

  def __iter__(self):
    for name in self._MAPPING:
      yield getattr(self, name)

  def PushInvocationValues(self):
    self.__invocation_value_stack.append({})

  def PopInvocationValues(self):
    self.__invocation_value_stack.pop()

  def SetInvocationValue(self, prop, value, flag):
    value_flags = self.GetLatestInvocationValues()
    if value:
      prop.Validate(value)
    value_flags[prop] = _Sections._ValueFlag(value, flag)

  def GetLatestInvocationValues(self):
    return self.__invocation_value_stack[-1]

  def GetInvocationStack(self):
    return self.__invocation_value_stack

  def Section(self, section_name):
    if section_name not in self.__sections:
      if section_name in self._MAPPING:
        return getattr(self, section_name)
      raise NoSuchPropertyError(
          'Section "{section}" does not exist.'.format(section=section_name))
    return self.__sections[section_name]

  def AllSections(self, include_hidden=False):
    for name in self._MAPPING:
      getattr(self, name)
    return [
        name for name, value in six.iteritems(self.__sections)
        if not value.is_hidden or include_hidden
    ]

  def AllValues(self,
                list_unset=False,
                include_hidden=False,
                properties_file=None,
                only_file_contents=False):
    """Gets the entire collection of property values for all sections.

    Args:
      list_unset: bool, If True, include unset properties in the result.
      include_hidden: bool, True to include hidden properties in the result. If
        a property has a value set but is hidden, it will be included regardless
        of this setting.
      properties_file: PropertyFile, the file to read settings from.  If None
        the active property file will be used.
      only_file_contents: bool, True if values should be taken only from the
        properties file, false if flags, env vars, etc. should be consulted too.
        Mostly useful for listing file contents.

    Returns:
      {str:{str:str}}, A dict of sections to dicts of properties to values.
    """
    result = {}
    for section in self:
      section_result = section.AllValues(
          list_unset=list_unset,
          include_hidden=include_hidden,
          properties_file=properties_file,
          only_file_contents=only_file_contents)
      if section_result:
        result[section.name] = section_result
    return result

  def AllPropertyValues(self,
                        list_unset=False,
                        include_hidden=False,
                        properties_file=None,
                        only_file_contents=False):
    """Gets the entire collection of property values for all sections.

    Args:
      list_unset: bool, If True, include unset properties in the result.
      include_hidden: bool, True to include hidden properties in the result. If
        a property has a value set but is hidden, it will be included regardless
        of this setting.
      properties_file: PropertyFile, the file to read settings from.  If None
        the active property file will be used.
      only_file_contents: bool, True if values should be taken only from the
        properties file, false if flags, env vars, etc. should be consulted too.
        Mostly useful for listing file contents.

    Returns:
      {str:{str:PropertyValue}}, A dict of sections to dicts of properties to
        property values.
    """
    result = {}
    for section in self:
      section_result = section.AllPropertyValues(
          list_unset=list_unset,
          include_hidden=include_hidden,
          properties_file=properties_file,
          only_file_contents=only_file_contents)
      if section_result:
        result[section.name] = section_result
    return result

  def GetHelpString(self):
    """Gets a string with the help contents for all properties and descriptions.

    Returns:
      str, The string for the man page section.
    """
    messages = []
    # Force load all sections to ensure they are listed in the help string.
    for name in self._MAPPING:
      getattr(self, name)

    sections = [self.default_section]
    default_section_name = self.default_section.name
    sections.extend(
        sorted([
            s for name, s in six.iteritems(self.__sections)
            if name != default_section_name and not s.is_hidden
        ]))
    for section in sections:
      props = sorted([p for p in section if not p.is_hidden])
      if not props:
        continue
      messages.append('_{section}_::'.format(section=section.name))
      for prop in props:
        messages.append('*{prop}*:::\n\n{text}'.format(
            prop=prop.name, text=prop.help_text))
    return '\n\n\n'.join(messages)


VALUES = _Sections()


def FromString(property_string):
  """Gets the property object corresponding the given string.

  Args:
    property_string: str, The string to parse.  It can be in the format
      section/property, or just property if the section is the default one.

  Returns:
    properties.Property, The property or None if it failed to parse to a valid
      property.
  """
  section, prop = ParsePropertyString(property_string)
  if not prop:
    return None
  return VALUES.Section(section).Property(prop)


def ParsePropertyString(property_string):
  """Parses a string into a section and property name.

  Args:
    property_string: str, The property string in the format section/property.

  Returns:
    (str, str), The section and property.  Both will be none if the input
    string is empty.  Property can be None if the string ends with a slash.
  """
  if not property_string:
    return None, None

  if '/' in property_string:
    section, prop = tuple(property_string.split('/', 1))
  else:
    section = None
    prop = property_string

  section = section or VALUES.default_section.name
  prop = prop or None
  return section, prop


class _ScopeInfo(object):

  # pylint: disable=redefined-builtin
  def __init__(self, id, description):
    self.id = id
    self.description = description


class Scope(object):
  """An enum class for the different types of property files that can be used."""

  INSTALLATION = _ScopeInfo(
      id='installation',
      description='The installation based configuration file applies to all '
      'users on the system that use this version of the Cloud SDK.  If the SDK '
      'was installed by an administrator, you will need administrator rights '
      'to make changes to this file.')
  USER = _ScopeInfo(
      id='user',
      description='The user based configuration file applies only to the '
      'current user of the system.  It will override any values from the '
      'installation configuration.')

  _ALL = [USER, INSTALLATION]
  _ALL_SCOPE_NAMES = [s.id for s in _ALL]

  @staticmethod
  def AllValues():
    """Gets all possible enum values.

    Returns:
      [Scope], All the enum values.
    """
    return list(Scope._ALL)

  @staticmethod
  def AllScopeNames():
    return list(Scope._ALL_SCOPE_NAMES)

  @staticmethod
  def FromId(scope_id):
    """Gets the enum corresponding to the given scope id.

    Args:
      scope_id: str, The scope id to parse.

    Raises:
      InvalidScopeValueError: If the given value cannot be parsed.

    Returns:
      OperatingSystemTuple, One of the OperatingSystem constants or None if the
      input is None.
    """
    if not scope_id:
      return None
    for scope in Scope._ALL:
      if scope.id == scope_id:
        return scope
    raise InvalidScopeValueError(scope_id)

  @staticmethod
  def GetHelpString():
    return '\n\n'.join(
        ['*{0}*::: {1}'.format(s.id, s.description) for s in Scope.AllValues()])


def PersistProperty(prop, value, scope=None):
  """Sets the given property in the properties file.

  This function should not generally be used as part of normal program
  execution.  The property files are user editable config files that they should
  control.  This is mostly for initial setup of properties that get set during
  SDK installation.

  Args:
    prop: properties.Property, The property to set.
    value: str, The value to set for the property. If None, the property is
      removed.
    scope: Scope, The config location to set the property in.  If given, only
      this location will be updated and it is an error if that location does not
      exist.  If not given, it will attempt to update the property in the first
      of the following places that exists: - the active named config - user
      level config It will never fall back to installation properties; you must
      use that scope explicitly to set that value.

  Raises:
    MissingInstallationConfig: If you are trying to set the installation config,
      but there is not SDK root.
  """
  prop.Validate(value)
  if six.PY3:
    value = _EscapePercentSign(value)
  if scope == Scope.INSTALLATION:
    config.EnsureSDKWriteAccess()
    config_file = config.Paths().installation_properties_path
    if not config_file:
      raise MissingInstallationConfig()
    prop_files_lib.PersistProperty(config_file, prop.section, prop.name, value)
    named_configs.ActivePropertiesFile.Invalidate(mark_changed=True)
  else:
    active_config = named_configs.ConfigurationStore.ActiveConfig()
    active_config.PersistProperty(prop.section, prop.name, value)
  # Print message if value being set/unset is overridden by environment var
  # to prevent user confusion
  env_name = prop.EnvironmentName()
  override = encoding.GetEncodedValue(os.environ, env_name)
  if override:
    warning_message = ('WARNING: Property [{0}] is overridden '
                       'by environment setting [{1}={2}]\n')
    # Writing to sys.stderr because of circular dependency
    # in googlecloudsdk.core.log on properties
    sys.stderr.write(warning_message.format(prop.name, env_name, override))


def _GetProperty(prop, properties_file, required):
  """Gets the given property.

  If the property has a designated command line argument and args is provided,
  check args for the value first. If the corresponding environment variable is
  set, use that second. If still nothing, use the callbacks.

  Args:
    prop: properties.Property, The property to get.
    properties_file: properties_file.PropertiesFile, An already loaded
      properties files to use.
    required: bool, True to raise an exception if the property is not set.

  Raises:
    RequiredPropertyError: If the property was required but unset.

  Returns:
    PropertyValue, The value of the property, or None if it is not set.
  """

  flag_to_use = None

  invocation_stack = VALUES.GetInvocationStack()
  if len(invocation_stack) > 1:
    # First item is the blank stack entry, second is from the user command args.
    first_invocation = invocation_stack[1]
    if prop in first_invocation:
      flag_to_use = first_invocation.get(prop).flag

  property_value = _GetPropertyWithoutDefault(prop, properties_file)
  if property_value is not None and property_value.value is not None:
    return property_value

  # Still nothing, check the final default.
  if prop.default is not None:
    return PropertyValue(
        Stringize(prop.default), PropertyValue.PropertySource.DEFAULT)

  # Not set, throw if required.
  if required:
    raise RequiredPropertyError(prop, flag=flag_to_use)

  return None


def _GetPropertyWithoutDefault(prop, properties_file):
  """Gets the given property without using a default.

  If the property has a designated command line argument and args is provided,
  check args for the value first. If the corresponding environment variable is
  set, use that second. Next, return whatever is in the property file.  Finally,
  use the callbacks to find values.  Do not check the default value.

  Args:
    prop: properties.Property, The property to get.
    properties_file: properties_file.PropertiesFile, An already loaded
      properties files to use.

  Returns:
    PropertyValue, The value of the property, or None if it is not set.
  """
  # Try to get a value from args, env, or property file.
  property_value = _GetPropertyWithoutCallback(prop, properties_file)
  if property_value and property_value.value is not None:
    return property_value

  # No value, try getting a value from the callbacks.
  for callback in prop.callbacks:
    value = callback()
    if value is not None:
      return PropertyValue(
          Stringize(value), PropertyValue.PropertySource.CALLBACK)

  return None


def _GetPropertyWithoutCallback(prop, properties_file):
  """Gets the given property without using a callback or default.

  If the property has a designated command line argument and args is provided,
  check args for the value first. If the corresponding environment variable is
  set, use that second. Finally, return whatever is in the property file.  Do
  not check for values in callbacks or defaults.

  Args:
    prop: properties.Property, The property to get.
    properties_file: PropertiesFile, An already loaded properties files to use.

  Returns:
    PropertyValue, The value of the property, or None if it is not set.
  """
  # Look for a value in the flags that were used on this command.
  invocation_stack = VALUES.GetInvocationStack()
  for value_flags in reversed(invocation_stack):
    if prop not in value_flags:
      continue
    value_flag = value_flags.get(prop, None)
    if not value_flag:
      continue
    if value_flag.value is not None:
      return PropertyValue(
          Stringize(value_flag.value), PropertyValue.PropertySource.FLAG)

  # Check the environment variable overrides.
  value = encoding.GetEncodedValue(os.environ, prop.EnvironmentName())
  if value is not None:
    return PropertyValue(
        Stringize(value), PropertyValue.PropertySource.ENVIRONMENT)

  # Check the property file itself.
  value = properties_file.Get(prop.section, prop.name)
  if value is not None:
    return PropertyValue(
        Stringize(value), PropertyValue.PropertySource.PROPERTY_FILE)

  return None


def _GetBoolProperty(prop, properties_file, required, validate=False):
  """Gets the given property in bool form.

  Args:
    prop: properties.Property, The property to get.
    properties_file: properties_file.PropertiesFile, An already loaded
      properties files to use.
    required: bool, True to raise an exception if the property is not set.
    validate: bool, True to validate the value

  Returns:
    bool, The value of the property, or None if it is not set.
  """
  property_value = _GetProperty(prop, properties_file, required)
  if validate:
    _BooleanValidator(prop.name, property_value)
  if property_value is None or property_value.value is None:
    return None
  property_string_value = Stringize(property_value.value).lower()
  if property_string_value == 'none':
    return None
  return property_string_value in ['1', 'true', 'on', 'yes', 'y']


def _GetIntProperty(prop, properties_file, required):
  """Gets the given property in integer form.

  Args:
    prop: properties.Property, The property to get.
    properties_file: properties_file.PropertiesFile, An already loaded
      properties files to use.
    required: bool, True to raise an exception if the property is not set.

  Returns:
    int, The integer value of the property, or None if it is not set.
  """
  property_value = _GetProperty(prop, properties_file, required)
  if property_value is None or property_value.value is None:
    return None
  try:
    return int(property_value.value)
  except ValueError:
    raise InvalidValueError(
        'The property [{prop}] must have an integer value: [{value}]'.format(
            prop=prop, value=property_value.value))


def IsDefaultUniverse():
  universe_domain_property = VALUES.core.universe_domain
  return universe_domain_property.Get() == universe_domain_property.default


def GetUniverseDomain():
  """Get the universe domain."""

  return VALUES.core.universe_domain.Get()


def GetMetricsEnvironment():
  """Get the metrics environment.

  Returns the property metrics/environment if set, if not, it tries to deduce if
  we're on some known platforms like devshell or GCE.

  Returns:
    None, if no environment is set or found
    str, a string denoting the environment if one is set or found
  """

  environment = VALUES.metrics.environment.Get()
  if environment:
    return environment

  # No explicit environment defined, try to deduce it.
  # pylint: disable=g-import-not-at-top
  from googlecloudsdk.core.credentials import devshell as c_devshell
  if c_devshell.IsDevshellEnvironment():
    return 'devshell'

  from googlecloudsdk.core.credentials import gce_cache
  if gce_cache.GetOnGCE(check_age=False):
    return 'GCE'

  return None


def _EscapePercentSign(value):
  """Escape '%' in property value.

  Do nothing if value contains '%%', i.e. value was escaped by user.

  Args:
    value: property value

  Returns:
    str, value with escaped % sign
  """
  if not isinstance(value, six.string_types) or '%%' in value:
    return value
  elif '%' in value:
    return value.replace('%', '%%')
  else:
    return value
