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