-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Azure Clouds #2795
Changes from 4 commits
b3df458
19270bb
1b1018c
8f30af4
e473ebe
6c6091d
9a77996
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,4 @@ azure-core | |
azure-identity | ||
azure-mgmt-compute>=22.1.0 | ||
azure-mgmt-resource>=15.0.0 | ||
msrestazure |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now the AgentTestSuiteCombinator also generates the cloud, as "c_cloud". For consistency, I am also picking up the location from the values generated by the combinator (instead of from the runbook). |
||
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), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently these 2 properties are require by LISA for China and Government. They are working on making LISA generate those values (as it does for Public). In the meanwhile, any location will do. |
||
"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], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how these are different from c_location. will Lisa use these two to create vm in this location rather than test_suite/image location? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. they are unrelated to the location for the test VM. LISA keeps some caches and creates them on those regions. |
||
"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("") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now I am logging the entire list, instead of explicitly specifying each item in the list. |
||
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<publisher>[^\s:]+)[\s:](?P<offer>[^\s:]+)[\s:](?P<sku>[^\s:]+)[\s:](?P<version>[^\s:]+)") | ||
|
||
|
||
@staticmethod | ||
def _is_urn(urn: str) -> bool: | ||
# URNs can be given as '<Publisher> <Offer> <Sku> <Version>' or '<Publisher>:<Offer>:<Sku>:<Version>' | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now the schedule is defined in the UI (we did the same for the main pipeline), so this schedule would be ignored. I'm deleting it altogether. |
||
- cron: "0 */12 * * *" # Run twice a day (every 12 hours) | ||
displayName: cleanup build | ||
branches: | ||
include: | ||
- develop | ||
always: true | ||
|
||
trigger: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The triggers are also defined in the UI, so removing 'trigger' and 'pr' as well |
||
- 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was copied from the initial prototype. The only secret we need is the subscription ID, which can be obtained using the CLI in the next task, so removing this. |
||
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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now image.locations can be specified on a per-cloud basis (as a dictionary with the cloud name as key); see comments in images.yml (or the wiki) for the syntax