scripts/Obtain-Workspace.ps1 (167 lines of code) (raw):
# ---------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# ---------------------------------------------------------
# Obtains a workspace for running tests
# If a workspace younger than window_seconds exists, it will be reused
# If not, a fresh workspace will be created
# Most of the tracking is actually done through the parent resource groups
# If cullWorkspaces is set, then workspaces older than 2*window_seconds are deleted
# This also produces two JSON files used by subsequent scripts:
# config.json -> A regular workspace config JSON file
# component_config.json -> Specifies the version of everything to upload (as epoch_seconds)
$baseName = "amlisdkv2"
$location = $env:WORKSPACE_LOCATION
$createdTag = "createdAt"
$ownerTeamTagKey = "owningTeam"
$ownerTeamTagValue = "AML_Intelligence"
$purposeTagKey = "workspacePurpose"
$purposeTagValue = "Automated_Tests_for_DPv2"
$workspaceYAML = "workspace.yaml"
$window_seconds = $env:WORKSPACE_WINDOW_SECONDS
$cullWorkspaces = $env:OLD_WORKSPACES_HANDLING -eq "Cull"
function Get-RecentResourceGroups(
[int]$min_epoch,
[string]$target_location
) {
# Returns resource groups created after the time specified by min_epoch
# and in target_location
# Uses the createdTag for this purpose
Write-Host "Searching for recent resource groups"
Write-Host "Minimum Epoch: $min_epoch"
# Would be nice to do this server-side
$all_groups = az group list --output json | ConvertFrom-Json
$filtered_groups = $all_groups.Where(
{
$_.name.contains($baseName) -and
$_.tags.$createdTag -gt $min_epoch -and
$_.location -eq $target_location
}
)
$sorted_groups = $filtered_groups | Sort-Object -Descending -Property {$_.name}
return $sorted_groups
}
function Get-OldResourceGroups(
[int]$max_epoch
) {
# Returns resource groups created before the time specified by max_epoch
# Uses the createdTag for this purpose
Write-Host "Searching for older resource groups"
Write-Host "Maximum Epoch: $max_epoch"
# Would be nice to do this server-side
$all_groups = az group list | ConvertFrom-Json
$filtered_groups = $all_groups.Where({ $_.name.contains($baseName) -and $_.tags.$createdTag -lt $max_epoch })
return $filtered_groups
}
function Get-WorkspaceFromResourceGroup(
[string]$resource_group_name
) {
Write-Host "Checking resource group $resource_group_name"
$workspaces = az ml workspace list --resource-group $resource_group_name --output json | ConvertFrom-Json
$filtered_workspaces = $workspaces.Where({ $_.name.contains($baseName) })
if ($filtered_workspaces.count -gt 0) {
$workspace = $workspaces[0]
}
else {
throw "Resource Group did not contain workspace with name starting with $baseName"
}
return $workspace
}
function Get-EpochSecs {
# Get time to nearest second
$epoch_time = Get-Date (Get-Date).ToUniversalTime() -UFormat %s
$epoch_secs = [Math]::Truncate($epoch_time)
return $epoch_secs
}
function Create-EpochWorkspace(
[int]$epoch_secs,
[string]$target_location
) {
$rg_name = "$basename-rg-$epoch_secs"
$ws_name = "$basename$epoch_secs"
Write-Host "Creating workspace $ws_name in resource group $rg_name in region $target_location"
$ws_data = @{}
$ws_data['name'] = $ws_name
$ws_data['tags'] = @{}
$ws_data['tags'][$createdTag] = "$epoch_secs"
$ws_data['tags'][$ownerTeamTagKey] = $ownerTeamTagValue
$ws_data['tags'][$purposeTagKey] = $purposeTagValue
ConvertTo-Yaml $ws_data | Out-File -FilePath $workspaceYAML -Encoding ascii
$rg = az group create --location $target_location --name $rg_name --tags "$createdTag=$epoch_secs" --debug
Write-Host "Resource group created"
Write-Host $rg
$ws = az ml workspace create --resource-group $rg_name --file $workspaceYAML | ConvertFrom-Json
return $ws
}
function Create-ConfigJson(
$workspace
) {
if (Get-Member -inputobject $workspace -name "storageAccount" -Membertype Properties) {
Write-Host "Getting storage account via storageAccount."
$parts = $workspace.storageAccount.split('/')
}
else {
Write-Host "Getting storage account via storage_account."
$parts = $workspace.storage_account.split('/')
}
$sub_id = $parts[2]
$rg_name = $parts[4]
Write-Host "Extracted subscription: $sub_id"
Write-Host "Extract resource group: $rg_name"
$json_config = @{}
$json_config["subscription_id"] = $sub_id
$json_config["resource_group"] = $rg_name
$json_config["workspace_name"] = $workspace.name
ConvertTo-Json $json_config | Out-File -FilePath 'config.json' -Encoding ascii
}
function Create-ComponentConfigJson(
[int]$epoch_secs
) {
$json_config = @{}
$json_config['version'] = $epoch_secs
ConvertTo-Json $json_config | Out-File -FilePath 'component_config.json' -Encoding ascii
}
# Install-Module powershell-yaml -Scope CurrentUser
$epoch_secs = Get-EpochSecs
if ( $cullWorkspaces ) {
Write-Host "Checking for old resource groups"
Write-Host
$old_rg_list = Get-OldResourceGroups($epoch_secs - 2 * $window_seconds)
if ( $old_rg_list.count -gt 0) {
Write-Host "Found $($old_rg_list.count) resource groups to clean up"
foreach ( $rg in $old_rg_list) {
Write-Host "Cleaning up $($rg.name)"
az group delete --name $rg.name --yes
}
}
else {
Write-Host "No old resource groups found"
}
}
else {
Write-Host "Skipping old resource group check"
}
Write-Host
Write-Host "Creating workspace if one not found"
Write-Host
$rg_list = Get-RecentResourceGroups -min_epoch ($epoch_secs - $window_seconds) -target_location $location
if ($rg_list.count -eq 0) {
Write-Host "No recent workspace, creating new one"
$_ = Create-EpochWorkspace -epoch_secs $epoch_secs -target_location $location
}
$rg_list = Get-RecentResourceGroups -min_epoch ($epoch_secs - $window_seconds) -target_location $location
Write-Host "Found $($rg_list.count) suitable resource groups"
$target_rg = $rg_list[0].name
$workspace = Get-WorkspaceFromResourceGroup($target_rg)
Write-Host
Write-Host "Workspace information"
Write-Host
Write-Host ($workspace | ConvertTo-Json)
Write-Host
Write-Host
Write-Host "Creating config.json"
Write-Host
Create-ConfigJson($workspace)
Write-Host
Write-Host "Creating component_config.json"
Write-Host
Create-ComponentConfigJson($epoch_secs)