HEX
Server: Apache/2.4.59 (Debian)
System: Linux keymana 4.19.0-21-cloud-amd64 #1 SMP Debian 4.19.249-2 (2022-06-30) x86_64
User: lijunjie (1003)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //lib/google-cloud-sdk/lib/googlecloudsdk/command_lib/kuberun/flags.py
# -*- coding: utf-8 -*- #
# Copyright 2020 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helpers to add flags to kuberun commands."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import abc

from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base

import six

_VISIBILITY_MODES = {
    'internal': 'Visible only within the cluster.',
    'external': 'Visible from outside the cluster.',
}


class BinaryCommandFlag(six.with_metaclass(abc.ABCMeta, object)):
  """Informal interface for flags that get passed through to an underlying binary."""

  @abc.abstractmethod
  def AddToParser(self, parser):
    """Adds this argument to the given parser.

    Args:
      parser: The argparse parser.
    """
    pass

  @abc.abstractmethod
  def FormatFlags(self, args):
    """Return flags in a format that can be passed to the underlying binary."""
    pass


class StringFlag(BinaryCommandFlag):
  """A flag that takes a string value that is just passed directly through to the binary."""

  def __init__(self, name, **kwargs):
    super(StringFlag, self).__init__()
    self.arg = base.Argument(name, **kwargs)

  def AddToParser(self, parser):
    return self.arg.AddToParser(parser)

  def FormatFlags(self, args):
    dest_name = _GetDestNameForFlag(self.arg.name)
    if args.IsSpecified(dest_name):
      return [self.arg.name, getattr(args, dest_name)]
    return []


class BooleanFlag(BinaryCommandFlag):
  """Encapsulates a boolean flag that can be either --<flag> or --no-<flag>."""

  def __init__(self, name, **kwargs):
    super(BooleanFlag, self).__init__()
    self.arg = base.Argument(
        name, action=arg_parsers.StoreTrueFalseAction, **kwargs)

  def AddToParser(self, parser):
    return self.arg.AddToParser(parser)

  def FormatFlags(self, args):
    base_flag = self.arg.name.replace('--', '')
    bool_flags = [self.arg.name, '--no-' + base_flag]
    return [f for f in bool_flags if f in args.GetSpecifiedArgNames()]


class BasicFlag(BinaryCommandFlag):
  """Encapsulates a flag that is passed through as-is when present."""

  def __init__(self, name, **kwargs):
    super(BasicFlag, self).__init__()
    self.arg = base.Argument(name, default=False, action='store_true', **kwargs)

  def AddToParser(self, parser):
    return self.arg.AddToParser(parser)

  def FormatFlags(self, args):
    dest_name = _GetDestNameForFlag(self.arg.name)
    if args.IsSpecified(dest_name):
      return [self.arg.name]
    return []


class FlagGroup(BinaryCommandFlag):
  """Encapsulates multiple flags that can't function independently."""

  def __init__(self, first, second, *args):
    """Create a new flag group.

    At least two flags must be specified.

    Args:
      first: the first flag in the group
      second: the second flag in the group
      *args: additional flags in the group
    """
    super(FlagGroup, self).__init__()
    all_flags = [first, second]
    all_flags.extend(args)
    self._flags = all_flags

  def AddToParser(self, parser):
    for f in self._flags:
      f.AddToParser(parser)

  def FormatFlags(self, args):
    all_flags = []
    for f in self._flags:
      all_flags.extend(f.FormatFlags(args))
    return all_flags


def NamespaceFlag():
  return StringFlag('--namespace', help='Kubernetes namespace to operate in.')


class NamespaceFlagGroup(BinaryCommandFlag):
  """Encapsulates logic for handling the mutually-exclusive flags --namespace and --all-namespaces."""

  def AddToParser(self, parser):
    mutex_group = parser.add_mutually_exclusive_group()
    NamespaceFlag().AddToParser(mutex_group)
    mutex_group.add_argument(
        '--all-namespaces',
        default=False,
        action='store_true',
        help='List the requested object(s) across all namespaces.')

  def FormatFlags(self, args):
    if args.IsSpecified('all_namespaces'):
      return ['--all-namespaces']
    return NamespaceFlag().FormatFlags(args)


