diff --git a/src/aks-preview/azext_aks_preview/_format.py b/src/aks-preview/azext_aks_preview/_format.py index c3e8d71e8a5..913db1c75d3 100644 --- a/src/aks-preview/azext_aks_preview/_format.py +++ b/src/aks-preview/azext_aks_preview/_format.py @@ -10,21 +10,6 @@ from jmespath import functions -def aks_run_command_result_format(cmdResult): - result = OrderedDict() - if cmdResult['provisioningState'] == "Succeeded": - result['exit code'] = cmdResult['exitCode'] - result['logs'] = cmdResult['logs'] - return result - if cmdResult['provisioningState'] == "Failed": - result['provisioning state'] = cmdResult['provisioningState'] - result['reason'] = cmdResult['reason'] - return result - result['provisioning state'] = cmdResult['provisioningState'] - result['started At'] = cmdResult['startedAt'] - return result - - def aks_agentpool_show_table_format(result): """Format an agent pool as summary results for display with "-o table".""" return [_aks_agentpool_table_format(result)] diff --git a/src/aks-preview/azext_aks_preview/_help.py b/src/aks-preview/azext_aks_preview/_help.py index c6a34bfb698..e9116cc8896 100644 --- a/src/aks-preview/azext_aks_preview/_help.py +++ b/src/aks-preview/azext_aks_preview/_help.py @@ -667,32 +667,6 @@ short-summary: Display diagnostic results for the Kubernetes cluster after kollect is done. """ -helps['aks command'] = """ - type: group - short-summary: see detail usage in 'az aks command invoke', 'az aks command result'. -""" - -helps['aks command invoke'] = """ - type: command - short-summary: run a shell command (with kubectl, helm) on your aks cluster, support attaching files as well. - parameters: - - name: --command -c - type: string - short-summary: command or shell script you want run. - - name: --file -f - type: string - short-summary: files will be used by the command, use '.' to attach the current folder. -""" - -helps['aks command result'] = """ - type: command - short-summary: fetch result from previously triggered 'aks command invoke'. - parameters: - - name: --command-id -i - type: string - short-summary: commandId returned from 'aks command invoke'. -""" - helps['aks maintenanceconfiguration'] = """ type: group short-summary: Commands to manage maintenance configurations in managed Kubernetes cluster. diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index 85813119af1..38fd102debf 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -196,13 +196,6 @@ def load_arguments(self, _): with self.argument_context('aks nodepool') as c: c.argument('cluster_name', type=str, help='The cluster name.') - with self.argument_context('aks command invoke') as c: - c.argument('command_string', type=str, options_list=["--command", "-c"], help='the command to run') - c.argument('command_files', options_list=["--file", "-f"], required=False, action="append", help='attach any files the command may use, or use \'.\' to upload the current folder.') - - with self.argument_context('aks command result') as c: - c.argument('command_id', type=str, options_list=["--command-id", "-i"], help='the command ID from "aks command invoke"') - for scope in ['aks nodepool add']: with self.argument_context(scope) as c: c.argument('nodepool_name', type=str, options_list=['--name', '-n'], validator=validate_nodepool_name, help='The node pool name.') diff --git a/src/aks-preview/azext_aks_preview/commands.py b/src/aks-preview/azext_aks_preview/commands.py index a8e31f01ce2..f3b477323c4 100644 --- a/src/aks-preview/azext_aks_preview/commands.py +++ b/src/aks-preview/azext_aks_preview/commands.py @@ -16,7 +16,6 @@ from ._format import aks_upgrades_table_format from ._format import aks_pod_identities_table_format from ._format import aks_pod_identity_exceptions_table_format -from ._format import aks_run_command_result_format def load_command_table(self, _): @@ -78,12 +77,6 @@ def load_command_table(self, _): g.custom_command('get-versions', 'aks_get_versions', table_transformer=aks_versions_table_format) - with self.command_group('aks command', managed_clusters_sdk, client_factory=cf_managed_clusters) as g: - g.custom_command('invoke', 'aks_runcommand', supports_no_wait=True, - table_transformer=aks_run_command_result_format) - g.custom_command('result', 'aks_command_result', - supports_no_wait=False, table_transformer=aks_run_command_result_format) - # AKS maintenance configuration commands with self.command_group('aks maintenanceconfiguration', maintenance_configuration_sdk, client_factory=cf_maintenance_configurations) as g: g.custom_command('list', 'aks_maintenanceconfiguration_list') diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index ff716640275..6a7e55af1a8 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -83,7 +83,6 @@ ManagedClusterPodIdentity, ManagedClusterPodIdentityException, UserAssignedIdentity, - RunCommandRequest, ComponentsQit0EtSchemasManagedclusterpropertiesPropertiesIdentityprofileAdditionalproperties) from ._client_factory import cf_resource_groups from ._client_factory import get_auth_management_client @@ -2294,115 +2293,6 @@ def aks_upgrade(cmd, # pylint: disable=unused-argument, too-many-return-state return sdk_no_wait(no_wait, client.begin_create_or_update, resource_group_name, name, instance, headers=headers) -def aks_runcommand(cmd, client, resource_group_name, name, command_string="", command_files=None): - colorama.init() - - mc = client.get(resource_group_name, name) - - if not command_string: - raise CLIError('Command cannot be empty.') - - request_payload = RunCommandRequest(command=command_string) - request_payload.context = _get_command_context(command_files) - if mc.aad_profile is not None and mc.aad_profile.managed: - request_payload.cluster_token = _get_dataplane_aad_token( - cmd.cli_ctx, "6dae42f8-4368-4678-94ff-3960e28e3630") - - commandResultFuture = client.begin_run_command( - resource_group_name, name, request_payload, polling_interval=5, retry_total=0) - - return _print_command_result(cmd.cli_ctx, commandResultFuture.result(300)) - - -def aks_command_result(cmd, client, resource_group_name, name, command_id=""): - if not command_id: - raise CLIError('CommandID cannot be empty.') - - commandResult = client.get_command_result( - resource_group_name, name, command_id) - return _print_command_result(cmd.cli_ctx, commandResult) - - -def _print_command_result(cli_ctx, commandResult): - # cli_ctx.data['safe_params'] contains list of parameter name user typed in, without value. - # cli core also use this calculate ParameterSetName header for all http request from cli. - if cli_ctx.data['safe_params'] is None or "-o" in cli_ctx.data['safe_params'] or "--output" in cli_ctx.data['safe_params']: - # user specified output format, honor their choice, return object to render pipeline - return commandResult - else: - # user didn't specified any format, we can customize the print for best experience - if commandResult.provisioning_state == "Succeeded": - # succeed, print exitcode, and logs - print(f"{colorama.Fore.GREEN}command started at {commandResult.started_at}, finished at {commandResult.finished_at}, with exitcode={commandResult.exit_code}{colorama.Style.RESET_ALL}") - print(commandResult.logs) - return - - if commandResult.provisioning_state == "Failed": - # failed, print reason in error - print( - f"{colorama.Fore.RED}command failed with reason: {commandResult.reason}{colorama.Style.RESET_ALL}") - return - - # *-ing state - print(f"{colorama.Fore.BLUE}command is in : {commandResult.provisioning_state} state{colorama.Style.RESET_ALL}") - return None - - -def _get_command_context(command_files): - if not command_files: - return "" - - filesToAttach = {} - # . means to attach current folder, cannot combine more files. (at least for now) - if len(command_files) == 1 and command_files[0] == ".": - # current folder - cwd = os.getcwd() - for filefolder, _, files in os.walk(cwd): - for file in files: - # retain folder structure - rel = os.path.relpath(filefolder, cwd) - filesToAttach[os.path.join( - filefolder, file)] = os.path.join(rel, file) - else: - for file in command_files: - if file == ".": - raise CLIError( - ". is used to attach current folder, not expecting other attachements.") - if os.path.isfile(file): - # for individual attached file, flatten them to same folder - filesToAttach[file] = os.path.basename(file) - else: - raise CLIError(f"{file} is not valid file, or not accessable.") - - if len(filesToAttach) < 1: - logger.debug("no files to attach!") - return "" - - zipStream = io.BytesIO() - zipFile = zipfile.ZipFile(zipStream, "w") - for _, (osfile, zipEntry) in enumerate(filesToAttach.items()): - zipFile.write(osfile, zipEntry) - # zipFile.printdir() // use this to debug - zipFile.close() - - return str(base64.encodebytes(zipStream.getbuffer()), "utf-8") - - -def _get_dataplane_aad_token(cli_ctx, serverAppId): - # this function is mostly copied from keyvault cli - import adal - try: - return Profile(cli_ctx=cli_ctx).get_raw_token(resource=serverAppId)[0][2].get('accessToken') - except adal.AdalError as err: - # pylint: disable=no-member - if (hasattr(err, 'error_response') and - ('error_description' in err.error_response) and - ('AADSTS70008:' in err.error_response['error_description'])): - raise CLIError( - "Credentials have expired due to inactivity. Please run 'az login'") - raise CLIError(err) - - def _upgrade_single_nodepool_image_version(no_wait, client, resource_group_name, cluster_name, nodepool_name): return sdk_no_wait(no_wait, client.begin_upgrade_node_image_version, resource_group_name, cluster_name, nodepool_name) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/recordings/test_aks_run_command.yaml b/src/aks-preview/azext_aks_preview/tests/latest/recordings/test_aks_run_command.yaml deleted file mode 100644 index ac50ac35768..00000000000 --- a/src/aks-preview/azext_aks_preview/tests/latest/recordings/test_aks_run_command.yaml +++ /dev/null @@ -1,754 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-resource/18.0.0 Python/3.8.10 (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/clitest000001?api-version=2021-04-01 - response: - body: - string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest000001","name":"clitest000001","type":"Microsoft.Resources/resourceGroups","location":"westus2","tags":{"product":"azurecli","cause":"automation","date":"2021-07-27T08:05:37Z"},"properties":{"provisioningState":"Succeeded"}}' - headers: - cache-control: - - no-cache - content-length: - - '313' - content-type: - - application/json; charset=utf-8 - date: - - Tue, 27 Jul 2021 08:05:39 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 -- request: - body: '{"identity": {"type": "SystemAssigned"}, "properties": {"kubernetesVersion": - "", "dnsPrefix": "cmdtesty2o-clitestrsta3ftbu-79a739", "agentPoolProfiles": - [{"count": 1, "vmSize": "Standard_DS2_v2", "osType": "Linux", "type": "VirtualMachineScaleSets", - "mode": "System", "enableNodePublicIP": false, "scaleSetPriority": "Regular", - "scaleSetEvictionPolicy": "Delete", "spotMaxPrice": -1.0, "enableEncryptionAtHost": - false, "enableUltraSSD": false, "enableFIPS": false, "name": "c000003"}], "linuxProfile": - {"adminUsername": "azureuser", "ssh": {"publicKeys": [{"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXXa38mTCuXRXIDO3Ik0tM6IiXqiJXpW17s43ngA/pOaZyBb+JzQ38BoItloVUyr8DuwYQSopy0aqOyWAj+ii/k4+tqrRe6PFGUQd1qMS7+2v/QuvLbhzsFVOFk64yRluri4T50m3GSu71jeubNvBVmAn84YZCwrN9h1OpjYRHQ3Rhmw6OufvBhW7ZWKUD49stNc1KKZsTvRmg7pN1eVmKHykpotdf6/UcEebDperarPSZm0Hl6JqzxzF5BbWVyFdMGGVqiFedD6aknWA0uVmnfwZen8VVHWpfTAyQj3FCS4PTl/g83Dxef77DiaV3wx6E6pxXV+EkPRgiVi/HIzVz"}]}}, - "addonProfiles": {}, "enableRBAC": true, "enablePodSecurityPolicy": false, "networkProfile": - {"networkPlugin": "kubenet", "podCidr": "10.244.0.0/16", "serviceCidr": "10.0.0.0/16", - "dnsServiceIP": "10.0.0.10", "dockerBridgeCidr": "172.17.0.1/16", "outboundType": - "loadBalancer", "loadBalancerSku": "standard"}, "disableLocalAccounts": false}, - "location": "westus2"}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - Content-Length: - - '1319' - Content-Type: - - application/json - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: PUT - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest000001/providers/Microsoft.ContainerService/managedClusters/cmdtest000002?api-version=2021-05-01 - response: - body: - string: "{\n \"id\": \"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/clitest000001/providers/Microsoft.ContainerService/managedClusters/cmdtest000002\",\n - \ \"location\": \"westus2\",\n \"name\": \"cmdtest000002\",\n \"type\": - \"Microsoft.ContainerService/ManagedClusters\",\n \"properties\": {\n \"provisioningState\": - \"Creating\",\n \"powerState\": {\n \"code\": \"Running\"\n },\n \"kubernetesVersion\": - \"1.19.11\",\n \"dnsPrefix\": \"cmdtesty2o-clitestrsta3ftbu-79a739\",\n - \ \"fqdn\": \"cmdtesty2o-clitestrsta3ftbu-79a739-be77ece6.hcp.westus2.azmk8s.io\",\n - \ \"azurePortalFQDN\": \"cmdtesty2o-clitestrsta3ftbu-79a739-be77ece6.portal.hcp.westus2.azmk8s.io\",\n - \ \"agentPoolProfiles\": [\n {\n \"name\": \"c000003\",\n \"count\": - 1,\n \"vmSize\": \"Standard_DS2_v2\",\n \"osDiskSizeGB\": 128,\n \"osDiskType\": - \"Managed\",\n \"kubeletDiskType\": \"OS\",\n \"maxPods\": 110,\n - \ \"type\": \"VirtualMachineScaleSets\",\n \"provisioningState\": \"Creating\",\n - \ \"powerState\": {\n \"code\": \"Running\"\n },\n \"orchestratorVersion\": - \"1.19.11\",\n \"enableNodePublicIP\": false,\n \"nodeLabels\": {},\n - \ \"mode\": \"System\",\n \"enableEncryptionAtHost\": false,\n \"enableUltraSSD\": - false,\n \"osType\": \"Linux\",\n \"osSKU\": \"Ubuntu\",\n \"nodeImageVersion\": - \"AKSUbuntu-1804gen2containerd-2021.07.03\",\n \"enableFIPS\": false\n - \ }\n ],\n \"linuxProfile\": {\n \"adminUsername\": \"azureuser\",\n - \ \"ssh\": {\n \"publicKeys\": [\n {\n \"keyData\": \"ssh-rsa - AAAAB3NzaC1yc2EAAAADAQABAAABAQDXXa38mTCuXRXIDO3Ik0tM6IiXqiJXpW17s43ngA/pOaZyBb+JzQ38BoItloVUyr8DuwYQSopy0aqOyWAj+ii/k4+tqrRe6PFGUQd1qMS7+2v/QuvLbhzsFVOFk64yRluri4T50m3GSu71jeubNvBVmAn84YZCwrN9h1OpjYRHQ3Rhmw6OufvBhW7ZWKUD49stNc1KKZsTvRmg7pN1eVmKHykpotdf6/UcEebDperarPSZm0Hl6JqzxzF5BbWVyFdMGGVqiFedD6aknWA0uVmnfwZen8VVHWpfTAyQj3FCS4PTl/g83Dxef77DiaV3wx6E6pxXV+EkPRgiVi/HIzVz\"\n - \ }\n ]\n }\n },\n \"servicePrincipalProfile\": {\n \"clientId\":\"00000000-0000-0000-0000-000000000001\"\n - \ },\n \"nodeResourceGroup\": \"MC_clitest000001_cmdtest000002_westus2\",\n - \ \"enableRBAC\": true,\n \"enablePodSecurityPolicy\": false,\n \"networkProfile\": - {\n \"networkPlugin\": \"kubenet\",\n \"loadBalancerSku\": \"standard\",\n - \ \"loadBalancerProfile\": {\n \"managedOutboundIPs\": {\n \"count\": - 1\n }\n },\n \"podCidr\": \"10.244.0.0/16\",\n \"serviceCidr\": - \"10.0.0.0/16\",\n \"dnsServiceIP\": \"10.0.0.10\",\n \"dockerBridgeCidr\": - \"172.17.0.1/16\",\n \"outboundType\": \"loadBalancer\"\n },\n \"maxAgentPools\": - 100,\n \"disableLocalAccounts\": false\n },\n \"identity\": {\n \"type\": - \"SystemAssigned\",\n \"principalId\":\"00000000-0000-0000-0000-000000000001\",\n - \ \"tenantId\": \"72f988bf-86f1-41af-91ab-2d7cd011db47\"\n },\n \"sku\": - {\n \"name\": \"Basic\",\n \"tier\": \"Free\"\n }\n }" - headers: - azure-asyncoperation: - - https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - cache-control: - - no-cache - content-length: - - '2689' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:05:43 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-ms-ratelimit-remaining-subscription-writes: - - '1199' - status: - code: 201 - message: Created -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - response: - body: - string: "{\n \"name\": \"018eba4f-d0ab-f44a-82f1-daf391ac19e5\",\n \"status\": - \"InProgress\",\n \"startTime\": \"2021-07-27T08:05:43.6833333Z\"\n }" - headers: - cache-control: - - no-cache - content-length: - - '126' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:06:13 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - response: - body: - string: "{\n \"name\": \"018eba4f-d0ab-f44a-82f1-daf391ac19e5\",\n \"status\": - \"InProgress\",\n \"startTime\": \"2021-07-27T08:05:43.6833333Z\"\n }" - headers: - cache-control: - - no-cache - content-length: - - '126' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:06:44 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - response: - body: - string: "{\n \"name\": \"018eba4f-d0ab-f44a-82f1-daf391ac19e5\",\n \"status\": - \"InProgress\",\n \"startTime\": \"2021-07-27T08:05:43.6833333Z\"\n }" - headers: - cache-control: - - no-cache - content-length: - - '126' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:07:14 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - response: - body: - string: "{\n \"name\": \"018eba4f-d0ab-f44a-82f1-daf391ac19e5\",\n \"status\": - \"InProgress\",\n \"startTime\": \"2021-07-27T08:05:43.6833333Z\"\n }" - headers: - cache-control: - - no-cache - content-length: - - '126' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:07:43 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - response: - body: - string: "{\n \"name\": \"018eba4f-d0ab-f44a-82f1-daf391ac19e5\",\n \"status\": - \"InProgress\",\n \"startTime\": \"2021-07-27T08:05:43.6833333Z\"\n }" - headers: - cache-control: - - no-cache - content-length: - - '126' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:08:13 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - response: - body: - string: "{\n \"name\": \"018eba4f-d0ab-f44a-82f1-daf391ac19e5\",\n \"status\": - \"InProgress\",\n \"startTime\": \"2021-07-27T08:05:43.6833333Z\"\n }" - headers: - cache-control: - - no-cache - content-length: - - '126' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:08:44 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.ContainerService/locations/westus2/operations/4fba8e01-abd0-4af4-82f1-daf391ac19e5?api-version=2016-03-30 - response: - body: - string: "{\n \"name\": \"018eba4f-d0ab-f44a-82f1-daf391ac19e5\",\n \"status\": - \"Succeeded\",\n \"startTime\": \"2021-07-27T08:05:43.6833333Z\",\n \"endTime\": - \"2021-07-27T08:09:13.5145207Z\"\n }" - headers: - cache-control: - - no-cache - content-length: - - '170' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:09:14 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks create - Connection: - - keep-alive - ParameterSetName: - - --resource-group --name --nodepool-name --generate-ssh-keys --vm-set-type - --node-count -o - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest000001/providers/Microsoft.ContainerService/managedClusters/cmdtest000002?api-version=2021-05-01 - response: - body: - string: "{\n \"id\": \"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/clitest000001/providers/Microsoft.ContainerService/managedClusters/cmdtest000002\",\n - \ \"location\": \"westus2\",\n \"name\": \"cmdtest000002\",\n \"type\": - \"Microsoft.ContainerService/ManagedClusters\",\n \"properties\": {\n \"provisioningState\": - \"Succeeded\",\n \"powerState\": {\n \"code\": \"Running\"\n },\n \"kubernetesVersion\": - \"1.19.11\",\n \"dnsPrefix\": \"cmdtesty2o-clitestrsta3ftbu-79a739\",\n - \ \"fqdn\": \"cmdtesty2o-clitestrsta3ftbu-79a739-be77ece6.hcp.westus2.azmk8s.io\",\n - \ \"azurePortalFQDN\": \"cmdtesty2o-clitestrsta3ftbu-79a739-be77ece6.portal.hcp.westus2.azmk8s.io\",\n - \ \"agentPoolProfiles\": [\n {\n \"name\": \"c000003\",\n \"count\": - 1,\n \"vmSize\": \"Standard_DS2_v2\",\n \"osDiskSizeGB\": 128,\n \"osDiskType\": - \"Managed\",\n \"kubeletDiskType\": \"OS\",\n \"maxPods\": 110,\n - \ \"type\": \"VirtualMachineScaleSets\",\n \"provisioningState\": \"Succeeded\",\n - \ \"powerState\": {\n \"code\": \"Running\"\n },\n \"orchestratorVersion\": - \"1.19.11\",\n \"enableNodePublicIP\": false,\n \"nodeLabels\": {},\n - \ \"mode\": \"System\",\n \"enableEncryptionAtHost\": false,\n \"enableUltraSSD\": - false,\n \"osType\": \"Linux\",\n \"osSKU\": \"Ubuntu\",\n \"nodeImageVersion\": - \"AKSUbuntu-1804gen2containerd-2021.07.03\",\n \"enableFIPS\": false\n - \ }\n ],\n \"linuxProfile\": {\n \"adminUsername\": \"azureuser\",\n - \ \"ssh\": {\n \"publicKeys\": [\n {\n \"keyData\": \"ssh-rsa - AAAAB3NzaC1yc2EAAAADAQABAAABAQDXXa38mTCuXRXIDO3Ik0tM6IiXqiJXpW17s43ngA/pOaZyBb+JzQ38BoItloVUyr8DuwYQSopy0aqOyWAj+ii/k4+tqrRe6PFGUQd1qMS7+2v/QuvLbhzsFVOFk64yRluri4T50m3GSu71jeubNvBVmAn84YZCwrN9h1OpjYRHQ3Rhmw6OufvBhW7ZWKUD49stNc1KKZsTvRmg7pN1eVmKHykpotdf6/UcEebDperarPSZm0Hl6JqzxzF5BbWVyFdMGGVqiFedD6aknWA0uVmnfwZen8VVHWpfTAyQj3FCS4PTl/g83Dxef77DiaV3wx6E6pxXV+EkPRgiVi/HIzVz\"\n - \ }\n ]\n }\n },\n \"servicePrincipalProfile\": {\n \"clientId\":\"00000000-0000-0000-0000-000000000001\"\n - \ },\n \"nodeResourceGroup\": \"MC_clitest000001_cmdtest000002_westus2\",\n - \ \"enableRBAC\": true,\n \"enablePodSecurityPolicy\": false,\n \"networkProfile\": - {\n \"networkPlugin\": \"kubenet\",\n \"loadBalancerSku\": \"Standard\",\n - \ \"loadBalancerProfile\": {\n \"managedOutboundIPs\": {\n \"count\": - 1\n },\n \"effectiveOutboundIPs\": [\n {\n \"id\": \"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_clitest000001_cmdtest000002_westus2/providers/Microsoft.Network/publicIPAddresses/c39e9d69-8f19-4b9b-bb29-77b8b19958ed\"\n - \ }\n ]\n },\n \"podCidr\": \"10.244.0.0/16\",\n \"serviceCidr\": - \"10.0.0.0/16\",\n \"dnsServiceIP\": \"10.0.0.10\",\n \"dockerBridgeCidr\": - \"172.17.0.1/16\",\n \"outboundType\": \"loadBalancer\"\n },\n \"maxAgentPools\": - 100,\n \"identityProfile\": {\n \"kubeletidentity\": {\n \"resourceId\": - \"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/MC_clitest000001_cmdtest000002_westus2/providers/Microsoft.ManagedIdentity/userAssignedIdentities/cmdtest000002-agentpool\",\n - \ \"clientId\":\"00000000-0000-0000-0000-000000000001\",\n \"objectId\":\"00000000-0000-0000-0000-000000000001\"\n - \ }\n },\n \"disableLocalAccounts\": false\n },\n \"identity\": {\n - \ \"type\": \"SystemAssigned\",\n \"principalId\":\"00000000-0000-0000-0000-000000000001\",\n - \ \"tenantId\": \"72f988bf-86f1-41af-91ab-2d7cd011db47\"\n },\n \"sku\": - {\n \"name\": \"Basic\",\n \"tier\": \"Free\"\n }\n }" - headers: - cache-control: - - no-cache - content-length: - - '3352' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:09:14 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: null - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - CommandName: - - aks command invoke - Connection: - - keep-alive - ParameterSetName: - - -g -n -o -c - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest000001/providers/Microsoft.ContainerService/managedClusters/cmdtest000002?api-version=2021-05-01 - response: - body: - string: "{\n \"id\": \"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/clitest000001/providers/Microsoft.ContainerService/managedClusters/cmdtest000002\",\n - \ \"location\": \"westus2\",\n \"name\": \"cmdtest000002\",\n \"type\": - \"Microsoft.ContainerService/ManagedClusters\",\n \"properties\": {\n \"provisioningState\": - \"Succeeded\",\n \"powerState\": {\n \"code\": \"Running\"\n },\n \"kubernetesVersion\": - \"1.19.11\",\n \"dnsPrefix\": \"cmdtesty2o-clitestrsta3ftbu-79a739\",\n - \ \"fqdn\": \"cmdtesty2o-clitestrsta3ftbu-79a739-be77ece6.hcp.westus2.azmk8s.io\",\n - \ \"azurePortalFQDN\": \"cmdtesty2o-clitestrsta3ftbu-79a739-be77ece6.portal.hcp.westus2.azmk8s.io\",\n - \ \"agentPoolProfiles\": [\n {\n \"name\": \"c000003\",\n \"count\": - 1,\n \"vmSize\": \"Standard_DS2_v2\",\n \"osDiskSizeGB\": 128,\n \"osDiskType\": - \"Managed\",\n \"kubeletDiskType\": \"OS\",\n \"maxPods\": 110,\n - \ \"type\": \"VirtualMachineScaleSets\",\n \"provisioningState\": \"Succeeded\",\n - \ \"powerState\": {\n \"code\": \"Running\"\n },\n \"orchestratorVersion\": - \"1.19.11\",\n \"enableNodePublicIP\": false,\n \"nodeLabels\": {},\n - \ \"mode\": \"System\",\n \"enableEncryptionAtHost\": false,\n \"enableUltraSSD\": - false,\n \"osType\": \"Linux\",\n \"osSKU\": \"Ubuntu\",\n \"nodeImageVersion\": - \"AKSUbuntu-1804gen2containerd-2021.07.03\",\n \"enableFIPS\": false\n - \ }\n ],\n \"linuxProfile\": {\n \"adminUsername\": \"azureuser\",\n - \ \"ssh\": {\n \"publicKeys\": [\n {\n \"keyData\": \"ssh-rsa - AAAAB3NzaC1yc2EAAAADAQABAAABAQDXXa38mTCuXRXIDO3Ik0tM6IiXqiJXpW17s43ngA/pOaZyBb+JzQ38BoItloVUyr8DuwYQSopy0aqOyWAj+ii/k4+tqrRe6PFGUQd1qMS7+2v/QuvLbhzsFVOFk64yRluri4T50m3GSu71jeubNvBVmAn84YZCwrN9h1OpjYRHQ3Rhmw6OufvBhW7ZWKUD49stNc1KKZsTvRmg7pN1eVmKHykpotdf6/UcEebDperarPSZm0Hl6JqzxzF5BbWVyFdMGGVqiFedD6aknWA0uVmnfwZen8VVHWpfTAyQj3FCS4PTl/g83Dxef77DiaV3wx6E6pxXV+EkPRgiVi/HIzVz\"\n - \ }\n ]\n }\n },\n \"servicePrincipalProfile\": {\n \"clientId\":\"00000000-0000-0000-0000-000000000001\"\n - \ },\n \"nodeResourceGroup\": \"MC_clitest000001_cmdtest000002_westus2\",\n - \ \"enableRBAC\": true,\n \"enablePodSecurityPolicy\": false,\n \"networkProfile\": - {\n \"networkPlugin\": \"kubenet\",\n \"loadBalancerSku\": \"Standard\",\n - \ \"loadBalancerProfile\": {\n \"managedOutboundIPs\": {\n \"count\": - 1\n },\n \"effectiveOutboundIPs\": [\n {\n \"id\": \"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/MC_clitest000001_cmdtest000002_westus2/providers/Microsoft.Network/publicIPAddresses/c39e9d69-8f19-4b9b-bb29-77b8b19958ed\"\n - \ }\n ]\n },\n \"podCidr\": \"10.244.0.0/16\",\n \"serviceCidr\": - \"10.0.0.0/16\",\n \"dnsServiceIP\": \"10.0.0.10\",\n \"dockerBridgeCidr\": - \"172.17.0.1/16\",\n \"outboundType\": \"loadBalancer\"\n },\n \"maxAgentPools\": - 100,\n \"identityProfile\": {\n \"kubeletidentity\": {\n \"resourceId\": - \"/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/MC_clitest000001_cmdtest000002_westus2/providers/Microsoft.ManagedIdentity/userAssignedIdentities/cmdtest000002-agentpool\",\n - \ \"clientId\":\"00000000-0000-0000-0000-000000000001\",\n \"objectId\":\"00000000-0000-0000-0000-000000000001\"\n - \ }\n },\n \"disableLocalAccounts\": false\n },\n \"identity\": {\n - \ \"type\": \"SystemAssigned\",\n \"principalId\":\"00000000-0000-0000-0000-000000000001\",\n - \ \"tenantId\": \"72f988bf-86f1-41af-91ab-2d7cd011db47\"\n },\n \"sku\": - {\n \"name\": \"Basic\",\n \"tier\": \"Free\"\n }\n }" - headers: - cache-control: - - no-cache - content-length: - - '3352' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:09:15 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -- request: - body: '{"command": "kubectl get pods -A", "context": ""}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - CommandName: - - aks command invoke - Connection: - - keep-alive - Content-Length: - - '49' - Content-Type: - - application/json - ParameterSetName: - - -g -n -o -c - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: POST - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest000001/providers/Microsoft.ContainerService/managedClusters/cmdtest000002/runCommand?api-version=2021-05-01 - response: - body: - string: '' - headers: - cache-control: - - no-cache - content-length: - - '0' - date: - - Tue, 27 Jul 2021 08:09:17 GMT - expires: - - '-1' - location: - - https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest000001/providers/Microsoft.ContainerService/managedclusters/cmdtest000002/commandResults/d909074c8d924c219b40c58f172b3ee3?api-version=2021-05-01 - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - x-content-type-options: - - nosniff - x-ms-ratelimit-remaining-subscription-writes: - - '1199' - status: - code: 202 - message: Accepted -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - CommandName: - - aks command invoke - Connection: - - keep-alive - ParameterSetName: - - -g -n -o -c - User-Agent: - - AZURECLI/2.26.1 azsdk-python-azure-mgmt-containerservice/16.0.0 Python/3.8.10 - (Linux-5.4.0-1051-azure-x86_64-with-glibc2.27) - method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest000001/providers/Microsoft.ContainerService/managedclusters/cmdtest000002/commandResults/d909074c8d924c219b40c58f172b3ee3?api-version=2021-05-01 - response: - body: - string: "{\n \"id\": \"d909074c8d924c219b40c58f172b3ee3\",\n \"properties\": - {\n \"provisioningState\": \"Succeeded\",\n \"exitCode\": 0,\n \"startedAt\": - \"2021-07-27T08:09:18Z\",\n \"finishedAt\": \"2021-07-27T08:09:20Z\",\n - \ \"logs\": \"NAMESPACE NAME READY - \ STATUS RESTARTS AGE\\naks-command command-d909074c8d924c219b40c58f172b3ee3 - \ 1/1 Running 0 3s\\nkube-system azure-ip-masq-agent-w59zr - \ 1/1 Running 0 68s\\nkube-system coredns-76c97c8599-m7szr - \ 1/1 Running 0 94s\\nkube-system coredns-76c97c8599-sd65f - \ 1/1 Running 0 52s\\nkube-system coredns-autoscaler-599949fd86-b4dlv - \ 1/1 Running 0 92s\\nkube-system konnectivity-agent-6646dfc4c7-ctmpx - \ 1/1 Running 0 92s\\nkube-system kube-proxy-66z7r - \ 1/1 Running 0 68s\\nkube-system - \ metrics-server-77c8679d7d-zm6bm 1/1 Running 0 94s\\n\"\n - \ }\n }" - headers: - cache-control: - - no-cache - content-length: - - '1038' - content-type: - - application/json - date: - - Tue, 27 Jul 2021 08:09:23 GMT - expires: - - '-1' - pragma: - - no-cache - server: - - nginx - strict-transport-security: - - max-age=31536000; includeSubDomains - transfer-encoding: - - chunked - vary: - - Accept-Encoding - x-content-type-options: - - nosniff - status: - code: 200 - message: OK -version: 1 diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_run_command.py b/src/aks-preview/azext_aks_preview/tests/latest/test_run_command.py deleted file mode 100644 index ad5fa23b880..00000000000 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_run_command.py +++ /dev/null @@ -1,78 +0,0 @@ -# -------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See License.txt in the project root for license information. -# -------------------------------------------------------------------------------------------- -import unittest - -from azure.mgmt.containerservice.v2019_11_01.models import ManagedClusterLoadBalancerProfile -from azure.mgmt.containerservice.v2019_11_01.models import ManagedClusterLoadBalancerProfileManagedOutboundIPs -from azure.mgmt.containerservice.v2019_11_01.models import ManagedClusterLoadBalancerProfileOutboundIPPrefixes -from azure.mgmt.containerservice.v2019_11_01.models import ManagedClusterLoadBalancerProfileOutboundIPs -from azure.cli.core.util import CLIError -from azext_aks_preview.custom import _get_command_context -from .recording_processors import KeyReplacer -from azure.cli.testsdk import ( - ResourceGroupPreparer, RoleBasedServicePrincipalPreparer, ScenarioTest, live_only) -from azure_devtools.scenario_tests import AllowLargeResponse -from azure.cli.testsdk.checkers import ( - StringContainCheck, StringContainCheckIgnoreCase) -from .test_aks_commands import _get_test_data_file - - -class TestRunCommand(ScenarioTest): - def __init__(self, method_name): - super(TestRunCommand, self).__init__( - method_name, recording_processors=[KeyReplacer()] - ) - - def test_get_command_context_invalid_file(self): - with self.assertRaises(CLIError) as cm: - _get_command_context([_get_test_data_file("notexistingfile")]) - self.assertIn('notexistingfile is not valid file, or not accessable.', str( - cm.exception)) - - def test_get_command_context_mixed(self): - with self.assertRaises(CLIError) as cm: - _get_command_context( - [".", _get_test_data_file("kubeletconfig.json")]) - self.assertEqual(str( - cm.exception), '. is used to attach current folder, not expecting other attachements.') - - def test_get_command_context_empty(self): - context = _get_command_context([]) - self.assertEqual(context, "") - - def test_get_command_context_valid(self): - context = _get_command_context( - [_get_test_data_file("kubeletconfig.json"), _get_test_data_file("linuxosconfig.json")]) - self.assertNotEqual(context, '') - - @AllowLargeResponse() - @ResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='westus2') - def test_aks_run_command(self, resource_group, resource_group_location): - # kwargs for string formatting - aks_name = self.create_random_name('cmdtest', 16) - node_pool_name = self.create_random_name('c', 6) - self.kwargs.update({ - 'resource_group': resource_group, - 'name': aks_name, - 'node_pool_name': node_pool_name - }) - - create_cmd = 'aks create --resource-group={resource_group} --name={name} ' \ - '--nodepool-name {node_pool_name} ' \ - '--generate-ssh-keys ' \ - '--vm-set-type VirtualMachineScaleSets --node-count=1 ' \ - '-o json' - self.cmd(create_cmd, checks=[ - self.check('provisioningState', 'Succeeded') - ]) - - runCommand = 'aks command invoke -g {resource_group} -n {name} -o json -c "kubectl get pods -A"' - self.cmd(runCommand, [ - self.check('provisioningState', 'Succeeded'), - self.check('exitCode', 0), - ]) - -if __name__ == '__main__': - unittest.main()