diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index 525c06dd971..a53baf9a877 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -11,9 +11,14 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ + +19.0.0b23 ++++++++ * `az aks update`: Fix `--enable-secret-rotation`, `--disable-secret-rotation`, and `--rotation-poll-interval` flags being silently ignored when updating Azure Key Vault Secrets Provider addon configuration. * `az aks create/update`: Add `--enable-azure-monitor-logs` support to container network logs validation. * `az aks create/update`: Add `--enable-default-domain` and `--disable-default-domain` parameters to manage the default domain feature for web app routing. +* `az aks create`: Add ephemeralDisk and elasticSan storage options to `--enable-azure-container-storage` for the latest version of Azure Container Storage. +* `az aks update`: Add ephemeralDisk and elasticSan storage options to `--enable-azure-container-storage` and `--disable-azure-container-storage` for the latest version of Azure Container Storage. 19.0.0b22 +++++++ diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index cc0cb8fc8a9..31007557c39 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -1100,7 +1100,7 @@ def load_arguments(self, _): # azure container storage c.argument( "enable_azure_container_storage", - arg_type=_get_enable_azure_container_storage_type(), + arg_type=_get_container_storage_enum_type(storage_pool_types), help="enable azure container storage. Can be used as a flag (defaults to True) or with a" " storage pool type value: (azureDisk, ephemeralDisk, elasticSan)", ) @@ -1670,13 +1670,13 @@ def load_arguments(self, _): # azure container storage c.argument( "enable_azure_container_storage", - arg_type=_get_enable_azure_container_storage_type(), + arg_type=_get_container_storage_enum_type(storage_pool_types), help="enable azure container storage. Can be used as a flag (defaults to True) or with a" " storage pool type value: (azureDisk, ephemeralDisk, elasticSan)", ) c.argument( "disable_azure_container_storage", - arg_type=_get_disable_azure_container_storage_type(), + arg_type=_get_container_storage_enum_type(disable_storage_pool_types), help="disable azure container storage or any one of the storage pool types." " Can be used as a flag (defaults to True) or with a storagepool type value:" " azureDisk, ephemeralDisk, elasticSan, all (to disable all storage pools).", @@ -3166,63 +3166,33 @@ def _get_default_install_location(exe_name): return install_location -def _get_enable_azure_container_storage_type(): +def _get_container_storage_enum_type(choices): """Custom argument type that accepts both None and enum values""" import argparse from azure.cli.core.azclierror import InvalidArgumentValueError class AzureContainerStorageAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): - if values is None: + if values in [[], None]: # When used as a flag without value, set as True setattr(namespace, self.dest, True) return - if isinstance(values, str): - # Handle enum values (case insensitive) - for storage_type in storage_pool_types: - if values.lower() == storage_type.lower(): - setattr(namespace, self.dest, storage_type) - return - - # Invalid value - valid_values = storage_pool_types - raise InvalidArgumentValueError( - f"Invalid value '{values}'. Valid values are: {', '.join(valid_values)}" - ) - - return CLIArgumentType( - nargs='?', # Optional argument - action=AzureContainerStorageAction, - ) - - -def _get_disable_azure_container_storage_type(): - """Custom argument type that accepts both None and enum values""" - import argparse - from azure.cli.core.azclierror import InvalidArgumentValueError - - class AzureContainerStorageAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - if values is None: - # When used as a flag without value, set as True - setattr(namespace, self.dest, True) + # Allow multiple enum values in a case insensitive manner + normalized_value_arr = values if isinstance(values, list) else str(values).split(',') + normalized_value_arr = [str(v).lower().strip() for v in normalized_value_arr] + valid_value_arr = [v for v in choices if v.lower() in normalized_value_arr] + if len(valid_value_arr) == len(normalized_value_arr): + normalized_values = valid_value_arr[0] if len(valid_value_arr) == 1 else valid_value_arr + setattr(namespace, self.dest, normalized_values) return - if isinstance(values, str): - # Handle enum values (case insensitive) - for storage_type in disable_storage_pool_types: - if values.lower() == storage_type.lower(): - setattr(namespace, self.dest, storage_type) - return - # Invalid value - valid_values = disable_storage_pool_types raise InvalidArgumentValueError( - f"Invalid value '{values}'. Valid values are: {', '.join(valid_values)}" + f"Invalid value '{values}'. Valid values are: {', '.join(choices)}" ) return CLIArgumentType( - nargs='?', # Optional argument + nargs='*', # Allow multiple values action=AzureContainerStorageAction, ) diff --git a/src/aks-preview/azext_aks_preview/azurecontainerstorage/_helpers.py b/src/aks-preview/azext_aks_preview/azurecontainerstorage/_helpers.py index 05862767521..47bf2aa72e6 100644 --- a/src/aks-preview/azext_aks_preview/azurecontainerstorage/_helpers.py +++ b/src/aks-preview/azext_aks_preview/azurecontainerstorage/_helpers.py @@ -7,7 +7,9 @@ from azext_aks_preview.azurecontainerstorage._consts import ( CONST_ACSTOR_ALL, + CONST_ACSTOR_EXT_INSTALLATION_NAME, CONST_ACSTOR_IO_ENGINE_LABEL_KEY, + CONST_ACSTOR_K8S_EXTENSION_NAME, CONST_DISK_TYPE_EPHEMERAL_VOLUME_ONLY, CONST_DISK_TYPE_PV_WITH_ANNOTATION, CONST_EPHEMERAL_NVME_PERF_TIER_BASIC, @@ -140,13 +142,13 @@ def check_if_extension_is_installed(cmd, resource_group, cluster_name) -> bool: extension_type = extension.extension_type.lower() if extension_type != CONST_ACSTOR_V1_K8S_EXTENSION_NAME: return_val = False - except: # pylint: disable=bare-except + except Exception: # pylint: disable=broad-except return_val = False return return_val -def get_extension_installed_and_cluster_configs( +def get_extension_installed_and_cluster_configs_v1( cmd, resource_group, cluster_name, @@ -237,7 +239,7 @@ def get_extension_installed_and_cluster_configs( is_ephemeralDisk_nvme_enabled = True break - except: # pylint: disable=bare-except + except Exception: # pylint: disable=broad-except is_extension_installed = False return ( @@ -252,6 +254,56 @@ def get_extension_installed_and_cluster_configs( ) +def get_extension_installed_and_cluster_configs( + cmd, + resource_group, + cluster_name +) -> Tuple[bool, bool, bool]: + client_factory = get_k8s_extension_module(CONST_K8S_EXTENSION_CLIENT_FACTORY_MOD_NAME) + client = client_factory.cf_k8s_extension_operation(cmd.cli_ctx) + k8s_extension_custom_mod = get_k8s_extension_module(CONST_K8S_EXTENSION_CUSTOM_MOD_NAME) + is_extension_installed = False + is_ephemeral_disk_enabled = False + is_elastic_san_enabled = False + + try: + extension = k8s_extension_custom_mod.show_k8s_extension( + client, + resource_group, + cluster_name, + CONST_ACSTOR_EXT_INSTALLATION_NAME, + "managedClusters", + ) + + extension_type = extension.extension_type.lower() + is_extension_installed = extension_type == CONST_ACSTOR_K8S_EXTENSION_NAME + config_settings = extension.configuration_settings + + if is_extension_installed and config_settings is not None: + is_ephemeral_disk_enabled = ( + config_settings.get("csiDriverConfigs.local-csi-driver.enabled", "False") == "True" + ) + is_elastic_san_enabled = ( + config_settings.get("csiDriverConfigs.azuresan-csi-driver.enabled", "False") == "True" + ) + + except Exception: # pylint: disable=broad-except + is_extension_installed = False + + return ( + is_extension_installed, + is_ephemeral_disk_enabled, + is_elastic_san_enabled, + ) + + +def should_delete_extension(storage_options_to_remove) -> bool: + return ( + storage_options_to_remove in [True, CONST_ACSTOR_ALL] or + (isinstance(storage_options_to_remove, list) and CONST_ACSTOR_ALL in storage_options_to_remove) + ) + + def get_container_storage_extension_installed( cmd, resource_group, diff --git a/src/aks-preview/azext_aks_preview/azurecontainerstorage/_validators.py b/src/aks-preview/azext_aks_preview/azurecontainerstorage/_validators.py index cc5625ffd68..d4f71d622a5 100644 --- a/src/aks-preview/azext_aks_preview/azurecontainerstorage/_validators.py +++ b/src/aks-preview/azext_aks_preview/azurecontainerstorage/_validators.py @@ -432,10 +432,12 @@ def validate_enable_azure_container_storage_v1_params( # pylint: disable=too-ma def validate_enable_azure_container_storage_params( + enablement_option, is_extension_installed, + is_ephemeral_disk_enabled, + is_elastic_san_enabled, is_v1_extension_installed, v1_extension_version, - storage_pool_type, storage_pool_name, storage_pool_sku, storage_pool_option, @@ -450,18 +452,28 @@ def validate_enable_azure_container_storage_params( 'that depend on Azure Container Storage.' ) - if is_extension_installed: - raise InvalidArgumentValueError( - 'Cannot enable Azure Container Storage as it is already enabled on the cluster.' - ) - - # Todo: Remove this for the 2.1.0 release - if storage_pool_type is not None and not isinstance(storage_pool_type, bool): - raise InvalidArgumentValueError( - 'The latest version of Azure Container Storage only supports ephemeral nvme storage and does not ' - 'require or support a storage-pool-type value for --enable-azure-container-storage parameter. ' - f'Please remove {storage_pool_type} from the command and try again.' - ) + if not enablement_option or enablement_option is True: + if is_extension_installed: + raise InvalidArgumentValueError( + 'Cannot enable Azure Container Storage as it is already enabled on the cluster.' + ) + else: + enablement_option_arr = enablement_option if isinstance(enablement_option, list) else [enablement_option] + enable_ephemeral_disk = CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK in enablement_option + enable_elastic_san = CONST_STORAGE_POOL_TYPE_ELASTIC_SAN in enablement_option + for option in enablement_option_arr: + if option not in [CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK, CONST_STORAGE_POOL_TYPE_ELASTIC_SAN]: + raise InvalidArgumentValueError( + f"Unsupported storage option '{option}'. " + f"Supported values are '{CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK}' " + f"and '{CONST_STORAGE_POOL_TYPE_ELASTIC_SAN}'." + ) + if is_ephemeral_disk_enabled == enable_ephemeral_disk and is_elastic_san_enabled == enable_elastic_san: + options_display = "', '".join(enablement_option_arr) + raise InvalidArgumentValueError( + f"Cannot enable the requested storage options ('{options_display}') " + "as they are already enabled on the cluster." + ) if storage_pool_name is not None: raise InvalidArgumentValueError( @@ -493,8 +505,10 @@ def validate_enable_azure_container_storage_params( def validate_disable_azure_container_storage_params( + disablement_option, is_extension_installed, - storage_pool_type, + is_ephemeral_disk_enabled, + is_elastic_san_enabled, storage_pool_name, storage_pool_sku, storage_pool_option, @@ -502,16 +516,31 @@ def validate_disable_azure_container_storage_params( ): if not is_extension_installed: raise InvalidArgumentValueError( - 'Cannot disable Azure Container Storage as it is not enabled on the cluster.' + 'Cannot disable Azure Container Storage as it could not be found on the cluster.' ) - # Todo: Remove this for the 2.1.0 release - if storage_pool_type is not None and not isinstance(storage_pool_type, bool): - raise InvalidArgumentValueError( - 'The latest version of Azure Container Storage only supports ephemeral nvme storage and does not ' - 'require or support a storage-pool-type value for --disable-azure-container-storage parameter. ' - f'Please remove {storage_pool_type} from the command and try again.' - ) + if disablement_option and disablement_option not in [True, CONST_ACSTOR_ALL]: + actionable = False + disablement_option_arr = disablement_option if isinstance(disablement_option, list) else [disablement_option] + for disable_option in disablement_option_arr: + if disable_option == CONST_ACSTOR_ALL: + actionable = True + elif disable_option == CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK: + actionable = actionable or is_ephemeral_disk_enabled + elif disable_option == CONST_STORAGE_POOL_TYPE_ELASTIC_SAN: + actionable = actionable or is_elastic_san_enabled + else: + raise InvalidArgumentValueError( + f"Cannot disable unsupported storage option '{disable_option}'. " + f"Supported values are '{CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK}', " + f"'{CONST_STORAGE_POOL_TYPE_ELASTIC_SAN}' and '{CONST_ACSTOR_ALL}'." + ) + if not actionable: + options_display = "', '".join(disablement_option_arr) + raise InvalidArgumentValueError( + f"Cannot disable the requested storage options ('{options_display}') " + "as they could not be found on the cluster." + ) if storage_pool_name is not None: raise InvalidArgumentValueError( diff --git a/src/aks-preview/azext_aks_preview/azurecontainerstorage/acstor_ops.py b/src/aks-preview/azext_aks_preview/azurecontainerstorage/acstor_ops.py index 981d8010fde..4369e6e4e1a 100644 --- a/src/aks-preview/azext_aks_preview/azurecontainerstorage/acstor_ops.py +++ b/src/aks-preview/azext_aks_preview/azurecontainerstorage/acstor_ops.py @@ -34,6 +34,7 @@ get_initial_resource_value_args, perform_role_operations_on_managed_rg, validate_storagepool_creation, + should_delete_extension, ) from knack.log import get_logger @@ -693,10 +694,13 @@ def perform_disable_azure_container_storage_v1( # pylint: disable=too-many-stat logger.warning("Azure Container Storage storagepool type %s has been disabled.", storage_pool_type) -def perform_enable_azure_container_storage( +def perform_azure_container_storage_update( cmd, resource_group, cluster_name, + storage_options_to_add, + is_extension_installed=False, + storage_options_to_remove=None, is_called_from_extension=False, ): # This will be set true only when aks-preview extension is used @@ -707,36 +711,70 @@ def perform_enable_azure_container_storage( client_factory = get_k8s_extension_module(CONST_K8S_EXTENSION_CLIENT_FACTORY_MOD_NAME) client = client_factory.cf_k8s_extension_operation(cmd.cli_ctx) - k8s_extension_custom_mod = get_k8s_extension_module(CONST_K8S_EXTENSION_CUSTOM_MOD_NAME) - config_settings = [] - delete_extension = False - try: - result = k8s_extension_custom_mod.create_k8s_extension( - cmd, - client, - resource_group, - cluster_name, - CONST_ACSTOR_EXT_INSTALLATION_NAME, - "managedClusters", - CONST_ACSTOR_K8S_EXTENSION_NAME, - auto_upgrade_minor_version=True, - release_train="stable", - scope="cluster", - release_namespace=CONST_ACSTOR_EXT_INSTALLATION_NAMESPACE, - configuration_settings=config_settings, - ) - op_text = "Azure Container Storage successfully installed" - long_op_result = LongRunningOperation(cmd.cli_ctx)(result) - if long_op_result.provisioning_state == "Succeeded": - logger.warning(op_text) - except Exception as ex: # pylint: disable=broad-except - logger.error("Azure Container Storage failed to install.\nError: %s", ex) - delete_extension = True + delete_extension = should_delete_extension(storage_options_to_remove) + delete_extension_auto = False + + if not delete_extension: + config_settings = [] + + storage_options_to_add = storage_options_to_add if isinstance(storage_options_to_add, (list, str)) else [] + storage_options_to_remove = storage_options_to_remove \ + if isinstance(storage_options_to_remove, (list, str)) else [] + + if CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK in storage_options_to_remove: + config_settings.append({"csiDriverConfigs.local-csi-driver.enabled": "False"}) + elif CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK in storage_options_to_add: + config_settings.append({"csiDriverConfigs.local-csi-driver.enabled": "True"}) + + if CONST_STORAGE_POOL_TYPE_ELASTIC_SAN in storage_options_to_remove: + config_settings.append({"csiDriverConfigs.azuresan-csi-driver.enabled": "False"}) + elif CONST_STORAGE_POOL_TYPE_ELASTIC_SAN in storage_options_to_add: + config_settings.append({"csiDriverConfigs.azuresan-csi-driver.enabled": "True"}) - if delete_extension: - logger.warning("Cleaning up the cluster by disabling Azure Container Storage") + try: + if is_extension_installed: + result = k8s_extension_custom_mod.update_k8s_extension( + cmd, + client, + resource_group, + cluster_name, + CONST_ACSTOR_EXT_INSTALLATION_NAME, + "managedClusters", + configuration_settings=config_settings, + yes=True, + ) + op_text = "Azure Container Storage successfully updated" + else: + result = k8s_extension_custom_mod.create_k8s_extension( + cmd, + client, + resource_group, + cluster_name, + CONST_ACSTOR_EXT_INSTALLATION_NAME, + "managedClusters", + CONST_ACSTOR_K8S_EXTENSION_NAME, + auto_upgrade_minor_version=True, + release_train="stable", + scope="cluster", + release_namespace=CONST_ACSTOR_EXT_INSTALLATION_NAMESPACE, + configuration_settings=config_settings, + ) + op_text = "Azure Container Storage successfully installed" + long_op_result = LongRunningOperation(cmd.cli_ctx)(result) + if long_op_result.provisioning_state == "Succeeded": + logger.warning(op_text) + except Exception as ex: # pylint: disable=broad-except + if is_extension_installed: + logger.error("Azure Container Storage failed to update.\nError: %s", ex) + else: + logger.error("Azure Container Storage failed to install.\nError: %s", ex) + delete_extension_auto = True + + if delete_extension or delete_extension_auto: + if delete_extension_auto: + logger.warning("Cleaning up the cluster by disabling Azure Container Storage") try: delete_op_result = k8s_extension_custom_mod.delete_k8s_extension( cmd, @@ -750,47 +788,12 @@ def perform_enable_azure_container_storage( LongRunningOperation(cmd.cli_ctx)(delete_op_result) logger.warning("Azure Container Storage has been disabled.") - logger.warning( - "Please retry enabling Azure Container Storage by running " - "`az aks update` along with `--enable-azure-container-storage`" - ) + if delete_extension_auto: + logger.warning( + "Please retry enabling Azure Container Storage by running " + "`az aks update` along with `--enable-azure-container-storage`" + ) except Exception as delete_ex: raise UnknownError( "Failed to disable Azure Container Storage with error: %s" % delete_ex ) from delete_ex - - -def perform_disable_azure_container_storage( - cmd, - resource_group, - cluster_name, - is_called_from_extension=False, -): - # This will be set true only when aks-preview extension is used - # and we want the aks-preview ManagedClusterDecorator to call the - # perform_disable_azure_container_storage function. - if not is_called_from_extension: - return - - client_factory = get_k8s_extension_module(CONST_K8S_EXTENSION_CLIENT_FACTORY_MOD_NAME) - client = client_factory.cf_k8s_extension_operation(cmd.cli_ctx) - k8s_extension_custom_mod = get_k8s_extension_module(CONST_K8S_EXTENSION_CUSTOM_MOD_NAME) - - try: - delete_op_result = k8s_extension_custom_mod.delete_k8s_extension( - cmd, - client, - resource_group, - cluster_name, - CONST_ACSTOR_EXT_INSTALLATION_NAME, - "managedClusters", - yes=True, - no_wait=False, - ) - - LongRunningOperation(cmd.cli_ctx)(delete_op_result) - logger.warning("Azure Container Storage has been disabled.") - except Exception as delete_ex: - raise UnknownError( - "Failed to disable Azure Container Storage with error: %s" % delete_ex - ) from delete_ex diff --git a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py index 0d8dba2d99d..5674a78ab22 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -87,8 +87,7 @@ from azext_aks_preview.azurecontainerstorage.acstor_ops import ( perform_disable_azure_container_storage_v1, perform_enable_azure_container_storage_v1, - perform_disable_azure_container_storage, - perform_enable_azure_container_storage, + perform_azure_container_storage_update, ) from azext_aks_preview.azuremonitormetrics.azuremonitorprofile import ( ensure_azure_monitor_profile_prerequisites, @@ -231,8 +230,7 @@ def external_functions(self) -> SimpleNamespace: external_functions[ "perform_disable_azure_container_storage_v1" ] = perform_disable_azure_container_storage_v1 - external_functions["perform_enable_azure_container_storage"] = perform_enable_azure_container_storage - external_functions["perform_disable_azure_container_storage"] = perform_disable_azure_container_storage + external_functions["perform_azure_container_storage_update"] = perform_azure_container_storage_update external_functions["sanitize_loganalytics_ws_resource_id"] = sanitize_loganalytics_ws_resource_id # Override base module function with preview version that uses REST API to avoid # "Request Header Fields Too Large" errors @@ -4504,13 +4502,18 @@ def set_up_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: """ self._ensure_mc(mc) - if self.context.raw_param.get("enable_azure_container_storage") is not None: - self.context.set_intermediate("enable_azure_container_storage", True, overwrite_exists=True) + enable_azure_container_storage_param = self.context.raw_param.get("enable_azure_container_storage") + if enable_azure_container_storage_param: + self.context.set_intermediate( + "enable_azure_container_storage", + enable_azure_container_storage_param, + overwrite_exists=True, + ) container_storage_version = self.context.raw_param.get("container_storage_version") if container_storage_version is not None and container_storage_version == CONST_ACSTOR_VERSION_V1: # read the azure container storage values passed - pool_type = self.context.raw_param.get("enable_azure_container_storage") + pool_type = enable_azure_container_storage_param enable_azure_container_storage = pool_type is not None ephemeral_disk_volume_type = self.context.raw_param.get("ephemeral_disk_volume_type") ephemeral_disk_nvme_perf_tier = self.context.raw_param.get("ephemeral_disk_nvme_perf_tier") @@ -4618,7 +4621,6 @@ def set_up_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: overwrite_exists=True ) else: - enable_azure_container_storage = self.context.raw_param.get("enable_azure_container_storage") storage_pool_name = self.context.raw_param.get("storage_pool_name") pool_sku = self.context.raw_param.get("storage_pool_sku") pool_option = self.context.raw_param.get("storage_pool_option") @@ -4628,10 +4630,12 @@ def set_up_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: validate_enable_azure_container_storage_params, ) validate_enable_azure_container_storage_params( + enable_azure_container_storage_param, + False, + False, False, False, "", - enable_azure_container_storage, storage_pool_name, pool_sku, pool_option, @@ -5228,10 +5232,11 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None: is_called_from_extension=True, ) else: - self.context.external_functions.perform_enable_azure_container_storage( + self.context.external_functions.perform_azure_container_storage_update( self.cmd, self.context.get_resource_group_name(), self.context.get_name(), + enable_azure_container_storage, is_called_from_extension=True, ) @@ -5611,7 +5616,7 @@ def update_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: pool_size = self.context.raw_param.get("storage_pool_size") agentpool_details = {} from azext_aks_preview.azurecontainerstorage._helpers import ( - get_extension_installed_and_cluster_configs + get_extension_installed_and_cluster_configs_v1 ) try: ( @@ -5623,7 +5628,7 @@ def update_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: current_core_value, existing_ephemeral_disk_volume_type, existing_perf_tier, - ) = get_extension_installed_and_cluster_configs( + ) = get_extension_installed_and_cluster_configs_v1( self.cmd, self.context.get_resource_group_name(), self.context.get_name(), @@ -5887,13 +5892,16 @@ def update_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: ) from azext_aks_preview.azurecontainerstorage._helpers import ( - get_container_storage_extension_installed + get_extension_installed_and_cluster_configs ) - is_extension_installed, _ = get_container_storage_extension_installed( + ( + is_extension_installed, + is_ephemeralDisk_enabled, + is_elasticSan_enabled, + ) = get_extension_installed_and_cluster_configs( self.cmd, self.context.get_resource_group_name(), self.context.get_name(), - CONST_ACSTOR_EXT_INSTALLATION_NAME, ) from azext_aks_preview.azurecontainerstorage._helpers import ( @@ -5917,10 +5925,12 @@ def update_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: validate_enable_azure_container_storage_params, ) validate_enable_azure_container_storage_params( + enable_azure_container_storage_param, is_extension_installed, + is_ephemeralDisk_enabled, + is_elasticSan_enabled, is_containerstorage_v1_installed, v1_extension_version, - enable_azure_container_storage_param, storage_pool_name, pool_sku, pool_option, @@ -5947,6 +5957,24 @@ def update_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: " after re-enabling it by running 'az aks update --enable-azure-container-storage'." " Would you like to proceed with the disabling?" ) + from azext_aks_preview.azurecontainerstorage._helpers import should_delete_extension + if not should_delete_extension(disable_azure_container_storage_param): + storage_option_display = storage_option_param_str = disable_azure_container_storage_param + if isinstance(disable_azure_container_storage_param, list): + storage_options = disable_azure_container_storage_param + storage_option_param_str = " ".join(storage_options) + if len(storage_options) > 2: + storage_options = ["', '".join(storage_options[:-1]), storage_options[-1]] + storage_option_display = "' and '".join(storage_options) + msg = ( + "Please make sure there are no existing PVs and PVCs that are provisioned by Azure" + f" Container Storage for '{storage_option_display}' before disabling." + " If storage options are disabled with remaining PVs and PVCs, any data associated with" + " those PVs and PVCs will not be erased and the nodes will be left in an unclean state." + " The PVs and PVCs can only be cleaned up after re-enabling it by running" + f" 'az aks update --enable-azure-container-storage {storage_option_param_str}'." + " Would you like to proceed with the disabling?" + ) if not self.context.get_yes() and not prompt_y_n(msg, default="n"): pre_disable_validate = True raise DecoratorEarlyExitException() @@ -5955,8 +5983,10 @@ def update_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: validate_disable_azure_container_storage_params ) validate_disable_azure_container_storage_params( - is_extension_installed, disable_azure_container_storage_param, + is_extension_installed, + is_ephemeralDisk_enabled, + is_elasticSan_enabled, storage_pool_name, pool_sku, pool_option, @@ -5969,11 +5999,17 @@ def update_azure_container_storage(self, mc: ManagedCluster) -> ManagedCluster: overwrite_exists=True ) - if enable_azure_container_storage_param: - self.context.set_intermediate("enable_azure_container_storage", True) - - if disable_azure_container_storage_param: - self.context.set_intermediate("disable_azure_container_storage", True) + self.context.set_intermediate( + "enable_azure_container_storage", + enable_azure_container_storage_param, + overwrite_exists=True, + ) + self.context.set_intermediate( + "disable_azure_container_storage", + disable_azure_container_storage_param, + overwrite_exists=True, + ) + self.context.set_intermediate("is_extension_installed", is_extension_installed, overwrite_exists=True) return mc @@ -7678,10 +7714,11 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None: is_called_from_extension=True, ) else: - self.context.external_functions.perform_enable_azure_container_storage( + self.context.external_functions.perform_azure_container_storage_update( self.cmd, self.context.get_resource_group_name(), self.context.get_name(), + enable_azure_container_storage, is_called_from_extension=True, ) @@ -7712,10 +7749,13 @@ def postprocessing_after_mc_created(self, cluster: ManagedCluster) -> None: is_called_from_extension=True, ) else: - self.context.external_functions.perform_disable_azure_container_storage( + self.context.external_functions.perform_azure_container_storage_update( self.cmd, self.context.get_resource_group_name(), self.context.get_name(), + None, + is_extension_installed, + disable_azure_container_storage, is_called_from_extension=True, ) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 7bf3f16eb28..f7bd3c1f4a1 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -14565,6 +14565,168 @@ def test_aks_create_with_azurecontainerstorage(self, resource_group, resource_gr ], ) + @live_only() + @AllowLargeResponse(999999) + @AKSCustomResourceGroupPreparer( + random_name_length=17, name_prefix="clitest", location="uksouth" + ) + def test_aks_create_with_azurecontainerstorage_with_ephemeral_disk_parameters( + self, resource_group, resource_group_location + ): + # reset the count so in replay mode the random names will start with 0 + self.test_resources_count = 0 + # kwargs for string formatting + aks_name = self.create_random_name("cliakstest", 16) + + node_vm_size = "standard_l8s_v3" + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + "location": resource_group_location, + "resource_type": "Microsoft.ContainerService/ManagedClusters", + "ssh_key_value": self.generate_ssh_keys(), + "node_vm_size": node_vm_size, + } + ) + + # add k8s-extension extension for azurecontainerstorage operations. + self.cmd("extension add --name k8s-extension") + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} --location={location} " + "--ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} " + "--node-count 3 --enable-managed-identity --enable-azure-container-storage ephemeralDisk --output=json" + ) + + # enabling azurecontainerstorage will not affect any field in the cluster. + # the only check we should perform is to verify that the cluster is provisioned successfully. + self.cmd( + create_cmd, + checks=[ + self.check("provisioningState", "Succeeded"), + ], + ) + + # delete + cmd = ( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait" + ) + self.cmd( + cmd, + checks=[ + self.is_empty(), + ], + ) + + @live_only() + @AllowLargeResponse(999999) + @AKSCustomResourceGroupPreparer( + random_name_length=17, name_prefix="clitest", location="uksouth" + ) + def test_aks_create_with_azurecontainerstorage_with_elastic_san_parameters( + self, resource_group, resource_group_location + ): + # reset the count so in replay mode the random names will start with 0 + self.test_resources_count = 0 + # kwargs for string formatting + aks_name = self.create_random_name("cliakstest", 16) + + node_vm_size = "standard_d4s_v3" + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + "location": resource_group_location, + "resource_type": "Microsoft.ContainerService/ManagedClusters", + "ssh_key_value": self.generate_ssh_keys(), + "node_vm_size": node_vm_size, + } + ) + + # add k8s-extension extension for azurecontainerstorage operations. + self.cmd("extension add --name k8s-extension") + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} --location={location} " + "--ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} " + "--node-count 3 --enable-managed-identity --enable-azure-container-storage elasticSan --output=json" + ) + + # enabling azurecontainerstorage will not affect any field in the cluster. + # the only check we should perform is to verify that the cluster is provisioned successfully. + self.cmd( + create_cmd, + checks=[ + self.check("provisioningState", "Succeeded"), + ], + ) + + # delete + cmd = ( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait" + ) + self.cmd( + cmd, + checks=[ + self.is_empty(), + ], + ) + + @live_only() + @AllowLargeResponse(999999) + @AKSCustomResourceGroupPreparer( + random_name_length=17, name_prefix="clitest", location="uksouth" + ) + def test_aks_create_with_azurecontainerstorage_with_multiple_parameters( + self, resource_group, resource_group_location + ): + # reset the count so in replay mode the random names will start with 0 + self.test_resources_count = 0 + # kwargs for string formatting + aks_name = self.create_random_name("cliakstest", 16) + + node_vm_size = "standard_d4s_v3" + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + "location": resource_group_location, + "resource_type": "Microsoft.ContainerService/ManagedClusters", + "ssh_key_value": self.generate_ssh_keys(), + "node_vm_size": node_vm_size, + } + ) + + # add k8s-extension extension for azurecontainerstorage operations. + self.cmd("extension add --name k8s-extension") + + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} --location={location} " + "--ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} " + "--node-count 3 --enable-managed-identity --enable-azure-container-storage ephemeralDisk elasticSan --output=json" + ) + + # enabling azurecontainerstorage will not affect any field in the cluster. + # the only check we should perform is to verify that the cluster is provisioned successfully. + self.cmd( + create_cmd, + checks=[ + self.check("provisioningState", "Succeeded"), + ], + ) + + # delete + cmd = ( + "aks delete --resource-group={resource_group} --name={name} --yes --no-wait" + ) + self.cmd( + cmd, + checks=[ + self.is_empty(), + ], + ) + @live_only() @AllowLargeResponse(99999) @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='ukwest') @@ -14581,7 +14743,7 @@ def test_aks_update_with_azurecontainerstorage(self, resource_group, resource_gr }) # add k8s-extension extension for azurecontainerstorage operations. - # self.cmd('extension add --name k8s-extension') + self.cmd('extension add --name k8s-extension') # create: without enable-azure-container-storage create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} --node-count 3 --enable-managed-identity --output=json' @@ -14644,6 +14806,244 @@ def test_aks_update_with_azurecontainerstorage(self, resource_group, resource_gr self.is_empty(), ]) + @live_only() + @AllowLargeResponse(99999) + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='uksouth') + def test_aks_update_with_azurecontainerstorage_with_ephemeral_disk(self, resource_group, resource_group_location): + + aks_name = self.create_random_name('cliakstest', 16) + node_vm_size = 'standard_l8s_v3' + self.kwargs.update({ + 'resource_group': resource_group, + 'name': aks_name, + 'location': resource_group_location, + 'ssh_key_value': self.generate_ssh_keys(), + 'node_vm_size': node_vm_size, + }) + + # add k8s-extension extension for azurecontainerstorage operations. + self.cmd('extension add --name k8s-extension') + + # create: without enable-azure-container-storage + create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} --node-count 3 --enable-managed-identity --output=json' + self.cmd(create_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # update: enable-azure-container-storage + update_cmd = 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' \ + '--enable-azure-container-storage ephemeralDisk' + + self.cmd(update_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # Verify that the azure-container-storage extension is installed + extension_list_cmd = "k8s-extension list --resource-group={resource_group} --cluster-name={name} --cluster-type managedClusters" + extensions = self.cmd(extension_list_cmd).get_output_in_json() + + # Check if azure-container-storage extension exists + acs_extension_found = False + for extension in extensions: + if extension.get("name") == CONST_ACSTOR_EXT_INSTALLATION_NAME : + acs_extension_found = True + # Additional checks on the extension properties + assert extension.get("provisioningState") == "Succeeded", "Extension provisioning failed" + assert extension.get("extensionType") == CONST_ACSTOR_K8S_EXTENSION_NAME, "Wrong extension type" + break + + assert acs_extension_found, "Azure Container Storage not found" + + # Sleep for 5 mins before next operation, + # since update operations take + # some time to finish. + time.sleep(10 * 60) + + # update: disable-azure-container-storage + update_cmd = 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' \ + '--disable-azure-container-storage' + self.cmd(update_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # Verify that the azure-container-storage extension doesn't exist anymore + extension_list_cmd = "k8s-extension list --resource-group={resource_group} --cluster-name={name} --cluster-type managedClusters" + extensions = self.cmd(extension_list_cmd).get_output_in_json() + + # Check if azure-container-storage extension sill exists + acs_extension_found = False + for extension in extensions: + if extension.get("name") == CONST_ACSTOR_EXT_INSTALLATION_NAME : + acs_extension_found = True + break + + assert not acs_extension_found, "Azure Container Storage v2 still exists after disable operation" + + # delete + cmd = 'aks delete --resource-group={resource_group} --name={name} --yes --no-wait' + self.cmd(cmd, checks=[ + self.is_empty(), + ]) + + @live_only() + @AllowLargeResponse(99999) + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='uksouth') + def test_aks_update_with_azurecontainerstorage_with_elastic_san(self, resource_group, resource_group_location): + + aks_name = self.create_random_name('cliakstest', 16) + node_vm_size = 'standard_l8s_v3' + self.kwargs.update({ + 'resource_group': resource_group, + 'name': aks_name, + 'location': resource_group_location, + 'ssh_key_value': self.generate_ssh_keys(), + 'node_vm_size': node_vm_size, + }) + + # add k8s-extension extension for azurecontainerstorage operations. + self.cmd('extension add --name k8s-extension') + + # create: without enable-azure-container-storage + create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} --node-count 3 --enable-managed-identity --output=json' + self.cmd(create_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # update: enable-azure-container-storage + update_cmd = 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' \ + '--enable-azure-container-storage elasticSan' + + self.cmd(update_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # Verify that the azure-container-storage extension is installed + extension_list_cmd = "k8s-extension list --resource-group={resource_group} --cluster-name={name} --cluster-type managedClusters" + extensions = self.cmd(extension_list_cmd).get_output_in_json() + + # Check if azure-container-storage extension exists + acs_extension_found = False + for extension in extensions: + if extension.get("name") == CONST_ACSTOR_EXT_INSTALLATION_NAME : + acs_extension_found = True + # Additional checks on the extension properties + assert extension.get("provisioningState") == "Succeeded", "Extension provisioning failed" + assert extension.get("extensionType") == CONST_ACSTOR_K8S_EXTENSION_NAME, "Wrong extension type" + break + + assert acs_extension_found, "Azure Container Storage not found" + + # Sleep for 5 mins before next operation, + # since update operations take + # some time to finish. + time.sleep(10 * 60) + + # update: disable-azure-container-storage + update_cmd = 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' \ + '--disable-azure-container-storage' + self.cmd(update_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # Verify that the azure-container-storage extension doesn't exist anymore + extension_list_cmd = "k8s-extension list --resource-group={resource_group} --cluster-name={name} --cluster-type managedClusters" + extensions = self.cmd(extension_list_cmd).get_output_in_json() + + # Check if azure-container-storage extension sill exists + acs_extension_found = False + for extension in extensions: + if extension.get("name") == CONST_ACSTOR_EXT_INSTALLATION_NAME : + acs_extension_found = True + break + + assert not acs_extension_found, "Azure Container Storage v2 still exists after disable operation" + + # delete + cmd = 'aks delete --resource-group={resource_group} --name={name} --yes --no-wait' + self.cmd(cmd, checks=[ + self.is_empty(), + ]) + + + @live_only() + @AllowLargeResponse(99999) + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='uksouth') + def test_aks_update_with_azurecontainerstorage_with_multiple_parameters(self, resource_group, resource_group_location): + + aks_name = self.create_random_name('cliakstest', 16) + node_vm_size = 'standard_l8s_v3' + self.kwargs.update({ + 'resource_group': resource_group, + 'name': aks_name, + 'location': resource_group_location, + 'ssh_key_value': self.generate_ssh_keys(), + 'node_vm_size': node_vm_size, + }) + + # add k8s-extension extension for azurecontainerstorage operations. + self.cmd('extension add --name k8s-extension') + + # create: without enable-azure-container-storage + create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} --node-count 3 --enable-managed-identity --output=json' + self.cmd(create_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # update: enable-azure-container-storage + update_cmd = 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' \ + '--enable-azure-container-storage ephemeralDisk elasticSan' + + self.cmd(update_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # Verify that the azure-container-storage extension is installed + extension_list_cmd = "k8s-extension list --resource-group={resource_group} --cluster-name={name} --cluster-type managedClusters" + extensions = self.cmd(extension_list_cmd).get_output_in_json() + + # Check if azure-container-storage extension exists + acs_extension_found = False + for extension in extensions: + if extension.get("name") == CONST_ACSTOR_EXT_INSTALLATION_NAME : + acs_extension_found = True + # Additional checks on the extension properties + assert extension.get("provisioningState") == "Succeeded", "Extension provisioning failed" + assert extension.get("extensionType") == CONST_ACSTOR_K8S_EXTENSION_NAME, "Wrong extension type" + break + + assert acs_extension_found, "Azure Container Storage not found" + + # Sleep for 5 mins before next operation, + # since update operations take + # some time to finish. + time.sleep(10 * 60) + + # update: disable-azure-container-storage + update_cmd = 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' \ + '--disable-azure-container-storage' + self.cmd(update_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # Verify that the azure-container-storage extension doesn't exist anymore + extension_list_cmd = "k8s-extension list --resource-group={resource_group} --cluster-name={name} --cluster-type managedClusters" + extensions = self.cmd(extension_list_cmd).get_output_in_json() + + # Check if azure-container-storage extension sill exists + acs_extension_found = False + for extension in extensions: + if extension.get("name") == CONST_ACSTOR_EXT_INSTALLATION_NAME : + acs_extension_found = True + break + + assert not acs_extension_found, "Azure Container Storage v2 still exists after disable operation" + + # delete + cmd = 'aks delete --resource-group={resource_group} --name={name} --yes --no-wait' + self.cmd(cmd, checks=[ + self.is_empty(), + ]) + @live_only() @AllowLargeResponse() @AKSCustomResourceGroupPreparer( diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py index db7b3c54948..22a95ec07f9 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py @@ -1675,26 +1675,37 @@ def test_valid_cluster_update(self): ) class TestValidateDisableAzureContainerStorage(unittest.TestCase): - def test_disable_with_storagepool_type_params(self): + def test_disable_unsupported_type(self): storage_pool_type = acstor_consts.CONST_STORAGE_POOL_TYPE_AZURE_DISK with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_disable_azure_container_storage_params( - True, storage_pool_type, "", "", "", "", + storage_pool_type, True, False, False, "", "", "", "", ) err = ( - 'The latest version of Azure Container Storage only supports ephemeral nvme storage and does not ' - 'require or support a storage-pool-type value for --disable-azure-container-storage parameter. ' - f'Please remove {storage_pool_type} from the command and try again.' + f"Cannot disable unsupported storage option '{storage_pool_type}'. " + "Supported values are 'ephemeralDisk', 'elasticSan' and 'all'." + ) + self.assertEqual(str(cm.exception), err) + + def test_disable_already_disabled_type(self): + storage_pool_type = acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK + with self.assertRaises(InvalidArgumentValueError) as cm: + acstor_validator.validate_disable_azure_container_storage_params( + storage_pool_type, True, False, False, "", "", "", "", + ) + err = ( + f"Cannot disable the requested storage options ('{storage_pool_type}') " + "as they could not be found on the cluster." ) self.assertEqual(str(cm.exception), err) def test_disable_when_acstor_not_enabled(self): with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_disable_azure_container_storage_params( - False, "", "", "", "", "", + "", False, False, False, "", "", "", "", ) err = ( - 'Cannot disable Azure Container Storage as it is not enabled on the cluster.' + 'Cannot disable Azure Container Storage as it could not be found on the cluster.' ) self.assertEqual(str(cm.exception), err) @@ -1702,7 +1713,7 @@ def test_disable_with_storage_pool_name(self): storage_pool_name = "valid-name" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_disable_azure_container_storage_params( - True, True, storage_pool_name, "", "", "", + True, True, False, False, storage_pool_name, "", "", "", ) err = ( 'The latest version of Azure Container Storage does not ' @@ -1715,7 +1726,7 @@ def test_disable_with_storage_pool_sku(self): storage_pool_sku = "valid-sku" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_disable_azure_container_storage_params( - True, True, None, storage_pool_sku, "", "", + True, True, False, False, None, storage_pool_sku, "", "", ) err = ( 'The latest version of Azure Container Storage does not ' @@ -1728,7 +1739,7 @@ def test_disable_with_storage_pool_option(self): storage_pool_option = "valid-option" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_disable_azure_container_storage_params( - True, True, None, None, storage_pool_option, "", + True, True, False, False, None, None, storage_pool_option, "", ) err = ( 'The latest version of Azure Container Storage does not ' @@ -1741,7 +1752,7 @@ def test_disable_with_storage_pool_size(self): storage_pool_size = "valid-size" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_disable_azure_container_storage_params( - True, True, None, None, None, storage_pool_size + True, True, False, False, None, None, None, storage_pool_size ) err = ( 'The latest version of Azure Container Storage does not ' @@ -1751,10 +1762,15 @@ def test_disable_with_storage_pool_size(self): self.assertEqual(str(cm.exception), err) class TestValidateEnableAzureContainerStorage(unittest.TestCase): - def test_enable_when_already_enabled(self): + def test_enable_extension(self): + acstor_validator.validate_enable_azure_container_storage_params( + True, False, False, False, False, "", None, None, None, None, + ) + + def test_enable_extension_when_already_enabled(self): with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_enable_azure_container_storage_params( - True, "", "", "", "", "", "", "", + "", True, False, False, False, "", None, None, None, None, ) err = ( 'Cannot enable Azure Container Storage as it is already enabled on the cluster.' @@ -1765,7 +1781,7 @@ def test_enable_when_v1_installed(self): v1_extension_version = "1.3.0" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_enable_azure_container_storage_params( - False, True, v1_extension_version, "", "", "", "", "", + "", False, False, False, True, v1_extension_version, None, None, None, None, ) err = ( f'Failed to enable the latest version of Azure Container Storage as version {v1_extension_version} ' @@ -1776,16 +1792,77 @@ def test_enable_when_v1_installed(self): ) self.assertEqual(str(cm.exception), err) - def test_enable_with_storagepool_type(self): - storage_pool_type = acstor_consts.CONST_STORAGE_POOL_TYPE_AZURE_DISK + def test_enable_supported_type_when_extension_disabled(self): + storage_type = acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK + acstor_validator.validate_enable_azure_container_storage_params( + storage_type, False, False, False, False, "", None, None, None, None, + ) + + def test_enable_supported_type_when_extension_enabled(self): + storage_type = acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK + acstor_validator.validate_enable_azure_container_storage_params( + storage_type, True, False, False, False, "", None, None, None, None, + ) + + def test_enable_supported_type_when_already_enabled(self): + storage_type = acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK + with self.assertRaises(InvalidArgumentValueError) as cm: + acstor_validator.validate_enable_azure_container_storage_params( + storage_type, True, True, False, False, "", None, None, None, None, + ) + err = ( + f"Cannot enable the requested storage options ('{storage_type}') " + "as they are already enabled on the cluster." + ) + self.assertEqual(str(cm.exception), err) + + def test_enable_multiple_types_when_extension_disabled(self): + storage_types = [acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK, acstor_consts.CONST_STORAGE_POOL_TYPE_ELASTIC_SAN] + acstor_validator.validate_enable_azure_container_storage_params( + storage_types, False, False, False, False, "", None, None, None, None, + ) + + def test_enable_multiple_types_when_some_are_not_enabled(self): + storage_types = [acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK, acstor_consts.CONST_STORAGE_POOL_TYPE_ELASTIC_SAN] + acstor_validator.validate_enable_azure_container_storage_params( + storage_types, True, True, False, False, "", None, None, None, None, + ) + + def test_enable_multiple_types_when_all_are_already_enabled(self): + storage_types = [acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK, acstor_consts.CONST_STORAGE_POOL_TYPE_ELASTIC_SAN] with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_enable_azure_container_storage_params( - False, False, "", storage_pool_type, None, None, None, None, + storage_types, True, True, True, False, "", None, None, None, None, ) err = ( - 'The latest version of Azure Container Storage only supports ephemeral nvme storage and does not ' - 'require or support a storage-pool-type value for --enable-azure-container-storage parameter. ' - f'Please remove {storage_pool_type} from the command and try again.' + f"Cannot enable the requested storage options ('{storage_types[0]}', '{storage_types[1]}') " + "as they are already enabled on the cluster." + ) + self.assertEqual(str(cm.exception), err) + + def test_enable_multiple_types_when_some_are_unsupported(self): + unsupported_storage_type = acstor_consts.CONST_STORAGE_POOL_TYPE_AZURE_DISK + supported_storage_type = acstor_consts.CONST_STORAGE_POOL_TYPE_EPHEMERAL_DISK + storage_types = [supported_storage_type, unsupported_storage_type] + with self.assertRaises(InvalidArgumentValueError) as cm: + acstor_validator.validate_enable_azure_container_storage_params( + storage_types, False, False, False, False, "", None, None, None, None, + ) + err = ( + f"Unsupported storage option '{unsupported_storage_type}'. " + "Supported values are 'ephemeralDisk' and 'elasticSan'." + ) + self.assertEqual(str(cm.exception), err) + + def test_enable_unsupported_type(self): + storage_type = acstor_consts.CONST_STORAGE_POOL_TYPE_AZURE_DISK + with self.assertRaises(InvalidArgumentValueError) as cm: + acstor_validator.validate_enable_azure_container_storage_params( + storage_type, False, False, False, False, "", None, None, None, None, + ) + err = ( + f"Unsupported storage option '{storage_type}'. " + "Supported values are 'ephemeralDisk' and 'elasticSan'." ) self.assertEqual(str(cm.exception), err) @@ -1793,7 +1870,7 @@ def test_enable_with_storage_pool_name(self): storage_pool_name = "valid-name" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_enable_azure_container_storage_params( - False, False, "", None, storage_pool_name, None, None, None, + None, False, False, False, False, "", storage_pool_name, None, None, None, ) err = ( 'The latest version of Azure Container Storage does not ' @@ -1806,7 +1883,7 @@ def test_enable_with_storage_pool_sku(self): storage_pool_sku = "valid-sku" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_enable_azure_container_storage_params( - False, False, "", None, None, storage_pool_sku, None, None, + None, False, False, False, False, "", None, storage_pool_sku, None, None, ) err = ( 'The latest version of Azure Container Storage does not ' @@ -1814,12 +1891,12 @@ def test_enable_with_storage_pool_sku(self): f'Please remove --storage-pool-sku {storage_pool_sku} from the command and try again.' ) self.assertEqual(str(cm.exception), err) - + def test_enable_with_storage_pool_option(self): storage_pool_option = "valid-option" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_enable_azure_container_storage_params( - False, False, "", None, None, None, storage_pool_option, None, + None, False, False, False, False, "", None, None, storage_pool_option, None, ) err = ( 'The latest version of Azure Container Storage does not ' @@ -1832,7 +1909,7 @@ def test_enable_with_storagepool_size(self): storage_pool_size = "valid-size" with self.assertRaises(InvalidArgumentValueError) as cm: acstor_validator.validate_enable_azure_container_storage_params( - False, False, "", None, None, None, None, storage_pool_size, + None, False, False, False, False, "", None, None, None, storage_pool_size, ) err = ( 'The latest version of Azure Container Storage does not ' diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 1d91f9c6ff8..1e0fbca9d4c 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import find_packages, setup -VERSION = "19.0.0b22" +VERSION = "19.0.0b23" CLASSIFIERS = [ "Development Status :: 4 - Beta",