1362 lines
36 KiB
Python
1362 lines
36 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2021 Google LLC. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Helpers for flags in commands working with GKE Multi-cloud."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import textwrap
|
|
|
|
from googlecloudsdk.api_lib.container.gkemulticloud import util as api_util
|
|
from googlecloudsdk.calliope import arg_parsers
|
|
from googlecloudsdk.command_lib.container.gkemulticloud import constants
|
|
from googlecloudsdk.command_lib.projects import util as project_util
|
|
from googlecloudsdk.command_lib.util.apis import arg_utils
|
|
from googlecloudsdk.core import properties
|
|
|
|
|
|
def _ToCamelCase(name):
|
|
"""Converts hyphen-case name to CamelCase."""
|
|
parts = name.split('-')
|
|
return ''.join(x.title() for x in parts)
|
|
|
|
|
|
def _ToSnakeCaseUpper(name):
|
|
"""Converts hyphen-case name to SNAKE_CASE."""
|
|
parts = name.split('-')
|
|
return '_'.join(parts).upper()
|
|
|
|
|
|
def _ToHyphenCase(name):
|
|
"""Converts SNAKE_CASE to hyphen-case."""
|
|
parts = name.split('_')
|
|
return '-'.join(parts).lower()
|
|
|
|
|
|
def _InvalidValueError(value, flag, detail):
|
|
return arg_parsers.ArgumentTypeError(
|
|
'Invalid value [{}] for argument {}. {}'.format(value, flag, detail)
|
|
)
|
|
|
|
|
|
_TAINT_EFFECT_ENUM_MAPPER = arg_utils.ChoiceEnumMapper(
|
|
'--node-taints',
|
|
api_util.GetMessagesModule().GoogleCloudGkemulticloudV1NodeTaint.EffectValueValuesEnum,
|
|
include_filter=lambda effect: 'UNSPECIFIED' not in effect,
|
|
)
|
|
|
|
_TAINT_FORMAT_HELP = 'Node taint is of format key=value:effect.'
|
|
|
|
_TAINT_EFFECT_HELP = 'Effect must be one of: {}.'.format(
|
|
', '.join([_ToCamelCase(e) for e in _TAINT_EFFECT_ENUM_MAPPER.choices])
|
|
)
|
|
|
|
_REPLICAPLACEMENT_FORMAT_HELP = (
|
|
'Replica placement is of format subnetid:zone, for example subnetid12345:1'
|
|
)
|
|
|
|
_LOGGING_CHOICES = [constants.SYSTEM, constants.WORKLOAD]
|
|
|
|
_ALLOW_DISABLE_LOGGING_CHOICES = [
|
|
constants.NONE,
|
|
constants.SYSTEM,
|
|
constants.WORKLOAD,
|
|
]
|
|
|
|
_BINAUTHZ_EVAL_MODE_ENUM_MAPPER = arg_utils.ChoiceEnumMapper(
|
|
'--binauthz-evaluation-mode',
|
|
api_util.GetMessagesModule().GoogleCloudGkemulticloudV1BinaryAuthorization.EvaluationModeValueValuesEnum,
|
|
include_filter=lambda mode: 'UNSPECIFIED' not in mode,
|
|
)
|
|
|
|
|
|
def AddPodAddressCidrBlocks(parser):
|
|
"""Adds the --pod-address-cidr-blocks flag."""
|
|
parser.add_argument(
|
|
'--pod-address-cidr-blocks',
|
|
required=True,
|
|
help=(
|
|
'IP address range for the pods in this cluster in CIDR '
|
|
'notation (e.g. 10.0.0.0/8).'
|
|
),
|
|
)
|
|
|
|
|
|
def GetPodAddressCidrBlocks(args):
|
|
"""Gets the value of --pod-address-cidr-blocks flag."""
|
|
cidr_blocks = getattr(args, 'pod_address_cidr_blocks', None)
|
|
return [cidr_blocks] if cidr_blocks else []
|
|
|
|
|
|
def AddServiceAddressCidrBlocks(parser):
|
|
"""Add the --service-address-cidr-blocks flag."""
|
|
parser.add_argument(
|
|
'--service-address-cidr-blocks',
|
|
required=True,
|
|
help=(
|
|
'IP address range for the services IPs in CIDR notation '
|
|
'(e.g. 10.0.0.0/8).'
|
|
),
|
|
)
|
|
|
|
|
|
def GetServiceAddressCidrBlocks(args):
|
|
"""Gets the value of --service-address-cidr-blocks flag."""
|
|
cidr_blocks = getattr(args, 'service_address_cidr_blocks', None)
|
|
return [cidr_blocks] if cidr_blocks else []
|
|
|
|
|
|
def AddSubnetID(parser, help_text, required=True):
|
|
"""Add the --subnet-id flag."""
|
|
parser.add_argument(
|
|
'--subnet-id',
|
|
required=required,
|
|
help='Subnet ID of an existing VNET to use for {}.'.format(help_text),
|
|
)
|
|
|
|
|
|
def GetSubnetID(args):
|
|
return getattr(args, 'subnet_id', None)
|
|
|
|
|
|
def AddOutputFile(parser, help_action):
|
|
"""Add an output file argument.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the output file argument to.
|
|
help_action: str, describes the action of what will be stored.
|
|
"""
|
|
parser.add_argument(
|
|
'--output-file', help='Path to the output file {}.'.format(help_action)
|
|
)
|
|
|
|
|
|
def AddValidateOnly(parser, help_action):
|
|
"""Add the --validate-only argument.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the argument to.
|
|
help_action: str, describes the action that will be validated.
|
|
"""
|
|
parser.add_argument(
|
|
'--validate-only',
|
|
action='store_true',
|
|
help="Validate the {}, but don't actually perform it.".format(
|
|
help_action
|
|
),
|
|
)
|
|
|
|
|
|
def GetValidateOnly(args):
|
|
return getattr(args, 'validate_only', None)
|
|
|
|
|
|
def AddEnableAutoRepair(parser, for_create=False):
|
|
help_text = """\
|
|
Enable node autorepair feature for a node pool. Use --no-enable-autorepair to disable.
|
|
|
|
$ {command} --enable-autorepair
|
|
"""
|
|
if for_create:
|
|
help_text += """
|
|
Node autorepair is disabled by default.
|
|
"""
|
|
parser.add_argument(
|
|
'--enable-autorepair', action='store_true', default=None, help=help_text
|
|
)
|
|
|
|
|
|
def GetAutoRepair(args):
|
|
return getattr(args, 'enable_autorepair', None)
|
|
|
|
|
|
def AddClusterVersion(parser, required=True):
|
|
parser.add_argument(
|
|
'--cluster-version',
|
|
required=required,
|
|
help='Kubernetes version to use for the cluster.',
|
|
)
|
|
|
|
|
|
def GetClusterVersion(args):
|
|
return getattr(args, 'cluster_version', None)
|
|
|
|
|
|
def AddDescription(parser, required=False):
|
|
parser.add_argument(
|
|
'--description', required=required, help='Description for the cluster.'
|
|
)
|
|
|
|
|
|
def GetDescription(args):
|
|
return getattr(args, 'description', None)
|
|
|
|
|
|
def AddClearDescription(parser):
|
|
"""Adds the --clear-description flag.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
parser.add_argument(
|
|
'--clear-description',
|
|
action='store_true',
|
|
default=None,
|
|
help='Clear the description for the cluster.',
|
|
)
|
|
|
|
|
|
def AddDescriptionForUpdate(parser):
|
|
"""Adds description related flags for update.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
group = parser.add_group('Description', mutex=True)
|
|
AddDescription(group)
|
|
AddClearDescription(group)
|
|
|
|
|
|
def AddAnnotations(parser, noun='cluster'):
|
|
parser.add_argument(
|
|
'--annotations',
|
|
type=arg_parsers.ArgDict(min_length=1),
|
|
metavar='ANNOTATION',
|
|
help='Annotations for the {}.'.format(noun),
|
|
)
|
|
|
|
|
|
def AddClearAnnotations(parser, noun):
|
|
"""Adds flag for clearing the annotations.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
noun: The resource type to which the flag is applicable.
|
|
"""
|
|
parser.add_argument(
|
|
'--clear-annotations',
|
|
action='store_true',
|
|
default=None,
|
|
help='Clear the annotations for the {}.'.format(noun),
|
|
)
|
|
|
|
|
|
def GetAnnotations(args):
|
|
return getattr(args, 'annotations', None) or {}
|
|
|
|
|
|
def AddAnnotationsForUpdate(parser, noun):
|
|
"""Adds annotations related flags for update.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
noun: The resource type to which the flag is applicable.
|
|
"""
|
|
group = parser.add_group('Annotations', mutex=True)
|
|
AddAnnotations(group, noun)
|
|
AddClearAnnotations(group, noun)
|
|
|
|
|
|
def AddNodeVersion(parser, required=True):
|
|
parser.add_argument(
|
|
'--node-version',
|
|
required=required,
|
|
help='Kubernetes version to use for the node pool.',
|
|
)
|
|
|
|
|
|
def GetNodeVersion(args):
|
|
return getattr(args, 'node_version', None)
|
|
|
|
|
|
def AddAutoscaling(parser, required=True):
|
|
"""Adds node pool autoscaling flags.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
required: bool, whether autoscaling flags are required.
|
|
"""
|
|
|
|
group = parser.add_argument_group('Node pool autoscaling', required=required)
|
|
group.add_argument(
|
|
'--min-nodes',
|
|
required=required,
|
|
type=int,
|
|
help='Minimum number of nodes in the node pool.',
|
|
)
|
|
group.add_argument(
|
|
'--max-nodes',
|
|
required=required,
|
|
type=int,
|
|
help='Maximum number of nodes in the node pool.',
|
|
)
|
|
|
|
|
|
def GetAutoscalingParams(args):
|
|
min_nodes = 0
|
|
max_nodes = 0
|
|
min_nodes = args.min_nodes
|
|
max_nodes = args.max_nodes
|
|
|
|
return (min_nodes, max_nodes)
|
|
|
|
|
|
def GetMinNodes(args):
|
|
return getattr(args, 'min_nodes', None)
|
|
|
|
|
|
def GetMaxNodes(args):
|
|
return getattr(args, 'max_nodes', None)
|
|
|
|
|
|
def AddMaxPodsPerNode(parser):
|
|
parser.add_argument(
|
|
'--max-pods-per-node',
|
|
type=int,
|
|
help='Maximum number of pods per node.',
|
|
required=True,
|
|
)
|
|
|
|
|
|
def GetMaxPodsPerNode(args):
|
|
return getattr(args, 'max_pods_per_node', None)
|
|
|
|
|
|
def AddAzureAvailabilityZone(parser):
|
|
parser.add_argument(
|
|
'--azure-availability-zone',
|
|
help='Azure availability zone where the node pool will be created.',
|
|
)
|
|
|
|
|
|
def GetAzureAvailabilityZone(args):
|
|
return getattr(args, 'azure_availability_zone', None)
|
|
|
|
|
|
def AddVMSize(parser):
|
|
parser.add_argument(
|
|
'--vm-size', help='Azure Virtual Machine Size (e.g. Standard_DS1_v).'
|
|
)
|
|
|
|
|
|
def GetVMSize(args):
|
|
return getattr(args, 'vm_size', None)
|
|
|
|
|
|
def AddSSHPublicKey(parser, required=True):
|
|
parser.add_argument(
|
|
'--ssh-public-key',
|
|
required=required,
|
|
help='SSH public key to use for authentication.',
|
|
)
|
|
|
|
|
|
def GetSSHPublicKey(args):
|
|
return getattr(args, 'ssh_public_key', None)
|
|
|
|
|
|
def AddRootVolumeSize(parser):
|
|
parser.add_argument(
|
|
'--root-volume-size',
|
|
type=arg_parsers.BinarySize(
|
|
suggested_binary_size_scales=['GB', 'GiB', 'TB', 'TiB'],
|
|
default_unit='Gi',
|
|
),
|
|
help=(
|
|
'Size of the root volume. The value must be a whole number followed'
|
|
' by a size unit of `GB` for gigabyte, or `TB` for terabyte. If no'
|
|
' size unit is specified, GB is assumed.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetRootVolumeSize(args):
|
|
size = getattr(args, 'root_volume_size', None)
|
|
if not size:
|
|
return None
|
|
|
|
# Volume sizes are currently in GB, argument is in B.
|
|
return int(size) >> 30
|
|
|
|
|
|
def AddMainVolumeSize(parser):
|
|
parser.add_argument(
|
|
'--main-volume-size',
|
|
type=arg_parsers.BinarySize(
|
|
suggested_binary_size_scales=['GB', 'GiB', 'TB', 'TiB'],
|
|
default_unit='Gi',
|
|
),
|
|
help=(
|
|
'Size of the main volume. The value must be a whole number followed'
|
|
' by a size unit of `GB` for gigabyte, or `TB` for terabyte. If no'
|
|
' size unit is specified, GB is assumed.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetMainVolumeSize(args):
|
|
size = getattr(args, 'main_volume_size', None)
|
|
if not size:
|
|
return None
|
|
|
|
# Volume sizes are currently in GB, argument is in B.
|
|
return int(size) >> 30
|
|
|
|
|
|
def AddTags(parser, noun):
|
|
help_text = """\
|
|
Applies the given tags (comma separated) on the {0}. Example:
|
|
|
|
$ {{command}} EXAMPLE_{1} --tags=tag1=one,tag2=two
|
|
""".format(noun, noun.replace(' ', '_').upper())
|
|
|
|
parser.add_argument(
|
|
'--tags',
|
|
type=arg_parsers.ArgDict(min_length=1),
|
|
metavar='TAG',
|
|
help=help_text,
|
|
)
|
|
|
|
|
|
def AddClearTags(parser, noun):
|
|
"""Adds flag for clearing the tags.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
noun: The resource type to which the flag is applicable.
|
|
"""
|
|
|
|
parser.add_argument(
|
|
'--clear-tags',
|
|
action='store_true',
|
|
default=None,
|
|
help="Clear any tags associated with the {}'s nodes. ".format(noun),
|
|
)
|
|
|
|
|
|
def AddTagsForUpdate(parser, noun):
|
|
"""Adds tags related flags for update.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
noun: The resource type to which the flags are applicable.
|
|
"""
|
|
group = parser.add_group('Tags', mutex=True)
|
|
AddTags(group, noun)
|
|
AddClearTags(group, noun)
|
|
|
|
|
|
def GetTags(args):
|
|
return getattr(args, 'tags', None) or {}
|
|
|
|
|
|
def AddDatabaseEncryption(parser):
|
|
"""Adds database encryption flags.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
parser.add_argument(
|
|
'--database-encryption-key-id',
|
|
help=(
|
|
'URL the of the Azure Key Vault key (with its version) '
|
|
'to use to encrypt / decrypt cluster secrets.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetDatabaseEncryptionKeyId(args):
|
|
return getattr(args, 'database_encryption_key_id', None)
|
|
|
|
|
|
def AddConfigEncryption(parser):
|
|
"""Adds config encryption flags.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
parser.add_argument(
|
|
'--config-encryption-key-id',
|
|
help=(
|
|
'URL the of the Azure Key Vault key (with its version) '
|
|
'to use to encrypt / decrypt config data.'
|
|
),
|
|
)
|
|
parser.add_argument(
|
|
'--config-encryption-public-key',
|
|
help=(
|
|
'RSA key of the Azure Key Vault public key to use for encrypting '
|
|
'config data.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetConfigEncryptionKeyId(args):
|
|
return getattr(args, 'config_encryption_key_id', None)
|
|
|
|
|
|
def GetConfigEncryptionPublicKey(args):
|
|
return getattr(args, 'config_encryption_public_key', None)
|
|
|
|
|
|
def AddNodeLabels(parser):
|
|
"""Adds the --node-labels flag."""
|
|
parser.add_argument(
|
|
'--node-labels',
|
|
type=arg_parsers.ArgDict(min_length=1),
|
|
metavar='NODE_LABEL',
|
|
help="Labels assigned to the node pool's nodes.",
|
|
)
|
|
|
|
|
|
def GetNodeLabels(args):
|
|
return getattr(args, 'node_labels', None) or {}
|
|
|
|
|
|
def AddClearNodeLabels(parser):
|
|
"""Adds the --clear-node-labels flag."""
|
|
parser.add_argument(
|
|
'--clear-node-labels',
|
|
action='store_true',
|
|
default=None,
|
|
help="Clear the labels assigned to the node pool's nodes.",
|
|
)
|
|
|
|
|
|
def AddNodeLabelsForUpdate(parser):
|
|
"""Adds node labels related flags for update."""
|
|
group = parser.add_group('Node labels', mutex=True)
|
|
AddNodeLabels(group)
|
|
AddClearNodeLabels(group)
|
|
|
|
|
|
def _ValidateNodeTaintFormat(taint):
|
|
"""Validates the node taint format.
|
|
|
|
Node taint is of format key=value:effect.
|
|
|
|
Args:
|
|
taint: Node taint.
|
|
|
|
Returns:
|
|
The node taint value and effect if the format is valid.
|
|
|
|
Raises:
|
|
ArgumentError: If the node taint format is invalid.
|
|
"""
|
|
strs = taint.split(':')
|
|
if len(strs) != 2:
|
|
raise _InvalidValueError(taint, '--node-taints', _TAINT_FORMAT_HELP)
|
|
value, effect = strs[0], strs[1]
|
|
return value, effect
|
|
|
|
|
|
def _ValidateNodeTaint(taint):
|
|
"""Validates the node taint.
|
|
|
|
Node taint is of format key=value:effect. Valid values for effect include
|
|
NoExecute, NoSchedule, PreferNoSchedule.
|
|
|
|
Args:
|
|
taint: Node taint.
|
|
|
|
Returns:
|
|
The node taint if it is valid.
|
|
|
|
Raises:
|
|
ArgumentError: If the node taint is invalid.
|
|
"""
|
|
unused_value, effect = _ValidateNodeTaintFormat(taint)
|
|
effects = [_ToCamelCase(e) for e in _TAINT_EFFECT_ENUM_MAPPER.choices]
|
|
if effect not in effects:
|
|
raise _InvalidValueError(effect, '--node-taints', _TAINT_EFFECT_HELP)
|
|
return taint
|
|
|
|
|
|
def AddNodeTaints(parser):
|
|
parser.add_argument(
|
|
'--node-taints',
|
|
type=arg_parsers.ArgDict(min_length=1, value_type=_ValidateNodeTaint),
|
|
metavar='NODE_TAINT',
|
|
help=(
|
|
'Taints assigned to nodes of the node pool. {} {}'.format(
|
|
_TAINT_FORMAT_HELP, _TAINT_EFFECT_HELP
|
|
)
|
|
),
|
|
)
|
|
|
|
|
|
def GetNodeTaints(args):
|
|
"""Gets node taint objects from the arguments.
|
|
|
|
Args:
|
|
args: Arguments parsed from the command.
|
|
|
|
Returns:
|
|
The list of node taint objects.
|
|
|
|
Raises:
|
|
ArgumentError: If the node taint format is invalid.
|
|
"""
|
|
taints = []
|
|
taint_effect_map = {
|
|
_ToCamelCase(e): e for e in _TAINT_EFFECT_ENUM_MAPPER.choices
|
|
}
|
|
node_taints = getattr(args, 'node_taints', None)
|
|
if node_taints:
|
|
for k, v in node_taints.items():
|
|
value, effect = _ValidateNodeTaintFormat(v)
|
|
effect = taint_effect_map[effect]
|
|
effect = _TAINT_EFFECT_ENUM_MAPPER.GetEnumForChoice(effect)
|
|
taint = api_util.GetMessagesModule().GoogleCloudGkemulticloudV1NodeTaint(
|
|
key=k, value=value, effect=effect
|
|
)
|
|
taints.append(taint)
|
|
return taints
|
|
|
|
|
|
def _ReplicaPlacementStrToObject(replicaplacement):
|
|
"""Converts a colon-delimited string to a GoogleCloudGkemulticloudV1ReplicaPlacement instance.
|
|
|
|
Replica placement is of format subnetid:zone.
|
|
|
|
Args:
|
|
replicaplacement: Replica placement.
|
|
|
|
Returns:
|
|
A GoogleCloudGkemulticloudV1ReplicaPlacement instance.
|
|
|
|
Raises:
|
|
ArgumentError: If the Replica placement format is invalid.
|
|
"""
|
|
strs = replicaplacement.split(':')
|
|
if len(strs) != 2:
|
|
raise _InvalidValueError(
|
|
replicaplacement, '--replica-placements', _REPLICAPLACEMENT_FORMAT_HELP
|
|
)
|
|
subnetid, zone = strs[0], strs[1]
|
|
return (
|
|
api_util.GetMessagesModule().GoogleCloudGkemulticloudV1ReplicaPlacement(
|
|
azureAvailabilityZone=zone, subnetId=subnetid
|
|
)
|
|
)
|
|
|
|
|
|
def AddReplicaPlacements(parser):
|
|
parser.add_argument(
|
|
'--replica-placements',
|
|
type=arg_parsers.ArgList(element_type=_ReplicaPlacementStrToObject),
|
|
metavar='REPLICA_PLACEMENT',
|
|
help=(
|
|
'Placement info for the control plane replicas. {}'.format(
|
|
_REPLICAPLACEMENT_FORMAT_HELP
|
|
)
|
|
),
|
|
)
|
|
|
|
|
|
def GetReplicaPlacements(args):
|
|
replica_placements = getattr(args, 'replica_placements', None)
|
|
return replica_placements if replica_placements else []
|
|
|
|
|
|
def AddAuthProviderCmdPath(parser):
|
|
parser.add_argument(
|
|
'--auth-provider-cmd-path',
|
|
hidden=True,
|
|
help='Path to the executable for the auth provider field in kubeconfig.',
|
|
)
|
|
|
|
|
|
def AddProxyConfig(parser):
|
|
"""Add proxy configuration flags.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
|
|
group = parser.add_argument_group('Proxy config')
|
|
group.add_argument(
|
|
'--proxy-resource-group-id',
|
|
required=True,
|
|
help='The ARM ID the of the resource group containing proxy keyvault.',
|
|
)
|
|
group.add_argument(
|
|
'--proxy-secret-id',
|
|
required=True,
|
|
help='The URL the of the proxy setting secret with its version.',
|
|
)
|
|
|
|
|
|
def GetProxyResourceGroupId(args):
|
|
return getattr(args, 'proxy_resource_group_id', None)
|
|
|
|
|
|
def GetProxySecretId(args):
|
|
return getattr(args, 'proxy_secret_id', None)
|
|
|
|
|
|
def AddFleetProject(parser):
|
|
parser.add_argument(
|
|
'--fleet-project',
|
|
type=arg_parsers.CustomFunctionValidator(
|
|
project_util.ValidateProjectIdentifier,
|
|
'--fleet-project must be a valid project ID or project number.',
|
|
),
|
|
required=True,
|
|
help=(
|
|
'ID or number of the Fleet host project where the cluster is'
|
|
' registered.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetFleetProject(args):
|
|
"""Gets and parses the fleet project argument.
|
|
|
|
Project ID if specified is converted to project number. The parsed fleet
|
|
project has format projects/<project-number>.
|
|
|
|
Args:
|
|
args: Arguments parsed from the command.
|
|
|
|
Returns:
|
|
The fleet project in format projects/<project-number>
|
|
or None if the fleet projectnot is not specified.
|
|
"""
|
|
p = getattr(args, 'fleet_project', None)
|
|
if not p:
|
|
return None
|
|
if not p.isdigit():
|
|
return 'projects/{}'.format(project_util.GetProjectNumber(p))
|
|
return 'projects/{}'.format(p)
|
|
|
|
|
|
def AddPrivateEndpoint(parser):
|
|
parser.add_argument(
|
|
'--private-endpoint',
|
|
default=False,
|
|
action='store_true',
|
|
help='If set, use private VPC for authentication.',
|
|
)
|
|
|
|
|
|
def AddExecCredential(parser):
|
|
parser.add_argument(
|
|
'--exec-credential',
|
|
default=False,
|
|
action='store_true',
|
|
help='If set, format access token as a Kubernetes execCredential object.',
|
|
)
|
|
|
|
|
|
def AddAdminUsers(parser, create=True):
|
|
help_txt = 'Users that can perform operations as a cluster administrator.'
|
|
if create:
|
|
help_txt += ' If not specified, the value of property core/account is used.'
|
|
parser.add_argument(
|
|
'--admin-users',
|
|
type=arg_parsers.ArgList(min_length=1),
|
|
metavar='USER',
|
|
help=help_txt,
|
|
)
|
|
|
|
|
|
def GetAdminUsers(args):
|
|
if not hasattr(args, 'admin_users'):
|
|
return None
|
|
if args.admin_users:
|
|
return args.admin_users
|
|
# Default to core/account property if not specified.
|
|
return [properties.VALUES.core.account.GetOrFail()]
|
|
|
|
|
|
def AddAdminGroupsForUpdate(parser):
|
|
"""Adds admin group configuration flags for update.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
|
|
group = parser.add_group('Admin groups', mutex=True)
|
|
AddAdminGroups(group)
|
|
AddClearAdminGroups(group)
|
|
|
|
|
|
def AddAdminGroups(parser):
|
|
help_txt = """
|
|
Groups of users that can perform operations as a cluster administrator.
|
|
"""
|
|
|
|
parser.add_argument(
|
|
'--admin-groups',
|
|
type=arg_parsers.ArgList(),
|
|
metavar='GROUP',
|
|
required=False,
|
|
help=help_txt,
|
|
)
|
|
|
|
|
|
def AddClearAdminGroups(parser):
|
|
"""Adds the --clear-admin-groups.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
parser.add_argument(
|
|
'--clear-admin-groups',
|
|
action='store_true',
|
|
default=None,
|
|
help='Clear the admin groups associated with the cluster',
|
|
)
|
|
|
|
|
|
def GetAdminGroups(args):
|
|
if not hasattr(args, 'admin_groups'):
|
|
return None
|
|
if args.admin_groups:
|
|
return args.admin_groups
|
|
return None
|
|
|
|
|
|
def AddLogging(parser, allow_disabled=False):
|
|
"""Adds the --logging flag."""
|
|
help_text = """
|
|
Set the components that have logging enabled.
|
|
|
|
Examples:
|
|
|
|
$ {command} --logging=SYSTEM
|
|
$ {command} --logging=SYSTEM,WORKLOAD"""
|
|
|
|
logging_choices = []
|
|
if allow_disabled:
|
|
logging_choices = _ALLOW_DISABLE_LOGGING_CHOICES
|
|
help_text += """
|
|
$ {command} --logging=NONE
|
|
"""
|
|
else:
|
|
logging_choices = _LOGGING_CHOICES
|
|
|
|
parser.add_argument(
|
|
'--logging',
|
|
type=arg_parsers.ArgList(min_length=1, choices=logging_choices),
|
|
metavar='COMPONENT',
|
|
help=help_text,
|
|
)
|
|
|
|
|
|
def GetLogging(args, allow_disabled=False):
|
|
"""Parses and validates the value of the --logging flag.
|
|
|
|
Args:
|
|
args: Arguments parsed from the command.
|
|
allow_disabled: If disabling logging is allowed for this cluster.
|
|
|
|
Returns:
|
|
The logging config object as GoogleCloudGkemulticloudV1LoggingConfig.
|
|
|
|
Raises:
|
|
ArgumentError: If the value of the --logging flag is invalid.
|
|
"""
|
|
logging = getattr(args, 'logging', None)
|
|
if not logging:
|
|
return None
|
|
|
|
if constants.NONE in logging and (
|
|
constants.SYSTEM in logging or constants.WORKLOAD in logging
|
|
):
|
|
raise _InvalidValueError(
|
|
','.join(logging),
|
|
'--logging',
|
|
'Invalid logging config. NONE is not supported with SYSTEM or'
|
|
' WORKLOAD.',
|
|
)
|
|
|
|
messages = api_util.GetMessagesModule()
|
|
config = messages.GoogleCloudGkemulticloudV1LoggingComponentConfig()
|
|
enum = config.EnableComponentsValueListEntryValuesEnum
|
|
|
|
if constants.NONE in logging:
|
|
if allow_disabled:
|
|
return messages.GoogleCloudGkemulticloudV1LoggingConfig(
|
|
componentConfig=config
|
|
)
|
|
else:
|
|
raise _InvalidValueError(
|
|
','.join(logging),
|
|
'--logging',
|
|
'Invalid logging config. NONE is not supported.',
|
|
)
|
|
|
|
if constants.SYSTEM not in logging:
|
|
raise _InvalidValueError(
|
|
','.join(logging),
|
|
'--logging',
|
|
'Must include SYSTEM logging if any logging is enabled.',
|
|
)
|
|
if constants.SYSTEM in logging:
|
|
config.enableComponents.append(enum.SYSTEM_COMPONENTS)
|
|
if constants.WORKLOAD in logging:
|
|
config.enableComponents.append(enum.WORKLOADS)
|
|
return messages.GoogleCloudGkemulticloudV1LoggingConfig(
|
|
componentConfig=config
|
|
)
|
|
|
|
|
|
def AddImageType(parser):
|
|
"""Adds the --image-type flag."""
|
|
help_text = """
|
|
Set the OS image type to use on node pool instances.
|
|
|
|
Examples:
|
|
|
|
$ {command} --image-type=windows
|
|
$ {command} --image-type=ubuntu
|
|
"""
|
|
parser.add_argument('--image-type', help=help_text)
|
|
|
|
|
|
def GetImageType(args):
|
|
return getattr(args, 'image_type', None)
|
|
|
|
|
|
def AddAzureRegion(parser):
|
|
parser.add_argument(
|
|
'--azure-region',
|
|
required=True,
|
|
help=(
|
|
'Azure location to deploy the cluster. '
|
|
'Refer to your Azure subscription for available locations.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetAzureRegion(args):
|
|
return getattr(args, 'azure_region', None)
|
|
|
|
|
|
def AddResourceGroupId(parser):
|
|
parser.add_argument(
|
|
'--resource-group-id',
|
|
required=True,
|
|
help='ID of the Azure Resource Group to associate the cluster with.',
|
|
)
|
|
|
|
|
|
def GetResourceGroupId(args):
|
|
return getattr(args, 'resource_group_id', None)
|
|
|
|
|
|
def AddVnetId(parser):
|
|
parser.add_argument(
|
|
'--vnet-id',
|
|
required=True,
|
|
help='ID of the Azure Virtual Network to associate with the cluster.',
|
|
)
|
|
|
|
|
|
def GetVnetId(args):
|
|
return getattr(args, 'vnet_id', None)
|
|
|
|
|
|
def AddServiceLoadBalancerSubnetId(parser):
|
|
parser.add_argument(
|
|
'--service-load-balancer-subnet-id',
|
|
help=(
|
|
'ARM ID of the subnet where Kubernetes private service type '
|
|
'load balancers are deployed, when the Service lacks a subnet '
|
|
'annotation.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetServiceLoadBalancerSubnetId(args):
|
|
return getattr(args, 'service_load_balancer_subnet_id', None)
|
|
|
|
|
|
def AddEndpointSubnetId(parser):
|
|
parser.add_argument(
|
|
'--endpoint-subnet-id',
|
|
help=(
|
|
'ARM ID of the subnet where the control plane load balancer '
|
|
'is deployed. When unspecified, it defaults to the control '
|
|
'plane subnet ID.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetEndpointSubnetId(args):
|
|
return getattr(args, 'endpoint_subnet_id', None)
|
|
|
|
|
|
def AddAzureServicesAuthentication(auth_config_group, create=True):
|
|
"""Adds --azure-tenant-id and --azure-application-id flags."""
|
|
group = auth_config_group.add_argument_group('Azure services authentication')
|
|
group.add_argument(
|
|
'--azure-tenant-id',
|
|
required=create,
|
|
help='ID of the Azure Tenant to manage Azure resources.',
|
|
)
|
|
group.add_argument(
|
|
'--azure-application-id',
|
|
required=create,
|
|
help='ID of the Azure Application to manage Azure resources.',
|
|
)
|
|
if not create:
|
|
AddClearClient(group)
|
|
|
|
|
|
def AddClearClient(parser):
|
|
"""Adds the --clear-client flag.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
parser.add_argument(
|
|
'--clear-client',
|
|
action='store_true',
|
|
default=None,
|
|
help=(
|
|
'Clear the Azure client. This flag is required when updating to use '
|
|
'Azure workload identity federation from Azure client to manage '
|
|
' Azure resources.'
|
|
),
|
|
)
|
|
|
|
|
|
def GetAzureTenantID(args):
|
|
return getattr(args, 'azure_tenant_id', None)
|
|
|
|
|
|
def GetAzureApplicationID(args):
|
|
return getattr(args, 'azure_application_id', None)
|
|
|
|
|
|
def AddMonitoringConfig(
|
|
parser, for_create=False, cloud_monitoring_option=False
|
|
):
|
|
"""Adds monitoring config flags to parser."""
|
|
prometheus_enable_help_text = """
|
|
Enables managed collection for Managed Service for Prometheus in the cluster.
|
|
|
|
See https://cloud.google.com/stackdriver/docs/managed-prometheus/setup-managed#enable-mgdcoll-gke
|
|
for more info.
|
|
|
|
Managed Prometheus is enabled by default for cluster versions 1.27 or greater,
|
|
use --no-enable-managed-prometheus to disable.
|
|
"""
|
|
cloud_monitoring_enable_help_text = """
|
|
Enables managed collection for Cloud Monitoring in the cluster.
|
|
|
|
Cloud Monitoring is enabled by default for all clusters. Beginning with
|
|
cluster version 1.31, use --disable-cloud-monitoring to disable.
|
|
"""
|
|
cloud_monitoring_disable_help_text = """
|
|
Disables managed collection for Cloud Monitoring in the cluster.
|
|
|
|
Cloud Monitoring is enabled by default for all clusters. Beginning with
|
|
cluster version 1.31, use --disable-cloud-monitoring to disable.
|
|
"""
|
|
if for_create:
|
|
parser.add_argument(
|
|
'--enable-managed-prometheus',
|
|
action='store_true',
|
|
default=None,
|
|
help=prometheus_enable_help_text,
|
|
)
|
|
if cloud_monitoring_option:
|
|
group = parser.add_group('Cloud Monitoring Config', mutex=True)
|
|
group.add_argument(
|
|
'--disable-cloud-monitoring',
|
|
action='store_true',
|
|
default=None,
|
|
help=cloud_monitoring_disable_help_text,
|
|
)
|
|
group.add_argument(
|
|
'--enable-cloud-monitoring',
|
|
action='store_true',
|
|
default=None,
|
|
help=cloud_monitoring_enable_help_text,
|
|
)
|
|
else:
|
|
group = parser.add_group('Monitoring Config', mutex=True)
|
|
group.add_argument(
|
|
'--disable-managed-prometheus',
|
|
action='store_true',
|
|
default=None,
|
|
help='Disable managed collection for Managed Service for Prometheus.',
|
|
)
|
|
group.add_argument(
|
|
'--enable-managed-prometheus',
|
|
action='store_true',
|
|
default=None,
|
|
help='Enable managed collection for Managed Service for Prometheus.',
|
|
)
|
|
if cloud_monitoring_option:
|
|
group = parser.add_group('Cloud Monitoring Config', mutex=True)
|
|
group.add_argument(
|
|
'--disable-cloud-monitoring',
|
|
action='store_true',
|
|
default=None,
|
|
help=(
|
|
'Disable managed collection for Cloud Monitoring.'
|
|
),
|
|
)
|
|
group.add_argument(
|
|
'--enable-cloud-monitoring',
|
|
action='store_true',
|
|
default=None,
|
|
help='Enable managed collection for Cloud Monitoring.',
|
|
)
|
|
|
|
|
|
def GetMonitoringConfig(args):
|
|
"""Parses and validates the value of the managed prometheus and cloud monitoring config flags.
|
|
|
|
Args:
|
|
args: Arguments parsed from the command.
|
|
|
|
Returns:
|
|
The monitoring config object as GoogleCloudGkemulticloudV1MonitoringConfig.
|
|
None if both enable_managed_prometheus and enable_cloud_monitoring are None.
|
|
"""
|
|
enabled_prometheus = getattr(args, 'enable_managed_prometheus', None)
|
|
disabled_prometheus = getattr(args, 'disable_managed_prometheus', None)
|
|
|
|
messages = api_util.GetMessagesModule()
|
|
prometheus_config = (
|
|
messages.GoogleCloudGkemulticloudV1ManagedPrometheusConfig()
|
|
)
|
|
if enabled_prometheus:
|
|
prometheus_config.enabled = True
|
|
elif disabled_prometheus:
|
|
prometheus_config.enabled = False
|
|
else:
|
|
prometheus_config = None
|
|
|
|
enabled_cloud_monitoring = getattr(args, 'enable_cloud_monitoring', None)
|
|
disabled_cloud_monitoring = getattr(args, 'disable_cloud_monitoring', None)
|
|
|
|
messages = api_util.GetMessagesModule()
|
|
cloud_monitoring_config = (
|
|
messages.GoogleCloudGkemulticloudV1CloudMonitoringConfig()
|
|
)
|
|
if enabled_cloud_monitoring:
|
|
cloud_monitoring_config.enabled = True
|
|
elif disabled_cloud_monitoring:
|
|
cloud_monitoring_config.enabled = False
|
|
else:
|
|
cloud_monitoring_config = None
|
|
|
|
if prometheus_config is None and cloud_monitoring_config is None:
|
|
return None
|
|
|
|
return messages.GoogleCloudGkemulticloudV1MonitoringConfig(
|
|
managedPrometheusConfig=prometheus_config,
|
|
cloudMonitoringConfig=cloud_monitoring_config,
|
|
)
|
|
|
|
|
|
def AddAllowMissing(parser, resource):
|
|
help_txt = """Allow idempotent deletion of {resource}.
|
|
The request will still succeed in case the {resource} does not exist.
|
|
"""
|
|
parser.add_argument(
|
|
'--allow-missing',
|
|
action='store_true',
|
|
help=help_txt.format(resource=resource),
|
|
)
|
|
|
|
|
|
def GetAllowMissing(args):
|
|
return getattr(args, 'allow_missing', None)
|
|
|
|
|
|
def AddIgnoreErrors(parser, platform, resource):
|
|
help_txt = """Force delete an {platform} {resource}.
|
|
Deletion of the {platform} {resource} will succeed even if errors occur
|
|
during deleting in-{resource} resources. Using this parameter may
|
|
result in orphaned resources in the {resource}.
|
|
"""
|
|
parser.add_argument(
|
|
'--ignore-errors',
|
|
action='store_true',
|
|
help=help_txt.format(resource=resource, platform=platform),
|
|
)
|
|
|
|
|
|
def GetIgnoreErrors(args):
|
|
return getattr(args, 'ignore_errors', None)
|
|
|
|
|
|
def AddBinauthzEvaluationMode(parser):
|
|
"""Adds --binauthz-evaluation-mode flag to parser."""
|
|
parser.add_argument(
|
|
'--binauthz-evaluation-mode',
|
|
choices=[
|
|
_ToSnakeCaseUpper(c) for c in _BINAUTHZ_EVAL_MODE_ENUM_MAPPER.choices
|
|
],
|
|
default=None,
|
|
help='Set Binary Authorization evaluation mode for this cluster.',
|
|
)
|
|
|
|
|
|
def GetBinauthzEvaluationMode(args):
|
|
evaluation_mode = getattr(args, 'binauthz_evaluation_mode', None)
|
|
if evaluation_mode is None:
|
|
return None
|
|
return _BINAUTHZ_EVAL_MODE_ENUM_MAPPER.GetEnumForChoice(
|
|
_ToHyphenCase(evaluation_mode)
|
|
)
|
|
|
|
|
|
def AddWorkloadVulnerabilityScanning(parser):
|
|
"""Adds --workload-vulnerability-scanning flag to parser."""
|
|
parser.add_argument(
|
|
'--workload-vulnerability-scanning',
|
|
choices=['disabled', 'enterprise'],
|
|
default=None,
|
|
hidden=True,
|
|
help=textwrap.dedent("""\
|
|
Sets the mode of the Kubernetes security posture API's workload vulnerability scanning.
|
|
To enable Advanced vulnerability insights mode explicitly set the flag to --workload-vulnerability-scanning=enterprise.
|
|
|
|
To disable in an existing cluster, explicitly set the flag to --workload-vulnerability-scanning=disabled.
|
|
"""),
|
|
)
|
|
|
|
|
|
def GetWorkloadVulnerabilityScanning(args):
|
|
vulnerability_mode = getattr(args, 'workload_vulnerability_scanning', None)
|
|
if vulnerability_mode is None:
|
|
return None
|
|
enum_type = (
|
|
api_util.GetMessagesModule().GoogleCloudGkemulticloudV1SecurityPostureConfig.VulnerabilityModeValueValuesEnum
|
|
)
|
|
mapping = {
|
|
'disabled': enum_type.VULNERABILITY_DISABLED,
|
|
'enterprise': enum_type.VULNERABILITY_ENTERPRISE,
|
|
}
|
|
return mapping[vulnerability_mode]
|
|
|
|
|
|
def AddMaxSurgeUpdate(parser):
|
|
"""Adds --max-surge-update flag to parser."""
|
|
help_text = """\
|
|
Maximum number of extra (surge) nodes to be created beyond the current size of
|
|
the node pool during its update process. Use --max-unavailable-update as well,
|
|
if needed, to control the overall surge settings.
|
|
|
|
To create an extra node each time the node pool is rolling updated, run:
|
|
|
|
$ {command} --max-surge-update=1 --max-unavailable-update=0
|
|
"""
|
|
parser.add_argument(
|
|
'--max-surge-update', type=int, default=None, help=help_text
|
|
)
|
|
|
|
|
|
def GetMaxSurgeUpdate(args):
|
|
return getattr(args, 'max_surge_update', None)
|
|
|
|
|
|
def AddMaxUnavailableUpdate(parser, for_create=False):
|
|
"""Adds --max-unavailable-update flag to parser."""
|
|
if for_create:
|
|
help_text = """\
|
|
Maximum number of nodes that can be simultaneously unavailable during this node
|
|
pool's update process. Use --max-surge-update as well, if needed, to control the
|
|
overall surge settings.
|
|
|
|
To update 3 nodes in parallel (1 + 2), but keep at least 4 nodes (6 - 2)
|
|
available each time the node pool is rolling updated, run:
|
|
|
|
$ {command} --min-nodes=6 --max-surge-update=1 --max-unavailable-update=2
|
|
"""
|
|
else:
|
|
help_text = """\
|
|
Maximum number of nodes that can be simultaneously unavailable during this node
|
|
pool's update process. Use --max-surge-update as well, if needed, to control the
|
|
overall surge settings.
|
|
|
|
To modify a node pool with 6 nodes such that, 3 nodes are updated in parallel
|
|
(1 + 2), but keep at least 4 nodes (6 - 2) available each time this
|
|
node pool is rolling updated, run:
|
|
|
|
$ {command} --max-surge-update=1 --max-unavailable-update=2
|
|
"""
|
|
parser.add_argument(
|
|
'--max-unavailable-update', type=int, default=None, help=help_text
|
|
)
|
|
|
|
|
|
def GetMaxUnavailableUpdate(args):
|
|
return getattr(args, 'max_unavailable_update', None)
|
|
|
|
|
|
def AddRespectPodDisruptionBudget(parser):
|
|
"""Adds --respect-pdb flag to parser."""
|
|
|
|
help_text = """\
|
|
Indicates whether the node pool rollback should respect pod disruption budget.
|
|
"""
|
|
|
|
parser.add_argument(
|
|
'--respect-pdb',
|
|
default=False,
|
|
action='store_true',
|
|
help=help_text,
|
|
)
|
|
|
|
|
|
def GetRespectPodDisruptionBudget(args):
|
|
return getattr(args, 'respect_pdb', None)
|
|
|
|
|
|
def AddResourceManagerTags(parser):
|
|
"""Adds --tags flag to parser.
|
|
|
|
Args:
|
|
parser: The argparse.parser to add the arguments to.
|
|
"""
|
|
help_text = """\
|
|
Tag keys/values directly bound to this resource.
|
|
|
|
The short name of a tag key or value can have a maximum length of 256
|
|
characters. The permitted character set for the short name includes UTF-8
|
|
encoded Unicode characters except single quotes, double quotes,
|
|
backslashes, and forward slashes.
|
|
"""
|
|
parser.add_argument(
|
|
'--tags',
|
|
type=arg_parsers.ArgDict(min_length=1),
|
|
metavar='TAG',
|
|
help=help_text,
|
|
)
|
|
|
|
|
|
def GetResourceManagerTags(args):
|
|
"""Parses and validates the value of the --tags flag.
|
|
|
|
Args:
|
|
args: Arguments parsed from the command.
|
|
|
|
Returns:
|
|
The tags object as a dictionary.
|
|
"""
|
|
return getattr(args, 'tags', None) or {}
|