File: //snap/google-cloud-cli/current/lib/googlecloudsdk/command_lib/container/fleet/update_mask.py
# -*- coding: utf-8 -*- #
# Copyright 2023 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Utilities for working with update mask."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from typing import Dict, Union
from googlecloudsdk.calliope import parser_extensions
# FLEET_MESSAGE_TO_FLAGS structure strictly follows proto message definition.
# A message path segment either directly maps to a flag,
# or it maps to a dictionary that has its child fields as keys,
# path structure as values.
FLEET_MESSAGE_TO_FLAGS = {
'display_name': '--display-name',
'default_cluster_config': {
'security_posture_config': {
'mode': '--security-posture',
'vulnerability_mode': '--workload-vulnerability-scanning',
},
'binary_authorization_config': {
'evaluation_mode': '--binauthz-evaluation-mode',
'policy_bindings': '--binauthz-policy-bindings',
},
'compliance_posture_config': ['--compliance', '--compliance-standards'],
},
}
# Define recursive type for update mask structure.
Level = Dict[str, Union[str, 'Level']]
def FlagToUpdateMaskPaths(
message_to_flags: Dict[str, Level]
) -> Dict[str, str]:
"""Construct flag to update mask paths during runtime.
From top level field, combine the string up to the leaf dict.
Flag fields are unique.
Args:
message_to_flags: Receive value from MESSAGE_TO_FLAGS.
Returns:
A dictionary that maps each flag to the corresponding field update path.
Given the below message to flag structure,
{
parent: {
child1: {
foo: '--flag-for-foo',
child2: {
bar: '--flag-for-bar'
}
}
}
}
It should produce the following flag to update path mapping:
{
'--flag-for-foo': parent.child1.foo,
'--flag-for-bar': parent.child1.child2.bar,
}
"""
def Recursive(level: Level) -> Dict[str, str]:
ret = {}
for curr_path, flag_or_level in level.items():
if isinstance(flag_or_level, str): # The value is a plain flag.
ret[flag_or_level] = curr_path
elif isinstance(flag_or_level, list):
for flag in flag_or_level:
ret[flag] = curr_path
else: # The value is of Level type.
for key, remain_path in Recursive(flag_or_level).items():
ret[key] = curr_path + '.' + remain_path
else:
return ret
return Recursive(message_to_flags)
def GetUpdateMask(
args: parser_extensions.Namespace, flag_to_update_mask_paths: Dict[str, str]
) -> str:
"""Maps specified flags to API field paths of mutable fields.
args.GetSpecifiedArgNames() returns a list of specified flags.
For example, `gcloud alpha container fleet create --display-name my-fleet
--format 'yaml(displayName)'`
args.GetSpecifiedArgNames() = ['--display-name', '--format']
Args:
args: All arguments passed from CLI.
flag_to_update_mask_paths: Mapping for a specific resource, such as user
cluster, or node pool.
Returns:
A string that contains yaml field paths to be used in the API update
request.
"""
ret = []
for flag, update_mask in flag_to_update_mask_paths.items():
if flag in args.GetSpecifiedArgNames():
ret.append(update_mask)
else:
return ','.join(sorted(set(ret)))
def GetFleetUpdateMask(args):
fleet_flag_to_update_mask_paths = FlagToUpdateMaskPaths(
FLEET_MESSAGE_TO_FLAGS
)
return GetUpdateMask(args, fleet_flag_to_update_mask_paths)
def HasBinauthzConfig(args) -> bool:
return args.IsKnownAndSpecified(
'binauthz_evaluation_mode'
) or args.IsKnownAndSpecified('binauthz_policy_bindings')