From 0f3c9d15740882d43275966f8e9f2e41d5c77288 Mon Sep 17 00:00:00 2001 From: Jonathan Innis Date: Wed, 8 Dec 2021 09:54:13 -0800 Subject: [PATCH] Update Identity Creation for Appliance to Latest Version (#95) * Update appliance API to latest version for identity * Create a utils file with get parent_api_version * Fix style errors --- .flake8 | 1 + .../azext_k8s_extension/consts.py | 18 +- .../azext_k8s_extension/custom.py | 355 +++++++++++------- .../partner_extensions/AzureMLKubernetes.py | 20 +- .../partner_extensions/DefaultExtension.py | 70 +++- .../PartnerExtensionModel.py | 50 ++- .../azext_k8s_extension/utils.py | 43 +++ 7 files changed, 379 insertions(+), 178 deletions(-) create mode 100644 src/k8s-extension/azext_k8s_extension/utils.py diff --git a/.flake8 b/.flake8 index 0295f1c0277..5d2b0466e0e 100644 --- a/.flake8 +++ b/.flake8 @@ -7,6 +7,7 @@ ignore = F401, # imported but unused, too many violations, to be removed in the future F811, # redefinition of unused, to be removed in the future C901 # code flow is too complex, too many violations, to be removed in the future + W503 # line break before binary operator effect on readability is subjective W504 # line break after binary operator effect on readability is subjective exclude = */vendored_sdks diff --git a/src/k8s-extension/azext_k8s_extension/consts.py b/src/k8s-extension/azext_k8s_extension/consts.py index 350344bc53a..dff2e32b886 100644 --- a/src/k8s-extension/azext_k8s_extension/consts.py +++ b/src/k8s-extension/azext_k8s_extension/consts.py @@ -4,8 +4,20 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -EXTENSION_NAME = 'k8s-extension' +EXTENSION_NAME = "k8s-extension" EXTENSION_PACKAGE_NAME = "azext_k8s_extension" -PROVIDER_NAMESPACE = 'Microsoft.KubernetesConfiguration' +PROVIDER_NAMESPACE = "Microsoft.KubernetesConfiguration" REGISTERED = "Registered" -DF_RM_HOSTNAME = 'api-dogfood.resources.windows-int.net' +DF_RM_HOSTNAME = "api-dogfood.resources.windows-int.net" + +CONNECTED_CLUSTER_RP = "Microsoft.Kubernetes" +MANAGED_CLUSTER_RP = "Microsoft.ContainerService" +APPLIANCE_RP = "Microsoft.ResourceConnector" + +CONNECTED_CLUSTER_TYPE = "connectedclusters" +MANAGED_CLUSTER_TYPE = "managedclusters" +APPLIANCE_TYPE = "appliances" + +CONNECTED_CLUSTER_API_VERSION = "2021-10-01" +MANAGED_CLUSTER_API_VERSION = "2021-10-01" +APPLIANCE_API_VERSION = "2021-10-31-preview" diff --git a/src/k8s-extension/azext_k8s_extension/custom.py b/src/k8s-extension/azext_k8s_extension/custom.py index c3720078056..8f845333f3b 100644 --- a/src/k8s-extension/azext_k8s_extension/custom.py +++ b/src/k8s-extension/azext_k8s_extension/custom.py @@ -5,12 +5,18 @@ # pylint: disable=unused-argument,too-many-locals -import json -from urllib.parse import urlparse +from azext_k8s_extension.utils import ( + get_cluster_rp_api_version, + is_dogfood_cluster, + read_config_settings_file, +) from knack.log import get_logger -from azure.cli.core.azclierror import ResourceNotFoundError, MutuallyExclusiveArgumentError, \ - InvalidArgumentValueError, RequiredArgumentMissingError +from azure.cli.core.azclierror import ( + ResourceNotFoundError, + MutuallyExclusiveArgumentError, + RequiredArgumentMissingError, +) from azure.cli.core.commands.client_factory import get_subscription_id from azure.cli.core.util import sdk_no_wait from azure.core.exceptions import HttpResponseError @@ -22,7 +28,10 @@ from .partner_extensions.OpenServiceMesh import OpenServiceMesh from .partner_extensions.AzureMLKubernetes import AzureMLKubernetes from .partner_extensions.Dapr import Dapr -from .partner_extensions.DefaultExtension import DefaultExtension, user_confirmation_factory +from .partner_extensions.DefaultExtension import ( + DefaultExtension, + user_confirmation_factory, +) from . import consts from ._client_factory import cf_resources @@ -33,11 +42,11 @@ # A factory method to return the correct extension class based off of the extension name def ExtensionFactory(extension_name): extension_map = { - 'microsoft.azuremonitor.containers': ContainerInsights, - 'microsoft.azuredefender.kubernetes': AzureDefender, - 'microsoft.openservicemesh': OpenServiceMesh, - 'microsoft.azureml.kubernetes': AzureMLKubernetes, - 'microsoft.dapr': Dapr, + "microsoft.azuremonitor.containers": ContainerInsights, + "microsoft.azuredefender.kubernetes": AzureDefender, + "microsoft.openservicemesh": OpenServiceMesh, + "microsoft.azureml.kubernetes": AzureMLKubernetes, + "microsoft.dapr": Dapr, } # Return the extension if we find it in the map, else return the default @@ -45,14 +54,14 @@ def ExtensionFactory(extension_name): def show_k8s_extension(client, resource_group_name, cluster_name, name, cluster_type): - """Get an existing K8s Extension. - """ + """Get an existing K8s Extension.""" # Determine ClusterRP - cluster_rp = __get_cluster_rp(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_type) try: - extension = client.get(resource_group_name, - cluster_rp, cluster_type, cluster_name, name) + extension = client.get( + resource_group_name, cluster_rp, cluster_type, cluster_name, name + ) return extension except HttpResponseError as ex: # Customize the error message for resources not found @@ -60,46 +69,69 @@ def show_k8s_extension(client, resource_group_name, cluster_name, name, cluster_ # If Cluster not found if ex.message.__contains__("(ResourceNotFound)"): message = "{0} Verify that the cluster-type is correct and the resource exists.".format( - ex.message) + ex.message + ) # If Configuration not found - elif ex.message.__contains__("Operation returned an invalid status code 'Not Found'"): - message = "(ExtensionNotFound) The Resource {0}/{1}/{2}/Microsoft.KubernetesConfiguration/" \ - "extensions/{3} could not be found!".format( - cluster_rp, cluster_type, cluster_name, name) + elif ex.message.__contains__( + "Operation returned an invalid status code 'Not Found'" + ): + message = ( + "(ExtensionNotFound) The Resource {0}/{1}/{2}/Microsoft.KubernetesConfiguration/" + "extensions/{3} could not be found!".format( + cluster_rp, cluster_type, cluster_name, name + ) + ) else: message = ex.message raise ResourceNotFoundError(message) from ex raise ex -def create_k8s_extension(cmd, client, resource_group_name, cluster_name, name, cluster_type, - extension_type, scope=None, auto_upgrade_minor_version=None, release_train=None, - version=None, target_namespace=None, release_namespace=None, configuration_settings=None, - configuration_protected_settings=None, configuration_settings_file=None, - configuration_protected_settings_file=None, no_wait=False): - """Create a new Extension Instance. - """ +def create_k8s_extension( + cmd, + client, + resource_group_name, + cluster_name, + name, + cluster_type, + extension_type, + scope=None, + auto_upgrade_minor_version=None, + release_train=None, + version=None, + target_namespace=None, + release_namespace=None, + configuration_settings=None, + configuration_protected_settings=None, + configuration_settings_file=None, + configuration_protected_settings_file=None, + no_wait=False, +): + """Create a new Extension Instance.""" extension_type_lower = extension_type.lower() - cluster_rp = __get_cluster_rp(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_type) # Configuration Settings & Configuration Protected Settings if configuration_settings is not None and configuration_settings_file is not None: raise MutuallyExclusiveArgumentError( - 'Error! Both configuration-settings and configuration-settings-file cannot be provided.' + "Error! Both configuration-settings and configuration-settings-file cannot be provided." ) - if configuration_protected_settings is not None and configuration_protected_settings_file is not None: + if ( + configuration_protected_settings is not None + and configuration_protected_settings_file is not None + ): raise MutuallyExclusiveArgumentError( - 'Error! Both configuration-protected-settings and configuration-protected-settings-file ' - 'cannot be provided.' + "Error! Both configuration-protected-settings and configuration-protected-settings-file " + "cannot be provided." ) config_settings = {} config_protected_settings = {} # Get Configuration Settings from file if configuration_settings_file is not None: - config_settings = __read_config_settings_file(configuration_settings_file) + config_settings = read_config_settings_file(configuration_settings_file) if configuration_settings is not None: for dicts in configuration_settings: @@ -108,7 +140,9 @@ def create_k8s_extension(cmd, client, resource_group_name, cluster_name, name, c # Get Configuration Protected Settings from file if configuration_protected_settings_file is not None: - config_protected_settings = __read_config_settings_file(configuration_protected_settings_file) + config_protected_settings = read_config_settings_file( + configuration_protected_settings_file + ) if configuration_protected_settings is not None: for dicts in configuration_protected_settings: @@ -127,12 +161,29 @@ def create_k8s_extension(cmd, client, resource_group_name, cluster_name, name, c # Get the extension class based on the extension name extension_class = ExtensionFactory(extension_type_lower) extension_instance, name, create_identity = extension_class.Create( - cmd, client, resource_group_name, cluster_name, name, cluster_type, extension_type_lower, scope, - auto_upgrade_minor_version, release_train, version, target_namespace, release_namespace, config_settings, - config_protected_settings, configuration_settings_file, configuration_protected_settings_file) + cmd, + client, + resource_group_name, + cluster_name, + name, + cluster_type, + extension_type_lower, + scope, + auto_upgrade_minor_version, + release_train, + version, + target_namespace, + release_namespace, + config_settings, + config_protected_settings, + configuration_settings_file, + configuration_protected_settings_file, + ) # Common validations - __validate_version_and_auto_upgrade(extension_instance.version, extension_instance.auto_upgrade_minor_version) + __validate_version_and_auto_upgrade( + extension_instance.version, extension_instance.auto_upgrade_minor_version + ) __validate_scope_after_customization(extension_instance.scope) # Check that registration has been done on Microsoft.KubernetesConfiguration for the subscription @@ -141,50 +192,80 @@ def create_k8s_extension(cmd, client, resource_group_name, cluster_name, name, c # Create identity, if required # We don't create the identity if we are in DF if create_identity and not is_dogfood_cluster(cmd): - identity_object, location = __create_identity(cmd, resource_group_name, cluster_name, cluster_type, cluster_rp) + identity_object, location = __create_identity( + cmd, resource_group_name, cluster_name, cluster_type + ) if identity_object is not None and location is not None: - extension_instance.identity, extension_instance.location = identity_object, location + extension_instance.identity, extension_instance.location = ( + identity_object, + location, + ) # Try to create the resource - return sdk_no_wait(no_wait, client.begin_create, resource_group_name, - cluster_rp, cluster_type, cluster_name, name, extension_instance) + return sdk_no_wait( + no_wait, + client.begin_create, + resource_group_name, + cluster_rp, + cluster_type, + cluster_name, + name, + extension_instance, + ) def list_k8s_extension(client, resource_group_name, cluster_name, cluster_type): - cluster_rp = __get_cluster_rp(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_type) return client.list(resource_group_name, cluster_rp, cluster_type, cluster_name) -def update_k8s_extension(cmd, client, resource_group_name, cluster_name, name, cluster_type, - auto_upgrade_minor_version='', release_train='', version='', - configuration_settings=None, configuration_protected_settings=None, - configuration_settings_file=None, configuration_protected_settings_file=None, - no_wait=False, yes=False): - """Patch an existing Extension Instance. - """ - - if configuration_settings or \ - configuration_protected_settings or \ - configuration_settings_file or \ - configuration_protected_settings_file: - msg = ('Updating properties in --config-settings or --config-protected-settings may lead to undesirable state' - ' if the cluster extension type does not support it. Please refer to the documentation of the' - ' cluster extension service to check if updates to these properties is supported.' - ' Do you wish to proceed?') +def update_k8s_extension( + cmd, + client, + resource_group_name, + cluster_name, + name, + cluster_type, + auto_upgrade_minor_version="", + release_train="", + version="", + configuration_settings=None, + configuration_protected_settings=None, + configuration_settings_file=None, + configuration_protected_settings_file=None, + no_wait=False, + yes=False, +): + """Patch an existing Extension Instance.""" + + if ( + configuration_settings + or configuration_protected_settings + or configuration_settings_file + or configuration_protected_settings_file + ): + msg = ( + "Updating properties in --config-settings or --config-protected-settings may lead to undesirable state" + " if the cluster extension type does not support it. Please refer to the documentation of the" + " cluster extension service to check if updates to these properties is supported." + " Do you wish to proceed?" + ) user_confirmation_factory(cmd, yes, msg) # Determine ClusterRP - cluster_rp = __get_cluster_rp(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_type) # We need to determine the ExtensionType to call ExtensionFactory and create Extension class - extension = show_k8s_extension(client, resource_group_name, cluster_name, name, cluster_type) + extension = show_k8s_extension( + client, resource_group_name, cluster_name, name, cluster_type + ) extension_type_lower = extension.extension_type.lower() config_settings = {} config_protected_settings = {} # Get Configuration Settings from file if configuration_settings_file is not None: - config_settings = __read_config_settings_file(configuration_settings_file) + config_settings = read_config_settings_file(configuration_settings_file) if configuration_settings is not None: for dicts in configuration_settings: @@ -193,7 +274,9 @@ def update_k8s_extension(cmd, client, resource_group_name, cluster_name, name, c # Get Configuration Protected Settings from file if configuration_protected_settings_file is not None: - config_protected_settings = __read_config_settings_file(configuration_protected_settings_file) + config_protected_settings = read_config_settings_file( + configuration_protected_settings_file + ) if configuration_protected_settings is not None: for dicts in configuration_protected_settings: @@ -203,55 +286,89 @@ def update_k8s_extension(cmd, client, resource_group_name, cluster_name, name, c # Get the extension class based on the extension type extension_class = ExtensionFactory(extension_type_lower) - upd_extension = extension_class.Update(cmd, resource_group_name, cluster_name, auto_upgrade_minor_version, release_train, version, - config_settings, config_protected_settings, yes) - - return sdk_no_wait(no_wait, client.begin_update, resource_group_name, cluster_rp, cluster_type, - cluster_name, name, upd_extension) - - -def delete_k8s_extension(cmd, client, resource_group_name, cluster_name, name, cluster_type, - no_wait=False, yes=False, force=False): - """Delete an existing Kubernetes Extension. - """ + upd_extension = extension_class.Update( + cmd, + resource_group_name, + cluster_name, + auto_upgrade_minor_version, + release_train, + version, + config_settings, + config_protected_settings, + yes, + ) + + return sdk_no_wait( + no_wait, + client.begin_update, + resource_group_name, + cluster_rp, + cluster_type, + cluster_name, + name, + upd_extension, + ) + + +def delete_k8s_extension( + cmd, + client, + resource_group_name, + cluster_name, + name, + cluster_type, + no_wait=False, + yes=False, + force=False, +): + """Delete an existing Kubernetes Extension.""" # Determine ClusterRP - cluster_rp = __get_cluster_rp(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_type) extension = None try: - extension = client.get(resource_group_name, cluster_rp, cluster_type, cluster_name, name) + extension = client.get( + resource_group_name, cluster_rp, cluster_type, cluster_name, name + ) except HttpResponseError: - logger.warning("No extension with name '%s' found on cluster '%s', so nothing to delete", name, cluster_name) + logger.warning( + "No extension with name '%s' found on cluster '%s', so nothing to delete", + name, + cluster_name, + ) return None extension_class = ExtensionFactory(extension.extension_type.lower()) # If there is any custom delete logic, this will call the logic - extension_class.Delete(cmd, client, resource_group_name, cluster_name, - name, cluster_type, yes) - - return sdk_no_wait(no_wait, client.begin_delete, resource_group_name, - cluster_rp, cluster_type, cluster_name, name, force_delete=force) - - -def __create_identity(cmd, resource_group_name, cluster_name, cluster_type, cluster_rp): + extension_class.Delete( + cmd, client, resource_group_name, cluster_name, name, cluster_type, yes + ) + + return sdk_no_wait( + no_wait, + client.begin_delete, + resource_group_name, + cluster_rp, + cluster_type, + cluster_name, + name, + force_delete=force, + ) + + +def __create_identity(cmd, resource_group_name, cluster_name, cluster_type): subscription_id = get_subscription_id(cmd.cli_ctx) resources = cf_resources(cmd.cli_ctx, subscription_id) - cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/{2}/{3}/{4}'.format(subscription_id, - resource_group_name, - cluster_rp, - cluster_type, - cluster_name) - - if cluster_rp == 'Microsoft.Kubernetes': - parent_api_version = '2020-01-01-preview' - elif cluster_rp == 'Microsoft.ResourceConnector': - parent_api_version = '2020-09-15-privatepreview' - elif cluster_rp == 'Microsoft.ContainerService': + if cluster_type.lower() == consts.MANAGED_CLUSTER_TYPE: return None, None - else: - raise InvalidArgumentValueError( - "Error! Cluster type '{}' is not supported for extension identity".format(cluster_type) + + cluster_rp, parent_api_version = get_cluster_rp_api_version(cluster_type) + + cluster_resource_id = ( + "/subscriptions/{0}/resourceGroups/{1}/providers/{2}/{3}/{4}".format( + subscription_id, resource_group_name, cluster_rp, cluster_type, cluster_name ) + ) try: resource = resources.get_by_id(cluster_resource_id, parent_api_version) @@ -263,32 +380,25 @@ def __create_identity(cmd, resource_group_name, cluster_name, cluster_type, clus return Identity(type=identity_type), location -def __get_cluster_rp(cluster_type): - rp = "" - if cluster_type.lower() == 'connectedclusters': - rp = 'Microsoft.Kubernetes' - elif cluster_type.lower() == 'appliances': - rp = 'Microsoft.ResourceConnector' - elif cluster_type.lower() == '' or cluster_type.lower() == 'managedclusters': - rp = 'Microsoft.ContainerService' - else: - raise InvalidArgumentValueError("Error! Cluster type '{}' is not supported".format(cluster_type)) - return rp - - def __validate_scope_and_namespace(scope, release_namespace, target_namespace): - if scope == 'cluster': + if scope == "cluster": if target_namespace is not None: message = "When --scope is 'cluster', --target-namespace must not be given." raise MutuallyExclusiveArgumentError(message) else: if release_namespace is not None: - message = "When --scope is 'namespace', --release-namespace must not be given." + message = ( + "When --scope is 'namespace', --release-namespace must not be given." + ) raise MutuallyExclusiveArgumentError(message) def __validate_scope_after_customization(scope_obj: Scope): - if scope_obj is not None and scope_obj.namespace is not None and scope_obj.namespace.target_namespace is None: + if ( + scope_obj is not None + and scope_obj.namespace is not None + and scope_obj.namespace.target_namespace is None + ): message = "When --scope is 'namespace', --target-namespace must be given." raise RequiredArgumentMissingError(message) @@ -300,18 +410,3 @@ def __validate_version_and_auto_upgrade(version, auto_upgrade_minor_version): raise MutuallyExclusiveArgumentError(message) auto_upgrade_minor_version = False - - -def __read_config_settings_file(file_path): - try: - with open(file_path, 'r') as f: - settings = json.load(f) - if len(settings) == 0: - raise Exception("File {} is empty".format(file_path)) - return settings - except ValueError as ex: - raise Exception("File {} is not a valid JSON file".format(file_path)) from ex - - -def is_dogfood_cluster(cmd): - return urlparse(cmd.cli_ctx.cloud.endpoints.resource_manager).hostname == consts.DF_RM_HOSTNAME diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureMLKubernetes.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureMLKubernetes.py index 4f4fb43fe15..d9c5f5e31f0 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureMLKubernetes.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureMLKubernetes.py @@ -10,6 +10,7 @@ import copy from hashlib import md5 from typing import Any, Dict, List, Tuple +from azext_k8s_extension.utils import get_cluster_rp_api_version import azure.mgmt.relay import azure.mgmt.relay.models @@ -111,7 +112,7 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t # get the arc's location subscription_id = get_subscription_id(cmd.cli_ctx) - cluster_rp, parent_api_version = _get_cluster_rp_api_version(cluster_type) + cluster_rp, parent_api_version = get_cluster_rp_api_version(cluster_type) cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/{2}' \ '/{3}/{4}'.format(subscription_id, resource_group_name, cluster_rp, cluster_type, cluster_name) cluster_location = '' @@ -620,23 +621,6 @@ def _get_value_from_config_protected_config(key, config, protected_config): return protected_config.get(key) -def _get_cluster_rp_api_version(cluster_type) -> Tuple[str, str]: - rp = '' - parent_api_version = '' - if cluster_type.lower() == 'connectedclusters': - rp = 'Microsoft.Kubernetes' - parent_api_version = '2020-01-01-preview' - elif cluster_type.lower() == 'appliances': - rp = 'Microsoft.ResourceConnector' - parent_api_version = '2020-09-15-privatepreview' - elif cluster_type.lower() == '' or cluster_type.lower() == 'managedclusters': - rp = 'Microsoft.ContainerService' - parent_api_version = '2021-05-01' - else: - raise InvalidArgumentValueError("Error! Cluster type '{}' is not supported".format(cluster_type)) - return rp, parent_api_version - - def _check_nodeselector_existed(configuration_settings, configuration_protected_settings): config_keys = configuration_settings.keys() config_protected_keys = configuration_protected_settings.keys() diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py index 8289e931336..a15defc72d2 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py @@ -17,19 +17,35 @@ class DefaultExtension(PartnerExtensionModel): - def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_type, extension_type, - scope, auto_upgrade_minor_version, release_train, version, target_namespace, - release_namespace, configuration_settings, configuration_protected_settings, - configuration_settings_file, configuration_protected_settings_file): + def Create( + self, + cmd, + client, + resource_group_name, + cluster_name, + name, + cluster_type, + extension_type, + scope, + auto_upgrade_minor_version, + release_train, + version, + target_namespace, + release_namespace, + configuration_settings, + configuration_protected_settings, + configuration_settings_file, + configuration_protected_settings_file, + ): """Default validations & defaults for Create - Must create and return a valid 'Extension' object. + Must create and return a valid 'Extension' object. """ ext_scope = None if scope is not None: - if scope.lower() == 'cluster': + if scope.lower() == "cluster": scope_cluster = ScopeCluster(release_namespace=release_namespace) ext_scope = Scope(cluster=scope_cluster, namespace=None) - elif scope.lower() == 'namespace': + elif scope.lower() == "namespace": scope_namespace = ScopeNamespace(target_namespace=target_namespace) ext_scope = Scope(namespace=scope_namespace, cluster=None) @@ -41,27 +57,43 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t version=version, scope=ext_scope, configuration_settings=configuration_settings, - configuration_protected_settings=configuration_protected_settings + configuration_protected_settings=configuration_protected_settings, ) return extension, name, create_identity - def Update(self, cmd, resource_group_name, cluster_name, auto_upgrade_minor_version, release_train, version, configuration_settings, - configuration_protected_settings, yes=False): + def Update( + self, + cmd, + resource_group_name, + cluster_name, + auto_upgrade_minor_version, + release_train, + version, + configuration_settings, + configuration_protected_settings, + yes=False, + ): """Default validations & defaults for Update - Must create and return a valid 'PatchExtension' object. + Must create and return a valid 'PatchExtension' object. """ - return PatchExtension(auto_upgrade_minor_version=auto_upgrade_minor_version, - release_train=release_train, - version=version, - configuration_settings=configuration_settings, - configuration_protected_settings=configuration_protected_settings) + return PatchExtension( + auto_upgrade_minor_version=auto_upgrade_minor_version, + release_train=release_train, + version=version, + configuration_settings=configuration_settings, + configuration_protected_settings=configuration_protected_settings, + ) - def Delete(self, cmd, client, resource_group_name, cluster_name, name, cluster_type, yes): + def Delete( + self, cmd, client, resource_group_name, cluster_name, name, cluster_type, yes + ): user_confirmation_factory(cmd, yes) -def user_confirmation_factory(cmd, yes, message="Are you sure you want to perform this operation?"): - if cmd.cli_ctx.config.getboolean('core', 'disable_confirm_prompt', fallback=False): +def user_confirmation_factory( + cmd, yes, message="Are you sure you want to perform this operation?" +): + if cmd.cli_ctx.config.getboolean("core", "disable_confirm_prompt", fallback=False): return user_confirmation(message, yes=yes) diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py index e1f37ec3a95..d4f1eeba6c3 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py @@ -10,18 +10,52 @@ class PartnerExtensionModel(ABC): @abstractmethod - def Create(self, cmd, client, resource_group_name: str, cluster_name: str, name: str, cluster_type: str, - extension_type: str, scope: str, auto_upgrade_minor_version: bool, release_train: str, version: str, - target_namespace: str, release_namespace: str, configuration_settings: dict, - configuration_protected_settings: dict, configuration_settings_file: str, - configuration_protected_settings_file: str) -> Extension: + def Create( + self, + cmd, + client, + resource_group_name: str, + cluster_name: str, + name: str, + cluster_type: str, + extension_type: str, + scope: str, + auto_upgrade_minor_version: bool, + release_train: str, + version: str, + target_namespace: str, + release_namespace: str, + configuration_settings: dict, + configuration_protected_settings: dict, + configuration_settings_file: str, + configuration_protected_settings_file: str, + ) -> Extension: pass @abstractmethod - def Update(self, cmd, resource_group_name: str, cluster_name: str, auto_upgrade_minor_version: bool, release_train: str, version: str, - configuration_settings: dict, configuration_protected_settings: dict, yes: bool) -> PatchExtension: + def Update( + self, + cmd, + resource_group_name: str, + cluster_name: str, + auto_upgrade_minor_version: bool, + release_train: str, + version: str, + configuration_settings: dict, + configuration_protected_settings: dict, + yes: bool, + ) -> PatchExtension: pass @abstractmethod - def Delete(self, cmd, client, resource_group_name: str, cluster_name: str, name: str, cluster_type: str, yes: bool): + def Delete( + self, + cmd, + client, + resource_group_name: str, + cluster_name: str, + name: str, + cluster_type: str, + yes: bool, + ): pass diff --git a/src/k8s-extension/azext_k8s_extension/utils.py b/src/k8s-extension/azext_k8s_extension/utils.py new file mode 100644 index 00000000000..9c7322cdf70 --- /dev/null +++ b/src/k8s-extension/azext_k8s_extension/utils.py @@ -0,0 +1,43 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import json +from typing import Tuple +from urllib.parse import urlparse +from azext_k8s_extension import consts +from azure.cli.core.azclierror import InvalidArgumentValueError + + +def get_cluster_rp_api_version(cluster_type) -> Tuple[str, str]: + if cluster_type.lower() == consts.CONNECTED_CLUSTER_TYPE: + return consts.CONNECTED_CLUSTER_RP, consts.CONNECTED_CLUSTER_API_VERSION + if cluster_type.lower() == consts.APPLIANCE_TYPE: + return consts.APPLIANCE_RP, consts.APPLIANCE_API_VERSION + if ( + cluster_type.lower() == "" + or cluster_type.lower() == consts.MANAGED_CLUSTER_TYPE + ): + return consts.MANAGED_CLUSTER_RP, consts.MANAGED_CLUSTER_API_VERSION + raise InvalidArgumentValueError( + "Error! Cluster type '{}' is not supported".format(cluster_type) + ) + + +def read_config_settings_file(file_path): + try: + with open(file_path, "r") as f: + settings = json.load(f) + if len(settings) == 0: + raise Exception("File {} is empty".format(file_path)) + return settings + except ValueError as ex: + raise Exception("File {} is not a valid JSON file".format(file_path)) from ex + + +def is_dogfood_cluster(cmd): + return ( + urlparse(cmd.cli_ctx.cloud.endpoints.resource_manager).hostname + == consts.DF_RM_HOSTNAME + )