1# -*- coding: utf-8 -*- #
2# Copyright 2014 Google LLC. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#    http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Read and write properties for the CloudSDK."""
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import unicode_literals
20
21import enum
22import functools
23import multiprocessing
24import os
25import re
26import sys
27
28from googlecloudsdk.core import argv_utils
29from googlecloudsdk.core import config
30from googlecloudsdk.core import exceptions
31from googlecloudsdk.core.configurations import named_configs
32from googlecloudsdk.core.configurations import properties_file as prop_files_lib
33from googlecloudsdk.core.docker import constants as const_lib
34from googlecloudsdk.core.util import encoding
35from googlecloudsdk.core.util import http_proxy_types
36from googlecloudsdk.core.util import scaled_integer
37from googlecloudsdk.core.util import times
38
39import six
40
41# Try to parse the command line flags at import time to see if someone provided
42# the --configuration flag.  If they did, this could affect the value of the
43# properties defined in that configuration.  Since some libraries (like logging)
44# use properties at startup, we want to use the correct configuration for that.
45named_configs.FLAG_OVERRIDE_STACK.PushFromArgs(argv_utils.GetDecodedArgv())
46
47_SET_PROJECT_HELP = """\
48To set your project, run:
49
50  $ gcloud config set project PROJECT_ID
51
52or to unset it, run:
53
54  $ gcloud config unset project"""
55
56_VALID_PROJECT_REGEX = re.compile(
57    r'^'
58    # An optional domain-like component, ending with a colon, e.g.,
59    # google.com:
60    r'(?:(?:[-a-z0-9]{1,63}\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?'
61    # Followed by a required identifier-like component, for example:
62    #   waffle-house    match
63    #   -foozle        no match
64    #   Foozle         no match
65    # We specifically disallow project number, even though some GCP backends
66    # could accept them.
67    # We also allow a leading digit as some legacy project ids can have
68    # a leading digit.
69    r'(?:(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))'
70    r'$')
71
72_VALID_ENDPOINT_OVERRIDE_REGEX = re.compile(
73    r'^'
74    # require http or https for scheme
75    r'(?:https?)://'
76    # netlocation portion of address. can be any of
77    # - domain name
78    # - 'localhost'
79    # - ipv4 addr
80    # - ipv6 addr
81    r'(?:'  # begin netlocation
82    # - domain name, e.g. 'test-foo.sandbox.googleapis.com', or 'localhost'
83    r'(?:[A-Z0-9](?:[A-Z0-9-.])+)|'
84    # - ipv4
85    r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'
86    # - ipv6
87    r'\[?[A-F0-9]*:[A-F0-9:]+\]?'
88    r')'  # end netlocation
89    # optional port
90    r'(?::\d+)?'
91    # require trailing slash, fragment optional
92    r'(?:/|[/?]\S+/)'
93    r'$',
94    re.IGNORECASE)
95
96_PUBSUB_NOTICE_URL = (
97    'https://cloud.google.com/functions/docs/writing/background#event_parameter'
98)
99
100
101def Stringize(value):
102  if isinstance(value, six.string_types):
103    return value
104  return str(value)
105
106
107def ExistingAbsoluteFilepathValidator(file_path):
108  """Checks to see if the file path exists and is an absolute path."""
109  if file_path is None:
110    return
111  if not os.path.isfile(file_path):
112    raise InvalidValueError('The provided path must exist.')
113  if not os.path.isabs(file_path):
114    raise InvalidValueError('The provided path must be absolute.')
115
116
117def _LooksLikeAProjectName(project):
118  """Heuristics testing if a string looks like a project name, but an id."""
119
120  if re.match(r'[-0-9A-Z]', project[0]):
121    return True
122
123  return any(c in project for c in ' !"\'')
124
125
126def _BooleanValidator(property_name, value):
127  """Validates boolean properties.
128
129  Args:
130    property_name: str, the name of the property
131    value: str | bool, the value to validate
132
133  Raises:
134    InvalidValueError: if value is not boolean
135  """
136  accepted_strings = [
137      'true', '1', 'on', 'yes', 'y', 'false', '0', 'off', 'no', 'n', '', 'none'
138  ]
139  if Stringize(value).lower() not in accepted_strings:
140    raise InvalidValueError(
141        'The [{0}] value [{1}] is not valid. Possible values: [{2}]. '
142        '(See http://yaml.org/type/bool.html)'.format(
143            property_name, value,
144            ', '.join([x if x else "''" for x in accepted_strings])))
145
146
147def _BuildTimeoutValidator(timeout):
148  """Validates build timeouts."""
149  if timeout is None:
150    return
151  seconds = times.ParseDuration(timeout, default_suffix='s').total_seconds
152  if seconds <= 0:
153    raise InvalidValueError('Timeout must be a positive time duration.')
154
155
156def _HumanReadableByteAmountValidator(size_string):
157  """Validates human readable byte amounts, e.g. 1KiB."""
158  if size_string is None:
159    return
160  try:
161    scaled_integer.ParseInteger(size_string)
162  except ValueError as e:
163    raise InvalidValueError(str(e))
164
165
166class Error(exceptions.Error):
167  """Exceptions for the properties module."""
168
169
170class PropertiesParseError(Error):
171  """An exception to be raised when a properties file is invalid."""
172
173
174class NoSuchPropertyError(Error):
175  """An exception to be raised when the desired property does not exist."""
176
177
178class MissingInstallationConfig(Error):
179  """An exception to be raised when the sdk root does not exist."""
180
181  def __init__(self):
182    super(MissingInstallationConfig, self).__init__(
183        'Installation properties could not be set because the installation '
184        'root of the Cloud SDK could not be found.')
185
186
187class InvalidScopeValueError(Error):
188  """Exception for when a string could not be parsed to a valid scope value."""
189
190  def __init__(self, given):
191    """Constructs a new exception.
192
193    Args:
194      given: str, The given string that could not be parsed.
195    """
196    super(InvalidScopeValueError, self).__init__(
197        'Could not parse [{0}] into a valid configuration scope.  '
198        'Valid values are [{1}]'.format(given,
199                                        ', '.join(Scope.AllScopeNames())))
200
201
202class InvalidValueError(Error):
203  """An exception to be raised when the set value of a property is invalid."""
204
205
206class InvalidProjectError(InvalidValueError):
207  """An exception for bad project names, with a little user help."""
208
209  def __init__(self, given):
210    super(InvalidProjectError, self).__init__(given + '\n' + _SET_PROJECT_HELP)
211
212
213class RequiredPropertyError(Error):
214  """Generic exception for when a required property was not set."""
215  FLAG_STRING = ('It can be set on a per-command basis by re-running your '
216                 'command with the [{flag}] flag.\n\n')
217
218  def __init__(self, prop, flag=None, extra_msg=None):
219    if prop.section != VALUES.default_section.name:
220      section = prop.section + '/'
221    else:
222      section = ''
223
224    flag = flag or prop.default_flag
225    if flag:
226      flag_msg = RequiredPropertyError.FLAG_STRING.format(flag=flag)
227    else:
228      flag_msg = ''
229
230    msg = ("""\
231The required property [{property_name}] is not currently set.
232{flag_msg}You may set it for your current workspace by running:
233
234  $ gcloud config set {section}{property_name} VALUE
235
236or it can be set temporarily by the environment variable [{env_var}]""".format(
237    property_name=prop.name,
238    flag_msg=flag_msg,
239    section=section,
240    env_var=prop.EnvironmentName()))
241    if extra_msg:
242      msg += '\n\n' + extra_msg
243    super(RequiredPropertyError, self).__init__(msg)
244    self.property = prop
245
246
247class _Sections(object):
248  """Represents the available sections in the properties file.
249
250  Attributes:
251    access_context_manager: Section, The section containing access context
252      manager properties for the Cloud SDK.
253    accessibility: Section, The section containing accessibility properties for
254      the Cloud SDK.
255    ai: Section, The section containing ai properties for the Cloud SDK.
256    ai_platform: Section, The section containing ai platform properties for the
257      Cloud SDK.
258    api_client_overrides: Section, The section containing API client override
259      properties for the Cloud SDK.
260    api_endpoint_overrides: Section, The section containing API endpoint
261      override properties for the Cloud SDK.
262    app: Section, The section containing app properties for the Cloud SDK.
263    auth: Section, The section containing auth properties for the Cloud SDK.
264    billing: Section, The section containing billing properties for the Cloud
265      SDK.
266    builds: Section, The section containing builds properties for the Cloud SDK.
267    artifacts: Section, The section containing artifacts properties for the
268      Cloud SDK.
269    code: Section, The section containing local development properties for Cloud
270      SDK.
271    component_manager: Section, The section containing properties for the
272      component_manager.
273    composer: Section, The section containing composer properties for the Cloud
274      SDK.
275    compute: Section, The section containing compute properties for the Cloud
276      SDK.
277    container: Section, The section containing container properties for the
278      Cloud SDK.
279    context_aware: Section, The section containing context aware access
280      configurations for the Cloud SDK.
281    core: Section, The section containing core properties for the Cloud SDK.
282    ssh: Section, The section containing ssh-related properties.
283    scc: Section, The section containing scc properties for the Cloud SDK.
284    deploy: Secion, The secion containing cloud deploy related properties for
285      the Cloud SDK.
286    dataproc: Section, The section containing dataproc properties for the Cloud
287      SDK.
288    dataflow: Section, The section containing dataflow properties for the Cloud
289      SDK.
290    datafusion: Section, The section containing datafusion properties for the
291      Cloud SDK.
292    declarative: Section, The section containing properties for declarative
293      workflows in the Cloud SDK.
294    default_section: Section, The main section of the properties file (core).
295    deployment_manager: Section, The section containing deployment_manager
296      properties for the Cloud SDK.
297    devshell: Section, The section containing devshell properties for the Cloud
298      SDK.
299    diagnostics: Section, The section containing diagnostics properties for the
300      Cloud SDK.
301    emulator: Section, The section containing emulator properties for the Cloud
302      SDK.
303    eventarc: Section, The section containing eventarc properties for the Cloud
304      SDK.
305    experimental: Section, The section containing experimental properties for
306      the Cloud SDK.
307    filestore: Section, The section containing filestore properties for the
308      Cloud SDK.
309    functions: Section, The section containing functions properties for the
310      Cloud SDK.
311    game_services: Section, The section containing gameservices properties for
312      the Cloud SDK.
313    gcloudignore: Section, The section containing gcloudignore properties for
314      the Cloud SDK.
315    healthcare: Section, The section containing healthcare properties for the
316      Cloud SDK.
317    interactive: Section, The section containing interactive properties for the
318      Cloud SDK.
319    kuberun: Section, The section containing kuberun properties for the Cloud
320      SDK.
321    lifesciences: Section, The section containing lifesciencs properties for the
322      Cloud SDK.
323    media_asset: Section, the section containing mediaasset protperties for the
324      Cloud SDK.
325    memcache: Section, The section containing memcache properties for the Cloud
326      SDK.
327    metastore: Section, The section containing metastore properties for the
328      Cloud SDK.
329    metrics: Section, The section containing metrics properties for the Cloud
330      SDK.
331    ml_engine: Section, The section containing ml_engine properties for the
332      Cloud SDK.
333    notebooks: Section, The section containing notebook properties for the Cloud
334      SDK.
335    privateca: Section, The section containing privateca properties for the
336      Cloud SDK.
337    proxy: Section, The section containing proxy properties for the Cloud SDK.
338    pubsub: Section, The section containing pubsub properties for the Cloud SDK.
339    redis: Section, The section containing redis properties for the Cloud SDK.
340    run: Section, The section containing run properties for the Cloud SDK.
341    secrets: Section, The section containing secretmanager properties for the
342      Cloud SDK.
343    spanner: Section, The section containing spanner properties for the Cloud
344      SDK.
345    storage: Section, The section containing storage properties for the Cloud
346      SDK.
347    survey: Section, The section containing survey properties for the Cloud SDK.
348    test: Section, The section containing test properties for the Cloud SDK.
349    transport: Section, The section containing transport properties for the
350      Cloud SDK.
351    transcoder: Section, The section containing transcoder properties for the
352      Cloud SDK.
353    vmware: Section, The section containing vmware properties for the Cloud SDK.
354    workflows: Section, The section containing workflows properties for the
355      Cloud SDK.
356  """
357
358  class _ValueFlag(object):
359
360    def __init__(self, value, flag):
361      self.value = value
362      self.flag = flag
363
364  def __init__(self):
365    self.access_context_manager = _SectionAccessContextManager()
366    self.accessibility = _SectionAccessibility()
367    self.ai = _SectionAi()
368    self.ai_platform = _SectionAiPlatform()
369    self.api_client_overrides = _SectionApiClientOverrides()
370    self.api_endpoint_overrides = _SectionApiEndpointOverrides()
371    self.app = _SectionApp()
372    self.artifacts = _SectionArtifacts()
373    self.auth = _SectionAuth()
374    self.billing = _SectionBilling()
375    self.builds = _SectionBuilds()
376    self.code = _SectionCode()
377    self.component_manager = _SectionComponentManager()
378    self.composer = _SectionComposer()
379    self.compute = _SectionCompute()
380    self.container = _SectionContainer()
381    self.context_aware = _SectionContextAware()
382    self.core = _SectionCore()
383    self.ssh = _SectionSsh()
384    self.scc = _SectionScc()
385    self.deploy = _SectionDeploy()
386    self.dataproc = _SectionDataproc()
387    self.dataflow = _SectionDataflow()
388    self.datafusion = _SectionDatafusion()
389    self.declarative = _SectionDeclarative()
390    self.deployment_manager = _SectionDeploymentManager()
391    self.devshell = _SectionDevshell()
392    self.diagnostics = _SectionDiagnostics()
393    self.emulator = _SectionEmulator()
394    self.eventarc = _SectionEventarc()
395    self.experimental = _SectionExperimental()
396    self.filestore = _SectionFilestore()
397    self.functions = _SectionFunctions()
398    self.game_services = _SectionGameServices()
399    self.gcloudignore = _SectionGcloudignore()
400    self.healthcare = _SectionHealthcare()
401    self.interactive = _SectionInteractive()
402    self.kuberun = _SectionKubeRun()
403    self.lifesciences = _SectionLifeSciences()
404    self.media_asset = _SectionMediaAsset()
405    self.memcache = _SectionMemcache()
406    self.metastore = _SectionMetastore()
407    self.metrics = _SectionMetrics()
408    self.ml_engine = _SectionMlEngine()
409    self.notebooks = _SectionNotebooks()
410    self.privateca = _SectionPrivateCa()
411    self.proxy = _SectionProxy()
412    self.pubsub = _SectionPubsub()
413    self.redis = _SectionRedis()
414    self.run = _SectionRun()
415    self.secrets = _SectionSecrets()
416    self.spanner = _SectionSpanner()
417    self.storage = _SectionStorage()
418    self.survey = _SectionSurvey()
419    self.test = _SectionTest()
420    self.transport = _SectionTransport()
421    self.transcoder = _SectionTranscoder()
422    self.vmware = _SectionVmware()
423    self.workflows = _SectionWorkflows()
424
425    sections = [
426        self.access_context_manager,
427        self.accessibility,
428        self.ai,
429        self.ai_platform,
430        self.api_client_overrides,
431        self.api_endpoint_overrides,
432        self.app,
433        self.auth,
434        self.billing,
435        self.builds,
436        self.artifacts,
437        self.code,
438        self.component_manager,
439        self.composer,
440        self.compute,
441        self.container,
442        self.context_aware,
443        self.core,
444        self.ssh,
445        self.scc,
446        self.dataproc,
447        self.dataflow,
448        self.datafusion,
449        self.deployment_manager,
450        self.devshell,
451        self.diagnostics,
452        self.emulator,
453        self.eventarc,
454        self.experimental,
455        self.filestore,
456        self.functions,
457        self.game_services,
458        self.gcloudignore,
459        self.healthcare,
460        self.interactive,
461        self.kuberun,
462        self.lifesciences,
463        self.media_asset,
464        self.memcache,
465        self.metastore,
466        self.metrics,
467        self.ml_engine,
468        self.notebooks,
469        self.pubsub,
470        self.privateca,
471        self.proxy,
472        self.redis,
473        self.run,
474        self.secrets,
475        self.spanner,
476        self.storage,
477        self.survey,
478        self.test,
479        self.transport,
480        self.transcoder,
481        self.vmware,
482        self.workflows,
483    ]
484    self.__sections = {section.name: section for section in sections}
485    self.__invocation_value_stack = [{}]
486
487  @property
488  def default_section(self):
489    return self.core
490
491  def __iter__(self):
492    return iter(self.__sections.values())
493
494  def PushInvocationValues(self):
495    self.__invocation_value_stack.append({})
496
497  def PopInvocationValues(self):
498    self.__invocation_value_stack.pop()
499
500  def SetInvocationValue(self, prop, value, flag):
501    """Set the value of this property for this command, using a flag.
502
503    Args:
504      prop: _Property, The property with an explicit value.
505      value: str, The value that should be returned while this command is
506        running.
507      flag: str, The flag that a user can use to set the property, reported if
508        it was required at some point but not set by the command line.
509    """
510    value_flags = self.GetLatestInvocationValues()
511    if value:
512      prop.Validate(value)
513    value_flags[prop] = _Sections._ValueFlag(value, flag)
514
515  def GetLatestInvocationValues(self):
516    return self.__invocation_value_stack[-1]
517
518  def GetInvocationStack(self):
519    return self.__invocation_value_stack
520
521  def Section(self, section):
522    """Gets a section given its name.
523
524    Args:
525      section: str, The section for the desired property.
526
527    Returns:
528      Section, The section corresponding to the given name.
529
530    Raises:
531      NoSuchPropertyError: If the section is not known.
532    """
533    try:
534      return self.__sections[section]
535    except KeyError:
536      raise NoSuchPropertyError(
537          'Section "{section}" does not exist.'.format(section=section))
538
539  def AllSections(self, include_hidden=False):
540    """Gets a list of all registered section names.
541
542    Args:
543      include_hidden: bool, True to include hidden properties in the result.
544
545    Returns:
546      [str], The section names.
547    """
548    return [
549        name for name, value in six.iteritems(self.__sections)
550        if not value.is_hidden or include_hidden
551    ]
552
553  def AllValues(self,
554                list_unset=False,
555                include_hidden=False,
556                properties_file=None,
557                only_file_contents=False):
558    """Gets the entire collection of property values for all sections.
559
560    Args:
561      list_unset: bool, If True, include unset properties in the result.
562      include_hidden: bool, True to include hidden properties in the result. If
563        a property has a value set but is hidden, it will be included regardless
564        of this setting.
565      properties_file: PropertyFile, the file to read settings from.  If None
566        the active property file will be used.
567      only_file_contents: bool, True if values should be taken only from the
568        properties file, false if flags, env vars, etc. should be consulted too.
569        Mostly useful for listing file contents.
570
571    Returns:
572      {str:{str:str}}, A dict of sections to dicts of properties to values.
573    """
574    result = {}
575    for section in self:
576      section_result = section.AllValues(
577          list_unset=list_unset,
578          include_hidden=include_hidden,
579          properties_file=properties_file,
580          only_file_contents=only_file_contents)
581      if section_result:
582        result[section.name] = section_result
583    return result
584
585  def GetHelpString(self):
586    """Gets a string with the help contents for all properties and descriptions.
587
588    Returns:
589      str, The string for the man page section.
590    """
591    messages = []
592    sections = [self.default_section]
593    default_section_name = self.default_section.name
594    sections.extend(
595        sorted([
596            s for name, s in six.iteritems(self.__sections)
597            if name != default_section_name and not s.is_hidden
598        ]))
599    for section in sections:
600      props = sorted([p for p in section if not p.is_hidden])
601      if not props:
602        continue
603      messages.append('_{section}_::'.format(section=section.name))
604      for prop in props:
605        messages.append('*{prop}*:::\n\n{text}'.format(
606            prop=prop.name, text=prop.help_text))
607    return '\n\n\n'.join(messages)
608
609
610class _Section(object):
611  """Represents a section of the properties file that has related properties.
612
613  Attributes:
614    name: str, The name of the section.
615    is_hidden: bool, True if the section is hidden, False otherwise.
616  """
617
618  def __init__(self, name, hidden=False):
619    self.__name = name
620    self.__is_hidden = hidden
621    self.__properties = {}
622
623  @property
624  def name(self):
625    return self.__name
626
627  @property
628  def is_hidden(self):
629    return self.__is_hidden
630
631  def __iter__(self):
632    return iter(self.__properties.values())
633
634  def __hash__(self):
635    return hash(self.name)
636
637  def __eq__(self, other):
638    return self.name == other.name
639
640  def __ne__(self, other):
641    return self.name != other.name
642
643  def __gt__(self, other):
644    return self.name > other.name
645
646  def __ge__(self, other):
647    return self.name >= other.name
648
649  def __lt__(self, other):
650    return self.name < other.name
651
652  def __le__(self, other):
653    return self.name <= other.name
654
655  #  pylint: disable=missing-docstring
656  def _Add(self,
657           name,
658           help_text=None,
659           internal=False,
660           hidden=False,
661           callbacks=None,
662           default=None,
663           validator=None,
664           choices=None,
665           completer=None,
666           default_flag=None):
667    prop = _Property(
668        section=self.__name,
669        name=name,
670        help_text=help_text,
671        internal=internal,
672        hidden=(self.is_hidden or hidden),
673        callbacks=callbacks,
674        default=default,
675        validator=validator,
676        choices=choices,
677        completer=completer,
678        default_flag=default_flag)
679    self.__properties[name] = prop
680    return prop
681
682  def _AddBool(self,
683               name,
684               help_text=None,
685               internal=False,
686               hidden=False,
687               callbacks=None,
688               default=None):
689    return self._Add(
690        name=name,
691        help_text=help_text,
692        internal=internal,
693        hidden=hidden,
694        callbacks=callbacks,
695        default=default,
696        validator=functools.partial(_BooleanValidator, name),
697        choices=('true', 'false'))
698
699  def Property(self, property_name):
700    """Gets a property from this section, given its name.
701
702    Args:
703      property_name: str, The name of the desired property.
704
705    Returns:
706      Property, The property corresponding to the given name.
707
708    Raises:
709      NoSuchPropertyError: If the property is not known for this section.
710    """
711    try:
712      return self.__properties[property_name]
713    except KeyError:
714      raise NoSuchPropertyError('Section [{s}] has no property [{p}].'.format(
715          s=self.__name, p=property_name))
716
717  def HasProperty(self, property_name):
718    """True iff section has given property.
719
720    Args:
721      property_name: str, The name of the property to check for membership.
722
723    Returns:
724      a boolean. True iff this section contains property_name.
725    """
726    return property_name in self.__properties
727
728  def AllProperties(self, include_hidden=False):
729    """Gets a list of all registered property names in this section.
730
731    Args:
732      include_hidden: bool, True to include hidden properties in the result.
733
734    Returns:
735      [str], The property names.
736    """
737    return [
738        name for name, prop in six.iteritems(self.__properties)
739        if include_hidden or not prop.is_hidden
740    ]
741
742  def AllValues(self,
743                list_unset=False,
744                include_hidden=False,
745                properties_file=None,
746                only_file_contents=False):
747    """Gets all the properties and their values for this section.
748
749    Args:
750      list_unset: bool, If True, include unset properties in the result.
751      include_hidden: bool, True to include hidden properties in the result. If
752        a property has a value set but is hidden, it will be included regardless
753        of this setting.
754      properties_file: properties_file.PropertiesFile, the file to read settings
755        from.  If None the active property file will be used.
756      only_file_contents: bool, True if values should be taken only from the
757        properties file, false if flags, env vars, etc. should be consulted too.
758        Mostly useful for listing file contents.
759
760    Returns:
761      {str:str}, The dict of {property:value} for this section.
762    """
763    properties_file = (
764        properties_file or named_configs.ActivePropertiesFile.Load())
765
766    result = {}
767    for prop in self:
768      if prop.is_internal:
769        # Never show internal properties, ever.
770        continue
771      if (prop.is_hidden and not include_hidden and
772          _GetPropertyWithoutCallback(prop, properties_file) is None):
773        continue
774
775      if only_file_contents:
776        value = properties_file.Get(prop.section, prop.name)
777      else:
778        value = _GetPropertyWithoutDefault(prop, properties_file)
779
780      if value is None:
781        if not list_unset:
782          # Never include if not set and not including unset values.
783          continue
784        if prop.is_hidden and not include_hidden:
785          # If including unset values, exclude if hidden and not including
786          # hidden properties.
787          continue
788
789      # Always include if value is set (even if hidden)
790      result[prop.name] = value
791    return result
792
793
794class _SectionKubeRun(_Section):
795  """Contains the properties for the 'kuberun' section."""
796
797  def __init__(self):
798    super(_SectionKubeRun, self).__init__('kuberun')
799    self.enable_experimental_commands = self._AddBool(
800        'enable_experimental_commands',
801        help_text='If True, experimental KubeRun commands will not prompt to '
802        'continue.',
803        hidden=True)
804
805    self.environment = self._Add(
806        'environment',
807        help_text='If set, this environment will be used as the deployment'
808        'target in all KubeRun commands.',
809        hidden=True)
810
811    self.cluster = self._Add(
812        'cluster',
813        help_text='ID of the cluster or fully qualified identifier '
814        'for the cluster',
815        hidden=True)
816
817    self.cluster_location = self._Add(
818        'cluster_location',
819        help_text='Zone or region in which the cluster is located.',
820        hidden=True)
821
822    self.use_kubeconfig = self._AddBool(
823        'use_kubeconfig',
824        help_text='Use the default or provided kubectl config file.',
825        hidden=True)
826
827    self.kubeconfig = self._Add(
828        'kubeconfig',
829        help_text='Absolute path to your kubectl config file.',
830        hidden=True)
831
832    self.context = self._Add(
833        'context',
834        help_text='Name of the context in your kubectl config file to use.',
835        hidden=True)
836
837
838class _SectionRun(_Section):
839  """Contains the properties for the 'run' section."""
840
841  def __init__(self):
842    super(_SectionRun, self).__init__('run')
843    self.region = self._Add(
844        'region',
845        help_text='Default region to use when working with Cloud '
846        'Run resources. When a `--region` flag is required '
847        'but not provided, the command will fall back to this value, if set.')
848
849    self.namespace = self._Add(
850        'namespace',
851        help_text='Specific to working with Cloud on GKE or '
852        'a Kubernetes cluster: Kubernetes namespace for the resource.',
853        hidden=True)
854
855    self.cluster = self._Add(
856        'cluster',
857        help_text='ID of the cluster or fully qualified identifier '
858        'for the cluster')
859
860    self.cluster_location = self._Add(
861        'cluster_location',
862        help_text='Zone or region in which the cluster is located.')
863
864    self.platform = self._Add(
865        'platform',
866        choices=['gke', 'managed', 'kubernetes'],
867        help_text='Target platform for running commands.')
868
869
870class _SectionSecrets(_Section):
871  """Contains the properties for the 'secrets' section."""
872
873  def __init__(self):
874    super(_SectionSecrets, self).__init__('secrets')
875    self.replication_policy = self._Add(
876        'replication-policy',
877        choices=['automatic', 'user-managed'],
878        help_text='The type of replication policy to apply to secrets. Allowed '
879        'values are "automatic" and "user-managed". If user-managed then '
880        'locations must also be provided.',
881    )
882    self.locations = self._Add(
883        'locations',
884        help_text='A comma separated list of the locations to replicate '
885        'secrets to. Only applies to secrets with a user-managed policy.')
886
887
888class _SectionSpanner(_Section):
889  """Contains the properties for the 'spanner' section."""
890
891  def __init__(self):
892    super(_SectionSpanner, self).__init__('spanner')
893    self.instance = self._Add(
894        'instance',
895        help_text='Default instance to use when working with Cloud Spanner '
896        'resources. When an instance is required but not provided by a flag, '
897        'the command will fall back to this value, if set.',
898        completer='googlecloudsdk.command_lib.spanner.flags:InstanceCompleter')
899
900
901class _SectionCompute(_Section):
902  """Contains the properties for the 'compute' section."""
903
904  def __init__(self):
905    super(_SectionCompute, self).__init__('compute')
906    self.zone = self._Add(
907        'zone',
908        help_text='Default zone to use when working with zonal Compute '
909        'Engine resources. When a `--zone` flag is required but not provided, '
910        'the command will fall back to this value, if set. To see valid '
911        'choices, run `gcloud compute zones list`.',
912        completer=('googlecloudsdk.command_lib.compute.completers:'
913                   'ZonesCompleter'))
914    self.region = self._Add(
915        'region',
916        help_text='Default region to use when working with regional Compute'
917        ' Engine resources. When a `--region` flag is required but not '
918        'provided, the command will fall back to this value, if set. To see '
919        'valid choices, run `gcloud compute regions list`.',
920        completer=('googlecloudsdk.command_lib.compute.completers:'
921                   'RegionsCompleter'))
922    self.gce_metadata_read_timeout_sec = self._Add(
923        'gce_metadata_read_timeout_sec',
924        default=20,
925        help_text='Timeout of requesting data from gce metadata endpoints.',
926        hidden=True)
927    self.gce_metadata_check_timeout_sec = self._Add(
928        'gce_metadata_check_timeout_sec',
929        default=3,
930        help_text='Timeout of checking if it is on gce environment.',
931        hidden=True)
932    self.use_new_list_usable_subnets_api = self._AddBool(
933        'use_new_list_usable_subnets_api',
934        default=False,
935        help_text=(
936            'If True, use the new API for listing usable subnets which only '
937            'returns subnets in the current project.'))
938
939
940class _SectionFunctions(_Section):
941  """Contains the properties for the 'functions' section."""
942
943  def __init__(self):
944    super(_SectionFunctions, self).__init__('functions')
945    self.region = self._Add(
946        'region',
947        default='us-central1',
948        help_text='Default region to use when working with Cloud '
949        'Functions resources. When a `--region` flag is required but not '
950        'provided, the command will fall back to this value, if set. To see '
951        'valid choices, run `gcloud beta functions regions list`.',
952        completer=('googlecloudsdk.command_lib.functions.flags:'
953                   'LocationsCompleter'))
954    self.v2 = self._AddBool(
955        'v2',
956        default=False,
957        help_text='Default product version to use when working with Cloud '
958        'Functions resources. When neither `--v2` nor `--no-v2` is provided, '
959        'the decision of whether to use V2 falls back to this value.')
960
961
962class _SectionGcloudignore(_Section):
963  """Contains the properties for the 'gcloudignore' section."""
964
965  def __init__(self):
966    super(_SectionGcloudignore, self).__init__('gcloudignore')
967    self.enabled = self._AddBool(
968        'enabled',
969        default=True,
970        help_text=(
971            'If True, do not upload `.gcloudignore` files (see `$ gcloud topic '
972            'gcloudignore`). If False, turn off the gcloudignore mechanism '
973            'entirely and upload all files.'))
974
975
976class _SectionHealthcare(_Section):
977  """Contains the properties for the 'healthcare' section."""
978
979  def __init__(self):
980    super(_SectionHealthcare, self).__init__('healthcare')
981    self.location = self._Add(
982        'location',
983        default='us-central1',
984        help_text='Default location to use when working with Cloud Healthcare  '
985        'resources. When a `--location` flag is required but not provided, the  '
986        'command will fall back to this value.')
987    self.dataset = self._Add(
988        'dataset',
989        help_text='Default dataset to use when working with Cloud Healthcare '
990        'resources. When a `--dataset` flag is required but not provided, the '
991        'command will fall back to this value, if set.')
992
993
994class _SectionLifeSciences(_Section):
995  """Contains the properties for the 'lifesciences' section."""
996
997  def __init__(self):
998    super(_SectionLifeSciences, self).__init__('lifesciences')
999    self.location = self._Add(
1000        'location',
1001        default='us-central1',
1002        help_text='Default location to use when working with Cloud Life Sciences  '
1003        'resources. When a `--location` flag is required but not provided, the  '
1004        'command will fall back to this value.')
1005
1006
1007class _SectionGameServices(_Section):
1008  """Contains the properties for the 'game_services' section."""
1009
1010  def __init__(self):
1011    super(_SectionGameServices, self).__init__('game_services')
1012    self.deployment = self._Add(
1013        'default_deployment',
1014        default='-',
1015        help_text=('Default deployment to use when working with Cloud Game '
1016                   'Services list configs. When a --deployment flag is '
1017                   'required in a list command but not provided, the command '
1018                   'will fall back to this value which envokes aggregated '
1019                   'list from the backend.'))
1020    self.location = self._Add(
1021        'location',
1022        default='global',
1023        help_text=(
1024            'Default location to use when working with Cloud Game Services '
1025            'resources. When a `--location` flag is required but not provided, '
1026            'the command will fall back to this value.'))
1027    self.realm = self._Add(
1028        'default_realm',
1029        default='-',
1030        help_text=(
1031            'Default realm to use when working with Cloud Game Services list '
1032            'clusters. When a --realm flag is required in a list command but '
1033            'not provided, the command will fall back to this value which '
1034            'envokes aggregated list from the backend.'))
1035
1036
1037class _SectionAccessibility(_Section):
1038  """Contains the properties for the 'accessibility' section."""
1039
1040  def __init__(self):
1041    super(_SectionAccessibility, self).__init__('accessibility')
1042    self.screen_reader = self._AddBool(
1043        'screen_reader',
1044        default=False,
1045        help_text='Make gcloud more screen reader friendly.')
1046
1047
1048class _SectionApp(_Section):
1049  """Contains the properties for the 'app' section."""
1050
1051  def __init__(self):
1052    super(_SectionApp, self).__init__('app')
1053    self.promote_by_default = self._AddBool(
1054        'promote_by_default',
1055        help_text='If True, when deploying a new version of a service, that '
1056        'version will be promoted to receive all traffic for the service. '
1057        'This property can be overridden via the `--promote-by-default` or '
1058        '`--no-promote-by-default` flags.',
1059        default=True)
1060    self.stop_previous_version = self._AddBool(
1061        'stop_previous_version',
1062        help_text='If True, when deploying a new version of a service, the '
1063        'previously deployed version is stopped. If False, older versions must '
1064        'be stopped manually.',
1065        default=True)
1066    self.trigger_build_server_side = self._AddBool(
1067        'trigger_build_server_side', hidden=True, default=None)
1068    self.cloud_build_timeout = self._Add(
1069        'cloud_build_timeout',
1070        validator=_BuildTimeoutValidator,
1071        help_text='Timeout, in seconds, to wait for Docker builds to '
1072        'complete during deployments. All Docker builds now use the '
1073        'Cloud Build API.')
1074    self.container_builder_image = self._Add(
1075        'container_builder_image',
1076        default='gcr.io/cloud-builders/docker',
1077        hidden=True)
1078    self.use_appengine_api = self._AddBool(
1079        'use_appengine_api', default=True, hidden=True)
1080    # This property is currently ignored except on OS X Sierra or beta
1081    # deployments.
1082    # There's a theoretical benefit to exceeding the number of cores available,
1083    # since the task is bound by network/API latency among other factors, and
1084    # mini-benchmarks validated this (I got speedup from 4 threads to 8 on a
1085    # 4-core machine).
1086    self.num_file_upload_threads = self._Add(
1087        'num_file_upload_threads', default=None, hidden=True)
1088
1089    def GetRuntimeRoot():
1090      sdk_root = config.Paths().sdk_root
1091      if sdk_root is None:
1092        return None
1093      else:
1094        return os.path.join(config.Paths().sdk_root, 'platform', 'ext-runtime')
1095
1096    self.runtime_root = self._Add(
1097        'runtime_root', callbacks=[GetRuntimeRoot], hidden=True)
1098
1099    # Whether or not to use the (currently under-development) Flex Runtime
1100    # Builders, as opposed to Externalized Runtimes.
1101    #   True  => ALWAYS
1102    #   False => NEVER
1103    #   Unset => default behavior, which varies between beta/GA commands
1104    self.use_runtime_builders = self._Add(
1105        'use_runtime_builders',
1106        default=None,
1107        help_text=('If set, opt in/out to a new code path for building '
1108                   'applications using pre-fabricated runtimes that can be '
1109                   'updated independently of client tooling. If not set, '
1110                   'the default path for each runtime is used.'))
1111    # The Cloud Storage path prefix for the Flex Runtime Builder configuration
1112    # files. The configuration files will live at
1113    # "<PREFIX>/<runtime>-<version>.yaml", with an additional
1114    # "<PREFIX>/runtime.version" indicating the latest version.
1115    self.runtime_builders_root = self._Add(
1116        'runtime_builders_root', default='gs://runtime-builders/', hidden=True)
1117
1118
1119class _SectionBuilds(_Section):
1120  """Contains the properties for the 'builds' section."""
1121
1122  def __init__(self):
1123    super(_SectionBuilds, self).__init__('builds')
1124
1125    self.timeout = self._Add(
1126        'timeout',
1127        validator=_BuildTimeoutValidator,
1128        help_text='Timeout, in seconds, to wait for builds to complete. If '
1129        'unset, defaults to 10 minutes.')
1130    self.check_tag = self._AddBool(
1131        'check_tag',
1132        default=True,
1133        hidden=True,
1134        help_text='If True, validate that the --tag value to builds '
1135        'submit is in the gcr.io, *.gcr.io, or *.pkg.dev namespace.')
1136    # TODO(b/118509363): Remove this after its default is True.
1137    self.use_kaniko = self._AddBool(
1138        'use_kaniko',
1139        default=False,
1140        help_text='If True, kaniko will be used to build images described by '
1141        'a Dockerfile, instead of `docker build`.')
1142    self.kaniko_cache_ttl = self._Add(
1143        'kaniko_cache_ttl',
1144        default=6,
1145        help_text='TTL, in hours, of cached layers when using Kaniko. If zero, '
1146        'layer caching is disabled.')
1147    self.kaniko_image = self._Add(
1148        'kaniko_image',
1149        default='gcr.io/kaniko-project/executor:latest',
1150        hidden=True,
1151        help_text='Kaniko builder image to use when use_kaniko=True. Defaults '
1152        'to gcr.io/kaniko-project/executor:latest')
1153
1154
1155class _SectionArtifacts(_Section):
1156  """Contains the properties for the 'artifacts' section."""
1157
1158  def __init__(self):
1159    super(_SectionArtifacts, self).__init__('artifacts')
1160
1161    self.repository = self._Add(
1162        'repository',
1163        help_text='Default repository to use when working with Artifact '
1164        'Registry resources. When a `repository` value is required but not '
1165        'provided, the command will fall back to this value, if set.')
1166
1167    self.location = self._Add(
1168        'location',
1169        help_text='Default location to use when working with Artifact Registry '
1170        'resources. When a `location` value is required but not provided, the '
1171        'command will fall back to this value, if set. If this value is unset, '
1172        'the default location is `global` when `location` value is optional.')
1173
1174
1175class _SectionContainer(_Section):
1176  """Contains the properties for the 'container' section."""
1177
1178  def __init__(self):
1179    super(_SectionContainer, self).__init__('container')
1180    self.cluster = self._Add(
1181        'cluster',
1182        help_text='Name of the cluster to use by default when '
1183        'working with Kubernetes Engine.')
1184    self.use_client_certificate = self._AddBool(
1185        'use_client_certificate',
1186        default=False,
1187        help_text='If True, use the cluster\'s client certificate to '
1188        'authenticate to the cluster API server.')
1189    self.use_app_default_credentials = self._AddBool(
1190        'use_application_default_credentials',
1191        default=False,
1192        help_text='If True, use application default credentials to authenticate'
1193        ' to the cluster API server.')
1194
1195    self.build_timeout = self._Add(
1196        'build_timeout',
1197        validator=_BuildTimeoutValidator,
1198        help_text='Timeout, in seconds, to wait for container builds to '
1199        'complete.')
1200    self.build_check_tag = self._AddBool(
1201        'build_check_tag',
1202        default=True,
1203        hidden=True,
1204        help_text='If True, validate that the --tag value to container builds '
1205        'submit is in the gcr.io or *.gcr.io namespace.')
1206
1207
1208class _SectionCore(_Section):
1209  """Contains the properties for the 'core' section."""
1210
1211  class InteractiveUXStyles(enum.Enum):
1212    NORMAL = 0
1213    OFF = 1
1214    TESTING = 2
1215
1216  def __init__(self):
1217    super(_SectionCore, self).__init__('core')
1218    self.account = self._Add(
1219        'account',
1220        help_text='Account `gcloud` should use for authentication. '
1221        'Run `gcloud auth list` to see your currently available accounts.')
1222    self.disable_collection_path_deprecation_warning = self._AddBool(
1223        'disable_collection_path_deprecation_warning',
1224        hidden=True,
1225        help_text='If False, any usage of collection paths will result in '
1226        'deprecation warning. Set it to False to disable it.')
1227    self.default_regional_backend_service = self._AddBool(
1228        'default_regional_backend_service',
1229        help_text='If True, backend services in `gcloud compute '
1230        'backend-services` will be regional by default. Setting the `--global` '
1231        'flag is required for global backend services.')
1232    self.disable_color = self._AddBool(
1233        'disable_color',
1234        help_text='If True, color will not be used when printing messages in '
1235        'the terminal.')
1236    self.disable_command_lazy_loading = self._AddBool(
1237        'disable_command_lazy_loading', hidden=True)
1238    self.disable_prompts = self._AddBool(
1239        'disable_prompts',
1240        help_text='If True, the default answer will be assumed for all user '
1241        'prompts. However, for any prompts that require user input, an error '
1242        'will be raised. This is equivalent to either using the global '
1243        '`--quiet` flag or setting the environment variable '
1244        '`CLOUDSDK_CORE_DISABLE_PROMPTS` to 1. Setting this property is '
1245        'useful when scripting with `gcloud`.')
1246    self.disable_usage_reporting = self._AddBool(
1247        'disable_usage_reporting',
1248        help_text='If True, anonymous statistics on SDK usage will not be '
1249        'collected. This value is set by default based on your choices during '
1250        'installation, but can be changed at any time.  For more information, '
1251        'see: https://cloud.google.com/sdk/usage-statistics')
1252    self.enable_gri = self._AddBool(
1253        'enable_gri',
1254        default=False,
1255        hidden=True,
1256        help_text='If True, the parser for gcloud Resource Identifiers will be'
1257        'enabled when interpreting resource arguments.')
1258    self.resource_completion_style = self._Add(
1259        'resource_completion_style',
1260        choices=('flags', 'gri'),
1261        default='flags',
1262        hidden=True,
1263        help_text='The resource completion style controls how resource strings '
1264        'are represented in command argument completions.  All styles, '
1265        'including uri, are handled on input.')
1266    self.lint = self._Add(
1267        'lint',
1268        # Current runtime lint patterns. Delete from this comment when the
1269        # pattern usage has been deleted.
1270        #
1271        #   AddCacheUpdaters: Throws an exception for each command that needs
1272        #     a parser.display_info.AddCacheUpdater() call.
1273        #
1274        # When running tests set default=PATTERN[,PATTERN...] here to weed out
1275        # all occurrences of the patterns. Patterns are checked using substring
1276        # matching on the lint property string value:
1277        #
1278        #   if 'AddCacheUpdaters' in properties.VALUES.core.lint.Get():
1279        #     # AddCacheUpdaters lint check enabled.
1280        default='none',
1281        hidden=True,
1282        help_text='Enable the runtime linter for specific patterns. '
1283        'Each occurrence of a runtime pattern raises an exception. '
1284        'The pattern names are source specific. Consult the source for '
1285        'details.')
1286    self.api_host = self._Add(
1287        'api_host', hidden=True, default='https://www.googleapis.com')
1288    self.verbosity = self._Add(
1289        'verbosity',
1290        help_text='Default logging verbosity for `gcloud` commands.  This is '
1291        'the equivalent of using the global `--verbosity` flag. Supported '
1292        'verbosity levels: `debug`, `info`, `warning`, `error`, `critical`, '
1293        'and `none`.')
1294    self.user_output_enabled = self._AddBool(
1295        'user_output_enabled',
1296        help_text='True, by default. If False, messages to the user and command'
1297        ' output on both standard output and standard error will be'
1298        ' suppressed.',
1299        default=True)
1300    self.interactive_ux_style = self._Add(
1301        'interactive_ux_style',
1302        help_text='How to display interactive UX elements like progress bars '
1303        'and trackers.',
1304        hidden=True,
1305        default=_SectionCore.InteractiveUXStyles.NORMAL,
1306        choices=[x.name for x in list(_SectionCore.InteractiveUXStyles)])
1307    self.log_http = self._AddBool(
1308        'log_http',
1309        help_text='If True, log HTTP requests and responses to the logs.  '
1310        'To see logs in the terminal, adjust `verbosity` settings. '
1311        'Otherwise, logs are available in their respective log files.',
1312        default=False)
1313    self.log_http_redact_token = self._AddBool(
1314        'log_http_redact_token',
1315        help_text='If true, this prevents log_http from printing access tokens.'
1316        ' This property does not have effect unless log_http is true.',
1317        default=True,
1318        hidden=True)
1319    self.log_http_streaming_body = self._AddBool(
1320        'log_http_streaming_body',
1321        help_text='If True, log the streaming body instead of logging'
1322        ' the "<streaming body>" text. This flag results in reading the entire'
1323        ' response body in memory.'
1324        ' This property does not have effect unless log_http is true.',
1325        default=False,
1326        hidden=True)
1327    self.http_timeout = self._Add('http_timeout', hidden=True)
1328    self.check_gce_metadata = self._AddBool(
1329        'check_gce_metadata', hidden=True, default=True)
1330    self.print_completion_tracebacks = self._AddBool(
1331        'print_completion_tracebacks',
1332        hidden=True,
1333        help_text='If True, print actual completion exceptions with traceback '
1334        'instead of the nice UX scrubbed exceptions.')
1335    self.print_unhandled_tracebacks = self._AddBool(
1336        'print_unhandled_tracebacks', hidden=True)
1337    self.print_handled_tracebacks = self._AddBool(
1338        'print_handled_tracebacks', hidden=True)
1339    self.trace_token = self._Add(
1340        'trace_token',
1341        help_text='Token used to route traces of service requests for '
1342        'investigation of issues. This token will be provided by Google '
1343        'support.')
1344    self.trace_email = self._Add('trace_email', hidden=True)
1345    self.trace_log = self._Add('trace_log', default=False, hidden=True)
1346    self.request_reason = self._Add('request_reason', hidden=True)
1347    self.pass_credentials_to_gsutil = self._AddBool(
1348        'pass_credentials_to_gsutil',
1349        default=True,
1350        help_text='If True, pass the configured Cloud SDK authentication '
1351        'to gsutil.')
1352    self.api_key = self._Add(
1353        'api_key',
1354        hidden=True,
1355        help_text='If provided, this API key is attached to all outgoing '
1356        'API calls.')
1357    self.should_prompt_to_enable_api = self._AddBool(
1358        'should_prompt_to_enable_api',
1359        default=True,
1360        hidden=True,
1361        help_text='If true, will prompt to enable an API if a command fails due'
1362        ' to the API not being enabled.')
1363    self.color_theme = self._Add(
1364        'color_theme',
1365        help_text='Color palette for output.',
1366        hidden=True,
1367        default='off',
1368        choices=['off', 'normal', 'testing'])
1369    self.use_legacy_flattened_format = self._AddBool(
1370        'use_legacy_flattened_format',
1371        hidden=True,
1372        default=False,
1373        help_text='If True, use legacy format for flattened() and text().'
1374        'Please note that this option will not be supported indefinitely.')
1375
1376    def ShowStructuredLogsValidator(show_structured_logs):
1377      if show_structured_logs is None:
1378        return
1379      if show_structured_logs not in ['always', 'log', 'terminal', 'never']:
1380        raise InvalidValueError(('show_structured_logs must be one of: '
1381                                 '[always, log, terminal, never]'))
1382
1383    self.show_structured_logs = self._Add(
1384        'show_structured_logs',
1385        choices=['always', 'log', 'terminal', 'never'],
1386        default='never',
1387        hidden=False,
1388        validator=ShowStructuredLogsValidator,
1389        help_text="""\
1390        Control when JSON-structured log messages for the current verbosity
1391        level (and above) will be written to standard error. If this property
1392        is disabled, logs are formatted as `text` by default.
1393
1394        Valid values are:
1395            *   `never` - Log messages as text
1396            *   `always` - Always log messages as JSON
1397            *   `log` - Only log messages as JSON if stderr is a file
1398            *    `terminal` - Only log messages as JSON if stderr is a terminal
1399
1400        If unset, default is `never`.""")
1401
1402    def MaxLogDaysValidator(max_log_days):
1403      if max_log_days is None:
1404        return
1405      try:
1406        if int(max_log_days) < 0:
1407          raise InvalidValueError('Max number of days must be at least 0')
1408      except ValueError:
1409        raise InvalidValueError('Max number of days must be an integer')
1410
1411    self.max_log_days = self._Add(
1412        'max_log_days',
1413        validator=MaxLogDaysValidator,
1414        help_text='Maximum number of days to retain log files before deleting.'
1415        ' If set to 0, turns off log garbage collection and does not delete log'
1416        ' files. If unset, the default is 30 days.',
1417        default='30')
1418
1419    self.disable_file_logging = self._AddBool(
1420        'disable_file_logging',
1421        default=False,
1422        help_text='If True, `gcloud` will not store logs to a file. This may '
1423        'be useful if disk space is limited.')
1424
1425    self.custom_ca_certs_file = self._Add(
1426        'custom_ca_certs_file',
1427        validator=ExistingAbsoluteFilepathValidator,
1428        help_text='Absolute path to a custom CA cert file.')
1429
1430    def ProjectValidator(project):
1431      """Checks to see if the project string is valid."""
1432      if project is None:
1433        return
1434
1435      if not isinstance(project, six.string_types):
1436        raise InvalidValueError('project must be a string')
1437      if project == '':  # pylint: disable=g-explicit-bool-comparison
1438        raise InvalidProjectError('The project property is set to the '
1439                                  'empty string, which is invalid.')
1440      if project.isdigit():
1441        raise InvalidProjectError(
1442            'The project property must be set to a valid project ID, not the '
1443            'project number [{value}]'.format(value=project))
1444
1445      if _VALID_PROJECT_REGEX.match(project):
1446        return
1447
1448      if _LooksLikeAProjectName(project):
1449        raise InvalidProjectError(
1450            'The project property must be set to a valid project ID, not the '
1451            'project name [{value}]'.format(value=project))
1452      # Non heuristics for a better error message.
1453      raise InvalidProjectError(
1454          'The project property must be set to a valid project ID, '
1455          '[{value}] is not a valid project ID.'.format(value=project))
1456
1457    self.project = self._Add(
1458        'project',
1459        help_text='Project ID of the Cloud Platform project to operate on '
1460        'by default.  This can be overridden by using the global `--project` '
1461        'flag.',
1462        validator=ProjectValidator,
1463        completer=('googlecloudsdk.command_lib.resource_manager.completers:'
1464                   'ProjectCompleter'),
1465        default_flag='--project')
1466    self.credentialed_hosted_repo_domains = self._Add(
1467        'credentialed_hosted_repo_domains', hidden=True)
1468
1469
1470class _SectionSsh(_Section):
1471  """Contains SSH-related properties."""
1472
1473  def __init__(self):
1474    super(_SectionSsh, self).__init__('ssh')
1475    self.putty_force_connect = self._AddBool(
1476        'putty_force_connect',
1477        default=True,  # For backwards compatibility only.
1478        help_text='Whether or not `gcloud` should automatically accept new or '
1479        'changed host keys when executing plink/pscp commands on Windows. '
1480        'Defaults to True, but can be set to False to present these '
1481        'interactive prompts to the user for host key checking.')
1482    self.verify_internal_ip = self._AddBool(
1483        'verify_internal_ip',
1484        default=True,
1485        help_text='Whether or not `gcloud` should perform an initial SSH '
1486        'connection to verify an instance ID is correct when connecting via '
1487        'its internal IP. Without this check, `gcloud` will simply connect to '
1488        'the internal IP of the desired instance, which may be wrong if the '
1489        'desired instance is in a different subnet but happens to share the '
1490        'same internal IP as an instance in the current subnet. Defaults to '
1491        'True.')
1492
1493
1494class _SectionDeclarative(_Section):
1495  """Contains the properties for the 'declarative' section."""
1496
1497  def __init__(self):
1498    super(_SectionDeclarative, self).__init__('declarative')
1499    self.client = self._Add(
1500        'client_type',
1501        choices=['dcl', 'kcc'],
1502        help_text='Underlying declarative client library to use for declarative commands.',
1503        default='kcc')
1504    self.format = self._Add(
1505        'format',
1506        choices=['krm', 'terraform'],
1507        help_text='Declarative format to use for declarative commands.',
1508        default='krm')
1509
1510
1511class _SectionScc(_Section):
1512  """Contains the properties for the 'scc' section."""
1513
1514  def __init__(self):
1515    super(_SectionScc, self).__init__('scc')
1516    self.organization = self._Add(
1517        'organization',
1518        help_text='Default organization `gcloud` should use for scc surface.')
1519    self.parent = self._Add(
1520        'parent',
1521        help_text='Default parent `gcloud` should use for scc surface.')
1522
1523
1524class _SectionAuth(_Section):
1525  """Contains the properties for the 'auth' section."""
1526  DEFAULT_AUTH_HOST = 'https://accounts.google.com/o/oauth2/auth'
1527  DEFAULT_TOKEN_HOST = 'https://oauth2.googleapis.com/token'
1528
1529  def __init__(self):
1530    super(_SectionAuth, self).__init__('auth')
1531    self.auth_host = self._Add(
1532        'auth_host', hidden=True, default=self.DEFAULT_AUTH_HOST)
1533    self.disable_credentials = self._AddBool(
1534        'disable_credentials',
1535        default=False,
1536        help_text='If True, `gcloud` will not attempt to load any credentials '
1537        'or authenticate any requests. This is useful when behind a proxy '
1538        'that adds authentication to requests.')
1539    self.token_host = self._Add(
1540        'token_host', hidden=True, default=self.DEFAULT_TOKEN_HOST)
1541    self.disable_ssl_validation = self._AddBool(
1542        'disable_ssl_validation', hidden=True)
1543    self.client_id = self._Add(
1544        'client_id', hidden=True, default=config.CLOUDSDK_CLIENT_ID)
1545    self.client_secret = self._Add(
1546        'client_secret',
1547        hidden=True,
1548        default=config.CLOUDSDK_CLIENT_NOTSOSECRET)
1549    self.authority_selector = self._Add('authority_selector', hidden=True)
1550    self.authorization_token_file = self._Add(
1551        'authorization_token_file', hidden=True)
1552    self.credential_file_override = self._Add(
1553        'credential_file_override', hidden=True)
1554    self.impersonate_service_account = self._Add(
1555        'impersonate_service_account',
1556        help_text='After setting this property, all API requests will be made '
1557        'as the given service account instead of the currently selected '
1558        'account. This is done without needing to create, download, and '
1559        'activate a key for the account. In order to perform operations as the '
1560        'service account, your currently selected account must have an IAM '
1561        'role that includes the iam.serviceAccounts.getAccessToken permission '
1562        'for the service account. The roles/iam.serviceAccountTokenCreator '
1563        'role has this permission or you may create a custom role.')
1564    self.disable_load_google_auth = self._AddBool(
1565        'disable_load_google_auth',
1566        default=False,
1567        hidden=True,
1568        help_text='Global switch to turn off loading credentials as '
1569        'google-auth. Users can use it to switch back to the old '
1570        'mode if google-auth breaks users.')
1571    self.opt_out_google_auth = self._AddBool(
1572        'opt_out_google_auth',
1573        default=False,
1574        hidden=True,
1575        help_text='A switch to disable google-auth for a surface or a command '
1576        'group, in case there are some edge cases or google-auth '
1577        'does not work for some surface.')
1578    self.disable_activate_service_account_google_auth = self._AddBool(
1579        'disable_activate_service_account_google_auth',
1580        default=False,
1581        hidden=True,
1582        help_text='True to have activate-service-account command fall back to '
1583        'execute against oauth2client library.')
1584
1585
1586class _SectionBilling(_Section):
1587  """Contains the properties for the 'auth' section."""
1588
1589  LEGACY = 'LEGACY'
1590  CURRENT_PROJECT = 'CURRENT_PROJECT'
1591  CURRENT_PROJECT_WITH_FALLBACK = 'CURRENT_PROJECT_WITH_FALLBACK'
1592
1593  def __init__(self):
1594    super(_SectionBilling, self).__init__('billing')
1595
1596    self.quota_project = self._Add(
1597        'quota_project',
1598        default=_SectionBilling.CURRENT_PROJECT,
1599        help_text="""\
1600        Project that will be charged quota for the
1601        operations performed in `gcloud`. When unset, the default is
1602        [CURRENT_PROJECT]; this will charge quota against the currently set
1603        project for operations performed on it. Additionally, some existing
1604        APIs will continue to use a shared project for quota by default, when
1605        this property is unset.
1606
1607        If you need to operate on one project, but
1608        need quota against a different project, you can use this property to
1609        specify the alternate project.""")
1610
1611
1612class _SectionMetrics(_Section):
1613  """Contains the properties for the 'metrics' section."""
1614
1615  def __init__(self):
1616    super(_SectionMetrics, self).__init__('metrics', hidden=True)
1617    self.environment = self._Add('environment', hidden=True)
1618    self.environment_version = self._Add('environment_version', hidden=True)
1619    self.command_name = self._Add('command_name', internal=True)
1620
1621
1622class _SectionComponentManager(_Section):
1623  """Contains the properties for the 'component_manager' section."""
1624
1625  def __init__(self):
1626    super(_SectionComponentManager, self).__init__('component_manager')
1627    self.additional_repositories = self._Add(
1628        'additional_repositories',
1629        help_text='Comma separated list of additional repositories to check '
1630        'for components.  This property is automatically managed by the '
1631        '`gcloud components repositories` commands.')
1632    self.disable_update_check = self._AddBool(
1633        'disable_update_check',
1634        help_text='If True, Cloud SDK will not automatically check for '
1635        'updates.')
1636    self.fixed_sdk_version = self._Add('fixed_sdk_version', hidden=True)
1637    self.snapshot_url = self._Add('snapshot_url', hidden=True)
1638    # We need the original snapshot_url because snapshot_url may be
1639    # overwritten by users. Without original_snapshot_url, users can be trapped
1640    # to the overwritten snapshot_url even after it is unset.
1641    self.original_snapshot_url = self._Add(
1642        'original_snapshot_url',
1643        internal=True,
1644        hidden=True,
1645        help_text='Snapshot URL when this installation is firstly installed.',
1646        default='https://dl.google.com/dl/cloudsdk/channels/rapid/components-2.json'
1647    )
1648
1649
1650class _SectionExperimental(_Section):
1651  """Contains the properties for gcloud experiments."""
1652
1653  def __init__(self):
1654    super(_SectionExperimental, self).__init__('experimental', hidden=True)
1655    self.fast_component_update = self._AddBool(
1656        'fast_component_update', default=False)
1657
1658
1659class _SectionFilestore(_Section):
1660  """Contains the properties for the 'filestore' section."""
1661
1662  def __init__(self):
1663    super(_SectionFilestore, self).__init__('filestore')
1664    self.location = self._Add(
1665        'location',
1666        help_text='Please use the `--location` flag or set the '
1667        'filestore/zone or filestore/region property.')
1668    self.zone = self._Add(
1669        'zone',
1670        help_text='Default zone to use when working with Cloud Filestore '
1671        'zones. When a `--zone` flag is required but not '
1672        'provided, the command will fall back to this value, if set.')
1673    self.region = self._Add(
1674        'region',
1675        help_text='Default region to use when working with Cloud Filestore '
1676        'regions. When a `--region` flag is required but not '
1677        'provided, the command will fall back to this value, if set.')
1678
1679
1680class _SectionTest(_Section):
1681  """Contains the properties for the 'test' section."""
1682
1683  def __init__(self):
1684    super(_SectionTest, self).__init__('test')
1685    self.results_base_url = self._Add('results_base_url', hidden=True)
1686    self.matrix_status_interval = self._Add(
1687        'matrix_status_interval', hidden=True)
1688
1689
1690class _SectionTransport(_Section):
1691  """Contains the properties for the 'transport' section."""
1692
1693  def __init__(self):
1694    super(_SectionTransport, self).__init__('transport', hidden=True)
1695    self.disable_requests_override = self._AddBool(
1696        'disable_requests_override',
1697        default=False,
1698        hidden=True,
1699        help_text='Global switch to turn off using requests as a '
1700        'transport. Users can use it to switch back to the old '
1701        'mode if requests breaks users.')
1702    self.opt_out_requests = self._AddBool(
1703        'opt_out_requests',
1704        default=False,
1705        hidden=True,
1706        help_text='A switch to disable requests for a surface or a command '
1707        'group.')
1708
1709
1710class _SectionMlEngine(_Section):
1711  """Contains the properties for the 'ml_engine' section."""
1712
1713  def __init__(self):
1714    super(_SectionMlEngine, self).__init__('ml_engine')
1715    self.polling_interval = self._Add(
1716        'polling_interval',
1717        default=60,
1718        help_text=('Interval (in seconds) at which to poll logs from your '
1719                   'Cloud ML Engine jobs. Note that making it much faster than '
1720                   'the default (60) will quickly use all of your quota.'))
1721    self.local_python = self._Add(
1722        'local_python',
1723        default=None,
1724        help_text=('Full path to the Python interpreter to use for '
1725                   'Cloud ML Engine local predict/train jobs. If not '
1726                   'specified, the default path is the one to the Python '
1727                   'interpreter found on system `PATH`.'))
1728
1729
1730class _SectionNotebooks(_Section):
1731  """Contains the properties for the 'notebooks' section."""
1732
1733  def __init__(self):
1734    super(_SectionNotebooks, self).__init__('notebooks')
1735
1736    self.location = self._Add(
1737        'location',
1738        help_text='Default location to use when working with Notebook '
1739        'resources. When a `location` value is required but not provided, the '
1740        'command will fall back to this value, if set.')
1741
1742
1743class _SectionPubsub(_Section):
1744  """Contains the properties for the 'pubsub' section."""
1745
1746  def __init__(self):
1747    super(_SectionPubsub, self).__init__('pubsub')
1748    self.legacy_output = self._AddBool(
1749        'legacy_output',
1750        default=False,
1751        internal=True,
1752        hidden=True,
1753        help_text=('Use the legacy output for beta pubsub commands. The legacy '
1754                   'output from beta is being deprecated. This property will '
1755                   'eventually be removed.'))
1756
1757
1758class _SectionComposer(_Section):
1759  """Contains the properties for the 'composer' section."""
1760
1761  def __init__(self):
1762    super(_SectionComposer, self).__init__('composer')
1763    self.location = self._Add(
1764        'location',
1765        help_text=(
1766            'Composer location to use. Each Composer location '
1767            'constitutes an independent resource namespace constrained to '
1768            'deploying environments into Compute Engine regions inside this '
1769            'location. This parameter corresponds to the '
1770            '/locations/<location> segment of the Composer resource URIs being '
1771            'referenced.'))
1772
1773
1774class _SectionDeploy(_Section):
1775  """Contains the properties for the 'deploy' section."""
1776
1777  def __init__(self):
1778    super(_SectionDeploy, self).__init__('deploy')
1779    self.region = self._Add(
1780        'region',
1781        help_text=(
1782            'Cloud Deploy region to use. Each Cloud Deploy '
1783            'region constitutes an independent resource namespace constrained '
1784            'to deploying instances into Compute Engine zones inside '
1785            'the region.'))
1786    self.delivery_pipeline = self._Add(
1787        'delivery_pipeline',
1788        help_text=('Delivery Pipeline being managed by Cloud Deploy.'))
1789
1790
1791class _SectionDataflow(_Section):
1792  """Contains the properties for the 'dataflow' section."""
1793
1794  def __init__(self):
1795    super(_SectionDataflow, self).__init__('dataflow')
1796    self.disable_public_ips = self._AddBool(
1797        'disable_public_ips',
1798        help_text='Specifies that Cloud Dataflow workers '
1799        'must not use public IP addresses.',
1800        default=False)
1801    self.print_only = self._AddBool(
1802        'print_only',
1803        help_text='Prints the container spec to stdout. Does not save in '
1804        'Google Cloud Storage.',
1805        default=False)
1806    self.enable_streaming_engine = self._AddBool(
1807        'enable_streaming_engine',
1808        help_text='Set this to true to enable Streaming Engine for the job.',
1809        default=False)
1810
1811
1812class _SectionDatafusion(_Section):
1813  """Contains the properties for the 'datafusion' section."""
1814
1815  def __init__(self):
1816    super(_SectionDatafusion, self).__init__('datafusion')
1817    self.location = self._Add(
1818        'location',
1819        help_text=(
1820            'Datafusion location to use. Each Datafusion location '
1821            'constitutes an independent resource namespace constrained to '
1822            'deploying environments into Compute Engine regions inside this '
1823            'location. This parameter corresponds to the '
1824            '/locations/<location> segment of the Datafusion resource URIs being '
1825            'referenced.'))
1826
1827
1828class _SectionDataproc(_Section):
1829  """Contains the properties for the 'ml_engine' section."""
1830
1831  def __init__(self):
1832    super(_SectionDataproc, self).__init__('dataproc')
1833    self.region = self._Add(
1834        'region',
1835        help_text=(
1836            'Cloud Dataproc region to use. Each Cloud Dataproc '
1837            'region constitutes an independent resource namespace constrained '
1838            'to deploying instances into Compute Engine zones inside '
1839            'the region.'))
1840
1841
1842class _SectionDeploymentManager(_Section):
1843  """Contains the properties for the 'deployment_manager' section."""
1844
1845  def __init__(self):
1846    super(_SectionDeploymentManager, self).__init__('deployment_manager')
1847    self.glob_imports = self._AddBool(
1848        'glob_imports',
1849        default=False,
1850        help_text=(
1851            'Enable import path globbing. Uses glob patterns to match multiple '
1852            'imports in a config file.'))
1853
1854
1855class _SectionInteractive(_Section):
1856  """Contains the properties for the 'interactive' section."""
1857
1858  def __init__(self):
1859    super(_SectionInteractive, self).__init__('interactive')
1860    self.bottom_bindings_line = self._AddBool(
1861        'bottom_bindings_line',
1862        default=True,
1863        help_text='If True, display the bottom key bindings line.')
1864    self.bottom_status_line = self._AddBool(
1865        'bottom_status_line',
1866        default=False,
1867        help_text='If True, display the bottom status line.')
1868    self.completion_menu_lines = self._Add(
1869        'completion_menu_lines',
1870        default=4,
1871        help_text='Number of lines in the completion menu.')
1872    self.context = self._Add(
1873        'context', default='', help_text='Command context string.')
1874    self.debug = self._AddBool(
1875        'debug',
1876        default=False,
1877        hidden=True,
1878        help_text='If True, enable the debugging display.')
1879    self.fixed_prompt_position = self._Add(
1880        'fixed_prompt_position',
1881        default=False,
1882        help_text='If True, display the prompt at the same position.')
1883    self.help_lines = self._Add(
1884        'help_lines',
1885        default=10,
1886        help_text='Maximum number of help snippet lines.')
1887    self.hidden = self._AddBool(
1888        'hidden',
1889        default=False,
1890        help_text='If True, expose hidden commands/flags.')
1891    self.justify_bottom_lines = self._AddBool(
1892        'justify_bottom_lines',
1893        default=False,
1894        help_text='If True, left- and right-justify bottom toolbar lines.')
1895    self.manpage_generator = self._Add(
1896        'manpage_generator',
1897        default=True,
1898        help_text=('If True, use the manpage CLI tree generator for '
1899                   'unsupported commands.'))
1900    self.multi_column_completion_menu = self._AddBool(
1901        'multi_column_completion_menu',
1902        default=False,
1903        help_text='If True, display the completions as a multi-column menu.')
1904    self.obfuscate = self._AddBool(
1905        'obfuscate',
1906        default=False,
1907        hidden=True,
1908        help_text='If True, obfuscate status PII.')
1909    self.prompt = self._Add(
1910        'prompt', default='$ ', help_text='Command prompt string.')
1911    self.show_help = self._AddBool(
1912        'show_help',
1913        default=True,
1914        help_text='If True, show help as command args are being entered.')
1915    self.suggest = self._AddBool(
1916        'suggest',
1917        default=False,
1918        help_text='If True, add command line suggestions based on history.')
1919
1920
1921class _SectionPrivateCa(_Section):
1922  """Contains the properties for the 'privateca' section."""
1923
1924  def __init__(self):
1925    super(_SectionPrivateCa, self).__init__('privateca')
1926    self.location = self._Add(
1927        'location',
1928        help_text='Default location to use when working with Private CA '
1929        'resources. When a `--location` flag is required but not provided, the '
1930        'command will fall back to this value, if set.',
1931        completer=('googlecloudsdk.command_lib.privateca.completers:'
1932                   'LocationsCompleter'))
1933
1934
1935class _SectionProxy(_Section):
1936  """Contains the properties for the 'proxy' section."""
1937
1938  def __init__(self):
1939    super(_SectionProxy, self).__init__('proxy')
1940    self.address = self._Add(
1941        'address', help_text='Hostname or IP address of proxy server.')
1942    self.port = self._Add(
1943        'port', help_text='Port to use when connected to the proxy server.')
1944    self.rdns = self._Add(
1945        'rdns',
1946        default=True,
1947        help_text='If True, DNS queries will not be performed '
1948        'locally, and instead, handed to the proxy to resolve. This is default'
1949        ' behavior.')
1950    self.username = self._Add(
1951        'username',
1952        help_text='Username to use when connecting, if the proxy '
1953        'requires authentication.')
1954    self.password = self._Add(
1955        'password',
1956        help_text='Password to use when connecting, if the proxy '
1957        'requires authentication.')
1958
1959    valid_proxy_types = sorted(http_proxy_types.PROXY_TYPE_MAP.keys())
1960
1961    def ProxyTypeValidator(proxy_type):
1962      if proxy_type is not None and proxy_type not in valid_proxy_types:
1963        raise InvalidValueError(
1964            'The proxy type property value [{0}] is not valid. '
1965            'Possible values: [{1}].'.format(proxy_type,
1966                                             ', '.join(valid_proxy_types)))
1967
1968    self.proxy_type = self._Add(
1969        'type',
1970        help_text='Type of proxy being used.  Supported proxy types are:'
1971        ' [{0}].'.format(', '.join(valid_proxy_types)),
1972        validator=ProxyTypeValidator,
1973        choices=valid_proxy_types)
1974
1975    self.use_urllib3_via_shim = self._AddBool(
1976        'use_urllib3_via_shim',
1977        default=False,
1978        hidden=True,
1979        help_text='If True, use `urllib3` to make requests via `httplib2shim`.')
1980
1981
1982class _SectionDevshell(_Section):
1983  """Contains the properties for the 'devshell' section."""
1984
1985  def __init__(self):
1986    super(_SectionDevshell, self).__init__('devshell')
1987    self.image = self._Add(
1988        'image', hidden=True, default=const_lib.DEFAULT_DEVSHELL_IMAGE)
1989    self.metadata_image = self._Add(
1990        'metadata_image', hidden=True, default=const_lib.METADATA_IMAGE)
1991
1992
1993class _SectionDiagnostics(_Section):
1994  """Contains the properties for the 'diagnostics' section."""
1995
1996  def __init__(self):
1997    super(_SectionDiagnostics, self).__init__('diagnostics', hidden=True)
1998    self.hidden_property_whitelist = self._Add(
1999        'hidden_property_whitelist',
2000        internal=True,
2001        help_text=('Comma separated list of hidden properties that should be '
2002                   'allowed by the hidden properties diagnostic.'))
2003
2004
2005class _SectionApiEndpointOverrides(_Section):
2006  """Contains the properties for the 'api-endpoint-overrides' section.
2007
2008  This overrides what endpoint to use when talking to the given API.
2009  """
2010
2011  def __init__(self):
2012    super(_SectionApiEndpointOverrides, self).__init__(
2013        'api_endpoint_overrides', hidden=True)
2014    self.accessapproval = self._Add('accessapproval')
2015    self.accesscontextmanager = self._Add('accesscontextmanager')
2016    self.anthosevents = self._Add('anthosevents')
2017    self.aiplatform = self._Add('aiplatform')
2018    self.apigateway = self._Add('apigateway')
2019    self.apigee = self._Add('apigee')
2020    self.appengine = self._Add('appengine')
2021    self.assuredworkloads = self._Add('assuredworkloads')
2022    self.bigtableadmin = self._Add('bigtableadmin')
2023    self.binaryauthorization = self._Add('binaryauthorization')
2024    self.artifactregistry = self._Add('artifactregistry')
2025    self.categorymanager = self._Add('categorymanager')
2026    self.certificatemanager = self._Add('certificatemanager')
2027    self.cloudasset = self._Add('cloudasset')
2028    self.cloudbilling = self._Add('cloudbilling')
2029    self.cloudbuild = self._Add('cloudbuild')
2030    self.cloudcommerceconsumerprocurement = self._Add(
2031        'cloudcommerceconsumerprocurement')
2032    self.clouddebugger = self._Add('clouddebugger')
2033    self.clouddeploy = self._Add('clouddeploy')
2034    self.clouderrorreporting = self._Add('clouderrorreporting')
2035    self.cloudfunctions = self._Add('cloudfunctions')
2036    self.cloudidentity = self._Add('cloudidentity')
2037    self.cloudiot = self._Add('cloudiot')
2038    self.cloudkms = self._Add('cloudkms')
2039    self.cloudresourcemanager = self._Add('cloudresourcemanager')
2040    self.cloudresourcesearch = self._Add('cloudresourcesearch')
2041    self.cloudscheduler = self._Add('cloudscheduler')
2042    self.cloudtasks = self._Add('cloudtasks')
2043    self.composer = self._Add('composer')
2044    self.compute = self._Add('compute')
2045    self.container = self._Add('container')
2046    self.containeranalysis = self._Add('containeranalysis')
2047    self.datacatalog = self._Add('datacatalog')
2048    self.dataflow = self._Add('dataflow')
2049    self.datafusion = self._Add('datafusion')
2050    self.datamigration = self._Add('datamigration')
2051    self.datapol = self._Add('datapol')
2052    self.dataproc = self._Add('dataproc')
2053    self.datastore = self._Add('datastore')
2054    self.deploymentmanager = self._Add('deploymentmanager')
2055    self.discovery = self._Add('discovery')
2056    self.dns = self._Add('dns')
2057    self.domains = self._Add('domains')
2058    self.eventarc = self._Add('eventarc')
2059    self.events = self._Add('events')
2060    self.kubernetesedge = self._Add('kubernetesedge')
2061    self.file = self._Add('file')
2062    self.firestore = self._Add('firestore')
2063    self.gameservices = self._Add('gameservices')
2064    self.genomics = self._Add('genomics')
2065    self.gkehub = self._Add('gkehub')
2066    self.healthcare = self._Add('healthcare')
2067    self.iam = self._Add('iam')
2068    self.iamassist = self._Add('iamassist')
2069    self.kubernetespolicy = self._Add('kubernetespolicy')
2070    self.labelmanager = self._Add('labelmanager')
2071    self.language = self._Add('language')
2072    self.lifesciences = self._Add('lifesciences')
2073    self.logging = self._Add('logging')
2074    self.luxadmin = self._Add('luxadmin')
2075    self.managedidentities = self._Add('managedidentities')
2076    self.manager = self._Add('manager')
2077    self.mediaasset = self._Add('mediaasset')
2078    self.memcache = self._Add('memcache')
2079    self.metastore = self._Add('metastore')
2080    self.ml = self._Add('ml')
2081    self.monitoring = self._Add('monitoring')
2082    self.networkconnectivity = self._Add('networkconnectivity')
2083    self.networkmanagement = self._Add('networkmanagement')
2084    self.networkservices = self._Add('networkservices')
2085    self.networksecurity = self._Add('networksecurity')
2086    self.notebooks = self._Add('notebooks')
2087    self.ondemandscanning = self._Add('ondemandscanning')
2088    self.orgpolicy = self._Add('orgpolicy')
2089    self.osconfig = self._Add('osconfig')
2090    self.oslogin = self._Add('oslogin')
2091    self.policysimulator = self._Add('policysimulator')
2092    self.policytroubleshooter = self._Add('policytroubleshooter')
2093    self.privateca = self._Add('privateca')
2094    self.publicca = self._Add('publicca')
2095    self.pubsub = self._Add('pubsub')
2096    self.pubsublite = self._Add('pubsublite')
2097    self.recommender = self._Add('recommender')
2098    self.remotebuildexecution = self._Add('remotebuildexecution')
2099    self.replicapoolupdater = self._Add('replicapoolupdater')
2100    self.resourcesettings = self._Add('resourcesettings')
2101    self.runtimeconfig = self._Add('runtimeconfig')
2102    self.redis = self._Add('redis')
2103    self.run = self._Add('run')
2104    self.scc = self._Add('securitycenter')
2105    self.servicemanagement = self._Add('servicemanagement')
2106    self.serviceregistry = self._Add('serviceregistry')
2107    self.serviceusage = self._Add('serviceusage')
2108    self.source = self._Add('source')
2109    self.sourcerepo = self._Add('sourcerepo')
2110    self.secrets = self._Add('secretmanager')
2111    self.servicedirectory = self._Add('servicedirectory')
2112    self.spanner = self._Add('spanner')
2113    self.speech = self._Add('speech')
2114    self.sql = self._Add('sql')
2115    self.storage = self._Add('storage')
2116    self.testing = self._Add('testing')
2117    self.toolresults = self._Add('toolresults')
2118    self.tpu = self._Add('tpu')
2119    self.vision = self._Add('vision')
2120    self.vpcaccess = self._Add('vpcaccess')
2121    self.workflowexecutions = self._Add('workflowexecutions')
2122    self.workflows = self._Add('workflows')
2123    self.sddc = self._Add('sddc')
2124
2125  def EndpointValidator(self, value):
2126    """Checks to see if the endpoint override string is valid."""
2127    if value is None:
2128      return
2129    if not _VALID_ENDPOINT_OVERRIDE_REGEX.match(value):
2130      raise InvalidValueError(
2131          'The endpoint_overrides property must be an absolute URI beginning '
2132          'with http:// or https:// and ending with a trailing \'/\'. '
2133          '[{value}] is not a valid endpoint override.'.format(value=value))
2134
2135  def _Add(self, name):
2136    return super(_SectionApiEndpointOverrides, self)._Add(
2137        name, validator=self.EndpointValidator)
2138
2139
2140class _SectionApiClientOverrides(_Section):
2141  """Contains the properties for the 'api-client-overrides' section.
2142
2143  This overrides the API client version to use when talking to this API.
2144  """
2145
2146  def __init__(self):
2147    super(_SectionApiClientOverrides, self).__init__(
2148        'api_client_overrides', hidden=True)
2149    self.appengine = self._Add('appengine')
2150    self.cloudidentity = self._Add('cloudidentity')
2151    self.compute = self._Add('compute')
2152    self.compute_alpha = self._Add('compute/alpha')
2153    self.compute_beta = self._Add('compute/beta')
2154    self.compute_v1 = self._Add('compute/v1')
2155    self.container = self._Add('container')
2156    self.luxadmin = self._Add('luxadmin')
2157    self.speech = self._Add('speech')
2158    self.sql = self._Add('sql')
2159    self.run = self._Add('run')
2160    self.scc = self._Add('securitycenter')
2161
2162
2163class _SectionEmulator(_Section):
2164  """Contains the properties for the 'emulator' section.
2165
2166  This is used to configure emulator properties for pubsub and datastore, such
2167  as host_port and data_dir.
2168  """
2169
2170  def __init__(self):
2171    super(_SectionEmulator, self).__init__('emulator', hidden=True)
2172    self.datastore_data_dir = self._Add('datastore_data_dir')
2173    self.pubsub_data_dir = self._Add('pubsub_data_dir')
2174    self.datastore_host_port = self._Add(
2175        'datastore_host_port', default='localhost:8081')
2176    self.pubsub_host_port = self._Add(
2177        'pubsub_host_port', default='localhost:8085')
2178    self.bigtable_host_port = self._Add(
2179        'bigtable_host_port', default='localhost:8086')
2180
2181
2182def AccessPolicyValidator(policy):
2183  """Checks to see if the Access Policy string is valid."""
2184  if policy is None:
2185    return
2186  if not policy.isdigit():
2187    raise InvalidValueError(
2188        'The access_context_manager.policy property must be set '
2189        'to the policy number, not a string.')
2190
2191
2192class _SectionAccessContextManager(_Section):
2193  """Contains the properties for the 'access_context_manager' section."""
2194
2195  def OrganizationValidator(self, org):
2196    """Checks to see if the Organization string is valid."""
2197    if org is None:
2198      return
2199    if not org.isdigit():
2200      raise InvalidValueError(
2201          'The access_context_manager.organization property must be set '
2202          'to the organization ID number, not a string.')
2203
2204  def __init__(self):
2205    super(_SectionAccessContextManager, self).__init__(
2206        'access_context_manager', hidden=True)
2207
2208    self.policy = self._Add(
2209        'policy',
2210        validator=AccessPolicyValidator,
2211        help_text=('ID of the policy resource to operate on. Can be found '
2212                   'by running the `access-context-manager policies list` '
2213                   'command.'))
2214    self.organization = self._Add(
2215        'organization',
2216        validator=self.OrganizationValidator,
2217        help_text=('Default organization cloud-bindings command group will '
2218                   'operate on.'))
2219
2220
2221class _SectionContextAware(_Section):
2222  """Contains the properties for the 'context_aware' section."""
2223
2224  def __init__(self):
2225    super(_SectionContextAware, self).__init__('context_aware')
2226    self.use_client_certificate = self._AddBool(
2227        'use_client_certificate',
2228        help_text=('If True, use client certificate to authorize user '
2229                   'device using Context-aware access. Some services may not '
2230                   'support client certificate authorization. If a command '
2231                   'sends requests to such services, the client certificate '
2232                   'will not be validated. '
2233                   'Run `gcloud topic client-certificate` for list of services '
2234                   'supporting this feature.'),
2235        default=False)
2236    self.auto_discovery_file_path = self._Add(
2237        'auto_discovery_file_path',
2238        validator=ExistingAbsoluteFilepathValidator,
2239        help_text='File path for auto discovery configuration file.',
2240        hidden=True)
2241
2242
2243class _SectionEventarc(_Section):
2244  """Contains the properties for the 'eventarc' section."""
2245
2246  def __init__(self):
2247    super(_SectionEventarc, self).__init__('eventarc')
2248    self.location = self._Add(
2249        'location',
2250        help_text='The default location to use when working with Eventarc '
2251        "resources. This should be either ``global'' or one of the supported "
2252        'regions. When a `--location` flag is required but not provided, the '
2253        'command will fall back to this value, if set.')
2254
2255
2256class _SectionMemcache(_Section):
2257  """Contains the properties for the 'memcache' section."""
2258
2259  def __init__(self):
2260    super(_SectionMemcache, self).__init__('memcache')
2261    self.region = self._Add(
2262        'region',
2263        help_text='Default region to use when working with Cloud Memorystore '
2264        'for Memcached resources. When a `region` is required but not provided '
2265        'by a flag, the command will fall back to this value, if set.')
2266
2267
2268class _SectionMetastore(_Section):
2269  """Contains the properties for the 'metastore' section."""
2270
2271  class Tier(enum.Enum):
2272    developer = 1
2273    enterprise = 3
2274
2275  def TierValidator(self, tier):
2276    if tier is None:
2277      return
2278
2279    if tier not in [x.name for x in list(_SectionMetastore.Tier)]:
2280      raise InvalidValueError(
2281          ('tier `{0}` must be one of: [developer, enterprise]'.format(tier)))
2282
2283  def __init__(self):
2284    super(_SectionMetastore, self).__init__('metastore')
2285    self.location = self._Add(
2286        'location',
2287        help_text='Default location to use when working with Dataproc '
2288        'Metastore. When a `location` is required but not provided by a flag, '
2289        'the command will fall back to this value, if set.')
2290    self.tier = self._Add(
2291        'tier',
2292        validator=self.TierValidator,
2293        help_text="""\
2294        Default tier to use when creating Dataproc Metastore services.
2295        When a `tier` is required but not provided by a flag,
2296        the command will fall back to this value, if set.
2297
2298        Valid values are:
2299            *   `developer` - The developer tier provides limited scalability
2300            and no fault tolerance. Good for low-cost proof-of-concept.
2301            *   `enterprise` - The enterprise tier provides multi-zone high
2302            availability, and sufficient scalability for enterprise-level
2303            Dataproc Metastore workloads.""",
2304        choices=[x.name for x in list(_SectionMetastore.Tier)])
2305
2306
2307class _SectionRedis(_Section):
2308  """Contains the properties for the 'redis' section."""
2309
2310  def __init__(self):
2311    super(_SectionRedis, self).__init__('redis')
2312    self.region = self._Add(
2313        'region',
2314        help_text='Default region to use when working with Cloud '
2315        'Memorystore for Redis resources. When a `region` is required but not '
2316        'provided by a flag, the command will fall back to this value, if set.')
2317
2318
2319class _SectionStorage(_Section):
2320  """Contains the properties for the 'storage' section."""
2321
2322  _CHECK_HASHES_HELP_TEXT = ("""\
2323      'check_hashes' specifies how strictly to require integrity checking for
2324      downloaded data. Legal values are:
2325
2326      'if_fast_else_fail' - (default) Only integrity check if the digest
2327      will run efficiently (using compiled code), else fail the download.
2328
2329      'if_fast_else_skip' - Only integrity check if the server supplies a hash
2330      and the local digest computation will run quickly, else skip the check.
2331
2332      'always' - Always check download integrity regardless of possible
2333      performance costs.
2334
2335      'never' - Don't perform download integrity checks. This setting is
2336      not recommended except for special cases such as measuring download
2337      performance excluding time for integrity checking.
2338
2339      This option exists to assist users who wish to download a GCS composite
2340      object and are unable to install crcmod with the C-extension. CRC32c is
2341      the only available integrity check for composite objects, and without the
2342      C-extension, download performance can be significantly degraded by the
2343      digest computation. This option is ignored for daisy-chain copies, which
2344      don't compute hashes but instead (inexpensively) compare the cloud source
2345      and destination hashes.""")
2346
2347  MAXIMUM_DEFAULT_PROCESS_COUNT = 12
2348  DEFAULT_THREAD_COUNT = 4
2349
2350  DEFAULT_CHUNK_SIZE = 104857600  # 100 MB, or 1024 * 1024 * 100.
2351  DEFAULT_RESUMABLE_THRESHOLD = 8388608  # 8 MB, or 1024 * 1024 * 8.
2352
2353  def __init__(self):
2354    super(_SectionStorage, self).__init__('storage', hidden=True)
2355    self.check_hashes = self._Add(
2356        'check_hashes',
2357        default='if_fast_else_fail',
2358        help_text=self._CHECK_HASHES_HELP_TEXT,
2359        choices=('if_fast_else_fail', 'if_fast_else_skip', 'always', 'never'))
2360
2361    self.chunk_size = self._Add(
2362        'chunk_size',
2363        default=self.DEFAULT_CHUNK_SIZE,
2364        help_text='Chunk size used for uploading and downloading from '
2365        'Cloud Storage.')
2366
2367    self.max_retries = self._Add(
2368        'max_retries',
2369        default=23,
2370        help_text='Max number of retries for operations like copy.')
2371
2372    self.max_retry_delay = self._Add(
2373        'max_retry_delay',
2374        default=32,
2375        help_text='Max second delay between retriable operations.')
2376
2377    self.process_count = self._Add(
2378        'process_count',
2379        default=min(multiprocessing.cpu_count(),
2380                    self.MAXIMUM_DEFAULT_PROCESS_COUNT),
2381        help_text='The maximum number of processes parallel execution should '
2382        'use. When process_count and thread_count are both 1, commands use '
2383        'sequential execution.')
2384
2385    self.resumable_threshold = self._Add(
2386        'resumable_threshold',
2387        default=self.DEFAULT_RESUMABLE_THRESHOLD,
2388        help_text='File operations above this size in bytes will use resumable'
2389        ' instead of one-shot strategies. For example, a resumable download.')
2390
2391    self.sliced_object_download_component_size = self._Add(
2392        'sliced_object_download_component_size',
2393        default='200M',
2394        validator=_HumanReadableByteAmountValidator,
2395        help_text='Target size and upper bound for files to be sliced into.'
2396        ' Analogous to parallel_composite_upload_component_size.')
2397
2398    self.sliced_object_download_max_components = self._Add(
2399        'sliced_object_download_max_components',
2400        default=4,
2401        validator=_HumanReadableByteAmountValidator,
2402        help_text='Specifies the maximum number of slices to be used when'
2403        ' performing a sliced object download.')
2404
2405    self.sliced_object_download_threshold = self._Add(
2406        'sliced_object_download_threshold',
2407        default='150M',
2408        validator=_HumanReadableByteAmountValidator,
2409        help_text='Slice files larger than this value. Zero will block sliced'
2410        ' downloads. Analogous to parallel_composite_upload_threshold.')
2411
2412    self.thread_count = self._Add(
2413        'thread_count',
2414        default=self.DEFAULT_THREAD_COUNT,
2415        help_text='The number of threads parallel execution should use per '
2416        'process. When process_count and thread_count are both 1, commands use '
2417        'sequential execution.')
2418
2419    self.parallel_composite_upload_component_size = self._Add(
2420        'parallel_composite_upload_component_size',
2421        default='50M',
2422        validator=_HumanReadableByteAmountValidator,
2423        help_text='Specifies the ideal size of a component in bytes, which '
2424        'will act as an upper bound to the size of the components if '
2425        'ceil(file_size / parallel_composite_upload_component_size) is less '
2426        'than the maximum number of objects the API allows composing at once. '
2427        'Values can be provided either in bytes or as human-readable values '
2428        '(e.g., "150M" to represent 150 mebibytes).')
2429
2430    self.parallel_composite_upload_threshold = self._Add(
2431        'parallel_composite_upload_threshold',
2432        default='0',
2433        validator=_HumanReadableByteAmountValidator,
2434        help_text='Specifies the maximum size of a file to upload in a single '
2435        'stream. Files larger than this threshold will be partitioned into '
2436        'component parts, uploaded in parallel, then composed into a single '
2437        'object. The number of components will be the smaller of '
2438        'ceil(file_size / parallel_composite_upload_component_size) and '
2439        'the maximum number of objects the API allows composing at once. For '
2440        'GCS this limit is 32. If this property is set to 0, then automatic '
2441        'parallel uploads will never occur.')
2442
2443    self.tracker_files_directory = self._Add(
2444        'tracker_files_directory',
2445        default=os.path.join('~', '.config', 'gcloud', 'surface_data',
2446                             'storage', 'tracker_files'),
2447        help_text='Directory path to tracker files for resumable operations.')
2448
2449    # TODO(b/109938541): Remove this after implementation seems stable.
2450    self.use_gsutil = self._AddBool(
2451        'use_gsutil',
2452        default=False,
2453        help_text='If True, use the deprecated upload implementation which '
2454        'uses gsutil.')
2455
2456    self.use_threading_local = self._AddBool(
2457        'use_threading_local',
2458        default=True,
2459        help_text='If True, reuses some resource if they are already declared on'
2460        ' a thread. If False, creates duplicates of resources like API clients'
2461        ' on the same thread. Turning off can help with some bugs but will'
2462        ' hurt performance.')
2463
2464
2465class _SectionSurvey(_Section):
2466  """Contains the properties for the 'survey' section."""
2467
2468  def __init__(self):
2469    super(_SectionSurvey, self).__init__('survey')
2470    self.disable_prompts = self._AddBool(
2471        'disable_prompts',
2472        default=False,
2473        help_text='If True, gcloud will not prompt you to take periodic usage '
2474        'experience surveys.')
2475
2476
2477class _SectionTranscoder(_Section):
2478  """Contains the properties for the 'transcoder' section."""
2479
2480  def __init__(self):
2481    super(_SectionTranscoder, self).__init__('transcoder', hidden=True)
2482    self.location = self._Add(
2483        'location',
2484        help_text=(
2485            'Transcoder location to use. This parameter corresponds to the '
2486            '/locations/<location> segment of the Transcoder resource URIs '
2487            'being referenced.'))
2488
2489
2490class _SectionWorkflows(_Section):
2491  """Contains the properties for the 'workflows' section."""
2492
2493  def __init__(self):
2494    super(_SectionWorkflows, self).__init__('workflows', hidden=True)
2495    self.location = self._Add(
2496        'location',
2497        default='us-central1',
2498        help_text='The default region to use when working with Cloud '
2499        'Workflows resources. When a `--location` flag is required '
2500        'but not provided, the command will fall back to this value, if set.')
2501
2502
2503class _SectionAi(_Section):
2504  """Contains the properties for the command group 'ai' section."""
2505
2506  def __init__(self):
2507    super(_SectionAi, self).__init__('ai')
2508    self.region = self._Add(
2509        'region',
2510        help_text='Default region to use when working with'
2511        'AI Platform resources. When a `--region` flag is required '
2512        'but not provided, the command will fall back to this value, if set.')
2513
2514
2515class _SectionAiPlatform(_Section):
2516  """Contains the properties for the command group 'ai_platform' section."""
2517
2518  def __init__(self):
2519    super(_SectionAiPlatform, self).__init__('ai_platform')
2520    self.region = self._Add(
2521        'region',
2522        help_text='Default region to use when working with AI Platform '
2523        'Training and Prediction resources (currently for Prediction only). '
2524        'It is ignored for training resources for now. The value should be '
2525        'either `global` or one of the supported regions. When a `--region` '
2526        'flag is required but not provided, the command will fall back to this '
2527        'value, if set.')
2528
2529
2530class _SectionVmware(_Section):
2531  """Contains the properties for the 'vmware' section."""
2532
2533  def __init__(self):
2534    super(_SectionVmware, self).__init__('vmware')
2535
2536    self.region = self._Add(
2537        'region',
2538        default='us-central1',
2539        help_text='Default region to use when working with VMware '
2540        'Engine resources.  When a `--region` '
2541        'flag is required but not provided, the command will fall back to '
2542        'this value, if set.')
2543
2544    self.node_type = self._Add(
2545        'node-type',
2546        default='c1-highmem-72-metal',
2547        hidden=True,
2548        help_text='Node type to use when creating a new cluster.')
2549
2550
2551class _SectionCode(_Section):
2552  """Contains the properties for the 'code' section."""
2553
2554  def __init__(self):
2555    super(_SectionCode, self).__init__('code', hidden=True)
2556
2557    self.minikube_event_timeout = self._Add(
2558        'minikube_event_timeout',
2559        default='90s',
2560        hidden=True,
2561        help_text='Terminate the cluster start process if this amount of time '
2562        'has passed since the last minikube event.')
2563
2564    self.minikube_path_override = self._Add(
2565        'minikube_paht_override',
2566        hidden=True,
2567        help_text='Location of minikube binary.')
2568
2569    self.skaffold_path_override = self._Add(
2570        'skaffold_path_override',
2571        hidden=True,
2572        help_text='Location of skaffold binary.')
2573
2574
2575class _SectionMediaAsset(_Section):
2576  """Contains the properties for the 'media_asset' section."""
2577
2578  def __init__(self):
2579    super(_SectionMediaAsset, self).__init__('media_asset')
2580    self.location = self._Add(
2581        'location',
2582        default='us-central1',
2583        help_text=(
2584            'Default location to use when working with Cloud Media Asset '
2585            'resources. When a `--location` flag is required but not provided, '
2586            'the command will fall back to this value.'))
2587
2588
2589class _Property(object):
2590  """An individual property that can be gotten from the properties file.
2591
2592  Attributes:
2593    section: str, The name of the section the property appears in in the file.
2594    name: str, The name of the property.
2595    help_text: str, The man page help for what this property does.
2596    is_hidden: bool, True to hide this property from display for users that
2597      don't know about them.
2598    is_internal: bool, True to hide this property from display even if it is
2599      set. Internal properties are implementation details not meant to be set by
2600      users.
2601    callbacks: [func], A list of functions to be called, in order, if no value
2602      is found elsewhere.  The result of a callback will be shown in when
2603      listing properties (if the property is not hidden).
2604    completer: [func], a completer function
2605    default: str, A final value to use if no value is found after the callbacks.
2606      The default value is never shown when listing properties regardless of
2607      whether the property is hidden or not.
2608    default_flag: default_flag name to include in RequiredPropertyError if
2609      property fails on Get. This can be used for flags that are tightly coupled
2610      with a property.
2611    validator: func(str), A function that is called on the value when .Set()'d
2612      or .Get()'d. For valid values, the function should do nothing. For invalid
2613      values, it should raise InvalidValueError with an explanation of why it
2614      was invalid.
2615    choices: [str], The allowable values for this property.  This is included in
2616      the help text and used in tab completion.
2617  """
2618
2619  def __init__(self,
2620               section,
2621               name,
2622               help_text=None,
2623               hidden=False,
2624               internal=False,
2625               callbacks=None,
2626               default=None,
2627               validator=None,
2628               choices=None,
2629               completer=None,
2630               default_flag=None):
2631    self.__section = section
2632    self.__name = name
2633    self.__help_text = help_text
2634    self.__hidden = hidden
2635    self.__internal = internal
2636    self.__callbacks = callbacks or []
2637    self.__default = default
2638    self.__validator = validator
2639    self.__choices = choices
2640    self.__completer = completer
2641    self.__default_flag = default_flag
2642
2643  @property
2644  def section(self):
2645    return self.__section
2646
2647  @property
2648  def name(self):
2649    return self.__name
2650
2651  @property
2652  def help_text(self):
2653    return self.__help_text
2654
2655  @property
2656  def is_hidden(self):
2657    return self.__hidden
2658
2659  @property
2660  def is_internal(self):
2661    return self.__internal
2662
2663  @property
2664  def default(self):
2665    return self.__default
2666
2667  @property
2668  def callbacks(self):
2669    return self.__callbacks
2670
2671  @property
2672  def choices(self):
2673    return self.__choices
2674
2675  @property
2676  def completer(self):
2677    return self.__completer
2678
2679  @property
2680  def default_flag(self):
2681    return self.__default_flag
2682
2683  def __hash__(self):
2684    return hash(self.section) + hash(self.name)
2685
2686  def __eq__(self, other):
2687    return self.section == other.section and self.name == other.name
2688
2689  def __ne__(self, other):
2690    return not self == other
2691
2692  def __gt__(self, other):
2693    return self.name > other.name
2694
2695  def __ge__(self, other):
2696    return self.name >= other.name
2697
2698  def __lt__(self, other):
2699    return self.name < other.name
2700
2701  def __le__(self, other):
2702    return self.name <= other.name
2703
2704  def GetOrFail(self):
2705    """Shortcut for Get(required=True).
2706
2707    Convinient as a callback function.
2708
2709    Returns:
2710      str, The value for this property.
2711    Raises:
2712      RequiredPropertyError if property is not set.
2713    """
2714
2715    return self.Get(required=True)
2716
2717  def Get(self, required=False, validate=True):
2718    """Gets the value for this property.
2719
2720    Looks first in the environment, then in the workspace config, then in the
2721    global config, and finally at callbacks.
2722
2723    Args:
2724      required: bool, True to raise an exception if the property is not set.
2725      validate: bool, Whether or not to run the fetched value through the
2726        validation function.
2727
2728    Returns:
2729      str, The value for this property.
2730    """
2731    value = _GetProperty(self, named_configs.ActivePropertiesFile.Load(),
2732                         required)
2733    if validate:
2734      self.Validate(value)
2735    return value
2736
2737  def IsExplicitlySet(self):
2738    """Determines if this property has been explicitly set by the user.
2739
2740    Properties with defaults or callbacks don't count as explicitly set.
2741
2742    Returns:
2743      True, if the value was explicitly set, False otherwise.
2744    """
2745    value = _GetPropertyWithoutCallback(
2746        self, named_configs.ActivePropertiesFile.Load())
2747    return value is not None
2748
2749  def Validate(self, value):
2750    """Test to see if the value is valid for this property.
2751
2752    Args:
2753      value: str, The value of the property to be validated.
2754
2755    Raises:
2756      InvalidValueError: If the value was invalid according to the property's
2757          validator.
2758    """
2759    if self.__validator:
2760      self.__validator(value)
2761
2762  def GetBool(self, required=False, validate=True):
2763    """Gets the boolean value for this property.
2764
2765    Looks first in the environment, then in the workspace config, then in the
2766    global config, and finally at callbacks.
2767
2768    Does not validate by default because boolean properties were not previously
2769    validated, and startup functions rely on boolean properties that may have
2770    invalid values from previous installations
2771
2772    Args:
2773      required: bool, True to raise an exception if the property is not set.
2774      validate: bool, Whether or not to run the fetched value through the
2775        validation function.
2776
2777    Returns:
2778      bool, The boolean value for this property, or None if it is not set.
2779
2780    Raises:
2781      InvalidValueError: if value is not boolean
2782    """
2783    value = _GetBoolProperty(
2784        self,
2785        named_configs.ActivePropertiesFile.Load(),
2786        required,
2787        validate=validate)
2788    return value
2789
2790  def GetInt(self, required=False, validate=True):
2791    """Gets the integer value for this property.
2792
2793    Looks first in the environment, then in the workspace config, then in the
2794    global config, and finally at callbacks.
2795
2796    Args:
2797      required: bool, True to raise an exception if the property is not set.
2798      validate: bool, Whether or not to run the fetched value through the
2799        validation function.
2800
2801    Returns:
2802      int, The integer value for this property.
2803    """
2804    value = _GetIntProperty(self, named_configs.ActivePropertiesFile.Load(),
2805                            required)
2806    if validate:
2807      self.Validate(value)
2808    return value
2809
2810  def Set(self, value):
2811    """Sets the value for this property as an environment variable.
2812
2813    Args:
2814      value: str/bool, The proposed value for this property.  If None, it is
2815        removed from the environment.
2816    """
2817    self.Validate(value)
2818    if value is not None:
2819      value = Stringize(value)
2820    encoding.SetEncodedValue(os.environ, self.EnvironmentName(), value)
2821
2822  def AddCallback(self, callback):
2823    """Adds another callback for this property."""
2824    self.__callbacks.append(callback)
2825
2826  def RemoveCallback(self, callback):
2827    """Removess given callback for this property."""
2828    self.__callbacks.remove(callback)
2829
2830  def EnvironmentName(self):
2831    """Get the name of the environment variable for this property.
2832
2833    Returns:
2834      str, The name of the correct environment variable.
2835    """
2836    return 'CLOUDSDK_{section}_{name}'.format(
2837        section=self.__section.upper(),
2838        name=self.__name.upper(),
2839    )
2840
2841  def __str__(self):
2842    return '{section}/{name}'.format(section=self.__section, name=self.__name)
2843
2844
2845VALUES = _Sections()
2846
2847
2848def FromString(property_string):
2849  """Gets the property object corresponding the given string.
2850
2851  Args:
2852    property_string: str, The string to parse.  It can be in the format
2853      section/property, or just property if the section is the default one.
2854
2855  Returns:
2856    properties.Property, The property or None if it failed to parse to a valid
2857      property.
2858  """
2859  section, prop = ParsePropertyString(property_string)
2860  if not prop:
2861    return None
2862  return VALUES.Section(section).Property(prop)
2863
2864
2865def ParsePropertyString(property_string):
2866  """Parses a string into a section and property name.
2867
2868  Args:
2869    property_string: str, The property string in the format section/property.
2870
2871  Returns:
2872    (str, str), The section and property.  Both will be none if the input
2873    string is empty.  Property can be None if the string ends with a slash.
2874  """
2875  if not property_string:
2876    return None, None
2877
2878  if '/' in property_string:
2879    section, prop = tuple(property_string.split('/', 1))
2880  else:
2881    section = None
2882    prop = property_string
2883
2884  section = section or VALUES.default_section.name
2885  prop = prop or None
2886  return section, prop
2887
2888
2889class _ScopeInfo(object):
2890
2891  # pylint: disable=redefined-builtin
2892  def __init__(self, id, description):
2893    self.id = id
2894    self.description = description
2895
2896
2897class Scope(object):
2898  """An enum class for the different types of property files that can be used."""
2899
2900  INSTALLATION = _ScopeInfo(
2901      id='installation',
2902      description='The installation based configuration file applies to all '
2903      'users on the system that use this version of the Cloud SDK.  If the SDK '
2904      'was installed by an administrator, you will need administrator rights '
2905      'to make changes to this file.')
2906  USER = _ScopeInfo(
2907      id='user',
2908      description='The user based configuration file applies only to the '
2909      'current user of the system.  It will override any values from the '
2910      'installation configuration.')
2911
2912  _ALL = [USER, INSTALLATION]
2913  _ALL_SCOPE_NAMES = [s.id for s in _ALL]
2914
2915  @staticmethod
2916  def AllValues():
2917    """Gets all possible enum values.
2918
2919    Returns:
2920      [Scope], All the enum values.
2921    """
2922    return list(Scope._ALL)
2923
2924  @staticmethod
2925  def AllScopeNames():
2926    return list(Scope._ALL_SCOPE_NAMES)
2927
2928  @staticmethod
2929  def FromId(scope_id):
2930    """Gets the enum corresponding to the given scope id.
2931
2932    Args:
2933      scope_id: str, The scope id to parse.
2934
2935    Raises:
2936      InvalidScopeValueError: If the given value cannot be parsed.
2937
2938    Returns:
2939      OperatingSystemTuple, One of the OperatingSystem constants or None if the
2940      input is None.
2941    """
2942    if not scope_id:
2943      return None
2944    for scope in Scope._ALL:
2945      if scope.id == scope_id:
2946        return scope
2947    raise InvalidScopeValueError(scope_id)
2948
2949  @staticmethod
2950  def GetHelpString():
2951    return '\n\n'.join(
2952        ['*{0}*::: {1}'.format(s.id, s.description) for s in Scope.AllValues()])
2953
2954
2955def PersistProperty(prop, value, scope=None):
2956  """Sets the given property in the properties file.
2957
2958  This function should not generally be used as part of normal program
2959  execution.  The property files are user editable config files that they should
2960  control.  This is mostly for initial setup of properties that get set during
2961  SDK installation.
2962
2963  Args:
2964    prop: properties.Property, The property to set.
2965    value: str, The value to set for the property. If None, the property is
2966      removed.
2967    scope: Scope, The config location to set the property in.  If given, only
2968      this location will be updated and it is an error if that location does not
2969      exist.  If not given, it will attempt to update the property in the
2970      first of the following places that exists: - the active named config -
2971        user level config It will never fall back to installation properties;
2972        you must use that scope explicitly to set that value.
2973
2974  Raises:
2975    MissingInstallationConfig: If you are trying to set the installation config,
2976      but there is not SDK root.
2977  """
2978  prop.Validate(value)
2979  if scope == Scope.INSTALLATION:
2980    config.EnsureSDKWriteAccess()
2981    config_file = config.Paths().installation_properties_path
2982    if not config_file:
2983      raise MissingInstallationConfig()
2984    prop_files_lib.PersistProperty(config_file, prop.section, prop.name, value)
2985    named_configs.ActivePropertiesFile.Invalidate(mark_changed=True)
2986  else:
2987    active_config = named_configs.ConfigurationStore.ActiveConfig()
2988    active_config.PersistProperty(prop.section, prop.name, value)
2989  # Print message if value being set/unset is overridden by environment var
2990  # to prevent user confusion
2991  env_name = prop.EnvironmentName()
2992  override = encoding.GetEncodedValue(os.environ, env_name)
2993  if override:
2994    warning_message = ('WARNING: Property [{0}] is overridden '
2995                       'by environment setting [{1}={2}]\n')
2996    # Writing to sys.stderr because of circular dependency
2997    # in googlecloudsdk.core.log on properties
2998    sys.stderr.write(warning_message.format(prop.name, env_name, override))
2999
3000
3001def _GetProperty(prop, properties_file, required):
3002  """Gets the given property.
3003
3004  If the property has a designated command line argument and args is provided,
3005  check args for the value first. If the corresponding environment variable is
3006  set, use that second. If still nothing, use the callbacks.
3007
3008  Args:
3009    prop: properties.Property, The property to get.
3010    properties_file: properties_file.PropertiesFile, An already loaded
3011      properties files to use.
3012    required: bool, True to raise an exception if the property is not set.
3013
3014  Raises:
3015    RequiredPropertyError: If the property was required but unset.
3016
3017  Returns:
3018    str, The value of the property, or None if it is not set.
3019  """
3020
3021  flag_to_use = None
3022
3023  invocation_stack = VALUES.GetInvocationStack()
3024  if len(invocation_stack) > 1:
3025    # First item is the blank stack entry, second is from the user command args.
3026    first_invocation = invocation_stack[1]
3027    if prop in first_invocation:
3028      flag_to_use = first_invocation.get(prop).flag
3029
3030  value = _GetPropertyWithoutDefault(prop, properties_file)
3031  if value is not None:
3032    return Stringize(value)
3033
3034  # Still nothing, check the final default.
3035  if prop.default is not None:
3036    return Stringize(prop.default)
3037
3038  # Not set, throw if required.
3039  if required:
3040    raise RequiredPropertyError(prop, flag=flag_to_use)
3041
3042  return None
3043
3044
3045def _GetPropertyWithoutDefault(prop, properties_file):
3046  """Gets the given property without using a default.
3047
3048  If the property has a designated command line argument and args is provided,
3049  check args for the value first. If the corresponding environment variable is
3050  set, use that second. Next, return whatever is in the property file.  Finally,
3051  use the callbacks to find values.  Do not check the default value.
3052
3053  Args:
3054    prop: properties.Property, The property to get.
3055    properties_file: properties_file.PropertiesFile, An already loaded
3056      properties files to use.
3057
3058  Returns:
3059    str, The value of the property, or None if it is not set.
3060  """
3061  # Try to get a value from args, env, or property file.
3062  value = _GetPropertyWithoutCallback(prop, properties_file)
3063  if value is not None:
3064    return Stringize(value)
3065
3066  # No value, try getting a value from the callbacks.
3067  for callback in prop.callbacks:
3068    value = callback()
3069    if value is not None:
3070      return Stringize(value)
3071
3072  return None
3073
3074
3075def _GetPropertyWithoutCallback(prop, properties_file):
3076  """Gets the given property without using a callback or default.
3077
3078  If the property has a designated command line argument and args is provided,
3079  check args for the value first. If the corresponding environment variable is
3080  set, use that second. Finally, return whatever is in the property file.  Do
3081  not check for values in callbacks or defaults.
3082
3083  Args:
3084    prop: properties.Property, The property to get.
3085    properties_file: PropertiesFile, An already loaded properties files to use.
3086
3087  Returns:
3088    str, The value of the property, or None if it is not set.
3089  """
3090  # Look for a value in the flags that were used on this command.
3091  invocation_stack = VALUES.GetInvocationStack()
3092  for value_flags in reversed(invocation_stack):
3093    if prop not in value_flags:
3094      continue
3095    value_flag = value_flags.get(prop, None)
3096    if not value_flag:
3097      continue
3098    if value_flag.value is not None:
3099      return Stringize(value_flag.value)
3100
3101  # Check the environment variable overrides.
3102  value = encoding.GetEncodedValue(os.environ, prop.EnvironmentName())
3103  if value is not None:
3104    return Stringize(value)
3105
3106  # Check the property file itself.
3107  value = properties_file.Get(prop.section, prop.name)
3108  if value is not None:
3109    return Stringize(value)
3110
3111  return None
3112
3113
3114def _GetBoolProperty(prop, properties_file, required, validate=False):
3115  """Gets the given property in bool form.
3116
3117  Args:
3118    prop: properties.Property, The property to get.
3119    properties_file: properties_file.PropertiesFile, An already loaded
3120      properties files to use.
3121    required: bool, True to raise an exception if the property is not set.
3122    validate: bool, True to validate the value
3123
3124  Returns:
3125    bool, The value of the property, or None if it is not set.
3126  """
3127  value = _GetProperty(prop, properties_file, required)
3128  if validate:
3129    _BooleanValidator(prop.name, value)
3130  if value is None or Stringize(value).lower() == 'none':
3131    return None
3132  return value.lower() in ['1', 'true', 'on', 'yes', 'y']
3133
3134
3135def _GetIntProperty(prop, properties_file, required):
3136  """Gets the given property in integer form.
3137
3138  Args:
3139    prop: properties.Property, The property to get.
3140    properties_file: properties_file.PropertiesFile, An already loaded
3141      properties files to use.
3142    required: bool, True to raise an exception if the property is not set.
3143
3144  Returns:
3145    int, The integer value of the property, or None if it is not set.
3146  """
3147  value = _GetProperty(prop, properties_file, required)
3148  if value is None:
3149    return None
3150  try:
3151    return int(value)
3152  except ValueError:
3153    raise InvalidValueError(
3154        'The property [{prop}] must have an integer value: [{value}]'.format(
3155            prop=prop, value=value))
3156
3157
3158def GetMetricsEnvironment():
3159  """Get the metrics environment.
3160
3161  Returns the property metrics/environment if set, if not, it tries to deduce if
3162  we're on some known platforms like devshell or GCE.
3163
3164  Returns:
3165    None, if no environment is set or found
3166    str, a string denoting the environment if one is set or found
3167  """
3168
3169  environment = VALUES.metrics.environment.Get()
3170  if environment:
3171    return environment
3172
3173  # No explicit environment defined, try to deduce it.
3174  # pylint: disable=g-import-not-at-top
3175  from googlecloudsdk.core.credentials import devshell as c_devshell
3176  if c_devshell.IsDevshellEnvironment():
3177    return 'devshell'
3178
3179  from googlecloudsdk.core.credentials import gce_cache
3180  if gce_cache.GetOnGCE(check_age=False):
3181    return 'GCE'
3182
3183  return None
3184