diff --git a/src/k8s-extension/HISTORY.rst b/src/k8s-extension/HISTORY.rst index 417028b263d..b1c3036379d 100644 --- a/src/k8s-extension/HISTORY.rst +++ b/src/k8s-extension/HISTORY.rst @@ -3,6 +3,10 @@ Release History =============== +1.2.5 +++++++++++++++++++ +* Add support for Microsoft.HybridContainerService.ProvisionedClusters + 1.2.4 ++++++++++++++++++ * microsoft.azureml.kubernetes: Do not invoke `create_or_update` for already existed resources. diff --git a/src/k8s-extension/azext_k8s_extension/_params.py b/src/k8s-extension/azext_k8s_extension/_params.py index 8a08c9cebcb..da10fc26537 100644 --- a/src/k8s-extension/azext_k8s_extension/_params.py +++ b/src/k8s-extension/azext_k8s_extension/_params.py @@ -3,84 +3,126 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from azure.cli.core.commands.parameters import ( - get_enum_type, - get_three_state_flag -) +from azure.cli.core.commands.parameters import get_enum_type, get_three_state_flag from azure.cli.core.commands.validators import get_default_location_from_resource_group from . import consts -from .action import ( - AddConfigurationSettings, - AddConfigurationProtectedSettings -) +from .action import AddConfigurationSettings, AddConfigurationProtectedSettings def load_arguments(self, _): with self.argument_context(consts.EXTENSION_NAME) as c: - c.argument('location', - validator=get_default_location_from_resource_group) - c.argument('name', - options_list=['--name', '-n'], - help='Name of the extension instance') - c.argument('extension_type', - help='Name of the extension type.') - c.argument('cluster_name', - options_list=['--cluster-name', '-c'], - help='Name of the Kubernetes cluster') - c.argument('cluster_type', - arg_type=get_enum_type(['connectedClusters', 'managedClusters', 'appliances']), - options_list=['--cluster-type', '-t'], - help='Specify Arc clusters or AKS managed clusters or Arc appliances.') - c.argument('scope', - arg_type=get_enum_type(['cluster', 'namespace']), - help='Specify the extension scope.') - c.argument('auto_upgrade_minor_version', - arg_group="Version", - options_list=['--auto-upgrade-minor-version', '--auto-upgrade'], - arg_type=get_three_state_flag(), - help='Automatically upgrade minor version of the extension instance.') - c.argument('version', - arg_group="Version", - help='Specify the version to install for the extension instance if' - ' --auto-upgrade-minor-version is not enabled.') - c.argument('release_train', - arg_group="Version", - help='Specify the release train for the extension type.') - c.argument('configuration_settings', - arg_group="Configuration", - options_list=['--configuration-settings', '--config-settings', '--config'], - action=AddConfigurationSettings, - nargs='+', - help='Configuration Settings as key=value pair. Repeat parameter for each setting') - c.argument('configuration_protected_settings', - arg_group="Configuration", - options_list=['--configuration-protected-settings', '--config-protected-settings', '--config-protected'], - action=AddConfigurationProtectedSettings, - nargs='+', - help='Configuration Protected Settings as key=value pair. Repeat parameter for each setting') - c.argument('configuration_settings_file', - arg_group="Configuration", - options_list=['--configuration-settings-file', '--config-settings-file', '--config-file'], - help='JSON file path for configuration-settings') - c.argument('configuration_protected_settings_file', - arg_group="Configuration", - options_list=['--configuration-protected-settings-file', '--config-protected-settings-file', '--config-protected-file'], - help='JSON file path for configuration-protected-settings') - c.argument('release_namespace', - help='Specify the namespace to install the extension release.') - 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\'') + c.argument("location", validator=get_default_location_from_resource_group) + c.argument( + "name", options_list=["--name", "-n"], help="Name of the extension instance" + ) + c.argument("extension_type", help="Name of the extension type.") + c.argument( + "cluster_name", + options_list=["--cluster-name", "-c"], + help="Name of the Kubernetes cluster", + ) + c.argument( + "cluster_type", + arg_type=get_enum_type( + [ + "connectedClusters", + "managedClusters", + "appliances", + "provisionedClusters", + ] + ), + options_list=["--cluster-type", "-t"], + help="Specify Arc clusters or AKS managed clusters or Arc appliances.", + ) + c.argument( + "cluster_resource_provider", + options_list=["--cluster-resource-provider", "--cluster-rp"], + help="Cluster Resource Provider name for this clusterType (Required for provisionedClusters)", + ) + c.argument( + "scope", + arg_type=get_enum_type(["cluster", "namespace"]), + help="Specify the extension scope.", + ) + c.argument( + "auto_upgrade_minor_version", + arg_group="Version", + options_list=["--auto-upgrade-minor-version", "--auto-upgrade"], + arg_type=get_three_state_flag(), + help="Automatically upgrade minor version of the extension instance.", + ) + c.argument( + "version", + arg_group="Version", + help="Specify the version to install for the extension instance if" + " --auto-upgrade-minor-version is not enabled.", + ) + c.argument( + "release_train", + arg_group="Version", + help="Specify the release train for the extension type.", + ) + c.argument( + "configuration_settings", + arg_group="Configuration", + options_list=["--configuration-settings", "--config-settings", "--config"], + action=AddConfigurationSettings, + nargs="+", + help="Configuration Settings as key=value pair. Repeat parameter for each setting", + ) + c.argument( + "configuration_protected_settings", + arg_group="Configuration", + options_list=[ + "--configuration-protected-settings", + "--config-protected-settings", + "--config-protected", + ], + action=AddConfigurationProtectedSettings, + nargs="+", + help="Configuration Protected Settings as key=value pair. Repeat parameter for each setting", + ) + c.argument( + "configuration_settings_file", + arg_group="Configuration", + options_list=[ + "--configuration-settings-file", + "--config-settings-file", + "--config-file", + ], + help="JSON file path for configuration-settings", + ) + c.argument( + "configuration_protected_settings_file", + arg_group="Configuration", + options_list=[ + "--configuration-protected-settings-file", + "--config-protected-settings-file", + "--config-protected-file", + ], + help="JSON file path for configuration-protected-settings", + ) + c.argument( + "release_namespace", + help="Specify the namespace to install the extension release.", + ) + 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} update") as c: - c.argument('yes', - options_list=['--yes', '-y'], - help='Ignore confirmation prompts') + c.argument( + "yes", options_list=["--yes", "-y"], help="Ignore confirmation prompts" + ) with self.argument_context(f"{consts.EXTENSION_NAME} delete") as c: - c.argument('yes', - options_list=['--yes', '-y'], - help='Ignore confirmation prompts') - c.argument('force', - help='Specify whether to force delete the extension from the cluster.') + c.argument( + "yes", options_list=["--yes", "-y"], help="Ignore confirmation prompts" + ) + c.argument( + "force", + help="Specify whether to force delete the extension from the cluster.", + ) diff --git a/src/k8s-extension/azext_k8s_extension/consts.py b/src/k8s-extension/azext_k8s_extension/consts.py index dff2e32b886..36d92210141 100644 --- a/src/k8s-extension/azext_k8s_extension/consts.py +++ b/src/k8s-extension/azext_k8s_extension/consts.py @@ -10,14 +10,17 @@ REGISTERED = "Registered" 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_RP = "microsoft.kubernetes" +MANAGED_CLUSTER_RP = "microsoft.containerservice" +APPLIANCE_RP = "microsoft.resourceconnector" +HYBRIDCONTAINERSERVICE_RP = "microsoft.hybridcontainerservice" CONNECTED_CLUSTER_TYPE = "connectedclusters" MANAGED_CLUSTER_TYPE = "managedclusters" APPLIANCE_TYPE = "appliances" +PROVISIONED_CLUSTER_TYPE = "provisionedclusters" CONNECTED_CLUSTER_API_VERSION = "2021-10-01" MANAGED_CLUSTER_API_VERSION = "2021-10-01" APPLIANCE_API_VERSION = "2021-10-31-preview" +HYBRIDCONTAINERSERVICE_API_VERSION = "2021-09-01-preview" diff --git a/src/k8s-extension/azext_k8s_extension/custom.py b/src/k8s-extension/azext_k8s_extension/custom.py index 9519c4b3a79..411745bd6e4 100644 --- a/src/k8s-extension/azext_k8s_extension/custom.py +++ b/src/k8s-extension/azext_k8s_extension/custom.py @@ -53,10 +53,17 @@ def ExtensionFactory(extension_name): return extension_map.get(extension_name, DefaultExtension)() -def show_k8s_extension(client, resource_group_name, cluster_name, name, cluster_type): +def show_k8s_extension( + client, + resource_group_name, + cluster_name, + name, + cluster_type, + cluster_resource_provider=None +): """Get an existing K8s Extension.""" # Determine ClusterRP - cluster_rp, _ = get_cluster_rp_api_version(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_rp=cluster_resource_provider, cluster_type=cluster_type) try: extension = client.get( @@ -95,6 +102,7 @@ def create_k8s_extension( name, cluster_type, extension_type, + cluster_resource_provider=None, scope=None, auto_upgrade_minor_version=None, release_train=None, @@ -110,7 +118,7 @@ def create_k8s_extension( """Create a new Extension Instance.""" extension_type_lower = extension_type.lower() - cluster_rp, _ = get_cluster_rp_api_version(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_rp=cluster_resource_provider, cluster_type=cluster_type) # Configuration Settings & Configuration Protected Settings if configuration_settings is not None and configuration_settings_file is not None: @@ -166,6 +174,7 @@ def create_k8s_extension( resource_group_name, cluster_name, name, + cluster_rp, cluster_type, extension_type_lower, scope, @@ -192,9 +201,10 @@ def create_k8s_extension( # 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 - ) + identity_object, location = __create_identity(cmd=cmd, resource_group_name=resource_group_name, + cluster_name=cluster_name, cluster_type=cluster_type, + cluster_rp=cluster_rp + ) if identity_object is not None and location is not None: extension_instance.identity, extension_instance.location = ( identity_object, @@ -214,8 +224,8 @@ def create_k8s_extension( ) -def list_k8s_extension(client, resource_group_name, cluster_name, cluster_type): - cluster_rp, _ = get_cluster_rp_api_version(cluster_type) +def list_k8s_extension(client, resource_group_name, cluster_name, cluster_type, cluster_resource_provider=None): + cluster_rp, _ = get_cluster_rp_api_version(cluster_rp=cluster_resource_provider, cluster_type=cluster_type) return client.list(resource_group_name, cluster_rp, cluster_type, cluster_name) @@ -226,6 +236,7 @@ def update_k8s_extension( cluster_name, name, cluster_type, + cluster_resource_provider=None, auto_upgrade_minor_version=None, release_train=None, version=None, @@ -253,22 +264,21 @@ def update_k8s_extension( user_confirmation_factory(cmd, yes, msg) # Determine ClusterRP - cluster_rp, _ = get_cluster_rp_api_version(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_rp=cluster_resource_provider, cluster_type=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 + client, resource_group_name, cluster_name, name, cluster_type, cluster_rp ) extension_type_lower = extension.extension_type.lower() - config_settings = None - config_protected_settings = None + 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) if configuration_settings is not None: - config_settings = {} for dicts in configuration_settings: for key, value in dicts.items(): config_settings[key] = value @@ -280,7 +290,6 @@ def update_k8s_extension( ) if configuration_protected_settings is not None: - config_protected_settings = {} for dicts in configuration_protected_settings: for key, value in dicts.items(): config_protected_settings[key] = value @@ -320,13 +329,14 @@ def delete_k8s_extension( cluster_name, name, cluster_type, + cluster_resource_provider=None, no_wait=False, yes=False, force=False, ): """Delete an existing Kubernetes Extension.""" # Determine ClusterRP - cluster_rp, _ = get_cluster_rp_api_version(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_rp=cluster_resource_provider, cluster_type=cluster_type) extension = None try: extension = client.get( @@ -343,7 +353,7 @@ def delete_k8s_extension( # 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 + cmd, client, resource_group_name, cluster_name, name, cluster_rp, cluster_type, yes ) return sdk_no_wait( @@ -358,7 +368,7 @@ def delete_k8s_extension( ) -def __create_identity(cmd, resource_group_name, cluster_name, cluster_type): +def __create_identity(cmd, resource_group_name, cluster_name, cluster_rp, cluster_type): subscription_id = get_subscription_id(cmd.cli_ctx) resources = cf_resources(cmd.cli_ctx, subscription_id) @@ -369,7 +379,7 @@ def __create_identity(cmd, resource_group_name, cluster_name, cluster_type): ): return None, None - cluster_rp, parent_api_version = get_cluster_rp_api_version(cluster_type) + cluster_rp, parent_api_version = get_cluster_rp_api_version(cluster_rp=cluster_rp, cluster_type=cluster_type) cluster_resource_id = ( "/subscriptions/{0}/resourceGroups/{1}/providers/{2}/{3}/{4}".format( 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 4dce31b9764..1191479eb93 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureDefender.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureDefender.py @@ -18,13 +18,30 @@ 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, - configuration_settings_file, configuration_protected_settings_file): + def Create( + self, + cmd, + client, + resource_group_name, + cluster_name, + name, + cluster_rp, + 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, + ): """ExtensionType 'microsoft.azuredefender.kubernetes' specific validations & defaults for Create - Must create and return a valid 'Extension' object. + Must create and return a valid 'Extension' object. """ # NOTE-1: Replace default scope creation with your customization! @@ -39,12 +56,27 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t is_ci_extension_type = False - logger.warning('Ignoring name, release-namespace and scope parameters since %s ' - 'only supports cluster scope and single instance of this extension.', extension_type) - logger.warning("Defaulting to extension name '%s' and release-namespace '%s'", name, release_namespace) + logger.warning( + "Ignoring name, release-namespace and scope parameters since %s " + "only supports cluster scope and single instance of this extension.", + extension_type, + ) + logger.warning( + "Defaulting to extension name '%s' and release-namespace '%s'", + name, + release_namespace, + ) - _get_container_insights_settings(cmd, resource_group_name, cluster_name, configuration_settings, - configuration_protected_settings, is_ci_extension_type) + _get_container_insights_settings( + cmd, + resource_group_name, + cluster_rp, + cluster_type, + cluster_name, + configuration_settings, + configuration_protected_settings, + is_ci_extension_type, + ) # NOTE-2: Return a valid Extension object, Instance name and flag for Identity create_identity = True @@ -55,6 +87,6 @@ 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_instance, name, create_identity 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 4f3613daa09..6ebdbd2628b 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureMLKubernetes.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/AzureMLKubernetes.py @@ -108,10 +108,11 @@ def __init__(self): self.OPEN_SHIFT = 'openshift' - 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, + def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_rp, 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): + if scope == 'namespace': raise InvalidArgumentValueError("Invalid scope '{}'. This extension can't be installed " "only at 'cluster' scope. " @@ -126,7 +127,8 @@ 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_rp=cluster_rp, + cluster_type=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 = '' @@ -216,7 +218,7 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t ) return extension, name, create_identity - 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_rp, cluster_type, yes): user_confirmation_factory(cmd, yes) def Update(self, cmd, resource_group_name, cluster_name, auto_upgrade_minor_version, release_train, version, configuration_settings, 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 f2eeb3ff5be..91915796bb7 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/ContainerInsights.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/ContainerInsights.py @@ -32,10 +32,11 @@ 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, + def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_rp, 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): + """ExtensionType 'microsoft.azuremonitor.containers' specific validations & defaults for Create Must create and return a valid 'Extension' object. @@ -56,7 +57,7 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t 'only supports cluster scope and single instance of this extension.', extension_type) logger.warning("Defaulting to extension name '%s' and release-namespace '%s'", name, release_namespace) - _get_container_insights_settings(cmd, resource_group_name, cluster_name, configuration_settings, + _get_container_insights_settings(cmd, resource_group_name, cluster_rp, cluster_type, cluster_name, configuration_settings, configuration_protected_settings, is_ci_extension_type) # NOTE-2: Return a valid Extension object, Instance name and flag for Identity @@ -72,11 +73,11 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t ) return extension, name, create_identity - 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_rp, cluster_type, yes): # Delete DCR-A if it exists incase of MSI Auth useAADAuth = False isDCRAExists = False - cluster_rp, _ = get_cluster_rp_api_version(cluster_type) + cluster_rp, _ = get_cluster_rp_api_version(cluster_rp=cluster_rp, cluster_type=cluster_type) try: extension = client.get(resource_group_name, cluster_rp, cluster_type, cluster_name, name) except Exception: @@ -140,8 +141,8 @@ def _invoke_deployment(cmd, resource_group_name, deployment_name, template, para return sdk_no_wait(no_wait, smc.begin_create_or_update, resource_group_name, deployment_name, deployment) -def _ensure_default_log_analytics_workspace_for_monitoring(cmd, subscription_id, - cluster_resource_group_name, cluster_name): +def _ensure_default_log_analytics_workspace_for_monitoring(cmd, subscription_id, cluster_resource_group_name, + cluster_rp, cluster_type, cluster_name): # mapping for azure public cloud # log analytics workspaces cannot be created in WCUS region due to capacity limits # so mapped to EUS per discussion with log analytics team @@ -236,8 +237,9 @@ def _ensure_default_log_analytics_workspace_for_monitoring(cmd, subscription_id, cluster_location = '' resources = cf_resources(cmd.cli_ctx, subscription_id) - cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Kubernetes' \ - '/connectedClusters/{2}'.format(subscription_id, cluster_resource_group_name, cluster_name) + cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/{2}/{3}/{4}'.format( + subscription_id, cluster_resource_group_name, cluster_rp, cluster_type, cluster_name) + try: resource = resources.get_by_id(cluster_resource_id, '2020-01-01-preview') cluster_location = resource.location.lower() @@ -438,8 +440,8 @@ def _ensure_container_insights_for_monitoring(cmd, workspace_resource_id): validate=False, no_wait=False, subscription_id=subscription_id) -def _get_container_insights_settings(cmd, cluster_resource_group_name, cluster_name, configuration_settings, - configuration_protected_settings, is_ci_extension_type): +def _get_container_insights_settings(cmd, cluster_resource_group_name, cluster_rp, cluster_type, cluster_name, + configuration_settings, configuration_protected_settings, is_ci_extension_type): subscription_id = get_subscription_id(cmd.cli_ctx) workspace_resource_id = '' @@ -477,7 +479,7 @@ def _get_container_insights_settings(cmd, cluster_resource_group_name, cluster_n if not workspace_resource_id: workspace_resource_id = _ensure_default_log_analytics_workspace_for_monitoring( - cmd, subscription_id, cluster_resource_group_name, cluster_name) + cmd, subscription_id, cluster_resource_group_name, cluster_rp, cluster_type, cluster_name) else: if not is_valid_resource_id(workspace_resource_id): raise InvalidArgumentValueError('{} is not a valid Azure resource ID.'.format(workspace_resource_id)) @@ -541,13 +543,14 @@ def get_existing_container_insights_extension_dcr_tags(cmd, dcr_url): return tags -def _ensure_container_insights_dcr_for_monitoring(cmd, subscription_id, cluster_resource_group_name, cluster_name, workspace_resource_id): +def _ensure_container_insights_dcr_for_monitoring(cmd, subscription_id, cluster_resource_group_name, cluster_rp, cluster_type, cluster_name, workspace_resource_id): from azure.core.exceptions import HttpResponseError cluster_region = '' resources = cf_resources(cmd.cli_ctx, subscription_id) - cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Kubernetes' \ - '/connectedClusters/{2}'.format(subscription_id, cluster_resource_group_name, cluster_name) + cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/{2}/{3}/{4}'.format( + subscription_id, cluster_resource_group_name, cluster_rp, cluster_type, cluster_name) + try: resource = resources.get_by_id(cluster_resource_id, '2020-01-01-preview') cluster_region = resource.location.lower() diff --git a/src/k8s-extension/azext_k8s_extension/partner_extensions/Dapr.py b/src/k8s-extension/azext_k8s_extension/partner_extensions/Dapr.py index 484c1f23d4a..ad029477d3a 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/Dapr.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/Dapr.py @@ -19,8 +19,8 @@ def __init__(self): # constants for configuration settings. self.CLUSTER_TYPE = 'global.clusterType' - 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, + def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_rp, 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): 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 bae3fe4d1f0..7f6c092f005 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/DefaultExtension.py @@ -24,6 +24,7 @@ def Create( resource_group_name, cluster_name, name, + cluster_rp, cluster_type, extension_type, scope, @@ -87,7 +88,7 @@ def Update( ) def Delete( - self, cmd, client, resource_group_name, cluster_name, name, cluster_type, yes + self, cmd, client, resource_group_name, cluster_name, name, cluster_rp, cluster_type, yes ): user_confirmation_factory(cmd, yes) 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 b1ad6694530..1098bbaa0c8 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/OpenServiceMesh.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/OpenServiceMesh.py @@ -9,6 +9,7 @@ import json from knack.log import get_logger +from ..utils import get_cluster_rp_api_version from azure.cli.core.azclierror import InvalidArgumentValueError from azure.cli.core.commands.client_factory import get_subscription_id @@ -32,8 +33,8 @@ 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, + def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_rp, 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): """ExtensionType 'microsoft.openservicemesh' specific validations & defaults for Create @@ -52,7 +53,15 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t # NOTE-2: Return a valid Extension object, Instance name and flag for Identity create_identity = True - _validate_tested_distro(cmd, resource_group_name, cluster_name, version, release_train) + _validate_tested_distro( + cmd=cmd, + cluster_resource_group_name=resource_group_name, + cluster_rp=cluster_rp, + cluster_type=cluster_type, + cluster_name=cluster_name, + extension_version=version, + extension_release_train=release_train + ) extension = Extension( extension_type=extension_type, @@ -68,7 +77,7 @@ def Create(self, cmd, client, resource_group_name, cluster_name, name, cluster_t return extension, name, create_identity -def _validate_tested_distro(cmd, cluster_resource_group_name, cluster_name, extension_version, extension_release_train): +def _validate_tested_distro(cmd, cluster_resource_group_name, cluster_rp, cluster_type, cluster_name, extension_version, extension_release_train): field_unavailable_error = '\"testedDistros\" field unavailable for version {0} of microsoft.openservicemesh, ' \ 'cannot determine if this Kubernetes distribution has been properly tested'.format(extension_version) @@ -78,10 +87,13 @@ def _validate_tested_distro(cmd, cluster_resource_group_name, cluster_name, exte subscription_id = get_subscription_id(cmd.cli_ctx) resources = cf_resources(cmd.cli_ctx, subscription_id) - cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Kubernetes' \ - '/connectedClusters/{2}'.format(subscription_id, cluster_resource_group_name, cluster_name) + cluster_resource_id = '/subscriptions/{0}/resourceGroups/{1}/providers/{2}/{3}/{4}'.format( + subscription_id, cluster_resource_group_name, cluster_rp, cluster_type, cluster_name) + + cluster_rp, parent_api_version = get_cluster_rp_api_version(cluster_rp=cluster_rp, + cluster_type=cluster_type) - resource = resources.get_by_id(cluster_resource_id, '2021-10-01') + resource = resources.get_by_id(cluster_resource_id, parent_api_version) cluster_location = resource.location cluster_distro = resource.properties['distribution'].lower() 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 c83429e2cfb..cb3dc04746b 100644 --- a/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py +++ b/src/k8s-extension/azext_k8s_extension/partner_extensions/PartnerExtensionModel.py @@ -17,6 +17,7 @@ def Create( resource_group_name: str, cluster_name: str, name: str, + cluster_rp: str, cluster_type: str, extension_type: str, scope: str, @@ -56,6 +57,7 @@ def Delete( resource_group_name: str, cluster_name: str, name: str, + cluster_rp: str, cluster_type: str, yes: bool, ): diff --git a/src/k8s-extension/azext_k8s_extension/utils.py b/src/k8s-extension/azext_k8s_extension/utils.py index c587c3050a1..1b7b29b4213 100644 --- a/src/k8s-extension/azext_k8s_extension/utils.py +++ b/src/k8s-extension/azext_k8s_extension/utils.py @@ -6,11 +6,22 @@ import json from typing import Tuple from urllib.parse import urlparse + +from azure.cli.core.azclierror import ( + InvalidArgumentValueError, + RequiredArgumentMissingError, +) from . import consts -from azure.cli.core.azclierror import InvalidArgumentValueError -def get_cluster_rp_api_version(cluster_type) -> Tuple[str, str]: +def get_cluster_rp_api_version(cluster_rp, cluster_type) -> Tuple[str, str]: + if cluster_type.lower() == consts.PROVISIONED_CLUSTER_TYPE: + if cluster_rp is None or cluster_rp.strip() == "": + raise RequiredArgumentMissingError( + "Error! Cluster Resource Provider value is required for Cluster Type '{}'".format( + cluster_type + ) + ) 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: @@ -20,6 +31,17 @@ def get_cluster_rp_api_version(cluster_type) -> Tuple[str, str]: or cluster_type.lower() == consts.MANAGED_CLUSTER_TYPE ): return consts.MANAGED_CLUSTER_RP, consts.MANAGED_CLUSTER_API_VERSION + if cluster_type.lower() == consts.PROVISIONED_CLUSTER_TYPE: + if cluster_rp.lower() == consts.HYBRIDCONTAINERSERVICE_RP: + return ( + consts.HYBRIDCONTAINERSERVICE_RP, + consts.HYBRIDCONTAINERSERVICE_API_VERSION, + ) + raise InvalidArgumentValueError( + "Error! Cluster type '{}' and Cluster Resource Provider '{}' combination is not supported".format( + cluster_type, cluster_rp + ) + ) raise InvalidArgumentValueError( "Error! Cluster type '{}' is not supported".format(cluster_type) ) diff --git a/src/k8s-extension/setup.py b/src/k8s-extension/setup.py index 2635d1027f9..91419f1755b 100644 --- a/src/k8s-extension/setup.py +++ b/src/k8s-extension/setup.py @@ -33,7 +33,7 @@ # TODO: Add any additional SDK dependencies here DEPENDENCIES = [] -VERSION = "1.2.3" +VERSION = "1.2.5.prov-preview-1" with open("README.rst", "r", encoding="utf-8") as f: README = f.read()