From 7deb0203b04a25698fcecd0cecb4e4bfaf696427 Mon Sep 17 00:00:00 2001 From: Jonathan Innis Date: Tue, 24 Aug 2021 16:29:19 -0700 Subject: [PATCH] Custom User Confirmation for Partners (#70) * Custom user confirmation * Check for disable confirm prompty for confirmation * Add yes to delete command --- .../azext_k8s_extension/_help.py | 40 ++++++++++++++----- .../azext_k8s_extension/_params.py | 5 +++ .../azext_k8s_extension/commands.py | 2 +- .../azext_k8s_extension/custom.py | 6 +-- .../partner_extensions/AzureDefender.py | 19 +-------- .../partner_extensions/AzureMLKubernetes.py | 16 +++----- .../partner_extensions/ContainerInsights.py | 19 +-------- .../partner_extensions/DefaultExtension.py | 12 +++++- .../DefaultExtensionWithIdentity.py | 7 +--- .../partner_extensions/OpenServiceMesh.py | 10 ++--- .../PartnerExtensionModel.py | 2 +- 11 files changed, 65 insertions(+), 73 deletions(-) diff --git a/src/k8s-extension/azext_k8s_extension/_help.py b/src/k8s-extension/azext_k8s_extension/_help.py index 862bf4572ac..23601420648 100644 --- a/src/k8s-extension/azext_k8s_extension/_help.py +++ b/src/k8s-extension/azext_k8s_extension/_help.py @@ -10,25 +10,47 @@ helps[f'{consts.EXTENSION_NAME}'] = """ type: group - short-summary: Commands to manage K8s-extensions. + short-summary: Commands to manage Kubernetes Extensions. """ -helps[f'{consts.EXTENSION_NAME} create'] = """ +helps[f'{consts.EXTENSION_NAME} create'] = f""" type: command - short-summary: Create a K8s-extension. + short-summary: Create a Kubernetes Extension. + examples: + - name: Create a Kubernetes Extension + text: |- + az {consts.EXTENSION_NAME} create --resource-group my-resource-group \\ + --cluster-name mycluster --cluster-type connectedClusters \\ + --name myextension --extension-type microsoft.openservicemesh \\ + --scope cluster --release-train stable """ -helps[f'{consts.EXTENSION_NAME} list'] = """ +helps[f'{consts.EXTENSION_NAME} list'] = f""" type: command - short-summary: List K8s-extensions. + short-summary: List Kubernetes Extensions. + examples: + - name: List all Kubernetes Extensions on a cluster + text: |- + az {consts.EXTENSION_NAME} list --resource-group my-resource-group \\ + --cluster-name mycluster --cluster-type connectedClusters """ -helps[f'{consts.EXTENSION_NAME} delete'] = """ +helps[f'{consts.EXTENSION_NAME} delete'] = f""" type: command - short-summary: Delete a K8s-extension. + short-summary: Delete a Kubernetes Extension. + examples: + - name: Delete an existing Kubernetes Extension + text: |- + az {consts.EXTENSION_NAME} delete --resource-group my-resource-group \\ + --cluster-name mycluster --cluster-type connectedClusters --name myextension """ -helps[f'{consts.EXTENSION_NAME} show'] = """ +helps[f'{consts.EXTENSION_NAME} show'] = f""" type: command - short-summary: Show details of a K8s-extension. + short-summary: Show a Kubernetes Extension. + examples: + - name: Show details of a Kubernetes Extension + text: |- + az {consts.EXTENSION_NAME} show --resource-group my-resource-group \\ + --cluster-name mycluster --cluster-type connectedClusters --name myextension """ diff --git a/src/k8s-extension/azext_k8s_extension/_params.py b/src/k8s-extension/azext_k8s_extension/_params.py index 93a880e9b57..58b252e7a16 100644 --- a/src/k8s-extension/azext_k8s_extension/_params.py +++ b/src/k8s-extension/azext_k8s_extension/_params.py @@ -74,3 +74,8 @@ def load_arguments(self, _): c.argument('target_namespace', help='Specify the target namespace to install to for the extension instance. This' ' parameter is required if extension scope is set to \'namespace\'') + + with self.argument_context(f"{consts.EXTENSION_NAME} delete") as c: + c.argument('yes', + options_list=['--yes', '-y'], + help='Ignore confirmation prompts') diff --git a/src/k8s-extension/azext_k8s_extension/commands.py b/src/k8s-extension/azext_k8s_extension/commands.py index abe6f501b79..96875ee06df 100644 --- a/src/k8s-extension/azext_k8s_extension/commands.py +++ b/src/k8s-extension/azext_k8s_extension/commands.py @@ -20,6 +20,6 @@ def load_command_table(self, _): is_preview=True) \ as g: g.custom_command('create', 'create_k8s_extension') - g.custom_command('delete', 'delete_k8s_extension', confirmation=True) + g.custom_command('delete', 'delete_k8s_extension') g.custom_command('list', 'list_k8s_extension', table_transformer=k8s_extension_list_table_format) g.custom_show_command('show', 'show_k8s_extension', table_transformer=k8s_extension_show_table_format) diff --git a/src/k8s-extension/azext_k8s_extension/custom.py b/src/k8s-extension/azext_k8s_extension/custom.py index b8a7e51bbcf..1b54ea8ebd1 100644 --- a/src/k8s-extension/azext_k8s_extension/custom.py +++ b/src/k8s-extension/azext_k8s_extension/custom.py @@ -191,7 +191,7 @@ def update_k8s_extension(client, resource_group_name, cluster_type, cluster_name # return client.update(resource_group_name, cluster_rp, cluster_type, cluster_name, name, upd_extension) -def delete_k8s_extension(client, resource_group_name, cluster_name, name, cluster_type): +def delete_k8s_extension(cmd, client, resource_group_name, cluster_name, name, cluster_type, yes=False): """Delete an existing Kubernetes Extension. """ @@ -201,12 +201,12 @@ def delete_k8s_extension(client, resource_group_name, cluster_name, name, cluste try: 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", cluster_name, 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(client, resource_group_name, cluster_name, name, cluster_type) + extension_class.Delete(cmd, client, resource_group_name, cluster_name, name, cluster_type, yes) return client.delete(resource_group_name, cluster_rp, cluster_type, cluster_name, name) diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureDefender.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureDefender.py index ffb6a926328..b8da6a293b6 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureDefender.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureDefender.py @@ -8,17 +8,16 @@ from knack.log import get_logger from ..vendored_sdks.models import ExtensionInstance -from ..vendored_sdks.models import ExtensionInstanceUpdate from ..vendored_sdks.models import ScopeCluster from ..vendored_sdks.models import Scope -from .PartnerExtensionModel import PartnerExtensionModel +from .DefaultExtension import DefaultExtension from .ContainerInsights import _get_container_insights_settings logger = get_logger(__name__) -class AzureDefender(PartnerExtensionModel): +class AzureDefender(DefaultExtension): 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, @@ -59,17 +58,3 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t configuration_protected_settings=configuration_protected_settings ) return extension_instance, name, create_identity - - def Update(self, extension, auto_upgrade_minor_version, release_train, version): - """ExtensionType 'microsoft.azuredefender.kubernetes' specific validations & defaults for Update - Must create and return a valid 'ExtensionInstanceUpdate' object. - - """ - return ExtensionInstanceUpdate( - auto_upgrade_minor_version=auto_upgrade_minor_version, - release_train=release_train, - version=version - ) - - def Delete(self, client, resource_group_name, cluster_name, name, cluster_type): - pass 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 5f34fb4cbf7..c85b88c943b 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.partner_extensions.DefaultExtension import DefaultExtension import azure.mgmt.relay import azure.mgmt.relay.models @@ -27,10 +28,9 @@ from msrestazure.azure_exceptions import CloudError from .._client_factory import cf_resources -from .PartnerExtensionModel import PartnerExtensionModel +from .DefaultExtension import DefaultExtension, user_confirmation_factory from ..vendored_sdks.models import ( ExtensionInstance, - ExtensionInstanceUpdate, Scope, ScopeCluster ) @@ -41,7 +41,7 @@ # pylint: disable=too-many-instance-attributes -class AzureMLKubernetes(PartnerExtensionModel): +class AzureMLKubernetes(DefaultExtension): def __init__(self): # constants for configuration settings. self.DEFAULT_RELEASE_NAMESPACE = 'azureml' @@ -158,18 +158,12 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t ) return extension_instance, name, create_identity - def Update(self, extension, auto_upgrade_minor_version, release_train, version): - return ExtensionInstanceUpdate( - auto_upgrade_minor_version=auto_upgrade_minor_version, - release_train=release_train, - version=version - ) - - def Delete(self, client, resource_group_name, cluster_name, name, cluster_type): + def Delete(self, cmd, client, resource_group_name, cluster_name, name, cluster_type, yes): # Give a warning message logger.warning("If nvidia.com/gpu or fuse resource is not recognized by kubernetes after this deletion, " "you probably have installed nvidia-device-plugin or fuse-device-plugin before installing AMLArc extension. " "Please try to reinstall device plugins to fix this issue.") + user_confirmation_factory(cmd, yes) def __validate_config(self, configuration_settings, configuration_protected_settings): # perform basic validation of the input config diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/ContainerInsights.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/ContainerInsights.py index 1c2a334cdda..3d4663ace14 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/ContainerInsights.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/ContainerInsights.py @@ -17,11 +17,10 @@ from msrestazure.tools import parse_resource_id, is_valid_resource_id from ..vendored_sdks.models import ExtensionInstance -from ..vendored_sdks.models import ExtensionInstanceUpdate from ..vendored_sdks.models import ScopeCluster from ..vendored_sdks.models import Scope -from .PartnerExtensionModel import PartnerExtensionModel +from .DefaultExtension import DefaultExtension from .._client_factory import ( cf_resources, cf_resource_groups, cf_log_analytics) @@ -29,7 +28,7 @@ logger = get_logger(__name__) -class ContainerInsights(PartnerExtensionModel): +class ContainerInsights(DefaultExtension): 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, @@ -71,20 +70,6 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t ) return extension_instance, name, create_identity - def Update(self, extension, auto_upgrade_minor_version, release_train, version): - """ExtensionType 'microsoft.azuremonitor.containers' specific validations & defaults for Update - Must create and return a valid 'ExtensionInstanceUpdate' object. - - """ - return ExtensionInstanceUpdate( - auto_upgrade_minor_version=auto_upgrade_minor_version, - release_train=release_train, - version=version - ) - - def Delete(self, client, resource_group_name, cluster_name, name, cluster_type): - pass - # Custom Validation Logic for Container Insights 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 8977ec4187e..f2128bc7ad1 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py @@ -5,6 +5,8 @@ # pylint: disable=unused-argument +from azure.cli.core.util import user_confirmation + from ..vendored_sdks.models import ExtensionInstance from ..vendored_sdks.models import ExtensionInstanceUpdate from ..vendored_sdks.models import ScopeCluster @@ -56,5 +58,11 @@ def Update(self, extension, auto_upgrade_minor_version, release_train, version): version=version ) - def Delete(self, client, resource_group_name, cluster_name, name, cluster_type): - pass + 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): + return + user_confirmation(message, yes=yes) diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtensionWithIdentity.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtensionWithIdentity.py index 52a372f7f84..d65d9f225ad 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtensionWithIdentity.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtensionWithIdentity.py @@ -11,10 +11,10 @@ from ..vendored_sdks.models import ScopeNamespace from ..vendored_sdks.models import Scope -from .PartnerExtensionModel import PartnerExtensionModel +from .DefaultExtension import DefaultExtension -class DefaultExtensionWithIdentity(PartnerExtensionModel): +class DefaultExtensionWithIdentity(DefaultExtension): 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, @@ -55,6 +55,3 @@ def Update(self, extension, auto_upgrade_minor_version, release_train, version): release_train=release_train, version=version ) - - def Delete(self, client, resource_group_name, cluster_name, name, cluster_type): - pass diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/OpenServiceMesh.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/OpenServiceMesh.py index 9cd1338f613..8480d155b05 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/OpenServiceMesh.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/OpenServiceMesh.py @@ -7,6 +7,7 @@ # pylint: disable=redefined-outer-name # pylint: disable=no-member +from azext_k8s_extension.partner_extensions.DefaultExtension import DefaultExtension from knack.log import get_logger from azure.cli.core.azclierror import InvalidArgumentValueError @@ -16,9 +17,7 @@ import yaml import requests -from ..partner_extensions import PartnerExtensionModel - -from .PartnerExtensionModel import PartnerExtensionModel +from .DefaultExtension import DefaultExtension from ..vendored_sdks.models import ( ExtensionInstance, @@ -32,7 +31,7 @@ logger = get_logger(__name__) -class OpenServiceMesh(PartnerExtensionModel): +class OpenServiceMesh(DefaultExtension): 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, @@ -90,9 +89,6 @@ def Update(self, extension, auto_upgrade_minor_version, release_train, version): version=version ) - def Delete(self, client, resource_group_name, cluster_name, name, cluster_type): - pass - def _validate_tested_distro(cmd, cluster_resource_group_name, cluster_name, extension_version): 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 98a6c1ea63f..8e89bfa15fc 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py @@ -23,5 +23,5 @@ def Update(self, extension: ExtensionInstance, auto_upgrade_minor_version: bool, pass @abstractmethod - def Delete(self, client, resource_group_name: str, cluster_name: str, name: str, cluster_type: str): + def Delete(self, cmd, client, resource_group_name: str, cluster_name: str, name: str, cluster_type: str, yes: bool): pass