class ClusterConnectionFlags(BinaryCommandFlag):
  """Encapsulates logic for handling flags used for connecting to a cluster."""

  def AddToParser(self, parser):
    mutex_group = parser.add_mutually_exclusive_group()
    gke_group = mutex_group.add_group()
    gke_group.add_argument(
        '--cluster',
        required=True,
        help='ID of the cluster or fully qualified identifier for the cluster. '
        'If specified, then --cluster-location is required. Cannot be '
        'specified together with --context and --kubeconfig.')
    gke_group.add_argument(
        '--cluster-location',
        required=True,
        help='Zone in which the cluster is located. If specified, then --cluster '
        'is required. Cannot be specified together with --context and '
        '--kubeconfig.')
    cluster_group = mutex_group.add_group()
    cluster_group.add_argument(
        '--context',
        help='Name of the context in your kubectl config file to use for '
        'connecting. Cannot be specified together with --cluster and '
        '--cluster-location.')
    cluster_group.add_argument(
        '--kubeconfig',
        help='Absolute path to your kubectl config file. If not specified, '
        'the colon- or semicolon-delimited list of paths specified by '
        '$KUBECONFIG will be used. If $KUBECONFIG is unset, this defaults to '
        '~/.kube/config. Cannot be specified together with --cluster and '
        '--cluster-location.')

  def FormatFlags(self, args):
    exec_args = []
    if args.IsSpecified('cluster'):
      exec_args.extend(['--cluster', args.cluster])
    if args.IsSpecified('cluster_location'):
      exec_args.extend(['--cluster-location', args.cluster_location])
    if args.IsSpecified('kubeconfig'):
      exec_args.extend(['--kubeconfig', args.kubeconfig])
    if args.IsSpecified('context'):
      exec_args.extend(['--context', args.context])
    return exec_args


class TrafficFlags(BinaryCommandFlag):
  """Encapsulates flags to configure traffic routes to the service."""

  def AddToParser(self, parser):
    mutex_group = parser.add_mutually_exclusive_group(required=True)
    mutex_group.add_argument(
        '--to-latest',
        default=False,
        action='store_true',
        help='If true, assign 100 percent of traffic to the \'latest\' revision '
        'of this service. Note that when a new revision is created, it will '
        'become the \'latest\' and traffic will be directed to it. Defaults to '
        'False. Synonymous with `--to-revisions=LATEST=100`.')
    mutex_group.add_argument(
        '--to-revisions',
        help='Comma-separated list of traffic assignments in the form '
        'REVISION-NAME=PERCENTAGE. REVISION-NAME must be the name for a revision '
        'for the service as returned by \'gcloud kuberun clusters revisions list\'. '
        'PERCENTAGE must be an integer percentage between 0 and 100 inclusive. '
        'E.g. service-nw9hs=10,service-nw9hs=20 Up to 100 percent of traffic may '
        'be assigned. If 100 percent of traffic is assigned, the Service traffic '
        'is updated as specified. If under 100 percent of traffic is assigned, '
        'the Service traffic is updated as specified for revisions with '
        'assignments and traffic is scaled up or down down proportionally as '
        'needed for revision that are currently serving traffic but that do not '
        'have new assignments. For example assume revision-1 is serving 40 '
        'percent of traffic and revision-2 is serving 60 percent. If revision-1 '
        'is assigned 45 percent of traffic and no assignment is made for '
        'revision-2, the service is updated with revsion-1 assigned 45 percent '
        'of traffic and revision-2 scaled down to 55 percent. You can use '
        '"LATEST" as a special revision name to always put the given percentage '
        'of traffic on the latest ready revision.')

  def FormatFlags(self, args):
    if args.IsSpecified('to_latest'):
      return ['--to-latest']
    elif args.IsSpecified('to_revisions'):
      return ['--to-revisions', args.to_revisions]


