From 68800b6486aa088a1fcf5339ed66cc12106bf322 Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Tue, 12 Nov 2019 12:59:47 +0530 Subject: [PATCH 01/58] Added spn and resource group creation in connectedk8s create --- .github/CODEOWNERS | 3 + src/connectedk8s/HISTORY.rst | 8 + src/connectedk8s/README.rst | 5 + .../azext_connectedk8s/__init__.py | 32 ++ .../azext_connectedk8s/_client_factory.py | 41 ++ src/connectedk8s/azext_connectedk8s/_help.py | 38 ++ .../azext_connectedk8s/_multi_api_adaptor.py | 70 +++ .../azext_connectedk8s/_params.py | 23 + .../azext_connectedk8s/_validators.py | 20 + .../azext_connectedk8s/azext_metadata.json | 5 + .../azext_connectedk8s/commands.py | 28 + src/connectedk8s/azext_connectedk8s/custom.py | 167 ++++++ .../azext_connectedk8s/tests/__init__.py | 5 + .../tests/latest/__init__.py | 5 + .../latest/test_connectedk8s_scenario.py | 40 ++ .../vendored_sdks/__init__.py | 18 + .../vendored_sdks/k8_connect_rp.py | 88 +++ .../vendored_sdks/models/__init__.py | 56 ++ .../vendored_sdks/models/connected_cluster.py | 91 ++++ .../connected_cluster_aad_access_profile.py | 52 ++ ...onnected_cluster_aad_access_profile_py3.py | 52 ++ .../models/connected_cluster_aad_profile.py | 46 ++ .../connected_cluster_aad_profile_py3.py | 46 ++ .../connected_cluster_access_profile.py | 56 ++ .../connected_cluster_access_profile_py3.py | 56 ++ .../models/connected_cluster_identity.py | 50 ++ .../models/connected_cluster_identity_py3.py | 50 ++ .../models/connected_cluster_paged.py | 27 + .../models/connected_cluster_patch.py | 28 + .../models/connected_cluster_patch_py3.py | 28 + .../models/connected_cluster_py3.py | 91 ++++ .../vendored_sdks/models/error_details.py | 40 ++ .../vendored_sdks/models/error_details_py3.py | 40 ++ .../vendored_sdks/models/error_response.py | 41 ++ .../models/error_response_py3.py | 41 ++ .../models/k8_connect_rp_enums.py | 26 + .../vendored_sdks/models/operation.py | 40 ++ .../vendored_sdks/models/operation_display.py | 41 ++ .../models/operation_display_py3.py | 41 ++ .../vendored_sdks/models/operation_paged.py | 27 + .../vendored_sdks/models/operation_py3.py | 40 ++ .../vendored_sdks/operations/__init__.py | 18 + .../connected_cluster_operations.py | 511 ++++++++++++++++++ .../vendored_sdks/operations/operations.py | 97 ++++ .../vendored_sdks/version.py | 13 + src/connectedk8s/setup.cfg | 2 + src/connectedk8s/setup.py | 62 +++ 47 files changed, 2405 insertions(+) create mode 100644 src/connectedk8s/HISTORY.rst create mode 100644 src/connectedk8s/README.rst create mode 100644 src/connectedk8s/azext_connectedk8s/__init__.py create mode 100644 src/connectedk8s/azext_connectedk8s/_client_factory.py create mode 100644 src/connectedk8s/azext_connectedk8s/_help.py create mode 100644 src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py create mode 100644 src/connectedk8s/azext_connectedk8s/_params.py create mode 100644 src/connectedk8s/azext_connectedk8s/_validators.py create mode 100644 src/connectedk8s/azext_connectedk8s/azext_metadata.json create mode 100644 src/connectedk8s/azext_connectedk8s/commands.py create mode 100644 src/connectedk8s/azext_connectedk8s/custom.py create mode 100644 src/connectedk8s/azext_connectedk8s/tests/__init__.py create mode 100644 src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py create mode 100644 src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/__init__.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_paged.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_paged.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/__init__.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py create mode 100644 src/connectedk8s/setup.cfg create mode 100644 src/connectedk8s/setup.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0f68e4cea52..2bd571915e6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -75,3 +75,6 @@ /src/connectedmachine/ @farehar /src/ip-group/ @haroldrandom +/src/azext_haikutest1/ @akashkeshari +/src/azext_mesh_copy/ @akashkeshari +/src/azext_connectedk8s/ @akashkeshari diff --git a/src/connectedk8s/HISTORY.rst b/src/connectedk8s/HISTORY.rst new file mode 100644 index 00000000000..8c34bccfff8 --- /dev/null +++ b/src/connectedk8s/HISTORY.rst @@ -0,0 +1,8 @@ +.. :changelog: + +Release History +=============== + +0.1.0 +++++++ +* Initial release. \ No newline at end of file diff --git a/src/connectedk8s/README.rst b/src/connectedk8s/README.rst new file mode 100644 index 00000000000..a56143da6e9 --- /dev/null +++ b/src/connectedk8s/README.rst @@ -0,0 +1,5 @@ +Microsoft Azure CLI 'connectedk8s' Extension +========================================== + +This package is for the 'connectedk8s' extension. +i.e. 'az connectedk8s' \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/__init__.py b/src/connectedk8s/azext_connectedk8s/__init__.py new file mode 100644 index 00000000000..6c6d17bacdd --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/__init__.py @@ -0,0 +1,32 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from azure.cli.core import AzCommandsLoader + +from azext_connectedk8s._help import helps # pylint: disable=unused-import + + +class Connectedk8sCommandsLoader(AzCommandsLoader): + + def __init__(self, cli_ctx=None): + from azure.cli.core.commands import CliCommandType + from azext_connectedk8s._client_factory import cf_connectedk8s + connectedk8s_custom = CliCommandType( + operations_tmpl='azext_connectedk8s.custom#{}', + client_factory=cf_connectedk8s) + super(Connectedk8sCommandsLoader, self).__init__(cli_ctx=cli_ctx, + custom_command_type=connectedk8s_custom) + + def load_command_table(self, args): + from azext_connectedk8s.commands import load_command_table + load_command_table(self, args) + return self.command_table + + def load_arguments(self, command): + from azext_connectedk8s._params import load_arguments + load_arguments(self, command) + + +COMMAND_LOADER_CLS = Connectedk8sCommandsLoader diff --git a/src/connectedk8s/azext_connectedk8s/_client_factory.py b/src/connectedk8s/azext_connectedk8s/_client_factory.py new file mode 100644 index 00000000000..2aca739459b --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/_client_factory.py @@ -0,0 +1,41 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +def cf_connectedk8s(cli_ctx, *_): + + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azext_connectedk8s.vendored_sdks import K8ConnectRP + return get_mgmt_service_client(cli_ctx, K8ConnectRP) + +def cf_connected_cluster(cli_ctx, _): + return cf_connectedk8s(cli_ctx).connected_cluster + +def _resource_client_factory(cli_ctx, **_): + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azure.cli.core.profiles import ResourceType + return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + +def _graph_client_factory(cli_ctx, **_): + from azure.cli.core._profile import Profile + from azure.cli.core.commands.client_factory import configure_common_settings + from azure.graphrbac import GraphRbacManagementClient + profile = Profile(cli_ctx=cli_ctx) + cred, _, tenant_id = profile.get_login_credentials( + resource=cli_ctx.cloud.endpoints.active_directory_graph_resource_id) + client = GraphRbacManagementClient(cred, tenant_id, + base_url=cli_ctx.cloud.endpoints.active_directory_graph_resource_id) + configure_common_settings(cli_ctx, client) + return client + +def _auth_client_factory(cli_ctx, scope=None): + import re + from azure.cli.core.profiles import ResourceType + from azure.cli.core.commands.client_factory import get_mgmt_service_client + subscription_id = None + if scope: + matched = re.match('/subscriptions/(?P[^/]*)/', scope) + if matched: + subscription_id = matched.groupdict()['subscription'] + return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION, subscription_id=subscription_id) diff --git a/src/connectedk8s/azext_connectedk8s/_help.py b/src/connectedk8s/azext_connectedk8s/_help.py new file mode 100644 index 00000000000..92bd55803b6 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/_help.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.help_files import helps # pylint: disable=unused-import + + +helps['connectedk8s'] = """ + type: group + short-summary: Commands to manage Connectedk8ss. +""" + +helps['connectedk8s create'] = """ + type: command + short-summary: Create a Connectedk8s. +""" + +helps['connectedk8s list'] = """ + type: command + short-summary: List Connectedk8ss. +""" + +helps['connectedk8s delete'] = """ + type: command + short-summary: Delete a Connectedk8s. +""" + +helps['connectedk8s show'] = """ + type: command + short-summary: Show details of a Connectedk8s. +""" + +helps['connectedk8s update'] = """ + type: command + short-summary: Update a Connectedk8s. +""" diff --git a/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py b/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py new file mode 100644 index 00000000000..783ca3e7ada --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py @@ -0,0 +1,70 @@ +from azure.cli.core.profiles import ResourceType, get_sdk, supported_api_version + + +class MultiAPIAdaptor(object): + # We will bridge all the code difference here caused by SDK breaking changes + def __init__(self, cli_ctx): + self.old_api = supported_api_version(cli_ctx, resource_type=ResourceType.MGMT_AUTHORIZATION, + max_api='2015-07-01') + self.cli_ctx = cli_ctx + + def _init_individual_permission(self, cfg): + Permission = get_sdk(self.cli_ctx, ResourceType.MGMT_AUTHORIZATION, 'Permission', mod='models', + operation_group='role_definitions') + permission = Permission(actions=cfg.get('actions', None), + not_actions=cfg.get('notActions', None)) + if not self.old_api: + permission.data_actions = cfg.get('dataActions', None) + permission.not_data_actions = cfg.get('notDataActions', None) + return permission + + def _init_permissions(self, role_definition_input): + # we will handle with or w/o 'permissions' + if 'permissions' in role_definition_input: + return [self._init_individual_permission(p) for p in role_definition_input['permissions']] + return [self._init_individual_permission(role_definition_input)] + + def create_role_definition(self, client, role_name, role_id, role_definition_input): + RoleDefinitionBase = get_sdk(self.cli_ctx, ResourceType.MGMT_AUTHORIZATION, + 'RoleDefinitionProperties' if self.old_api else 'RoleDefinition', + mod='models', operation_group='role_definitions') + role_configuration = RoleDefinitionBase(role_name=role_name, + description=role_definition_input.get('description', None), + type='CustomRole', + assignable_scopes=role_definition_input['assignableScopes'], + permissions=self._init_permissions(role_definition_input)) + scope = role_definition_input['assignableScopes'][0] + if self.old_api: + return client.create_or_update(role_definition_id=role_id, scope=scope, properties=role_configuration) + return client.create_or_update(role_definition_id=role_id, scope=scope, role_definition=role_configuration) + + def create_role_assignment(self, client, assignment_name, role_id, object_id, scope, assignee_principal_type=None): + RoleAssignmentCreateParameters = get_sdk( + self.cli_ctx, ResourceType.MGMT_AUTHORIZATION, + 'RoleAssignmentProperties' if self.old_api else 'RoleAssignmentCreateParameters', + mod='models', operation_group='role_assignments') + parameters = RoleAssignmentCreateParameters(role_definition_id=role_id, principal_id=object_id) + if assignee_principal_type: + parameters.principal_type = assignee_principal_type + return client.create(scope, assignment_name, parameters) + + def get_role_property(self, obj, property_name): + if self.old_api: + if isinstance(obj, dict): + obj = obj['properties'] + else: + obj = obj.properties + if isinstance(obj, dict): + return obj[property_name] + return getattr(obj, property_name) + + def set_role_property(self, obj, property_name, property_value): + if self.old_api: + if isinstance(obj, dict): + obj = obj['properties'] + else: + obj = obj.properties + if isinstance(obj, dict): + obj[property_name] = property_value + else: + obj.property_name = property_value \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py new file mode 100644 index 00000000000..1ea56bd0e7a --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -0,0 +1,23 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long + +from knack.arguments import CLIArgumentType + + +def load_arguments(self, _): + + from azure.cli.core.commands.parameters import tags_type + from azure.cli.core.commands.validators import get_default_location_from_resource_group + + cluster_name_type = CLIArgumentType(options_list='--cluster-name-name', help='Name of the Connectedk8s.', id_part='name') + + with self.argument_context('connectedk8s') as c: + c.argument('tags', tags_type) + c.argument('location', validator=get_default_location_from_resource_group) + c.argument('cluster_name', cluster_name_type, options_list=['--name', '-n']) + + with self.argument_context('connectedk8s list') as c: + c.argument('cluster_name', cluster_name_type, id_part=None) diff --git a/src/connectedk8s/azext_connectedk8s/_validators.py b/src/connectedk8s/azext_connectedk8s/_validators.py new file mode 100644 index 00000000000..821630f5f34 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/_validators.py @@ -0,0 +1,20 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +def example_name_or_id_validator(cmd, namespace): + # Example of a storage account name or ID validator. + # See: https://github.com/Azure/azure-cli/blob/dev/doc/authoring_command_modules/authoring_commands.md#supporting-name-or-id-parameters + from azure.cli.core.commands.client_factory import get_subscription_id + from msrestazure.tools import is_valid_resource_id, resource_id + if namespace.storage_account: + if not is_valid_resource_id(namespace.RESOURCE): + namespace.storage_account = resource_id( + subscription=get_subscription_id(cmd.cli_ctx), + resource_group=namespace.resource_group_name, + namespace='Microsoft.Storage', + type='storageAccounts', + name=namespace.storage_account + ) diff --git a/src/connectedk8s/azext_connectedk8s/azext_metadata.json b/src/connectedk8s/azext_connectedk8s/azext_metadata.json new file mode 100644 index 00000000000..4060dd78264 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/azext_metadata.json @@ -0,0 +1,5 @@ +{ + "azext.isPreview": true, + "azext.minCliCoreVersion": "2.0.67", + "azext.maxCliCoreVersion": "2.1.0" +} \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/commands.py b/src/connectedk8s/azext_connectedk8s/commands.py new file mode 100644 index 00000000000..c6f307848a1 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/commands.py @@ -0,0 +1,28 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +# pylint: disable=line-too-long +from azure.cli.core.commands import CliCommandType +from azext_connectedk8s._client_factory import (cf_connectedk8s,cf_connected_cluster) + + +def load_command_table(self, _): + + connectedk8s_sdk = CliCommandType( + operations_tmpl='azext_connectedk8s.vendored_sdks.operations#ConnectedClusterOperations.{}', + client_factory=cf_connectedk8s) + + + with self.command_group('connectedk8s', connectedk8s_sdk, client_factory=cf_connected_cluster) as g: + g.custom_command('create', 'create_connectedk8s') + g.command('delete', 'delete') + g.custom_command('list', 'list_connectedk8s') + g.show_command('show', 'get') + g.generic_update_command('update', setter_name='update', custom_func_name='update_connectedk8s') + + + with self.command_group('connectedk8s', is_preview=True): + pass + diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py new file mode 100644 index 00000000000..23d8bf3ed5b --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -0,0 +1,167 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.util import CLIError +from azure.cli.core.profiles import ResourceType, get_sdk +from azext_connectedk8s._client_factory import _graph_client_factory +from azext_connectedk8s._client_factory import _resource_client_factory +from azext_connectedk8s._client_factory import _auth_client_factory +import datetime +from msrest.serialization import TZ_UTC +from dateutil.relativedelta import relativedelta +import uuid +from azure.graphrbac.models import (PasswordCredential, ApplicationCreateParameters, ServicePrincipalCreateParameters) +from knack.log import get_logger +from azext_connectedk8s._multi_api_adaptor import MultiAPIAdaptor + +logger = get_logger(__name__) + +def create_connectedk8s(cmd, client, subscription_name, resource_group_name, cluster_name, onboarding_spn_id=None, onboarding_spn_secret=None, location=None, tags=None): + resource_group_params = {'location': location} + rg_client = _resource_client_factory(cmd.cli_ctx) + rg_client.resource_groups.create_or_update(resource_group_name, resource_group_params) + if(onboarding_spn_id is None): + graph_client = _graph_client_factory(cmd.cli_ctx) + role_client = _auth_client_factory(cmd.cli_ctx).role_assignments + scopes = ['/subscriptions/' + role_client.config.subscription_id] + print(scopes) + years = 1 + import time + app_start_date = datetime.datetime.now(TZ_UTC) + app_end_date = app_start_date + relativedelta(years=years) + app_display_name = ('azure-cli-' + app_start_date.strftime('%Y-%m-%d-%H-%M-%S')) + name = 'http://' + app_display_name + password = str(uuid.uuid4()) + aad_application = create_application(cmd, + display_name=app_display_name, + homepage='https://' + app_display_name, + identifier_uris=[name], + available_to_other_tenants=False, + password=password, + key_value=None, + start_date=app_start_date, + end_date=app_end_date, + credential_description='rbac') + _RETRY_TIMES = 36 + app_id = aad_application.app_id + aad_sp = None + for l in range(0, _RETRY_TIMES): + try: + aad_sp = _create_service_principal(cmd.cli_ctx, app_id, resolve_app=False) + break + except Exception as ex: # pylint: disable=broad-except + if l < _RETRY_TIMES and ( + ' does not reference ' in str(ex) or ' does not exist ' in str(ex)): + time.sleep(5) + logger.warning('Retrying service principal creation: %s/%s', l + 1, _RETRY_TIMES) + else: + logger.warning( + "Creating service principal failed for appid '%s'. Trace followed:\n%s", + name, ex.response.headers if hasattr(ex, + 'response') else ex) # pylint: disable=no-member + raise + role = 'Contributor' + sp_oid = aad_sp.object_id + for scope in scopes: + logger.warning('Creating a role assignment under the scope of "%s"', scope) + for l in range(0, _RETRY_TIMES): + try: + _create_role_assignment(cmd.cli_ctx, role, sp_oid, None, scope, resolve_assignee=False) + print("created rass") + break + except Exception as ex: + if l < _RETRY_TIMES and ' does not exist in the directory ' in str(ex): + time.sleep(5) + logger.warning(' Retrying role assignment creation: %s/%s', l + 1, + _RETRY_TIMES) + continue + elif _error_caused_by_role_assignment_exists(ex): + logger.warning(' Role assignment already exits.\n') + break + else: + # dump out history for diagnoses + logger.warning(' Role assignment creation failed.\n') + if getattr(ex, 'response', None) is not None: + logger.warning(' role assignment response headers: %s\n', + ex.response.headers) # pylint: disable=no-member + raise + print(app_id) + print(password) + print(name) + print(app_display_name) + print(graph_client.config.tenant_id) + return client.create(subscription_name, resource_group_name, cluster_name, onboarding_spn_id, onboarding_spn_secret, "connected_cluster") + + +def list_connectedk8s(cmd, client, resource_group_name=None): + raise CLIError('TODO: Implement `connectedk8s list`') + + +def update_connectedk8s(cmd, instance, tags=None): + with cmd.update_context(instance) as c: + c.set_param('tags', tags) + return instance + +def create_application(cmd, display_name, homepage=None, identifier_uris=None, # pylint: disable=too-many-locals + available_to_other_tenants=False, password=None, reply_urls=None, + key_value=None, key_type=None, key_usage=None, start_date=None, end_date=None, + oauth2_allow_implicit_flow=None, required_resource_accesses=None, native_app=None, + credential_description=None, app_roles=None): + print(identifier_uris) + graph_client = _graph_client_factory(cmd.cli_ctx) + password_creds = [PasswordCredential(start_date=start_date, end_date=end_date, key_id=str(uuid.uuid4()), value=password, custom_key_identifier=None)] + app_create_param = ApplicationCreateParameters(available_to_other_tenants=False, + display_name=display_name, + identifier_uris=identifier_uris, + homepage='https://' + display_name, + reply_urls=None, + key_credentials=None, + password_credentials=password_creds, + oauth2_allow_implicit_flow=None, + required_resource_access=None, + app_roles=None) + try: + result = graph_client.applications.create(app_create_param) + except GraphErrorException as ex: + if 'insufficient privileges' in str(ex).lower(): + link = 'https://docs.microsoft.com/azure/azure-resource-manager/resource-group-create-service-principal-portal' # pylint: disable=line-too-long + raise CLIError("Directory permission is needed for the current user to register the application. " + "For how to configure, please refer '{}'. Original error: {}".format(link, ex)) + raise + return result + +def _create_service_principal(cli_ctx, identifier, resolve_app=True): + client = _graph_client_factory(cli_ctx) + app_id = identifier + if resolve_app: + if _is_guid(identifier): + result = list(client.applications.list(filter="appId eq '{}'".format(identifier))) + else: + result = list(client.applications.list( + filter="identifierUris/any(s:s eq '{}')".format(identifier))) + + try: + if not result: # assume we get an object id + result = [client.applications.get(identifier)] + app_id = result[0].app_id + except GraphErrorException: + pass # fallback to appid (maybe from an external tenant?) + + return client.service_principals.create(ServicePrincipalCreateParameters(app_id=app_id, account_enabled=True)) + +def _error_caused_by_role_assignment_exists(ex): + return getattr(ex, 'status_code', None) == 409 and 'role assignment already exists' in ex.message + +def _create_role_assignment(cli_ctx, role, assignee, resource_group_name=None, scope=None, + resolve_assignee=True, assignee_principal_type=None): + factory = _auth_client_factory(cli_ctx, scope) + assignments_client = factory.role_assignments + definitions_client = factory.role_definitions + role = 'b24988ac-6180-42a0-ab88-20f7382dd24c' + role_id = '/subscriptions/{}/providers/Microsoft.Authorization/roleDefinitions/{}'.format(definitions_client.config.subscription_id, role) + object_id = assignee + worker = MultiAPIAdaptor(cli_ctx) + return worker.create_role_assignment(assignments_client, uuid.uuid4(), role_id, object_id, scope, + assignee_principal_type) \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/tests/__init__.py b/src/connectedk8s/azext_connectedk8s/tests/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/tests/__init__.py @@ -0,0 +1,5 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py b/src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py @@ -0,0 +1,5 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py b/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py new file mode 100644 index 00000000000..8e81e27f72a --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py @@ -0,0 +1,40 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure_devtools.scenario_tests import AllowLargeResponse +from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer) + + +TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..')) + + +class Connectedk8sScenarioTest(ScenarioTest): + + @ResourceGroupPreparer(name_prefix='cli_test_connectedk8s') + def test_connectedk8s(self, resource_group): + + self.kwargs.update({ + 'name': 'test1' + }) + + self.cmd('connectedk8s create -g {rg} -n {name} --tags foo=doo', checks=[ + self.check('tags.foo', 'doo'), + self.check('name', '{name}') + ]) + self.cmd('connectedk8s update -g {rg} -n {name} --tags foo=boo', checks=[ + self.check('tags.foo', 'boo') + ]) + count = len(self.cmd('connectedk8s list').get_output_in_json()) + self.cmd('connectedk8s show - {rg} -n {name}', checks=[ + self.check('name', '{name}'), + self.check('resourceGroup', '{rg}'), + self.check('tags.foo', 'boo') + ]) + self.cmd('connectedk8s delete -g {rg} -n {name}') + final_count = len(self.cmd('connectedk8s list').get_output_in_json()) + self.assertTrue(final_count, count - 1) \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/__init__.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/__init__.py new file mode 100644 index 00000000000..9a8b55d5c21 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from .k8_connect_rp import K8ConnectRP +from .version import VERSION + +__all__ = ['K8ConnectRP'] + +__version__ = VERSION + diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py new file mode 100644 index 00000000000..f06c500aebb --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py @@ -0,0 +1,88 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.service_client import SDKClient +from msrest import Serializer, Deserializer +from msrestazure import AzureConfiguration +from .version import VERSION +from .operations.connected_cluster_operations import ConnectedClusterOperations +from .operations.operations import Operations +from . import models + + +class K8ConnectRPConfiguration(AzureConfiguration): + """Configuration for K8ConnectRP + Note that all parameters used to create this instance are saved as instance + attributes. + + :param credentials: Credentials needed for the client to connect to Azure. + :type credentials: :mod:`A msrestazure Credentials + object` + :param subscription_id: The ID of the subscription to which the kubernetes + cluster is registered. + :type subscription_id: str + :param str base_url: Service URL + """ + + def __init__( + self, credentials, subscription_id, base_url=None): + + if credentials is None: + raise ValueError("Parameter 'credentials' must not be None.") + if subscription_id is None: + raise ValueError("Parameter 'subscription_id' must not be None.") + if not base_url: + base_url = 'https://management.azure.com' + + super(K8ConnectRPConfiguration, self).__init__(base_url) + + self.add_user_agent('azure-mgmt-hybridkubernetes/{}'.format(VERSION)) + self.add_user_agent('Azure-SDK-For-Python') + + self.credentials = credentials + self.subscription_id = subscription_id + + +class K8ConnectRP(SDKClient): + """Azure Connected Cluster Resource Provider API for adopting any Kubernetes Cluster + + :ivar config: Configuration for client. + :vartype config: K8ConnectRPConfiguration + + :ivar connected_cluster: ConnectedCluster operations + :vartype connected_cluster: azure.mgmt.hybridkubernetes.operations.ConnectedClusterOperations + :ivar operations: Operations operations + :vartype operations: azure.mgmt.hybridkubernetes.operations.Operations + + :param credentials: Credentials needed for the client to connect to Azure. + :type credentials: :mod:`A msrestazure Credentials + object` + :param subscription_id: The ID of the subscription to which the kubernetes + cluster is registered. + :type subscription_id: str + :param str base_url: Service URL + """ + + def __init__( + self, credentials, subscription_id, base_url=None): + + self.config = K8ConnectRPConfiguration(credentials, subscription_id, base_url) + super(K8ConnectRP, self).__init__(self.config.credentials, self.config) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self.api_version = '2019-09-01-privatepreview' + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + + self.connected_cluster = ConnectedClusterOperations( + self._client, self.config, self._serialize, self._deserialize) + self.operations = Operations( + self._client, self.config, self._serialize, self._deserialize) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py new file mode 100644 index 00000000000..4ea247e54e3 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py @@ -0,0 +1,56 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +try: + from .connected_cluster_aad_access_profile_py3 import ConnectedClusterAADAccessProfile + from .connected_cluster_access_profile_py3 import ConnectedClusterAccessProfile + from .operation_display_py3 import OperationDisplay + from .operation_py3 import Operation + from .connected_cluster_identity_py3 import ConnectedClusterIdentity + from .connected_cluster_aad_profile_py3 import ConnectedClusterAADProfile + from .connected_cluster_py3 import ConnectedCluster + from .connected_cluster_patch_py3 import ConnectedClusterPatch + from .error_details_py3 import ErrorDetails + from .error_response_py3 import ErrorResponse, ErrorResponseException +except (SyntaxError, ImportError): + from .connected_cluster_aad_access_profile import ConnectedClusterAADAccessProfile + from .connected_cluster_access_profile import ConnectedClusterAccessProfile + from .operation_display import OperationDisplay + from .operation import Operation + from .connected_cluster_identity import ConnectedClusterIdentity + from .connected_cluster_aad_profile import ConnectedClusterAADProfile + from .connected_cluster import ConnectedCluster + from .connected_cluster_patch import ConnectedClusterPatch + from .error_details import ErrorDetails + from .error_response import ErrorResponse, ErrorResponseException +from .connected_cluster_paged import ConnectedClusterPaged +from .operation_paged import OperationPaged +from .k8_connect_rp_enums import ( + AzureEnvironment, + ResourceIdentityType, +) + +__all__ = [ + 'ConnectedClusterAADAccessProfile', + 'ConnectedClusterAccessProfile', + 'OperationDisplay', + 'Operation', + 'ConnectedClusterIdentity', + 'ConnectedClusterAADProfile', + 'ConnectedCluster', + 'ConnectedClusterPatch', + 'ErrorDetails', + 'ErrorResponse', 'ErrorResponseException', + 'ConnectedClusterPaged', + 'OperationPaged', + 'AzureEnvironment', + 'ResourceIdentityType', +] diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py new file mode 100644 index 00000000000..8777987b9de --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py @@ -0,0 +1,91 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedCluster(Model): + """ConnectedCluster. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Type of the resource requested. Possible values include: + 'Microsoft.Kubernetes/connectedClusters' + :vartype type: str or ~azure.mgmt.hybridkubernetes.models.enum + :param location: Required. Location of the cluster + :type location: str + :param tags: Required. Connected Cluster Resource Tags. + :type tags: dict[str, str] + :param identity: The identity of the connected cluster, if configured. + :type identity: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterIdentity + :param agent_public_key_certificate: Required. Base64 encoded public + certificate used by the agent to do the initial handshake to the backend + services in Azure. + :type agent_public_key_certificate: str + :param aad_profile: Required. + :type aad_profile: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAADProfile + :param kubernetes_version: The Kubernetes version of the connected cluster + resource + :type kubernetes_version: str + :param total_node_count: Number of nodes present in the connected cluster + resource + :type total_node_count: int + :param agent_version: Version of the agent running on the connected + cluster resource + :type agent_version: str + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + 'location': {'required': True}, + 'tags': {'required': True}, + 'agent_public_key_certificate': {'required': True}, + 'aad_profile': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'ConnectedClusterIdentity'}, + 'agent_public_key_certificate': {'key': 'properties.agentPublicKeyCertificate', 'type': 'str'}, + 'aad_profile': {'key': 'properties.aadProfile', 'type': 'ConnectedClusterAADProfile'}, + 'kubernetes_version': {'key': 'properties.kubernetesVersion', 'type': 'str'}, + 'total_node_count': {'key': 'properties.totalNodeCount', 'type': 'int'}, + 'agent_version': {'key': 'properties.agentVersion', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectedCluster, self).__init__(**kwargs) + self.id = None + self.name = None + self.type = None + self.location = kwargs.get('location', None) + self.tags = kwargs.get('tags', None) + self.identity = kwargs.get('identity', None) + self.agent_public_key_certificate = kwargs.get('agent_public_key_certificate', None) + self.aad_profile = kwargs.get('aad_profile', None) + self.kubernetes_version = kwargs.get('kubernetes_version', None) + self.total_node_count = kwargs.get('total_node_count', None) + self.agent_version = kwargs.get('agent_version', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py new file mode 100644 index 00000000000..528fbf69a20 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py @@ -0,0 +1,52 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAADAccessProfile(Model): + """ConnectedClusterAADAccessProfile. + + :param end_point: The endpoint to connect to the cluster + :type end_point: str + :param endpoint_authority_data: The end point authority data to connect to + the cluster + :type endpoint_authority_data: str + :param api_server_id: AAD API Server Id + :type api_server_id: str + :param client_id: Id of the client + :type client_id: str + :param environment: The azure environment. Possible values include: + 'AzurePublicCloud', 'AzureUSGovernmentCloud', 'AzureChinaCloud', + 'AzureGermanCloud' + :type environment: str or + ~azure.mgmt.hybridkubernetes.models.AzureEnvironment + :param tenant_id: AAD Tenent Id + :type tenant_id: str + """ + + _attribute_map = { + 'end_point': {'key': 'endPoint', 'type': 'str'}, + 'endpoint_authority_data': {'key': 'endpointAuthorityData', 'type': 'str'}, + 'api_server_id': {'key': 'apiServerId', 'type': 'str'}, + 'client_id': {'key': 'clientId', 'type': 'str'}, + 'environment': {'key': 'environment', 'type': 'AzureEnvironment'}, + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterAADAccessProfile, self).__init__(**kwargs) + self.end_point = kwargs.get('end_point', None) + self.endpoint_authority_data = kwargs.get('endpoint_authority_data', None) + self.api_server_id = kwargs.get('api_server_id', None) + self.client_id = kwargs.get('client_id', None) + self.environment = kwargs.get('environment', None) + self.tenant_id = kwargs.get('tenant_id', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py new file mode 100644 index 00000000000..c2b7fa7aab4 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py @@ -0,0 +1,52 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAADAccessProfile(Model): + """ConnectedClusterAADAccessProfile. + + :param end_point: The endpoint to connect to the cluster + :type end_point: str + :param endpoint_authority_data: The end point authority data to connect to + the cluster + :type endpoint_authority_data: str + :param api_server_id: AAD API Server Id + :type api_server_id: str + :param client_id: Id of the client + :type client_id: str + :param environment: The azure environment. Possible values include: + 'AzurePublicCloud', 'AzureUSGovernmentCloud', 'AzureChinaCloud', + 'AzureGermanCloud' + :type environment: str or + ~azure.mgmt.hybridkubernetes.models.AzureEnvironment + :param tenant_id: AAD Tenent Id + :type tenant_id: str + """ + + _attribute_map = { + 'end_point': {'key': 'endPoint', 'type': 'str'}, + 'endpoint_authority_data': {'key': 'endpointAuthorityData', 'type': 'str'}, + 'api_server_id': {'key': 'apiServerId', 'type': 'str'}, + 'client_id': {'key': 'clientId', 'type': 'str'}, + 'environment': {'key': 'environment', 'type': 'AzureEnvironment'}, + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + } + + def __init__(self, *, end_point: str=None, endpoint_authority_data: str=None, api_server_id: str=None, client_id: str=None, environment=None, tenant_id: str=None, **kwargs) -> None: + super(ConnectedClusterAADAccessProfile, self).__init__(**kwargs) + self.end_point = end_point + self.endpoint_authority_data = endpoint_authority_data + self.api_server_id = api_server_id + self.client_id = client_id + self.environment = environment + self.tenant_id = tenant_id diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile.py new file mode 100644 index 00000000000..608f5cf0449 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile.py @@ -0,0 +1,46 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAADProfile(Model): + """ConnectedClusterAADProfile. + + All required parameters must be populated in order to send to Azure. + + :param tenant_id: Required. The aad tenant id which is configured on + target K8s cluster + :type tenant_id: str + :param client_app_id: Required. The client app id configured on target K8 + cluster + :type client_app_id: str + :param server_app_id: Required. The server app id to access AD server + :type server_app_id: str + """ + + _validation = { + 'tenant_id': {'required': True}, + 'client_app_id': {'required': True}, + 'server_app_id': {'required': True}, + } + + _attribute_map = { + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'client_app_id': {'key': 'clientAppId', 'type': 'str'}, + 'server_app_id': {'key': 'serverAppId', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterAADProfile, self).__init__(**kwargs) + self.tenant_id = kwargs.get('tenant_id', None) + self.client_app_id = kwargs.get('client_app_id', None) + self.server_app_id = kwargs.get('server_app_id', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile_py3.py new file mode 100644 index 00000000000..dbc126a4531 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_profile_py3.py @@ -0,0 +1,46 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAADProfile(Model): + """ConnectedClusterAADProfile. + + All required parameters must be populated in order to send to Azure. + + :param tenant_id: Required. The aad tenant id which is configured on + target K8s cluster + :type tenant_id: str + :param client_app_id: Required. The client app id configured on target K8 + cluster + :type client_app_id: str + :param server_app_id: Required. The server app id to access AD server + :type server_app_id: str + """ + + _validation = { + 'tenant_id': {'required': True}, + 'client_app_id': {'required': True}, + 'server_app_id': {'required': True}, + } + + _attribute_map = { + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'client_app_id': {'key': 'clientAppId', 'type': 'str'}, + 'server_app_id': {'key': 'serverAppId', 'type': 'str'}, + } + + def __init__(self, *, tenant_id: str, client_app_id: str, server_app_id: str, **kwargs) -> None: + super(ConnectedClusterAADProfile, self).__init__(**kwargs) + self.tenant_id = tenant_id + self.client_app_id = client_app_id + self.server_app_id = server_app_id diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py new file mode 100644 index 00000000000..da4052a0745 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py @@ -0,0 +1,56 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAccessProfile(Model): + """ConnectedClusterAccessProfile. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar type: Required. Type of the resource on which the credentials are + requested. Default value: "Microsoft.Kubernetes/connectedClusters" . + :vartype type: str + :param location: Required. Location of the cluster + :type location: str + :param kube_config: Required. Base64 Encoded kubeconfig for accessing + target cluster + :type kube_config: str + :param aad_access_profile: Required. + :type aad_access_profile: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAADAccessProfile + """ + + _validation = { + 'type': {'required': True, 'constant': True}, + 'location': {'required': True}, + 'kube_config': {'required': True}, + 'aad_access_profile': {'required': True}, + } + + _attribute_map = { + 'type': {'key': 'type', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'kube_config': {'key': 'properties.kubeConfig', 'type': 'str'}, + 'aad_access_profile': {'key': 'properties.aadAccessProfile', 'type': 'ConnectedClusterAADAccessProfile'}, + } + + type = "Microsoft.Kubernetes/connectedClusters" + + def __init__(self, **kwargs): + super(ConnectedClusterAccessProfile, self).__init__(**kwargs) + self.location = kwargs.get('location', None) + self.kube_config = kwargs.get('kube_config', None) + self.aad_access_profile = kwargs.get('aad_access_profile', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py new file mode 100644 index 00000000000..df39e6fcc95 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py @@ -0,0 +1,56 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAccessProfile(Model): + """ConnectedClusterAccessProfile. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar type: Required. Type of the resource on which the credentials are + requested. Default value: "Microsoft.Kubernetes/connectedClusters" . + :vartype type: str + :param location: Required. Location of the cluster + :type location: str + :param kube_config: Required. Base64 Encoded kubeconfig for accessing + target cluster + :type kube_config: str + :param aad_access_profile: Required. + :type aad_access_profile: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAADAccessProfile + """ + + _validation = { + 'type': {'required': True, 'constant': True}, + 'location': {'required': True}, + 'kube_config': {'required': True}, + 'aad_access_profile': {'required': True}, + } + + _attribute_map = { + 'type': {'key': 'type', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'kube_config': {'key': 'properties.kubeConfig', 'type': 'str'}, + 'aad_access_profile': {'key': 'properties.aadAccessProfile', 'type': 'ConnectedClusterAADAccessProfile'}, + } + + type = "Microsoft.Kubernetes/connectedClusters" + + def __init__(self, *, location: str, kube_config: str, aad_access_profile, **kwargs) -> None: + super(ConnectedClusterAccessProfile, self).__init__(**kwargs) + self.location = location + self.kube_config = kube_config + self.aad_access_profile = aad_access_profile diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py new file mode 100644 index 00000000000..40e9885b6a9 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py @@ -0,0 +1,50 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterIdentity(Model): + """Identity for the virtual machine. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar principal_id: The principal id of connected cluster identity. This + property will only be provided for a system assigned identity. + :vartype principal_id: str + :ivar tenant_id: The tenant id associated with the connected cluster. This + property will only be provided for a system assigned identity. + :vartype tenant_id: str + :param type: The type of identity used for the connected cluster. The type + 'SystemAssigned includes a system created identity. The type 'None' will + remove any identities from the virtual machine. Possible values include: + 'SystemAssigned', 'None' + :type type: str or + ~azure.mgmt.hybridkubernetes.models.ResourceIdentityType + """ + + _validation = { + 'principal_id': {'readonly': True}, + 'tenant_id': {'readonly': True}, + } + + _attribute_map = { + 'principal_id': {'key': 'principalId', 'type': 'str'}, + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'ResourceIdentityType'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterIdentity, self).__init__(**kwargs) + self.principal_id = None + self.tenant_id = None + self.type = kwargs.get('type', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py new file mode 100644 index 00000000000..58d5d5ce5f9 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py @@ -0,0 +1,50 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterIdentity(Model): + """Identity for the virtual machine. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar principal_id: The principal id of connected cluster identity. This + property will only be provided for a system assigned identity. + :vartype principal_id: str + :ivar tenant_id: The tenant id associated with the connected cluster. This + property will only be provided for a system assigned identity. + :vartype tenant_id: str + :param type: The type of identity used for the connected cluster. The type + 'SystemAssigned includes a system created identity. The type 'None' will + remove any identities from the virtual machine. Possible values include: + 'SystemAssigned', 'None' + :type type: str or + ~azure.mgmt.hybridkubernetes.models.ResourceIdentityType + """ + + _validation = { + 'principal_id': {'readonly': True}, + 'tenant_id': {'readonly': True}, + } + + _attribute_map = { + 'principal_id': {'key': 'principalId', 'type': 'str'}, + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'ResourceIdentityType'}, + } + + def __init__(self, *, type=None, **kwargs) -> None: + super(ConnectedClusterIdentity, self).__init__(**kwargs) + self.principal_id = None + self.tenant_id = None + self.type = type diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_paged.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_paged.py new file mode 100644 index 00000000000..55600c14031 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_paged.py @@ -0,0 +1,27 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.paging import Paged + + +class ConnectedClusterPaged(Paged): + """ + A paging container for iterating over a list of :class:`ConnectedCluster ` object + """ + + _attribute_map = { + 'next_link': {'key': 'nextLink', 'type': 'str'}, + 'current_page': {'key': 'value', 'type': '[ConnectedCluster]'} + } + + def __init__(self, *args, **kwargs): + + super(ConnectedClusterPaged, self).__init__(*args, **kwargs) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch.py new file mode 100644 index 00000000000..030bd6a1191 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch.py @@ -0,0 +1,28 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterPatch(Model): + """Object containing updates for patch operations. + + :param tags: Resource tags. + :type tags: dict[str, str] + """ + + _attribute_map = { + 'tags': {'key': 'tags', 'type': '{str}'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterPatch, self).__init__(**kwargs) + self.tags = kwargs.get('tags', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch_py3.py new file mode 100644 index 00000000000..95e9400072b --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_patch_py3.py @@ -0,0 +1,28 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterPatch(Model): + """Object containing updates for patch operations. + + :param tags: Resource tags. + :type tags: dict[str, str] + """ + + _attribute_map = { + 'tags': {'key': 'tags', 'type': '{str}'}, + } + + def __init__(self, *, tags=None, **kwargs) -> None: + super(ConnectedClusterPatch, self).__init__(**kwargs) + self.tags = tags diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py new file mode 100644 index 00000000000..6d238fafe2b --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py @@ -0,0 +1,91 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedCluster(Model): + """ConnectedCluster. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Type of the resource requested. Possible values include: + 'Microsoft.Kubernetes/connectedClusters' + :vartype type: str or ~azure.mgmt.hybridkubernetes.models.enum + :param location: Required. Location of the cluster + :type location: str + :param tags: Required. Connected Cluster Resource Tags. + :type tags: dict[str, str] + :param identity: The identity of the connected cluster, if configured. + :type identity: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterIdentity + :param agent_public_key_certificate: Required. Base64 encoded public + certificate used by the agent to do the initial handshake to the backend + services in Azure. + :type agent_public_key_certificate: str + :param aad_profile: Required. + :type aad_profile: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAADProfile + :param kubernetes_version: The Kubernetes version of the connected cluster + resource + :type kubernetes_version: str + :param total_node_count: Number of nodes present in the connected cluster + resource + :type total_node_count: int + :param agent_version: Version of the agent running on the connected + cluster resource + :type agent_version: str + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + 'location': {'required': True}, + 'tags': {'required': True}, + 'agent_public_key_certificate': {'required': True}, + 'aad_profile': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'ConnectedClusterIdentity'}, + 'agent_public_key_certificate': {'key': 'properties.agentPublicKeyCertificate', 'type': 'str'}, + 'aad_profile': {'key': 'properties.aadProfile', 'type': 'ConnectedClusterAADProfile'}, + 'kubernetes_version': {'key': 'properties.kubernetesVersion', 'type': 'str'}, + 'total_node_count': {'key': 'properties.totalNodeCount', 'type': 'int'}, + 'agent_version': {'key': 'properties.agentVersion', 'type': 'str'}, + } + + def __init__(self, *, location: str, tags, agent_public_key_certificate: str, aad_profile, identity=None, kubernetes_version: str=None, total_node_count: int=None, agent_version: str=None, **kwargs) -> None: + super(ConnectedCluster, self).__init__(**kwargs) + self.id = None + self.name = None + self.type = None + self.location = location + self.tags = tags + self.identity = identity + self.agent_public_key_certificate = agent_public_key_certificate + self.aad_profile = aad_profile + self.kubernetes_version = kubernetes_version + self.total_node_count = total_node_count + self.agent_version = agent_version diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details.py new file mode 100644 index 00000000000..2626803ba6d --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ErrorDetails(Model): + """The error response details containing error code and error message. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar code: The error code. + :vartype code: str + :ivar message: The error message. + :vartype message: str + """ + + _validation = { + 'code': {'readonly': True}, + 'message': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ErrorDetails, self).__init__(**kwargs) + self.code = None + self.message = None diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details_py3.py new file mode 100644 index 00000000000..92ce6394eac --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_details_py3.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ErrorDetails(Model): + """The error response details containing error code and error message. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar code: The error code. + :vartype code: str + :ivar message: The error message. + :vartype message: str + """ + + _validation = { + 'code': {'readonly': True}, + 'message': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + } + + def __init__(self, **kwargs) -> None: + super(ErrorDetails, self).__init__(**kwargs) + self.code = None + self.message = None diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response.py new file mode 100644 index 00000000000..a810aa1fc2e --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model +from msrest.exceptions import HttpOperationError + + +class ErrorResponse(Model): + """The error response that indicates why an operation has failed. + + :param error: + :type error: ~azure.mgmt.hybridkubernetes.models.ErrorDetails + """ + + _attribute_map = { + 'error': {'key': 'error', 'type': 'ErrorDetails'}, + } + + def __init__(self, **kwargs): + super(ErrorResponse, self).__init__(**kwargs) + self.error = kwargs.get('error', None) + + +class ErrorResponseException(HttpOperationError): + """Server responsed with exception of type: 'ErrorResponse'. + + :param deserialize: A deserializer + :param response: Server response to be deserialized. + """ + + def __init__(self, deserialize, response, *args): + + super(ErrorResponseException, self).__init__(deserialize, response, 'ErrorResponse', *args) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response_py3.py new file mode 100644 index 00000000000..c0d78c8e994 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/error_response_py3.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model +from msrest.exceptions import HttpOperationError + + +class ErrorResponse(Model): + """The error response that indicates why an operation has failed. + + :param error: + :type error: ~azure.mgmt.hybridkubernetes.models.ErrorDetails + """ + + _attribute_map = { + 'error': {'key': 'error', 'type': 'ErrorDetails'}, + } + + def __init__(self, *, error=None, **kwargs) -> None: + super(ErrorResponse, self).__init__(**kwargs) + self.error = error + + +class ErrorResponseException(HttpOperationError): + """Server responsed with exception of type: 'ErrorResponse'. + + :param deserialize: A deserializer + :param response: Server response to be deserialized. + """ + + def __init__(self, deserialize, response, *args): + + super(ErrorResponseException, self).__init__(deserialize, response, 'ErrorResponse', *args) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py new file mode 100644 index 00000000000..ed048f0836c --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py @@ -0,0 +1,26 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum + + +class AzureEnvironment(str, Enum): + + azure_public_cloud = "AzurePublicCloud" + azure_us_government_cloud = "AzureUSGovernmentCloud" + azure_china_cloud = "AzureChinaCloud" + azure_german_cloud = "AzureGermanCloud" + + +class ResourceIdentityType(str, Enum): + + system_assigned = "SystemAssigned" + none = "None" diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation.py new file mode 100644 index 00000000000..a92f2775c6f --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class Operation(Model): + """The Connected cluster API operation. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: Operation name: {Microsoft.Kubernetes}/{resource}/{operation} + :vartype name: str + :ivar display: The object that represents the operation. + :vartype display: ~azure.mgmt.hybridkubernetes.models.OperationDisplay + """ + + _validation = { + 'name': {'readonly': True}, + 'display': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'display': {'key': 'display', 'type': 'OperationDisplay'}, + } + + def __init__(self, **kwargs): + super(Operation, self).__init__(**kwargs) + self.name = None + self.display = None diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display.py new file mode 100644 index 00000000000..d7d73494c89 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class OperationDisplay(Model): + """The object that represents the operation. + + :param provider: Service provider: Microsoft.connectedClusters + :type provider: str + :param resource: Connected Cluster Resource on which the operation is + performed + :type resource: str + :param operation: Operation type: Read, write, delete, etc. + :type operation: str + :param description: Description of the operation. + :type description: str + """ + + _attribute_map = { + 'provider': {'key': 'provider', 'type': 'str'}, + 'resource': {'key': 'resource', 'type': 'str'}, + 'operation': {'key': 'operation', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(OperationDisplay, self).__init__(**kwargs) + self.provider = kwargs.get('provider', None) + self.resource = kwargs.get('resource', None) + self.operation = kwargs.get('operation', None) + self.description = kwargs.get('description', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display_py3.py new file mode 100644 index 00000000000..ec4cfb57ef1 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_display_py3.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class OperationDisplay(Model): + """The object that represents the operation. + + :param provider: Service provider: Microsoft.connectedClusters + :type provider: str + :param resource: Connected Cluster Resource on which the operation is + performed + :type resource: str + :param operation: Operation type: Read, write, delete, etc. + :type operation: str + :param description: Description of the operation. + :type description: str + """ + + _attribute_map = { + 'provider': {'key': 'provider', 'type': 'str'}, + 'resource': {'key': 'resource', 'type': 'str'}, + 'operation': {'key': 'operation', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + } + + def __init__(self, *, provider: str=None, resource: str=None, operation: str=None, description: str=None, **kwargs) -> None: + super(OperationDisplay, self).__init__(**kwargs) + self.provider = provider + self.resource = resource + self.operation = operation + self.description = description diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_paged.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_paged.py new file mode 100644 index 00000000000..56892781d2b --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_paged.py @@ -0,0 +1,27 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.paging import Paged + + +class OperationPaged(Paged): + """ + A paging container for iterating over a list of :class:`Operation ` object + """ + + _attribute_map = { + 'next_link': {'key': 'nextLink', 'type': 'str'}, + 'current_page': {'key': 'value', 'type': '[Operation]'} + } + + def __init__(self, *args, **kwargs): + + super(OperationPaged, self).__init__(*args, **kwargs) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_py3.py new file mode 100644 index 00000000000..d0871dcc46f --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/operation_py3.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class Operation(Model): + """The Connected cluster API operation. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: Operation name: {Microsoft.Kubernetes}/{resource}/{operation} + :vartype name: str + :ivar display: The object that represents the operation. + :vartype display: ~azure.mgmt.hybridkubernetes.models.OperationDisplay + """ + + _validation = { + 'name': {'readonly': True}, + 'display': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'display': {'key': 'display', 'type': 'OperationDisplay'}, + } + + def __init__(self, **kwargs) -> None: + super(Operation, self).__init__(**kwargs) + self.name = None + self.display = None diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/__init__.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/__init__.py new file mode 100644 index 00000000000..86d5be1f5b6 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/__init__.py @@ -0,0 +1,18 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from .connected_cluster_operations import ConnectedClusterOperations +from .operations import Operations + +__all__ = [ + 'ConnectedClusterOperations', + 'Operations', +] diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py new file mode 100644 index 00000000000..ef5538373f9 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py @@ -0,0 +1,511 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +import uuid +from msrest.pipeline import ClientRawResponse + +from .. import models + + +class ConnectedClusterOperations(object): + """ConnectedClusterOperations operations. + + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + :ivar api_version: Version of the API called. Constant value: "2019-09-01-privatepreview". + """ + + models = models + + def __init__(self, client, config, serializer, deserializer): + + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self.api_version = "2019-09-01-privatepreview" + + self.config = config + + def create( + self, subscription_name, resource_group_name, cluster_name, onboarding_spn_id, onboarding_spn_secret, connected_cluster, custom_headers=None, raw=False, **operation_config): + """Registers a new K8s cluster. + + API to register a new K8s cluster and thereby create a tracked resource + in ARM. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param connected_cluster: + :type connected_cluster: + ~azure.mgmt.hybridkubernetes.models.ConnectedCluster + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedCluster or ClientRawResponse if raw=true + :rtype: ~azure.mgmt.hybridkubernetes.models.ConnectedCluster or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.create.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = 'application/json; charset=utf-8' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct body + body_content = self._serialize.body(connected_cluster, 'ConnectedCluster') + + # Construct and send request + request = self._client.put(url, query_parameters, header_parameters, body_content) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200, 201]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedCluster', response) + if response.status_code == 201: + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + create.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def update( + self, resource_group_name, cluster_name, tags=None, custom_headers=None, raw=False, **operation_config): + """Updates a connected cluster. + + API to update certain properties of the connected cluster resource. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param tags: Resource tags. + :type tags: dict[str, str] + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedCluster or ClientRawResponse if raw=true + :rtype: ~azure.mgmt.hybridkubernetes.models.ConnectedCluster or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + connected_cluster_patch = models.ConnectedClusterPatch(tags=tags) + + # Construct URL + url = self.update.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = 'application/json; charset=utf-8' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct body + body_content = self._serialize.body(connected_cluster_patch, 'ConnectedClusterPatch') + + # Construct and send request + request = self._client.patch(url, query_parameters, header_parameters, body_content) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + update.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def get( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Gets data of the specified cluster. + + API to get the properties of a specific registered K8s cluster. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedCluster or ClientRawResponse if raw=true + :rtype: ~azure.mgmt.hybridkubernetes.models.ConnectedCluster or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.get.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + get.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def delete( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Deletes a specified cluster. + + API to delete an existing K8s cluster being tracked. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: None or ClientRawResponse if raw=true + :rtype: None or ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.delete.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + # Construct headers + header_parameters = {} + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.delete(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200, 204]: + raise models.ErrorResponseException(self._deserialize, response) + + if raw: + client_raw_response = ClientRawResponse(None, response) + return client_raw_response + delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def list_credentials( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Lists the endpoint and credentials details to connect to the K8s + cluster's API server. + + API to fetch endpoint and credential details to connect to the K8s + cluster’s API server via Azure. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedClusterAccessProfile or ClientRawResponse if + raw=true + :rtype: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAccessProfile or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.list_credentials.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.post(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedClusterAccessProfile', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + list_credentials.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}/listCredentials'} + + def list_by_resource_group( + self, resource_group_name, custom_headers=None, raw=False, **operation_config): + """Lists all connected clusters. + + API to enumerate registered connected K8s clusters under a Resource + Group. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: An iterator like instance of ConnectedCluster + :rtype: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterPaged[~azure.mgmt.hybridkubernetes.models.ConnectedCluster] + :raises: + :class:`ErrorResponseException` + """ + def internal_paging(next_link=None, raw=False): + + if not next_link: + # Construct URL + url = self.list_by_resource_group.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + else: + url = next_link + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + return response + + # Deserialize response + deserialized = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies) + + if raw: + header_dict = {} + client_raw_response = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies, header_dict) + return client_raw_response + + return deserialized + list_by_resource_group.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters'} + + def list_by_subscription( + self, custom_headers=None, raw=False, **operation_config): + """Lists all connected clusters. + + API to enumerate registered connected K8s clusters under a + Subscription. + + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: An iterator like instance of ConnectedCluster + :rtype: + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterPaged[~azure.mgmt.hybridkubernetes.models.ConnectedCluster] + :raises: + :class:`ErrorResponseException` + """ + def internal_paging(next_link=None, raw=False): + + if not next_link: + # Construct URL + url = self.list_by_subscription.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + else: + url = next_link + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + return response + + # Deserialize response + deserialized = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies) + + if raw: + header_dict = {} + client_raw_response = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies, header_dict) + return client_raw_response + + return deserialized + list_by_subscription.metadata = {'url': '/subscriptions/{subscriptionId}/providers/Microsoft.Kubernetes/connectedClusters'} diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py new file mode 100644 index 00000000000..8a7689df517 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py @@ -0,0 +1,97 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +import uuid +from msrest.pipeline import ClientRawResponse + +from .. import models + + +class Operations(object): + """Operations operations. + + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + :ivar api_version: Version of the API called. Constant value: "2019-09-01-privatepreview". + """ + + models = models + + def __init__(self, client, config, serializer, deserializer): + + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self.api_version = "2019-09-01-privatepreview" + + self.config = config + + def get( + self, custom_headers=None, raw=False, **operation_config): + """Lists all of the available API operations for Connected Cluster + resource. + + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: An iterator like instance of Operation + :rtype: + ~azure.mgmt.hybridkubernetes.models.OperationPaged[~azure.mgmt.hybridkubernetes.models.Operation] + :raises: + :class:`ErrorResponseException` + """ + def internal_paging(next_link=None, raw=False): + + if not next_link: + # Construct URL + url = self.get.metadata['url'] + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + else: + url = next_link + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + return response + + # Deserialize response + deserialized = models.OperationPaged(internal_paging, self._deserialize.dependencies) + + if raw: + header_dict = {} + client_raw_response = models.OperationPaged(internal_paging, self._deserialize.dependencies, header_dict) + return client_raw_response + + return deserialized + get.metadata = {'url': '/providers/Microsoft.Kubernetes/operations'} diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py new file mode 100644 index 00000000000..e0ec669828c --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py @@ -0,0 +1,13 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +VERSION = "0.1.0" + diff --git a/src/connectedk8s/setup.cfg b/src/connectedk8s/setup.cfg new file mode 100644 index 00000000000..3c6e79cf31d --- /dev/null +++ b/src/connectedk8s/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/src/connectedk8s/setup.py b/src/connectedk8s/setup.py new file mode 100644 index 00000000000..a3b89d76a8c --- /dev/null +++ b/src/connectedk8s/setup.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +from codecs import open +from setuptools import setup, find_packages +try: + from azure_bdist_wheel import cmdclass +except ImportError: + from distutils import log as logger + logger.warn("Wheel is not available, disabling bdist_wheel hook") + +# TODO: Confirm this is the right version number you want and it matches your +# HISTORY.rst entry. +VERSION = '0.1.0' + +# The full list of classifiers is available at +# https://pypi.python.org/pypi?%3Aaction=list_classifiers +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'License :: OSI Approved :: MIT License', +] + +# TODO: Add any additional SDK dependencies here +DEPENDENCIES = [ + 'azure-cli-core' +] + +with open('README.rst', 'r', encoding='utf-8') as f: + README = f.read() +with open('HISTORY.rst', 'r', encoding='utf-8') as f: + HISTORY = f.read() + +setup( + name='connectedk8s', + version=VERSION, + description='Microsoft Azure Command-Line Tools Connectedk8s Extension', + # TODO: Update author and email, if applicable + author='Microsoft Corporation', + author_email='azpycli@microsoft.com', + # TODO: consider pointing directly to your source code instead of the generic repo + url='https://github.com/Azure/azure-cli-extensions', + long_description=README + '\n\n' + HISTORY, + license='MIT', + classifiers=CLASSIFIERS, + packages=find_packages(), + install_requires=DEPENDENCIES, + package_data={'azext_connectedk8s': ['azext_metadata.json']}, +) \ No newline at end of file From 50906983782f2ad2bf2350efc5a3b8592b3bba46 Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Fri, 22 Nov 2019 16:21:59 +0530 Subject: [PATCH 02/58] Vanilla CLI Code for Connected Cluster --- UpgradeLog.htm | Bin 0 -> 32094 bytes .../azext_connectedk8s/_client_factory.py | 3 +- .../azext_connectedk8s/_params.py | 2 +- .../azext_connectedk8s/commands.py | 4 +- src/connectedk8s/azext_connectedk8s/custom.py | 289 +++++++++++++----- .../connected_cluster_operations.py | 140 ++++++++- 6 files changed, 351 insertions(+), 87 deletions(-) create mode 100644 UpgradeLog.htm diff --git a/UpgradeLog.htm b/UpgradeLog.htm new file mode 100644 index 0000000000000000000000000000000000000000..684ad31e81883d1a3ba42a00ab5ff970a3a1132f GIT binary patch literal 32094 zcmeI5`&Sf6mVoou-E;PTsL`F)UK;^XM0<1&PaDApg4jCtnS+3UA|i!=&dmPlyZe0+ zg!!OIDvL+Yob}YH%FM`!8}}9W-nbE&^}qk~r|b{eMD`>b&la+m+0$$?Yh~-%ZuTMD z%ce6|MWiP9=SqbMK0v=0XV%>7$W-F};3{I^E66P<71@N)J(@Y?aUMZb5!_;NOt z4IO@OW>NTI#1_QwRF4qlyBzI90!v}6Zo(z;y_C(v?KSQzf#+!9rA$dJ zP;Z@5$G6uHf=pH!b@6(U>m6uzw8au_k`|+nulF|St2g?$fwaO$d$ks@9Ow~}*zW*C zNK)Gr&Od_unrKth|6kyMG{}cR`Yn$FZCbM3N;w}7;w!F}) z5dLmsN9A)o<|XN@H;u^kSBv-+?kUY1(76q#dV|?%1V^7$9D9@WlD@t6ql}|2^aXFc z3(nCqwgLrm>@uEg@bG(sYrW_uw5~v1GYgz7RMm>yKq`Y+CXc;Qa@x_9Fw3R6k0kY1 zMjlFZ3#xa5&Dm^-<5`T<$oHbZIyzD>5urujya(p1*?x!ww0y>5AE86f=9qSjFLNEe zaGR3m`p7o&wn2+KMPG93i?y8iEW~e zH;3M~sAqa>?-`%B@F!~CHX8Xcdv~ajjx(bGJ$`eY9J)}QJbV4gb+fGR>P@e{x89$*nvNP5!7^Y_a_+#4=KZrGTH?GHXMqvWworC{-=J9vo*%Y=Rc-$X~h}FMab;9S@C3c8;$on(opXc zFH?@D4>@TG*02DMDK*=CQ*RQ=p&Fqp^2df_|GG`Xt%~0ua4@N zD@xdDN%EXi$^^%?k|fy{V?1V_v@FX&aGvCOZo$+{p{H!JbNx1-xF!~3CM|^)#k$eL z2*Z3pvpK=_K!vMkMVicSpT7?u6V*O{W;-! z1@%geQlgH?r4R+@b&QkVa_0`w>K{2aw|SR=P+kQnVtLV{W(j*?q}t5VC7p@<=_~Ni zSpGJ7bjM*cjXbUF%eFDfH(5BZQe>^|+vn;_(19e!qJ*1Tz{?d2Snm6xB^oJeCeL1Z zKN>83i7=|V_pX0yJ&!If^NI1sB;!=5=2lk1K7zdFH22Jo)YW+rB)%19BWtxa zC&}Bm#<5eWb^elb=aV~?a_zf|H*oc=*Li$`sBg(vM{}6X#$+1joucbL_~p(bcPqF8 z>m0OP)=UTM`*H`9Yc6R`^ls3Ti;dID`e~*hYJJ>`l zip{jdeK$?*Thi;u+rKbI{QL^MO(}B?k{v0xfMq8J&-+K=3)jDuj3ebSB=dsfqr*~HF=C4+*!WTUT%_lH`1t0q zd<>19MDmmLun2B9Y5j@FdI61j&Uc*OXY6D8g(z~6NNbILUu6$zHAYYF&D#L0mz0l( zzL$Y~hjBbbD=B?Mty@4F1?Dnz_tVcLclY_e2d4wz&mpq|dfx=*K5$;r<_U0zfw<23 z8a39rHwzXIsXtE3b*^@RCKe;`?l!gdC>iB=Eqly&FK}p&vjZTB^$6HJ0OM=WI1iLB z)E)!&DA#M8tpWc5S7vHQsqvov-Rt^-^I3FmmO5Ia73%HK`VspSR-kU09_H8w{Q^2~ za%T%pErZ)ru(EgO0k^|SY8&2spv64c3;<0|?@@0Hu04e#&e&!s`%15S)O<`&t|_m8 zt@WRR(=2?F9|!P4`HXTt2ga}9-3q_Yf&YQ?1Iq1nfL18 zWvnsDy*>Kega2Bz17NsUgBIuu7eA_+gg#S8$vJ-?!lR5gZo4XNBH}f%}j? zT<`AZ&N?Lvv>BvVN5NZgd_x`gI6s2=Ev|3Tasrs^$m|`*8Txz}pggD7QLY__3qkI~ z)E$SrkD=}tN`N;MVBJ2^NDj(UuBQhgvyw~NZz&EM}np4)&?~#rX+;I`w`9?Zp zSkP+3(#AH%h~*=8c@9he44Zlz%eRkx{ECJCh)sMKyyFbkcLhtgh4o(H`u)jXZ zG(zh~c#1*FjY6&gYmZuO%9fyaj=smR(#u?r1OEjUbv$?-t^FJ@m$AuHz})4`_<9aY zEf!1EeT-E$8)O9VKKPJ3#0!srGfbIr@hZ@qi})ndEEqfi?`4kWI;S{0fPyLP_G+MN z9M3+(_Y;1>$Z&*y^_oU%de=p+pVDTTJ~#0N_W2k}Z_r9Q4k()nea_R`IHetG3hM>b z{t6~{fO>~J{ou66*&0yX18wv(!P#k z2pH~mSmM4ue-n<*(8pZxh|~B)uiMb%-0%Thy#qCOg0CK+&sV^HPi-@-{a`vmt!G^C z(`GJk`UWLKU}l7U13$V5o<@#eL)lC2JqFq)G8+flD`>mMJtLHnz!|;&8hC!7WDq(h z!Q~6m_zD)zYGydMT(7U>-H~PFHcgv@pjCU6z2)o+ca`!Y+`9$tw}Y0=P<|V_<^o3J z^s~-=WjRF2dw!q7!xy2qcIb5iN(Z6F$j6zg*@=(gll-|6>+kE)%Q>*~@#N(`zWeiC zwCH=rB4r24`p_w3QP!XDe*E|I-fn=4^YLT4HLBWtck7p8LV*Jy*gTrjL23G5fIvoX>&cTlDXG zxDmt%t@}B?p!Ez8E&GKQ-Z5TS0LC?XHS!V9T_}GCoG;L`8$96v*Jk&fua82_0q zgR|dvT=#PvqP`hSvo>2`<97`oVv;9A@>)#r*6s1Pj^Nbd^;OY)IDM>%~ zE^x-1ul$^kZ_MGB?cn!$u50Ki^R5nm&*Y~Zd|FG*b?3WJ{QZ2Q+34!r_7hfh0h{_9 zYpBnAhHW!PxR33AiWhl~jT^x#k7A|w`E^cl16%o-dS<9cv9He6oFPqOxt-^XVIwE8 zrrKF&T~n01DltGm%aoWQHe39e*6Y|;{i%I>#~IQTWkYxm^Q8~?m}Pv<@iW(txmp3g zPn^G^q_d`m}n-61)w`ghJc^+IJV6z|4<0MxbSn02PHo<6+@>%F` z)~GLBWQMo{j5o9$q?i5R0mrfD`msm&fLGLf2|cR;pCw9W;mTWZcU5o?I?Q@H=lTrX zHhs_Y8Klika2x`Pd>!Wg5HN-U%+IutR}<8D1Q&19+e@zRfY%6EjspD^FkNkMPmrs~ z)4|iYOJXocYnmFjsJ8*mE7W=kz2<`Dr)vb8^d`Rs z=-X9AV;;G-OAWdH0Xe?`p1yn^59&_)SCqd1x_aQfLCVeM|3cmOKr+K^R3QF?+%Zf4 z3JiCsJne1#T|>3fWiT-l^Q z^-yd!kb!>N+5Z;OaaKM-?+?K8Hg~@U-c7^n1$ukOXBylm!DSy<&aEGEXPNW&U^+p4 zGxtlhcGc-Ae3I4)Xu1hy#!c=jy#pWT>0cduPkH*B?cDj~{IG8_$#?w6H<|cW8nwpt zU`LGckndS>g*LwP_+AI|SX$b|@gHkBaPs;`Q5whfaW%Vww6>r;O8rWZ zo8utYsw!7q*{`E8u0I_&;#TR)?0^xsQQ%QtxUV+nf-8mMqf}Orxw5P4y*Xd%`;$XW zxZ^HT-;@)nJS$$Ej^gS_QyYF?s_SJ~M{Trwt~)zhbi~H}mT~WpUfxXX8@`Tit#-b1 zDD4MzhmbbPmW~o_UvAgTnw!y8KYdz4JLqm4twBBBMnYx#qXkTOE&wC$MSgbJ`xw|g zK7@SrTOX$#_dewjt)p7FiGNSDY7LL-g35GdQc*E}y-Nn$ay+A~w+r{DHUf9f!>i*$?2;`R;Xa(9ar$ zJPH0JVWD+2gW~h;-Egld539U%v@L>79CJ#sj*oeKbsg+&wTJ02vYsV**5lC%QjKqk@M%i-#q|nDYU-?6;-|-YzZq-R*Qvi-hsRON6(d^Tso|>J z$1p!|$Blayi}H@vrD(nCZDcp&HI39V*P~{Aw_lxYLfyDKd;66n)M?xuZ%6CLo$Z>A z|BJArEiAFN_$aOCXJN~lpWChYo>a%A`z4Gn9F4w9>}v2$?l;go*N?%X)rsfsb9N4C z_Qy!bH45Kb(LJ1c+tZEpigoI&R3}{XSi9cR*5g;#Ym7LZhqxm!MTNzvvs1*Fas<^` zt@8u99X(%s+QmBYxwL#Ics@HVN2&RncGK+D`*rvqwe6{;w0sGg6Z0AM`rM7>Rzhh^ z<{bEY_IJt$_!^ry&eOee>_4spE0#sYMpar=ZQI8KLEG zA6D+l&NmS&u_$lnik6h6o$i(OB+XEK^Fuys$=^s(hS43i$&SZ;|4GNAFX=}!62^c3 zcGgCg5$7MX*ThDzq1W$Ups0_W-zdNCrS2oM_nP0=#LvGG%U`8#r#ip!T)>anp973P zQ>#;*1m~~RvgS{r_G^A$1NjG@Hhi7^Cr=r?Mz{VOtMwWUc}+a^8<2hKz%}A}>yXQ@ zJHB<+sOy!HeKlCrO>(KxcBlhanE&`}>3XR97K|@}`w#ry4@dKmOPo8OXq|xdMcXuU zH+hE4Cuxk_-H~6%wUg{mdFJVCaRu7zAa*zYlWteB*8X%CMLGwbrf$NiI~_{)?+@^* z$gg^uk_L81o3+c(zjrcbYDCd0b>rRYc0le&K&C|=hOt7D;=?M|7%?R*kM)5=Ti2u#4WtcKM!86 zRhs)b8LxWz)l0ul@<&JKJQ1wp?WAl$Sp&Y0(u*0Tc}yBJIB|woeCGDbH*K%Jho&9$ zOxfPNLS`Z*gJ{}enZ|_m2tv1GYhg-h3o&L|p_r_Q8 zd=gfGcY-fAP2;j{cuZJd3MhRqUPy zJD=jJd`+w9Iqj!hPb^P@>ngZAQxkSaA;uaf!^-#0wQJxNYn=?UyidIjZnfsgu*+Vt z4tBL>5w;vovP?(qmQO&%2unRYy1#G|z0CJtx_c%5>ygh@s~Ki(wQH`=d0m~oUZ%vI zX-1#^3x@NJKF-}CUB-8nx8z3w~$bG`N~`J^cK=-WMCXJ6=14_?J% z6dG459#Jb_^y}3&d4@RItN1>gcF+#{wf}0AQH~g+#f`Qq?GTW|-P+6C7p$`vw3+RW zEsQUUywj89F;Ta86eEFW$k>0*k@^%-D4}A^QucUdo@mXTrAk}XI+VTZ@sUWmo^~Pi zpKDE|w8-_`?nfHN!6N$QZuyX??!~LiFFkh=qi*}nG0!zZN^>7v^_|D@TrA4gvRfR= zzWZ*+xoD?YA^C#)j5p^Cj;n1x(c&0U#^}T~mGxkS(q1rUcGawp^R>7SIIahq16PJ| zhNvwQjwSh9v)l*ezOa?>J}h^Pr}s(avp@AG_Ls7?`5e-0i#vS9K8~LF_Pg>M5ZtAb zR%`t4O=qw^ZSDJ6wXMcHZN9GET<6+<5$os5C(+~TL_cdQvxnzqz3;JmdzhHw4*TP8 zutMQJC*O!)wnvE{_t;IIe?|{ifc!_p38KN9!0<^x-)A)t>h*DFl+pq2`7FI}jhqNQ z9rd$G)AG^fS<$XkHM?p}bG_9r9Ub}Q*~&zaMcSK`a>qq0bXNp zRT|3>tbH`>T|9;R$9_2$dAJsnA2B*0A=(s)V70bM-dd4az<(>W` z{==-XjMCD^$9;raLEl8_I#}@F)8Dn+n0;?i*e3y8jsk9W5roi%yR0d;!0`U zV=Z5O4n)n5|D)`gTH!k|ivN9c9WU>!y*O7oP8YhDW_gU2`G3^jq*aot+i#5neto=8yCL^1h$_9~?R(DF6Tf literal 0 HcmV?d00001 diff --git a/src/connectedk8s/azext_connectedk8s/_client_factory.py b/src/connectedk8s/azext_connectedk8s/_client_factory.py index 2aca739459b..90ecd5dc01b 100644 --- a/src/connectedk8s/azext_connectedk8s/_client_factory.py +++ b/src/connectedk8s/azext_connectedk8s/_client_factory.py @@ -12,7 +12,7 @@ def cf_connectedk8s(cli_ctx, *_): def cf_connected_cluster(cli_ctx, _): return cf_connectedk8s(cli_ctx).connected_cluster -def _resource_client_factory(cli_ctx, **_): +def _resource_client_factory(cli_ctx): from azure.cli.core.commands.client_factory import get_mgmt_service_client from azure.cli.core.profiles import ResourceType return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) @@ -34,6 +34,7 @@ def _auth_client_factory(cli_ctx, scope=None): from azure.cli.core.profiles import ResourceType from azure.cli.core.commands.client_factory import get_mgmt_service_client subscription_id = None + #print(subscription_id) if scope: matched = re.match('/subscriptions/(?P[^/]*)/', scope) if matched: diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py index 1ea56bd0e7a..ac990ee46df 100644 --- a/src/connectedk8s/azext_connectedk8s/_params.py +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -16,7 +16,7 @@ def load_arguments(self, _): with self.argument_context('connectedk8s') as c: c.argument('tags', tags_type) - c.argument('location', validator=get_default_location_from_resource_group) + #c.argument('location', validator=get_default_location_from_resource_group) c.argument('cluster_name', cluster_name_type, options_list=['--name', '-n']) with self.argument_context('connectedk8s list') as c: diff --git a/src/connectedk8s/azext_connectedk8s/commands.py b/src/connectedk8s/azext_connectedk8s/commands.py index c6f307848a1..3221c2f4917 100644 --- a/src/connectedk8s/azext_connectedk8s/commands.py +++ b/src/connectedk8s/azext_connectedk8s/commands.py @@ -17,9 +17,9 @@ def load_command_table(self, _): with self.command_group('connectedk8s', connectedk8s_sdk, client_factory=cf_connected_cluster) as g: g.custom_command('create', 'create_connectedk8s') - g.command('delete', 'delete') + g.custom_command('delete', 'delete_connectedk8s') g.custom_command('list', 'list_connectedk8s') - g.show_command('show', 'get') + g.custom_show_command('show', 'get_connectedk8s') g.generic_update_command('update', setter_name='update', custom_func_name='update_connectedk8s') diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 23d8bf3ed5b..989a1c3e427 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -5,99 +5,239 @@ from knack.util import CLIError from azure.cli.core.profiles import ResourceType, get_sdk +from azure.cli.core.commands.client_factory import get_subscription_id from azext_connectedk8s._client_factory import _graph_client_factory from azext_connectedk8s._client_factory import _resource_client_factory from azext_connectedk8s._client_factory import _auth_client_factory +from azext_connectedk8s._multi_api_adaptor import MultiAPIAdaptor import datetime from msrest.serialization import TZ_UTC from dateutil.relativedelta import relativedelta import uuid from azure.graphrbac.models import (PasswordCredential, ApplicationCreateParameters, ServicePrincipalCreateParameters) from knack.log import get_logger -from azext_connectedk8s._multi_api_adaptor import MultiAPIAdaptor +import os +import json +from azure.mgmt.subscription import SubscriptionClient +from azure.common.client_factory import get_client_from_cli_profile +from azure.cli.core._profile import Profile +from azure.cli.core.api import get_config_dir +import platform +import yaml, copy, base64, atexit, tempfile +from kubernetes.client import ApiClient, Configuration + logger = get_logger(__name__) -def create_connectedk8s(cmd, client, subscription_name, resource_group_name, cluster_name, onboarding_spn_id=None, onboarding_spn_secret=None, location=None, tags=None): - resource_group_params = {'location': location} +def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboarding_spn_id=None, onboarding_spn_secret=None, location=None, kube_config=None, kube_context=None, tags=None): + #print(kube_config) + subscription_id = get_subscription_id(cmd.cli_ctx) + print(subscription_id) + """if(subscription_id is not None): + try: + sub_client = get_client_from_cli_profile(SubscriptionClient) + except CLIError: + logger.info("Not logged in, running az login") + _run_az_cli_login() + sub_client = get_client_from_cli_profile(SubscriptionClient) + #print(sub_client.subscriptions.list()) + sub_list = [] + for sub in sub_client.subscriptions.list(): + sub_list.append(sub.subscription_id) + print(sub_list) + if(subscription_id not in sub_list): + raise CLIError("Provided subscription name does not exist")""" + + #if(resource_group_name is None): + # raise CLIError("Provide resource group name") + #print(get_subscription_id(cmd.cli_ctx)) + #print(subscription_name) rg_client = _resource_client_factory(cmd.cli_ctx) - rg_client.resource_groups.create_or_update(resource_group_name, resource_group_params) + rg_name_list = [] + for item in rg_client.resource_groups.list(): + rg_name_list.append(item.name) + if(item.name==resource_group_name and location!=item.location): + raise CLIError("The resource group already exists in the location {}".format(item.location)) + print(rg_name_list) + if(resource_group_name not in rg_name_list): + print("Creating resource group") + if(location is None): + raise CLIError("Provide the location to create the resource group") + #profile = Profile(cli_ctx=cmd.cli_ctx) + #print(json.dumps(profile.get_sp_auth_info(subscription_name), indent=2)) + # = profile.get_raw_token(cmd, subscription=subscription_name, resource=resource_group_name) + #accToken = profile.get_access_token(cmd, subscription=subscription_name, resource=resource_group_name) + #print(accToken) + resource_group_params = {'location': location} + rg_client.resource_groups.create_or_update(resource_group_name, resource_group_params) + + print("start") + #print(get_subscription_id(cmd.cli_ctx)) + graph_client = _graph_client_factory(cmd.cli_ctx) + onboarding_tenant_id = graph_client.config.tenant_id + if(onboarding_spn_id is not None and onboarding_spn_secret is None): + raise CLIError("Provide the onboarding spn password") + #client1 = _graph_client_factory(cmd.cli_ctx) + spn_list = list_spn(graph_client) + spn_appid_list=[] + for i in spn_list: + spn_appid_list.append(i.app_id) + if(onboarding_spn_id is not None and onboarding_spn_id not in spn_appid_list): + raise CLIError("Provided service principal does not exist") if(onboarding_spn_id is None): - graph_client = _graph_client_factory(cmd.cli_ctx) - role_client = _auth_client_factory(cmd.cli_ctx).role_assignments - scopes = ['/subscriptions/' + role_client.config.subscription_id] - print(scopes) - years = 1 - import time - app_start_date = datetime.datetime.now(TZ_UTC) - app_end_date = app_start_date + relativedelta(years=years) - app_display_name = ('azure-cli-' + app_start_date.strftime('%Y-%m-%d-%H-%M-%S')) - name = 'http://' + app_display_name - password = str(uuid.uuid4()) - aad_application = create_application(cmd, - display_name=app_display_name, - homepage='https://' + app_display_name, - identifier_uris=[name], - available_to_other_tenants=False, - password=password, - key_value=None, - start_date=app_start_date, - end_date=app_end_date, - credential_description='rbac') - _RETRY_TIMES = 36 - app_id = aad_application.app_id - aad_sp = None - for l in range(0, _RETRY_TIMES): - try: - aad_sp = _create_service_principal(cmd.cli_ctx, app_id, resolve_app=False) - break - except Exception as ex: # pylint: disable=broad-except - if l < _RETRY_TIMES and ( - ' does not reference ' in str(ex) or ' does not exist ' in str(ex)): - time.sleep(5) - logger.warning('Retrying service principal creation: %s/%s', l + 1, _RETRY_TIMES) - else: - logger.warning( - "Creating service principal failed for appid '%s'. Trace followed:\n%s", - name, ex.response.headers if hasattr(ex, - 'response') else ex) # pylint: disable=no-member - raise - role = 'Contributor' - sp_oid = aad_sp.object_id - for scope in scopes: - logger.warning('Creating a role assignment under the scope of "%s"', scope) + file_name_connectedk8s = 'azureArcServicePrincipal.json' + principal_obj = load_acs_service_principal(subscription_id, file_name=file_name_connectedk8s) + #print(principal_obj) + var = None + if principal_obj: + if(principal_obj.get('service_principal') not in spn_appid_list): + erase_acs_service_principal(file_name=file_name_connectedk8s) + var = "temp" + if (principal_obj and var is None): + onboarding_spn_id = principal_obj.get('service_principal') + #print(onboarding_spn_id) + onboarding_spn_secret = principal_obj.get('client_secret') + else: + graph_client = _graph_client_factory(cmd.cli_ctx) + role_client = _auth_client_factory(cmd.cli_ctx).role_assignments + print(role_client.config.subscription_id) + scopes = ['/subscriptions/' + role_client.config.subscription_id] + years = 1 + import time + app_start_date = datetime.datetime.now(TZ_UTC) + app_end_date = app_start_date + relativedelta(years=years) + app_display_name = ('azure-cli-' + app_start_date.strftime('%Y-%m-%d-%H-%M-%S')) + name = 'http://' + app_display_name + password = str(uuid.uuid4()) + aad_application = create_application(cmd, + display_name=app_display_name, + homepage='https://' + app_display_name, + identifier_uris=[name], + available_to_other_tenants=False, + password=password, + key_value=None, + start_date=app_start_date, + end_date=app_end_date, + credential_description='rbac') + _RETRY_TIMES = 36 + app_id = aad_application.app_id + aad_sp = None for l in range(0, _RETRY_TIMES): try: - _create_role_assignment(cmd.cli_ctx, role, sp_oid, None, scope, resolve_assignee=False) - print("created rass") + aad_sp = _create_service_principal(cmd.cli_ctx, app_id, resolve_app=False) break - except Exception as ex: - if l < _RETRY_TIMES and ' does not exist in the directory ' in str(ex): + except Exception as ex: # pylint: disable=broad-except + if l < _RETRY_TIMES and ( + ' does not reference ' in str(ex) or ' does not exist ' in str(ex)): time.sleep(5) - logger.warning(' Retrying role assignment creation: %s/%s', l + 1, - _RETRY_TIMES) - continue - elif _error_caused_by_role_assignment_exists(ex): - logger.warning(' Role assignment already exits.\n') - break + logger.warning('Retrying service principal creation: %s/%s', l + 1, _RETRY_TIMES) else: - # dump out history for diagnoses - logger.warning(' Role assignment creation failed.\n') - if getattr(ex, 'response', None) is not None: - logger.warning(' role assignment response headers: %s\n', - ex.response.headers) # pylint: disable=no-member - raise - print(app_id) - print(password) - print(name) - print(app_display_name) - print(graph_client.config.tenant_id) - return client.create(subscription_name, resource_group_name, cluster_name, onboarding_spn_id, onboarding_spn_secret, "connected_cluster") + logger.warning( + "Creating service principal failed for appid '%s'. Trace followed:\n%s", + name, ex.response.headers if hasattr(ex, + 'response') else ex) # pylint: disable=no-member + raise + #correct + store_acs_service_principal(subscription_id, password, app_id, file_name=file_name_connectedk8s) + role = 'Kubernetes Cluster - Azure Arc Onborading Role' + sp_oid = aad_sp.object_id + for scope in scopes: + logger.warning('Creating a role assignment under the scope of "%s"', scope) + for l in range(0, _RETRY_TIMES): + try: + _create_role_assignment(cmd.cli_ctx, role, sp_oid, None, scope, resolve_assignee=False) + break + except Exception as ex: + if l < _RETRY_TIMES and ' does not exist in the directory ' in str(ex): + time.sleep(5) + logger.warning(' Retrying role assignment creation: %s/%s', l + 1, + _RETRY_TIMES) + continue + elif _error_caused_by_role_assignment_exists(ex): + logger.warning(' Role assignment already exits.\n') + break + else: + # dump out history for diagnoses + logger.warning(' Role assignment creation failed.\n') + if getattr(ex, 'response', None) is not None: + logger.warning(' role assignment response headers: %s\n', + ex.response.headers) # pylint: disable=no-member + raise + onboarding_spn_id = app_id + onboarding_spn_secret = password + print("spn_id: "+app_id) + print("spn_secret: "+password) + print("name: "+name) + print("app_display_name: "+app_display_name) + print("tenant_id: "+graph_client.config.tenant_id) + print() + #load_kube_config(config_file=kube_config, context=kube_context) + return client.create(resource_group_name, cluster_name, onboarding_tenant_id, onboarding_spn_id, onboarding_spn_secret, location, kube_config, kube_context) +def erase_acs_service_principal(file_name='acsServicePrincipal.json'): + config_path = os.path.join(get_config_dir(), file_name) + open(config_path, 'w').close() -def list_connectedk8s(cmd, client, resource_group_name=None): - raise CLIError('TODO: Implement `connectedk8s list`') +def load_acs_service_principal(subscription_id, file_name='acsServicePrincipal.json'): + config_path = os.path.join(get_config_dir(), file_name) + #print(config_path + "load") + config = load_service_principals(config_path) + #print(config) + if not config: + return None + return config.get(subscription_id) +def load_service_principals(config_path): + if not os.path.exists(config_path): + return None + fd = os.open(config_path, os.O_RDONLY) + try: + with os.fdopen(fd) as f: + return json.loads(f.read()) + except: # pylint: disable=bare-except + return None + +def store_acs_service_principal(subscription_id, client_secret, service_principal, + file_name='acsServicePrincipal.json'): + obj = {} + if client_secret: + obj['client_secret'] = client_secret + if service_principal: + obj['service_principal'] = service_principal + + config_path = os.path.join(get_config_dir(), file_name) + full_config = load_service_principals(config_path=config_path) + if not full_config: + full_config = {} + full_config[subscription_id] = obj + + with os.fdopen(os.open(config_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0o600), + 'w+') as spFile: + json.dump(full_config, spFile) + +def list_spn(client): + show_mine = True + if show_mine: + return list_owned_objects(client.signed_in_user, 'servicePrincipal') + +def list_owned_objects(client, object_type=None): + result = client.list_owned_objects() + #print(result) + if object_type: + result = [r for r in result if r.object_type and r.object_type.lower() == object_type.lower()] + #print(result) + return result + +def get_connectedk8s(cmd, client, resource_group_name, cluster_name): + return client.get(resource_group_name, cluster_name) + +def list_connectedk8s(cmd, client, resource_group_name=None): + if(resource_group_name is None): + return client.list_by_subscription() + return client.list_by_resource_group(resource_group_name) + +def delete_connectedk8s(cmd, client, resource_group_name, cluster_name, kube_config=None, kube_context=None): + return client.delete(resource_group_name, cluster_name, kube_config, kube_context) def update_connectedk8s(cmd, instance, tags=None): with cmd.update_context(instance) as c: @@ -109,7 +249,6 @@ def create_application(cmd, display_name, homepage=None, identifier_uris=None, key_value=None, key_type=None, key_usage=None, start_date=None, end_date=None, oauth2_allow_implicit_flow=None, required_resource_accesses=None, native_app=None, credential_description=None, app_roles=None): - print(identifier_uris) graph_client = _graph_client_factory(cmd.cli_ctx) password_creds = [PasswordCredential(start_date=start_date, end_date=end_date, key_id=str(uuid.uuid4()), value=password, custom_key_identifier=None)] app_create_param = ApplicationCreateParameters(available_to_other_tenants=False, @@ -156,12 +295,14 @@ def _error_caused_by_role_assignment_exists(ex): def _create_role_assignment(cli_ctx, role, assignee, resource_group_name=None, scope=None, resolve_assignee=True, assignee_principal_type=None): + print("now") factory = _auth_client_factory(cli_ctx, scope) assignments_client = factory.role_assignments definitions_client = factory.role_definitions - role = 'b24988ac-6180-42a0-ab88-20f7382dd24c' + role = '34e09817-6cbe-4d01-b1a2-e0eac5743d41' role_id = '/subscriptions/{}/providers/Microsoft.Authorization/roleDefinitions/{}'.format(definitions_client.config.subscription_id, role) object_id = assignee worker = MultiAPIAdaptor(cli_ctx) return worker.create_role_assignment(assignments_client, uuid.uuid4(), role_id, object_id, scope, - assignee_principal_type) \ No newline at end of file + assignee_principal_type) + diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py index ef5538373f9..ed84a7d4e36 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py @@ -13,7 +13,12 @@ from msrest.pipeline import ClientRawResponse from .. import models - +import os +import subprocess +from kubernetes import client, config +from subprocess import Popen, PIPE +import os.path, json +from knack.util import CLIError class ConnectedClusterOperations(object): """ConnectedClusterOperations operations. @@ -37,7 +42,49 @@ def __init__(self, client, config, serializer, deserializer): self.config = config def create( - self, subscription_name, resource_group_name, cluster_name, onboarding_spn_id, onboarding_spn_secret, connected_cluster, custom_headers=None, raw=False, **operation_config): + self, resource_group_name, cluster_name, onboarding_tenant_id, onboarding_spn_id, onboarding_spn_secret, location, kube_config, kube_context, custom_headers=None, raw=False, **operation_config): + subscription_id = self.config.subscription_id + if ((kube_config is not None and os.path.isfile(kube_config) is False) or kube_config is None): + print("The kube config file does not exist or the Path is invalid. Using default kube config ...") + kube_config = os.getenv('KUBECONFIG') + if(kube_config is None): + kube_config = "C:\\Users\\akkeshar\\.kube\\config" + contexts = config.list_kube_config_contexts(config_file=kube_config) + contexts_list = [] + for ctxs in contexts[0]: + contexts_list.append(ctxs.get('name')) + if(kube_context not in contexts_list): + print("The kube context does on exist. Using default context ...") + kube_context = contexts[1].get('name') + + cmd1 = "helm repo add haikupreview https://haikupreview.azurecr.io/helm/v1/repo" + response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) + output1, error1 = response1.communicate() + if response1.returncode != 0: + raise CLIError(error1) + else: + print(output1) + + cmd2 = "helm fetch haikupreview/haiku-agents" + response2 = subprocess.Popen(cmd2, stdout=PIPE, stderr=PIPE) + output2, error2 = response2.communicate() + if response2.returncode != 0: + raise CLIError(error2) + else: + print(output2) + + env3 = os.environ.copy() + env3["KUBECONFIG"] = kube_config + env3["HELM_KUBECONTEXT"] = kube_context + cmd3 = "helm install haiku haiku-agents-0.1.7.tgz --set global.subscriptionId={} --set global.resourceGroupName={} --set global.resourceName={} --set global.location={} --set global.tenantId={} --set global.clientId={} --set global.clientSecret={} --output json".format(subscription_id, resource_group_name, cluster_name, location, onboarding_tenant_id, onboarding_spn_id, onboarding_spn_secret) + response3 = subprocess.Popen(cmd3, env=env3, stdout=PIPE, stderr=PIPE) + output3, error3 = response3.communicate() + if response3.returncode != 0: + raise CLIError(error3) + else: + #get_res = self.get(cluster_name, resource_group_name) + print("Haiku agents deployed successfully") + return """Registers a new K8s cluster. API to register a new K8s cluster and thereby create a tracked resource @@ -177,6 +224,8 @@ def update( if response.status_code == 200: deserialized = self._deserialize('ConnectedCluster', response) + + if raw: client_raw_response = ClientRawResponse(deserialized, response) return client_raw_response @@ -207,6 +256,7 @@ def get( :raises: :class:`ErrorResponseException` """ + #print("test2") # Construct URL url = self.get.metadata['url'] path_format_arguments = { @@ -215,7 +265,7 @@ def get( 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') } url = self._client.format_url(url, **path_format_arguments) - + print(url) # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -250,7 +300,12 @@ def get( get.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} def delete( - self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + self, resource_group_name, cluster_name, kube_config, kube_context, custom_headers=None, raw=False, **operation_config): + #print("test") + #return + #cmd_delete_agent = "helm delete haiku" + #res_delete_agent = os.system(cmd_delete_agent) + #print(res_delete_agent) """Deletes a specified cluster. API to delete an existing K8s cluster being tracked. @@ -271,6 +326,41 @@ def delete( :raises: :class:`ErrorResponseException` """ + url_config = self.delete.metadata['url'] + url = url_config + "/providers/Microsoft.KubernetesConfiguration/sourceControlConfigurations" + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url_config = self._client.format_url(url, **path_format_arguments) + print(url_config) + + query_parameters_config = {} + query_parameters_config['api-version'] = "2019-11-01-preview" + print(query_parameters_config) + + + request_config = self._client.get(url_config, query_parameters_config) + response_config = self._client.send(request_config, stream=False, **operation_config) + print(response_config.status_code) + #print("test") + if response_config.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response_config) + if response_config.status_code == 200: + #print(response_config['name']) + deserialized = self._deserialize('ConnectedCluster', response_config) + + for conf in deserialized.additional_properties['value']: + temp_req = self._client.delete(url_config+"/"+conf['name'], query_parameters_config) + temp_res = self._client.send(temp_req, stream=False, **operation_config) + if response_config.status_code not in [200, 202]: + raise models.ErrorResponseException(self._deserialize, temp_res) + + print("All configs deleted") + #print(deserialized[1].name) + #print("config_deleted") + # Construct URL url = self.delete.metadata['url'] path_format_arguments = { @@ -279,10 +369,14 @@ def delete( 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') } url = self._client.format_url(url, **path_format_arguments) - + print(url) + #url = url + "/providers/Microsoft.KubernetesConfiguration/sourceControlConfigurations/cluster2" + #print(url) + # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + print(query_parameters) # Construct headers header_parameters = {} @@ -300,6 +394,32 @@ def delete( if response.status_code not in [200, 204]: raise models.ErrorResponseException(self._deserialize, response) + + if ((kube_config is not None and os.path.isfile(kube_config) is False) or kube_config is None): + print("The kube config file does not exist or the Path is invalid. Using default kube config ...") + kube_config = os.getenv('KUBECONFIG') + if(kube_config is None): + kube_config = "C:\\Users\\akkeshar\\.kube\\config" + contexts = config.list_kube_config_contexts(config_file=kube_config) + contexts_list = [] + for ctxs in contexts[0]: + contexts_list.append(ctxs.get('name')) + if(kube_context not in contexts_list): + print("The kube context does on exist. Using default context ...") + kube_context = contexts[1].get('name') + + env = os.environ.copy() + env["KUBECONFIG"] = kube_config + env["HELM_KUBECONTEXT"] = kube_context + cmd = "helm delete haiku" + response_del = subprocess.Popen(cmd, env=env, stdout=PIPE, stderr=PIPE) + output, error = response_del.communicate() + if response_del.returncode != 0: + raise CLIError(error) + else: + #get_res = self.get(cluster_name, resource_group_name) + print(output) + if raw: client_raw_response = ClientRawResponse(None, response) return client_raw_response @@ -405,7 +525,7 @@ def internal_paging(next_link=None, raw=False): 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str') } url = self._client.format_url(url, **path_format_arguments) - + print(url) # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -426,8 +546,9 @@ def internal_paging(next_link=None, raw=False): # Construct and send request request = self._client.get(url, query_parameters, header_parameters) + print("1") response = self._client.send(request, stream=False, **operation_config) - + print(response) if response.status_code not in [200]: raise models.ErrorResponseException(self._deserialize, response) @@ -471,7 +592,7 @@ def internal_paging(next_link=None, raw=False): 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str') } url = self._client.format_url(url, **path_format_arguments) - + print(url) # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -493,9 +614,10 @@ def internal_paging(next_link=None, raw=False): # Construct and send request request = self._client.get(url, query_parameters, header_parameters) response = self._client.send(request, stream=False, **operation_config) + print(response) if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) + raise CLIError(models.ErrorResponseException(self._deserialize, response)) return response From fda117da15d092ec7adaf49611c40750cc51b19e Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Wed, 11 Mar 2020 23:13:50 +0530 Subject: [PATCH 03/58] saved location data CLI changes after 0.1.2 --- .github/CODEOWNERS | 1 + src/connect_2020/HISTORY.rst | 8 + src/connect_2020/README.rst | 5 + .../azext_connect_2020/__init__.py | 32 + .../azext_connect_2020/_client_factory.py | 10 + src/connect_2020/azext_connect_2020/_help.py | 38 ++ .../azext_connect_2020/_params.py | 23 + .../azext_connect_2020/_validators.py | 20 + .../azext_connect_2020/azext_metadata.json | 5 + .../azext_connect_2020/commands.py | 28 + src/connect_2020/azext_connect_2020/custom.py | 20 + .../azext_connect_2020/tests/__init__.py | 5 + .../tests/latest/__init__.py | 5 + .../latest/test_connect_2020_scenario.py | 40 ++ .../vendored_sdks/__init__.py | 14 + .../vendored_sdks/k8_connect_rp.py | 84 +++ .../vendored_sdks/models/__init__.py | 55 ++ .../vendored_sdks/models/connected_cluster.py | 95 +++ .../models/connected_cluster_aad_profile.py | 42 ++ .../connected_cluster_aad_profile_py3.py | 42 ++ .../models/connected_cluster_identity.py | 48 ++ .../models/connected_cluster_identity_py3.py | 48 ++ .../models/connected_cluster_paged.py | 23 + .../models/connected_cluster_patch.py | 24 + .../models/connected_cluster_patch_py3.py | 24 + .../models/connected_cluster_proxy_profile.py | 25 + .../connected_cluster_proxy_profile_py3.py | 25 + .../models/connected_cluster_py3.py | 95 +++ .../vendored_sdks/models/credential_result.py | 36 + .../models/credential_result_py3.py | 36 + .../vendored_sdks/models/error_details.py | 36 + .../vendored_sdks/models/error_details_py3.py | 36 + .../vendored_sdks/models/error_response.py | 37 ++ .../models/error_response_py3.py | 37 ++ .../models/k8_connect_rp_enums.py | 24 + .../vendored_sdks/models/location_data.py | 45 ++ .../vendored_sdks/models/location_data_py3.py | 45 ++ .../vendored_sdks/models/operation.py | 36 + .../vendored_sdks/models/operation_display.py | 37 ++ .../models/operation_display_py3.py | 37 ++ .../vendored_sdks/models/operation_paged.py | 23 + .../vendored_sdks/models/operation_py3.py | 36 + .../vendored_sdks/operations/__init__.py | 14 + .../connected_cluster_operations.py | 618 ++++++++++++++++++ .../vendored_sdks/operations/operations.py | 90 +++ .../vendored_sdks/version.py | 9 + src/connect_2020/setup.cfg | 2 + src/connect_2020/setup.py | 62 ++ .../azext_connectedk8s/__init__.py | 5 +- .../azext_connectedk8s/_client_factory.py | 31 +- .../azext_connectedk8s/_format.py | 29 + src/connectedk8s/azext_connectedk8s/_help.py | 55 +- .../azext_connectedk8s/_multi_api_adaptor.py | 2 +- .../azext_connectedk8s/_params.py | 15 +- .../azext_connectedk8s/_validators.py | 1 - .../azext_connectedk8s/commands.py | 20 +- src/connectedk8s/azext_connectedk8s/custom.py | 533 +++++++++++---- .../azext_connectedk8s/tests/__init__.py | 2 +- .../tests/latest/__init__.py | 2 +- .../latest/recordings/test_connectedk8s.yaml | 48 ++ .../latest/test_connectedk8s_scenario.py | 20 +- .../vendored_sdks/k8_connect_rp.py | 3 +- .../connected_cluster_operations.py | 313 ++++++--- .../vendored_sdks/version.py | 7 +- src/connectedk8s/setup.py | 10 +- 65 files changed, 2997 insertions(+), 279 deletions(-) create mode 100644 src/connect_2020/HISTORY.rst create mode 100644 src/connect_2020/README.rst create mode 100644 src/connect_2020/azext_connect_2020/__init__.py create mode 100644 src/connect_2020/azext_connect_2020/_client_factory.py create mode 100644 src/connect_2020/azext_connect_2020/_help.py create mode 100644 src/connect_2020/azext_connect_2020/_params.py create mode 100644 src/connect_2020/azext_connect_2020/_validators.py create mode 100644 src/connect_2020/azext_connect_2020/azext_metadata.json create mode 100644 src/connect_2020/azext_connect_2020/commands.py create mode 100644 src/connect_2020/azext_connect_2020/custom.py create mode 100644 src/connect_2020/azext_connect_2020/tests/__init__.py create mode 100644 src/connect_2020/azext_connect_2020/tests/latest/__init__.py create mode 100644 src/connect_2020/azext_connect_2020/tests/latest/test_connect_2020_scenario.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/__init__.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/k8_connect_rp.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/__init__.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_paged.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/k8_connect_rp_enums.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/operation.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_paged.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_py3.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/operations/__init__.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/operations/connected_cluster_operations.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/operations/operations.py create mode 100644 src/connect_2020/azext_connect_2020/vendored_sdks/version.py create mode 100644 src/connect_2020/setup.cfg create mode 100644 src/connect_2020/setup.py create mode 100644 src/connectedk8s/azext_connectedk8s/_format.py create mode 100644 src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2bd571915e6..b3c1d8e4e27 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -78,3 +78,4 @@ /src/azext_haikutest1/ @akashkeshari /src/azext_mesh_copy/ @akashkeshari /src/azext_connectedk8s/ @akashkeshari +/src/azext_connect_2020/ @akashkeshari diff --git a/src/connect_2020/HISTORY.rst b/src/connect_2020/HISTORY.rst new file mode 100644 index 00000000000..8c34bccfff8 --- /dev/null +++ b/src/connect_2020/HISTORY.rst @@ -0,0 +1,8 @@ +.. :changelog: + +Release History +=============== + +0.1.0 +++++++ +* Initial release. \ No newline at end of file diff --git a/src/connect_2020/README.rst b/src/connect_2020/README.rst new file mode 100644 index 00000000000..119ea984dda --- /dev/null +++ b/src/connect_2020/README.rst @@ -0,0 +1,5 @@ +Microsoft Azure CLI 'connect_2020' Extension +========================================== + +This package is for the 'connect_2020' extension. +i.e. 'az connect_2020' \ No newline at end of file diff --git a/src/connect_2020/azext_connect_2020/__init__.py b/src/connect_2020/azext_connect_2020/__init__.py new file mode 100644 index 00000000000..391b55cc42f --- /dev/null +++ b/src/connect_2020/azext_connect_2020/__init__.py @@ -0,0 +1,32 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from azure.cli.core import AzCommandsLoader + +from azext_connect_2020._help import helps # pylint: disable=unused-import + + +class Connect_2020CommandsLoader(AzCommandsLoader): + + def __init__(self, cli_ctx=None): + from azure.cli.core.commands import CliCommandType + from azext_connect_2020._client_factory import cf_connect_2020 + connect_2020_custom = CliCommandType( + operations_tmpl='azext_connect_2020.custom#{}', + client_factory=cf_connect_2020) + super(Connect_2020CommandsLoader, self).__init__(cli_ctx=cli_ctx, + custom_command_type=connect_2020_custom) + + def load_command_table(self, args): + from azext_connect_2020.commands import load_command_table + load_command_table(self, args) + return self.command_table + + def load_arguments(self, command): + from azext_connect_2020._params import load_arguments + load_arguments(self, command) + + +COMMAND_LOADER_CLS = Connect_2020CommandsLoader diff --git a/src/connect_2020/azext_connect_2020/_client_factory.py b/src/connect_2020/azext_connect_2020/_client_factory.py new file mode 100644 index 00000000000..dbfb89f5cb2 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/_client_factory.py @@ -0,0 +1,10 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +def cf_connect_2020(cli_ctx, *_): + + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azext_connect_2020.vendored_sdks import K8ConnectRP + return get_mgmt_service_client(cli_ctx, K8ConnectRP) diff --git a/src/connect_2020/azext_connect_2020/_help.py b/src/connect_2020/azext_connect_2020/_help.py new file mode 100644 index 00000000000..9c972f2b998 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/_help.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.help_files import helps # pylint: disable=unused-import + + +helps['connect_2020'] = """ + type: group + short-summary: Commands to manage Connect_2020s. +""" + +helps['connect_2020 create'] = """ + type: command + short-summary: Create a Connect_2020. +""" + +helps['connect_2020 list'] = """ + type: command + short-summary: List Connect_2020s. +""" + +helps['connect_2020 delete'] = """ + type: command + short-summary: Delete a Connect_2020. +""" + +helps['connect_2020 show'] = """ + type: command + short-summary: Show details of a Connect_2020. +""" + +helps['connect_2020 update'] = """ + type: command + short-summary: Update a Connect_2020. +""" diff --git a/src/connect_2020/azext_connect_2020/_params.py b/src/connect_2020/azext_connect_2020/_params.py new file mode 100644 index 00000000000..adce0be7c70 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/_params.py @@ -0,0 +1,23 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long + +from knack.arguments import CLIArgumentType + + +def load_arguments(self, _): + + from azure.cli.core.commands.parameters import tags_type + from azure.cli.core.commands.validators import get_default_location_from_resource_group + + cluster_name_type = CLIArgumentType(options_list='--cluster-name-name', help='Name of the Connect_2020.', id_part='name') + + with self.argument_context('connect_2020') as c: + c.argument('tags', tags_type) + c.argument('location', validator=get_default_location_from_resource_group) + c.argument('cluster_name', cluster_name_type, options_list=['--name', '-n']) + + with self.argument_context('connect_2020 list') as c: + c.argument('cluster_name', cluster_name_type, id_part=None) diff --git a/src/connect_2020/azext_connect_2020/_validators.py b/src/connect_2020/azext_connect_2020/_validators.py new file mode 100644 index 00000000000..821630f5f34 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/_validators.py @@ -0,0 +1,20 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +def example_name_or_id_validator(cmd, namespace): + # Example of a storage account name or ID validator. + # See: https://github.com/Azure/azure-cli/blob/dev/doc/authoring_command_modules/authoring_commands.md#supporting-name-or-id-parameters + from azure.cli.core.commands.client_factory import get_subscription_id + from msrestazure.tools import is_valid_resource_id, resource_id + if namespace.storage_account: + if not is_valid_resource_id(namespace.RESOURCE): + namespace.storage_account = resource_id( + subscription=get_subscription_id(cmd.cli_ctx), + resource_group=namespace.resource_group_name, + namespace='Microsoft.Storage', + type='storageAccounts', + name=namespace.storage_account + ) diff --git a/src/connect_2020/azext_connect_2020/azext_metadata.json b/src/connect_2020/azext_connect_2020/azext_metadata.json new file mode 100644 index 00000000000..4060dd78264 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/azext_metadata.json @@ -0,0 +1,5 @@ +{ + "azext.isPreview": true, + "azext.minCliCoreVersion": "2.0.67", + "azext.maxCliCoreVersion": "2.1.0" +} \ No newline at end of file diff --git a/src/connect_2020/azext_connect_2020/commands.py b/src/connect_2020/azext_connect_2020/commands.py new file mode 100644 index 00000000000..5d7cd67eafe --- /dev/null +++ b/src/connect_2020/azext_connect_2020/commands.py @@ -0,0 +1,28 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +# pylint: disable=line-too-long +from azure.cli.core.commands import CliCommandType +from azext_connect_2020._client_factory import cf_connect_2020 + + +def load_command_table(self, _): + + connect_2020_sdk = CliCommandType( + operations_tmpl='azext_connect_2020.vendored_sdks.operations#ConnectedClusterOperations.{}', + client_factory=cf_connect_2020) + + + with self.command_group('connect_2020', connect_2020_sdk, client_factory=cf_connect_2020) as g: + g.custom_command('create', 'create_connect_2020') + g.command('delete', 'delete') + g.custom_command('list', 'list_connect_2020') + g.show_command('show', 'get') + g.generic_update_command('update', setter_name='update', custom_func_name='update_connect_2020') + + + with self.command_group('connect_2020', is_preview=True): + pass + diff --git a/src/connect_2020/azext_connect_2020/custom.py b/src/connect_2020/azext_connect_2020/custom.py new file mode 100644 index 00000000000..fd1d54c70f4 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/custom.py @@ -0,0 +1,20 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.util import CLIError + + +def create_connect_2020(cmd, client, resource_group_name, cluster_name, location=None, tags=None): + raise CLIError('TODO: Implement `connect_2020 create`') + + +def list_connect_2020(cmd, client, resource_group_name=None): + raise CLIError('TODO: Implement `connect_2020 list`') + + +def update_connect_2020(cmd, instance, tags=None): + with cmd.update_context(instance) as c: + c.set_param('tags', tags) + return instance \ No newline at end of file diff --git a/src/connect_2020/azext_connect_2020/tests/__init__.py b/src/connect_2020/azext_connect_2020/tests/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/tests/__init__.py @@ -0,0 +1,5 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/connect_2020/azext_connect_2020/tests/latest/__init__.py b/src/connect_2020/azext_connect_2020/tests/latest/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/tests/latest/__init__.py @@ -0,0 +1,5 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/connect_2020/azext_connect_2020/tests/latest/test_connect_2020_scenario.py b/src/connect_2020/azext_connect_2020/tests/latest/test_connect_2020_scenario.py new file mode 100644 index 00000000000..d97c4d9ef4f --- /dev/null +++ b/src/connect_2020/azext_connect_2020/tests/latest/test_connect_2020_scenario.py @@ -0,0 +1,40 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure_devtools.scenario_tests import AllowLargeResponse +from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer) + + +TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..')) + + +class Connect_2020ScenarioTest(ScenarioTest): + + @ResourceGroupPreparer(name_prefix='cli_test_connect_2020') + def test_connect_2020(self, resource_group): + + self.kwargs.update({ + 'name': 'test1' + }) + + self.cmd('connect_2020 create -g {rg} -n {name} --tags foo=doo', checks=[ + self.check('tags.foo', 'doo'), + self.check('name', '{name}') + ]) + self.cmd('connect_2020 update -g {rg} -n {name} --tags foo=boo', checks=[ + self.check('tags.foo', 'boo') + ]) + count = len(self.cmd('connect_2020 list').get_output_in_json()) + self.cmd('connect_2020 show - {rg} -n {name}', checks=[ + self.check('name', '{name}'), + self.check('resourceGroup', '{rg}'), + self.check('tags.foo', 'boo') + ]) + self.cmd('connect_2020 delete -g {rg} -n {name}') + final_count = len(self.cmd('connect_2020 list').get_output_in_json()) + self.assertTrue(final_count, count - 1) \ No newline at end of file diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/__init__.py b/src/connect_2020/azext_connect_2020/vendored_sdks/__init__.py new file mode 100644 index 00000000000..f803c0b9a77 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/__init__.py @@ -0,0 +1,14 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from .k8_connect_rp import K8ConnectRP +from .version import VERSION + +__all__ = ['K8ConnectRP'] + +__version__ = VERSION + diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/k8_connect_rp.py b/src/connect_2020/azext_connect_2020/vendored_sdks/k8_connect_rp.py new file mode 100644 index 00000000000..c7ed25f8572 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/k8_connect_rp.py @@ -0,0 +1,84 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.service_client import SDKClient +from msrest import Serializer, Deserializer +from msrestazure import AzureConfiguration +from .version import VERSION +from .operations.connected_cluster_operations import ConnectedClusterOperations +from .operations.operations import Operations +from . import models + + +class K8ConnectRPConfiguration(AzureConfiguration): + """Configuration for K8ConnectRP + Note that all parameters used to create this instance are saved as instance + attributes. + + :param credentials: Credentials needed for the client to connect to Azure. + :type credentials: :mod:`A msrestazure Credentials + object` + :param subscription_id: The ID of the subscription to which the kubernetes + cluster is registered. + :type subscription_id: str + :param str base_url: Service URL + """ + + def __init__( + self, credentials, subscription_id, base_url=None): + + if credentials is None: + raise ValueError("Parameter 'credentials' must not be None.") + if subscription_id is None: + raise ValueError("Parameter 'subscription_id' must not be None.") + if not base_url: + base_url = 'https://management.azure.com' + + super(K8ConnectRPConfiguration, self).__init__(base_url) + + self.add_user_agent('k8connectrp/{}'.format(VERSION)) + self.add_user_agent('Azure-SDK-For-Python') + + self.credentials = credentials + self.subscription_id = subscription_id + + +class K8ConnectRP(SDKClient): + """Azure Connected Cluster Resource Provider API for adopting any Kubernetes Cluster + + :ivar config: Configuration for client. + :vartype config: K8ConnectRPConfiguration + + :ivar connected_cluster: ConnectedCluster operations + :vartype connected_cluster: hybridkubernetes.operations.ConnectedClusterOperations + :ivar operations: Operations operations + :vartype operations: hybridkubernetes.operations.Operations + + :param credentials: Credentials needed for the client to connect to Azure. + :type credentials: :mod:`A msrestazure Credentials + object` + :param subscription_id: The ID of the subscription to which the kubernetes + cluster is registered. + :type subscription_id: str + :param str base_url: Service URL + """ + + def __init__( + self, credentials, subscription_id, base_url=None): + + self.config = K8ConnectRPConfiguration(credentials, subscription_id, base_url) + super(K8ConnectRP, self).__init__(self.config.credentials, self.config) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self.api_version = '2020-01-01-preview' + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + + self.connected_cluster = ConnectedClusterOperations( + self._client, self.config, self._serialize, self._deserialize) + self.operations = Operations( + self._client, self.config, self._serialize, self._deserialize) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/__init__.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/__init__.py new file mode 100644 index 00000000000..dd0b0c1fabe --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/__init__.py @@ -0,0 +1,55 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +try: + from .connected_cluster_proxy_profile_py3 import ConnectedClusterProxyProfile + from .operation_display_py3 import OperationDisplay + from .operation_py3 import Operation + from .connected_cluster_identity_py3 import ConnectedClusterIdentity + from .connected_cluster_aad_profile_py3 import ConnectedClusterAADProfile + from .location_data_py3 import LocationData + from .connected_cluster_py3 import ConnectedCluster + from .credential_result_py3 import CredentialResult + from .connected_cluster_patch_py3 import ConnectedClusterPatch + from .error_details_py3 import ErrorDetails + from .error_response_py3 import ErrorResponse, ErrorResponseException +except (SyntaxError, ImportError): + from .connected_cluster_proxy_profile import ConnectedClusterProxyProfile + from .operation_display import OperationDisplay + from .operation import Operation + from .connected_cluster_identity import ConnectedClusterIdentity + from .connected_cluster_aad_profile import ConnectedClusterAADProfile + from .location_data import LocationData + from .connected_cluster import ConnectedCluster + from .credential_result import CredentialResult + from .connected_cluster_patch import ConnectedClusterPatch + from .error_details import ErrorDetails + from .error_response import ErrorResponse, ErrorResponseException +from .connected_cluster_paged import ConnectedClusterPaged +from .operation_paged import OperationPaged +from .k8_connect_rp_enums import ( + ProvisioningState, + ResourceIdentityType, +) + +__all__ = [ + 'ConnectedClusterProxyProfile', + 'OperationDisplay', + 'Operation', + 'ConnectedClusterIdentity', + 'ConnectedClusterAADProfile', + 'LocationData', + 'ConnectedCluster', + 'CredentialResult', + 'ConnectedClusterPatch', + 'ErrorDetails', + 'ErrorResponse', 'ErrorResponseException', + 'ConnectedClusterPaged', + 'OperationPaged', + 'ProvisioningState', + 'ResourceIdentityType', +] diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster.py new file mode 100644 index 00000000000..9193e3da7f9 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster.py @@ -0,0 +1,95 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedCluster(Model): + """ConnectedCluster. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Type of the resource requested + :vartype type: str + :param location: Required. Location of the cluster + :type location: str + :param tags: Connected Cluster Resource Tags. + :type tags: dict[str, str] + :param identity: Required. The identity of the connected cluster. + :type identity: ~hybridkubernetes.models.ConnectedClusterIdentity + :param agent_public_key_certificate: Required. Base64 encoded public + certificate used by the agent to do the initial handshake to the backend + services in Azure. + :type agent_public_key_certificate: str + :param aad_profile: Required. + :type aad_profile: ~hybridkubernetes.models.ConnectedClusterAADProfile + :param kubernetes_version: The Kubernetes version of the connected cluster + resource + :type kubernetes_version: str + :param total_node_count: Number of nodes present in the connected cluster + resource + :type total_node_count: int + :param agent_version: Version of the agent running on the connected + cluster resource + :type agent_version: str + :param location_data: Metadata pertaining to the geographic location of + the resource. + :type location_data: ~hybridkubernetes.models.LocationData + :param provisioning_state: Possible values include: 'Succeeded', 'Failed', + 'Canceled', 'Provisioning', 'Updating', 'Deleting', 'Accepted' + :type provisioning_state: str or + ~hybridkubernetes.models.ProvisioningState + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + 'location': {'required': True}, + 'identity': {'required': True}, + 'agent_public_key_certificate': {'required': True}, + 'aad_profile': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'ConnectedClusterIdentity'}, + 'agent_public_key_certificate': {'key': 'properties.agentPublicKeyCertificate', 'type': 'str'}, + 'aad_profile': {'key': 'properties.aadProfile', 'type': 'ConnectedClusterAADProfile'}, + 'kubernetes_version': {'key': 'properties.kubernetesVersion', 'type': 'str'}, + 'total_node_count': {'key': 'properties.totalNodeCount', 'type': 'int'}, + 'agent_version': {'key': 'properties.agentVersion', 'type': 'str'}, + 'location_data': {'key': 'properties.locationData', 'type': 'LocationData'}, + 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectedCluster, self).__init__(**kwargs) + self.id = None + self.name = None + self.type = None + self.location = kwargs.get('location', None) + self.tags = kwargs.get('tags', None) + self.identity = kwargs.get('identity', None) + self.agent_public_key_certificate = kwargs.get('agent_public_key_certificate', None) + self.aad_profile = kwargs.get('aad_profile', None) + self.kubernetes_version = kwargs.get('kubernetes_version', None) + self.total_node_count = kwargs.get('total_node_count', None) + self.agent_version = kwargs.get('agent_version', None) + self.location_data = kwargs.get('location_data', None) + self.provisioning_state = kwargs.get('provisioning_state', None) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile.py new file mode 100644 index 00000000000..5b4622d6fd2 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile.py @@ -0,0 +1,42 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAADProfile(Model): + """ConnectedClusterAADProfile. + + All required parameters must be populated in order to send to Azure. + + :param tenant_id: Required. The aad tenant id which is configured on + target K8s cluster + :type tenant_id: str + :param client_app_id: Required. The client app id configured on target K8 + cluster + :type client_app_id: str + :param server_app_id: Required. The server app id to access AD server + :type server_app_id: str + """ + + _validation = { + 'tenant_id': {'required': True}, + 'client_app_id': {'required': True}, + 'server_app_id': {'required': True}, + } + + _attribute_map = { + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'client_app_id': {'key': 'clientAppId', 'type': 'str'}, + 'server_app_id': {'key': 'serverAppId', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterAADProfile, self).__init__(**kwargs) + self.tenant_id = kwargs.get('tenant_id', None) + self.client_app_id = kwargs.get('client_app_id', None) + self.server_app_id = kwargs.get('server_app_id', None) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile_py3.py new file mode 100644 index 00000000000..e64fee8decd --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_aad_profile_py3.py @@ -0,0 +1,42 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterAADProfile(Model): + """ConnectedClusterAADProfile. + + All required parameters must be populated in order to send to Azure. + + :param tenant_id: Required. The aad tenant id which is configured on + target K8s cluster + :type tenant_id: str + :param client_app_id: Required. The client app id configured on target K8 + cluster + :type client_app_id: str + :param server_app_id: Required. The server app id to access AD server + :type server_app_id: str + """ + + _validation = { + 'tenant_id': {'required': True}, + 'client_app_id': {'required': True}, + 'server_app_id': {'required': True}, + } + + _attribute_map = { + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'client_app_id': {'key': 'clientAppId', 'type': 'str'}, + 'server_app_id': {'key': 'serverAppId', 'type': 'str'}, + } + + def __init__(self, *, tenant_id: str, client_app_id: str, server_app_id: str, **kwargs) -> None: + super(ConnectedClusterAADProfile, self).__init__(**kwargs) + self.tenant_id = tenant_id + self.client_app_id = client_app_id + self.server_app_id = server_app_id diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity.py new file mode 100644 index 00000000000..820745bfb00 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity.py @@ -0,0 +1,48 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterIdentity(Model): + """Identity for the connected cluster. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar principal_id: The principal id of connected cluster identity. This + property will only be provided for a system assigned identity. + :vartype principal_id: str + :ivar tenant_id: The tenant id associated with the connected cluster. This + property will only be provided for a system assigned identity. + :vartype tenant_id: str + :ivar type: Required. The type of identity used for the connected cluster. + The type 'SystemAssigned, includes a system created identity. Default + value: "SystemAssigned" . + :vartype type: str + """ + + _validation = { + 'principal_id': {'readonly': True}, + 'tenant_id': {'readonly': True}, + 'type': {'required': True, 'constant': True}, + } + + _attribute_map = { + 'principal_id': {'key': 'principalId', 'type': 'str'}, + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + } + + type = "SystemAssigned" + + def __init__(self, **kwargs): + super(ConnectedClusterIdentity, self).__init__(**kwargs) + self.principal_id = None + self.tenant_id = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity_py3.py new file mode 100644 index 00000000000..38d2028609a --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_identity_py3.py @@ -0,0 +1,48 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterIdentity(Model): + """Identity for the connected cluster. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar principal_id: The principal id of connected cluster identity. This + property will only be provided for a system assigned identity. + :vartype principal_id: str + :ivar tenant_id: The tenant id associated with the connected cluster. This + property will only be provided for a system assigned identity. + :vartype tenant_id: str + :ivar type: Required. The type of identity used for the connected cluster. + The type 'SystemAssigned, includes a system created identity. Default + value: "SystemAssigned" . + :vartype type: str + """ + + _validation = { + 'principal_id': {'readonly': True}, + 'tenant_id': {'readonly': True}, + 'type': {'required': True, 'constant': True}, + } + + _attribute_map = { + 'principal_id': {'key': 'principalId', 'type': 'str'}, + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + } + + type = "SystemAssigned" + + def __init__(self, **kwargs) -> None: + super(ConnectedClusterIdentity, self).__init__(**kwargs) + self.principal_id = None + self.tenant_id = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_paged.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_paged.py new file mode 100644 index 00000000000..9b7ed6757f1 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_paged.py @@ -0,0 +1,23 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.paging import Paged + + +class ConnectedClusterPaged(Paged): + """ + A paging container for iterating over a list of :class:`ConnectedCluster ` object + """ + + _attribute_map = { + 'next_link': {'key': 'nextLink', 'type': 'str'}, + 'current_page': {'key': 'value', 'type': '[ConnectedCluster]'} + } + + def __init__(self, *args, **kwargs): + + super(ConnectedClusterPaged, self).__init__(*args, **kwargs) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch.py new file mode 100644 index 00000000000..3386a8125f7 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch.py @@ -0,0 +1,24 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterPatch(Model): + """Object containing updates for patch operations. + + :param tags: Resource tags. + :type tags: dict[str, str] + """ + + _attribute_map = { + 'tags': {'key': 'tags', 'type': '{str}'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterPatch, self).__init__(**kwargs) + self.tags = kwargs.get('tags', None) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch_py3.py new file mode 100644 index 00000000000..551d050719c --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_patch_py3.py @@ -0,0 +1,24 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterPatch(Model): + """Object containing updates for patch operations. + + :param tags: Resource tags. + :type tags: dict[str, str] + """ + + _attribute_map = { + 'tags': {'key': 'tags', 'type': '{str}'}, + } + + def __init__(self, *, tags=None, **kwargs) -> None: + super(ConnectedClusterPatch, self).__init__(**kwargs) + self.tags = tags diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile.py new file mode 100644 index 00000000000..a3588aefe10 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile.py @@ -0,0 +1,25 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterProxyProfile(Model): + """ConnectedClusterProxyProfile. + + :param proxy_connection: Endpoint and authentication details to connect to + proxy + :type proxy_connection: str + """ + + _attribute_map = { + 'proxy_connection': {'key': 'proxyConnection', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterProxyProfile, self).__init__(**kwargs) + self.proxy_connection = kwargs.get('proxy_connection', None) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile_py3.py new file mode 100644 index 00000000000..1e69814a9a0 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_proxy_profile_py3.py @@ -0,0 +1,25 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterProxyProfile(Model): + """ConnectedClusterProxyProfile. + + :param proxy_connection: Endpoint and authentication details to connect to + proxy + :type proxy_connection: str + """ + + _attribute_map = { + 'proxy_connection': {'key': 'proxyConnection', 'type': 'str'}, + } + + def __init__(self, *, proxy_connection: str=None, **kwargs) -> None: + super(ConnectedClusterProxyProfile, self).__init__(**kwargs) + self.proxy_connection = proxy_connection diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_py3.py new file mode 100644 index 00000000000..1d336b90439 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/connected_cluster_py3.py @@ -0,0 +1,95 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedCluster(Model): + """ConnectedCluster. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar id: Resource Id + :vartype id: str + :ivar name: Resource name + :vartype name: str + :ivar type: Type of the resource requested + :vartype type: str + :param location: Required. Location of the cluster + :type location: str + :param tags: Connected Cluster Resource Tags. + :type tags: dict[str, str] + :param identity: Required. The identity of the connected cluster. + :type identity: ~hybridkubernetes.models.ConnectedClusterIdentity + :param agent_public_key_certificate: Required. Base64 encoded public + certificate used by the agent to do the initial handshake to the backend + services in Azure. + :type agent_public_key_certificate: str + :param aad_profile: Required. + :type aad_profile: ~hybridkubernetes.models.ConnectedClusterAADProfile + :param kubernetes_version: The Kubernetes version of the connected cluster + resource + :type kubernetes_version: str + :param total_node_count: Number of nodes present in the connected cluster + resource + :type total_node_count: int + :param agent_version: Version of the agent running on the connected + cluster resource + :type agent_version: str + :param location_data: Metadata pertaining to the geographic location of + the resource. + :type location_data: ~hybridkubernetes.models.LocationData + :param provisioning_state: Possible values include: 'Succeeded', 'Failed', + 'Canceled', 'Provisioning', 'Updating', 'Deleting', 'Accepted' + :type provisioning_state: str or + ~hybridkubernetes.models.ProvisioningState + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + 'location': {'required': True}, + 'identity': {'required': True}, + 'agent_public_key_certificate': {'required': True}, + 'aad_profile': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'ConnectedClusterIdentity'}, + 'agent_public_key_certificate': {'key': 'properties.agentPublicKeyCertificate', 'type': 'str'}, + 'aad_profile': {'key': 'properties.aadProfile', 'type': 'ConnectedClusterAADProfile'}, + 'kubernetes_version': {'key': 'properties.kubernetesVersion', 'type': 'str'}, + 'total_node_count': {'key': 'properties.totalNodeCount', 'type': 'int'}, + 'agent_version': {'key': 'properties.agentVersion', 'type': 'str'}, + 'location_data': {'key': 'properties.locationData', 'type': 'LocationData'}, + 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, + } + + def __init__(self, *, location: str, identity, agent_public_key_certificate: str, aad_profile, tags=None, kubernetes_version: str=None, total_node_count: int=None, agent_version: str=None, location_data=None, provisioning_state=None, **kwargs) -> None: + super(ConnectedCluster, self).__init__(**kwargs) + self.id = None + self.name = None + self.type = None + self.location = location + self.tags = tags + self.identity = identity + self.agent_public_key_certificate = agent_public_key_certificate + self.aad_profile = aad_profile + self.kubernetes_version = kubernetes_version + self.total_node_count = total_node_count + self.agent_version = agent_version + self.location_data = location_data + self.provisioning_state = provisioning_state diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result.py new file mode 100644 index 00000000000..df3057e4131 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result.py @@ -0,0 +1,36 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class CredentialResult(Model): + """The credential result response. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: The name of the credential. + :vartype name: str + :ivar kubeconfig: Base64-encoded Kubernetes configuration file. + :vartype kubeconfig: bytearray + """ + + _validation = { + 'name': {'readonly': True}, + 'kubeconfig': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'kubeconfig': {'key': 'kubeconfig', 'type': 'bytearray'}, + } + + def __init__(self, **kwargs): + super(CredentialResult, self).__init__(**kwargs) + self.name = None + self.kubeconfig = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result_py3.py new file mode 100644 index 00000000000..f1548d88cfc --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/credential_result_py3.py @@ -0,0 +1,36 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class CredentialResult(Model): + """The credential result response. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: The name of the credential. + :vartype name: str + :ivar kubeconfig: Base64-encoded Kubernetes configuration file. + :vartype kubeconfig: bytearray + """ + + _validation = { + 'name': {'readonly': True}, + 'kubeconfig': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'kubeconfig': {'key': 'kubeconfig', 'type': 'bytearray'}, + } + + def __init__(self, **kwargs) -> None: + super(CredentialResult, self).__init__(**kwargs) + self.name = None + self.kubeconfig = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details.py new file mode 100644 index 00000000000..edb67474331 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details.py @@ -0,0 +1,36 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ErrorDetails(Model): + """The error response details containing error code and error message. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar code: The error code. + :vartype code: str + :ivar message: The error message. + :vartype message: str + """ + + _validation = { + 'code': {'readonly': True}, + 'message': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ErrorDetails, self).__init__(**kwargs) + self.code = None + self.message = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details_py3.py new file mode 100644 index 00000000000..279f042c56f --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_details_py3.py @@ -0,0 +1,36 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ErrorDetails(Model): + """The error response details containing error code and error message. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar code: The error code. + :vartype code: str + :ivar message: The error message. + :vartype message: str + """ + + _validation = { + 'code': {'readonly': True}, + 'message': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + } + + def __init__(self, **kwargs) -> None: + super(ErrorDetails, self).__init__(**kwargs) + self.code = None + self.message = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response.py new file mode 100644 index 00000000000..261f9ba5324 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response.py @@ -0,0 +1,37 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model +from msrest.exceptions import HttpOperationError + + +class ErrorResponse(Model): + """The error response that indicates why an operation has failed. + + :param error: + :type error: ~hybridkubernetes.models.ErrorDetails + """ + + _attribute_map = { + 'error': {'key': 'error', 'type': 'ErrorDetails'}, + } + + def __init__(self, **kwargs): + super(ErrorResponse, self).__init__(**kwargs) + self.error = kwargs.get('error', None) + + +class ErrorResponseException(HttpOperationError): + """Server responsed with exception of type: 'ErrorResponse'. + + :param deserialize: A deserializer + :param response: Server response to be deserialized. + """ + + def __init__(self, deserialize, response, *args): + + super(ErrorResponseException, self).__init__(deserialize, response, 'ErrorResponse', *args) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response_py3.py new file mode 100644 index 00000000000..8d2fcebd72f --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/error_response_py3.py @@ -0,0 +1,37 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model +from msrest.exceptions import HttpOperationError + + +class ErrorResponse(Model): + """The error response that indicates why an operation has failed. + + :param error: + :type error: ~hybridkubernetes.models.ErrorDetails + """ + + _attribute_map = { + 'error': {'key': 'error', 'type': 'ErrorDetails'}, + } + + def __init__(self, *, error=None, **kwargs) -> None: + super(ErrorResponse, self).__init__(**kwargs) + self.error = error + + +class ErrorResponseException(HttpOperationError): + """Server responsed with exception of type: 'ErrorResponse'. + + :param deserialize: A deserializer + :param response: Server response to be deserialized. + """ + + def __init__(self, deserialize, response, *args): + + super(ErrorResponseException, self).__init__(deserialize, response, 'ErrorResponse', *args) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/k8_connect_rp_enums.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/k8_connect_rp_enums.py new file mode 100644 index 00000000000..5b7faef1440 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/k8_connect_rp_enums.py @@ -0,0 +1,24 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum + + +class ProvisioningState(str, Enum): + + succeeded = "Succeeded" + failed = "Failed" + canceled = "Canceled" + provisioning = "Provisioning" + updating = "Updating" + deleting = "Deleting" + accepted = "Accepted" + + +class ResourceIdentityType(str, Enum): + + system_assigned = "SystemAssigned" diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data.py new file mode 100644 index 00000000000..7615747c98d --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data.py @@ -0,0 +1,45 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class LocationData(Model): + """Metadata pertaining to the geographic location of the resource. + + All required parameters must be populated in order to send to Azure. + + :param name: Required. A canonical name for the geographic or physical + location. + :type name: str + :param city: The city or locality where the resource is located. + :type city: str + :param district: The district, state, or province where the resource is + located. + :type district: str + :param country_or_region: The country or region where the resource is + located + :type country_or_region: str + """ + + _validation = { + 'name': {'required': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'city': {'key': 'city', 'type': 'str'}, + 'district': {'key': 'district', 'type': 'str'}, + 'country_or_region': {'key': 'countryOrRegion', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(LocationData, self).__init__(**kwargs) + self.name = kwargs.get('name', None) + self.city = kwargs.get('city', None) + self.district = kwargs.get('district', None) + self.country_or_region = kwargs.get('country_or_region', None) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data_py3.py new file mode 100644 index 00000000000..a21ddd101aa --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/location_data_py3.py @@ -0,0 +1,45 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class LocationData(Model): + """Metadata pertaining to the geographic location of the resource. + + All required parameters must be populated in order to send to Azure. + + :param name: Required. A canonical name for the geographic or physical + location. + :type name: str + :param city: The city or locality where the resource is located. + :type city: str + :param district: The district, state, or province where the resource is + located. + :type district: str + :param country_or_region: The country or region where the resource is + located + :type country_or_region: str + """ + + _validation = { + 'name': {'required': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'city': {'key': 'city', 'type': 'str'}, + 'district': {'key': 'district', 'type': 'str'}, + 'country_or_region': {'key': 'countryOrRegion', 'type': 'str'}, + } + + def __init__(self, *, name: str, city: str=None, district: str=None, country_or_region: str=None, **kwargs) -> None: + super(LocationData, self).__init__(**kwargs) + self.name = name + self.city = city + self.district = district + self.country_or_region = country_or_region diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation.py new file mode 100644 index 00000000000..72699bcc2b2 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation.py @@ -0,0 +1,36 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class Operation(Model): + """The Connected cluster API operation. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: Operation name: {Microsoft.Kubernetes}/{resource}/{operation} + :vartype name: str + :ivar display: The object that represents the operation. + :vartype display: ~hybridkubernetes.models.OperationDisplay + """ + + _validation = { + 'name': {'readonly': True}, + 'display': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'display': {'key': 'display', 'type': 'OperationDisplay'}, + } + + def __init__(self, **kwargs): + super(Operation, self).__init__(**kwargs) + self.name = None + self.display = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display.py new file mode 100644 index 00000000000..a487f2e160e --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display.py @@ -0,0 +1,37 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class OperationDisplay(Model): + """The object that represents the operation. + + :param provider: Service provider: Microsoft.connectedClusters + :type provider: str + :param resource: Connected Cluster Resource on which the operation is + performed + :type resource: str + :param operation: Operation type: Read, write, delete, etc. + :type operation: str + :param description: Description of the operation. + :type description: str + """ + + _attribute_map = { + 'provider': {'key': 'provider', 'type': 'str'}, + 'resource': {'key': 'resource', 'type': 'str'}, + 'operation': {'key': 'operation', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(OperationDisplay, self).__init__(**kwargs) + self.provider = kwargs.get('provider', None) + self.resource = kwargs.get('resource', None) + self.operation = kwargs.get('operation', None) + self.description = kwargs.get('description', None) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display_py3.py new file mode 100644 index 00000000000..07abffa0d76 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_display_py3.py @@ -0,0 +1,37 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class OperationDisplay(Model): + """The object that represents the operation. + + :param provider: Service provider: Microsoft.connectedClusters + :type provider: str + :param resource: Connected Cluster Resource on which the operation is + performed + :type resource: str + :param operation: Operation type: Read, write, delete, etc. + :type operation: str + :param description: Description of the operation. + :type description: str + """ + + _attribute_map = { + 'provider': {'key': 'provider', 'type': 'str'}, + 'resource': {'key': 'resource', 'type': 'str'}, + 'operation': {'key': 'operation', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + } + + def __init__(self, *, provider: str=None, resource: str=None, operation: str=None, description: str=None, **kwargs) -> None: + super(OperationDisplay, self).__init__(**kwargs) + self.provider = provider + self.resource = resource + self.operation = operation + self.description = description diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_paged.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_paged.py new file mode 100644 index 00000000000..4e30fcc4b30 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_paged.py @@ -0,0 +1,23 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.paging import Paged + + +class OperationPaged(Paged): + """ + A paging container for iterating over a list of :class:`Operation ` object + """ + + _attribute_map = { + 'next_link': {'key': 'nextLink', 'type': 'str'}, + 'current_page': {'key': 'value', 'type': '[Operation]'} + } + + def __init__(self, *args, **kwargs): + + super(OperationPaged, self).__init__(*args, **kwargs) diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_py3.py b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_py3.py new file mode 100644 index 00000000000..acc5d36b067 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/models/operation_py3.py @@ -0,0 +1,36 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class Operation(Model): + """The Connected cluster API operation. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: Operation name: {Microsoft.Kubernetes}/{resource}/{operation} + :vartype name: str + :ivar display: The object that represents the operation. + :vartype display: ~hybridkubernetes.models.OperationDisplay + """ + + _validation = { + 'name': {'readonly': True}, + 'display': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'display': {'key': 'display', 'type': 'OperationDisplay'}, + } + + def __init__(self, **kwargs) -> None: + super(Operation, self).__init__(**kwargs) + self.name = None + self.display = None diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/operations/__init__.py b/src/connect_2020/azext_connect_2020/vendored_sdks/operations/__init__.py new file mode 100644 index 00000000000..ffbdc1f83e3 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/operations/__init__.py @@ -0,0 +1,14 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from .connected_cluster_operations import ConnectedClusterOperations +from .operations import Operations + +__all__ = [ + 'ConnectedClusterOperations', + 'Operations', +] diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/operations/connected_cluster_operations.py b/src/connect_2020/azext_connect_2020/vendored_sdks/operations/connected_cluster_operations.py new file mode 100644 index 00000000000..b82af79195c --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/operations/connected_cluster_operations.py @@ -0,0 +1,618 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +import uuid +from msrest.pipeline import ClientRawResponse +from msrest.polling import LROPoller, NoPolling +from msrestazure.polling.arm_polling import ARMPolling + +from .. import models + + +class ConnectedClusterOperations(object): + """ConnectedClusterOperations operations. + + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = models + + def __init__(self, client, config, serializer, deserializer): + + self._client = client + self._serialize = serializer + self._deserialize = deserializer + + self.config = config + + + def _create_initial( + self, resource_group_name, cluster_name, connected_cluster, custom_headers=None, raw=False, **operation_config): + # Construct URL + url = self.create.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = 'application/json; charset=utf-8' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct body + body_content = self._serialize.body(connected_cluster, 'ConnectedCluster') + + # Construct and send request + request = self._client.put(url, query_parameters, header_parameters, body_content) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200, 201]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedCluster', response) + if response.status_code == 201: + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + + def create( + self, resource_group_name, cluster_name, connected_cluster, custom_headers=None, raw=False, polling=True, **operation_config): + """Registers a new K8s cluster. + + API to register a new K8s cluster and thereby create a tracked resource + in ARM. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param connected_cluster: + :type connected_cluster: ~hybridkubernetes.models.ConnectedCluster + :param dict custom_headers: headers that will be added to the request + :param bool raw: The poller return type is ClientRawResponse, the + direct response alongside the deserialized response + :param polling: True for ARMPolling, False for no polling, or a + polling object for personal polling strategy + :return: An instance of LROPoller that returns ConnectedCluster or + ClientRawResponse if raw==True + :rtype: + ~msrestazure.azure_operation.AzureOperationPoller[~hybridkubernetes.models.ConnectedCluster] + or + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~hybridkubernetes.models.ConnectedCluster]] + :raises: + :class:`ErrorResponseException` + """ + raw_result = self._create_initial( + resource_group_name=resource_group_name, + cluster_name=cluster_name, + connected_cluster=connected_cluster, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + + def get_long_running_output(response): + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + create.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def update( + self, resource_group_name, cluster_name, tags=None, custom_headers=None, raw=False, **operation_config): + """Updates a connected cluster. + + API to update certain properties of the connected cluster resource. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param tags: Resource tags. + :type tags: dict[str, str] + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedCluster or ClientRawResponse if raw=true + :rtype: ~hybridkubernetes.models.ConnectedCluster or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + connected_cluster_patch = models.ConnectedClusterPatch(tags=tags) + + # Construct URL + url = self.update.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + header_parameters['Content-Type'] = 'application/json; charset=utf-8' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct body + body_content = self._serialize.body(connected_cluster_patch, 'ConnectedClusterPatch') + + # Construct and send request + request = self._client.patch(url, query_parameters, header_parameters, body_content) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + update.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def get( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Gets data of the specified cluster. + + API to get the properties of a specific registered K8s cluster. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedCluster or ClientRawResponse if raw=true + :rtype: ~hybridkubernetes.models.ConnectedCluster or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.get.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + get.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + + def _delete_initial( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + # Construct URL + url = self.delete.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + # Construct headers + header_parameters = {} + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.delete(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200, 202, 204]: + raise models.ErrorResponseException(self._deserialize, response) + + if raw: + client_raw_response = ClientRawResponse(None, response) + return client_raw_response + + def delete( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, polling=True, **operation_config): + """Deletes a specified cluster. + + API to delete an existing K8s cluster being tracked. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: The poller return type is ClientRawResponse, the + direct response alongside the deserialized response + :param polling: True for ARMPolling, False for no polling, or a + polling object for personal polling strategy + :return: An instance of LROPoller that returns None or + ClientRawResponse if raw==True + :rtype: ~msrestazure.azure_operation.AzureOperationPoller[None] or + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[None]] + :raises: + :class:`ErrorResponseException` + """ + raw_result = self._delete_initial( + resource_group_name=resource_group_name, + cluster_name=cluster_name, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + + def get_long_running_output(response): + if raw: + client_raw_response = ClientRawResponse(None, response) + return client_raw_response + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def list_cluster_user_credential( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Gets cluster user credential of a connected cluster. + + Gets cluster user credential of the connected cluster with a specified + resource group and name. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: CredentialResult or ClientRawResponse if raw=true + :rtype: ~hybridkubernetes.models.CredentialResult or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.list_cluster_user_credential.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.post(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('CredentialResult', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + list_cluster_user_credential.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}/listClusterUserCredential'} + + def list_proxy_connection_detail( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Lists the endpoint and credential for proxy. + + API to fetch endpoint and credential details to proxy server. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedClusterProxyProfile or ClientRawResponse if raw=true + :rtype: ~hybridkubernetes.models.ConnectedClusterProxyProfile or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.list_proxy_connection_detail.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.post(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedClusterProxyProfile', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + list_proxy_connection_detail.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}/listProxyConnectionDetail'} + + def list_by_resource_group( + self, resource_group_name, custom_headers=None, raw=False, **operation_config): + """Lists all connected clusters. + + API to enumerate registered connected K8s clusters under a Resource + Group. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: An iterator like instance of ConnectedCluster + :rtype: + ~hybridkubernetes.models.ConnectedClusterPaged[~hybridkubernetes.models.ConnectedCluster] + :raises: + :class:`ErrorResponseException` + """ + def internal_paging(next_link=None, raw=False): + + if not next_link: + # Construct URL + url = self.list_by_resource_group.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + else: + url = next_link + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + return response + + # Deserialize response + deserialized = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies) + + if raw: + header_dict = {} + client_raw_response = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies, header_dict) + return client_raw_response + + return deserialized + list_by_resource_group.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters'} + + def list_by_subscription( + self, custom_headers=None, raw=False, **operation_config): + """Lists all connected clusters. + + API to enumerate registered connected K8s clusters under a + Subscription. + + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: An iterator like instance of ConnectedCluster + :rtype: + ~hybridkubernetes.models.ConnectedClusterPaged[~hybridkubernetes.models.ConnectedCluster] + :raises: + :class:`ErrorResponseException` + """ + def internal_paging(next_link=None, raw=False): + + if not next_link: + # Construct URL + url = self.list_by_subscription.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + + else: + url = next_link + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + return response + + # Deserialize response + deserialized = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies) + + if raw: + header_dict = {} + client_raw_response = models.ConnectedClusterPaged(internal_paging, self._deserialize.dependencies, header_dict) + return client_raw_response + + return deserialized + list_by_subscription.metadata = {'url': '/subscriptions/{subscriptionId}/providers/Microsoft.Kubernetes/connectedClusters'} diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/operations/operations.py b/src/connect_2020/azext_connect_2020/vendored_sdks/operations/operations.py new file mode 100644 index 00000000000..c0c68c0562c --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/operations/operations.py @@ -0,0 +1,90 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +import uuid +from msrest.pipeline import ClientRawResponse + +from .. import models + + +class Operations(object): + """Operations operations. + + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = models + + def __init__(self, client, config, serializer, deserializer): + + self._client = client + self._serialize = serializer + self._deserialize = deserializer + + self.config = config + + def get( + self, custom_headers=None, raw=False, **operation_config): + """Lists all of the available API operations for Connected Cluster + resource. + + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: An iterator like instance of Operation + :rtype: + ~hybridkubernetes.models.OperationPaged[~hybridkubernetes.models.Operation] + :raises: + :class:`ErrorResponseException` + """ + def internal_paging(next_link=None, raw=False): + + if not next_link: + # Construct URL + url = self.get.metadata['url'] + + # Construct parameters + query_parameters = {} + + else: + url = next_link + query_parameters = {} + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + raise models.ErrorResponseException(self._deserialize, response) + + return response + + # Deserialize response + deserialized = models.OperationPaged(internal_paging, self._deserialize.dependencies) + + if raw: + header_dict = {} + client_raw_response = models.OperationPaged(internal_paging, self._deserialize.dependencies, header_dict) + return client_raw_response + + return deserialized + get.metadata = {'url': '/providers/Microsoft.Kubernetes/operations'} diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/version.py b/src/connect_2020/azext_connect_2020/vendored_sdks/version.py new file mode 100644 index 00000000000..290c01f6677 --- /dev/null +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/version.py @@ -0,0 +1,9 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +VERSION = "2020-01-01-preview" + diff --git a/src/connect_2020/setup.cfg b/src/connect_2020/setup.cfg new file mode 100644 index 00000000000..3c6e79cf31d --- /dev/null +++ b/src/connect_2020/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/src/connect_2020/setup.py b/src/connect_2020/setup.py new file mode 100644 index 00000000000..f0e6c6d29d4 --- /dev/null +++ b/src/connect_2020/setup.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +from codecs import open +from setuptools import setup, find_packages +try: + from azure_bdist_wheel import cmdclass +except ImportError: + from distutils import log as logger + logger.warn("Wheel is not available, disabling bdist_wheel hook") + +# TODO: Confirm this is the right version number you want and it matches your +# HISTORY.rst entry. +VERSION = '0.1.0' + +# The full list of classifiers is available at +# https://pypi.python.org/pypi?%3Aaction=list_classifiers +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'License :: OSI Approved :: MIT License', +] + +# TODO: Add any additional SDK dependencies here +DEPENDENCIES = [ + 'azure-cli-core' +] + +with open('README.rst', 'r', encoding='utf-8') as f: + README = f.read() +with open('HISTORY.rst', 'r', encoding='utf-8') as f: + HISTORY = f.read() + +setup( + name='connect_2020', + version=VERSION, + description='Microsoft Azure Command-Line Tools Connect_2020 Extension', + # TODO: Update author and email, if applicable + author='Microsoft Corporation', + author_email='azpycli@microsoft.com', + # TODO: consider pointing directly to your source code instead of the generic repo + url='https://github.com/Azure/azure-cli-extensions', + long_description=README + '\n\n' + HISTORY, + license='MIT', + classifiers=CLASSIFIERS, + packages=find_packages(), + install_requires=DEPENDENCIES, + package_data={'azext_connect_2020': ['azext_metadata.json']}, +) \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/__init__.py b/src/connectedk8s/azext_connectedk8s/__init__.py index 6c6d17bacdd..3b8ce055503 100644 --- a/src/connectedk8s/azext_connectedk8s/__init__.py +++ b/src/connectedk8s/azext_connectedk8s/__init__.py @@ -5,7 +5,7 @@ from azure.cli.core import AzCommandsLoader -from azext_connectedk8s._help import helps # pylint: disable=unused-import +#from azext_connectedk8s._help import helps # pylint: disable=unused-import class Connectedk8sCommandsLoader(AzCommandsLoader): @@ -16,8 +16,7 @@ def __init__(self, cli_ctx=None): connectedk8s_custom = CliCommandType( operations_tmpl='azext_connectedk8s.custom#{}', client_factory=cf_connectedk8s) - super(Connectedk8sCommandsLoader, self).__init__(cli_ctx=cli_ctx, - custom_command_type=connectedk8s_custom) + super(Connectedk8sCommandsLoader, self).__init__(cli_ctx=cli_ctx, custom_command_type=connectedk8s_custom) def load_command_table(self, args): from azext_connectedk8s.commands import load_command_table diff --git a/src/connectedk8s/azext_connectedk8s/_client_factory.py b/src/connectedk8s/azext_connectedk8s/_client_factory.py index 90ecd5dc01b..cb7b0f79102 100644 --- a/src/connectedk8s/azext_connectedk8s/_client_factory.py +++ b/src/connectedk8s/azext_connectedk8s/_client_factory.py @@ -2,25 +2,33 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +from azure.cli.core.commands.client_factory import get_mgmt_service_client +from azure.cli.core.profiles import ResourceType +from azure.cli.core._profile import Profile +from azure.cli.core.commands.client_factory import configure_common_settings +from azure.graphrbac import GraphRbacManagementClient +import re -def cf_connectedk8s(cli_ctx, *_): - from azure.cli.core.commands.client_factory import get_mgmt_service_client +def cf_connectedk8s(cli_ctx, *_): from azext_connectedk8s.vendored_sdks import K8ConnectRP return get_mgmt_service_client(cli_ctx, K8ConnectRP) + def cf_connected_cluster(cli_ctx, _): return cf_connectedk8s(cli_ctx).connected_cluster -def _resource_client_factory(cli_ctx): - from azure.cli.core.commands.client_factory import get_mgmt_service_client - from azure.cli.core.profiles import ResourceType - return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + +def cf_resource_groups(cli_ctx, subscription_id=None): + return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES, + subscription_id=subscription_id).resource_groups + + +def _resource_client_factory(cli_ctx, subscription_id=None): + return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES, subscription_id=subscription_id) + def _graph_client_factory(cli_ctx, **_): - from azure.cli.core._profile import Profile - from azure.cli.core.commands.client_factory import configure_common_settings - from azure.graphrbac import GraphRbacManagementClient profile = Profile(cli_ctx=cli_ctx) cred, _, tenant_id = profile.get_login_credentials( resource=cli_ctx.cloud.endpoints.active_directory_graph_resource_id) @@ -29,12 +37,9 @@ def _graph_client_factory(cli_ctx, **_): configure_common_settings(cli_ctx, client) return client + def _auth_client_factory(cli_ctx, scope=None): - import re - from azure.cli.core.profiles import ResourceType - from azure.cli.core.commands.client_factory import get_mgmt_service_client subscription_id = None - #print(subscription_id) if scope: matched = re.match('/subscriptions/(?P[^/]*)/', scope) if matched: diff --git a/src/connectedk8s/azext_connectedk8s/_format.py b/src/connectedk8s/azext_connectedk8s/_format.py new file mode 100644 index 00000000000..909e297abc2 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/_format.py @@ -0,0 +1,29 @@ +from jmespath import compile as compile_jmes, Options +from collections import OrderedDict + + +def connectedk8s_show_table_format(result): + """Format a connected cluster as summary results for display with "-o table".""" + return [_connectedk8s_table_format(result)] + +def connectedk8s_list_table_format(results): + """Format an connected cluster list for display with "-o table".""" + return [_connectedk8s_list_table_format(r) for r in results] + +def _connectedk8s_table_format(result): + parsed = compile_jmes("""{ + name: name, + location: location, + resourceGroup: resourceGroup + }""") + # use ordered dicts so headers are predictable + return parsed.search(result, Options(dict_cls=OrderedDict)) + +def _connectedk8s_list_table_format(result): + parsed = compile_jmes("""{ + name: name, + location: location, + resourceGroup: resourceGroup + }""") + # use ordered dicts so headers are predictable + return parsed.search(result, Options(dict_cls=OrderedDict)) \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/_help.py b/src/connectedk8s/azext_connectedk8s/_help.py index 92bd55803b6..70ae9f5ab72 100644 --- a/src/connectedk8s/azext_connectedk8s/_help.py +++ b/src/connectedk8s/azext_connectedk8s/_help.py @@ -9,30 +9,71 @@ helps['connectedk8s'] = """ type: group - short-summary: Commands to manage Connectedk8ss. + short-summary: Commands to manage connected kubernetes clusters. """ -helps['connectedk8s create'] = """ +helps['connectedk8s connect'] = """ type: command - short-summary: Create a Connectedk8s. + short-summary: Onboard a connected kubernetes cluster to azure. + parameters: + - name: --kube-config + type: string + short-summary: Path to the kube config file + - name: --kube-context + type: string + short-summary: Kubconfig context from current machine + - name: --onboarding-spn-id + type: string + short-summary: Azure Active Directory application id with access to connected cluster resource creation + - name: --onboarding-spn-secret + type: string + short-summary: Secret for Azure Active Directory application id with access to connected cluster resource creation + examples: + - name: Onboard a connected kubernetes cluster with default kube config and kube context. + text: az connectedk8s connect -g resourceGroupName -n connectedClusterName + - name: Onboard a connected kubernetes cluster by specifying the kubeconfig and kubecontext. + text: az connectedk8s connect -g resourceGroupName -n connectedClusterName --kube-config /path/to/kubeconfig --kube-context kubeContextName + - name: Onboard a connected kubernetes cluster by specifying the onboarding spn details. + text: az connectedk8s connect -g resourceGroupName -n connectedClusterName --onboarding-spn-id spnClientId --onboarding-spn-secret spnClientSecret """ helps['connectedk8s list'] = """ type: command - short-summary: List Connectedk8ss. + short-summary: List connected kubernetes clusters. + examples: + - name: List all connected kubernetes clusters in a resource group. + text: az connectedk8s list -g resourceGroupName --subscription subscriptionName + - name: List all connected kubernetes clusters in a subscription. + text: az connectedk8s list --subscription subscriptionName + """ helps['connectedk8s delete'] = """ type: command - short-summary: Delete a Connectedk8s. + short-summary: Delete a connected kubernetes cluster along with connected cluster agents. + parameters: + - name: --kube-config + type: string + short-summary: Path to the kube config file + - name: --kube-context + type: string + short-summary: Kubconfig context from current machine + examples: + - name: Delete a connected kubernetes cluster and connected cluster agents with default kubeconfig and kubecontext. + text: az connectedk8s delete -g resourceGroupName -n connectedClusterName + - name: Delete a connected kubernetes cluster by specifying the kubeconfig and kubecontext for connected cluster agents deletion. + text: az connectedk8s delete -g resourceGroupName -n connectedClusterName --kube-config /path/to/kubeconfig --kube-context kubeContextName """ helps['connectedk8s show'] = """ type: command - short-summary: Show details of a Connectedk8s. + short-summary: Show details of a connected kubernetes cluster. + examples: + - name: Show the details for a connected kubernetes cluster + text: az connectedk8s show -g resourceGroupName -n connectedClusterName """ helps['connectedk8s update'] = """ type: command - short-summary: Update a Connectedk8s. + short-summary: Update a connected kubernetes cluster. """ diff --git a/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py b/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py index 783ca3e7ada..dd5e6716457 100644 --- a/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py +++ b/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py @@ -67,4 +67,4 @@ def set_role_property(self, obj, property_name, property_value): if isinstance(obj, dict): obj[property_name] = property_value else: - obj.property_name = property_value \ No newline at end of file + obj.property_name = property_value diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py index ac990ee46df..855c3d7df86 100644 --- a/src/connectedk8s/azext_connectedk8s/_params.py +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -9,15 +9,16 @@ def load_arguments(self, _): - from azure.cli.core.commands.parameters import tags_type - from azure.cli.core.commands.validators import get_default_location_from_resource_group + from azure.cli.core.commands.parameters import tags_type, get_resource_name_completion_list, name_type - cluster_name_type = CLIArgumentType(options_list='--cluster-name-name', help='Name of the Connectedk8s.', id_part='name') + #cluster_name_type = CLIArgumentType(options_list='--cluster-name-name', help='Name of the Connected Kubernetes Cluster.', id_part='name') with self.argument_context('connectedk8s') as c: + c.argument('resource_group_name', name_type, help='Name of the resource group.', + completer=get_resource_name_completion_list('Microsoft.Kubernetes/ConnectedClusters'), options_list=['--resource-group', '-g']) c.argument('tags', tags_type) - #c.argument('location', validator=get_default_location_from_resource_group) - c.argument('cluster_name', cluster_name_type, options_list=['--name', '-n']) + c.argument('cluster_name', name_type, help='Name of the connected cluster.', + completer=get_resource_name_completion_list('Microsoft.Kubernetes/ConnectedClusters'), options_list=['--name', '-n']) - with self.argument_context('connectedk8s list') as c: - c.argument('cluster_name', cluster_name_type, id_part=None) + #with self.argument_context('connectedk8s list') as c: + # c.argument('cluster_name', cluster_name_type, id_part=None, options_list=['--name', '-n']) diff --git a/src/connectedk8s/azext_connectedk8s/_validators.py b/src/connectedk8s/azext_connectedk8s/_validators.py index 821630f5f34..d46d7e58f7e 100644 --- a/src/connectedk8s/azext_connectedk8s/_validators.py +++ b/src/connectedk8s/azext_connectedk8s/_validators.py @@ -6,7 +6,6 @@ def example_name_or_id_validator(cmd, namespace): # Example of a storage account name or ID validator. - # See: https://github.com/Azure/azure-cli/blob/dev/doc/authoring_command_modules/authoring_commands.md#supporting-name-or-id-parameters from azure.cli.core.commands.client_factory import get_subscription_id from msrestazure.tools import is_valid_resource_id, resource_id if namespace.storage_account: diff --git a/src/connectedk8s/azext_connectedk8s/commands.py b/src/connectedk8s/azext_connectedk8s/commands.py index 3221c2f4917..d2d4f36a454 100644 --- a/src/connectedk8s/azext_connectedk8s/commands.py +++ b/src/connectedk8s/azext_connectedk8s/commands.py @@ -5,24 +5,24 @@ # pylint: disable=line-too-long from azure.cli.core.commands import CliCommandType -from azext_connectedk8s._client_factory import (cf_connectedk8s,cf_connected_cluster) +from azext_connectedk8s._client_factory import (cf_connectedk8s, cf_connected_cluster) +from ._format import connectedk8s_show_table_format +from ._format import connectedk8s_list_table_format def load_command_table(self, _): connectedk8s_sdk = CliCommandType( operations_tmpl='azext_connectedk8s.vendored_sdks.operations#ConnectedClusterOperations.{}', - client_factory=cf_connectedk8s) - + client_factory=cf_connectedk8s + ) with self.command_group('connectedk8s', connectedk8s_sdk, client_factory=cf_connected_cluster) as g: - g.custom_command('create', 'create_connectedk8s') - g.custom_command('delete', 'delete_connectedk8s') - g.custom_command('list', 'list_connectedk8s') - g.custom_show_command('show', 'get_connectedk8s') - g.generic_update_command('update', setter_name='update', custom_func_name='update_connectedk8s') - + g.custom_command('connect', 'create_connectedk8s', supports_no_wait=True) + g.custom_command('delete', 'delete_connectedk8s', confirmation=True) + g.custom_command('list', 'list_connectedk8s', table_transformer=connectedk8s_list_table_format) + g.custom_command('show', 'get_connectedk8s', table_transformer=connectedk8s_show_table_format) + #g.generic_update_command('update', setter_name='update', custom_func_name='update_connectedk8s') with self.command_group('connectedk8s', is_preview=True): pass - diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 989a1c3e427..158d7c3b2bd 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -4,121 +4,129 @@ # -------------------------------------------------------------------------------------------- from knack.util import CLIError -from azure.cli.core.profiles import ResourceType, get_sdk from azure.cli.core.commands.client_factory import get_subscription_id from azext_connectedk8s._client_factory import _graph_client_factory +from azext_connectedk8s._client_factory import cf_resource_groups from azext_connectedk8s._client_factory import _resource_client_factory from azext_connectedk8s._client_factory import _auth_client_factory from azext_connectedk8s._multi_api_adaptor import MultiAPIAdaptor -import datetime from msrest.serialization import TZ_UTC from dateutil.relativedelta import relativedelta -import uuid from azure.graphrbac.models import (PasswordCredential, ApplicationCreateParameters, ServicePrincipalCreateParameters) +from azure.graphrbac.operations.service_principals_operations import ServicePrincipalsOperations from knack.log import get_logger +from azure.graphrbac.models import GraphErrorException +from azure.cli.core.api import get_config_dir +from azure.cli.core.util import sdk_no_wait +from msrestazure.azure_exceptions import CloudError +from kubernetes import client, config +import kubernetes.client +from kubernetes.client.rest import ApiException import os +import subprocess +from subprocess import Popen, PIPE import json -from azure.mgmt.subscription import SubscriptionClient -from azure.common.client_factory import get_client_from_cli_profile -from azure.cli.core._profile import Profile -from azure.cli.core.api import get_config_dir -import platform -import yaml, copy, base64, atexit, tempfile -from kubernetes.client import ApiClient, Configuration +import uuid +import datetime +import time +from applicationinsights import TelemetryClient logger = get_logger(__name__) +APP_KEY = '9a93ae7c-eaf8-4e21-a1f2-6a424cc48a44' + + +def create_connectedk8s(cmd, client, resource_group_name, cluster_name, + onboarding_spn_id=None, onboarding_spn_secret=None, + location=None, kube_config=None, kube_context=None, no_wait=False, + location_data_name=None, location_data_country_or_region=None, + location_data_district=None, location_data_city=None): + print("Ensure that you have the latest helm version installed before proceeding to avoid unexpected errors.") + print("This operation might take a while...\n") + + tc = TelemetryClient(APP_KEY) + tc.track_event('testEvent') + tc.flush() + + # Checking location data info + if location_data_name is None: + if ((location_data_country_or_region is not None) or (location_data_district is not None) or (location_data_city is not None)): + raise CLIError("--location-data-name is required when providing location data info.") -def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboarding_spn_id=None, onboarding_spn_secret=None, location=None, kube_config=None, kube_context=None, tags=None): - #print(kube_config) + # Setting subscription id subscription_id = get_subscription_id(cmd.cli_ctx) - print(subscription_id) - """if(subscription_id is not None): + + resourceClient = _resource_client_factory(cmd.cli_ctx, subscription_id=subscription_id) + + # Resource group Creation + if location is None: try: - sub_client = get_client_from_cli_profile(SubscriptionClient) - except CLIError: - logger.info("Not logged in, running az login") - _run_az_cli_login() - sub_client = get_client_from_cli_profile(SubscriptionClient) - #print(sub_client.subscriptions.list()) - sub_list = [] - for sub in sub_client.subscriptions.list(): - sub_list.append(sub.subscription_id) - print(sub_list) - if(subscription_id not in sub_list): - raise CLIError("Provided subscription name does not exist")""" - - #if(resource_group_name is None): - # raise CLIError("Provide resource group name") - #print(get_subscription_id(cmd.cli_ctx)) - #print(subscription_name) - rg_client = _resource_client_factory(cmd.cli_ctx) - rg_name_list = [] - for item in rg_client.resource_groups.list(): - rg_name_list.append(item.name) - if(item.name==resource_group_name and location!=item.location): - raise CLIError("The resource group already exists in the location {}".format(item.location)) - print(rg_name_list) - if(resource_group_name not in rg_name_list): - print("Creating resource group") - if(location is None): - raise CLIError("Provide the location to create the resource group") - #profile = Profile(cli_ctx=cmd.cli_ctx) - #print(json.dumps(profile.get_sp_auth_info(subscription_name), indent=2)) - # = profile.get_raw_token(cmd, subscription=subscription_name, resource=resource_group_name) - #accToken = profile.get_access_token(cmd, subscription=subscription_name, resource=resource_group_name) - #print(accToken) + location = resourceClient.resource_groups.get(resource_group_name).location + except: + raise CLIError("Resource Group Creation Failed. Please provide location to create the Resource Group") + + rp_locations = [] + providerDetails = resourceClient.providers.get('Microsoft.Kubernetes') + for resourceTypes in providerDetails.resource_types: + if resourceTypes.resource_type == 'connectedClusters': + rp_locations = [location.replace(" ", "").lower() for location in resourceTypes.locations] + if location.lower() not in rp_locations: + raise CLIError("The connected cluster resource creation is supported only in the following locations: " + ', '.join(map(str, rp_locations)) + ". Please use the --location flag to specify right location.") + break + + if (resource_group_exists(cmd.cli_ctx, resource_group_name, subscription_id) is False): resource_group_params = {'location': location} - rg_client.resource_groups.create_or_update(resource_group_name, resource_group_params) + try: + resourceClient.resource_groups.create_or_update(resource_group_name, resource_group_params) + except Exception as e: + raise CLIError("Resource Group Creation Failed." + str(e.message)) - print("start") - #print(get_subscription_id(cmd.cli_ctx)) + # SPN creation graph_client = _graph_client_factory(cmd.cli_ctx) onboarding_tenant_id = graph_client.config.tenant_id - if(onboarding_spn_id is not None and onboarding_spn_secret is None): - raise CLIError("Provide the onboarding spn password") - #client1 = _graph_client_factory(cmd.cli_ctx) - spn_list = list_spn(graph_client) - spn_appid_list=[] - for i in spn_list: - spn_appid_list.append(i.app_id) - if(onboarding_spn_id is not None and onboarding_spn_id not in spn_appid_list): - raise CLIError("Provided service principal does not exist") - if(onboarding_spn_id is None): - file_name_connectedk8s = 'azureArcServicePrincipal.json' - principal_obj = load_acs_service_principal(subscription_id, file_name=file_name_connectedk8s) - #print(principal_obj) - var = None + + if (onboarding_spn_id is not None and onboarding_spn_secret is None): + raise CLIError("Provide the onboarding spn secret.") + + if onboarding_spn_id is None: + try: + spn_list = list_owned_objects(graph_client.signed_in_user, 'servicePrincipal') + except Exception as ex: + raise CLIError("Problem loading the service principals. Check if you have sufficient access to list/create service principals. Error Message: " + str(ex)) + spn_appid_list = [] + for spn in spn_list: + spn_appid_list.append(spn.app_id) + file_name_connectedk8s = 'azureArcServicePrincipal.json' # File containing SPN details + principal_obj = load_acs_service_principal(subscription_id, + file_name=file_name_connectedk8s) # Loading spn from file + spn_present = True if principal_obj: - if(principal_obj.get('service_principal') not in spn_appid_list): + if principal_obj.get('service_principal') not in spn_appid_list: erase_acs_service_principal(file_name=file_name_connectedk8s) - var = "temp" - if (principal_obj and var is None): + spn_present = False + if (principal_obj and spn_present is True): onboarding_spn_id = principal_obj.get('service_principal') - #print(onboarding_spn_id) onboarding_spn_secret = principal_obj.get('client_secret') else: + #print("Creating New SPN ...") graph_client = _graph_client_factory(cmd.cli_ctx) role_client = _auth_client_factory(cmd.cli_ctx).role_assignments - print(role_client.config.subscription_id) scopes = ['/subscriptions/' + role_client.config.subscription_id] years = 1 - import time app_start_date = datetime.datetime.now(TZ_UTC) app_end_date = app_start_date + relativedelta(years=years) - app_display_name = ('azure-cli-' + app_start_date.strftime('%Y-%m-%d-%H-%M-%S')) + app_display_name = ('cluster-onboarding-spn-' + app_start_date.strftime('%Y-%m-%d-%H-%M-%S')) name = 'http://' + app_display_name password = str(uuid.uuid4()) - aad_application = create_application(cmd, - display_name=app_display_name, - homepage='https://' + app_display_name, - identifier_uris=[name], - available_to_other_tenants=False, - password=password, - key_value=None, - start_date=app_start_date, - end_date=app_end_date, - credential_description='rbac') + aad_application = create_aad_application(cmd, + display_name=app_display_name, + homepage='https://' + app_display_name, + identifier_uris=[name], + available_to_other_tenants=False, + password=password, key_value=None, + start_date=app_start_date, + end_date=app_end_date, + credential_description='rbac') _RETRY_TIMES = 36 app_id = aad_application.app_id aad_sp = None @@ -134,15 +142,15 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboardi else: logger.warning( "Creating service principal failed for appid '%s'. Trace followed:\n%s", - name, ex.response.headers if hasattr(ex, - 'response') else ex) # pylint: disable=no-member + name, ex.response.headers if hasattr(ex, 'response') else ex) # pylint: disable=no-member raise - #correct - store_acs_service_principal(subscription_id, password, app_id, file_name=file_name_connectedk8s) + # correct + + + # Creating Role Binding role = 'Kubernetes Cluster - Azure Arc Onborading Role' sp_oid = aad_sp.object_id for scope in scopes: - logger.warning('Creating a role assignment under the scope of "%s"', scope) for l in range(0, _RETRY_TIMES): try: _create_role_assignment(cmd.cli_ctx, role, sp_oid, None, scope, resolve_assignee=False) @@ -150,43 +158,218 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboardi except Exception as ex: if l < _RETRY_TIMES and ' does not exist in the directory ' in str(ex): time.sleep(5) - logger.warning(' Retrying role assignment creation: %s/%s', l + 1, - _RETRY_TIMES) + #logger.warning(' Retrying role assignment creation: %s/%s', l + 1, _RETRY_TIMES) continue elif _error_caused_by_role_assignment_exists(ex): - logger.warning(' Role assignment already exits.\n') + #logger.warning(' Role assignment already exits.\n') break else: - # dump out history for diagnoses - logger.warning(' Role assignment creation failed.\n') if getattr(ex, 'response', None) is not None: - logger.warning(' role assignment response headers: %s\n', - ex.response.headers) # pylint: disable=no-member + logger.warning(' role assignment response headers: %s\n', ex.response.headers) # pylint: disable=no-member raise onboarding_spn_id = app_id onboarding_spn_secret = password - print("spn_id: "+app_id) - print("spn_secret: "+password) - print("name: "+name) - print("app_display_name: "+app_display_name) - print("tenant_id: "+graph_client.config.tenant_id) - print() - #load_kube_config(config_file=kube_config, context=kube_context) - return client.create(resource_group_name, cluster_name, onboarding_tenant_id, onboarding_spn_id, onboarding_spn_secret, location, kube_config, kube_context) + store_acs_service_principal(subscription_id, onboarding_spn_secret, onboarding_spn_id, file_name=file_name_connectedk8s) + + # Setting kubeconfig + if kube_config is None: + kube_config = os.getenv('KUBECONFIG') + if kube_config is None: + kube_config = os.path.join(os.path.expanduser('~'), '.kube', 'config') + + # Removing quotes from kubeconfig path + if (kube_config.startswith("'") or kube_config.startswith('"')): + kube_config = kube_config[1:] + if (kube_config.endswith("'") or kube_config.endswith('"')): + kube_config = kube_config[:-1] + + # Loading the kubeconfig file in kubernetes client configuration + configuration = kubernetes.client.Configuration() + try: + config.load_kube_config(config_file=kube_config, context=kube_context, client_configuration=configuration) + except Exception as e: + raise CLIError("Problem loading the kubeconfig file." + str(e)) + + # Checking the connection to kubernetes cluster. This check was added to avoid large timeouts when connecting to AAD Enabled AKS clusters if the user had not logged in. + api_instance = kubernetes.client.NetworkingV1Api(kubernetes.client.ApiClient(configuration)) + try: + api_response = api_instance.get_api_resources() + except ApiException as e: + print("Exception when calling NetworkingV1Api->get_api_resources: %s\n" % e) + raise CLIError("If you are using AAD Enabled cluster, check if you have logged in to the cluster properly and try again") + + + # Checking helm installation + if kube_context is None: + cmd = ["helm", "--kubeconfig", kube_config, "--debug"] + else: + cmd = ["helm", "--kubeconfig", kube_config, "--kube-context", kube_context, "--debug"] + try: + response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) + output, error = response.communicate() + if response.returncode != 0: + if "unknown flag" in error.decode("ascii"): + raise CLIError("Please install the latest version of helm") + raise CLIError(error.decode("ascii")) + except FileNotFoundError: + raise CLIError("Helm is not installed or requires elevated permissions. Please ensure that you have the latest version of helm installed on your machine.") + except subprocess.CalledProcessError as e2: + e2.output = e2.output.decode("ascii") + print(e2.output) + + # Check helm version + if kube_context is None: + cmd = ["helm", "version", "--short", "--kubeconfig", kube_config] + else: + cmd = ["helm", "version", "--short", "--kubeconfig", kube_config, "--kube-context", kube_context] + response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) + output, error = response.communicate() + if response.returncode != 0: + raise CLIError("Unable to determine helm version: " + error.decode("ascii")) + else: + if "v2" in output.decode("ascii"): + raise CLIError("Please install the latest version of helm and then try again") + + # Check Release Existance + if kube_context is None: + cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config] + else: + cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config, "--kube-context", kube_context] + response_list = subprocess.Popen(cmd_list, stdout=PIPE, stderr=PIPE) + output_list, error_list = response_list.communicate() + if response_list.returncode != 0: + raise CLIError(error_list.decode("ascii")) + else: + output_list = output_list.decode("ascii") + output_list = json.loads(output_list) + release_name_list = [] + for release in output_list: + release_name_list.append(release['name']) + if "azure-arc" in release_name_list: + # Loading config map + api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) + namespace = 'azure-arc' + try: + api_response = api_instance.list_namespaced_config_map(namespace) + except ApiException as e: + print("Exception when calling CoreV1Api->list_namespaced_config_map: %s\n" % e) + config_present = False + for configmap in api_response.items: + if configmap.metadata.name == 'azure-clusterconfig': + config_present = True + if (configmap.data["AZURE_RESOURCE_GROUP"].lower() == resource_group_name.lower() and configmap.data["AZURE_RESOURCE_NAME"].lower() == cluster_name.lower()): + raise CLIError("Agents corresponding to the provided resource are already installed on this cluster. If the connected cluster resource does not exist, run 'az connectedk8s delete -g {} -n {}' to delete the agents and then try creating again.".format(resource_group_name, cluster_name)) + else: + raise CLIError("Resource creation failed. Agents corresponding to some other resource are already installed on this cluster. Agents installed on this cluster correspond to the resource group name '{}' and resource name '{}'.".format(configmap.data["AZURE_RESOURCE_GROUP"], configmap.data["AZURE_RESOURCE_NAME"])) + if config_present is False: + raise CLIError("Helm release named 'azure-arc' is already present but the azure-arc agent pods are either missing or deployed unsuccessfully.") + + # Adding helm repo + if kube_context is None: + cmd1 = ["helm", "repo", "add", "azurearcfork8s", "https://azurearcfork8s.azurecr.io/helm/v1/repo", "--kubeconfig", kube_config] + else: + cmd1 = ["helm", "repo", "add", "azurearcfork8s", "https://azurearcfork8s.azurecr.io/helm/v1/repo", "--kubeconfig", kube_config, "--kube-context", kube_context] + response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) + output1, error1 = response1.communicate() + if response1.returncode != 0: + raise CLIError("Helm unable to add repository: " + error1.decode("ascii")) + + # Install agents + cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] + if kube_context: + cmd.extend(["--kube-context", kube_context]) + if location_data_name: + cmd.extend(["--set", "global.locationDataName={}".format(location_data_name)]) + if location_data_country_or_region: + cmd.extend(["--set", "global.locationDataCountryOrRegion={}".format(location_data_country_or_region)]) + if location_data_district: + cmd.extend(["--set", "global.locationDataDistrict={}".format(location_data_district)]) + if location_data_city: + cmd.extend(["--set", "global.locationDataCity={}".format(location_data_city)]) + + #if kube_context is None: + # cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] + #else: + # cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--kube-context", kube_context, "--output", "json"] + response4 = subprocess.Popen(cmd4, stdout=PIPE, stderr=PIPE) + output4, error4 = response4.communicate() + if response4.returncode != 0: + raise CLIError("Unable to install helm release: " + error4.decode("ascii")) + + if no_wait is True: + print("Resource creation request accepted. Please run 'kubectl get pods -n azure-arc' to see whether the pods are in a running state and run 'az connectedk8s show -g {} -n {}' to check if the resource was created successfully".format(resource_group_name, cluster_name)) + return + time.sleep(5) + print() + + # Checking pod status + api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) + namespace = 'azure-arc' + connect_agent_state = None + timeout = time.time() + 120 + found_running = 0 + while connect_agent_state is None: + if(time.time()>timeout): + break + try: + api_response = api_instance.list_namespaced_pod(namespace) + #print(api_response.items) + except ApiException as e: + print("Exception when calling CoreV1Api->list_namespaced_pod: %s\n" % e) + for pod in api_response.items: + for container_status in pod.status.container_statuses: + if container_status.name == 'connect-agent': + connect_agent_state = container_status.state.running + if connect_agent_state is not None: + found_running = found_running + 1 + time.sleep(2) + if found_running > 5: + break + else: + connect_agent_state = None + if connect_agent_state is None: + raise CLIError("There was a problem with connect-agent deployment. Please run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent' to debug the error.") + + # Checking the status of connected cluster resource + max_retry = 30 + retry_exception = Exception(None) + for _ in range(0, max_retry): + try: + return sdk_no_wait(no_wait, client.create, resource_group_name=resource_group_name, cluster_name=cluster_name) + except CloudError as ex: + retry_exception = ex + if ('not found' in ex.message or 'Not Found' in ex.message): + time.sleep(3) + else: + raise ex + if ('not found' in retry_exception.message or 'Not Found' in retry_exception.message): + raise CLIError("Resource Creation Failed. Please run 'kubectl get pods -n azure-arc' to see whether the connect agent pod is in running state. If not, run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent' to debug the error.") + else: + raise retry_exception + + +def resource_group_exists(ctx, resource_group_name, subscription_id=None): + groups = cf_resource_groups(ctx, subscription_id=subscription_id) + try: + rg = groups.get(resource_group_name) + return True + except: + return False + def erase_acs_service_principal(file_name='acsServicePrincipal.json'): config_path = os.path.join(get_config_dir(), file_name) open(config_path, 'w').close() + def load_acs_service_principal(subscription_id, file_name='acsServicePrincipal.json'): config_path = os.path.join(get_config_dir(), file_name) - #print(config_path + "load") config = load_service_principals(config_path) - #print(config) if not config: return None return config.get(subscription_id) + def load_service_principals(config_path): if not os.path.exists(config_path): return None @@ -197,6 +380,7 @@ def load_service_principals(config_path): except: # pylint: disable=bare-except return None + def store_acs_service_principal(subscription_id, client_secret, service_principal, file_name='acsServicePrincipal.json'): obj = {} @@ -215,42 +399,146 @@ def store_acs_service_principal(subscription_id, client_secret, service_principa 'w+') as spFile: json.dump(full_config, spFile) -def list_spn(client): - show_mine = True - if show_mine: - return list_owned_objects(client.signed_in_user, 'servicePrincipal') def list_owned_objects(client, object_type=None): result = client.list_owned_objects() - #print(result) if object_type: result = [r for r in result if r.object_type and r.object_type.lower() == object_type.lower()] - #print(result) return result + def get_connectedk8s(cmd, client, resource_group_name, cluster_name): return client.get(resource_group_name, cluster_name) + def list_connectedk8s(cmd, client, resource_group_name=None): - if(resource_group_name is None): + if resource_group_name is None: return client.list_by_subscription() return client.list_by_resource_group(resource_group_name) - + + def delete_connectedk8s(cmd, client, resource_group_name, cluster_name, kube_config=None, kube_context=None): - return client.delete(resource_group_name, cluster_name, kube_config, kube_context) + print("Ensure that you have the latest helm version installed before proceeding to avoid unexpected errors.") + print("This operation might take a while ...\n") + + # ARM delete Connected Cluster Resource + client.delete_cluster(resource_group_name, cluster_name) + + # Setting kubeconfig + if kube_config is None: + kube_config = os.getenv('KUBECONFIG') + if kube_config is None: + kube_config = os.path.join(os.path.expanduser('~'), '.kube', 'config') + + # Loading the kubeconfig file in kubernetes client configuration + configuration = kubernetes.client.Configuration() + try: + config.load_kube_config(config_file=kube_config, context=kube_context, client_configuration=configuration) + except Exception as e: + print("Problem loading the kubeconfig file.") + raise CLIError(e) + + # Checking the connection to kubernetes cluster. This check was added to avoid large timeouts when connecting to AAD Enabled AKS clusters if the user had not logged in. + api_instance = kubernetes.client.NetworkingV1Api(kubernetes.client.ApiClient(configuration)) + try: + api_response = api_instance.get_api_resources() + except ApiException as e: + print("Exception when calling NetworkingV1Api->get_api_resources: %s\n" % e) + raise CLIError("If you are using AAD Enabled cluster, check if you have logged in to the cluster properly and try again") + + # Checking helm installation + if kube_context is None: + cmd = ["helm", "--kubeconfig", kube_config, "--debug"] + else: + cmd = ["helm", "--kubeconfig", kube_config, "--kube-context", kube_context, "--debug"] + try: + response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) + output, error = response.communicate() + if response.returncode != 0: + if "unknown flag" in error.decode("ascii"): + raise CLIError("Please install the latest version of helm") + raise CLIError(error.decode("ascii")) + except FileNotFoundError: + raise CLIError("Helm is not installed or requires elevated permissions.") + except subprocess.CalledProcessError as e2: + e2.output = e2.output.decode("ascii") + print(e2.output) + + # Check helm version + if kube_context is None: + cmd = ["helm", "version", "--short", "--kubeconfig", kube_config] + else: + cmd = ["helm", "version", "--short", "--kubeconfig", kube_config, "--kube-context", kube_context] + response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) + output, error = response.communicate() + if response.returncode != 0: + raise CLIError("Unable to determine helm version: " + error.decode("ascii")) + else: + if "v2" in output.decode("ascii"): + raise CLIError("Please install the latest version of helm and then try again") + + # Check Release Existance + release_namespace = None + if kube_context is None: + cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config] + else: + cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config, "--kube-context", kube_context] + response_list = subprocess.Popen(cmd_list, stdout=PIPE, stderr=PIPE) + output_list, error_list = response_list.communicate() + if response_list.returncode != 0: + raise CLIError(error_list.decode("ascii")) + else: + output_list = output_list.decode("ascii") + output_list = json.loads(output_list) + for release in output_list: + if release['name'] == 'azure-arc': + release_namespace = release['namespace'] + break + if release_namespace is None: + return + + # Loading config map + api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) + namespace = 'azure-arc' + try: + api_response = api_instance.list_namespaced_config_map(namespace) + except ApiException as e: + print("Exception when calling CoreV1Api->list_namespaced_config_map: %s\n" % e) + for configmap in api_response.items: + if configmap.metadata.name == 'azure-clusterconfig': + if (configmap.data["AZURE_RESOURCE_GROUP"].lower() == resource_group_name.lower() and configmap.data["AZURE_RESOURCE_NAME"].lower() == cluster_name.lower()): + break + else: + raise CLIError("The kube config does not correspond to the connected cluster resource provided. Agents installed on this cluster correspond to the resource group name '{}' and resource name '{}'.".format(configmap.data["AZURE_RESOURCE_GROUP"], configmap.data["AZURE_RESOURCE_NAME"])) + + # Deleting the azure-arc agents + if kube_context is None: + cmd1 = ["helm", "delete", "azure-arc", "--namespace", release_namespace, "--kubeconfig", kube_config] + else: + cmd1 = ["helm", "delete", "azure-arc", "--namespace", release_namespace, "--kubeconfig", kube_config, "--kube-context", kube_context] + response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) + output1, error1 = response1.communicate() + if response1.returncode != 0: + raise CLIError("Helm release deletion failed: " + error1.decode("ascii")) + return + def update_connectedk8s(cmd, instance, tags=None): with cmd.update_context(instance) as c: c.set_param('tags', tags) return instance -def create_application(cmd, display_name, homepage=None, identifier_uris=None, # pylint: disable=too-many-locals + +def create_aad_application(cmd, display_name, homepage=None, identifier_uris=None, # pylint: disable=too-many-locals available_to_other_tenants=False, password=None, reply_urls=None, key_value=None, key_type=None, key_usage=None, start_date=None, end_date=None, oauth2_allow_implicit_flow=None, required_resource_accesses=None, native_app=None, credential_description=None, app_roles=None): graph_client = _graph_client_factory(cmd.cli_ctx) - password_creds = [PasswordCredential(start_date=start_date, end_date=end_date, key_id=str(uuid.uuid4()), value=password, custom_key_identifier=None)] + password_creds = [PasswordCredential(start_date=start_date, + end_date=end_date, key_id=str(uuid.uuid4()), + value=password, + custom_key_identifier=None)] app_create_param = ApplicationCreateParameters(available_to_other_tenants=False, display_name=display_name, identifier_uris=identifier_uris, @@ -271,6 +559,7 @@ def create_application(cmd, display_name, homepage=None, identifier_uris=None, raise return result + def _create_service_principal(cli_ctx, identifier, resolve_app=True): client = _graph_client_factory(cli_ctx) app_id = identifier @@ -290,12 +579,21 @@ def _create_service_principal(cli_ctx, identifier, resolve_app=True): return client.service_principals.create(ServicePrincipalCreateParameters(app_id=app_id, account_enabled=True)) + +def _is_guid(guid): + try: + uuid.UUID(guid) + return True + except ValueError: + return False + + def _error_caused_by_role_assignment_exists(ex): return getattr(ex, 'status_code', None) == 409 and 'role assignment already exists' in ex.message + def _create_role_assignment(cli_ctx, role, assignee, resource_group_name=None, scope=None, resolve_assignee=True, assignee_principal_type=None): - print("now") factory = _auth_client_factory(cli_ctx, scope) assignments_client = factory.role_assignments definitions_client = factory.role_definitions @@ -303,6 +601,5 @@ def _create_role_assignment(cli_ctx, role, assignee, resource_group_name=None, s role_id = '/subscriptions/{}/providers/Microsoft.Authorization/roleDefinitions/{}'.format(definitions_client.config.subscription_id, role) object_id = assignee worker = MultiAPIAdaptor(cli_ctx) - return worker.create_role_assignment(assignments_client, uuid.uuid4(), role_id, object_id, scope, - assignee_principal_type) - + return worker.create_role_assignment(assignments_client, uuid.uuid4(), role_id, + object_id, scope, assignee_principal_type) diff --git a/src/connectedk8s/azext_connectedk8s/tests/__init__.py b/src/connectedk8s/azext_connectedk8s/tests/__init__.py index 2dcf9bb68b3..99c0f28cd71 100644 --- a/src/connectedk8s/azext_connectedk8s/tests/__init__.py +++ b/src/connectedk8s/azext_connectedk8s/tests/__init__.py @@ -2,4 +2,4 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for # license information. -# ----------------------------------------------------------------------------- \ No newline at end of file +# ----------------------------------------------------------------------------- diff --git a/src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py b/src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py index 2dcf9bb68b3..99c0f28cd71 100644 --- a/src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py +++ b/src/connectedk8s/azext_connectedk8s/tests/latest/__init__.py @@ -2,4 +2,4 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for # license information. -# ----------------------------------------------------------------------------- \ No newline at end of file +# ----------------------------------------------------------------------------- diff --git a/src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml b/src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml new file mode 100644 index 00000000000..a4e7f1cb48b --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml @@ -0,0 +1,48 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - connectedk8s connect + Connection: + - keep-alive + ParameterSetName: + - -g -n + User-Agent: + - python/3.7.0 (Windows-10-10.0.18362-SP0) msrest/0.6.10 msrest_azure/0.6.2 + azure-mgmt-resource/4.0.0 Azure-SDK-For-Python AZURECLI/2.0.77 + accept-language: + - en-US + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups?api-version=2019-07-01 + response: + body: + string: '{"value":[{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_pasankav_telemetry_eastus","name":"MC_pasankav_telemetry_eastus","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/pasankav/providers/Microsoft.ContainerService/managedClusters/telemetry","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mevoraminikube","name":"mevoraminikube","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mitstesting2","name":"mitstesting2","type":"Microsoft.Resources/resourceGroups","location":"southcentralus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_pasankav_Metrics_eastus2","name":"MC_pasankav_Metrics_eastus2","type":"Microsoft.Resources/resourceGroups","location":"eastus2","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/pasankav/providers/Microsoft.ContainerService/managedClusters/Metrics","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/brkhandeRunnerTestbk10","name":"brkhandeRunnerTestbk10","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/akkesharrg3","name":"akkesharrg3","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/deepakjrg2","name":"deepakjrg2","type":"Microsoft.Resources/resourceGroups","location":"centralus","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/saharshtest","name":"saharshtest","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/AzureBackupRG_eastus_1","name":"AzureBackupRG_eastus_1","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"subscriptions/1bfbb5d0-917e-4346-9026-1d3b344417f5/providers/Microsoft.RecoveryServices/","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ansheno","name":"ansheno","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_ansheno_anshenoAadkube_eastus","name":"MC_ansheno_anshenoAadkube_eastus","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/ansheno/providers/Microsoft.ContainerService/managedClusters/anshenoAadkube","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_connectedk8s000001","name":"cli_test_connectedk8s000001","type":"Microsoft.Resources/resourceGroups","location":"westus","tags":{"product":"azurecli","cause":"automation","date":"2020-01-06T11:00:52Z"},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-WUS2","name":"DefaultResourceGroup-WUS2","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/aks","name":"aks","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/akkeshar","name":"akkeshar","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cloud-shell-storage-centralindia","name":"cloud-shell-storage-centralindia","type":"Microsoft.Resources/resourceGroups","location":"centralindia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cleanupservice","name":"cleanupservice","type":"Microsoft.Resources/resourceGroups","location":"westus","tags":{"What + Is Cleanup Service":"https://aka.ms/WhatIsCleanupService"},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-EUS","name":"DefaultResourceGroup-EUS","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/NetworkWatcherRG","name":"NetworkWatcherRG","type":"Microsoft.Resources/resourceGroups","location":"southcentralus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/PAKUNAPA-WS2-vmwrg","name":"PAKUNAPA-WS2-vmwrg","type":"Microsoft.Resources/resourceGroups","location":"southeastasia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/RAPATCHI-PC-vmwrg","name":"RAPATCHI-PC-vmwrg","type":"Microsoft.Resources/resourceGroups","location":"southeastasia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rapatchidiscovery","name":"rapatchidiscovery","type":"Microsoft.Resources/resourceGroups","location":"centralus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/GenevaWarmPathManageRG","name":"GenevaWarmPathManageRG","type":"Microsoft.Resources/resourceGroups","location":"centralindia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/deepakjrg1","name":"deepakjrg1","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/k8c-test","name":"k8c-test","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/deepakj-aks-aad","name":"deepakj-aks-aad","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_deepakj-aks-aad_myAKSCluster_eastus","name":"MC_deepakj-aks-aad_myAKSCluster_eastus","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/deepakj-aks-aad/providers/Microsoft.ContainerService/managedClusters/myAKSCluster","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/haiku-clusterrg","name":"haiku-clusterrg","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/pasankav","name":"pasankav","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/vso-rg-5724251","name":"vso-rg-5724251","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/KD-Rg","name":"KD-Rg","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_KD-Rg_KSD-AKSCluster_westus2","name":"MC_KD-Rg_KSD-AKSCluster_westus2","type":"Microsoft.Resources/resourceGroups","location":"westus2","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/KD-Rg/providers/Microsoft.ContainerService/managedClusters/KSD-AKSCluster","tags":{},"properties":{"provisioningState":"Succeeded"}}]}' + headers: + cache-control: + - no-cache + content-length: + - '8723' + content-type: + - application/json; charset=utf-8 + date: + - Mon, 06 Jan 2020 11:00:58 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +version: 1 diff --git a/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py b/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py index 8e81e27f72a..f2d7a1f9f94 100644 --- a/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py +++ b/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py @@ -19,22 +19,20 @@ class Connectedk8sScenarioTest(ScenarioTest): def test_connectedk8s(self, resource_group): self.kwargs.update({ - 'name': 'test1' + 'name': 'test1', + 'rg': resource_group }) - self.cmd('connectedk8s create -g {rg} -n {name} --tags foo=doo', checks=[ - self.check('tags.foo', 'doo'), + self.cmd('connectedk8s connect -g akkeshar3 -n {name}', checks=[ self.check('name', '{name}') ]) - self.cmd('connectedk8s update -g {rg} -n {name} --tags foo=boo', checks=[ - self.check('tags.foo', 'boo') - ]) + #self.cmd('connectedk8s update -g {rg} -n {name}', checks=[ + #]) count = len(self.cmd('connectedk8s list').get_output_in_json()) - self.cmd('connectedk8s show - {rg} -n {name}', checks=[ + self.cmd('connectedk8s show -g {rg} -n {name}', checks=[ self.check('name', '{name}'), - self.check('resourceGroup', '{rg}'), - self.check('tags.foo', 'boo') + self.check('resourceGroup', '{rg}') ]) - self.cmd('connectedk8s delete -g {rg} -n {name}') + self.cmd('connectedk8s delete -g {rg} -n {name} --yes') final_count = len(self.cmd('connectedk8s list').get_output_in_json()) - self.assertTrue(final_count, count - 1) \ No newline at end of file + self.assertTrue(final_count, count - 1) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py index f06c500aebb..463297cde00 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py @@ -34,7 +34,6 @@ class K8ConnectRPConfiguration(AzureConfiguration): def __init__( self, credentials, subscription_id, base_url=None): - if credentials is None: raise ValueError("Parameter 'credentials' must not be None.") if subscription_id is None: @@ -78,7 +77,7 @@ def __init__( super(K8ConnectRP, self).__init__(self.config.credentials, self.config) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} - self.api_version = '2019-09-01-privatepreview' + self.api_version = '2020-01-01-preview' self._serialize = Serializer(client_models) self._deserialize = Deserializer(client_models) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py index ed84a7d4e36..5623577131e 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py @@ -19,6 +19,10 @@ from subprocess import Popen, PIPE import os.path, json from knack.util import CLIError +from msrest.polling import LROPoller, NoPolling +from msrestazure.polling.arm_polling import ARMPolling +from msrestazure.azure_exceptions import CloudError + class ConnectedClusterOperations(object): """ConnectedClusterOperations operations. @@ -38,53 +42,40 @@ def __init__(self, client, config, serializer, deserializer): self._serialize = serializer self._deserialize = deserializer self.api_version = "2019-09-01-privatepreview" + #self.api_version = "2020-01-01-preview" self.config = config def create( - self, resource_group_name, cluster_name, onboarding_tenant_id, onboarding_spn_id, onboarding_spn_secret, location, kube_config, kube_context, custom_headers=None, raw=False, **operation_config): - subscription_id = self.config.subscription_id - if ((kube_config is not None and os.path.isfile(kube_config) is False) or kube_config is None): - print("The kube config file does not exist or the Path is invalid. Using default kube config ...") - kube_config = os.getenv('KUBECONFIG') - if(kube_config is None): - kube_config = "C:\\Users\\akkeshar\\.kube\\config" - contexts = config.list_kube_config_contexts(config_file=kube_config) - contexts_list = [] - for ctxs in contexts[0]: - contexts_list.append(ctxs.get('name')) - if(kube_context not in contexts_list): - print("The kube context does on exist. Using default context ...") - kube_context = contexts[1].get('name') - - cmd1 = "helm repo add haikupreview https://haikupreview.azurecr.io/helm/v1/repo" - response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) - output1, error1 = response1.communicate() - if response1.returncode != 0: - raise CLIError(error1) - else: - print(output1) + self, resource_group_name, cluster_name, custom_headers=None, raw=False, polling=True, **operation_config): + + raw_result = self.get_cluster( + resource_group_name=resource_group_name, + cluster_name=cluster_name, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + def get_long_running_output(response): + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + response = LROPoller(self._client, raw_result, get_long_running_output, polling_method) + if polling is False: + print("Resource creation request accepted. Please run 'kubectl get pods -n azure-arc' to see whether the pods are in a running state and run 'az connectedk8s show -g {} -n {}' to check if the resource was created successfully".format(resource_group_name, cluster_name)) + return response - cmd2 = "helm fetch haikupreview/haiku-agents" - response2 = subprocess.Popen(cmd2, stdout=PIPE, stderr=PIPE) - output2, error2 = response2.communicate() - if response2.returncode != 0: - raise CLIError(error2) - else: - print(output2) - - env3 = os.environ.copy() - env3["KUBECONFIG"] = kube_config - env3["HELM_KUBECONTEXT"] = kube_context - cmd3 = "helm install haiku haiku-agents-0.1.7.tgz --set global.subscriptionId={} --set global.resourceGroupName={} --set global.resourceName={} --set global.location={} --set global.tenantId={} --set global.clientId={} --set global.clientSecret={} --output json".format(subscription_id, resource_group_name, cluster_name, location, onboarding_tenant_id, onboarding_spn_id, onboarding_spn_secret) - response3 = subprocess.Popen(cmd3, env=env3, stdout=PIPE, stderr=PIPE) - output3, error3 = response3.communicate() - if response3.returncode != 0: - raise CLIError(error3) - else: - #get_res = self.get(cluster_name, resource_group_name) - print("Haiku agents deployed successfully") - return """Registers a new K8s cluster. API to register a new K8s cluster and thereby create a tracked resource @@ -256,7 +247,6 @@ def get( :raises: :class:`ErrorResponseException` """ - #print("test2") # Construct URL url = self.get.metadata['url'] path_format_arguments = { @@ -265,7 +255,7 @@ def get( 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') } url = self._client.format_url(url, **path_format_arguments) - print(url) + # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -299,13 +289,76 @@ def get( return deserialized get.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + def get_cluster( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Gets data of the specified cluster. + + API to get the properties of a specific registered K8s cluster. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: ConnectedCluster or ClientRawResponse if raw=true + :rtype: ~azure.mgmt.hybridkubernetes.models.ConnectedCluster or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.get.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + # Construct headers + header_parameters = {} + header_parameters['Accept'] = 'application/json' + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.get(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + + if response.status_code not in [200]: + exp = CloudError(response) + exp.request_id = response.headers.get('x-ms-request-id') + #raise exp + #raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + + def delete( - self, resource_group_name, cluster_name, kube_config, kube_context, custom_headers=None, raw=False, **operation_config): - #print("test") - #return - #cmd_delete_agent = "helm delete haiku" - #res_delete_agent = os.system(cmd_delete_agent) - #print(res_delete_agent) + self, resource_group_name, cluster_name, custom_headers=None, raw=False, polling=True, **operation_config): """Deletes a specified cluster. API to delete an existing K8s cluster being tracked. @@ -326,6 +379,7 @@ def delete( :raises: :class:`ErrorResponseException` """ + """# Construct Cluster Config URL url_config = self.delete.metadata['url'] url = url_config + "/providers/Microsoft.KubernetesConfiguration/sourceControlConfigurations" path_format_arguments = { @@ -334,23 +388,21 @@ def delete( 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') } url_config = self._client.format_url(url, **path_format_arguments) - print(url_config) + # Construct cluster config parameters query_parameters_config = {} query_parameters_config['api-version'] = "2019-11-01-preview" - print(query_parameters_config) - + # Get all configs request_config = self._client.get(url_config, query_parameters_config) response_config = self._client.send(request_config, stream=False, **operation_config) - print(response_config.status_code) - #print("test") + if response_config.status_code not in [200]: raise models.ErrorResponseException(self._deserialize, response_config) if response_config.status_code == 200: - #print(response_config['name']) deserialized = self._deserialize('ConnectedCluster', response_config) + # Delete all configs for conf in deserialized.additional_properties['value']: temp_req = self._client.delete(url_config+"/"+conf['name'], query_parameters_config) temp_res = self._client.send(temp_req, stream=False, **operation_config) @@ -358,28 +410,24 @@ def delete( raise models.ErrorResponseException(self._deserialize, temp_res) print("All configs deleted") - #print(deserialized[1].name) - #print("config_deleted") + # Check if the resource exists # Construct URL - url = self.delete.metadata['url'] + url = self.get.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') } url = self._client.format_url(url, **path_format_arguments) - print(url) - #url = url + "/providers/Microsoft.KubernetesConfiguration/sourceControlConfigurations/cluster2" - #print(url) - + # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') - print(query_parameters) # Construct headers header_parameters = {} + header_parameters['Accept'] = 'application/json' if self.config.generate_client_request_id: header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) if custom_headers: @@ -388,38 +436,120 @@ def delete( header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') # Construct and send request - request = self._client.delete(url, query_parameters, header_parameters) + request = self._client.get(url, query_parameters, header_parameters) response = self._client.send(request, stream=False, **operation_config) - if response.status_code not in [200, 204]: + if response.status_code not in [200]: raise models.ErrorResponseException(self._deserialize, response) - - if ((kube_config is not None and os.path.isfile(kube_config) is False) or kube_config is None): - print("The kube config file does not exist or the Path is invalid. Using default kube config ...") + # Resource existance check completed""" + + """# Setting kubeconfig + if kube_config is None: kube_config = os.getenv('KUBECONFIG') - if(kube_config is None): - kube_config = "C:\\Users\\akkeshar\\.kube\\config" - contexts = config.list_kube_config_contexts(config_file=kube_config) - contexts_list = [] - for ctxs in contexts[0]: - contexts_list.append(ctxs.get('name')) - if(kube_context not in contexts_list): - print("The kube context does on exist. Using default context ...") - kube_context = contexts[1].get('name') - - env = os.environ.copy() - env["KUBECONFIG"] = kube_config - env["HELM_KUBECONTEXT"] = kube_context - cmd = "helm delete haiku" - response_del = subprocess.Popen(cmd, env=env, stdout=PIPE, stderr=PIPE) - output, error = response_del.communicate() - if response_del.returncode != 0: - raise CLIError(error) + if kube_config is None: + kube_config = os.path.join(os.path.expanduser('~'), '.kube', 'config') + + # Checking helm installation + if kube_context is None: + cmd = "helm --kubeconfig {} --debug".format(kube_config) + else: + cmd = "helm --kubeconfig {} --kube-context {} --debug".format(kube_config, kube_context) + try: + response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) + output, error = response.communicate() + if response.returncode != 0: + raise CLIError(error) + #else: + # print("Helm is installed") + except FileNotFoundError: + raise CLIError("Helm is not installed or requires elevated permissions") + except subprocess.CalledProcessError as e2: + print(e2.output) + + # Confirm the helm release to be uninstalled + if kube_context is None: + print("The helm release deployment for the config {} and the current context will be deleted".format(kube_config)) + action = input ("Do you wish to continue? (y/n): ") + if(action != 'y'): + raise CLIError("Operation terminated") + else: + print("The helm release deployment for the config {} and the context {} will be deleted".format(kube_config, kube_context)) + action = input ("Do you wish to continue? (y/n): ") + if(action != 'y'): + raise CLIError("Operation terminated")""" + + + # Deleting the connected cluster resource: + + raw_result = self.delete_cluster( + resource_group_name=resource_group_name, + cluster_name=cluster_name, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + + def get_long_running_output(response): + if raw: + client_raw_response = ClientRawResponse(None, response) + return client_raw_response + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + + # Connected cluster deletion completed + + """# Deleting the haiku agents + if kube_context is None: + cmd1 = "helm delete haiku --kubeconfig {} --debug".format(kube_config) else: - #get_res = self.get(cluster_name, resource_group_name) - print(output) + cmd1 = "helm delete haiku --kubeconfig {} --kube-context {} --debug".format(kube_config, kube_context) + response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) + output1, error1 = response1.communicate() + if response1.returncode != 0: + raise CLIError(error1) + else: + print(output1)""" + delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + + def delete_cluster( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + # Construct cluster URL + url = self.delete.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct cluster parameters + query_parameters = {} + query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') + + # Construct cluster headers + header_parameters = {} + if self.config.generate_client_request_id: + header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) + if custom_headers: + header_parameters.update(custom_headers) + if self.config.accept_language is not None: + header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + + # Construct and send request + request = self._client.delete(url, query_parameters, header_parameters) + response = self._client.send(request, stream=False, **operation_config) + #print(response.status_code) + + if response.status_code not in [200, 204]: + raise models.ErrorResponseException(self._deserialize, response) if raw: client_raw_response = ClientRawResponse(None, response) return client_raw_response @@ -525,7 +655,6 @@ def internal_paging(next_link=None, raw=False): 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str') } url = self._client.format_url(url, **path_format_arguments) - print(url) # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -546,9 +675,7 @@ def internal_paging(next_link=None, raw=False): # Construct and send request request = self._client.get(url, query_parameters, header_parameters) - print("1") response = self._client.send(request, stream=False, **operation_config) - print(response) if response.status_code not in [200]: raise models.ErrorResponseException(self._deserialize, response) @@ -592,7 +719,6 @@ def internal_paging(next_link=None, raw=False): 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str') } url = self._client.format_url(url, **path_format_arguments) - print(url) # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -614,7 +740,6 @@ def internal_paging(next_link=None, raw=False): # Construct and send request request = self._client.get(url, query_parameters, header_parameters) response = self._client.send(request, stream=False, **operation_config) - print(response) if response.status_code not in [200]: raise CLIError(models.ErrorResponseException(self._deserialize, response)) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py index e0ec669828c..ff98863cf45 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py @@ -1,13 +1,8 @@ # coding=utf-8 # -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# # Code generated by Microsoft (R) AutoRest Code Generator. # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- -VERSION = "0.1.0" - +VERSION = "2020-01-01-preview" diff --git a/src/connectedk8s/setup.py b/src/connectedk8s/setup.py index a3b89d76a8c..55225296f0f 100644 --- a/src/connectedk8s/setup.py +++ b/src/connectedk8s/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.1.0' +VERSION = '0.1.1' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers @@ -36,7 +36,9 @@ # TODO: Add any additional SDK dependencies here DEPENDENCIES = [ - 'azure-cli-core' + 'azure-cli-core', + 'kubernetes~=11.0.0b2', + 'knack' ] with open('README.rst', 'r', encoding='utf-8') as f: @@ -50,7 +52,7 @@ description='Microsoft Azure Command-Line Tools Connectedk8s Extension', # TODO: Update author and email, if applicable author='Microsoft Corporation', - author_email='azpycli@microsoft.com', + author_email='k8connect@microsoft.com', # TODO: consider pointing directly to your source code instead of the generic repo url='https://github.com/Azure/azure-cli-extensions', long_description=README + '\n\n' + HISTORY, @@ -59,4 +61,4 @@ packages=find_packages(), install_requires=DEPENDENCIES, package_data={'azext_connectedk8s': ['azext_metadata.json']}, -) \ No newline at end of file +) From e781176885052b0e60e067ea8e8f8ea12ee3135b Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Thu, 12 Mar 2020 00:51:25 +0530 Subject: [PATCH 04/58] Added the 0.1.2 version CLI code in master --- .../azext_connectedk8s/__init__.py | 2 +- .../azext_connectedk8s/_params.py | 14 +++--- src/connectedk8s/azext_connectedk8s/custom.py | 42 ++++------------ .../latest/recordings/test_connectedk8s.yaml | 48 ------------------- .../latest/test_connectedk8s_scenario.py | 18 +++---- .../vendored_sdks/k8_connect_rp.py | 3 +- .../connected_cluster_operations.py | 1 - .../vendored_sdks/version.py | 7 ++- src/connectedk8s/setup.py | 2 +- 9 files changed, 35 insertions(+), 102 deletions(-) delete mode 100644 src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml diff --git a/src/connectedk8s/azext_connectedk8s/__init__.py b/src/connectedk8s/azext_connectedk8s/__init__.py index 3b8ce055503..d6aab5202db 100644 --- a/src/connectedk8s/azext_connectedk8s/__init__.py +++ b/src/connectedk8s/azext_connectedk8s/__init__.py @@ -5,7 +5,7 @@ from azure.cli.core import AzCommandsLoader -#from azext_connectedk8s._help import helps # pylint: disable=unused-import +from azext_connectedk8s._help import helps # pylint: disable=unused-import class Connectedk8sCommandsLoader(AzCommandsLoader): diff --git a/src/connectedk8s/azext_connectedk8s/_params.py b/src/connectedk8s/azext_connectedk8s/_params.py index 855c3d7df86..e0a63b4fb7e 100644 --- a/src/connectedk8s/azext_connectedk8s/_params.py +++ b/src/connectedk8s/azext_connectedk8s/_params.py @@ -9,16 +9,14 @@ def load_arguments(self, _): - from azure.cli.core.commands.parameters import tags_type, get_resource_name_completion_list, name_type + from azure.cli.core.commands.parameters import tags_type - #cluster_name_type = CLIArgumentType(options_list='--cluster-name-name', help='Name of the Connected Kubernetes Cluster.', id_part='name') + cluster_name_type = CLIArgumentType(options_list='--cluster-name-name', help='Name of the Connectedk8s.', id_part='name') with self.argument_context('connectedk8s') as c: - c.argument('resource_group_name', name_type, help='Name of the resource group.', - completer=get_resource_name_completion_list('Microsoft.Kubernetes/ConnectedClusters'), options_list=['--resource-group', '-g']) c.argument('tags', tags_type) - c.argument('cluster_name', name_type, help='Name of the connected cluster.', - completer=get_resource_name_completion_list('Microsoft.Kubernetes/ConnectedClusters'), options_list=['--name', '-n']) + # c.argument('location', validator=get_default_location_from_resource_group) + c.argument('cluster_name', cluster_name_type, options_list=['--name', '-n']) - #with self.argument_context('connectedk8s list') as c: - # c.argument('cluster_name', cluster_name_type, id_part=None, options_list=['--name', '-n']) + with self.argument_context('connectedk8s list') as c: + c.argument('cluster_name', cluster_name_type, id_part=None) diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 158d7c3b2bd..1fd3bb80777 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -29,30 +29,18 @@ import uuid import datetime import time -from applicationinsights import TelemetryClient logger = get_logger(__name__) -APP_KEY = '9a93ae7c-eaf8-4e21-a1f2-6a424cc48a44' def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboarding_spn_id=None, onboarding_spn_secret=None, - location=None, kube_config=None, kube_context=None, no_wait=False, - location_data_name=None, location_data_country_or_region=None, - location_data_district=None, location_data_city=None): + location=None, kube_config=None, kube_context=None, no_wait=False,): print("Ensure that you have the latest helm version installed before proceeding to avoid unexpected errors.") + return print("This operation might take a while...\n") - tc = TelemetryClient(APP_KEY) - tc.track_event('testEvent') - tc.flush() - - # Checking location data info - if location_data_name is None: - if ((location_data_country_or_region is not None) or (location_data_district is not None) or (location_data_city is not None)): - raise CLIError("--location-data-name is required when providing location data info.") - # Setting subscription id subscription_id = get_subscription_id(cmd.cli_ctx) @@ -63,7 +51,7 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, try: location = resourceClient.resource_groups.get(resource_group_name).location except: - raise CLIError("Resource Group Creation Failed. Please provide location to create the Resource Group") + raise CLIError("The provided resource group does not exist. Please provide location to create the Resource Group") rp_locations = [] providerDetails = resourceClient.providers.get('Microsoft.Kubernetes') @@ -79,7 +67,7 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, try: resourceClient.resource_groups.create_or_update(resource_group_name, resource_group_params) except Exception as e: - raise CLIError("Resource Group Creation Failed." + str(e.message)) + raise CLIError("Resource Group Creation Failed." + str(e.message)) # SPN creation graph_client = _graph_client_factory(cmd.cli_ctx) @@ -108,7 +96,7 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboarding_spn_id = principal_obj.get('service_principal') onboarding_spn_secret = principal_obj.get('client_secret') else: - #print("Creating New SPN ...") + # Creating New SPN graph_client = _graph_client_factory(cmd.cli_ctx) role_client = _auth_client_factory(cmd.cli_ctx).role_assignments scopes = ['/subscriptions/' + role_client.config.subscription_id] @@ -275,22 +263,10 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, raise CLIError("Helm unable to add repository: " + error1.decode("ascii")) # Install agents - cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] - if kube_context: - cmd.extend(["--kube-context", kube_context]) - if location_data_name: - cmd.extend(["--set", "global.locationDataName={}".format(location_data_name)]) - if location_data_country_or_region: - cmd.extend(["--set", "global.locationDataCountryOrRegion={}".format(location_data_country_or_region)]) - if location_data_district: - cmd.extend(["--set", "global.locationDataDistrict={}".format(location_data_district)]) - if location_data_city: - cmd.extend(["--set", "global.locationDataCity={}".format(location_data_city)]) - - #if kube_context is None: - # cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] - #else: - # cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--kube-context", kube_context, "--output", "json"] + if kube_context is None: + cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] + else: + cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--kube-context", kube_context, "--output", "json"] response4 = subprocess.Popen(cmd4, stdout=PIPE, stderr=PIPE) output4, error4 = response4.communicate() if response4.returncode != 0: diff --git a/src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml b/src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml deleted file mode 100644 index a4e7f1cb48b..00000000000 --- a/src/connectedk8s/azext_connectedk8s/tests/latest/recordings/test_connectedk8s.yaml +++ /dev/null @@ -1,48 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - CommandName: - - connectedk8s connect - Connection: - - keep-alive - ParameterSetName: - - -g -n - User-Agent: - - python/3.7.0 (Windows-10-10.0.18362-SP0) msrest/0.6.10 msrest_azure/0.6.2 - azure-mgmt-resource/4.0.0 Azure-SDK-For-Python AZURECLI/2.0.77 - accept-language: - - en-US - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups?api-version=2019-07-01 - response: - body: - string: '{"value":[{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_pasankav_telemetry_eastus","name":"MC_pasankav_telemetry_eastus","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/pasankav/providers/Microsoft.ContainerService/managedClusters/telemetry","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mevoraminikube","name":"mevoraminikube","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mitstesting2","name":"mitstesting2","type":"Microsoft.Resources/resourceGroups","location":"southcentralus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_pasankav_Metrics_eastus2","name":"MC_pasankav_Metrics_eastus2","type":"Microsoft.Resources/resourceGroups","location":"eastus2","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/pasankav/providers/Microsoft.ContainerService/managedClusters/Metrics","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/brkhandeRunnerTestbk10","name":"brkhandeRunnerTestbk10","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/akkesharrg3","name":"akkesharrg3","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/deepakjrg2","name":"deepakjrg2","type":"Microsoft.Resources/resourceGroups","location":"centralus","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/saharshtest","name":"saharshtest","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/AzureBackupRG_eastus_1","name":"AzureBackupRG_eastus_1","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"subscriptions/1bfbb5d0-917e-4346-9026-1d3b344417f5/providers/Microsoft.RecoveryServices/","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ansheno","name":"ansheno","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_ansheno_anshenoAadkube_eastus","name":"MC_ansheno_anshenoAadkube_eastus","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/ansheno/providers/Microsoft.ContainerService/managedClusters/anshenoAadkube","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_connectedk8s000001","name":"cli_test_connectedk8s000001","type":"Microsoft.Resources/resourceGroups","location":"westus","tags":{"product":"azurecli","cause":"automation","date":"2020-01-06T11:00:52Z"},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-WUS2","name":"DefaultResourceGroup-WUS2","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/aks","name":"aks","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/akkeshar","name":"akkeshar","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cloud-shell-storage-centralindia","name":"cloud-shell-storage-centralindia","type":"Microsoft.Resources/resourceGroups","location":"centralindia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cleanupservice","name":"cleanupservice","type":"Microsoft.Resources/resourceGroups","location":"westus","tags":{"What - Is Cleanup Service":"https://aka.ms/WhatIsCleanupService"},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/DefaultResourceGroup-EUS","name":"DefaultResourceGroup-EUS","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/NetworkWatcherRG","name":"NetworkWatcherRG","type":"Microsoft.Resources/resourceGroups","location":"southcentralus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/PAKUNAPA-WS2-vmwrg","name":"PAKUNAPA-WS2-vmwrg","type":"Microsoft.Resources/resourceGroups","location":"southeastasia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/RAPATCHI-PC-vmwrg","name":"RAPATCHI-PC-vmwrg","type":"Microsoft.Resources/resourceGroups","location":"southeastasia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rapatchidiscovery","name":"rapatchidiscovery","type":"Microsoft.Resources/resourceGroups","location":"centralus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/GenevaWarmPathManageRG","name":"GenevaWarmPathManageRG","type":"Microsoft.Resources/resourceGroups","location":"centralindia","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/deepakjrg1","name":"deepakjrg1","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/k8c-test","name":"k8c-test","type":"Microsoft.Resources/resourceGroups","location":"eastus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/deepakj-aks-aad","name":"deepakj-aks-aad","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_deepakj-aks-aad_myAKSCluster_eastus","name":"MC_deepakj-aks-aad_myAKSCluster_eastus","type":"Microsoft.Resources/resourceGroups","location":"eastus","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/deepakj-aks-aad/providers/Microsoft.ContainerService/managedClusters/myAKSCluster","tags":{},"properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/haiku-clusterrg","name":"haiku-clusterrg","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/pasankav","name":"pasankav","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/vso-rg-5724251","name":"vso-rg-5724251","type":"Microsoft.Resources/resourceGroups","location":"eastus","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/KD-Rg","name":"KD-Rg","type":"Microsoft.Resources/resourceGroups","location":"westus2","properties":{"provisioningState":"Succeeded"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_KD-Rg_KSD-AKSCluster_westus2","name":"MC_KD-Rg_KSD-AKSCluster_westus2","type":"Microsoft.Resources/resourceGroups","location":"westus2","managedBy":"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/KD-Rg/providers/Microsoft.ContainerService/managedClusters/KSD-AKSCluster","tags":{},"properties":{"provisioningState":"Succeeded"}}]}' - headers: - cache-control: - - no-cache - content-length: - - '8723' - content-type: - - application/json; charset=utf-8 - date: - - Mon, 06 Jan 2020 11:00:58 GMT - expires: - - '-1' - pragma: - - no-cache - strict-transport-security: - - max-age=31536000; includeSubDomains - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -version: 1 diff --git a/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py b/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py index f2d7a1f9f94..37bde2d024c 100644 --- a/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py +++ b/src/connectedk8s/azext_connectedk8s/tests/latest/test_connectedk8s_scenario.py @@ -19,20 +19,22 @@ class Connectedk8sScenarioTest(ScenarioTest): def test_connectedk8s(self, resource_group): self.kwargs.update({ - 'name': 'test1', - 'rg': resource_group + 'name': 'test1' }) - self.cmd('connectedk8s connect -g akkeshar3 -n {name}', checks=[ + self.cmd('connectedk8s create -g {rg} -n {name} --tags foo=doo', checks=[ + self.check('tags.foo', 'doo'), self.check('name', '{name}') ]) - #self.cmd('connectedk8s update -g {rg} -n {name}', checks=[ - #]) + self.cmd('connectedk8s update -g {rg} -n {name} --tags foo=boo', checks=[ + self.check('tags.foo', 'boo') + ]) count = len(self.cmd('connectedk8s list').get_output_in_json()) - self.cmd('connectedk8s show -g {rg} -n {name}', checks=[ + self.cmd('connectedk8s show - {rg} -n {name}', checks=[ self.check('name', '{name}'), - self.check('resourceGroup', '{rg}') + self.check('resourceGroup', '{rg}'), + self.check('tags.foo', 'boo') ]) - self.cmd('connectedk8s delete -g {rg} -n {name} --yes') + self.cmd('connectedk8s delete -g {rg} -n {name}') final_count = len(self.cmd('connectedk8s list').get_output_in_json()) self.assertTrue(final_count, count - 1) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py index 463297cde00..f06c500aebb 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py @@ -34,6 +34,7 @@ class K8ConnectRPConfiguration(AzureConfiguration): def __init__( self, credentials, subscription_id, base_url=None): + if credentials is None: raise ValueError("Parameter 'credentials' must not be None.") if subscription_id is None: @@ -77,7 +78,7 @@ def __init__( super(K8ConnectRP, self).__init__(self.config.credentials, self.config) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} - self.api_version = '2020-01-01-preview' + self.api_version = '2019-09-01-privatepreview' self._serialize = Serializer(client_models) self._deserialize = Deserializer(client_models) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py index 5623577131e..a9a1eed01ba 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py @@ -42,7 +42,6 @@ def __init__(self, client, config, serializer, deserializer): self._serialize = serializer self._deserialize = deserializer self.api_version = "2019-09-01-privatepreview" - #self.api_version = "2020-01-01-preview" self.config = config diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py index ff98863cf45..e0ec669828c 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py @@ -1,8 +1,13 @@ # coding=utf-8 # -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# # Code generated by Microsoft (R) AutoRest Code Generator. # Changes may cause incorrect behavior and will be lost if the code is # regenerated. # -------------------------------------------------------------------------- -VERSION = "2020-01-01-preview" +VERSION = "0.1.0" + diff --git a/src/connectedk8s/setup.py b/src/connectedk8s/setup.py index 55225296f0f..c9fa0dca63d 100644 --- a/src/connectedk8s/setup.py +++ b/src/connectedk8s/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.1.1' +VERSION = '0.1.2' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From 969a45fb2411e4b68ed4788263b8202b6b7ea245 Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Thu, 12 Mar 2020 01:18:51 +0530 Subject: [PATCH 05/58] Removed a print statement --- src/connectedk8s/azext_connectedk8s/custom.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 1fd3bb80777..b5120f19459 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -38,7 +38,6 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboarding_spn_id=None, onboarding_spn_secret=None, location=None, kube_config=None, kube_context=None, no_wait=False,): print("Ensure that you have the latest helm version installed before proceeding to avoid unexpected errors.") - return print("This operation might take a while...\n") # Setting subscription id From d60bd23ea65776a346e279590236510d919cbf66 Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Thu, 12 Mar 2020 18:22:59 +0530 Subject: [PATCH 06/58] Fixed container status check --- .../azext_connectedk8s/azext_metadata.json | 3 +- src/connectedk8s/azext_connectedk8s/custom.py | 50 ++++++++++++++----- src/connectedk8s/setup.py | 2 +- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/azext_metadata.json b/src/connectedk8s/azext_connectedk8s/azext_metadata.json index 4060dd78264..55c81bf3328 100644 --- a/src/connectedk8s/azext_connectedk8s/azext_metadata.json +++ b/src/connectedk8s/azext_connectedk8s/azext_metadata.json @@ -1,5 +1,4 @@ { "azext.isPreview": true, - "azext.minCliCoreVersion": "2.0.67", - "azext.maxCliCoreVersion": "2.1.0" + "azext.minCliCoreVersion": "2.0.67" } \ No newline at end of file diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index b5120f19459..435b0332f49 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -274,14 +274,35 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, if no_wait is True: print("Resource creation request accepted. Please run 'kubectl get pods -n azure-arc' to see whether the pods are in a running state and run 'az connectedk8s show -g {} -n {}' to check if the resource was created successfully".format(resource_group_name, cluster_name)) return - time.sleep(5) - print() - - # Checking pod status + + # Checking availability of connect agent containers + api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) + namespace = 'azure-arc' + timeout = time.time() + 180 + container_available = False + while container_available is False: + if(time.time()>timeout): + break + try: + api_response = api_instance.list_namespaced_pod(namespace) + except ApiException as e: + print("Exception when calling CoreV1Api->list_namespaced_pod: %s\n" % e) + for pod in api_response.items: + if pod.metadata.name.startswith('connect-agent'): + if pod.status.container_statuses is not None: + container_available = True + break + else: + time.sleep(5) + break + if container_available is False: + raise CLIError("Unable to get container status of the connect agent pod. Please run 'kubectl get pods -n azure-arc' to check whether pods are in running state.") + + # Checking container status of connect agent pod api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) namespace = 'azure-arc' connect_agent_state = None - timeout = time.time() + 120 + timeout = time.time() + 300 found_running = 0 while connect_agent_state is None: if(time.time()>timeout): @@ -292,18 +313,21 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, except ApiException as e: print("Exception when calling CoreV1Api->list_namespaced_pod: %s\n" % e) for pod in api_response.items: - for container_status in pod.status.container_statuses: - if container_status.name == 'connect-agent': - connect_agent_state = container_status.state.running - if connect_agent_state is not None: - found_running = found_running + 1 - time.sleep(2) + if pod.metadata.name.startswith('connect-agent'): + for container_status in pod.status.container_statuses: + if container_status.name == 'connect-agent': + connect_agent_state = container_status.state.running + if connect_agent_state is not None: + found_running = found_running + 1 + time.sleep(3) + break + break if found_running > 5: break else: connect_agent_state = None if connect_agent_state is None: - raise CLIError("There was a problem with connect-agent deployment. Please run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent' to debug the error.") + raise CLIError("There was a problem with connect-agent deployment. Please run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent -c connect-agent' to debug the error.") # Checking the status of connected cluster resource max_retry = 30 @@ -318,7 +342,7 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, else: raise ex if ('not found' in retry_exception.message or 'Not Found' in retry_exception.message): - raise CLIError("Resource Creation Failed. Please run 'kubectl get pods -n azure-arc' to see whether the connect agent pod is in running state. If not, run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent' to debug the error.") + raise CLIError("Resource Creation Failed. Please run 'kubectl get pods -n azure-arc' to see whether the connect agent pod is in running state. If not, run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent -c connect-agent' to debug the error.") else: raise retry_exception diff --git a/src/connectedk8s/setup.py b/src/connectedk8s/setup.py index c9fa0dca63d..7fab802e19a 100644 --- a/src/connectedk8s/setup.py +++ b/src/connectedk8s/setup.py @@ -16,7 +16,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.1.2' +VERSION = '0.1.3' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From 45e2db77b59deab12339b2be76d57819371df097 Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Fri, 13 Mar 2020 19:54:23 +0530 Subject: [PATCH 07/58] Updated pod status check --- src/connectedk8s/azext_connectedk8s/custom.py | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 435b0332f49..c67ff664ad2 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -290,6 +290,8 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, for pod in api_response.items: if pod.metadata.name.startswith('connect-agent'): if pod.status.container_statuses is not None: + # Checking container status of connect agent pod + check_pod_status(api_instance=api_instance, namespace=namespace, configuration=configuration) container_available = True break else: @@ -298,37 +300,6 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, if container_available is False: raise CLIError("Unable to get container status of the connect agent pod. Please run 'kubectl get pods -n azure-arc' to check whether pods are in running state.") - # Checking container status of connect agent pod - api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) - namespace = 'azure-arc' - connect_agent_state = None - timeout = time.time() + 300 - found_running = 0 - while connect_agent_state is None: - if(time.time()>timeout): - break - try: - api_response = api_instance.list_namespaced_pod(namespace) - #print(api_response.items) - except ApiException as e: - print("Exception when calling CoreV1Api->list_namespaced_pod: %s\n" % e) - for pod in api_response.items: - if pod.metadata.name.startswith('connect-agent'): - for container_status in pod.status.container_statuses: - if container_status.name == 'connect-agent': - connect_agent_state = container_status.state.running - if connect_agent_state is not None: - found_running = found_running + 1 - time.sleep(3) - break - break - if found_running > 5: - break - else: - connect_agent_state = None - if connect_agent_state is None: - raise CLIError("There was a problem with connect-agent deployment. Please run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent -c connect-agent' to debug the error.") - # Checking the status of connected cluster resource max_retry = 30 retry_exception = Exception(None) @@ -405,6 +376,35 @@ def list_owned_objects(client, object_type=None): result = [r for r in result if r.object_type and r.object_type.lower() == object_type.lower()] return result +def check_pod_status(api_instance, namespace, configuration): + connect_agent_state = None + timeout = time.time() + 300 + found_running = 0 + while connect_agent_state is None: + if(time.time()>timeout): + break + try: + api_response = api_instance.list_namespaced_pod(namespace) + #print(api_response.items) + except ApiException as e: + print("Exception when calling CoreV1Api->list_namespaced_pod: %s\n" % e) + for pod in api_response.items: + if pod.metadata.name.startswith('connect-agent'): + for container_status in pod.status.container_statuses: + if container_status.name == 'connect-agent': + connect_agent_state = container_status.state.running + if connect_agent_state is not None: + found_running = found_running + 1 + time.sleep(3) + break + break + if found_running > 5: + break + else: + connect_agent_state = None + if connect_agent_state is None: + raise CLIError("There was a problem with connect-agent deployment. Please run 'kubectl -n azure-arc logs -l app.kubernetes.io/component=connect-agent -c connect-agent' to debug the error.") + def get_connectedk8s(cmd, client, resource_group_name, cluster_name): return client.get(resource_group_name, cluster_name) From 85aa7d919132433d1d5414af2d719f1dd71c6a4f Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Wed, 25 Mar 2020 04:04:47 +0530 Subject: [PATCH 08/58] Added 2020 version models and operations --- .../vendored_sdks/version.py | 2 +- src/connectedk8s/azext_connectedk8s/_help.py | 15 + src/connectedk8s/azext_connectedk8s/custom.py | 179 ++++---- .../vendored_sdks/k8_connect_rp.py | 2 +- .../vendored_sdks/models/__init__.py | 19 +- .../vendored_sdks/models/connected_cluster.py | 22 +- .../connected_cluster_aad_access_profile.py | 52 --- ...onnected_cluster_aad_access_profile_py3.py | 52 --- .../connected_cluster_access_profile.py | 56 --- .../connected_cluster_access_profile_py3.py | 56 --- .../models/connected_cluster_identity.py | 20 +- .../models/connected_cluster_identity_py3.py | 22 +- .../models/connected_cluster_proxy_profile.py | 29 ++ .../connected_cluster_proxy_profile_py3.py | 29 ++ .../models/connected_cluster_py3.py | 24 +- .../vendored_sdks/models/credential_result.py | 40 ++ .../models/credential_result_py3.py | 40 ++ .../models/k8_connect_rp_enums.py | 14 +- .../vendored_sdks/models/location_data.py | 49 +++ .../vendored_sdks/models/location_data_py3.py | 49 +++ .../connected_cluster_operations.py | 391 ++++++------------ .../vendored_sdks/operations/operations.py | 3 - .../vendored_sdks/version.py | 2 +- 23 files changed, 558 insertions(+), 609 deletions(-) delete mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py delete mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py delete mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py delete mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result_py3.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data.py create mode 100644 src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data_py3.py diff --git a/src/connect_2020/azext_connect_2020/vendored_sdks/version.py b/src/connect_2020/azext_connect_2020/vendored_sdks/version.py index 290c01f6677..335668e2a9b 100644 --- a/src/connect_2020/azext_connect_2020/vendored_sdks/version.py +++ b/src/connect_2020/azext_connect_2020/vendored_sdks/version.py @@ -5,5 +5,5 @@ # regenerated. # -------------------------------------------------------------------------- -VERSION = "2020-01-01-preview" +VERSION = "0.1.4" diff --git a/src/connectedk8s/azext_connectedk8s/_help.py b/src/connectedk8s/azext_connectedk8s/_help.py index 70ae9f5ab72..3c480bad1cb 100644 --- a/src/connectedk8s/azext_connectedk8s/_help.py +++ b/src/connectedk8s/azext_connectedk8s/_help.py @@ -28,6 +28,19 @@ - name: --onboarding-spn-secret type: string short-summary: Secret for Azure Active Directory application id with access to connected cluster resource creation + - name: --location-data-name + type: string + short-summary: A canonical name for the geographic or physical location of the on-prem kubernetes cluster + - name: --location-data-country-or-region + type: string + short-summary: The country or region where the on-prem kubernetes cluster is located + - name: --location-data-district + type: string + short-summary: The district, state, or province where the on-prem kubernetes cluster is located + - name: --location-data-city + type: string + short-summary: The city or locality where the on-prem kubernetes cluster is located + examples: - name: Onboard a connected kubernetes cluster with default kube config and kube context. text: az connectedk8s connect -g resourceGroupName -n connectedClusterName @@ -35,6 +48,8 @@ text: az connectedk8s connect -g resourceGroupName -n connectedClusterName --kube-config /path/to/kubeconfig --kube-context kubeContextName - name: Onboard a connected kubernetes cluster by specifying the onboarding spn details. text: az connectedk8s connect -g resourceGroupName -n connectedClusterName --onboarding-spn-id spnClientId --onboarding-spn-secret spnClientSecret + - name: Onboard a connected kubernetes cluster specifying location data info. + text: az connectedk8s connect -g resourceGroupName -n connectedClusterName --location-data-name locationDataName """ helps['connectedk8s list'] = """ diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index c67ff664ad2..18f52552cee 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -36,10 +36,17 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, onboarding_spn_id=None, onboarding_spn_secret=None, - location=None, kube_config=None, kube_context=None, no_wait=False,): + location=None, kube_config=None, kube_context=None, no_wait=False, + location_data_name=None, location_data_country_or_region=None, + location_data_district=None, location_data_city=None): print("Ensure that you have the latest helm version installed before proceeding to avoid unexpected errors.") print("This operation might take a while...\n") + # Checking location data info + if location_data_name is None: + if ((location_data_country_or_region is not None) or (location_data_district is not None) or (location_data_city is not None)): + raise CLIError("--location-data-name is required when providing location data info.") + # Setting subscription id subscription_id = get_subscription_id(cmd.cli_ctx) @@ -184,20 +191,18 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, except ApiException as e: print("Exception when calling NetworkingV1Api->get_api_resources: %s\n" % e) raise CLIError("If you are using AAD Enabled cluster, check if you have logged in to the cluster properly and try again") - # Checking helm installation - if kube_context is None: - cmd = ["helm", "--kubeconfig", kube_config, "--debug"] - else: - cmd = ["helm", "--kubeconfig", kube_config, "--kube-context", kube_context, "--debug"] + cmd_helm_installed = ["helm", "--kubeconfig", kube_config, "--debug"] + if kube_context: + cmd_helm_installed.extend(["--kube-context", kube_context]) try: - response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) - output, error = response.communicate() - if response.returncode != 0: - if "unknown flag" in error.decode("ascii"): + response_helm_installed = subprocess.Popen(cmd_helm_installed, stdout=PIPE, stderr=PIPE) + output_helm_installed, error_helm_installed = response_helm_installed.communicate() + if response_helm_installed.returncode != 0: + if "unknown flag" in error_helm_installed.decode("ascii"): raise CLIError("Please install the latest version of helm") - raise CLIError(error.decode("ascii")) + raise CLIError(error_helm_installed.decode("ascii")) except FileNotFoundError: raise CLIError("Helm is not installed or requires elevated permissions. Please ensure that you have the latest version of helm installed on your machine.") except subprocess.CalledProcessError as e2: @@ -205,32 +210,30 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, print(e2.output) # Check helm version - if kube_context is None: - cmd = ["helm", "version", "--short", "--kubeconfig", kube_config] - else: - cmd = ["helm", "version", "--short", "--kubeconfig", kube_config, "--kube-context", kube_context] - response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) - output, error = response.communicate() - if response.returncode != 0: - raise CLIError("Unable to determine helm version: " + error.decode("ascii")) + cmd_helm_version = ["helm", "version", "--short", "--kubeconfig", kube_config] + if kube_context: + cmd_helm_version.extend(["--kube-context", kube_context]) + response_helm_version = subprocess.Popen(cmd_helm_version, stdout=PIPE, stderr=PIPE) + output_helm_version, error_helm_version = response_helm_version.communicate() + if response_helm_version.returncode != 0: + raise CLIError("Unable to determine helm version: " + error_helm_version.decode("ascii")) else: - if "v2" in output.decode("ascii"): + if "v2" in output_helm_version.decode("ascii"): raise CLIError("Please install the latest version of helm and then try again") - # Check Release Existance - if kube_context is None: - cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config] - else: - cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config, "--kube-context", kube_context] - response_list = subprocess.Popen(cmd_list, stdout=PIPE, stderr=PIPE) - output_list, error_list = response_list.communicate() - if response_list.returncode != 0: - raise CLIError(error_list.decode("ascii")) + # Check Release Existance + cmd_helm_release = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config] + if kube_context: + cmd_helm_release.extend(["--kube-context", kube_context]) + response_helm_release = subprocess.Popen(cmd_helm_release, stdout=PIPE, stderr=PIPE) + output_helm_release, error_helm_release = response_helm_release.communicate() + if response_helm_release.returncode != 0: + raise CLIError(error_helm_release.decode("ascii")) else: - output_list = output_list.decode("ascii") - output_list = json.loads(output_list) + output_helm_release = output_helm_release.decode("ascii") + output_helm_release = json.loads(output_helm_release) release_name_list = [] - for release in output_list: + for release in output_helm_release: release_name_list.append(release['name']) if "azure-arc" in release_name_list: # Loading config map @@ -252,24 +255,30 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, raise CLIError("Helm release named 'azure-arc' is already present but the azure-arc agent pods are either missing or deployed unsuccessfully.") # Adding helm repo - if kube_context is None: - cmd1 = ["helm", "repo", "add", "azurearcfork8s", "https://azurearcfork8s.azurecr.io/helm/v1/repo", "--kubeconfig", kube_config] - else: - cmd1 = ["helm", "repo", "add", "azurearcfork8s", "https://azurearcfork8s.azurecr.io/helm/v1/repo", "--kubeconfig", kube_config, "--kube-context", kube_context] - response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) - output1, error1 = response1.communicate() - if response1.returncode != 0: - raise CLIError("Helm unable to add repository: " + error1.decode("ascii")) + cmd_helm_repo = ["helm", "repo", "add", "azurearcfork8s", "https://azurearcfork8s.azurecr.io/helm/v1/repo", "--kubeconfig", kube_config] + if kube_context: + cmd_helm_repo.extend(["--kube-context", kube_context]) + response_helm_repo = subprocess.Popen(cmd_helm_repo, stdout=PIPE, stderr=PIPE) + output_helm_repo, error_helm_repo = response_helm_repo.communicate() + if response_helm_repo.returncode != 0: + raise CLIError("Helm unable to add repository: " + error_helm_repo.decode("ascii")) # Install agents - if kube_context is None: - cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] - else: - cmd4 = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--kube-context", kube_context, "--output", "json"] - response4 = subprocess.Popen(cmd4, stdout=PIPE, stderr=PIPE) - output4, error4 = response4.communicate() - if response4.returncode != 0: - raise CLIError("Unable to install helm release: " + error4.decode("ascii")) + cmd_helm_install = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] + if kube_context: + cmd_helm_install.extend(["--kube-context", kube_context]) + if location_data_name: + cmd_helm_install.extend(["--set", "locationData.name={}".format(location_data_name)]) + if location_data_country_or_region: + cmd_helm_install.extend(["--set", "locationData.countryOrRegion={}".format(location_data_country_or_region)]) + if location_data_district: + cmd_helm_install.extend(["--set", "locationData.district={}".format(location_data_district)]) + if location_data_city: + cmd_helm_install.extend(["--set", "locationData.city={}".format(location_data_city)]) + response_helm_install = subprocess.Popen(cmd_helm_install, stdout=PIPE, stderr=PIPE) + output_helm_install, error_helm_install = response_helm_install.communicate() + if response_helm_install.returncode != 0: + raise CLIError("Unable to install helm release: " + error_helm_install.decode("ascii")) if no_wait is True: print("Resource creation request accepted. Please run 'kubectl get pods -n azure-arc' to see whether the pods are in a running state and run 'az connectedk8s show -g {} -n {}' to check if the resource was created successfully".format(resource_group_name, cluster_name)) @@ -446,50 +455,47 @@ def delete_connectedk8s(cmd, client, resource_group_name, cluster_name, kube_con raise CLIError("If you are using AAD Enabled cluster, check if you have logged in to the cluster properly and try again") # Checking helm installation - if kube_context is None: - cmd = ["helm", "--kubeconfig", kube_config, "--debug"] - else: - cmd = ["helm", "--kubeconfig", kube_config, "--kube-context", kube_context, "--debug"] + cmd_helm_installed = ["helm", "--kubeconfig", kube_config, "--debug"] + if kube_context: + cmd_helm_installed.extend(["--kube-context", kube_context]) try: - response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) - output, error = response.communicate() - if response.returncode != 0: - if "unknown flag" in error.decode("ascii"): + response_helm_installed = subprocess.Popen(cmd_helm_installed, stdout=PIPE, stderr=PIPE) + output_helm_installed, error_helm_installed = response_helm_installed.communicate() + if response_helm_installed.returncode != 0: + if "unknown flag" in error_helm_installed.decode("ascii"): raise CLIError("Please install the latest version of helm") - raise CLIError(error.decode("ascii")) + raise CLIError(error_helm_installed.decode("ascii")) except FileNotFoundError: - raise CLIError("Helm is not installed or requires elevated permissions.") + raise CLIError("Helm is not installed or requires elevated permissions. Please ensure that you have the latest version of helm installed on your machine.") except subprocess.CalledProcessError as e2: e2.output = e2.output.decode("ascii") print(e2.output) # Check helm version - if kube_context is None: - cmd = ["helm", "version", "--short", "--kubeconfig", kube_config] + cmd_helm_version = ["helm", "version", "--short", "--kubeconfig", kube_config] + if kube_context: + cmd_helm_version.extend(["--kube-context", kube_context]) + response_helm_version = subprocess.Popen(cmd_helm_version, stdout=PIPE, stderr=PIPE) + output_helm_version, error_helm_version = response_helm_version.communicate() + if response_helm_version.returncode != 0: + raise CLIError("Unable to determine helm version: " + error_helm_version.decode("ascii")) else: - cmd = ["helm", "version", "--short", "--kubeconfig", kube_config, "--kube-context", kube_context] - response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) - output, error = response.communicate() - if response.returncode != 0: - raise CLIError("Unable to determine helm version: " + error.decode("ascii")) - else: - if "v2" in output.decode("ascii"): + if "v2" in output_helm_version.decode("ascii"): raise CLIError("Please install the latest version of helm and then try again") # Check Release Existance - release_namespace = None - if kube_context is None: - cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config] - else: - cmd_list = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config, "--kube-context", kube_context] - response_list = subprocess.Popen(cmd_list, stdout=PIPE, stderr=PIPE) - output_list, error_list = response_list.communicate() - if response_list.returncode != 0: - raise CLIError(error_list.decode("ascii")) + release_namespace = None + cmd_helm_release = ["helm", "list", "-a", "--all-namespaces", "--output", "json", "--kubeconfig", kube_config] + if kube_context: + cmd_helm_release.extend(["--kube-context", kube_context]) + response_helm_release = subprocess.Popen(cmd_helm_release, stdout=PIPE, stderr=PIPE) + output_helm_release, error_helm_release = response_helm_release.communicate() + if response_helm_release.returncode != 0: + raise CLIError(error_helm_release.decode("ascii")) else: - output_list = output_list.decode("ascii") - output_list = json.loads(output_list) - for release in output_list: + output_helm_release = output_helm_release.decode("ascii") + output_helm_release = json.loads(output_helm_release) + for release in output_helm_release: if release['name'] == 'azure-arc': release_namespace = release['namespace'] break @@ -511,14 +517,13 @@ def delete_connectedk8s(cmd, client, resource_group_name, cluster_name, kube_con raise CLIError("The kube config does not correspond to the connected cluster resource provided. Agents installed on this cluster correspond to the resource group name '{}' and resource name '{}'.".format(configmap.data["AZURE_RESOURCE_GROUP"], configmap.data["AZURE_RESOURCE_NAME"])) # Deleting the azure-arc agents - if kube_context is None: - cmd1 = ["helm", "delete", "azure-arc", "--namespace", release_namespace, "--kubeconfig", kube_config] - else: - cmd1 = ["helm", "delete", "azure-arc", "--namespace", release_namespace, "--kubeconfig", kube_config, "--kube-context", kube_context] - response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) - output1, error1 = response1.communicate() - if response1.returncode != 0: - raise CLIError("Helm release deletion failed: " + error1.decode("ascii")) + cmd_helm_delete = ["helm", "delete", "azure-arc", "--namespace", release_namespace, "--kubeconfig", kube_config] + if kube_context: + cmd_helm_delete.extend(["--kube-context", kube_context]) + response_helm_delete = subprocess.Popen(cmd_helm_delete, stdout=PIPE, stderr=PIPE) + output_helm_delete, error_helm_delete = response_helm_delete.communicate() + if response_helm_delete.returncode != 0: + raise CLIError("Helm release deletion failed: " + error_helm_delete.decode("ascii")) return diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py index f06c500aebb..41ccd527f19 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/k8_connect_rp.py @@ -78,7 +78,7 @@ def __init__( super(K8ConnectRP, self).__init__(self.config.credentials, self.config) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} - self.api_version = '2019-09-01-privatepreview' + self.api_version = '2020-01-01-preview' self._serialize = Serializer(client_models) self._deserialize = Deserializer(client_models) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py index 4ea247e54e3..eab4921999f 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/__init__.py @@ -10,47 +10,50 @@ # -------------------------------------------------------------------------- try: - from .connected_cluster_aad_access_profile_py3 import ConnectedClusterAADAccessProfile - from .connected_cluster_access_profile_py3 import ConnectedClusterAccessProfile + from .connected_cluster_proxy_profile_py3 import ConnectedClusterProxyProfile from .operation_display_py3 import OperationDisplay from .operation_py3 import Operation from .connected_cluster_identity_py3 import ConnectedClusterIdentity from .connected_cluster_aad_profile_py3 import ConnectedClusterAADProfile + from .location_data_py3 import LocationData from .connected_cluster_py3 import ConnectedCluster + from .credential_result_py3 import CredentialResult from .connected_cluster_patch_py3 import ConnectedClusterPatch from .error_details_py3 import ErrorDetails from .error_response_py3 import ErrorResponse, ErrorResponseException except (SyntaxError, ImportError): - from .connected_cluster_aad_access_profile import ConnectedClusterAADAccessProfile - from .connected_cluster_access_profile import ConnectedClusterAccessProfile + from .connected_cluster_proxy_profile import ConnectedClusterProxyProfile from .operation_display import OperationDisplay from .operation import Operation from .connected_cluster_identity import ConnectedClusterIdentity from .connected_cluster_aad_profile import ConnectedClusterAADProfile + from .location_data import LocationData from .connected_cluster import ConnectedCluster + from .credential_result import CredentialResult from .connected_cluster_patch import ConnectedClusterPatch from .error_details import ErrorDetails from .error_response import ErrorResponse, ErrorResponseException from .connected_cluster_paged import ConnectedClusterPaged from .operation_paged import OperationPaged from .k8_connect_rp_enums import ( - AzureEnvironment, + ProvisioningState, ResourceIdentityType, ) __all__ = [ - 'ConnectedClusterAADAccessProfile', - 'ConnectedClusterAccessProfile', + 'ConnectedClusterProxyProfile', 'OperationDisplay', 'Operation', 'ConnectedClusterIdentity', 'ConnectedClusterAADProfile', + 'LocationData', 'ConnectedCluster', + 'CredentialResult', 'ConnectedClusterPatch', 'ErrorDetails', 'ErrorResponse', 'ErrorResponseException', 'ConnectedClusterPaged', 'OperationPaged', - 'AzureEnvironment', + 'ProvisioningState', 'ResourceIdentityType', ] diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py index 8777987b9de..55d3f5b77c2 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster.py @@ -24,14 +24,13 @@ class ConnectedCluster(Model): :vartype id: str :ivar name: Resource name :vartype name: str - :ivar type: Type of the resource requested. Possible values include: - 'Microsoft.Kubernetes/connectedClusters' - :vartype type: str or ~azure.mgmt.hybridkubernetes.models.enum + :ivar type: Type of the resource requested + :vartype type: str :param location: Required. Location of the cluster :type location: str - :param tags: Required. Connected Cluster Resource Tags. + :param tags: Connected Cluster Resource Tags. :type tags: dict[str, str] - :param identity: The identity of the connected cluster, if configured. + :param identity: Required. The identity of the connected cluster. :type identity: ~azure.mgmt.hybridkubernetes.models.ConnectedClusterIdentity :param agent_public_key_certificate: Required. Base64 encoded public @@ -50,6 +49,13 @@ class ConnectedCluster(Model): :param agent_version: Version of the agent running on the connected cluster resource :type agent_version: str + :param location_data: Metadata pertaining to the geographic location of + the resource. + :type location_data: ~azure.mgmt.hybridkubernetes.models.LocationData + :param provisioning_state: Possible values include: 'Succeeded', 'Failed', + 'Canceled', 'Provisioning', 'Updating', 'Deleting', 'Accepted' + :type provisioning_state: str or + ~azure.mgmt.hybridkubernetes.models.ProvisioningState """ _validation = { @@ -57,7 +63,7 @@ class ConnectedCluster(Model): 'name': {'readonly': True}, 'type': {'readonly': True}, 'location': {'required': True}, - 'tags': {'required': True}, + 'identity': {'required': True}, 'agent_public_key_certificate': {'required': True}, 'aad_profile': {'required': True}, } @@ -74,6 +80,8 @@ class ConnectedCluster(Model): 'kubernetes_version': {'key': 'properties.kubernetesVersion', 'type': 'str'}, 'total_node_count': {'key': 'properties.totalNodeCount', 'type': 'int'}, 'agent_version': {'key': 'properties.agentVersion', 'type': 'str'}, + 'location_data': {'key': 'properties.locationData', 'type': 'LocationData'}, + 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, } def __init__(self, **kwargs): @@ -89,3 +97,5 @@ def __init__(self, **kwargs): self.kubernetes_version = kwargs.get('kubernetes_version', None) self.total_node_count = kwargs.get('total_node_count', None) self.agent_version = kwargs.get('agent_version', None) + self.location_data = kwargs.get('location_data', None) + self.provisioning_state = kwargs.get('provisioning_state', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py deleted file mode 100644 index 528fbf69a20..00000000000 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile.py +++ /dev/null @@ -1,52 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is -# regenerated. -# -------------------------------------------------------------------------- - -from msrest.serialization import Model - - -class ConnectedClusterAADAccessProfile(Model): - """ConnectedClusterAADAccessProfile. - - :param end_point: The endpoint to connect to the cluster - :type end_point: str - :param endpoint_authority_data: The end point authority data to connect to - the cluster - :type endpoint_authority_data: str - :param api_server_id: AAD API Server Id - :type api_server_id: str - :param client_id: Id of the client - :type client_id: str - :param environment: The azure environment. Possible values include: - 'AzurePublicCloud', 'AzureUSGovernmentCloud', 'AzureChinaCloud', - 'AzureGermanCloud' - :type environment: str or - ~azure.mgmt.hybridkubernetes.models.AzureEnvironment - :param tenant_id: AAD Tenent Id - :type tenant_id: str - """ - - _attribute_map = { - 'end_point': {'key': 'endPoint', 'type': 'str'}, - 'endpoint_authority_data': {'key': 'endpointAuthorityData', 'type': 'str'}, - 'api_server_id': {'key': 'apiServerId', 'type': 'str'}, - 'client_id': {'key': 'clientId', 'type': 'str'}, - 'environment': {'key': 'environment', 'type': 'AzureEnvironment'}, - 'tenant_id': {'key': 'tenantId', 'type': 'str'}, - } - - def __init__(self, **kwargs): - super(ConnectedClusterAADAccessProfile, self).__init__(**kwargs) - self.end_point = kwargs.get('end_point', None) - self.endpoint_authority_data = kwargs.get('endpoint_authority_data', None) - self.api_server_id = kwargs.get('api_server_id', None) - self.client_id = kwargs.get('client_id', None) - self.environment = kwargs.get('environment', None) - self.tenant_id = kwargs.get('tenant_id', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py deleted file mode 100644 index c2b7fa7aab4..00000000000 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_aad_access_profile_py3.py +++ /dev/null @@ -1,52 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is -# regenerated. -# -------------------------------------------------------------------------- - -from msrest.serialization import Model - - -class ConnectedClusterAADAccessProfile(Model): - """ConnectedClusterAADAccessProfile. - - :param end_point: The endpoint to connect to the cluster - :type end_point: str - :param endpoint_authority_data: The end point authority data to connect to - the cluster - :type endpoint_authority_data: str - :param api_server_id: AAD API Server Id - :type api_server_id: str - :param client_id: Id of the client - :type client_id: str - :param environment: The azure environment. Possible values include: - 'AzurePublicCloud', 'AzureUSGovernmentCloud', 'AzureChinaCloud', - 'AzureGermanCloud' - :type environment: str or - ~azure.mgmt.hybridkubernetes.models.AzureEnvironment - :param tenant_id: AAD Tenent Id - :type tenant_id: str - """ - - _attribute_map = { - 'end_point': {'key': 'endPoint', 'type': 'str'}, - 'endpoint_authority_data': {'key': 'endpointAuthorityData', 'type': 'str'}, - 'api_server_id': {'key': 'apiServerId', 'type': 'str'}, - 'client_id': {'key': 'clientId', 'type': 'str'}, - 'environment': {'key': 'environment', 'type': 'AzureEnvironment'}, - 'tenant_id': {'key': 'tenantId', 'type': 'str'}, - } - - def __init__(self, *, end_point: str=None, endpoint_authority_data: str=None, api_server_id: str=None, client_id: str=None, environment=None, tenant_id: str=None, **kwargs) -> None: - super(ConnectedClusterAADAccessProfile, self).__init__(**kwargs) - self.end_point = end_point - self.endpoint_authority_data = endpoint_authority_data - self.api_server_id = api_server_id - self.client_id = client_id - self.environment = environment - self.tenant_id = tenant_id diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py deleted file mode 100644 index da4052a0745..00000000000 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile.py +++ /dev/null @@ -1,56 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is -# regenerated. -# -------------------------------------------------------------------------- - -from msrest.serialization import Model - - -class ConnectedClusterAccessProfile(Model): - """ConnectedClusterAccessProfile. - - Variables are only populated by the server, and will be ignored when - sending a request. - - All required parameters must be populated in order to send to Azure. - - :ivar type: Required. Type of the resource on which the credentials are - requested. Default value: "Microsoft.Kubernetes/connectedClusters" . - :vartype type: str - :param location: Required. Location of the cluster - :type location: str - :param kube_config: Required. Base64 Encoded kubeconfig for accessing - target cluster - :type kube_config: str - :param aad_access_profile: Required. - :type aad_access_profile: - ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAADAccessProfile - """ - - _validation = { - 'type': {'required': True, 'constant': True}, - 'location': {'required': True}, - 'kube_config': {'required': True}, - 'aad_access_profile': {'required': True}, - } - - _attribute_map = { - 'type': {'key': 'type', 'type': 'str'}, - 'location': {'key': 'location', 'type': 'str'}, - 'kube_config': {'key': 'properties.kubeConfig', 'type': 'str'}, - 'aad_access_profile': {'key': 'properties.aadAccessProfile', 'type': 'ConnectedClusterAADAccessProfile'}, - } - - type = "Microsoft.Kubernetes/connectedClusters" - - def __init__(self, **kwargs): - super(ConnectedClusterAccessProfile, self).__init__(**kwargs) - self.location = kwargs.get('location', None) - self.kube_config = kwargs.get('kube_config', None) - self.aad_access_profile = kwargs.get('aad_access_profile', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py deleted file mode 100644 index df39e6fcc95..00000000000 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_access_profile_py3.py +++ /dev/null @@ -1,56 +0,0 @@ -# coding=utf-8 -# -------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for -# license information. -# -# Code generated by Microsoft (R) AutoRest Code Generator. -# Changes may cause incorrect behavior and will be lost if the code is -# regenerated. -# -------------------------------------------------------------------------- - -from msrest.serialization import Model - - -class ConnectedClusterAccessProfile(Model): - """ConnectedClusterAccessProfile. - - Variables are only populated by the server, and will be ignored when - sending a request. - - All required parameters must be populated in order to send to Azure. - - :ivar type: Required. Type of the resource on which the credentials are - requested. Default value: "Microsoft.Kubernetes/connectedClusters" . - :vartype type: str - :param location: Required. Location of the cluster - :type location: str - :param kube_config: Required. Base64 Encoded kubeconfig for accessing - target cluster - :type kube_config: str - :param aad_access_profile: Required. - :type aad_access_profile: - ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAADAccessProfile - """ - - _validation = { - 'type': {'required': True, 'constant': True}, - 'location': {'required': True}, - 'kube_config': {'required': True}, - 'aad_access_profile': {'required': True}, - } - - _attribute_map = { - 'type': {'key': 'type', 'type': 'str'}, - 'location': {'key': 'location', 'type': 'str'}, - 'kube_config': {'key': 'properties.kubeConfig', 'type': 'str'}, - 'aad_access_profile': {'key': 'properties.aadAccessProfile', 'type': 'ConnectedClusterAADAccessProfile'}, - } - - type = "Microsoft.Kubernetes/connectedClusters" - - def __init__(self, *, location: str, kube_config: str, aad_access_profile, **kwargs) -> None: - super(ConnectedClusterAccessProfile, self).__init__(**kwargs) - self.location = location - self.kube_config = kube_config - self.aad_access_profile = aad_access_profile diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py index 40e9885b6a9..fe8efff1395 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity.py @@ -13,38 +13,40 @@ class ConnectedClusterIdentity(Model): - """Identity for the virtual machine. + """Identity for the connected cluster. Variables are only populated by the server, and will be ignored when sending a request. + All required parameters must be populated in order to send to Azure. + :ivar principal_id: The principal id of connected cluster identity. This property will only be provided for a system assigned identity. :vartype principal_id: str :ivar tenant_id: The tenant id associated with the connected cluster. This property will only be provided for a system assigned identity. :vartype tenant_id: str - :param type: The type of identity used for the connected cluster. The type - 'SystemAssigned includes a system created identity. The type 'None' will - remove any identities from the virtual machine. Possible values include: - 'SystemAssigned', 'None' - :type type: str or - ~azure.mgmt.hybridkubernetes.models.ResourceIdentityType + :ivar type: Required. The type of identity used for the connected cluster. + The type 'SystemAssigned, includes a system created identity. Default + value: "SystemAssigned" . + :vartype type: str """ _validation = { 'principal_id': {'readonly': True}, 'tenant_id': {'readonly': True}, + 'type': {'required': True, 'constant': True}, } _attribute_map = { 'principal_id': {'key': 'principalId', 'type': 'str'}, 'tenant_id': {'key': 'tenantId', 'type': 'str'}, - 'type': {'key': 'type', 'type': 'ResourceIdentityType'}, + 'type': {'key': 'type', 'type': 'str'}, } + type = "SystemAssigned" + def __init__(self, **kwargs): super(ConnectedClusterIdentity, self).__init__(**kwargs) self.principal_id = None self.tenant_id = None - self.type = kwargs.get('type', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py index 58d5d5ce5f9..b12a5982adb 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_identity_py3.py @@ -13,38 +13,40 @@ class ConnectedClusterIdentity(Model): - """Identity for the virtual machine. + """Identity for the connected cluster. Variables are only populated by the server, and will be ignored when sending a request. + All required parameters must be populated in order to send to Azure. + :ivar principal_id: The principal id of connected cluster identity. This property will only be provided for a system assigned identity. :vartype principal_id: str :ivar tenant_id: The tenant id associated with the connected cluster. This property will only be provided for a system assigned identity. :vartype tenant_id: str - :param type: The type of identity used for the connected cluster. The type - 'SystemAssigned includes a system created identity. The type 'None' will - remove any identities from the virtual machine. Possible values include: - 'SystemAssigned', 'None' - :type type: str or - ~azure.mgmt.hybridkubernetes.models.ResourceIdentityType + :ivar type: Required. The type of identity used for the connected cluster. + The type 'SystemAssigned, includes a system created identity. Default + value: "SystemAssigned" . + :vartype type: str """ _validation = { 'principal_id': {'readonly': True}, 'tenant_id': {'readonly': True}, + 'type': {'required': True, 'constant': True}, } _attribute_map = { 'principal_id': {'key': 'principalId', 'type': 'str'}, 'tenant_id': {'key': 'tenantId', 'type': 'str'}, - 'type': {'key': 'type', 'type': 'ResourceIdentityType'}, + 'type': {'key': 'type', 'type': 'str'}, } - def __init__(self, *, type=None, **kwargs) -> None: + type = "SystemAssigned" + + def __init__(self, **kwargs) -> None: super(ConnectedClusterIdentity, self).__init__(**kwargs) self.principal_id = None self.tenant_id = None - self.type = type diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile.py new file mode 100644 index 00000000000..1998cf3dbff --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile.py @@ -0,0 +1,29 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterProxyProfile(Model): + """ConnectedClusterProxyProfile. + + :param proxy_connection: Endpoint and authentication details to connect to + proxy + :type proxy_connection: str + """ + + _attribute_map = { + 'proxy_connection': {'key': 'proxyConnection', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectedClusterProxyProfile, self).__init__(**kwargs) + self.proxy_connection = kwargs.get('proxy_connection', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile_py3.py new file mode 100644 index 00000000000..1764747b000 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_proxy_profile_py3.py @@ -0,0 +1,29 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class ConnectedClusterProxyProfile(Model): + """ConnectedClusterProxyProfile. + + :param proxy_connection: Endpoint and authentication details to connect to + proxy + :type proxy_connection: str + """ + + _attribute_map = { + 'proxy_connection': {'key': 'proxyConnection', 'type': 'str'}, + } + + def __init__(self, *, proxy_connection: str=None, **kwargs) -> None: + super(ConnectedClusterProxyProfile, self).__init__(**kwargs) + self.proxy_connection = proxy_connection diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py index 6d238fafe2b..42ddd10cf2c 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/connected_cluster_py3.py @@ -24,14 +24,13 @@ class ConnectedCluster(Model): :vartype id: str :ivar name: Resource name :vartype name: str - :ivar type: Type of the resource requested. Possible values include: - 'Microsoft.Kubernetes/connectedClusters' - :vartype type: str or ~azure.mgmt.hybridkubernetes.models.enum + :ivar type: Type of the resource requested + :vartype type: str :param location: Required. Location of the cluster :type location: str - :param tags: Required. Connected Cluster Resource Tags. + :param tags: Connected Cluster Resource Tags. :type tags: dict[str, str] - :param identity: The identity of the connected cluster, if configured. + :param identity: Required. The identity of the connected cluster. :type identity: ~azure.mgmt.hybridkubernetes.models.ConnectedClusterIdentity :param agent_public_key_certificate: Required. Base64 encoded public @@ -50,6 +49,13 @@ class ConnectedCluster(Model): :param agent_version: Version of the agent running on the connected cluster resource :type agent_version: str + :param location_data: Metadata pertaining to the geographic location of + the resource. + :type location_data: ~azure.mgmt.hybridkubernetes.models.LocationData + :param provisioning_state: Possible values include: 'Succeeded', 'Failed', + 'Canceled', 'Provisioning', 'Updating', 'Deleting', 'Accepted' + :type provisioning_state: str or + ~azure.mgmt.hybridkubernetes.models.ProvisioningState """ _validation = { @@ -57,7 +63,7 @@ class ConnectedCluster(Model): 'name': {'readonly': True}, 'type': {'readonly': True}, 'location': {'required': True}, - 'tags': {'required': True}, + 'identity': {'required': True}, 'agent_public_key_certificate': {'required': True}, 'aad_profile': {'required': True}, } @@ -74,9 +80,11 @@ class ConnectedCluster(Model): 'kubernetes_version': {'key': 'properties.kubernetesVersion', 'type': 'str'}, 'total_node_count': {'key': 'properties.totalNodeCount', 'type': 'int'}, 'agent_version': {'key': 'properties.agentVersion', 'type': 'str'}, + 'location_data': {'key': 'properties.locationData', 'type': 'LocationData'}, + 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, } - def __init__(self, *, location: str, tags, agent_public_key_certificate: str, aad_profile, identity=None, kubernetes_version: str=None, total_node_count: int=None, agent_version: str=None, **kwargs) -> None: + def __init__(self, *, location: str, identity, agent_public_key_certificate: str, aad_profile, tags=None, kubernetes_version: str=None, total_node_count: int=None, agent_version: str=None, location_data=None, provisioning_state=None, **kwargs) -> None: super(ConnectedCluster, self).__init__(**kwargs) self.id = None self.name = None @@ -89,3 +97,5 @@ def __init__(self, *, location: str, tags, agent_public_key_certificate: str, aa self.kubernetes_version = kubernetes_version self.total_node_count = total_node_count self.agent_version = agent_version + self.location_data = location_data + self.provisioning_state = provisioning_state diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result.py new file mode 100644 index 00000000000..0e3395dee39 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class CredentialResult(Model): + """The credential result response. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: The name of the credential. + :vartype name: str + :ivar kubeconfig: Base64-encoded Kubernetes configuration file. + :vartype kubeconfig: bytearray + """ + + _validation = { + 'name': {'readonly': True}, + 'kubeconfig': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'kubeconfig': {'key': 'kubeconfig', 'type': 'bytearray'}, + } + + def __init__(self, **kwargs): + super(CredentialResult, self).__init__(**kwargs) + self.name = None + self.kubeconfig = None diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result_py3.py new file mode 100644 index 00000000000..deeb6055f1c --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/credential_result_py3.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class CredentialResult(Model): + """The credential result response. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar name: The name of the credential. + :vartype name: str + :ivar kubeconfig: Base64-encoded Kubernetes configuration file. + :vartype kubeconfig: bytearray + """ + + _validation = { + 'name': {'readonly': True}, + 'kubeconfig': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'kubeconfig': {'key': 'kubeconfig', 'type': 'bytearray'}, + } + + def __init__(self, **kwargs) -> None: + super(CredentialResult, self).__init__(**kwargs) + self.name = None + self.kubeconfig = None diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py index ed048f0836c..5d4ec6f2d61 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/k8_connect_rp_enums.py @@ -12,15 +12,17 @@ from enum import Enum -class AzureEnvironment(str, Enum): +class ProvisioningState(str, Enum): - azure_public_cloud = "AzurePublicCloud" - azure_us_government_cloud = "AzureUSGovernmentCloud" - azure_china_cloud = "AzureChinaCloud" - azure_german_cloud = "AzureGermanCloud" + succeeded = "Succeeded" + failed = "Failed" + canceled = "Canceled" + provisioning = "Provisioning" + updating = "Updating" + deleting = "Deleting" + accepted = "Accepted" class ResourceIdentityType(str, Enum): system_assigned = "SystemAssigned" - none = "None" diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data.py new file mode 100644 index 00000000000..1b988d52b48 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data.py @@ -0,0 +1,49 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class LocationData(Model): + """Metadata pertaining to the geographic location of the resource. + + All required parameters must be populated in order to send to Azure. + + :param name: Required. A canonical name for the geographic or physical + location. + :type name: str + :param city: The city or locality where the resource is located. + :type city: str + :param district: The district, state, or province where the resource is + located. + :type district: str + :param country_or_region: The country or region where the resource is + located + :type country_or_region: str + """ + + _validation = { + 'name': {'required': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'city': {'key': 'city', 'type': 'str'}, + 'district': {'key': 'district', 'type': 'str'}, + 'country_or_region': {'key': 'countryOrRegion', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(LocationData, self).__init__(**kwargs) + self.name = kwargs.get('name', None) + self.city = kwargs.get('city', None) + self.district = kwargs.get('district', None) + self.country_or_region = kwargs.get('country_or_region', None) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data_py3.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data_py3.py new file mode 100644 index 00000000000..a241cd99280 --- /dev/null +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/models/location_data_py3.py @@ -0,0 +1,49 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is +# regenerated. +# -------------------------------------------------------------------------- + +from msrest.serialization import Model + + +class LocationData(Model): + """Metadata pertaining to the geographic location of the resource. + + All required parameters must be populated in order to send to Azure. + + :param name: Required. A canonical name for the geographic or physical + location. + :type name: str + :param city: The city or locality where the resource is located. + :type city: str + :param district: The district, state, or province where the resource is + located. + :type district: str + :param country_or_region: The country or region where the resource is + located + :type country_or_region: str + """ + + _validation = { + 'name': {'required': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'city': {'key': 'city', 'type': 'str'}, + 'district': {'key': 'district', 'type': 'str'}, + 'country_or_region': {'key': 'countryOrRegion', 'type': 'str'}, + } + + def __init__(self, *, name: str, city: str=None, district: str=None, country_or_region: str=None, **kwargs) -> None: + super(LocationData, self).__init__(**kwargs) + self.name = name + self.city = city + self.district = district + self.country_or_region = country_or_region diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py index a9a1eed01ba..b2879d4aaed 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py @@ -11,18 +11,12 @@ import uuid from msrest.pipeline import ClientRawResponse - -from .. import models -import os -import subprocess -from kubernetes import client, config -from subprocess import Popen, PIPE -import os.path, json -from knack.util import CLIError from msrest.polling import LROPoller, NoPolling from msrestazure.polling.arm_polling import ARMPolling from msrestazure.azure_exceptions import CloudError +from .. import models + class ConnectedClusterOperations(object): """ConnectedClusterOperations operations. @@ -31,7 +25,7 @@ class ConnectedClusterOperations(object): :param config: Configuration of service client. :param serializer: An object model serializer. :param deserializer: An object model deserializer. - :ivar api_version: Version of the API called. Constant value: "2019-09-01-privatepreview". + :ivar api_version: Version of the API called. Constant value: "2020-01-01-preview". """ models = models @@ -41,65 +35,13 @@ def __init__(self, client, config, serializer, deserializer): self._client = client self._serialize = serializer self._deserialize = deserializer - self.api_version = "2019-09-01-privatepreview" - + self.api_version = "2020-01-01-preview" + #self.api_version = "2019-09-01-privatepreview" self.config = config - def create( - self, resource_group_name, cluster_name, custom_headers=None, raw=False, polling=True, **operation_config): - - raw_result = self.get_cluster( - resource_group_name=resource_group_name, - cluster_name=cluster_name, - custom_headers=custom_headers, - raw=True, - **operation_config - ) - def get_long_running_output(response): - deserialized = self._deserialize('ConnectedCluster', response) - - if raw: - client_raw_response = ClientRawResponse(deserialized, response) - return client_raw_response - - return deserialized - lro_delay = operation_config.get( - 'long_running_operation_timeout', - self.config.long_running_operation_timeout) - if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) - elif polling is False: polling_method = NoPolling() - else: polling_method = polling - response = LROPoller(self._client, raw_result, get_long_running_output, polling_method) - if polling is False: - print("Resource creation request accepted. Please run 'kubectl get pods -n azure-arc' to see whether the pods are in a running state and run 'az connectedk8s show -g {} -n {}' to check if the resource was created successfully".format(resource_group_name, cluster_name)) - return response - - """Registers a new K8s cluster. - - API to register a new K8s cluster and thereby create a tracked resource - in ARM. - - :param resource_group_name: The name of the resource group to which - the kubernetes cluster is registered. - :type resource_group_name: str - :param cluster_name: The name of the Kubernetes cluster on which get - is called. - :type cluster_name: str - :param connected_cluster: - :type connected_cluster: - ~azure.mgmt.hybridkubernetes.models.ConnectedCluster - :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: ConnectedCluster or ClientRawResponse if raw=true - :rtype: ~azure.mgmt.hybridkubernetes.models.ConnectedCluster or - ~msrest.pipeline.ClientRawResponse - :raises: - :class:`ErrorResponseException` - """ + def _create_initial( + self, resource_group_name, cluster_name, connected_cluster, custom_headers=None, raw=False, **operation_config): # Construct URL url = self.create.metadata['url'] path_format_arguments = { @@ -146,6 +88,62 @@ def get_long_running_output(response): return client_raw_response return deserialized + + def create( + self, resource_group_name, cluster_name, connected_cluster, custom_headers=None, raw=False, polling=True, **operation_config): + """Registers a new K8s cluster. + + API to register a new K8s cluster and thereby create a tracked resource + in ARM. + + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param connected_cluster: + :type connected_cluster: + ~azure.mgmt.hybridkubernetes.models.ConnectedCluster + :param dict custom_headers: headers that will be added to the request + :param bool raw: The poller return type is ClientRawResponse, the + direct response alongside the deserialized response + :param polling: True for ARMPolling, False for no polling, or a + polling object for personal polling strategy + :return: An instance of LROPoller that returns ConnectedCluster or + ClientRawResponse if raw==True + :rtype: + ~msrestazure.azure_operation.AzureOperationPoller[~azure.mgmt.hybridkubernetes.models.ConnectedCluster] + or + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~azure.mgmt.hybridkubernetes.models.ConnectedCluster]] + :raises: + :class:`ErrorResponseException` + """ + raw_result = self._create_initial( + resource_group_name=resource_group_name, + cluster_name=cluster_name, + connected_cluster=connected_cluster, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + + def get_long_running_output(response): + deserialized = self._deserialize('ConnectedCluster', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) create.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} def update( @@ -214,8 +212,6 @@ def update( if response.status_code == 200: deserialized = self._deserialize('ConnectedCluster', response) - - if raw: client_raw_response = ClientRawResponse(deserialized, response) return client_raw_response @@ -288,31 +284,11 @@ def get( return deserialized get.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} - def get_cluster( - self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): - """Gets data of the specified cluster. - API to get the properties of a specific registered K8s cluster. - - :param resource_group_name: The name of the resource group to which - the kubernetes cluster is registered. - :type resource_group_name: str - :param cluster_name: The name of the Kubernetes cluster on which get - is called. - :type cluster_name: str - :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: ConnectedCluster or ClientRawResponse if raw=true - :rtype: ~azure.mgmt.hybridkubernetes.models.ConnectedCluster or - ~msrest.pipeline.ClientRawResponse - :raises: - :class:`ErrorResponseException` - """ + def _delete_initial( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): # Construct URL - url = self.get.metadata['url'] + url = self.delete.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), @@ -335,27 +311,16 @@ def get_cluster( header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') # Construct and send request - request = self._client.get(url, query_parameters, header_parameters) + request = self._client.delete(url, query_parameters, header_parameters) response = self._client.send(request, stream=False, **operation_config) - if response.status_code not in [200]: - exp = CloudError(response) - exp.request_id = response.headers.get('x-ms-request-id') - #raise exp - #raise models.ErrorResponseException(self._deserialize, response) - - deserialized = None - - if response.status_code == 200: - deserialized = self._deserialize('ConnectedCluster', response) + if response.status_code not in [200, 202, 204]: + raise models.ErrorResponseException(self._deserialize, response) if raw: - client_raw_response = ClientRawResponse(deserialized, response) + client_raw_response = ClientRawResponse(None, response) return client_raw_response - return deserialized - - def delete( self, resource_group_name, cluster_name, custom_headers=None, raw=False, polling=True, **operation_config): """Deletes a specified cluster. @@ -369,119 +334,18 @@ def delete( is called. :type cluster_name: str :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: None or ClientRawResponse if raw=true - :rtype: None or ~msrest.pipeline.ClientRawResponse + :param bool raw: The poller return type is ClientRawResponse, the + direct response alongside the deserialized response + :param polling: True for ARMPolling, False for no polling, or a + polling object for personal polling strategy + :return: An instance of LROPoller that returns None or + ClientRawResponse if raw==True + :rtype: ~msrestazure.azure_operation.AzureOperationPoller[None] or + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[None]] :raises: :class:`ErrorResponseException` """ - """# Construct Cluster Config URL - url_config = self.delete.metadata['url'] - url = url_config + "/providers/Microsoft.KubernetesConfiguration/sourceControlConfigurations" - path_format_arguments = { - 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), - 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') - } - url_config = self._client.format_url(url, **path_format_arguments) - - # Construct cluster config parameters - query_parameters_config = {} - query_parameters_config['api-version'] = "2019-11-01-preview" - - # Get all configs - request_config = self._client.get(url_config, query_parameters_config) - response_config = self._client.send(request_config, stream=False, **operation_config) - - if response_config.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response_config) - if response_config.status_code == 200: - deserialized = self._deserialize('ConnectedCluster', response_config) - - # Delete all configs - for conf in deserialized.additional_properties['value']: - temp_req = self._client.delete(url_config+"/"+conf['name'], query_parameters_config) - temp_res = self._client.send(temp_req, stream=False, **operation_config) - if response_config.status_code not in [200, 202]: - raise models.ErrorResponseException(self._deserialize, temp_res) - - print("All configs deleted") - # Check if the resource exists - - # Construct URL - url = self.get.metadata['url'] - path_format_arguments = { - 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), - 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') - } - url = self._client.format_url(url, **path_format_arguments) - - # Construct parameters - query_parameters = {} - query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') - - # Construct headers - header_parameters = {} - header_parameters['Accept'] = 'application/json' - if self.config.generate_client_request_id: - header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) - if custom_headers: - header_parameters.update(custom_headers) - if self.config.accept_language is not None: - header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') - - # Construct and send request - request = self._client.get(url, query_parameters, header_parameters) - response = self._client.send(request, stream=False, **operation_config) - - if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) - - # Resource existance check completed""" - - """# Setting kubeconfig - if kube_config is None: - kube_config = os.getenv('KUBECONFIG') - if kube_config is None: - kube_config = os.path.join(os.path.expanduser('~'), '.kube', 'config') - - # Checking helm installation - if kube_context is None: - cmd = "helm --kubeconfig {} --debug".format(kube_config) - else: - cmd = "helm --kubeconfig {} --kube-context {} --debug".format(kube_config, kube_context) - try: - response = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) - output, error = response.communicate() - if response.returncode != 0: - raise CLIError(error) - #else: - # print("Helm is installed") - except FileNotFoundError: - raise CLIError("Helm is not installed or requires elevated permissions") - except subprocess.CalledProcessError as e2: - print(e2.output) - - # Confirm the helm release to be uninstalled - if kube_context is None: - print("The helm release deployment for the config {} and the current context will be deleted".format(kube_config)) - action = input ("Do you wish to continue? (y/n): ") - if(action != 'y'): - raise CLIError("Operation terminated") - else: - print("The helm release deployment for the config {} and the context {} will be deleted".format(kube_config, kube_context)) - action = input ("Do you wish to continue? (y/n): ") - if(action != 'y'): - raise CLIError("Operation terminated")""" - - - # Deleting the connected cluster resource: - - raw_result = self.delete_cluster( + raw_result = self._delete_initial( resource_group_name=resource_group_name, cluster_name=cluster_name, custom_headers=custom_headers, @@ -501,40 +365,48 @@ def get_long_running_output(response): elif polling is False: polling_method = NoPolling() else: polling_method = polling return LROPoller(self._client, raw_result, get_long_running_output, polling_method) - - # Connected cluster deletion completed - - """# Deleting the haiku agents - if kube_context is None: - cmd1 = "helm delete haiku --kubeconfig {} --debug".format(kube_config) - else: - cmd1 = "helm delete haiku --kubeconfig {} --kube-context {} --debug".format(kube_config, kube_context) - response1 = subprocess.Popen(cmd1, stdout=PIPE, stderr=PIPE) - output1, error1 = response1.communicate() - if response1.returncode != 0: - raise CLIError(error1) - else: - print(output1)""" delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} + def list_cluster_user_credential( + self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): + """Gets cluster user credential of a connected cluster. + + Gets cluster user credential of the connected cluster with a specified + resource group and name. - def delete_cluster( - self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): - # Construct cluster URL - url = self.delete.metadata['url'] + :param resource_group_name: The name of the resource group to which + the kubernetes cluster is registered. + :type resource_group_name: str + :param cluster_name: The name of the Kubernetes cluster on which get + is called. + :type cluster_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: CredentialResult or ClientRawResponse if raw=true + :rtype: ~azure.mgmt.hybridkubernetes.models.CredentialResult or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.list_cluster_user_credential.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), 'clusterName': self._serialize.url("cluster_name", cluster_name, 'str') } url = self._client.format_url(url, **path_format_arguments) - - # Construct cluster parameters + + # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') - # Construct cluster headers + # Construct headers header_parameters = {} + header_parameters['Accept'] = 'application/json' if self.config.generate_client_request_id: header_parameters['x-ms-client-request-id'] = str(uuid.uuid1()) if custom_headers: @@ -543,24 +415,29 @@ def delete_cluster( header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') # Construct and send request - request = self._client.delete(url, query_parameters, header_parameters) + request = self._client.post(url, query_parameters, header_parameters) response = self._client.send(request, stream=False, **operation_config) - #print(response.status_code) - if response.status_code not in [200, 204]: + if response.status_code not in [200]: raise models.ErrorResponseException(self._deserialize, response) + + deserialized = None + + if response.status_code == 200: + deserialized = self._deserialize('CredentialResult', response) + if raw: - client_raw_response = ClientRawResponse(None, response) + client_raw_response = ClientRawResponse(deserialized, response) return client_raw_response - delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}'} - def list_credentials( + return deserialized + list_cluster_user_credential.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}/listClusterUserCredential'} + + def list_proxy_connection_detail( self, resource_group_name, cluster_name, custom_headers=None, raw=False, **operation_config): - """Lists the endpoint and credentials details to connect to the K8s - cluster's API server. + """Lists the endpoint and credential for proxy. - API to fetch endpoint and credential details to connect to the K8s - cluster’s API server via Azure. + API to fetch endpoint and credential details to proxy server. :param resource_group_name: The name of the resource group to which the kubernetes cluster is registered. @@ -573,16 +450,15 @@ def list_credentials( deserialized response :param operation_config: :ref:`Operation configuration overrides`. - :return: ConnectedClusterAccessProfile or ClientRawResponse if - raw=true + :return: ConnectedClusterProxyProfile or ClientRawResponse if raw=true :rtype: - ~azure.mgmt.hybridkubernetes.models.ConnectedClusterAccessProfile or + ~azure.mgmt.hybridkubernetes.models.ConnectedClusterProxyProfile or ~msrest.pipeline.ClientRawResponse :raises: :class:`ErrorResponseException` """ # Construct URL - url = self.list_credentials.metadata['url'] + url = self.list_proxy_connection_detail.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), @@ -614,14 +490,14 @@ def list_credentials( deserialized = None if response.status_code == 200: - deserialized = self._deserialize('ConnectedClusterAccessProfile', response) + deserialized = self._deserialize('ConnectedClusterProxyProfile', response) if raw: client_raw_response = ClientRawResponse(deserialized, response) return client_raw_response return deserialized - list_credentials.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}/listCredentials'} + list_proxy_connection_detail.metadata = {'url': '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Kubernetes/connectedClusters/{clusterName}/listProxyConnectionDetail'} def list_by_resource_group( self, resource_group_name, custom_headers=None, raw=False, **operation_config): @@ -654,6 +530,7 @@ def internal_paging(next_link=None, raw=False): 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str') } url = self._client.format_url(url, **path_format_arguments) + # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -675,8 +552,11 @@ def internal_paging(next_link=None, raw=False): # Construct and send request request = self._client.get(url, query_parameters, header_parameters) response = self._client.send(request, stream=False, **operation_config) + if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) + exp = CloudError(response) + exp.request_id = response.headers.get('x-ms-request-id') + raise exp return response @@ -718,6 +598,7 @@ def internal_paging(next_link=None, raw=False): 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str') } url = self._client.format_url(url, **path_format_arguments) + # Construct parameters query_parameters = {} query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') @@ -741,7 +622,9 @@ def internal_paging(next_link=None, raw=False): response = self._client.send(request, stream=False, **operation_config) if response.status_code not in [200]: - raise CLIError(models.ErrorResponseException(self._deserialize, response)) + exp = CloudError(response) + exp.request_id = response.headers.get('x-ms-request-id') + raise exp return response diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py index 8a7689df517..657b2121e9c 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/operations.py @@ -22,7 +22,6 @@ class Operations(object): :param config: Configuration of service client. :param serializer: An object model serializer. :param deserializer: An object model deserializer. - :ivar api_version: Version of the API called. Constant value: "2019-09-01-privatepreview". """ models = models @@ -32,7 +31,6 @@ def __init__(self, client, config, serializer, deserializer): self._client = client self._serialize = serializer self._deserialize = deserializer - self.api_version = "2019-09-01-privatepreview" self.config = config @@ -60,7 +58,6 @@ def internal_paging(next_link=None, raw=False): # Construct parameters query_parameters = {} - query_parameters['api-version'] = self._serialize.query("self.api_version", self.api_version, 'str') else: url = next_link diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py index e0ec669828c..e7efe25ea7e 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/version.py @@ -9,5 +9,5 @@ # regenerated. # -------------------------------------------------------------------------- -VERSION = "0.1.0" +VERSION = "0.1.1" From d345685df3b48022e3a1ec0530b8b84681710278 Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Mon, 30 Mar 2020 00:34:21 +0530 Subject: [PATCH 09/58] Updated with key pair generation --- .../azext_connectedk8s/_client_factory.py | 9 - .../azext_connectedk8s/_multi_api_adaptor.py | 70 ---- src/connectedk8s/azext_connectedk8s/custom.py | 379 ++++++------------ .../connected_cluster_operations.py | 4 +- 4 files changed, 128 insertions(+), 334 deletions(-) delete mode 100644 src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py diff --git a/src/connectedk8s/azext_connectedk8s/_client_factory.py b/src/connectedk8s/azext_connectedk8s/_client_factory.py index cb7b0f79102..0df05f138ee 100644 --- a/src/connectedk8s/azext_connectedk8s/_client_factory.py +++ b/src/connectedk8s/azext_connectedk8s/_client_factory.py @@ -36,12 +36,3 @@ def _graph_client_factory(cli_ctx, **_): base_url=cli_ctx.cloud.endpoints.active_directory_graph_resource_id) configure_common_settings(cli_ctx, client) return client - - -def _auth_client_factory(cli_ctx, scope=None): - subscription_id = None - if scope: - matched = re.match('/subscriptions/(?P[^/]*)/', scope) - if matched: - subscription_id = matched.groupdict()['subscription'] - return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION, subscription_id=subscription_id) diff --git a/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py b/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py deleted file mode 100644 index dd5e6716457..00000000000 --- a/src/connectedk8s/azext_connectedk8s/_multi_api_adaptor.py +++ /dev/null @@ -1,70 +0,0 @@ -from azure.cli.core.profiles import ResourceType, get_sdk, supported_api_version - - -class MultiAPIAdaptor(object): - # We will bridge all the code difference here caused by SDK breaking changes - def __init__(self, cli_ctx): - self.old_api = supported_api_version(cli_ctx, resource_type=ResourceType.MGMT_AUTHORIZATION, - max_api='2015-07-01') - self.cli_ctx = cli_ctx - - def _init_individual_permission(self, cfg): - Permission = get_sdk(self.cli_ctx, ResourceType.MGMT_AUTHORIZATION, 'Permission', mod='models', - operation_group='role_definitions') - permission = Permission(actions=cfg.get('actions', None), - not_actions=cfg.get('notActions', None)) - if not self.old_api: - permission.data_actions = cfg.get('dataActions', None) - permission.not_data_actions = cfg.get('notDataActions', None) - return permission - - def _init_permissions(self, role_definition_input): - # we will handle with or w/o 'permissions' - if 'permissions' in role_definition_input: - return [self._init_individual_permission(p) for p in role_definition_input['permissions']] - return [self._init_individual_permission(role_definition_input)] - - def create_role_definition(self, client, role_name, role_id, role_definition_input): - RoleDefinitionBase = get_sdk(self.cli_ctx, ResourceType.MGMT_AUTHORIZATION, - 'RoleDefinitionProperties' if self.old_api else 'RoleDefinition', - mod='models', operation_group='role_definitions') - role_configuration = RoleDefinitionBase(role_name=role_name, - description=role_definition_input.get('description', None), - type='CustomRole', - assignable_scopes=role_definition_input['assignableScopes'], - permissions=self._init_permissions(role_definition_input)) - scope = role_definition_input['assignableScopes'][0] - if self.old_api: - return client.create_or_update(role_definition_id=role_id, scope=scope, properties=role_configuration) - return client.create_or_update(role_definition_id=role_id, scope=scope, role_definition=role_configuration) - - def create_role_assignment(self, client, assignment_name, role_id, object_id, scope, assignee_principal_type=None): - RoleAssignmentCreateParameters = get_sdk( - self.cli_ctx, ResourceType.MGMT_AUTHORIZATION, - 'RoleAssignmentProperties' if self.old_api else 'RoleAssignmentCreateParameters', - mod='models', operation_group='role_assignments') - parameters = RoleAssignmentCreateParameters(role_definition_id=role_id, principal_id=object_id) - if assignee_principal_type: - parameters.principal_type = assignee_principal_type - return client.create(scope, assignment_name, parameters) - - def get_role_property(self, obj, property_name): - if self.old_api: - if isinstance(obj, dict): - obj = obj['properties'] - else: - obj = obj.properties - if isinstance(obj, dict): - return obj[property_name] - return getattr(obj, property_name) - - def set_role_property(self, obj, property_name, property_value): - if self.old_api: - if isinstance(obj, dict): - obj = obj['properties'] - else: - obj = obj.properties - if isinstance(obj, dict): - obj[property_name] = property_value - else: - obj.property_name = property_value diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 18f52552cee..b031f86145a 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -8,20 +8,18 @@ from azext_connectedk8s._client_factory import _graph_client_factory from azext_connectedk8s._client_factory import cf_resource_groups from azext_connectedk8s._client_factory import _resource_client_factory -from azext_connectedk8s._client_factory import _auth_client_factory -from azext_connectedk8s._multi_api_adaptor import MultiAPIAdaptor from msrest.serialization import TZ_UTC from dateutil.relativedelta import relativedelta -from azure.graphrbac.models import (PasswordCredential, ApplicationCreateParameters, ServicePrincipalCreateParameters) -from azure.graphrbac.operations.service_principals_operations import ServicePrincipalsOperations from knack.log import get_logger -from azure.graphrbac.models import GraphErrorException from azure.cli.core.api import get_config_dir from azure.cli.core.util import sdk_no_wait from msrestazure.azure_exceptions import CloudError from kubernetes import client, config import kubernetes.client from kubernetes.client.rest import ApiException +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.backends import default_backend import os import subprocess from subprocess import Popen, PIPE @@ -29,13 +27,13 @@ import uuid import datetime import time +import base64 logger = get_logger(__name__) def create_connectedk8s(cmd, client, resource_group_name, cluster_name, - onboarding_spn_id=None, onboarding_spn_secret=None, location=None, kube_config=None, kube_context=None, no_wait=False, location_data_name=None, location_data_country_or_region=None, location_data_district=None, location_data_city=None): @@ -48,123 +46,12 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, raise CLIError("--location-data-name is required when providing location data info.") # Setting subscription id - subscription_id = get_subscription_id(cmd.cli_ctx) + subscription_id = get_subscription_id(cmd.cli_ctx) - resourceClient = _resource_client_factory(cmd.cli_ctx, subscription_id=subscription_id) - - # Resource group Creation - if location is None: - try: - location = resourceClient.resource_groups.get(resource_group_name).location - except: - raise CLIError("The provided resource group does not exist. Please provide location to create the Resource Group") - - rp_locations = [] - providerDetails = resourceClient.providers.get('Microsoft.Kubernetes') - for resourceTypes in providerDetails.resource_types: - if resourceTypes.resource_type == 'connectedClusters': - rp_locations = [location.replace(" ", "").lower() for location in resourceTypes.locations] - if location.lower() not in rp_locations: - raise CLIError("The connected cluster resource creation is supported only in the following locations: " + ', '.join(map(str, rp_locations)) + ". Please use the --location flag to specify right location.") - break - - if (resource_group_exists(cmd.cli_ctx, resource_group_name, subscription_id) is False): - resource_group_params = {'location': location} - try: - resourceClient.resource_groups.create_or_update(resource_group_name, resource_group_params) - except Exception as e: - raise CLIError("Resource Group Creation Failed." + str(e.message)) - - # SPN creation + # Fetching Tenant Id graph_client = _graph_client_factory(cmd.cli_ctx) onboarding_tenant_id = graph_client.config.tenant_id - if (onboarding_spn_id is not None and onboarding_spn_secret is None): - raise CLIError("Provide the onboarding spn secret.") - - if onboarding_spn_id is None: - try: - spn_list = list_owned_objects(graph_client.signed_in_user, 'servicePrincipal') - except Exception as ex: - raise CLIError("Problem loading the service principals. Check if you have sufficient access to list/create service principals. Error Message: " + str(ex)) - spn_appid_list = [] - for spn in spn_list: - spn_appid_list.append(spn.app_id) - file_name_connectedk8s = 'azureArcServicePrincipal.json' # File containing SPN details - principal_obj = load_acs_service_principal(subscription_id, - file_name=file_name_connectedk8s) # Loading spn from file - spn_present = True - if principal_obj: - if principal_obj.get('service_principal') not in spn_appid_list: - erase_acs_service_principal(file_name=file_name_connectedk8s) - spn_present = False - if (principal_obj and spn_present is True): - onboarding_spn_id = principal_obj.get('service_principal') - onboarding_spn_secret = principal_obj.get('client_secret') - else: - # Creating New SPN - graph_client = _graph_client_factory(cmd.cli_ctx) - role_client = _auth_client_factory(cmd.cli_ctx).role_assignments - scopes = ['/subscriptions/' + role_client.config.subscription_id] - years = 1 - app_start_date = datetime.datetime.now(TZ_UTC) - app_end_date = app_start_date + relativedelta(years=years) - app_display_name = ('cluster-onboarding-spn-' + app_start_date.strftime('%Y-%m-%d-%H-%M-%S')) - name = 'http://' + app_display_name - password = str(uuid.uuid4()) - aad_application = create_aad_application(cmd, - display_name=app_display_name, - homepage='https://' + app_display_name, - identifier_uris=[name], - available_to_other_tenants=False, - password=password, key_value=None, - start_date=app_start_date, - end_date=app_end_date, - credential_description='rbac') - _RETRY_TIMES = 36 - app_id = aad_application.app_id - aad_sp = None - for l in range(0, _RETRY_TIMES): - try: - aad_sp = _create_service_principal(cmd.cli_ctx, app_id, resolve_app=False) - break - except Exception as ex: # pylint: disable=broad-except - if l < _RETRY_TIMES and ( - ' does not reference ' in str(ex) or ' does not exist ' in str(ex)): - time.sleep(5) - logger.warning('Retrying service principal creation: %s/%s', l + 1, _RETRY_TIMES) - else: - logger.warning( - "Creating service principal failed for appid '%s'. Trace followed:\n%s", - name, ex.response.headers if hasattr(ex, 'response') else ex) # pylint: disable=no-member - raise - # correct - - - # Creating Role Binding - role = 'Kubernetes Cluster - Azure Arc Onborading Role' - sp_oid = aad_sp.object_id - for scope in scopes: - for l in range(0, _RETRY_TIMES): - try: - _create_role_assignment(cmd.cli_ctx, role, sp_oid, None, scope, resolve_assignee=False) - break - except Exception as ex: - if l < _RETRY_TIMES and ' does not exist in the directory ' in str(ex): - time.sleep(5) - #logger.warning(' Retrying role assignment creation: %s/%s', l + 1, _RETRY_TIMES) - continue - elif _error_caused_by_role_assignment_exists(ex): - #logger.warning(' Role assignment already exits.\n') - break - else: - if getattr(ex, 'response', None) is not None: - logger.warning(' role assignment response headers: %s\n', ex.response.headers) # pylint: disable=no-member - raise - onboarding_spn_id = app_id - onboarding_spn_secret = password - store_acs_service_principal(subscription_id, onboarding_spn_secret, onboarding_spn_id, file_name=file_name_connectedk8s) - # Setting kubeconfig if kube_config is None: kube_config = os.getenv('KUBECONFIG') @@ -192,6 +79,15 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, print("Exception when calling NetworkingV1Api->get_api_resources: %s\n" % e) raise CLIError("If you are using AAD Enabled cluster, check if you have logged in to the cluster properly and try again") + # Test + api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) + try: + api_response = api_instance.list_namespaced_pod('azure-arc') + except ApiException as e: + print("Exception when calling CoreV1Api->list_namespaced_pod: %s\n" % e) + for pod in api_response.items: + print(pod) + # Checking helm installation cmd_helm_installed = ["helm", "--kubeconfig", kube_config, "--debug"] if kube_context: @@ -242,17 +138,52 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, try: api_response = api_instance.list_namespaced_config_map(namespace) except ApiException as e: - print("Exception when calling CoreV1Api->list_namespaced_config_map: %s\n" % e) + raise CLIError("Exception when calling CoreV1Api->list_namespaced_config_map: " + str(e)) config_present = False for configmap in api_response.items: if configmap.metadata.name == 'azure-clusterconfig': config_present = True - if (configmap.data["AZURE_RESOURCE_GROUP"].lower() == resource_group_name.lower() and configmap.data["AZURE_RESOURCE_NAME"].lower() == cluster_name.lower()): - raise CLIError("Agents corresponding to the provided resource are already installed on this cluster. If the connected cluster resource does not exist, run 'az connectedk8s delete -g {} -n {}' to delete the agents and then try creating again.".format(resource_group_name, cluster_name)) + configmap_resource_group_name = configmap.data["AZURE_RESOURCE_GROUP"] + configmap_cluster_name = configmap.data["AZURE_RESOURCE_NAME"] + if connected_cluster_exists(client, configmap_resource_group_name, configmap_cluster_name): + if (configmap_resource_group_name.lower() == resource_group_name.lower() and configmap_cluster_name.lower() == cluster_name.lower()): + # Change this to re-put + return client.get(configmap_resource_group_name, configmap_cluster_name) + else: + raise CLIError("The kubernetes cluster you are trying to onboard is already onboarded to the resource group '{}' with resource name '{}'.".format(configmap_resource_group_name, configmap_cluster_name)) else: - raise CLIError("Resource creation failed. Agents corresponding to some other resource are already installed on this cluster. Agents installed on this cluster correspond to the resource group name '{}' and resource name '{}'.".format(configmap.data["AZURE_RESOURCE_GROUP"], configmap.data["AZURE_RESOURCE_NAME"])) + # Chage to cleanup by CLI + raise CLIError("Helm release 'azure-arc' is already installed but the corresponding connected cluster resource is missing. Please run 'az connectedk8s delete -g {} -n {}' to clean up and try onboarding again".format(configmap_resource_group_name, configmap_cluster_name)) + break if config_present is False: - raise CLIError("Helm release named 'azure-arc' is already present but the azure-arc agent pods are either missing or deployed unsuccessfully.") + raise CLIError("Helm release 'azure-arc' is already installed but the config map is missing. Please run 'helm delete azure-arc' to clean up and try onboarding again.") + else: + if connected_cluster_exists(client, resource_group_name, cluster_name): + raise CLIError("The connected cluster resource already exists and correspods to a different kubernetes cluster. To onboard this kubernetes cluster to azure, please provide a different resource name or resource group name.") + + # Resource group Creation + resourceClient = _resource_client_factory(cmd.cli_ctx, subscription_id=subscription_id) + if location is None: + try: + location = resourceClient.resource_groups.get(resource_group_name).location + except: + raise CLIError("The provided resource group does not exist. Please provide location to create the Resource Group") + + rp_locations = [] + providerDetails = resourceClient.providers.get('Microsoft.Kubernetes') + for resourceTypes in providerDetails.resource_types: + if resourceTypes.resource_type == 'connectedClusters': + rp_locations = [location.replace(" ", "").lower() for location in resourceTypes.locations] + if location.lower() not in rp_locations: + raise CLIError("The connected cluster resource creation is supported only in the following locations: " + ', '.join(map(str, rp_locations)) + ". Please use the --location flag to specify right location.") + break + + if (resource_group_exists(cmd.cli_ctx, resource_group_name, subscription_id) is False): + resource_group_params = {'location': location} + try: + resourceClient.resource_groups.create_or_update(resource_group_name, resource_group_params) + except Exception as e: + raise CLIError("Resource Group Creation Failed." + str(e.message)) # Adding helm repo cmd_helm_repo = ["helm", "repo", "add", "azurearcfork8s", "https://azurearcfork8s.azurecr.io/helm/v1/repo", "--kubeconfig", kube_config] @@ -263,30 +194,44 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, if response_helm_repo.returncode != 0: raise CLIError("Helm unable to add repository: " + error_helm_repo.decode("ascii")) + # Generate public-private key pair + private_key = rsa.generate_private_key(backend=default_backend(), public_exponent=65537, key_size=4096) + public_key = get_public_key(private_key) + private_key_base64 = get_private_key_base64(private_key) + + # Test Helm Install + chart_path = "C:\\Repos\\test\\AzureCLI\\azure-cli-extensions\\azure-arc-k8sagents-0.1.0.tgz" + cmd_helm_install = ["helm", "install", "azure-arc", chart_path, "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.connectPrivateKey={}".format(private_key_base64), "--set", "systemDefaultValues.spnOnboarding=false", "--kubeconfig", kube_config, "--output", "json"] + if kube_context: + cmd_helm_install.extend(["--kube-context", kube_context]) + response_helm_install = subprocess.Popen(cmd_helm_install, stdout=PIPE, stderr=PIPE) + output_helm_install, error_helm_install = response_helm_install.communicate() + if response_helm_install.returncode != 0: + raise CLIError("Unable to install helm release: " + error_helm_install.decode("ascii")) + return + # Install agents - cmd_helm_install = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.clientId={}".format(onboarding_spn_id), "--set", "global.clientSecret={}".format(onboarding_spn_secret), "--kubeconfig", kube_config, "--output", "json"] + cmd_helm_install = ["helm", "install", "azure-arc", "azurearcfork8s/azure-arc-k8sagents", "--set", "global.subscriptionId={}".format(subscription_id), "--set", "global.resourceGroupName={}".format(resource_group_name), "--set", "global.resourceName={}".format(cluster_name), "--set", "global.location={}".format(location), "--set", "global.tenantId={}".format(onboarding_tenant_id), "--set", "global.connectPrivateKey={}".format(private_key_base64), "--set", "systemDefaultValues.spnOnboarding=false", "--kubeconfig", kube_config, "--output", "json"] if kube_context: cmd_helm_install.extend(["--kube-context", kube_context]) - if location_data_name: - cmd_helm_install.extend(["--set", "locationData.name={}".format(location_data_name)]) - if location_data_country_or_region: - cmd_helm_install.extend(["--set", "locationData.countryOrRegion={}".format(location_data_country_or_region)]) - if location_data_district: - cmd_helm_install.extend(["--set", "locationData.district={}".format(location_data_district)]) - if location_data_city: - cmd_helm_install.extend(["--set", "locationData.city={}".format(location_data_city)]) response_helm_install = subprocess.Popen(cmd_helm_install, stdout=PIPE, stderr=PIPE) output_helm_install, error_helm_install = response_helm_install.communicate() if response_helm_install.returncode != 0: - raise CLIError("Unable to install helm release: " + error_helm_install.decode("ascii")) + raise CLIError("Unable to install helm release: " + error_helm_install.decode("ascii")) + + # Create connected cluster resource + if no_wait is True: print("Resource creation request accepted. Please run 'kubectl get pods -n azure-arc' to see whether the pods are in a running state and run 'az connectedk8s show -g {} -n {}' to check if the resource was created successfully".format(resource_group_name, cluster_name)) return - # Checking availability of connect agent containers - api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) + # Getting pod names namespace = 'azure-arc' + api_instance = kubernetes.client.CoreV1Api(kubernetes.client.ApiClient(configuration)) + pod_list = get_pod_names(api_instance, namespace) + + # Checking availability of connect agent containers timeout = time.time() + 180 container_available = False while container_available is False: @@ -297,15 +242,14 @@ def create_connectedk8s(cmd, client, resource_group_name, cluster_name, except ApiException as e: print("Exception when calling CoreV1Api->list_namespaced_pod: %s\n" % e) for pod in api_response.items: - if pod.metadata.name.startswith('connect-agent'): - if pod.status.container_statuses is not None: - # Checking container status of connect agent pod - check_pod_status(api_instance=api_instance, namespace=namespace, configuration=configuration) - container_available = True - break - else: - time.sleep(5) + if (pod.status.container_statuses is not None and pod.metadata.name in pod_list): + # Checking container status of each pod + check_pod_status(api_instance, namespace, pod.metadata.name) + container_available = True break + else: + time.sleep(5) + break if container_available is False: raise CLIError("Unable to get container status of the connect agent pod. Please run 'kubectl get pods -n azure-arc' to check whether pods are in running state.") @@ -335,57 +279,48 @@ def resource_group_exists(ctx, resource_group_name, subscription_id=None): except: return False +def connected_cluster_exists(client, resource_group_name, cluster_name): + try: + client.get(resource_group_name, cluster_name) + except Exception as ex: + if (('was not found' in str(ex)) or ('could not be found' in str(ex))): + return False + else: + raise CLIError("Unable to determine if the connected cluster resource exists. " + str(ex)) + return True -def erase_acs_service_principal(file_name='acsServicePrincipal.json'): - config_path = os.path.join(get_config_dir(), file_name) - open(config_path, 'w').close() +def get_public_key(private_key): + public_key = private_key.public_key().public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.PKCS1).decode('utf-8').rstrip() + ind1 = public_key.find('\n') + ind2 = public_key.rfind('\n') + return public_key[ind1+1:ind2].replace('\n', '') -def load_acs_service_principal(subscription_id, file_name='acsServicePrincipal.json'): - config_path = os.path.join(get_config_dir(), file_name) - config = load_service_principals(config_path) - if not config: - return None - return config.get(subscription_id) +def get_private_key_base64(private_key): + private_pem = private_key.private_bytes(encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) + return base64.b64encode(private_pem).decode('utf-8') -def load_service_principals(config_path): - if not os.path.exists(config_path): - return None - fd = os.open(config_path, os.O_RDONLY) - try: - with os.fdopen(fd) as f: - return json.loads(f.read()) - except: # pylint: disable=bare-except - return None - - -def store_acs_service_principal(subscription_id, client_secret, service_principal, - file_name='acsServicePrincipal.json'): - obj = {} - if client_secret: - obj['client_secret'] = client_secret - if service_principal: - obj['service_principal'] = service_principal - - config_path = os.path.join(get_config_dir(), file_name) - full_config = load_service_principals(config_path=config_path) - if not full_config: - full_config = {} - full_config[subscription_id] = obj - - with os.fdopen(os.open(config_path, os.O_RDWR | os.O_CREAT | os.O_TRUNC, 0o600), - 'w+') as spFile: - json.dump(full_config, spFile) - - -def list_owned_objects(client, object_type=None): - result = client.list_owned_objects() - if object_type: - result = [r for r in result if r.object_type and r.object_type.lower() == object_type.lower()] - return result - -def check_pod_status(api_instance, namespace, configuration): + +def get_pod_names(api_instance, namespace): + pod_list = [] + timeout = time.time() + 60 + while(not pod_list): + try: + api_response = api_instance.list_namespaced_pod(namespace) + for pod in api_response.items: + pod_list.append(pod.metadata.name) + except ApiException as e: + print("Exception when calling get pods: %s\n" % e) + pod_list = [] + time.sleep(5) + if time.time()>timeout: + raise CLIError("") + return pod_list + + + +def check_pod_status(api_instance, namespace, podname): connect_agent_state = None timeout = time.time() + 300 found_running = 0 @@ -533,57 +468,6 @@ def update_connectedk8s(cmd, instance, tags=None): return instance -def create_aad_application(cmd, display_name, homepage=None, identifier_uris=None, # pylint: disable=too-many-locals - available_to_other_tenants=False, password=None, reply_urls=None, - key_value=None, key_type=None, key_usage=None, start_date=None, end_date=None, - oauth2_allow_implicit_flow=None, required_resource_accesses=None, native_app=None, - credential_description=None, app_roles=None): - graph_client = _graph_client_factory(cmd.cli_ctx) - password_creds = [PasswordCredential(start_date=start_date, - end_date=end_date, key_id=str(uuid.uuid4()), - value=password, - custom_key_identifier=None)] - app_create_param = ApplicationCreateParameters(available_to_other_tenants=False, - display_name=display_name, - identifier_uris=identifier_uris, - homepage='https://' + display_name, - reply_urls=None, - key_credentials=None, - password_credentials=password_creds, - oauth2_allow_implicit_flow=None, - required_resource_access=None, - app_roles=None) - try: - result = graph_client.applications.create(app_create_param) - except GraphErrorException as ex: - if 'insufficient privileges' in str(ex).lower(): - link = 'https://docs.microsoft.com/azure/azure-resource-manager/resource-group-create-service-principal-portal' # pylint: disable=line-too-long - raise CLIError("Directory permission is needed for the current user to register the application. " - "For how to configure, please refer '{}'. Original error: {}".format(link, ex)) - raise - return result - - -def _create_service_principal(cli_ctx, identifier, resolve_app=True): - client = _graph_client_factory(cli_ctx) - app_id = identifier - if resolve_app: - if _is_guid(identifier): - result = list(client.applications.list(filter="appId eq '{}'".format(identifier))) - else: - result = list(client.applications.list( - filter="identifierUris/any(s:s eq '{}')".format(identifier))) - - try: - if not result: # assume we get an object id - result = [client.applications.get(identifier)] - app_id = result[0].app_id - except GraphErrorException: - pass # fallback to appid (maybe from an external tenant?) - - return client.service_principals.create(ServicePrincipalCreateParameters(app_id=app_id, account_enabled=True)) - - def _is_guid(guid): try: uuid.UUID(guid) @@ -594,16 +478,3 @@ def _is_guid(guid): def _error_caused_by_role_assignment_exists(ex): return getattr(ex, 'status_code', None) == 409 and 'role assignment already exists' in ex.message - - -def _create_role_assignment(cli_ctx, role, assignee, resource_group_name=None, scope=None, - resolve_assignee=True, assignee_principal_type=None): - factory = _auth_client_factory(cli_ctx, scope) - assignments_client = factory.role_assignments - definitions_client = factory.role_definitions - role = '34e09817-6cbe-4d01-b1a2-e0eac5743d41' - role_id = '/subscriptions/{}/providers/Microsoft.Authorization/roleDefinitions/{}'.format(definitions_client.config.subscription_id, role) - object_id = assignee - worker = MultiAPIAdaptor(cli_ctx) - return worker.create_role_assignment(assignments_client, uuid.uuid4(), role_id, - object_id, scope, assignee_principal_type) diff --git a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py index b2879d4aaed..43021ad1e90 100644 --- a/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py +++ b/src/connectedk8s/azext_connectedk8s/vendored_sdks/operations/connected_cluster_operations.py @@ -270,7 +270,9 @@ def get( response = self._client.send(request, stream=False, **operation_config) if response.status_code not in [200]: - raise models.ErrorResponseException(self._deserialize, response) + exp = CloudError(response) + exp.request_id = response.headers.get('x-ms-request-id') + raise exp deserialized = None From 4e902908edf61f632336fd01fa54a1256ae8c8d1 Mon Sep 17 00:00:00 2001 From: Akash Keshari Date: Wed, 1 Apr 2020 12:31:36 +0530 Subject: [PATCH 10/58] review of public private key pair --- azure-arc-k8sagents/.helmignore | 22 ++ azure-arc-k8sagents/Chart.yaml | 8 + .../azure-agent-clusterrolebinding.yaml | 18 + .../azure-arc-connect-agent-deployment.yaml | 77 ++++ ...re-arc-connect-proxy-agent-deployment.yaml | 30 ++ .../azure-arc-fluent-bit-daemonset.yaml | 66 ++++ .../azure-arc-metrics-agent-deployment.yaml | 30 ++ .../azure-clusteridentityoperator.yaml | 102 ++++++ .../azure-config-agent-deployment.yaml | 71 ++++ .../templates/azure-config.yaml | 26 ++ .../templates/azure-configoperator.yaml | 89 +++++ .../templates/azure-fluentd-config.yaml | 57 +++ ...zure.com_azureclusteridentityrequests.yaml | 78 +++++ .../clusterconfig.azure.com_gitconfigs.yaml | 110 ++++++ azure-arc-k8sagents/templates/namespace.yaml | 4 + azure-arc-k8sagents/templates/secrets.yaml | 8 + azure-arc-k8sagents/values.yaml | 118 +++++++ setupChart-0.1.19.tgz | Bin 0 -> 3644 bytes src/connectedk8s/azext_connectedk8s/custom.py | 331 ++++++++++-------- .../connected_cluster_operations.py | 1 - src/connectedk8s/setup.py | 3 +- 21 files changed, 1095 insertions(+), 154 deletions(-) create mode 100644 azure-arc-k8sagents/.helmignore create mode 100644 azure-arc-k8sagents/Chart.yaml create mode 100644 azure-arc-k8sagents/templates/azure-agent-clusterrolebinding.yaml create mode 100644 azure-arc-k8sagents/templates/azure-arc-connect-agent-deployment.yaml create mode 100644 azure-arc-k8sagents/templates/azure-arc-connect-proxy-agent-deployment.yaml create mode 100644 azure-arc-k8sagents/templates/azure-arc-fluent-bit-daemonset.yaml create mode 100644 azure-arc-k8sagents/templates/azure-arc-metrics-agent-deployment.yaml create mode 100644 azure-arc-k8sagents/templates/azure-clusteridentityoperator.yaml create mode 100644 azure-arc-k8sagents/templates/azure-config-agent-deployment.yaml create mode 100644 azure-arc-k8sagents/templates/azure-config.yaml create mode 100644 azure-arc-k8sagents/templates/azure-configoperator.yaml create mode 100644 azure-arc-k8sagents/templates/azure-fluentd-config.yaml create mode 100644 azure-arc-k8sagents/templates/clusterconfig.azure.com.azure.com_azureclusteridentityrequests.yaml create mode 100644 azure-arc-k8sagents/templates/clusterconfig.azure.com_gitconfigs.yaml create mode 100644 azure-arc-k8sagents/templates/namespace.yaml create mode 100644 azure-arc-k8sagents/templates/secrets.yaml create mode 100644 azure-arc-k8sagents/values.yaml create mode 100644 setupChart-0.1.19.tgz diff --git a/azure-arc-k8sagents/.helmignore b/azure-arc-k8sagents/.helmignore new file mode 100644 index 00000000000..50af0317254 --- /dev/null +++ b/azure-arc-k8sagents/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/azure-arc-k8sagents/Chart.yaml b/azure-arc-k8sagents/Chart.yaml new file mode 100644 index 00000000000..e65838a5679 --- /dev/null +++ b/azure-arc-k8sagents/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for azure-arc-k8sagents +home: https://github.com/Azure/ClusterConfigurationAgent/azure-arc-k8sagents +name: azure-arc-k8sagents +sources: +- https://github.com/Azure/ClusterConfigurationAgent/azure-arc-k8sagents +version: 0.1.61 diff --git a/azure-arc-k8sagents/templates/azure-agent-clusterrolebinding.yaml b/azure-arc-k8sagents/templates/azure-agent-clusterrolebinding.yaml new file mode 100644 index 00000000000..ca71c39eb92 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-agent-clusterrolebinding.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: azure-arc-operatorsa + namespace: azure-arc +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: azure-arc-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: azure-arc-operatorsa + namespace: azure-arc diff --git a/azure-arc-k8sagents/templates/azure-arc-connect-agent-deployment.yaml b/azure-arc-k8sagents/templates/azure-arc-connect-agent-deployment.yaml new file mode 100644 index 00000000000..25138706451 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-arc-connect-agent-deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: connect-agent + namespace: azure-arc + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-agent + annotations: + description: {{ .Chart.Description }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-agent + template: + metadata: + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-agent + spec: + serviceAccountName: azure-arc-operatorsa + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: connect-agent + image: "{{ .Values.systemDefaultValues.image.repository }}/{{ .Values.systemDefaultValues.image.releaseName }}/{{ index .Values.systemDefaultValues "connect-agent" "image" }}:{{ index .Values.systemDefaultValues "connect-agent" "tag" }}" + imagePullPolicy: {{ .Values.systemDefaultValues.image.imagePullPolicy }} + envFrom: + - configMapRef: + name: azure-clusterconfig + env: + - name: AZURE_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: azure-arc-onboarding + key: value + {{ if .Values.systemDefaultValues.IsDebug}} + ports: + - containerPort: 40000 + name: debug + protocol: TCP + securityContext: + capabilities: + add: + - SYS_PTRACE + {{end}} +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + prometheus.io/port: "8443" + prometheus.io/scheme: https + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-agent + name: connect-agent-metrics-service + namespace: azure-arc +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-agent \ No newline at end of file diff --git a/azure-arc-k8sagents/templates/azure-arc-connect-proxy-agent-deployment.yaml b/azure-arc-k8sagents/templates/azure-arc-connect-proxy-agent-deployment.yaml new file mode 100644 index 00000000000..f995c138758 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-arc-connect-proxy-agent-deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: connect-proxy-agent + namespace: azure-arc + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-proxy-agent + annotations: + description: {{ .Chart.Description }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-proxy-agent + template: + metadata: + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: connect-proxy-agent + spec: + serviceAccountName: azure-arc-operatorsa + containers: + - name: connect-proxy-agent + image: "{{ .Values.systemDefaultValues.image.repository }}/{{ .Values.systemDefaultValues.image.releaseName }}/{{ index .Values.systemDefaultValues "connect-proxy-agent" "image" }}:{{ index .Values.systemDefaultValues "connect-proxy-agent" "tag" }}" + imagePullPolicy: {{ .Values.systemDefaultValues.image.imagePullPolicy }} + envFrom: + - configMapRef: + name: azure-clusterconfig \ No newline at end of file diff --git a/azure-arc-k8sagents/templates/azure-arc-fluent-bit-daemonset.yaml b/azure-arc-k8sagents/templates/azure-arc-fluent-bit-daemonset.yaml new file mode 100644 index 00000000000..733e036174c --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-arc-fluent-bit-daemonset.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: fluent-bit + namespace: azure-arc + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: fluent-bit + annotations: + description: {{ .Chart.Description }} +spec: + selector: + matchLabels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: fluent-bit + template: + metadata: + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: fluent-bit + spec: + serviceAccountName: azure-arc-operatorsa + affinity: + podAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - azure-arc-k8s + topologyKey: "kubernetes.io/hostname" + namespaces: + - azure-arc + containers: + - name: fluent-bit + image: "{{ .Values.systemDefaultValues.image.repository }}/{{ .Values.systemDefaultValues.image.releaseName }}/{{ index .Values.systemDefaultValues "fluent-bit" "image" }}:{{ index .Values.systemDefaultValues "fluent-bit" "tag" }}" + imagePullPolicy: {{ .Values.systemDefaultValues.image.imagePullPolicy }} + envFrom: + - configMapRef: + name: azure-clusterconfig + ports: + - containerPort: 2020 + volumeMounts: + - name: varlog + mountPath: /var/log + - name: varlibdockercontainers + mountPath: /var/lib/docker/containers + readOnly: true + - name: fluentbit-clusterconfig + mountPath: /fluent-bit/etc/ + terminationGracePeriodSeconds: 10 + volumes: + - name: varlog + hostPath: + path: /var/log + - name: varlibdockercontainers + hostPath: + path: /var/lib/docker/containers + - name: fluentbit-clusterconfig + configMap: + name: azure-fluentd-config + tolerations: + - key: node-role.kubernetes.io/master + operator: Exists + effect: NoSchedule \ No newline at end of file diff --git a/azure-arc-k8sagents/templates/azure-arc-metrics-agent-deployment.yaml b/azure-arc-k8sagents/templates/azure-arc-metrics-agent-deployment.yaml new file mode 100644 index 00000000000..94974646101 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-arc-metrics-agent-deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: metrics-agent + namespace: azure-arc + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: metrics-agent + annotations: + description: {{ .Chart.Description }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: metrics-agent + template: + metadata: + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: metrics-agent + spec: + serviceAccountName: azure-arc-operatorsa + containers: + - name: metrics-agent + image: "{{ .Values.systemDefaultValues.image.repository }}/{{ .Values.systemDefaultValues.image.releaseName }}/{{ index .Values.systemDefaultValues "metrics-agent" "image" }}:{{ index .Values.systemDefaultValues "metrics-agent" "tag" }}" + imagePullPolicy: {{ .Values.systemDefaultValues.image.imagePullPolicy }} + envFrom: + - configMapRef: + name: azure-clusterconfig \ No newline at end of file diff --git a/azure-arc-k8sagents/templates/azure-clusteridentityoperator.yaml b/azure-arc-k8sagents/templates/azure-clusteridentityoperator.yaml new file mode 100644 index 00000000000..795f27dfb68 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-clusteridentityoperator.yaml @@ -0,0 +1,102 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clusteridentityoperator + namespace: azure-arc + labels: + control-plane: cluster-identity-manager +spec: + replicas: 1 + selector: + matchLabels: + control-plane: cluster-identity-manager + template: + metadata: + labels: + control-plane: cluster-identity-manager + spec: + serviceAccountName: azure-arc-operatorsa + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + {{if .Values.systemDefaultValues.IsIdentityDebug}} + - "--listen=:40000" + - "--headless=true" + - "--api-version=2" + - "exec" + - "/data/identityOperator" + - "--" + {{end}} + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" + command: + {{ if .Values.systemDefaultValues.IsIdentityDebug}} + - /dlv + {{else}} + - /data/identityOperator + {{end}} + image: "{{ .Values.systemDefaultValues.image.repository }}/{{ .Values.systemDefaultValues.image.releaseName }}/{{ index .Values.systemDefaultValues "clusteridentityoperator" "image" }}:{{ index .Values.systemDefaultValues "clusteridentityoperator" "tag" }}" + imagePullPolicy: {{ .Values.systemDefaultValues.image.imagePullPolicy }} + envFrom: + - configMapRef: + name: azure-clusterconfig + {{if .Values.systemDefaultValues.clusteridentityoperator.armIdOverride}} + env: + - name: AZURE_RESOURCE_NAME + value: {{.Values.systemDefaultValues.clusteridentityoperator.resourceName}} + - name: AZURE_SUBSCRIPTION_ID + value: {{ .Values.systemDefaultValues.clusteridentityoperator.subscription}} + - name: AZURE_RESOURCE_GROUP + value: {{ .Values.systemDefaultValues.clusteridentityoperator.resourceGroup}} + - name: ONBOARDING_SECRET_NAME + value: {{ .Values.systemDefaultValues.clusteridentityoperator.secretName}} + - name: ONBOARDING_SECRET_NAMESPACE + value: {{ .Values.systemDefaultValues.clusteridentityoperator.secretNamespace}} + {{end}} + resources: + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 100Mi + {{ if .Values.systemDefaultValues.IsIdentityDebug}} + ports: + - containerPort: 40000 + name: debug + protocol: TCP + securityContext: + capabilities: + add: + - SYS_PTRACE + {{end}} + terminationGracePeriodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + prometheus.io/port: "8443" + prometheus.io/scheme: https + prometheus.io/scrape: "true" + labels: + control-plane: cluster-identity-manager + name: cluster-identity-manager-metrics-service + namespace: azure-arc +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: cluster-identity-manager diff --git a/azure-arc-k8sagents/templates/azure-config-agent-deployment.yaml b/azure-arc-k8sagents/templates/azure-config-agent-deployment.yaml new file mode 100644 index 00000000000..d0650e00335 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-config-agent-deployment.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: config-agent + namespace: azure-arc + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: config-agent + annotations: + description: {{ .Chart.Description }} +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: config-agent + template: + metadata: + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: config-agent + spec: + serviceAccountName: azure-arc-operatorsa + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: config-agent + image: "{{ .Values.systemDefaultValues.image.repository }}/{{ .Values.systemDefaultValues.image.releaseName }}/{{ index .Values.systemDefaultValues "config-agent" "image" }}:{{ index .Values.systemDefaultValues "config-agent" "tag" }}" + imagePullPolicy: {{ .Values.systemDefaultValues.image.imagePullPolicy }} + envFrom: + - configMapRef: + name: azure-clusterconfig + {{ if .Values.systemDefaultValues.IsDebug}} + ports: + - containerPort: 40000 + name: debug + protocol: TCP + securityContext: + capabilities: + add: + - SYS_PTRACE + {{end}} +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + prometheus.io/port: "8443" + prometheus.io/scheme: https + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: config-agent + name: config-agent-metrics-service + namespace: azure-arc +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + app.kubernetes.io/name: azure-arc-k8s + app.kubernetes.io/component: config-agent \ No newline at end of file diff --git a/azure-arc-k8sagents/templates/azure-config.yaml b/azure-arc-k8sagents/templates/azure-config.yaml new file mode 100644 index 00000000000..3521e867bd5 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-config.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: azure-clusterconfig + namespace: azure-arc +data: + AZURE_RESOURCE_NAME: {{ .Values.global.resourceName }} + AZURE_SUBSCRIPTION_ID: {{ .Values.global.subscriptionId }} + AZURE_TENANT_ID: {{ .Values.global.tenantId }} + AZURE_CLIENT_ID: {{ .Values.global.clientId }} + AZURE_RESOURCE_GROUP: {{ .Values.global.resourceGroupName }} + AZURE_REGION: {{ .Values.global.location }} + ONBOARDING_SECRET_NAME: "azure-arc-connect-privatekey" + ONBOARDING_SECRET_NAMESPACE: "azure-arc" + MANAGED_IDENTITY_AUTH: "{{ .Values.systemDefaultValues.optIn.ManagedIdentityAuth }}" + CLUSTER_TYPE: {{ index .Values.systemDefaultValues "config-agent" "cluster_type" }}{{ if index .Values.systemDefaultValues "config-agent" "dp_endpoint_override" }} + DP_ENDPOINT_OVERRIDE: {{ index .Values.systemDefaultValues "config-agent" "dp_endpoint_override" }}{{ end }} + FLUX_CLIENT_DEFAULT_LOCATION: {{ index .Values.systemDefaultValues "config-agent" "flux_client_default_location" }} + AZURE_ARC_AGENT_VERSION: {{ .Values.systemDefaultValues.azureArcAgents.version }} + ARC_AGENT_HELM_CHART_NAME: "{{ .Chart.Name }}" + HELM_AUTO_UPDATE_CHECK_FREQUENCY_IN_MINUTES: "{{ .Values.global.UpdateCheckFrequencyInMinutes }}" + AZURE_ARC_AUTOUPDATE: "{{ .Values.global.autoUpdate }}" + ARC_AGENT_RELEASE_TRAIN: {{.Values.global.releaseTrain}} + AZURE_ARC_RELEASE_NAME: "{{ .Release.Name }}" + AZURE_ARC_HELM_NAMESPACE: "{{ .Release.Namespace }}" + DEBUG_LOGGING: "{{ .Values.systemDefaultValues.debugLogging }}" diff --git a/azure-arc-k8sagents/templates/azure-configoperator.yaml b/azure-arc-k8sagents/templates/azure-configoperator.yaml new file mode 100644 index 00000000000..0b18fb1c6cc --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-configoperator.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: azure-arc + labels: + control-plane: controller-manager +spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + spec: + serviceAccountName: azure-arc-operatorsa + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=10" + ports: + - containerPort: 8443 + name: https + - name: manager + args: + {{if .Values.systemDefaultValues.IsDebug}} + - "--listen=:40000" + - "--headless=true" + - "--api-version=2" + - "exec" + - "/data/manager" + - "--" + {{end}} + - "--metrics-addr=127.0.0.1:8080" + - "--enable-leader-election" + command: + {{ if .Values.systemDefaultValues.IsDebug}} + - /dlv + {{else}} + - /data/manager + {{end}} + image: "{{ .Values.systemDefaultValues.image.repository }}/{{ .Values.systemDefaultValues.image.releaseName }}/{{ index .Values.systemDefaultValues "config-operator" "image" }}:{{ index .Values.systemDefaultValues "config-operator" "tag" }}" + imagePullPolicy: {{ .Values.systemDefaultValues.image.imagePullPolicy }} + envFrom: + - configMapRef: + name: azure-clusterconfig + resources: + limits: + cpu: 100m + memory: 300Mi + requests: + cpu: 100m + memory: 100Mi + {{ if .Values.systemDefaultValues.IsDebug}} + ports: + - containerPort: 40000 + name: debug + protocol: TCP + securityContext: + capabilities: + add: + - SYS_PTRACE + {{end}} + terminationGracePeriodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + prometheus.io/port: "8443" + prometheus.io/scheme: https + prometheus.io/scrape: "true" + labels: + control-plane: controller-manager + name: controller-manager-metrics-service + namespace: azure-arc +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/azure-arc-k8sagents/templates/azure-fluentd-config.yaml b/azure-arc-k8sagents/templates/azure-fluentd-config.yaml new file mode 100644 index 00000000000..88d4dd6a485 --- /dev/null +++ b/azure-arc-k8sagents/templates/azure-fluentd-config.yaml @@ -0,0 +1,57 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: azure-fluentd-config + namespace: azure-arc +data: + fluent-bit.conf: | + [SERVICE] + Flush 300 + Log_Level info + Daemon off + Parsers_File parsers.conf + HTTP_Server Off + HTTP_Listen 0.0.0.0 + HTTP_Port 2020 + + @INCLUDE input-kubernetes.conf + @INCLUDE output.conf + + input-kubernetes.conf: | + [INPUT] + Name tail + Tag azure-arc.* + Path /var/log/containers/*azure-arc*.log + Parser docker + Mem_Buf_Limit 1MB + + parsers.conf: | + [PARSER] + Name json + Format json + Time_Key time + Time_Format %Y-%m-%dT%H:%M:%S.%LZ + + [PARSER] + Name docker + Format json + Time_Key time + Time_Format %Y-%m-%dT%H:%M:%S.%LZ + Time_Keep On + # Command | Decoder | Field | Optional Action + # =============|==================|================= + Decode_Field_As escaped log + + [PARSER] + Name syslog + Format regex + Regex ^\<(?[0-9]+)\>(?