tools/scripts/acr-copy-release.ps1 (228 lines of code) (raw):
<#
.SYNOPSIS
Creates release images with a particular release version in
production ACR from the tested development version.
.DESCRIPTION
The script requires az to be installed and already logged on to a
subscription. This means it should be run in a azcliv2 task in the
azure pipeline or "az login" must have been performed already.
Releases images with a given version number.
.PARAMETER BuildRegistry
The name of the source registry where development image is present.
.PARAMETER BuildSubscription
The subscription where the build registry is located
.PARAMETER BuildNamespace
The namespace in the build registry (optional)
.PARAMETER ReleaseRegistry
The name of the destination registry where release images will
be created.
.PARAMETER ReleaseSubscription
The subscription of the release registry is different than build
registry subscription
.PARAMETER ResourceGroupName
The name of the resource group to create if release registry does not
exist (Optional).
.PARAMETER ResourceGroupLocation
The location of the resource group to create (Optional).
.PARAMETER ReleaseVersion
The build version for the development image that is being released.
.PARAMETER RepositoryPattern
A pattern to filter the repositories through.
.PARAMETER IsLatest
Release as latest image
.PARAMETER IsMajorUpdate
Release as major update
.PARAMETER PreviewVersion
Release only as -ReleaseVersion with -preview{PreviewVersion} appended
to the tag. This will not update rolling tags like latest and major
version and allow customers to test the releae image ahead of release.
.PARAMETER RemoveNamespaceOnRelease
Remove namespace (e.g. public) on release.
#>
Param(
[string] $BuildRegistry = "industrialiot",
[string] $BuildSubscription = "IOT_GERMANY",
[string] $BuildNamespace = $null,
[string] $ReleaseRegistry = "industrialiotprod",
[string] $ReleaseSubscription = "IOT_GERMANY",
[string] $ResourceGroupName = $null,
[string] $ResourceGroupLocation = $null,
[Parameter(Mandatory = $true)] [string] $ReleaseVersion,
[string] $RepositoryPattern = $null,
[switch] $IsLatest,
[switch] $IsMajorUpdate,
[string] $PreviewVersion,
[switch] $RemoveNamespaceOnRelease
)
if (![string]::IsNullOrEmpty($script:ResourceGroupName)) {
# check if release registry exists and if not create it
$argumentList = @("acr", "show", "--name", $script:ReleaseRegistry,
"--subscription", $script:ReleaseSubscription)
$registry = & "az" @argumentList | ConvertFrom-Json
if (!$registry) {
# create registry - check if group exists and if not create it.
$argumentList = @("group", "show", "-g", $script:ResourceGroupName,
"--subscription", $script:ReleaseSubscription)
$group = & "az" @argumentList 2>$null | ConvertFrom-Json
if (!$group) {
if ([string]::IsNullOrEmpty($script:ResourceGroupLocation)) {
throw "Need a resource group location to create the resource group."
}
$argumentList = @("group", "create", "-g", $script:ResourceGroupName, `
"-l", $script:ResourceGroupLocation,
"--subscription", $script:ReleaseSubscription)
$group = & "az" @argumentList | ConvertFrom-Json
if ($LastExitCode -ne 0) {
throw "az $($argumentList) failed with $($LastExitCode)."
}
Write-Host "Created new Resource group $ResourceGroupName."
}
if ([string]::IsNullOrEmpty($script:ResourceGroupLocation)) {
$script:ResourceGroupLocation = $group.location
}
$argumentList = @("acr", "create", "-g", $script:ResourceGroupName, "-n", `
$script:ReleaseRegistry, "-l", $script:ResourceGroupLocation, `
"--sku", "Basic", "--admin-enabled", "true",
"--subscription", $script:ReleaseSubscription)
$registry = & "az" @argumentList | ConvertFrom-Json
if ($LastExitCode -ne 0) {
throw "az $($argumentList) failed with $($LastExitCode)."
}
Write-Host "Created container registry $($registry.name) in $script:ResourceGroupName."
}
}
# Set build subscription if provided
if (![string]::IsNullOrEmpty($script:BuildSubscription)) {
Write-Debug "Setting subscription to $($script:BuildSubscription)"
$argumentList = @("account", "set",
"--subscription", $script:BuildSubscription, "-ojson")
& "az" @argumentList 2>&1 | ForEach-Object { Write-Host "$_" }
if ($LastExitCode -ne 0) {
throw "az $($argumentList) failed with $($LastExitCode)."
}
}
# Get build repositories
$argumentList = @("acr", "repository", "list",
"--name", $script:BuildRegistry, "-ojson",
"--subscription", $script:BuildSubscription)
$result = (& "az" @argumentList 2>&1 | ForEach-Object { "$_" })
if ($LastExitCode -ne 0) {
throw "az $($argumentList) failed with $($LastExitCode)."
}
$BuildRepositories = $result | ConvertFrom-Json
# In each repo - check whether a release tag exists
$jobs = @()
foreach ($Repository in $BuildRepositories) {
$BuildTag = "$($Repository):$($script:ReleaseVersion)"
if (![string]::IsNullOrEmpty($script:BuildNamespace) -and `
!$Repository.StartsWith($script:BuildNamespace)) {
continue
}
if (![string]::IsNullOrEmpty($script:RepositoryPattern) -and `
($Repository -notlike $script:RepositoryPattern)) {
continue
}
if ($Repository -like "*/opc-plc") {
# do not touch/break the opc plc releases
continue
}
# see if build tag exists
$argumentList = @("acr", "repository", "show",
"--name", $script:BuildRegistry,
"--subscription", $script:BuildSubscription,
"-t", $BuildTag,
"-ojson"
)
$result = (& "az" @argumentList 2>&1 | ForEach-Object { "$_" })
if ($LastExitCode -ne 0) {
Write-Host "Image $BuildTag not found..."
continue
}
$Image = $result | ConvertFrom-Json
if ([string]::IsNullOrEmpty($Image.digest)) {
Write-Host "Image $BuildTag not found..."
continue
}
$ReleaseTags = @()
if ($script:PreviewVersion) {
if ($script:IsMajorUpdate.IsPresent -or $script:IsLatest.IsPresent) {
throw "IsMajorUpdate and IsLatest is not allowed when PreviewVersion is specified."
}
$versionTag = "$($script:ReleaseVersion)-preview$($script:PreviewVersion)"
$ReleaseTags += $versionTag
}
else {
if ($script:IsLatest.IsPresent) {
$ReleaseTags += "latest"
}
# Example: if release version is 2.8.1, then base image tags are "2", "2.8", "2.8.1"
$versionParts = $script:ReleaseVersion.Split('-')[0].Split('.')
if ($versionParts.Count -gt 0) {
$versionTag = $versionParts[0]
if ($script:IsMajorUpdate.IsPresent -or $script:IsLatest.IsPresent) {
$ReleaseTags += $versionTag
}
for ($i = 1; $i -lt ($versionParts.Count); $i++) {
$versionTag = ("$($versionTag).{0}" -f $versionParts[$i])
$ReleaseTags += $versionTag
}
}
}
# Create acr command line
# --force is needed to replace existing tags like "latest" with new images
$argumentList = @("acr", "import", "-ojson", "--force",
"--name", $script:ReleaseRegistry,
"--source", $BuildTag,
"--registry", $script:BuildRegistry
)
# set release subscription
if (![string]::IsNullOrEmpty($script:ReleaseSubscription)) {
$argumentList += "--subscription"
$argumentList += $script:ReleaseSubscription
}
else {
$argumentList += "--subscription"
$argumentList += $script:BuildSubscription
}
# add the output / release image tags
if ($script:RemoveNamespaceOnRelease.IsPresent `
-and (!$Repository.StartsWith("iot/")) `
-and (!$Repository.StartsWith("iotedge/"))) {
$TargetRepository = $Repository.Substring($Repository.IndexOf('/') + 1)
}
else {
$TargetRepository = $Repository
}
foreach ($ReleaseTag in $ReleaseTags) {
$argumentList += "--image"
$argumentList += "$($TargetRepository):$($ReleaseTag)"
}
$FullImageName = "$($script:BuildRegistry).azurecr.io/$($BuildTag)"
$ConsoleOutput = "Copying $FullImageName $($Image.digest) with tags '$($ReleaseTags -join ", ")' to release $script:ReleaseRegistry"
Write-Host "Starting Job $ConsoleOutput..."
$jobs += Start-Job -Name $FullImageName -ArgumentList @($argumentList, $ConsoleOutput) -ScriptBlock {
$argumentList = $args[0]
$ConsoleOutput = $args[1]
Write-Host "$($ConsoleOutput)..."
& az @argumentList 2>&1 | ForEach-Object { "$_" }
if ($LastExitCode -ne 0) {
Write-Warning "$($ConsoleOutput) failed with $($LastExitCode) - 2nd attempt..."
& "az" @argumentList 2>&1 | ForEach-Object { "$_" }
if ($LastExitCode -ne 0) {
throw "Error: $($ConsoleOutput) - 2nd attempt failed with $($LastExitCode)."
}
}
Write-Host "$($ConsoleOutput) completed."
}
}
# Wait for copy jobs to finish for this repo.
if ($jobs.Count -ne 0) {
Write-Host "Waiting for copy jobs to finish for $($script:ReleaseRegistry)."
# Wait until all jobs are completed
Receive-Job -Job $jobs -WriteEvents -Wait | Out-Host
$jobs | Out-Host
$jobs | Where-Object { $_.State -ne "Completed" } | ForEach-Object {
throw "ERROR: Copying $($_.Name). resulted in $($_.State)."
}
}
Write-Host "All copy jobs completed successfully for $($script:ReleaseRegistry)."