def CommonServiceFlags(is_deploy=False):
  return FlagGroup(NamespaceFlag(), ImageFlag(required=is_deploy), CPUFlag(),
                   MemoryFlag(), PortFlag(), Http2Flag(), ConcurrencyFlag(),
                   EntrypointFlags(), ScalingFlags(), LabelsFlags(),
                   ConfigMapFlags(), SecretsFlags(), EnvVarsFlags(),
                   ConnectivityFlag(), ServiceAccountFlag(),
                   RevisionSuffixFlag(), TimeoutFlag())


def ImageFlag(required=False):
  return StringFlag(
      '--image',
      help='Name of the container image to deploy (e.g. gcr.io/cloudrun/hello:latest).',
      required=required)


def CPUFlag():
  return StringFlag(
      '--cpu',
      help='CPU limit, in Kubernetes cpu units, for the resource. Ex: .5, 500m, 2.'
  )


def MemoryFlag():
  return StringFlag(
      '--memory', help='Memory limit for the resource. Ex: 1Gi, 512Mi.')


def PortFlag():
  return StringFlag(
      '--port',
      help='Container port to receive requests at. Also sets the $PORT environment variable. Must be a number between 1 and 65535, inclusive. To unset this field, pass the special value "default".'
  )


def Http2Flag():
  return BooleanFlag(
      '--use-http2',
      help='If true, uses HTTP/2 for connections to the service.')


def ConcurrencyFlag():
  return StringFlag(
      '--concurrency',
      help='Maximum number of concurrent requests allowed per container instance. If concurrency is unspecified, any number of concurrent requests are allowed. To unset this field, provide the special value "default".'
  )


def EntrypointFlags():
  """Encapsulate flags for customizing container command."""
  args_flag = StringFlag(
      '--args',
      help='Comma-separated arguments passed to the command run by the '
      "container image. If not specified and no '--command' is provided, the "
      "container image's default Cmd is used. Otherwise, if not specified, no "
      'arguments are passed. To reset this field to its default, pass an empty '
      'string.')
  command_flag = StringFlag(
      '--command',
      help='Entrypoint for the container image. If not specified, the '
      "container image's default Entrypoint is run. To reset this field to its "
      'default, pass an empty string.')
  return FlagGroup(args_flag, command_flag)


def ScalingFlags():
  min_instances_flag = StringFlag(
      '--min-instances',
      help='Minimum number of container instances of the Service to run '
      "or 'default' to remove any minimum.")
  max_instances_flag = StringFlag(
      '--max-instances',
      help='Maximum number of container instances of the Service to run. '
      "Use 'default' to unset the limit and use the platform default.")
  return FlagGroup(min_instances_flag, max_instances_flag)


class LabelsFlags(BinaryCommandFlag):
  """Encapsulates flags to configure label of the service."""

  def AddToParser(self, parser):
    # TODO(b/166474467): revisit if no-opt flags should be kept for deploy
    mutex_group = parser.add_mutually_exclusive_group()
    mutex_group.add_argument(
        '--clear-labels',
        default=False,
        action='store_true',
        help='If true, removes all labels. If --update-labels is also specified then '
        '--clear-labels is applied first.')
    mutex_group.add_argument(
        '--labels',
        help='List of label KEY=VALUE pairs to add. An alias to --update-labels.'
    )
    update_group = mutex_group.add_group()
    update_group.add_argument(
        '--remove-labels',
        help='List of label keys to remove. If a label does not exist it is '
        'silently ignored. If --update-labels is also specified then '
        '--remove-labels is applied first.')
    update_group.add_argument(
        '--update-labels',
        help='List of label KEY=VALUE pairs to update. If a label exists its '
        'value is modified, otherwise a new label is created.')

  def FormatFlags(self, args):
    command_flags = []
    if args.IsSpecified('clear_labels'):
      command_flags.append('--clear-labels')
    if args.IsSpecified('labels'):
      command_flags.extend(['--labels', args.labels])
    if args.IsSpecified('remove_labels'):
      command_flags.extend(['--remove-labels', args.remove_labels])
    if args.IsSpecified('update_labels'):
      command_flags.extend(['--update-labels', args.update_labels])
    return command_flags


