From 9390ce579f2dd2307371eefb5c0f46b1bb17f013 Mon Sep 17 00:00:00 2001 From: Norberto Arrieta Date: Tue, 11 Apr 2023 10:26:51 -0700 Subject: [PATCH] Add support for Azure Clouds (#2795) * Add support for Azure Clouds --------- Co-authored-by: narrieta --- test-requirements.txt | 1 + .../orchestrator/lib/agent_test_loader.py | 15 ++- .../orchestrator/lib/agent_test_suite.py | 7 +- .../lib/agent_test_suite_combinator.py | 61 ++++++++++--- tests_e2e/orchestrator/runbook.yml | 23 +++-- .../sample_runbooks/existing_vm.yml | 22 ++++- tests_e2e/pipeline/pipeline-cleanup.yml | 91 ++++++++++--------- tests_e2e/pipeline/pipeline.yml | 26 ++++-- tests_e2e/test_suites/images.yml | 60 +++++++++--- tests_e2e/tests/lib/agent_test_context.py | 6 +- tests_e2e/tests/lib/azure_clouds.py | 24 +++++ tests_e2e/tests/lib/identifiers.py | 5 +- tests_e2e/tests/lib/virtual_machine.py | 16 +++- tests_e2e/tests/lib/vm_extension.py | 10 +- 14 files changed, 261 insertions(+), 106 deletions(-) create mode 100644 tests_e2e/tests/lib/azure_clouds.py diff --git a/test-requirements.txt b/test-requirements.txt index 3c54ab997..6b8a78bd0 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -19,3 +19,4 @@ azure-core azure-identity azure-mgmt-compute>=22.1.0 azure-mgmt-resource>=15.0.0 +msrestazure diff --git a/tests_e2e/orchestrator/lib/agent_test_loader.py b/tests_e2e/orchestrator/lib/agent_test_loader.py index a0f0bfaaf..f1a2dfc9d 100644 --- a/tests_e2e/orchestrator/lib/agent_test_loader.py +++ b/tests_e2e/orchestrator/lib/agent_test_loader.py @@ -48,7 +48,7 @@ class VmImageInfo(object): # The URN of the image (publisher, offer, version separated by spaces) urn: str # Indicates that the image is available only on those locations. If empty, the image should be available in all locations - locations: List[str] + locations: Dict[str, List[str]] # Indicates that the image is available only for those VM sizes. If empty, the image should be available for all VM sizes vm_sizes: List[str] @@ -109,8 +109,9 @@ def _validate(self): if suite.location != '': for suite_image in suite.images: for image in self.images[suite_image]: - if len(image.locations) > 0: - if suite.location not in image.locations: + # If the image has a location restriction, validate that it is available on the location the suite must run on + if image.locations: + if not any(suite.location in l for l in image.locations.values()): raise Exception(f"Test suite {suite.name} must be executed in {suite.location}, but <{image.urn}> is not available in that location") @staticmethod @@ -223,14 +224,18 @@ def _load_images() -> Dict[str, List[VmImageInfo]]: i = VmImageInfo() if isinstance(description, str): i.urn = description - i.locations = [] + i.locations = {} i.vm_sizes = [] else: if "urn" not in description: raise Exception(f"Image {name} is missing the 'urn' property: {description}") i.urn = description["urn"] - i.locations = description["locations"] if "locations" in description else [] + i.locations = description["locations"] if "locations" in description else {} i.vm_sizes = description["vm_sizes"] if "vm_sizes" in description else [] + for cloud in i.locations.keys(): + if cloud not in ["AzureCloud", "AzureChinaCloud", "AzureUSGovernment"]: + raise Exception(f"Invalid cloud {cloud} for image {name} in images.yml") + images[name] = [i] # now load the image-sets, mapping them to the images that we just computed diff --git a/tests_e2e/orchestrator/lib/agent_test_suite.py b/tests_e2e/orchestrator/lib/agent_test_suite.py index 0c95daf60..847ace756 100644 --- a/tests_e2e/orchestrator/lib/agent_test_suite.py +++ b/tests_e2e/orchestrator/lib/agent_test_suite.py @@ -40,8 +40,7 @@ ) from lisa.environment import EnvironmentStatus # pylint: disable=E0401 from lisa.messages import TestStatus, TestResultMessage # pylint: disable=E0401 -from lisa.sut_orchestrator import AZURE # pylint: disable=E0401 -from lisa.sut_orchestrator.azure.common import get_node_context, AzureNodeSchema # pylint: disable=E0401 +from lisa.sut_orchestrator.azure.common import get_node_context # pylint: disable=E0401 import makepkg from azurelinuxagent.common.version import AGENT_VERSION @@ -133,11 +132,11 @@ def __init__(self, metadata: TestSuiteMetadata) -> None: def _initialize(self, node: Node, variables: Dict[str, Any], lisa_working_path: str, lisa_log_path: str, lisa_log: Logger): connection_info = node.connection_info node_context = get_node_context(node) - runbook = node.capability.get_extended_runbook(AzureNodeSchema, AZURE) self.__context = self._Context( vm=VmIdentifier( - location=runbook.location, + cloud=self._get_required_parameter(variables, "c_cloud"), + location=self._get_required_parameter(variables, "c_location"), subscription=node.features._platform.subscription_id, resource_group=node_context.resource_group_name, name=node_context.vm_name), diff --git a/tests_e2e/orchestrator/lib/agent_test_suite_combinator.py b/tests_e2e/orchestrator/lib/agent_test_suite_combinator.py index 28fca0fad..423e54290 100644 --- a/tests_e2e/orchestrator/lib/agent_test_suite_combinator.py +++ b/tests_e2e/orchestrator/lib/agent_test_suite_combinator.py @@ -98,9 +98,21 @@ def _next(self) -> Optional[Dict[str, Any]]: return result _DEFAULT_LOCATIONS = { - "china": "china north 2", - "government": "usgovarizona", - "public": "westus2" + "AzureCloud": "westus2", + "AzureChinaCloud": "chinanorth2", + "AzureUSGovernment": "usgovarizona", + } + + _MARKETPLACE_IMAGE_INFORMATION_LOCATIONS = { + "AzureCloud": "", # empty indicates the default location used by LISA + "AzureChinaCloud": "chinanorth2", + "AzureUSGovernment": "usgovarizona", + } + + _SHARED_RESOURCE_GROUP_LOCATIONS = { + "AzureCloud": "", # empty indicates the default location used by LISA + "AzureChinaCloud": "chinanorth2", + "AzureUSGovernment": "usgovarizona", } def create_environment_for_existing_vm(self) -> List[Dict[str, Any]]: @@ -178,15 +190,23 @@ def create_environment_list(self) -> List[Dict[str, Any]]: raise Exception(f"Invalid URN: {image.urn}") name = f"{match.group('offer')}-{match.group('sku')}" - # If the runbook specified a location, use it. Then try the suite location, if any. Otherwise, check if the image specifies - # a list of locations and use any of them. If no location is specified so far, use the default. + location: str = None + # If the runbook specified a location, use it. if self.runbook.location != "": location = self.runbook.location + # Then try the suite location, if any. elif suite_info.location != '': location = suite_info.location - elif len(image.locations) > 0: - location = image.locations[0] - else: + # If the image has a location restriction, use any location where it is available. + # However, if it is not available on any location, skip the image. + elif image.locations: + image_locations = image.locations.get(self.runbook.cloud) + if image_locations is not None: + if len(image_locations) == 0: + continue + location = image_locations[0] + # If no location has been selected, use the default. + if location is None: location = AgentTestSuitesCombinator._DEFAULT_LOCATIONS[self.runbook.cloud] # If the runbook specified a VM size, use it. Else if the image specifies a list of VM sizes, use any of them. Otherwise, @@ -202,11 +222,14 @@ def create_environment_list(self) -> List[Dict[str, Any]]: # create an environment for exclusive use by this suite environment_list.append({ "c_marketplace_image": marketplace_image, + "c_cloud": self.runbook.cloud, "c_location": location, "c_vm_size": vm_size, "c_vhd": vhd, "c_test_suites": [suite_info], - "c_env_name": f"{name}-{suite_info.name}" + "c_env_name": f"{name}-{suite_info.name}", + "c_marketplace_image_information_location": self._MARKETPLACE_IMAGE_INFORMATION_LOCATIONS[self.runbook.cloud], + "c_shared_resource_group_location": self._SHARED_RESOURCE_GROUP_LOCATIONS[self.runbook.cloud] }) else: # add this suite to the shared environments @@ -216,27 +239,35 @@ def create_environment_list(self) -> List[Dict[str, Any]]: else: shared_environments[key] = { "c_marketplace_image": marketplace_image, + "c_cloud": self.runbook.cloud, "c_location": location, "c_vm_size": vm_size, "c_vhd": vhd, "c_test_suites": [suite_info], - "c_env_name": key + "c_env_name": key, + "c_marketplace_image_information_location": self._MARKETPLACE_IMAGE_INFORMATION_LOCATIONS[self.runbook.cloud], + "c_shared_resource_group_location": self._SHARED_RESOURCE_GROUP_LOCATIONS[self.runbook.cloud] } environment_list.extend(shared_environments.values()) + if len(environment_list) == 0: + raise Exception("No VM images were found to execute the test suites.") + log: logging.Logger = logging.getLogger("lisa") - log.info("******** Environments *****") - for e in environment_list: - log.info( - "{ c_marketplace_image: '%s', c_location: '%s', c_vm_size: '%s', c_vhd: '%s', c_test_suites: '%s', c_env_name: '%s' }", - e['c_marketplace_image'], e['c_location'], e['c_vm_size'], e['c_vhd'], [s.name for s in e['c_test_suites']], e['c_env_name']) + log.info("") + log.info("******** Agent Test Environments *****") + for environment in environment_list: + test_suites = [s.name for s in environment['c_test_suites']] + log.info("Settings for %s:\n%s\n", environment['c_env_name'], '\n'.join([f"\t{name}: {value if name != 'c_test_suites' else test_suites}" for name, value in environment.items()])) log.info("***************************") + log.info("") return environment_list _URN = re.compile(r"(?P[^\s:]+)[\s:](?P[^\s:]+)[\s:](?P[^\s:]+)[\s:](?P[^\s:]+)") + @staticmethod def _is_urn(urn: str) -> bool: # URNs can be given as ' ' or ':::' diff --git a/tests_e2e/orchestrator/runbook.yml b/tests_e2e/orchestrator/runbook.yml index 8075725eb..8180be732 100644 --- a/tests_e2e/orchestrator/runbook.yml +++ b/tests_e2e/orchestrator/runbook.yml @@ -51,7 +51,7 @@ variable: - name: test_suites value: "agent_bvt" - name: cloud - value: "public" + value: "AzureCloud" - name: image value: "" - name: location @@ -64,21 +64,26 @@ variable: # prefixed with "c_" to distinguish them from the rest of the variables, whose value can be set from # the command line. # - # c_marketplace_image, c_vm_size, c_location, and c_vhd are handled by LISA and define - # the set of test VMs that need to be created, while c_test_suites and c_env_name are parameters - # for the AgentTestSuite; the former defines the test suites that must be executed on each - # of those test VMs and the latter is the name of the environment, which is used for logging - # purposes (NOTE: the AgentTestSuite also uses c_vhd). + # Most of these variables are handled by LISA and are used to define the set of test VMs that need to be + # created. The variables marked with 'is_case_visible' are also referenced by the AgentTestSuite. # - name: c_env_name value: "" is_case_visible: true - name: c_marketplace_image value: "" + - name: c_marketplace_image_information_location + value: "" + - name: c_shared_resource_group_location + value: "" - name: c_vm_size value: "" + - name: c_cloud + value: "" + is_case_visible: true - name: c_location value: "" + is_case_visible: true - name: c_vhd value: "" is_case_visible: true @@ -107,6 +112,12 @@ platform: keep_environment: $(keep_environment) azure: deploy: True +# +# TODO: Enable these parameters once LISA supports all Azure clouds +# +# cloud: $(cloud) +# marketplace_image_information_location: $(c_marketplace_image_information_location) +# shared_resource_group_location: $(c_shared_resource_group_location) subscription_id: $(subscription_id) wait_delete: false requirement: diff --git a/tests_e2e/orchestrator/sample_runbooks/existing_vm.yml b/tests_e2e/orchestrator/sample_runbooks/existing_vm.yml index 2a5109f41..bbfcf8a7f 100644 --- a/tests_e2e/orchestrator/sample_runbooks/existing_vm.yml +++ b/tests_e2e/orchestrator/sample_runbooks/existing_vm.yml @@ -32,7 +32,7 @@ variable: # These variables identify the existing VM, and the user for SSH connections # - name: cloud - value: "public" + value: "AzureCloud" - name: subscription_id value: "" - name: resource_group_name @@ -80,18 +80,24 @@ variable: # prefixed with "c_" to distinguish them from the rest of the variables, whose value can be set from # the command line. # - # c_marketplace_image, c_vm_size, c_location, and c_vhd are handled by LISA and define - # the set of test VMs that need to be created, while c_test_suites is a parameter - # for the AgentTestSuite and defines the test suites that must be executed on each - # of those test VMs (the AgentTestSuite also uses c_vhd) + # Most of these variables are handled by LISA and are used to define the set of test VMs that need to be + # created. The variables marked with 'is_case_visible' are also referenced by the AgentTestSuite. # - name: c_env_name value: "" is_case_visible: true - name: c_vm_name value: "" + - name: c_marketplace_image_information_location + value: "" + - name: c_shared_resource_group_location + value: "" + - name: c_cloud + value: "" + is_case_visible: true - name: c_location value: "" + is_case_visible: true - name: c_test_suites value: [] is_case_visible: true @@ -114,6 +120,12 @@ platform: admin_username: $(user) admin_private_key_file: $(identity_file) azure: +# +# TODO: Enable these parameters once LISA supports all Azure clouds +# +# cloud: $(cloud) +# marketplace_image_information_location: $(c_marketplace_image_information_location) +# shared_resource_group_location: $(c_shared_resource_group_location) resource_group_name: $(resource_group_name) deploy: false subscription_id: $(subscription_id) diff --git a/tests_e2e/pipeline/pipeline-cleanup.yml b/tests_e2e/pipeline/pipeline-cleanup.yml index ba880a4f4..b82ad53ee 100644 --- a/tests_e2e/pipeline/pipeline-cleanup.yml +++ b/tests_e2e/pipeline/pipeline-cleanup.yml @@ -1,58 +1,59 @@ # # Pipeline for cleaning up any remaining Resource Groups generated by the Azure.WALinuxAgent pipeline. # -# Deletes any resource groups that are more than a day old and contain string "lisa-WALinuxAgent-" +# Deletes any resource groups that are older than 'older_than' and match the 'name_pattern' regular expression # -schedules: - - cron: "0 */12 * * *" # Run twice a day (every 12 hours) - displayName: cleanup build - branches: - include: - - develop - always: true -trigger: - - develop +parameters: + - name: name_pattern + displayName: Regular expression to match the name of the resource groups to delete + type: string + default: lisa-WALinuxAgent-.* -pr: none + - name: older_than + displayName: Delete resources older than (use the syntax of the "date -d" command) + type: string + default: 1 day ago + + - name: service_connections + type: object + default: + - azuremanagement +# +# TODO: Enable these services connections once we create test pipelines for all Azure clouds +# +# - azuremanagement.china +# - azuremanagement.government pool: vmImage: ubuntu-latest -variables: - - name: azureConnection - value: 'azuremanagement' - - name: rgPrefix - value: 'lisa-WALinuxAgent-' - steps: + - ${{ each service_connection in parameters.service_connections }}: + - task: AzureCLI@2 + inputs: + azureSubscription: ${{ service_connection }} + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + set -euxo pipefail - - task: AzureKeyVault@2 - displayName: "Fetch secrets from KV" - inputs: - azureSubscription: '$(azureConnection)' - KeyVaultName: 'dcrV2SPs' - SecretsFilter: '*' - RunAsPreJob: true + # + # We use the REST API to list the resource groups because we need the createdTime and that + # property is not available via the az-cli commands. + # + subscription_id=$(az account list --all --query "[?isDefault].id" -o tsv) + + date=$(date --utc +%Y-%m-%d'T'%H:%M:%S.%N'Z' -d "${{ parameters.older_than }}") - - task: AzureCLI@2 - inputs: - azureSubscription: '$(azureConnection)' - scriptType: 'bash' - scriptLocation: 'inlineScript' - inlineScript: | - set -euxo pipefail - date=`date --utc +%Y-%m-%d'T'%H:%M:%S.%N'Z' -d "1 day ago"` - - # Using the Azure REST GET resourceGroups API call as we can add the createdTime to the results. - # This feature is not available via the az-cli commands directly so we have to use the Azure REST APIs - - az rest --method GET \ - --url "https://management.azure.com/subscriptions/$(SUBSCRIPTION-ID)/resourcegroups" \ - --url-parameters api-version=2021-04-01 \$expand=createdTime \ - --output json \ - --query value \ - | jq --arg date "$date" '.[] | select (.createdTime < $date).name' \ - | grep "$(rgPrefix)" \ - | xargs -l -t -r az group delete --no-wait -y -n \ - || echo "No resource groups found to delete" + rest_endpoint=$(az cloud show --query "endpoints.resourceManager" -o tsv) + + az rest --method GET \ + --url "${rest_endpoint}/subscriptions/${subscription_id}/resourcegroups" \ + --url-parameters api-version=2021-04-01 \$expand=createdTime \ + --output json \ + --query value \ + | jq --arg date "$date" '.[] | select (.createdTime < $date).name' \ + | grep '${{ parameters.name_pattern }}' \ + | xargs -l -t -r az group delete --no-wait -y -n \ + || echo "No resource groups found to delete" diff --git a/tests_e2e/pipeline/pipeline.yml b/tests_e2e/pipeline/pipeline.yml index 1de541634..b0f9ebaa1 100644 --- a/tests_e2e/pipeline/pipeline.yml +++ b/tests_e2e/pipeline/pipeline.yml @@ -50,11 +50,6 @@ parameters: - failed - no -trigger: - - develop - -pr: none - pool: vmImage: ubuntu-latest @@ -69,9 +64,24 @@ jobs: addToPath: true architecture: 'x64' - # Extract the Azure cloud from the "connection_info" variable and store it in the "cloud" variable. - # The cloud name is used as a suffix of the value for "connection_info" and comes after the last '-'. - - bash: echo "##vso[task.setvariable variable=cloud]$(echo $CONNECTION_INFO | sed 's/^.*-//')" + # Extract the Azure cloud from the "connection_info" variable. Its value includes one of + # 'public', 'china', or 'government' as a suffix (the suffix comes after the last '-'). + - bash: | + case $(echo $CONNECTION_INFO | sed 's/^.*-//') in + public) + echo "##vso[task.setvariable variable=cloud]AzureCloud" + ;; + china) + echo "##vso[task.setvariable variable=cloud]AzureChinaCloud" + ;; + government) + echo "##vso[task.setvariable variable=cloud]AzureUSGovernment" + ;; + *) + echo "Invalid CONNECTION_INFO: $CONNECTION_INFO" >&2 + exit 1 + ;; + esac displayName: "Set Cloud type" - task: DownloadSecureFile@1 diff --git a/tests_e2e/test_suites/images.yml b/tests_e2e/test_suites/images.yml index 253f8a138..6fef5314d 100644 --- a/tests_e2e/test_suites/images.yml +++ b/tests_e2e/test_suites/images.yml @@ -47,7 +47,7 @@ image-sets: # mariner_2_arm64: # urn: "microsoftcblmariner cbl-mariner cbl-mariner-2-arm64 latest" # locations: -# - "eastus" +# - AzureCloud: ["eastus"] # vm_sizes: # - "Standard_D2pls_v5" # @@ -55,38 +55,76 @@ image-sets: # two properties can be used to specify that the image is available only in # some locations, or that it can be used only on some VM sizes. # +# The 'locations' property consists of 3 items, one for each cloud (AzureCloud, +# AzureUSGovernment and AzureChinaCloud). For each of these items: +# +# - If the item is not present, the image is available in all locations for that cloud. +# - If the value is a list of locations, the image is available only in those locations +# - If the value is an empty list, the image is not available in that cloud. +# # URNs follow the format ' ' or # ':::' # images: - alma_9: "almalinux almalinux 9-gen2 latest" + alma_9: + urn: "almalinux almalinux 9-gen2 latest" + locations: + AzureChinaCloud: [] centos_610: "OpenLogic CentOS 6.10 latest" centos_79: "OpenLogic CentOS 7_9 latest" debian_8: "credativ Debian 8 latest" debian_9: "credativ Debian 9 latest" debian_10: "Debian debian-10 10 latest" debian_11: "Debian debian-11 11 latest" - debian_11_arm64: "Debian debian-11 11-backports-arm64 latest" - flatcar: "kinvolk flatcar-container-linux-free stable latest" + debian_11_arm64: + urn: "Debian debian-11 11-backports-arm64 latest" + locations: + AzureUSGovernment: [] + flatcar: + urn: "kinvolk flatcar-container-linux-free stable latest" + locations: + AzureChinaCloud: [] + AzureUSGovernment: [] flatcar_arm64: urn: "kinvolk flatcar-container-linux-corevm stable latest" + locations: + AzureChinaCloud: [] vm_sizes: - "Standard_D2pls_v5" - mariner_1: "microsoftcblmariner cbl-mariner cbl-mariner-1 latest" + mariner_1: + urn: "microsoftcblmariner cbl-mariner cbl-mariner-1 latest" + locations: + AzureChinaCloud: [] mariner_2: "microsoftcblmariner cbl-mariner cbl-mariner-2 latest" mariner_2_arm64: urn: "microsoftcblmariner cbl-mariner cbl-mariner-2-arm64 latest" locations: - - "eastus" + AzureCloud: ["eastus"] vm_sizes: - "Standard_D2pls_v5" - rocky_9: "erockyenterprisesoftwarefoundationinc1653071250513 rockylinux-9 rockylinux-9 latest" + rocky_9: + urn: "erockyenterprisesoftwarefoundationinc1653071250513 rockylinux-9 rockylinux-9 latest" + locations: + AzureChinaCloud: [] + AzureUSGovernment: [] suse_12: "SUSE sles-12-sp5-basic gen1 latest" suse_15: "SUSE sles-15-sp2-basic gen2 latest" - rhel_79: "RedHat RHEL 7_9 latest" - rhel_82: "RedHat RHEL 8.2 latest" - rhel_90: "RedHat RHEL 9_0 latest" - rhel_90_arm64: "RedHat rhel-arm64 9_0-arm64 latest" + rhel_79: + urn: "RedHat RHEL 7_9 latest" + locations: + AzureChinaCloud: [] + rhel_82: + urn: "RedHat RHEL 8.2 latest" + locations: + AzureChinaCloud: [] + rhel_90: + urn: "RedHat RHEL 9_0 latest" + locations: + AzureChinaCloud: [] + rhel_90_arm64: + urn: "RedHat rhel-arm64 9_0-arm64 latest" + locations: + AzureChinaCloud: [] ubuntu_1604: "Canonical UbuntuServer 16.04-LTS latest" ubuntu_1804: "Canonical UbuntuServer 18.04-LTS latest" ubuntu_2004: "Canonical 0001-com-ubuntu-server-focal 20_04-lts latest" diff --git a/tests_e2e/tests/lib/agent_test_context.py b/tests_e2e/tests/lib/agent_test_context.py index ca9fc64ad..28d663f8a 100644 --- a/tests_e2e/tests/lib/agent_test_context.py +++ b/tests_e2e/tests/lib/agent_test_context.py @@ -127,6 +127,7 @@ def from_args(): Creates an AgentTestContext from the command line arguments. """ parser = argparse.ArgumentParser() + parser.add_argument('-c', '--cloud', dest="cloud", required=False, choices=['AzureCloud', 'AzureChinaCloud', 'AzureUSGovernment'], default="AzureCloud") parser.add_argument('-g', '--group', required=True) parser.add_argument('-l', '--location', required=True) parser.add_argument('-s', '--subscription', required=True) @@ -138,7 +139,7 @@ def from_args(): parser.add_argument('-a', '--ip-address', dest="ip_address", required=False) # Use the vm name as default parser.add_argument('-u', '--username', required=False, default=os.getenv("USER")) - parser.add_argument('-k', '--private-key-file', dest="private_key_file", required=False, default=Path.home()/".ssh"/"id_rsa") + parser.add_argument('-k', '--private-key-file', dest="private_key_file", required=False, default=str(Path.home()/".ssh"/"id_rsa")) parser.add_argument('-p', '--ssh-port', dest="ssh_port", required=False, default=AgentTestContext.Connection.DEFAULT_SSH_PORT) args = parser.parse_args() @@ -149,12 +150,13 @@ def from_args(): return AgentTestContext( vm=VmIdentifier( + cloud=args.cloud, location=args.location, subscription=args.subscription, resource_group=args.group, name=args.vm), paths=AgentTestContext.Paths( - working_directory=working_directory, + working_directory=Path(working_directory), remote_working_directory=Path(args.remote_working_directory), test_source_directory=Path(args.test_source_directory)), connection=AgentTestContext.Connection( diff --git a/tests_e2e/tests/lib/azure_clouds.py b/tests_e2e/tests/lib/azure_clouds.py new file mode 100644 index 000000000..2e1f5674e --- /dev/null +++ b/tests_e2e/tests/lib/azure_clouds.py @@ -0,0 +1,24 @@ +# Microsoft Azure Linux Agent +# +# Copyright 2018 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from typing import Dict +from msrestazure.azure_cloud import Cloud, AZURE_PUBLIC_CLOUD, AZURE_CHINA_CLOUD, AZURE_US_GOV_CLOUD + +AZURE_CLOUDS: Dict[str, Cloud] = { + "AzureCloud": AZURE_PUBLIC_CLOUD, + "AzureChinaCloud": AZURE_CHINA_CLOUD, + "AzureUSGovernment": AZURE_US_GOV_CLOUD +} diff --git a/tests_e2e/tests/lib/identifiers.py b/tests_e2e/tests/lib/identifiers.py index 48794140b..398ffd61c 100644 --- a/tests_e2e/tests/lib/identifiers.py +++ b/tests_e2e/tests/lib/identifiers.py @@ -17,10 +17,11 @@ class VmIdentifier(object): - def __init__(self, location, subscription, resource_group, name): + def __init__(self, cloud: str, location: str, subscription: str, resource_group: str, name: str): """ Represents the information that identifies a VM to the ARM APIs """ + self.cloud: str = cloud self.location = location self.subscription: str = subscription self.resource_group: str = resource_group @@ -31,7 +32,7 @@ def __str__(self): class VmExtensionIdentifier(object): - def __init__(self, publisher, ext_type, version): + def __init__(self, publisher: str, ext_type: str, version: str): """ Represents the information that identifies an extension to the ARM APIs diff --git a/tests_e2e/tests/lib/virtual_machine.py b/tests_e2e/tests/lib/virtual_machine.py index 032a7e0f5..79b86a6f3 100644 --- a/tests_e2e/tests/lib/virtual_machine.py +++ b/tests_e2e/tests/lib/virtual_machine.py @@ -28,7 +28,9 @@ from azure.mgmt.compute import ComputeManagementClient from azure.mgmt.compute.models import VirtualMachineExtension, VirtualMachineScaleSetExtension, VirtualMachineInstanceView, VirtualMachineScaleSetInstanceView from azure.mgmt.resource import ResourceManagementClient +from msrestazure.azure_cloud import Cloud +from tests_e2e.tests.lib.azure_clouds import AZURE_CLOUDS from tests_e2e.tests.lib.identifiers import VmIdentifier from tests_e2e.tests.lib.logging import log from tests_e2e.tests.lib.retry import execute_with_retry @@ -43,8 +45,18 @@ class VirtualMachineBaseClass(ABC): def __init__(self, vm: VmIdentifier): super().__init__() self._identifier: VmIdentifier = vm - self._compute_client = ComputeManagementClient(credential=DefaultAzureCredential(), subscription_id=vm.subscription) - self._resource_client = ResourceManagementClient(credential=DefaultAzureCredential(), subscription_id=vm.subscription) + cloud: Cloud = AZURE_CLOUDS[vm.cloud] + credential: DefaultAzureCredential = DefaultAzureCredential(authority=cloud.endpoints.active_directory) + self._compute_client = ComputeManagementClient( + credential=credential, + subscription_id=vm.subscription, + base_url=cloud.endpoints.resource_manager, + credential_scopes=[cloud.endpoints.resource_manager + "/.default"]) + self._resource_client = ResourceManagementClient( + credential=credential, + subscription_id=vm.subscription, + base_url=cloud.endpoints.resource_manager, + credential_scopes=[cloud.endpoints.resource_manager + "/.default"]) @abstractmethod def get_instance_view(self) -> Any: # Returns VirtualMachineInstanceView or VirtualMachineScaleSetInstanceView diff --git a/tests_e2e/tests/lib/vm_extension.py b/tests_e2e/tests/lib/vm_extension.py index eab676e75..abefcc723 100644 --- a/tests_e2e/tests/lib/vm_extension.py +++ b/tests_e2e/tests/lib/vm_extension.py @@ -30,7 +30,9 @@ from azure.mgmt.compute import ComputeManagementClient from azure.mgmt.compute.models import VirtualMachineExtension, VirtualMachineScaleSetExtension, VirtualMachineExtensionInstanceView from azure.identity import DefaultAzureCredential +from msrestazure.azure_cloud import Cloud +from tests_e2e.tests.lib.azure_clouds import AZURE_CLOUDS from tests_e2e.tests.lib.identifiers import VmIdentifier, VmExtensionIdentifier from tests_e2e.tests.lib.logging import log from tests_e2e.tests.lib.retry import execute_with_retry @@ -51,7 +53,13 @@ def __init__(self, vm: VmIdentifier, extension: VmExtensionIdentifier, resource_ self._vm: VmIdentifier = vm self._identifier = extension self._resource_name = resource_name - self._compute_client: ComputeManagementClient = ComputeManagementClient(credential=DefaultAzureCredential(), subscription_id=vm.subscription) + cloud: Cloud = AZURE_CLOUDS[vm.cloud] + credential: DefaultAzureCredential = DefaultAzureCredential(authority=cloud.endpoints.active_directory) + self._compute_client: ComputeManagementClient = ComputeManagementClient( + credential=credential, + subscription_id=vm.subscription, + base_url=cloud.endpoints.resource_manager, + credential_scopes=[cloud.endpoints.resource_manager + "/.default"]) def enable( self,