From 485ad7228c324f6eada6825b9ee695fcf668e4dd Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 10:52:55 +1000 Subject: [PATCH 01/24] feat: Enthusiastic Promotor skeleton --- .../enthusiastic-promotor.ps1 | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 EnthusiasticPromotions/enthusiastic-promotor.ps1 diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 new file mode 100644 index 0000000..d1689d2 --- /dev/null +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -0,0 +1,45 @@ + +$promotionProjectId = "Projects-386" +$dockerhubEnvironmentId = "Environments-62" + +$hostedSpace = "Spaces-142" +$dwProjectId = "Projects-5063" +$productionEnvironmentId = "Environments-842" +$productionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") + +$branchUrl = "https://deploy-fnm.testoctopus.app" +$branchApiKey = "API-Key" + +$deployUrl = "https://deploy.octopus.app" +$deployApiKey = "API-Key" + +function Get-PromotionCandidates { + $promotedDeploymentsResponse = (Invoke-WebRequest -Uri "$branchUrl/api/deployments?projects=$promotionProjectId&environments=$dockerhubEnvironmentId" -Headers @{ "X-Octopus-ApiKey"=$branchApiKey }).Content | ConvertFrom-Json + $promotedReleases = $promotedDeploymentsResponse.Items | Select-Object -ExpandProperty "ReleaseId" -Unique + + $allReleasesResponse = (Invoke-WebRequest -Uri "$branchUrl/api/deployments?projects=$promotionProjectId" -Headers @{ "X-Octopus-ApiKey"=$branchApiKey }).Content | ConvertFrom-Json + $allReleases = $allReleasesResponse.Items | Select-Object -ExpandProperty "ReleaseId" -Unique + + $candidates = $allReleases | Where-Object { -not ($_ -in $promotedReleases) } + + Write-Host $candidates +} + +function Get-ProductionDWVersions { + $releases = @(); + + foreach ($tenant in $productionTenants) { + $releasesInProductionResponse = (Invoke-WebRequest -Uri "$deployUrl/api/$hostedSpace/deployments?projects=$dwProjectId&environments=$productionEnvironmentId&tenants=$tenant" -Headers @{ "X-Octopus-ApiKey"=$deployApiKey }).Content | ConvertFrom-Json + $release = $releasesInProductionResponse.Items | Sort-Object -Property "Created" -Descending | Select-Object -First 1 + + $releases += $release + } + + Write-Host ($releases | Select-Object -ExpandProperty "ReleaseId") +} + +function Get-CachedImageVersions() { + +} + +Get-ProductionDWVersions From 8c981c79f691a2e4db4b2768df5d3d364cb065c2 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 10:53:04 +1000 Subject: [PATCH 02/24] task: add tests file --- EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 new file mode 100644 index 0000000..e69de29 From 030ee6f8a7ae41e179e9d3f802dd839826315d63 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 11:36:51 +1000 Subject: [PATCH 03/24] task: Make Get-PromotionCandidates testable --- .../enthusiastic-promotor.ps1 | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index d1689d2..e934195 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -13,14 +13,25 @@ $branchApiKey = "API-Key" $deployUrl = "https://deploy.octopus.app" $deployApiKey = "API-Key" -function Get-PromotionCandidates { - $promotedDeploymentsResponse = (Invoke-WebRequest -Uri "$branchUrl/api/deployments?projects=$promotionProjectId&environments=$dockerhubEnvironmentId" -Headers @{ "X-Octopus-ApiKey"=$branchApiKey }).Content | ConvertFrom-Json - $promotedReleases = $promotedDeploymentsResponse.Items | Select-Object -ExpandProperty "ReleaseId" -Unique +function Get-FromApi($url, $apiKey) { + Write-Verbose "Getting response from $url" + $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $enthusiasticPromoterApiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 + + # log out the json, so we can diagnose what's happening / write a test for it + write-verbose "--------------------------------------------------------" + write-verbose "response:" + write-verbose "--------------------------------------------------------" + write-verbose ($result | ConvertTo-Json -depth 10) + write-verbose "--------------------------------------------------------" + return $result +} - $allReleasesResponse = (Invoke-WebRequest -Uri "$branchUrl/api/deployments?projects=$promotionProjectId" -Headers @{ "X-Octopus-ApiKey"=$branchApiKey }).Content | ConvertFrom-Json - $allReleases = $allReleasesResponse.Items | Select-Object -ExpandProperty "ReleaseId" -Unique +function Get-PromotionCandidates($dynamicWorkerReleases, $dynamicWorkerDeployments) { + + $uniqueReleases = $dynamicWorkerReleases.Items | Select-Object -ExpandProperty "ReleaseId" -Unique + $promotedReleases = $dynamicWorkerDeployments.Items | Select-Object -ExpandProperty "ReleaseId" -Unique - $candidates = $allReleases | Where-Object { -not ($_ -in $promotedReleases) } + $candidates = $uniqueReleases | Where-Object { -not ($_ -in $promotedReleases) } Write-Host $candidates } @@ -38,8 +49,9 @@ function Get-ProductionDWVersions { Write-Host ($releases | Select-Object -ExpandProperty "ReleaseId") } -function Get-CachedImageVersions() { - -} +function Execute { + $dynamicWorkerReleases = Get-FromApi "$branchUrl/api/deployments?projects=$promotionProjectId" $branchApiKey + $dynamicWorkerDeployments = Get-FromApi "$branchUrl/api/deployments?projects=$promotionProjectId&environments=$dockerhubEnvironmentId" $branchApiKey -Get-ProductionDWVersions + Get-ProductionDWVersions +} \ No newline at end of file From f41a4dfe1b751792a057b02530eb0568e3b26979 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 11:37:05 +1000 Subject: [PATCH 04/24] test: first test --- EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index e69de29..65c638e 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -0,0 +1,10 @@ +BeforeAll { + . (Join-Path -Path $PSScriptRoot -ChildPath "enthusiastic-promotor.ps1") +} + +Describe "Get-PromotionCandidates" { + It "with no parameters returns nothing" { + $result = Get-PromotionCandidates + $result.Count | Should -Be 0 + } +} From 5893dbf250d1f111bcd906e9421736739caadfcb Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 11:39:42 +1000 Subject: [PATCH 05/24] task: Add cmdletbinding for verbose logging --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index e934195..b045158 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -1,3 +1,5 @@ +[CmdletBinding()] +param () $promotionProjectId = "Projects-386" $dockerhubEnvironmentId = "Environments-62" From 84f66a1bfae05b6a20ee7cf29b05d30adb8317cd Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 14:46:18 +1000 Subject: [PATCH 06/24] task: pull inputs up to params --- .../enthusiastic-promotor.ps1 | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index b045158..ac68be2 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -1,23 +1,24 @@ [CmdletBinding()] -param () +param ( + [Parameter(Mandatory = $true)][string] $dynamicWorkerInstanceApiKey, + [Parameter()][string] $dynamicWorkerInstanceUrl = "https://deploy.octopus.app", + [Parameter()][string] $dynamicWorkerProjectId = "Projects-5063", + [Parameter()][string] $dynamicWorkerSpaceId = "Spaces-142", + + [Parameter(Mandatory = $true)][string] $targetInstanceApiKey, + [Parameter()][string] $targetInstanceUrl = "https://deploy-fnm.testoctopus.app", + [Parameter()][string] $targetProjectId = "Projects-381", + [Parameter()][string] $targetSpaceId = "Spaces-1", + [Parameter()][string] $runbookProjectId = "Projects-386" +) -$promotionProjectId = "Projects-386" $dockerhubEnvironmentId = "Environments-62" - -$hostedSpace = "Spaces-142" -$dwProjectId = "Projects-5063" $productionEnvironmentId = "Environments-842" $productionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") -$branchUrl = "https://deploy-fnm.testoctopus.app" -$branchApiKey = "API-Key" - -$deployUrl = "https://deploy.octopus.app" -$deployApiKey = "API-Key" - function Get-FromApi($url, $apiKey) { Write-Verbose "Getting response from $url" - $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $enthusiasticPromoterApiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 + # $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $enthusiasticPromoterApiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 # log out the json, so we can diagnose what's happening / write a test for it write-verbose "--------------------------------------------------------" @@ -42,7 +43,7 @@ function Get-ProductionDWVersions { $releases = @(); foreach ($tenant in $productionTenants) { - $releasesInProductionResponse = (Invoke-WebRequest -Uri "$deployUrl/api/$hostedSpace/deployments?projects=$dwProjectId&environments=$productionEnvironmentId&tenants=$tenant" -Headers @{ "X-Octopus-ApiKey"=$deployApiKey }).Content | ConvertFrom-Json + $releasesInProductionResponse = (Invoke-WebRequest -Uri "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$productionEnvironmentId&tenants=$tenant" -Headers @{ "X-Octopus-ApiKey"=$dynamicWorkerInstanceApiKey }).Content | ConvertFrom-Json $release = $releasesInProductionResponse.Items | Sort-Object -Property "Created" -Descending | Select-Object -First 1 $releases += $release @@ -51,9 +52,5 @@ function Get-ProductionDWVersions { Write-Host ($releases | Select-Object -ExpandProperty "ReleaseId") } -function Execute { - $dynamicWorkerReleases = Get-FromApi "$branchUrl/api/deployments?projects=$promotionProjectId" $branchApiKey - $dynamicWorkerDeployments = Get-FromApi "$branchUrl/api/deployments?projects=$promotionProjectId&environments=$dockerhubEnvironmentId" $branchApiKey - - Get-ProductionDWVersions -} \ No newline at end of file +$dynamicWorkerReleases = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId" $targetInstanceApiKey +$dynamicWorkerDeployments = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId&environments=$dockerhubEnvironmentId" $targetInstanceApiKey From 6fcd63a5dcc5f4be60a13f468bc61432a43f6e81 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 15:19:32 +1000 Subject: [PATCH 07/24] task: use classes for Deployment and Release --- .../enthusiastic-promotor.ps1 | 57 ++++++++++++++++--- .../enthusiastic-promotor.tests.ps1 | 29 +++++++++- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index ac68be2..a36cc7c 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -1,11 +1,11 @@ [CmdletBinding()] param ( - [Parameter(Mandatory = $true)][string] $dynamicWorkerInstanceApiKey, + [Parameter()][string] $dynamicWorkerInstanceApiKey = "", [Parameter()][string] $dynamicWorkerInstanceUrl = "https://deploy.octopus.app", [Parameter()][string] $dynamicWorkerProjectId = "Projects-5063", [Parameter()][string] $dynamicWorkerSpaceId = "Spaces-142", - [Parameter(Mandatory = $true)][string] $targetInstanceApiKey, + [Parameter()][string] $targetInstanceApiKey = "", [Parameter()][string] $targetInstanceUrl = "https://deploy-fnm.testoctopus.app", [Parameter()][string] $targetProjectId = "Projects-381", [Parameter()][string] $targetSpaceId = "Spaces-1", @@ -16,6 +16,32 @@ $dockerhubEnvironmentId = "Environments-62" $productionEnvironmentId = "Environments-842" $productionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") +class Release { + [string]$ReleaseId + [string]$ProjectId + + Release() {} + + Release($releaseId, $projectId) { + $this.ReleaseId = $releaseId + $this.ProjectId = $projectId + } +} + +class Deployment { + [string]$DeploymentId + [string]$ReleaseId + [string]$EnvironmentId + + Deployment() {} + + Deployment($deploymentId, $releaseId, $environmentId) { + $this.DeploymentId = $deploymentId + $this.ReleaseId = $releaseId + $this.EnvironmentId = $environmentId + } +} + function Get-FromApi($url, $apiKey) { Write-Verbose "Getting response from $url" # $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $enthusiasticPromoterApiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 @@ -29,10 +55,13 @@ function Get-FromApi($url, $apiKey) { return $result } -function Get-PromotionCandidates($dynamicWorkerReleases, $dynamicWorkerDeployments) { - - $uniqueReleases = $dynamicWorkerReleases.Items | Select-Object -ExpandProperty "ReleaseId" -Unique - $promotedReleases = $dynamicWorkerDeployments.Items | Select-Object -ExpandProperty "ReleaseId" -Unique +function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[]]$dynamicWorkerDeployments) { + if ($dynamicWorkerReleases.Count -eq 0 -or $dynamicWorkerDeployments.Count -eq 0) { + return + } + + $uniqueReleases = $dynamicWorkerReleases | Select-Object Property "ReleaseId" -Unique + $promotedReleases = $dynamicWorkerDeployments | Select-Object -Property "ReleaseId" -Unique $candidates = $uniqueReleases | Where-Object { -not ($_ -in $promotedReleases) } @@ -43,7 +72,7 @@ function Get-ProductionDWVersions { $releases = @(); foreach ($tenant in $productionTenants) { - $releasesInProductionResponse = (Invoke-WebRequest -Uri "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$productionEnvironmentId&tenants=$tenant" -Headers @{ "X-Octopus-ApiKey"=$dynamicWorkerInstanceApiKey }).Content | ConvertFrom-Json + $releasesInProductionResponse = Get-FromApi "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$productionEnvironmentId&tenants=$tenant" $dynamicWorkerInstanceApiKey $release = $releasesInProductionResponse.Items | Sort-Object -Property "Created" -Descending | Select-Object -First 1 $releases += $release @@ -52,5 +81,15 @@ function Get-ProductionDWVersions { Write-Host ($releases | Select-Object -ExpandProperty "ReleaseId") } -$dynamicWorkerReleases = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId" $targetInstanceApiKey -$dynamicWorkerDeployments = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId&environments=$dockerhubEnvironmentId" $targetInstanceApiKey +function Get-Release($projectId, $baseUrl, $apiToken) { + $releasesResponse = Get-FromApi "$baseUrl/api/projects/$projectId" $targetInstanceApiKey + $releasesResponse.Items | Foreach-Object { [Release]::new($_.Id, $_.ProjectId) } +} + +function Get-Deployment($projectId, $environment, $baseUrl, $apiToken) { + $deploymentsResponse = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId&environments=$dockerhubEnvironmentId" $targetInstanceApiKey + $deploymentsResponse.Items | Foreach-Object { [Deployment]::new($_.Id, $_.ReleaseId, $_.EnvironmentId) } +} + +$dynamicWorkerReleases = Get-Release $targetProjectId $targetInstanceUrl $targetInstanceApiKey +$dynamicWorkerDeployments = Get-Deployment $targetProjectId $dockerhubEnvironmentId $targetInstanceUrl $targetInstanceApiKey diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 65c638e..0a06425 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -4,7 +4,34 @@ BeforeAll { Describe "Get-PromotionCandidates" { It "with no parameters returns nothing" { - $result = Get-PromotionCandidates + # Act + $result = Get-PromotionCandidates ([Release[]] @()) ([Deployment[]] @()) + + # Assert + $result.Count | Should -Be 0 + } + + It "with a Release and no Deployments returns nothing" { + # Arrange + $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) + $deployments = [Deployment[]] @() + + # Act + $result = Get-PromotionCandidates $releases $deployments + + # Assert + $result.Count | Should -Be 0 + } + + It "with no Releases and a Deployment returns nothing" { + # Arrange + $releases = [Release[]] @() + $deployments = [Deployment[]] @( [Deployment]::new("Deployment-1", "Release-1", "Project-1") ) + + # Act + $result = Get-PromotionCandidates $releases $deployments + + # Assert $result.Count | Should -Be 0 } } From 7db2a485f2a2604f2ba24e1342506b0554bc9b1d Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 16:01:00 +1000 Subject: [PATCH 08/24] task: pull classes out to a module --- .../enthusiastic-promotor.ps1 | 33 ++++--------------- .../enthusiastic-promotor.psm1 | 21 ++++++++++++ .../enthusiastic-promotor.tests.ps1 | 7 +++- 3 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 EnthusiasticPromotions/enthusiastic-promotor.psm1 diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index a36cc7c..0479af1 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -1,3 +1,5 @@ +using module .\enthusiastic-promotor.psm1 + [CmdletBinding()] param ( [Parameter()][string] $dynamicWorkerInstanceApiKey = "", @@ -9,39 +11,16 @@ param ( [Parameter()][string] $targetInstanceUrl = "https://deploy-fnm.testoctopus.app", [Parameter()][string] $targetProjectId = "Projects-381", [Parameter()][string] $targetSpaceId = "Spaces-1", - [Parameter()][string] $runbookProjectId = "Projects-386" + [Parameter()][string] $runbookProjectId = "Projects-386", + + [Parameter()][string] $targetProjectTestEnvironment = "Environments-61", + [Parameter()][string] $targetProjectProdEnvironment = "Environments-62" ) $dockerhubEnvironmentId = "Environments-62" $productionEnvironmentId = "Environments-842" $productionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") -class Release { - [string]$ReleaseId - [string]$ProjectId - - Release() {} - - Release($releaseId, $projectId) { - $this.ReleaseId = $releaseId - $this.ProjectId = $projectId - } -} - -class Deployment { - [string]$DeploymentId - [string]$ReleaseId - [string]$EnvironmentId - - Deployment() {} - - Deployment($deploymentId, $releaseId, $environmentId) { - $this.DeploymentId = $deploymentId - $this.ReleaseId = $releaseId - $this.EnvironmentId = $environmentId - } -} - function Get-FromApi($url, $apiKey) { Write-Verbose "Getting response from $url" # $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $enthusiasticPromoterApiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 diff --git a/EnthusiasticPromotions/enthusiastic-promotor.psm1 b/EnthusiasticPromotions/enthusiastic-promotor.psm1 new file mode 100644 index 0000000..9d7fa61 --- /dev/null +++ b/EnthusiasticPromotions/enthusiastic-promotor.psm1 @@ -0,0 +1,21 @@ +class Release { + [string]$ReleaseId + [string]$ProjectId + + Release($releaseId, $projectId) { + $this.ReleaseId = $releaseId + $this.ProjectId = $projectId + } +} + +class Deployment { + [string]$DeploymentId + [string]$ReleaseId + [string]$EnvironmentId + + Deployment($deploymentId, $releaseId, $environmentId) { + $this.DeploymentId = $deploymentId + $this.ReleaseId = $releaseId + $this.EnvironmentId = $environmentId + } +} diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 0a06425..4ebbb62 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -1,5 +1,10 @@ +using module .\enthusiastic-promotor.psm1 + +$stagingEnvironment = "Environments-1" +$prodEnvironment = "Environments-2" + BeforeAll { - . (Join-Path -Path $PSScriptRoot -ChildPath "enthusiastic-promotor.ps1") + . .\enthusiastic-promotor.ps1 -targetProjectTestEnvironment $stagingEnvironment -targetProjectProdEnvironment $prodEnvironment } Describe "Get-PromotionCandidates" { From facbc21cf31e2388c0e2fb182bba3c10752744ca Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 16:03:02 +1000 Subject: [PATCH 09/24] feat: filter fully-deployed releases --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 8 +++----- .../enthusiastic-promotor.tests.ps1 | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 0479af1..4f9ad84 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -39,12 +39,10 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ return } - $uniqueReleases = $dynamicWorkerReleases | Select-Object Property "ReleaseId" -Unique - $promotedReleases = $dynamicWorkerDeployments | Select-Object -Property "ReleaseId" -Unique + $deploymentsByRelease = $dynamicWorkerDeployments | Group-Object -Property "ReleaseId" + $candidateReleases = $deploymentsByRelease | Where-Object { ($_.Group | Select-Object -Property EnvironmentId) -contains $targetProjectTestEnvironment -and -not ($_.Group | Select-Object -Property EnvironmentId) -contains $targetProjectProdEnvironment } - $candidates = $uniqueReleases | Where-Object { -not ($_ -in $promotedReleases) } - - Write-Host $candidates + $candidateReleases } function Get-ProductionDWVersions { diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 4ebbb62..d294b24 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -39,4 +39,19 @@ Describe "Get-PromotionCandidates" { # Assert $result.Count | Should -Be 0 } + + It "with one Release deployed to all environments returns nothing" { + # Arrange + $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) + $deployments = [Deployment[]] @( + [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) + [Deployment]::new("Deployment-1", "Release-1", $prodEnvironment) + ) + + # Act + $result = Get-PromotionCandidates $releases $deployments + + # Assert + $result.Count | Should -Be 0 + } } From 5c975ae2b71c2fa1e78f51ccfb508ff47caf1cd2 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 16:10:49 +1000 Subject: [PATCH 10/24] feat: return releases not yet in prod as candidates --- .../enthusiastic-promotor.ps1 | 11 +++++++-- .../enthusiastic-promotor.tests.ps1 | 23 ++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 4f9ad84..a1334ed 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -40,9 +40,16 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ } $deploymentsByRelease = $dynamicWorkerDeployments | Group-Object -Property "ReleaseId" - $candidateReleases = $deploymentsByRelease | Where-Object { ($_.Group | Select-Object -Property EnvironmentId) -contains $targetProjectTestEnvironment -and -not ($_.Group | Select-Object -Property EnvironmentId) -contains $targetProjectProdEnvironment } + + $candidateReleases = @() + foreach ($release in $deploymentsByRelease) { + $deployedToEnvironments = $release.Group | Select-Object -ExpandProperty EnvironmentId + if ($deployedToEnvironments -contains $targetProjectTestEnvironment -and -not ($deployedToEnvironments -contains $targetProjectProdEnvironment)) { + $candidateReleases += $release.Name + } + } - $candidateReleases + $dynamicWorkerReleases | Where-Object { $candidateReleases -contains $_.ReleaseId } } function Get-ProductionDWVersions { diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index d294b24..77d6d55 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -40,7 +40,7 @@ Describe "Get-PromotionCandidates" { $result.Count | Should -Be 0 } - It "with one Release deployed to all environments returns nothing" { + It "ignores already promoted Releases" { # Arrange $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) $deployments = [Deployment[]] @( @@ -54,4 +54,25 @@ Describe "Get-PromotionCandidates" { # Assert $result.Count | Should -Be 0 } + + It "includes unpromoted Releases" { + # Arrange + $releases = [Release[]] @( + [Release]::new("Release-1", "Project-1") + [Release]::new("Release-2", "Project-1") + ) + $deployments = [Deployment[]] @( + [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) + [Deployment]::new("Deployment-1", "Release-1", $prodEnvironment) + + [Deployment]::new("Deployment-1", "Release-2", $stagingEnvironment) + ) + + # Act + $result = Get-PromotionCandidates $releases $deployments + + # Assert + $result.Count | Should -Be 1 + $result[0].ReleaseId | Should -Be "Release-2" + } } From 6f248f2a4aa9f00b87ff6c5d5ece96f7d19ab941 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 16:50:21 +1000 Subject: [PATCH 11/24] feat: ignore superseded releases --- .../enthusiastic-promotor.ps1 | 23 +++++++++---- .../enthusiastic-promotor.psm1 | 7 ++++ .../enthusiastic-promotor.tests.ps1 | 34 ++++++++++++++++--- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index a1334ed..36a6536 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -39,17 +39,26 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ return } - $deploymentsByRelease = $dynamicWorkerDeployments | Group-Object -Property "ReleaseId" - + $chronologicalReleases = $dynamicWorkerReleases | Sort-Object -Property "Created", "ReleaseId" -PipelineVariable Release | Foreach-Object { @{ Release = $Release; Deployments = ($dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) } } + $candidateReleases = @() - foreach ($release in $deploymentsByRelease) { - $deployedToEnvironments = $release.Group | Select-Object -ExpandProperty EnvironmentId - if ($deployedToEnvironments -contains $targetProjectTestEnvironment -and -not ($deployedToEnvironments -contains $targetProjectProdEnvironment)) { - $candidateReleases += $release.Name + foreach ($release in $chronologicalReleases) { + $deployedToEnvironments = $dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseId } | Select-Object -ExpandProperty EnvironmentId + + if ($deployedToEnvironments -contains $targetProjectTestEnvironment) { + if ($deployedToEnvironments -contains $targetProjectProdEnvironment) { + foreach ($supersededCandidate in $candidateReleases) { + Write-Verbose "Ignoring $($supersededCandidate.ReleaseID) because it is superseded by $($release.ReleaseId), which was created later and has been fully promoted." + } + + $candidateReleases = @() + } else { + $candidateReleases += $release.Release + } } } - $dynamicWorkerReleases | Where-Object { $candidateReleases -contains $_.ReleaseId } + $candidateReleases } function Get-ProductionDWVersions { diff --git a/EnthusiasticPromotions/enthusiastic-promotor.psm1 b/EnthusiasticPromotions/enthusiastic-promotor.psm1 index 9d7fa61..425971f 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.psm1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.psm1 @@ -1,11 +1,18 @@ class Release { [string]$ReleaseId [string]$ProjectId + [DateTime]$CreatedDate Release($releaseId, $projectId) { $this.ReleaseId = $releaseId $this.ProjectId = $projectId } + + Release($releaseId, $projectId, $createdDate) { + $this.ReleaseId = $releaseId + $this.ProjectId = $projectId + $this.CreatedDate = $createdDate + } } class Deployment { diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 77d6d55..ce9edc3 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -45,7 +45,7 @@ Describe "Get-PromotionCandidates" { $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) $deployments = [Deployment[]] @( [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - [Deployment]::new("Deployment-1", "Release-1", $prodEnvironment) + [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) ) # Act @@ -58,14 +58,14 @@ Describe "Get-PromotionCandidates" { It "includes unpromoted Releases" { # Arrange $releases = [Release[]] @( - [Release]::new("Release-1", "Project-1") - [Release]::new("Release-2", "Project-1") + [Release]::new("Release-1", "Project-1") + [Release]::new("Release-2", "Project-1") ) $deployments = [Deployment[]] @( [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - [Deployment]::new("Deployment-1", "Release-1", $prodEnvironment) + [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) - [Deployment]::new("Deployment-1", "Release-2", $stagingEnvironment) + [Deployment]::new("Deployment-3", "Release-2", $stagingEnvironment) ) # Act @@ -75,4 +75,28 @@ Describe "Get-PromotionCandidates" { $result.Count | Should -Be 1 $result[0].ReleaseId | Should -Be "Release-2" } + + It "excludes superseded unpromoted Releases" { + # Arrange + $releases = [Release[]] @( + [Release]::new("Release-1", "Project-1", [DateTime]::Now.AddDays(-10)) + [Release]::new("Release-2", "Project-1", [DateTime]::Now.AddDays(-7)) + [Release]::new("Release-3", "Project-1", [DateTime]::Now.AddDays(-3)) + ) + $deployments = [Deployment[]] @( + [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) + + [Deployment]::new("Deployment-2", "Release-2", $stagingEnvironment) + [Deployment]::new("Deployment-3", "Release-2", $prodEnvironment) + + [Deployment]::new("Deployment-4", "Release-3", $stagingEnvironment) + ) + + # Act + $result = Get-PromotionCandidates $releases $deployments + + # Assert + $result.Count | Should -Be 1 + $result[0].ReleaseId | Should -Be "Release-3" + } } From adc8de514467eb3b2314fedee34d38278e6f2e9d Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 16:55:49 +1000 Subject: [PATCH 12/24] task: pull environment details up to params --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 36a6536..53a6a2f 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -7,6 +7,8 @@ param ( [Parameter()][string] $dynamicWorkerProjectId = "Projects-5063", [Parameter()][string] $dynamicWorkerSpaceId = "Spaces-142", + [Parameter()][string] $dynamicWorkerProdEnvironment = "Environments-842", + [Parameter()][string] $targetInstanceApiKey = "", [Parameter()][string] $targetInstanceUrl = "https://deploy-fnm.testoctopus.app", [Parameter()][string] $targetProjectId = "Projects-381", @@ -17,8 +19,6 @@ param ( [Parameter()][string] $targetProjectProdEnvironment = "Environments-62" ) -$dockerhubEnvironmentId = "Environments-62" -$productionEnvironmentId = "Environments-842" $productionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") function Get-FromApi($url, $apiKey) { @@ -65,7 +65,7 @@ function Get-ProductionDWVersions { $releases = @(); foreach ($tenant in $productionTenants) { - $releasesInProductionResponse = Get-FromApi "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$productionEnvironmentId&tenants=$tenant" $dynamicWorkerInstanceApiKey + $releasesInProductionResponse = Get-FromApi "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$dynamicWorkerProdEnvironment&tenants=$tenant" $dynamicWorkerInstanceApiKey $release = $releasesInProductionResponse.Items | Sort-Object -Property "Created" -Descending | Select-Object -First 1 $releases += $release @@ -79,10 +79,10 @@ function Get-Release($projectId, $baseUrl, $apiToken) { $releasesResponse.Items | Foreach-Object { [Release]::new($_.Id, $_.ProjectId) } } -function Get-Deployment($projectId, $environment, $baseUrl, $apiToken) { - $deploymentsResponse = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId&environments=$dockerhubEnvironmentId" $targetInstanceApiKey +function Get-Deployment($projectId, $baseUrl, $apiToken) { + $deploymentsResponse = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId" $targetInstanceApiKey $deploymentsResponse.Items | Foreach-Object { [Deployment]::new($_.Id, $_.ReleaseId, $_.EnvironmentId) } } $dynamicWorkerReleases = Get-Release $targetProjectId $targetInstanceUrl $targetInstanceApiKey -$dynamicWorkerDeployments = Get-Deployment $targetProjectId $dockerhubEnvironmentId $targetInstanceUrl $targetInstanceApiKey +$dynamicWorkerDeployments = Get-Deployment $targetProjectId $targetInstanceUrl $targetInstanceApiKey From 40233821b75fd50abfcab59167a95e2a01e3f916 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 16:56:10 +1000 Subject: [PATCH 13/24] task: make code more readable --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 53a6a2f..43ef67f 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -39,7 +39,13 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ return } - $chronologicalReleases = $dynamicWorkerReleases | Sort-Object -Property "Created", "ReleaseId" -PipelineVariable Release | Foreach-Object { @{ Release = $Release; Deployments = ($dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) } } + $chronologicalReleases = $dynamicWorkerReleases | ` + Sort-Object -Property "Created", "ReleaseId" -PipelineVariable Release | ` + Foreach-Object { @{ + Release = $Release; + Deployments = ($dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) + } + } $candidateReleases = @() foreach ($release in $chronologicalReleases) { From c9ae0ff5e909cbebd0d088ad9bbdf2d9326c765f Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 16:57:20 +1000 Subject: [PATCH 14/24] task: small rename --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 43ef67f..e96150d 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -19,7 +19,7 @@ param ( [Parameter()][string] $targetProjectProdEnvironment = "Environments-62" ) -$productionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") +$dynamicWorkerProductionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") function Get-FromApi($url, $apiKey) { Write-Verbose "Getting response from $url" @@ -70,7 +70,7 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ function Get-ProductionDWVersions { $releases = @(); - foreach ($tenant in $productionTenants) { + foreach ($tenant in $dynamicWorkerProductionTenants) { $releasesInProductionResponse = Get-FromApi "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$dynamicWorkerProdEnvironment&tenants=$tenant" $dynamicWorkerInstanceApiKey $release = $releasesInProductionResponse.Items | Sort-Object -Property "Created" -Descending | Select-Object -First 1 From be0a71deb019186a88b77c8b6a2ffacd04d8b71e Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 20 Jun 2022 17:11:10 +1000 Subject: [PATCH 15/24] task: clean up the API calls --- .../enthusiastic-promotor.ps1 | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index e96150d..2f212d3 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -23,14 +23,14 @@ $dynamicWorkerProductionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-828 function Get-FromApi($url, $apiKey) { Write-Verbose "Getting response from $url" - # $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $enthusiasticPromoterApiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 + $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $apiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 # log out the json, so we can diagnose what's happening / write a test for it - write-verbose "--------------------------------------------------------" - write-verbose "response:" - write-verbose "--------------------------------------------------------" - write-verbose ($result | ConvertTo-Json -depth 10) - write-verbose "--------------------------------------------------------" + Write-Debug "--------------------------------------------------------" + Write-Debug "response:" + Write-Debug "--------------------------------------------------------" + Write-Debug ($result | ConvertTo-Json -depth 10) + Write-Debug "--------------------------------------------------------" return $result } @@ -54,7 +54,7 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ if ($deployedToEnvironments -contains $targetProjectTestEnvironment) { if ($deployedToEnvironments -contains $targetProjectProdEnvironment) { foreach ($supersededCandidate in $candidateReleases) { - Write-Verbose "Ignoring $($supersededCandidate.ReleaseID) because it is superseded by $($release.ReleaseId), which was created later and has been fully promoted." + Write-Host "Ignoring $($supersededCandidate.ReleaseID) because it is superseded by $($release.ReleaseId), which was created later and has been fully promoted." } $candidateReleases = @() @@ -81,8 +81,8 @@ function Get-ProductionDWVersions { } function Get-Release($projectId, $baseUrl, $apiToken) { - $releasesResponse = Get-FromApi "$baseUrl/api/projects/$projectId" $targetInstanceApiKey - $releasesResponse.Items | Foreach-Object { [Release]::new($_.Id, $_.ProjectId) } + $releasesResponse = Get-FromApi "$baseUrl/api/projects/$projectId/releases" $targetInstanceApiKey + $releasesResponse.Items | Foreach-Object { [Release]::new($_.Id, $_.ProjectId, $_.Assembled) } } function Get-Deployment($projectId, $baseUrl, $apiToken) { @@ -92,3 +92,5 @@ function Get-Deployment($projectId, $baseUrl, $apiToken) { $dynamicWorkerReleases = Get-Release $targetProjectId $targetInstanceUrl $targetInstanceApiKey $dynamicWorkerDeployments = Get-Deployment $targetProjectId $targetInstanceUrl $targetInstanceApiKey + +Get-PromotionCandidates $dynamicWorkerReleases $dynamicWorkerDeployments \ No newline at end of file From 0fe76058c50eb079f2298822cb4a5727889d7072 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Wed, 22 Jun 2022 09:08:05 +1000 Subject: [PATCH 16/24] task: update param defaults --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 2f212d3..3be29a0 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -10,10 +10,10 @@ param ( [Parameter()][string] $dynamicWorkerProdEnvironment = "Environments-842", [Parameter()][string] $targetInstanceApiKey = "", - [Parameter()][string] $targetInstanceUrl = "https://deploy-fnm.testoctopus.app", + [Parameter()][string] $targetInstanceUrl = "https://deploy.octopus.app", [Parameter()][string] $targetProjectId = "Projects-381", - [Parameter()][string] $targetSpaceId = "Spaces-1", - [Parameter()][string] $runbookProjectId = "Projects-386", + [Parameter()][string] $targetSpaceId = "Projects-6481", + [Parameter()][string] $runbookProjectId = "", [Parameter()][string] $targetProjectTestEnvironment = "Environments-61", [Parameter()][string] $targetProjectProdEnvironment = "Environments-62" From 55c2e22c4cee482eb7be4f60096db6f76b27a4a0 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Wed, 22 Jun 2022 09:12:02 +1000 Subject: [PATCH 17/24] task: clearer distinction between Octo projects to query --- .../enthusiastic-promotor.ps1 | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 3be29a0..ed18296 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -19,7 +19,20 @@ param ( [Parameter()][string] $targetProjectProdEnvironment = "Environments-62" ) -$dynamicWorkerProductionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") +$workerToolsProject = @{ + BaseUri = $targetInstanceUrl + ApiKey = $targetInstanceApiKey + ProjectId = $targetProjectId + SpaceId = $targetSpaceId +} + +$dynamicWorkerProject = @{ + BaseUri = $dynamicWorkerInstanceUrl + ApiKey = $dynamicWorkerInstanceApiKey + ProjectId = $dynamicWorkerProjectId + SpaceId = $dynamicWorkerSpaceId + ProductionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") +} function Get-FromApi($url, $apiKey) { Write-Verbose "Getting response from $url" @@ -80,17 +93,17 @@ function Get-ProductionDWVersions { Write-Host ($releases | Select-Object -ExpandProperty "ReleaseId") } -function Get-Release($projectId, $baseUrl, $apiToken) { - $releasesResponse = Get-FromApi "$baseUrl/api/projects/$projectId/releases" $targetInstanceApiKey +function Get-Release($octopusProject) { + $releasesResponse = Get-FromApi "$($octopusProject.BaseUri)/api/projects/$($octopusProject.ProjectId)/releases" $octopusProject.ApiKey $releasesResponse.Items | Foreach-Object { [Release]::new($_.Id, $_.ProjectId, $_.Assembled) } } -function Get-Deployment($projectId, $baseUrl, $apiToken) { - $deploymentsResponse = Get-FromApi "$targetInstanceUrl/api/deployments?projects=$targetProjectId" $targetInstanceApiKey +function Get-Deployment($octopusProject) { + $deploymentsResponse = Get-FromApi "$($octopusProject.BaseUri)/api/deployments?projects=$($octopusProject.ProjectId)" $octopusProject.ApiKey $deploymentsResponse.Items | Foreach-Object { [Deployment]::new($_.Id, $_.ReleaseId, $_.EnvironmentId) } } -$dynamicWorkerReleases = Get-Release $targetProjectId $targetInstanceUrl $targetInstanceApiKey -$dynamicWorkerDeployments = Get-Deployment $targetProjectId $targetInstanceUrl $targetInstanceApiKey - -Get-PromotionCandidates $dynamicWorkerReleases $dynamicWorkerDeployments \ No newline at end of file +# Find our candidates for promotion +$workerToolsReleases = Get-Release $workerToolsProject +$workerToolsDeployments = Get-Deployment $workerToolsProject +$promotionCandidates = Get-PromotionCandidates $workerToolsReleases $workerToolsDeployments From 85769d0d3784129816a1e5f56a77e70c38a1f22d Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Wed, 22 Jun 2022 10:36:35 +1000 Subject: [PATCH 18/24] task: fix variable scoping for tests --- .../enthusiastic-promotor.ps1 | 22 +++--- .../enthusiastic-promotor.tests.ps1 | 72 ++++++++++--------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index ed18296..e679b02 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -47,7 +47,7 @@ function Get-FromApi($url, $apiKey) { return $result } -function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[]]$dynamicWorkerDeployments) { +function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[]]$dynamicWorkerDeployments, [string]$testEnvironment, [string]$prodEnvironment) { if ($dynamicWorkerReleases.Count -eq 0 -or $dynamicWorkerDeployments.Count -eq 0) { return } @@ -59,13 +59,13 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ Deployments = ($dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) } } - + $candidateReleases = @() foreach ($release in $chronologicalReleases) { - $deployedToEnvironments = $dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseId } | Select-Object -ExpandProperty EnvironmentId - - if ($deployedToEnvironments -contains $targetProjectTestEnvironment) { - if ($deployedToEnvironments -contains $targetProjectProdEnvironment) { + $deployedToEnvironments = $dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseID } | Select-Object -ExpandProperty EnvironmentId + + if ($deployedToEnvironments -contains $testEnvironment) { + if ($deployedToEnvironments -contains $prodEnvironment) { foreach ($supersededCandidate in $candidateReleases) { Write-Host "Ignoring $($supersededCandidate.ReleaseID) because it is superseded by $($release.ReleaseId), which was created later and has been fully promoted." } @@ -103,7 +103,9 @@ function Get-Deployment($octopusProject) { $deploymentsResponse.Items | Foreach-Object { [Deployment]::new($_.Id, $_.ReleaseId, $_.EnvironmentId) } } -# Find our candidates for promotion -$workerToolsReleases = Get-Release $workerToolsProject -$workerToolsDeployments = Get-Deployment $workerToolsProject -$promotionCandidates = Get-PromotionCandidates $workerToolsReleases $workerToolsDeployments +function Invoke-Promotions() { + # Find our candidates for promotion + $workerToolsReleases = Get-Release $workerToolsProject + $workerToolsDeployments = Get-Deployment $workerToolsProject + $promotionCandidates = Get-PromotionCandidates $workerToolsReleases $workerToolsDeployments $targetProjectTestEnvironment $targetProjectProdEnvironment +} diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index ce9edc3..32459c2 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -1,43 +1,49 @@ using module .\enthusiastic-promotor.psm1 -$stagingEnvironment = "Environments-1" -$prodEnvironment = "Environments-2" - BeforeAll { - . .\enthusiastic-promotor.ps1 -targetProjectTestEnvironment $stagingEnvironment -targetProjectProdEnvironment $prodEnvironment + . .\enthusiastic-promotor.ps1 + + $stagingEnvironment = "Environments-1" + $prodEnvironment = "Environments-2" } Describe "Get-PromotionCandidates" { - It "with no parameters returns nothing" { - # Act - $result = Get-PromotionCandidates ([Release[]] @()) ([Deployment[]] @()) - - # Assert - $result.Count | Should -Be 0 + Context "with no parameters" { + It "returns nothing" { + # Act + $result = Get-PromotionCandidates ([Release[]] @()) ([Deployment[]] @()) $stagingEnvironment $prodEnvironment + + # Assert + $result.Count | Should -Be 0 + } } - It "with a Release and no Deployments returns nothing" { - # Arrange - $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) - $deployments = [Deployment[]] @() - - # Act - $result = Get-PromotionCandidates $releases $deployments - - # Assert - $result.Count | Should -Be 0 + Context "with a Release and no Deployments" { + It "returns nothing" { + # Arrange + $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) + $deployments = [Deployment[]] @() + + # Act + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + + # Assert + $result.Count | Should -Be 0 + } } - It "with no Releases and a Deployment returns nothing" { - # Arrange - $releases = [Release[]] @() - $deployments = [Deployment[]] @( [Deployment]::new("Deployment-1", "Release-1", "Project-1") ) - - # Act - $result = Get-PromotionCandidates $releases $deployments - - # Assert - $result.Count | Should -Be 0 + Context "with no Releases and a Deployment" { + It "returns nothing" { + # Arrange + $releases = [Release[]] @() + $deployments = [Deployment[]] @( [Deployment]::new("Deployment-1", "Release-1", "Project-1") ) + + # Act + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + + # Assert + $result.Count | Should -Be 0 + } } It "ignores already promoted Releases" { @@ -49,7 +55,7 @@ Describe "Get-PromotionCandidates" { ) # Act - $result = Get-PromotionCandidates $releases $deployments + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 0 @@ -69,7 +75,7 @@ Describe "Get-PromotionCandidates" { ) # Act - $result = Get-PromotionCandidates $releases $deployments + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 1 @@ -93,7 +99,7 @@ Describe "Get-PromotionCandidates" { ) # Act - $result = Get-PromotionCandidates $releases $deployments + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 1 From 28cbb69b31c434f6e4a0198c19485a72828a7848 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Wed, 22 Jun 2022 10:39:39 +1000 Subject: [PATCH 19/24] test: add context to tests without it --- .../enthusiastic-promotor.tests.ps1 | 101 +++++++++--------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 32459c2..04daecd 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -45,64 +45,65 @@ Describe "Get-PromotionCandidates" { $result.Count | Should -Be 0 } } + Context "with valid Releases and Deployments" { + It "ignores already promoted Releases" { + # Arrange + $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) + $deployments = [Deployment[]] @( + [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) + [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) + ) - It "ignores already promoted Releases" { - # Arrange - $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) - $deployments = [Deployment[]] @( - [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) - ) + # Act + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + # Assert + $result.Count | Should -Be 0 + } - # Assert - $result.Count | Should -Be 0 - } + It "includes unpromoted Releases" { + # Arrange + $releases = [Release[]] @( + [Release]::new("Release-1", "Project-1") + [Release]::new("Release-2", "Project-1") + ) + $deployments = [Deployment[]] @( + [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) + [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) - It "includes unpromoted Releases" { - # Arrange - $releases = [Release[]] @( - [Release]::new("Release-1", "Project-1") - [Release]::new("Release-2", "Project-1") - ) - $deployments = [Deployment[]] @( - [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) - - [Deployment]::new("Deployment-3", "Release-2", $stagingEnvironment) - ) - - # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - - # Assert - $result.Count | Should -Be 1 - $result[0].ReleaseId | Should -Be "Release-2" - } + [Deployment]::new("Deployment-3", "Release-2", $stagingEnvironment) + ) - It "excludes superseded unpromoted Releases" { - # Arrange - $releases = [Release[]] @( - [Release]::new("Release-1", "Project-1", [DateTime]::Now.AddDays(-10)) - [Release]::new("Release-2", "Project-1", [DateTime]::Now.AddDays(-7)) - [Release]::new("Release-3", "Project-1", [DateTime]::Now.AddDays(-3)) - ) - $deployments = [Deployment[]] @( - [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) + # Act + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + + # Assert + $result.Count | Should -Be 1 + $result[0].ReleaseId | Should -Be "Release-2" + } + + It "excludes superseded unpromoted Releases" { + # Arrange + $releases = [Release[]] @( + [Release]::new("Release-1", "Project-1", [DateTime]::Now.AddDays(-10)) + [Release]::new("Release-2", "Project-1", [DateTime]::Now.AddDays(-7)) + [Release]::new("Release-3", "Project-1", [DateTime]::Now.AddDays(-3)) + ) + $deployments = [Deployment[]] @( + [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - [Deployment]::new("Deployment-2", "Release-2", $stagingEnvironment) - [Deployment]::new("Deployment-3", "Release-2", $prodEnvironment) + [Deployment]::new("Deployment-2", "Release-2", $stagingEnvironment) + [Deployment]::new("Deployment-3", "Release-2", $prodEnvironment) - [Deployment]::new("Deployment-4", "Release-3", $stagingEnvironment) - ) + [Deployment]::new("Deployment-4", "Release-3", $stagingEnvironment) + ) - # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + # Act + $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - # Assert - $result.Count | Should -Be 1 - $result[0].ReleaseId | Should -Be "Release-3" + # Assert + $result.Count | Should -Be 1 + $result[0].ReleaseId | Should -Be "Release-3" + } } } From 0dcb302bda1f784a8d9d65b2e03868b330a6f6d3 Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Wed, 22 Jun 2022 10:43:38 +1000 Subject: [PATCH 20/24] fix: incorrect references --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index e679b02..12bee2d 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -62,12 +62,12 @@ function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[ $candidateReleases = @() foreach ($release in $chronologicalReleases) { - $deployedToEnvironments = $dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseID } | Select-Object -ExpandProperty EnvironmentId + $deployedToEnvironments = $dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseId } | Select-Object -ExpandProperty EnvironmentId if ($deployedToEnvironments -contains $testEnvironment) { if ($deployedToEnvironments -contains $prodEnvironment) { foreach ($supersededCandidate in $candidateReleases) { - Write-Host "Ignoring $($supersededCandidate.ReleaseID) because it is superseded by $($release.ReleaseId), which was created later and has been fully promoted." + Write-Verbose "Ignoring $($supersededCandidate.ReleaseId) because it is superseded by $($release.Release.ReleaseId), which was created later and has been fully promoted." } $candidateReleases = @() From e37fa329959346bd703a6ab610368e493ba4d64c Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Wed, 22 Jun 2022 10:45:06 +1000 Subject: [PATCH 21/24] fix: naming in Get-PromotionCandidate --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 12bee2d..84c2439 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -47,22 +47,22 @@ function Get-FromApi($url, $apiKey) { return $result } -function Get-PromotionCandidates([Release[]]$dynamicWorkerReleases, [Deployment[]]$dynamicWorkerDeployments, [string]$testEnvironment, [string]$prodEnvironment) { - if ($dynamicWorkerReleases.Count -eq 0 -or $dynamicWorkerDeployments.Count -eq 0) { +function Get-PromotionCandidates([Release[]]$workerToolReleases, [Deployment[]]$workerToolDeployments, [string]$testEnvironment, [string]$prodEnvironment) { + if ($workerToolReleases.Count -eq 0 -or $workerToolDeployments.Count -eq 0) { return } - $chronologicalReleases = $dynamicWorkerReleases | ` + $chronologicalReleases = $workerToolReleases | ` Sort-Object -Property "Created", "ReleaseId" -PipelineVariable Release | ` Foreach-Object { @{ Release = $Release; - Deployments = ($dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) + Deployments = ($workerToolDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) } } $candidateReleases = @() foreach ($release in $chronologicalReleases) { - $deployedToEnvironments = $dynamicWorkerDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseId } | Select-Object -ExpandProperty EnvironmentId + $deployedToEnvironments = $workerToolDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseId } | Select-Object -ExpandProperty EnvironmentId if ($deployedToEnvironments -contains $testEnvironment) { if ($deployedToEnvironments -contains $prodEnvironment) { From 0aeb230a7842d5502402bd3d7c69e5b4dcac029d Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Wed, 22 Jun 2022 10:47:03 +1000 Subject: [PATCH 22/24] task: use appropriate verb for PromotionCandidates function --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 2 +- .../enthusiastic-promotor.tests.ps1 | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 84c2439..cbdcba7 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -47,7 +47,7 @@ function Get-FromApi($url, $apiKey) { return $result } -function Get-PromotionCandidates([Release[]]$workerToolReleases, [Deployment[]]$workerToolDeployments, [string]$testEnvironment, [string]$prodEnvironment) { +function Select-PromotionCandidates([Release[]]$workerToolReleases, [Deployment[]]$workerToolDeployments, [string]$testEnvironment, [string]$prodEnvironment) { if ($workerToolReleases.Count -eq 0 -or $workerToolDeployments.Count -eq 0) { return } diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 04daecd..8c671cf 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -7,11 +7,11 @@ BeforeAll { $prodEnvironment = "Environments-2" } -Describe "Get-PromotionCandidates" { +Describe "Select-PromotionCandidates" { Context "with no parameters" { It "returns nothing" { # Act - $result = Get-PromotionCandidates ([Release[]] @()) ([Deployment[]] @()) $stagingEnvironment $prodEnvironment + $result = Select-PromotionCandidates ([Release[]] @()) ([Deployment[]] @()) $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 0 @@ -25,7 +25,7 @@ Describe "Get-PromotionCandidates" { $deployments = [Deployment[]] @() # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 0 @@ -39,7 +39,7 @@ Describe "Get-PromotionCandidates" { $deployments = [Deployment[]] @( [Deployment]::new("Deployment-1", "Release-1", "Project-1") ) # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 0 @@ -55,7 +55,7 @@ Describe "Get-PromotionCandidates" { ) # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 0 @@ -75,7 +75,7 @@ Describe "Get-PromotionCandidates" { ) # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 1 @@ -99,7 +99,7 @@ Describe "Get-PromotionCandidates" { ) # Act - $result = Get-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment + $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment # Assert $result.Count | Should -Be 1 From f22e7a83276569f230ed99a7e28fb1e8e2694b1a Mon Sep 17 00:00:00 2001 From: Matt Hilton Date: Mon, 27 Jun 2022 11:23:05 +1000 Subject: [PATCH 23/24] Start work on Select-ProductionDynamicWorkerRelease --- EnthusiasticPromotions/enthusiastic-promotor.ps1 | 14 ++++++++++++-- .../enthusiastic-promotor.tests.ps1 | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index cbdcba7..4c7cf8c 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -80,7 +80,7 @@ function Select-PromotionCandidates([Release[]]$workerToolReleases, [Deployment[ $candidateReleases } -function Get-ProductionDWVersions { +function Select-ProductionDynamicWorkerRelease([Release[]]$dynamicWorkerReleases, [Deployment[]]$dynamicWorkerDeployments) { $releases = @(); foreach ($tenant in $dynamicWorkerProductionTenants) { @@ -107,5 +107,15 @@ function Invoke-Promotions() { # Find our candidates for promotion $workerToolsReleases = Get-Release $workerToolsProject $workerToolsDeployments = Get-Deployment $workerToolsProject - $promotionCandidates = Get-PromotionCandidates $workerToolsReleases $workerToolsDeployments $targetProjectTestEnvironment $targetProjectProdEnvironment + $promotionCandidates = Select-PromotionCandidates $workerToolsReleases $workerToolsDeployments $targetProjectTestEnvironment $targetProjectProdEnvironment + + # Find the current Dynamic Worker production version + $dynamicWorkerReleases = Get-Release $dynamicWorkerProject + $dynamicWorkerDeployments = Get-Deployment $dynamicWorkerProject + $currentProductionDw = Select-ProductionDynamicWorkerRelease $dynamicWorkerReleases $dynamicWorkerDeployments + + # Figure out cached versions of current prod release from TC parameter + + # Promote whichever candidates are ready + } diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 8c671cf..236883e 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -107,3 +107,19 @@ Describe "Select-PromotionCandidates" { } } } + +Describe "Select-ProductionDynamicWorkerRelease" { + Context "with no parameters" { + It "returns nothing" { + # Arrange + $releases = [Release[]] @() + $deployments = [Deployment[]] @() + + # Act + $result = Select-ProductionDynamicWorkerRelease $releases $deployments + + # Assert + $result.Count | Should -Be 0 + } + } +} \ No newline at end of file From ad514ec3dfc4346637b5549489e4777dd695ba80 Mon Sep 17 00:00:00 2001 From: Yihao Date: Wed, 29 Jun 2022 19:08:46 +1200 Subject: [PATCH 24/24] Implement enthusiastic promotor --- .../enthusiastic-promotor.ps1 | 355 ++++++++++++++---- .../enthusiastic-promotor.tests.ps1 | 223 +++++------ 2 files changed, 396 insertions(+), 182 deletions(-) diff --git a/EnthusiasticPromotions/enthusiastic-promotor.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.ps1 index 4c7cf8c..6fda895 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.ps1 @@ -1,121 +1,334 @@ -using module .\enthusiastic-promotor.psm1 - [CmdletBinding()] param ( - [Parameter()][string] $dynamicWorkerInstanceApiKey = "", - [Parameter()][string] $dynamicWorkerInstanceUrl = "https://deploy.octopus.app", - [Parameter()][string] $dynamicWorkerProjectId = "Projects-5063", - [Parameter()][string] $dynamicWorkerSpaceId = "Spaces-142", + [Parameter()][string] $dynamicWorkerInstanceApiKey, + [Parameter()][string] $dynamicWorkerInstanceUrl, + [Parameter()][string] $dynamicWorkerSpaceId, + [Parameter()][string] $dynamicWorkerProjectId, + [Parameter()][string] $dynamicWorkerProdEnvironmentId, - [Parameter()][string] $dynamicWorkerProdEnvironment = "Environments-842", + [Parameter()][string] $targetInstanceApiKey, + [Parameter()][string] $targetInstanceUrl, + [Parameter()][string] $targetSpaceId, + [Parameter()][string] $targetProjectId, + [Parameter()][string] $targetProjectStagingEnvironmentId, + [Parameter()][string] $targetProjectProdEnvironmentId, - [Parameter()][string] $targetInstanceApiKey = "", - [Parameter()][string] $targetInstanceUrl = "https://deploy.octopus.app", - [Parameter()][string] $targetProjectId = "Projects-381", - [Parameter()][string] $targetSpaceId = "Projects-6481", - [Parameter()][string] $runbookProjectId = "", + [Parameter()][string] $teamCityToken, + [Parameter()][string] $teamCityUrl, + [Parameter()][string] $teamCityProjectName, - [Parameter()][string] $targetProjectTestEnvironment = "Environments-61", - [Parameter()][string] $targetProjectProdEnvironment = "Environments-62" + [Parameter()][string] $osSuffix, + [Parameter()][switch] $dryRun = $false ) -$workerToolsProject = @{ - BaseUri = $targetInstanceUrl - ApiKey = $targetInstanceApiKey - ProjectId = $targetProjectId - SpaceId = $targetSpaceId -} - -$dynamicWorkerProject = @{ - BaseUri = $dynamicWorkerInstanceUrl - ApiKey = $dynamicWorkerInstanceApiKey - ProjectId = $dynamicWorkerProjectId - SpaceId = $dynamicWorkerSpaceId - ProductionTenants = @("Tenants-8286", "Tenants-8287", "Tenants-8288") -} +$dynamicWorkerProductionTenantIds = @( + "Tenants-8286" + "Tenants-8287" + "Tenants-8288" +) -function Get-FromApi($url, $apiKey) { +function Get-FromApi($url, $headers, $formatter) { Write-Verbose "Getting response from $url" - $result = Invoke-RestMethod -Uri $url -Headers @{ 'X-Octopus-ApiKey' = $apiKey } -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 - - # log out the json, so we can diagnose what's happening / write a test for it + $result = Invoke-RestMethod -Uri "$url" -Headers $headers -TimeoutSec 60 -RetryIntervalSec 10 -MaximumRetryCount 2 Write-Debug "--------------------------------------------------------" Write-Debug "response:" Write-Debug "--------------------------------------------------------" - Write-Debug ($result | ConvertTo-Json -depth 10) + Write-Debug $($formatter.Invoke($result)) Write-Debug "--------------------------------------------------------" return $result } -function Select-PromotionCandidates([Release[]]$workerToolReleases, [Deployment[]]$workerToolDeployments, [string]$testEnvironment, [string]$prodEnvironment) { - if ($workerToolReleases.Count -eq 0 -or $workerToolDeployments.Count -eq 0) { - return +function Get-FromOctopusApi($url, $apiKey) { + Get-FromApi $url @{ "X-Octopus-ApiKey" = $apiKey } { Param($result) $result | ConvertTo-Json -depth 10 } +} + +function Get-FromTeamCityApi($url, $token) { + Get-FromApi $url @{ "Authorization" = "Bearer $teamCityToken" } { Param($result) $result.outerXml } +} + +function Get-Releases($octopusProject) { + $releasesResponse = Get-FromOctopusApi "$($octopusProject.BaseUri)/api/$($octopusProject.SpaceId)/projects/$($octopusProject.ProjectId)/releases" $octopusProject.ApiKey + $releasesResponse.Items | Foreach-Object { + @{ + ReleaseId = $_.Id + ProjectId = $_.ProjectId + Version = $_.Version + Created = $_.Assembled + } } +} + +function Get-Deployments($octopusProject) { + $deploymentsResponse = Get-FromOctopusApi "$($octopusProject.BaseUri)/api/$($octopusProject.SpaceId)/deployments?projects=$($octopusProject.ProjectId)" $octopusProject.ApiKey + $deploymentsResponse.Items | Foreach-Object { + @{ + DeploymentId = $_.Id + ReleaseId = $_.ReleaseId + EnvironmentId = $_.EnvironmentId + TaskId = $_.TaskId + } + } +} + +function Get-ReleaseVersion($octopusProject, $releaseId) { + $releaseDetailsResponse = Get-FromOctopusApi "$($octopusProject.BaseUri)/api/$($octopusProject.SpaceId)/releases/$releaseId" $octopusProject.ApiKey + $releaseDetailsResponse.Version +} + +function Test-DeploymentSuccessful($octopusProject, $deployment) { + $taskResponse = Get-FromOctopusApi "$($octopusProject.BaseUri)/api/$($octopusProject.SpaceId)/tasks/$($deployment.TaskId)" $octopusProject.ApiKey + $taskResponse.FinishedSuccessfully +} + +function Get-ProductionDynamicWorkerReleaseIds($dynamicWorkerProductionTenantIds) { + $deployments = @(); + foreach ($tenantId in $dynamicWorkerProductionTenantIds) { + $productionDynamicWorkerDeploymentsResponse = Get-FromOctopusApi "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$dynamicWorkerProdEnvironmentId&tenants=$tenantId" $dynamicWorkerInstanceApiKey + $deployment = $productionDynamicWorkerDeploymentsResponse.Items | Sort-Object -Property "Created" -Descending | Select-Object -First 1 + $deployments += $deployment + } + $deployments | Select-Object -ExpandProperty ReleaseId -Unique +} + +function Get-DynamicWorkerBuildId($releaseId) { + $buildNumber = Get-ReleaseVersion $dynamicWorkerProject $releaseId + $buildInformationResponse = Get-FromTeamCityApi "$teamCityUrl/app/rest/builds?locator=buildType:$teamCityProjectName,number:$buildNumber" + $buildInformationResponse | Select-Xml -XPath "/builds/build" | ForEach-Object { $_.Node.id } | Select-Object -First 1 +} - $chronologicalReleases = $workerToolReleases | ` - Sort-Object -Property "Created", "ReleaseId" -PipelineVariable Release | ` - Foreach-Object { @{ - Release = $Release; - Deployments = ($workerToolDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) - } +function Get-CachedWorkerToolsVersions($releaseId) { + $buildId = Get-DynamicWorkerBuildId $releaseId + $buildParametersResponse = Get-FromTeamCityApi "$teamCityUrl/app/rest/builds/$buildId/resulting-properties" + $cachedWorkerToolsVersionsValue = $buildParametersResponse ` + | Select-Xml -XPath "/properties/property" ` + | Where-Object { $_.Node.name -eq "CachedWorkerToolsVersions" } ` + | Select-Object -First 1 -ExpandProperty value + $cachedWorkerToolsVersions = $cachedWorkerToolsVersionsValue -split "," | Select-Object -Unique + Write-Verbose "Cached worker tools versions for Dynamic Worker release $($releaseId):" + $cachedWorkerToolsVersions | ForEach-Object { Write-Verbose " - $_" } + $cachedWorkerToolsVersions +} + +function Select-PromotionCandidates($workerToolReleases, $workerToolDeployments, $stagingEnvironmentId, $prodEnvironmentId) { + if ($workerToolReleases.Count -eq 0 -or $workerToolDeployments.Count -eq 0) { + return @() } + $chronologicalReleases = $workerToolReleases ` + | Sort-Object -Property "Created", "ReleaseId" -PipelineVariable Release ` + | Foreach-Object { + @{ + Release = $Release; + Deployments = ($workerToolDeployments | Where-Object { $_.ReleaseId -eq $Release.ReleaseId }) + } + } + $candidateReleases = @() foreach ($release in $chronologicalReleases) { $deployedToEnvironments = $workerToolDeployments | Where-Object { $_.ReleaseId -eq $release.Release.ReleaseId } | Select-Object -ExpandProperty EnvironmentId - - if ($deployedToEnvironments -contains $testEnvironment) { - if ($deployedToEnvironments -contains $prodEnvironment) { + if ($deployedToEnvironments -contains $stagingEnvironmentId) { + if ($deployedToEnvironments -contains $prodEnvironmentId) { foreach ($supersededCandidate in $candidateReleases) { - Write-Verbose "Ignoring $($supersededCandidate.ReleaseId) because it is superseded by $($release.Release.ReleaseId), which was created later and has been fully promoted." + Write-Verbose "Ignoring $($supersededCandidate.Version) because it is superseded by $($release.Release.Version), which was created later and has been fully promoted." } $candidateReleases = @() } else { $candidateReleases += $release.Release } + } else { + Write-Verbose "Ignoring $($release.Release.Version) because it has not been successfully deployed to Staging" } } $candidateReleases } -function Select-ProductionDynamicWorkerRelease([Release[]]$dynamicWorkerReleases, [Deployment[]]$dynamicWorkerDeployments) { - $releases = @(); +function Select-CommonCachedVersions($cachedVersionLists) { + if ($cachedVersionLists.Count -eq 0) { + return @() + } - foreach ($tenant in $dynamicWorkerProductionTenants) { - $releasesInProductionResponse = Get-FromApi "$dynamicWorkerInstanceUrl/api/$dynamicWorkerSpaceId/deployments?projects=$dynamicWorkerProjectId&environments=$dynamicWorkerProdEnvironment&tenants=$tenant" $dynamicWorkerInstanceApiKey - $release = $releasesInProductionResponse.Items | Sort-Object -Property "Created" -Descending | Select-Object -First 1 - - $releases += $release + $cachedVersions = $cachedVersionLists[0]; + foreach ($versionList in $cachedVersionLists) { + $cachedVersions = $versionList | Where-Object { $cachedVersions -contains $_ } } - Write-Host ($releases | Select-Object -ExpandProperty "ReleaseId") + $cachedVersions | Select-Object -Unique } -function Get-Release($octopusProject) { - $releasesResponse = Get-FromApi "$($octopusProject.BaseUri)/api/projects/$($octopusProject.ProjectId)/releases" $octopusProject.ApiKey - $releasesResponse.Items | Foreach-Object { [Release]::new($_.Id, $_.ProjectId, $_.Assembled) } +function Select-CachedCandidates($promotionCandidates, $cachedWorkerToolsVersions, $osSuffix) { + $promotionCandidates | Where-Object { "$($_.version)-$osSuffix" -in $cachedWorkerToolsVersions } } -function Get-Deployment($octopusProject) { - $deploymentsResponse = Get-FromApi "$($octopusProject.BaseUri)/api/deployments?projects=$($octopusProject.ProjectId)" $octopusProject.ApiKey - $deploymentsResponse.Items | Foreach-Object { [Deployment]::new($_.Id, $_.ReleaseId, $_.EnvironmentId) } +function New-Promotion($release) { + & octo deploy-release ` + --deployTo $targetProjectProdEnvironmentId ` + --version $release.Version ` + --project $targetProjectId ` + --apiKey $targetInstanceApiKey ` + --server "$targetInstanceUrl" ` + --space $targetSpaceId } -function Invoke-Promotions() { - # Find our candidates for promotion - $workerToolsReleases = Get-Release $workerToolsProject - $workerToolsDeployments = Get-Deployment $workerToolsProject - $promotionCandidates = Select-PromotionCandidates $workerToolsReleases $workerToolsDeployments $targetProjectTestEnvironment $targetProjectProdEnvironment +function Invoke-Promotion() { + Write-Host "Finding promotion candidates..." - # Find the current Dynamic Worker production version - $dynamicWorkerReleases = Get-Release $dynamicWorkerProject - $dynamicWorkerDeployments = Get-Deployment $dynamicWorkerProject - $currentProductionDw = Select-ProductionDynamicWorkerRelease $dynamicWorkerReleases $dynamicWorkerDeployments + $workerToolsReleases = Get-Releases $workerToolsProject + $workerToolsDeployments = Get-Deployments $octopusProject | Where-Object { Test-DeploymentSuccessful $octopusProject $_ } + $promotionCandidates = Select-PromotionCandidates $workerToolsReleases $workerToolsDeployments $targetProjectStagingEnvironmentId $targetProjectProdEnvironmentId - # Figure out cached versions of current prod release from TC parameter + if ($promotionCandidates.Count -eq 0) { + Write-Host "No candidates are waiting for promotion" + exit 0 + } - # Promote whichever candidates are ready + Write-Host "Candidates for promotion:" + $promotionCandidates | ForEach-Object { Write-Host " - $($_.Version)" } + Write-Host "Finding cached Worker Tools versions in production..." + + $cachedWorkerToolsVersionLists = Get-ProductionDynamicWorkerReleaseIds $dynamicWorkerProject.ProductionTenants ` + | ForEach-Object { , (Get-CachedWorkerToolsVersions $_) } + $commonCachedWorkerToolsVersions = Select-CommonCachedVersions $cachedWorkerToolsVersionLists + + if ($commonCachedWorkerToolsVersions.Count -eq 0) { + Write-Warning "Cannot find Worker Tools version cached in production" + exit 1 + } + + Write-Host "Cached Worker Tools versions in production:" + $commonCachedWorkerToolsVersions | ForEach-Object { Write-Host " - $_" } + + Write-Host "Deciding Worker Tools releases to promote..." + + $cachedCandidates = Select-CachedCandidates $promotionCandidates $commonCachedWorkerToolsVersions $osSuffix + + if ($cachedCandidates.Count -eq 0) { + Write-Host "No candidates are cached in production" + exit 0 + } + + Write-Host "Worker Tools releases to promote are:" + $cachedCandidates | ForEach-Object { Write-Host " - $($_.Version)" } + + foreach ($release in $cachedCandidates) { + if ($dryRun) { + Write-Host "Skip promoting version $($release.Version) since this is a dry run" + } else { + New-Promotion $release + Write-Host "Promoted release $($release.Version)" + } + } +} + +function Test-AnyArgsPassed { + return $dynamicWorkerInstanceApiKey ` + -or $dynamicWorkerInstanceUrl ` + -or $dynamicWorkerSpaceId ` + -or $dynamicWorkerProjectId ` + -or $dynamicWorkerProdEnvironmentId ` + -or $targetInstanceApiKey ` + -or $targetInstanceUrl ` + -or $targetSpaceId ` + -or $targetProjectId ` + -or $targetProjectStagingEnvironmentId ` + -or $targetProjectProdEnvironmentId ` + -or $teamCityToken ` + -or $teamCityUrl ` + -or $teamCityProjectName ` + -or $osSuffix } + +function Test-AllArgsPassed { + return $dynamicWorkerInstanceApiKey ` + -and $dynamicWorkerInstanceUrl ` + -and $dynamicWorkerSpaceId ` + -and $dynamicWorkerProjectId ` + -and $dynamicWorkerProdEnvironmentId ` + -and $targetInstanceApiKey ` + -and $targetInstanceUrl ` + -and $targetSpaceId ` + -and $targetProjectId ` + -and $targetProjectStagingEnvironmentId ` + -and $targetProjectProdEnvironmentId ` + -and $teamCityToken ` + -and $teamCityUrl ` + -and $teamCityProjectName ` + -and $osSuffix +} + + +if (Test-Path variable:OctopusParameters) { + Write-Host "Reading parameters from `$OctopusParameters" + + $dynamicWorkerInstanceApiKey = $OctopusParameters["DynamicWorkerInstanceApiKey"] + $dynamicWorkerInstanceUrl = $OctopusParameters["DynamicWorkerInstanceUrl"] + $dynamicWorkerSpaceId = $OctopusParameters["DynamicWorkerSpaceId"] + $dynamicWorkerProjectId = $OctopusParameters["DynamicWorkerProjectId"] + $dynamicWorkerProdEnvironmentId = $OctopusParameters["DynamicWorkerProdEnvironmentId"] + + $targetInstanceApiKey = $OctopusParameters["TargetInstanceApiKey"] + $targetInstanceUrl = $OctopusParameters["Octopus.Web.ServerUri"] + $targetSpaceId = $OctopusParameters["Octopus.Space.Id"] + $targetProjectId = $OctopusParameters["Octopus.Project.Id"] + $targetProjectStagingEnvironmentId = $OctopusParameters["TargetInstanceStagingEnvironmentId"] + $targetProjectProdEnvironmentId = $OctopusParameters["TargetInstanceProdEnvironmentId"] + + $teamCityToken = $OctopusParameters["TeamCityToken"] + $teamCityUrl = $OctopusParameters["TeamCityUrl"] + $teamCityProjectName = $OctopusParameters["TeamCityProjectName"] + + $osSuffix = $OctopusParameters["OsSuffix"] +} elseif (Test-AllArgsPassed) { + Write-Host "Reading parameters from command line args" +} elseif (Test-AnyArgsPassed) { + Write-Warning "Some command line args have been passed, but some are missing. Please validated the args you are passing!" + exit 1 +} + +$workerToolsProject = @{ + BaseUri = $targetInstanceUrl + ApiKey = $targetInstanceApiKey + ProjectId = $targetProjectId + SpaceId = $targetSpaceId +} + +$dynamicWorkerProject = @{ + BaseUri = $dynamicWorkerInstanceUrl + ApiKey = $dynamicWorkerInstanceApiKey + ProjectId = $dynamicWorkerProjectId + SpaceId = $dynamicWorkerSpaceId + ProductionTenants = $dynamicWorkerProductionTenantIds +} + +if (Test-AllArgsPassed) { + Write-Debug "Running with parameters: " + Write-Debug " dynamicWorkerInstanceUrl: $dynamicWorkerInstanceUrl" + Write-Debug " dynamicWorkerSpaceId: $dynamicWorkerSpaceId" + Write-Debug " dynamicWorkerProjectId: $dynamicWorkerProjectId" + Write-Debug " dynamicWorkerProdEnvironmentId: $dynamicWorkerProdEnvironmentId" + + Write-Debug " targetInstanceUrl: $targetInstanceUrl" + Write-Debug " targetSpaceId: $targetSpaceId" + Write-Debug " targetProjectId: $targetProjectId" + Write-Debug " targetProjectStagingEnvironmentId: $targetProjectStagingEnvironmentId" + Write-Debug " targetProjectProdEnvironmentId: $targetProjectProdEnvironmentId" + + Write-Debug " teamCityUrl: $teamCityUrl" + Write-Debug " teamCityProjectName: $teamCityProjectName" + + Write-Debug " osSuffix: $osSuffix" + + try { + Invoke-Promotion + } catch { + [System.Console]::Error.WriteLine("$($error[0].CategoryInfo.Category): $($error[0].Exception.Message)") + [System.Console]::Error.WriteLine($error[0].InvocationInfo.PositionMessage) + [System.Console]::Error.WriteLine($error[0].ScriptStackTrace) + if ($null -ne $error[0].ErrorDetails) { + [System.Console]::Error.WriteLine($error[0].ErrorDetails.Message) + } + exit 1 + } +} \ No newline at end of file diff --git a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 index 236883e..4137c6b 100644 --- a/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 +++ b/EnthusiasticPromotions/enthusiastic-promotor.tests.ps1 @@ -1,125 +1,126 @@ -using module .\enthusiastic-promotor.psm1 - BeforeAll { - . .\enthusiastic-promotor.ps1 - - $stagingEnvironment = "Environments-1" - $prodEnvironment = "Environments-2" + . $PSScriptRoot/enthusiastic-promotor.ps1 + + $stagingEnvironmentId = "Environments-1" + $prodEnvironmentId = "Environments-2" + + function New-Release($releaseId, $projectId, $version, $created) { + return New-Object PSObject -Property @{ + ReleaseId = $releaseId + ProjectId = $projectId + Version = $version + Created = $created + } + } + + function New-Deployment($deploymentId, $releaseId, $environmentId) { + return New-Object PSObject -Property @{ + DeploymentId = $deploymentId + ReleaseId = $releaseId + EnvironmentId = $environmentId + } + } } Describe "Select-PromotionCandidates" { - Context "with no parameters" { - It "returns nothing" { - # Act - $result = Select-PromotionCandidates ([Release[]] @()) ([Deployment[]] @()) $stagingEnvironment $prodEnvironment - - # Assert - $result.Count | Should -Be 0 - } + It "ignores already promoted Releases" { + # Arrange + $releases = New-Release "Release-1" "Project-1" "0.0.1" + $deployments = @( + New-Deployment "Deployment-1" "Release-1" $stagingEnvironmentId + New-Deployment "Deployment-2" "Release-1" $prodEnvironmentId + ) + + # Act + $result = @(Select-PromotionCandidates $releases $deployments $stagingEnvironmentId $prodEnvironmentId) + + # Assert + $result.Count | Should -Be 0 } - Context "with a Release and no Deployments" { - It "returns nothing" { - # Arrange - $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) - $deployments = [Deployment[]] @() - - # Act - $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - - # Assert - $result.Count | Should -Be 0 - } + It "includes unpromoted Releases" { + # Arrange + $releases = @( + New-Release "Release-1" "Project-1" "0.0.1" + New-Release "Release-2" "Project-1" "0.0.2" + ) + $deployments = @( + New-Deployment "Deployment-1" "Release-1" $stagingEnvironmentId + New-Deployment "Deployment-2" "Release-1" $prodEnvironmentId + New-Deployment "Deployment-3" "Release-2" $stagingEnvironmentId + ) + + # Act + $result = @(Select-PromotionCandidates $releases $deployments $stagingEnvironmentId $prodEnvironmentId) + + # Assert + $result.Count | Should -Be 1 + $result[0].ReleaseId | Should -Be "Release-2" } - Context "with no Releases and a Deployment" { - It "returns nothing" { - # Arrange - $releases = [Release[]] @() - $deployments = [Deployment[]] @( [Deployment]::new("Deployment-1", "Release-1", "Project-1") ) - - # Act - $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - - # Assert - $result.Count | Should -Be 0 - } + It "excludes superseded unpromoted Releases" { + # Arrange + $releases = @( + New-Release "Release-1" "Project-1" "0.0.1" [DateTime]::Now.AddDays(-10) + New-Release "Release-2" "Project-1" "0.0.2" [DateTime]::Now.AddDays(-7) + New-Release "Release-3" "Project-1" "0.0.3" [DateTime]::Now.AddDays(-3) + ) + $deployments = @( + New-Deployment "Deployment-1" "Release-1" $stagingEnvironmentId + + New-Deployment "Deployment-2" "Release-2" $stagingEnvironmentId + New-Deployment "Deployment-3" "Release-2" $prodEnvironmentId + New-Deployment "Deployment-4" "Release-3" $stagingEnvironmentId + ) + + # Act + $result = @(Select-PromotionCandidates $releases $deployments $stagingEnvironmentId $prodEnvironmentId) + + # Assert + $result.Count | Should -Be 1 + $result[0].ReleaseId | Should -Be "Release-3" } - Context "with valid Releases and Deployments" { - It "ignores already promoted Releases" { - # Arrange - $releases = [Release[]] @( [Release]::new("Release-1", "Project-1") ) - $deployments = [Deployment[]] @( - [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) - ) - - # Act - $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - - # Assert - $result.Count | Should -Be 0 - } - - It "includes unpromoted Releases" { - # Arrange - $releases = [Release[]] @( - [Release]::new("Release-1", "Project-1") - [Release]::new("Release-2", "Project-1") - ) - $deployments = [Deployment[]] @( - [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - [Deployment]::new("Deployment-2", "Release-1", $prodEnvironment) - - [Deployment]::new("Deployment-3", "Release-2", $stagingEnvironment) - ) - - # Act - $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - - # Assert - $result.Count | Should -Be 1 - $result[0].ReleaseId | Should -Be "Release-2" - } - - It "excludes superseded unpromoted Releases" { - # Arrange - $releases = [Release[]] @( - [Release]::new("Release-1", "Project-1", [DateTime]::Now.AddDays(-10)) - [Release]::new("Release-2", "Project-1", [DateTime]::Now.AddDays(-7)) - [Release]::new("Release-3", "Project-1", [DateTime]::Now.AddDays(-3)) - ) - $deployments = [Deployment[]] @( - [Deployment]::new("Deployment-1", "Release-1", $stagingEnvironment) - - [Deployment]::new("Deployment-2", "Release-2", $stagingEnvironment) - [Deployment]::new("Deployment-3", "Release-2", $prodEnvironment) - - [Deployment]::new("Deployment-4", "Release-3", $stagingEnvironment) - ) - - # Act - $result = Select-PromotionCandidates $releases $deployments $stagingEnvironment $prodEnvironment - - # Assert - $result.Count | Should -Be 1 - $result[0].ReleaseId | Should -Be "Release-3" - } +} + +Describe "Select-CommonCachedVersions" { + It "selects the intersection of cached versions lists" { + # Arrange + $cachedVersionLists = @( + , @("0.0.1-ubuntu.18.04", "0.0.2-ubuntu.18.04") + , @("0.0.2-ubuntu.18.04", "0.0.3-ubuntu.18.04") + , @("0.0.2-ubuntu.18.04", "0.0.4-ubuntu.18.04") + ) + + # Act + $result = @(Select-CommonCachedVersions $cachedVersionLists) + + # Assert + $result.Count | Should -Be 1 + $result[0] | Should -Be "0.0.2-ubuntu.18.04" } } -Describe "Select-ProductionDynamicWorkerRelease" { - Context "with no parameters" { - It "returns nothing" { - # Arrange - $releases = [Release[]] @() - $deployments = [Deployment[]] @() - - # Act - $result = Select-ProductionDynamicWorkerRelease $releases $deployments - - # Assert - $result.Count | Should -Be 0 - } +Describe "Select-CachedCandidates" { + It "selects candidates that are cached in production" { + # Arrange + $release2 = New-Release "Releases-2" "Projects-1" "0.0.2" + $release3 = New-Release "Releases-3" "Projects-1" "0.0.3" + $release4 = New-Release "Releases-4" "Projects-1" "0.0.4" + + $promotionCandidates = @($release2, $release3, $release4) + + $cachedWorkerToolsVersions = @( + "0.0.1-ubuntu.18.04" + "0.0.2-ubuntu.18.04" + "0.0.3-ubuntu.18.04" + ) + + # Act + $result = Select-CachedCandidates $promotionCandidates $cachedWorkerToolsVersions "ubuntu.18.04" + + # Assert + $result.Count | Should -Be 2 + $release2 | Should -BeIn $result + $release3 | Should -BeIn $result } } \ No newline at end of file