class ConfigMapFlags(BinaryCommandFlag):
  """Encapsulates flags to configure config maps mounting."""

  def AddToParser(self, parser):
    # TODO(b/166474467): revisit if no-opt flags should be kept for deploy
    mutex_group = parser.add_mutually_exclusive_group(
        help='Config map to mount or provide as environment variables. '
        "Keys starting with a forward slash '/' are mount paths. All other keys "
        'correspond to environment variables. The values associated with each of '
        'these should be in the form CONFIG_MAP_NAME:KEY_IN_CONFIG_MAP; you may '
        'omit the key within the config map to specify a mount of all keys '
        'within the config map. For example: '
        '`--update-config-maps=/my/path=myconfig,ENV=otherconfig:key.json` '
        "will create a volume with config map 'myconfig' and mount that volume "
        "at '/my/path'. Because no config map key was specified, all keys in "
        "'myconfig' will be included. An environment variable named ENV will "
        "also be created whose value is the value of 'key.json' in 'otherconfig'."
    )
    mutex_group.add_argument(
        '--clear-config-maps',
        default=False,
        action='store_true',
        help='If true, removes all config-maps.')
    mutex_group.add_argument(
        '--set-config-maps',
        help='List of key-value pairs to set as config-maps. All existing '
        'config-maps will be removed first.')
    update_group = mutex_group.add_group(
        help='Only --update-config-maps and --remove-config-maps can be used '
        'together. If both are specified, --remove-config-maps will be applied '
        'first.')
    update_group.add_argument(
        '--remove-config-maps', help='List of config-maps to be removed.')
    update_group.add_argument(
        '--update-config-maps',
        help='List of key-value pairs to set as config-maps.')

  def FormatFlags(self, args):
    command_flags = []
    if args.IsSpecified('clear_config_maps'):
      command_flags.append('--clear-config-maps')
    if args.IsSpecified('set_config_maps'):
      command_flags.extend(['--set-config-maps', args.set_config_maps])
    if args.IsSpecified('remove_config_maps'):
      command_flags.extend(['--remove-config-maps', args.remove_config_maps])
    if args.IsSpecified('update_config_maps'):
      command_flags.extend(['--update-config-maps', args.update_config_maps])
    return command_flags


class SecretsFlags(BinaryCommandFlag):
  """Encapsulates flags to configure secrets mounting."""

  def AddToParser(self, parser):
    # TODO(b/166474467): revisit if no-opt flags should be kept for deploy
    mutex_group = parser.add_mutually_exclusive_group(
        help='Secrets to mount or provide as environment variables. Keys '
        "starting with a forward slash '/' are mount paths. All other keys "
        'correspond to environment variables. The values associated with each '
        'of these should be in the form SECRET_NAME:KEY_IN_SECRET; you may omit '
        'the key within the secret to specify a mount of all keys within the '
        'secret. For example: '
        '`--update-secrets=/my/path=mysecret,ENV=othersecret:key.json` will '
        "create a volume with secret 'mysecret' and mount that volume at "
        "'/my/path'. Because no secret key was specified, all keys in 'mysecret' "
        'will be included. An environment variable named ENV will also be '
        "created whose value is the value of 'key.json' in 'othersecret'.")
    mutex_group.add_argument(
        '--clear-secrets',
        default=False,
        action='store_true',
        help='Remove all secrets.')
    mutex_group.add_argument(
        '--set-secrets',
        help='List of key-value pairs to set as secrets. All '
        'existing secrets will be removed first.')
    update_group = mutex_group.add_group(
        help='Only --update-secrets and --remove-secrets can be used together. '
        'If both are specified, --remove-secrets will be applied first.')
    update_group.add_argument(
        '--remove-secrets', help='List of secrets to be removed.')
    update_group.add_argument(
        '--update-secrets', help='List of key-value pairs to set as secrets.')

  def FormatFlags(self, args):
    command_flags = []
    if args.IsSpecified('clear_secrets'):
      command_flags.append('--clear-secrets')
    if args.IsSpecified('set_secrets'):
      command_flags.extend(['--set-secrets', args.set_secrets])
    if args.IsSpecified('remove_secrets'):
      command_flags.extend(['--remove-secrets', args.remove_secrets])
    if args.IsSpecified('update_secrets'):
      command_flags.extend(['--update-secrets', args.update_secrets])
    return command_flags


class EnvVarsFlags(BinaryCommandFlag):
  """Encapsulates flags to configure environment variables."""

  def AddToParser(self, parser):
    mutex_group = parser.add_mutually_exclusive_group()
    mutex_group.add_argument(
        '--clear-env-vars',
        default=False,
        action='store_true',
        help='If true, removes all environment variables.')
    mutex_group.add_argument(
        '--set-env-vars',
        help='List of key-value pairs to set as environment '
        'variables. All existing environment variables will be removed first.')
    update_group = mutex_group.add_group(
        help='Only --update-env-vars and --remove-env-vars can be used together. '
        'If both are specified, --remove-env-vars will be applied first.')
    update_group.add_argument(
        '--remove-env-vars',
        help='List of environment variables to be removed.')
    update_group.add_argument(
        '--update-env-vars',
        help='List of key-value pairs to set as environment variables.')

  def FormatFlags(self, args):
    command_flags = []
    if args.IsSpecified('clear_env_vars'):
      command_flags.append('--clear-env-vars')
    if args.IsSpecified('set_env_vars'):
      command_flags.extend(['--set-env-vars', args.set_env_vars])
    if args.IsSpecified('remove_env_vars'):
      command_flags.extend(['--remove-env-vars', args.remove_env_vars])
    if args.IsSpecified('update_env_vars'):
      command_flags.extend(['--update-env-vars', args.update_env_vars])
    return command_flags


def ConnectivityFlag():
  return StringFlag(
      '--connectivity',
      help='Defaults to \'external\'. If \'external\', the service can be '
      'invoked through the internet, in addition to through the cluster '
      'network.',
      choices=_VISIBILITY_MODES)


def NoTrafficFlag():
  return BasicFlag(
      '--no-traffic',
      help='If set, any traffic assigned to the LATEST revision will be '
      'assigned to the specific revision bound to LATEST before the '
      'deployment. This means the revision being deployed will not receive '
      'traffic. After a deployment with this flag, the LATEST revision will '
      'not receive traffic on future deployments.')


def ServiceAccountFlag():
  return StringFlag(
      '--service-account',
      help='Service account associated with the revision of the service. '
      'The service account represents the identity of the running revision, '
      'and determines what permissions the revision has. This is the name of '
      'a Kubernetes service account in the same namespace as the service. '
      'If not provided, the revision will use the default Kubernetes '
      'namespace service account.')


def TimeoutFlag():
  return StringFlag(
      '--timeout',
      help='Maximum request execution time (timeout). It is specified '
      'as a duration; for example, "10m5s" is ten minutes and five seconds. '
      'If you don\'t specify a unit, seconds is assumed. For example, "10" is '
      '10 seconds.',
      type=arg_parsers.Duration(lower_bound='1s', parsed_unit='s'))


def RevisionSuffixFlag():
  return StringFlag(
      '--revision-suffix',
      help='Suffix of the revision name. Revision names always start with the '
      'service name automatically. For example, specifying '
      "`--revision-suffix=v1` for a service named 'helloworld', would lead "
      "to a revision named 'helloworld-v1'.")


def AsyncFlag():
  return BasicFlag(
      '--async',
      help='Return immediately, without waiting for the operation in progress '
      'to complete.')


def RegisterFlags(parser, flags):
  """Helper to register a set of flags with a given parser."""
  for f in flags:
    f.AddToParser(parser)


def _GetDestNameForFlag(flag):
  return flag.replace('--', '').replace('-', '_')