Skip to content

Commit 09ffb59

Browse files
[Rollout] Production rollout 2024-10-30 (#4113)
2 parents fc7f00a + c1e940d commit 09ffb59

24 files changed

+287
-240
lines changed

.vault-config/product-construction-prod.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,19 @@ secrets:
1919
hasPrivateKey: true
2020
hasWebhookSecret: false
2121
hasOAuthSecret: true
22+
23+
dotnet-bot-arcade-services-content-rw:
24+
type: github-access-token
25+
parameters:
26+
gitHubBotAccountSecret:
27+
location: engkeyvault
28+
name: BotAccount-dotnet-bot
29+
gitHubBotAccountName: dotnet-bot
30+
31+
dotnet-bot-maestro-auth-test-content-rw:
32+
type: github-access-token
33+
parameters:
34+
gitHubBotAccountSecret:
35+
location: engkeyvault
36+
name: BotAccount-dotnet-bot
37+
gitHubBotAccountName: dotnet-bot

azure-pipelines-product-construction-service.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pr:
1212

1313
variables:
1414
# https://dev.azure.com/dnceng/internal/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId=189
15+
# Required for MaestroAppClientId, MaestroStagingAppClientId
1516
- group: Publish-Build-Assets
1617
- name: resourceGroupName
1718
value: product-construction-service

docs/DevGuide.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ You can deploy your branch to the staging environment where E2E tests can be run
8282

8383
If you want to run the C# scenario tests (make sure that you followed the getting started steps before), you will need to set some environment variables:
8484

85-
1. GITHUB_TOKEN : Get a github PAT from https://github.com/settings/tokens
85+
1. GITHUB_TOKEN : See [instructions](#generating-github-pat-for-local-scenario-test-runs) below
8686
1. DARC_PACKAGE_SOURCE : Get the path to the darc nuget package (which would be in `arcade-services\artifacts\packages\Debug\NonShipping\`, see below for getting this built)
8787
1. MAESTRO_BASEURIS : Run ngrok and get the https url
8888

@@ -173,3 +173,14 @@ You can disable the DNS Service by deleting `DnsService` from the add-on feature
173173
]
174174
```
175175
If you change any settings in `ClusterManifestTemplate.json` run `Reset Local Cluster` from Service Fabric Local Cluster Manager to recreate the cluster configuration using the new settings
176+
177+
## Generating GitHub PAT for local scenario test runs
178+
179+
The GitHub scenario tests are ran against a dedicated organization - [`maestro-auth-tests`](https://github.com/maestro-auth-test). As such, a PAT with adequate permissions is required to run them locally.
180+
181+
To generate one, navigate to https://github.com/settings/tokens and select the `Fine-grained tokens` sub-menu on the navigation bar. The token should be generated with the following settings:
182+
- Resource owner: `maestro-auth-test` (if this option is not available in the resource settings please ask the team to add you to the test organization)
183+
- Repository access: `All repositories`
184+
- Repository permissions: `Contents` - `Access: Read and Write`
185+
186+
This configuration will allow the tests to read and write to the test repos without any additional access to the org or the account itself.

eng/templates/jobs/e2e-pcs-tests.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ jobs:
1717
# https://dev.azure.com/dnceng/internal/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId=20&path=Publish-Build-Assets
1818
# Required for MaestroAppClientId, MaestroStagingAppClientId
1919
- group: Publish-Build-Assets
20+
# https://dev.azure.com/dnceng/internal/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId=202&path=Arcade-Services-Scenario-Tests
21+
# Required for dotnet-bot-maestro-auth-test-content-rw
22+
- group: Arcade-Services-Scenario-Tests
2023
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/heads/production'), startsWith(variables['Build.SourceBranch'], 'refs/heads/production-'), eq(variables['Build.SourceBranch'], 'refs/heads/production'))) }}:
2124
- group: MaestroInt KeyVault
2225
- name: PcsTestEndpoint
@@ -106,7 +109,7 @@ jobs:
106109
env:
107110
PCS_BASEURI: ${{ variables.PcsTestEndpoint }}
108111
PCS_TOKEN: $(GetAuthInfo.Token)
109-
GITHUB_TOKEN: $(maestro-scenario-test-github-token)
112+
GITHUB_TOKEN: $(dotnet-bot-maestro-auth-test-content-rw)
110113
AZDO_TOKEN: $(AzdoToken)
111114
DARC_PACKAGE_SOURCE: $(Pipeline.Workspace)\PackageArtifacts
112115
DARC_DIR: $(Build.SourcesDirectory)\darc

eng/templates/jobs/e2e-tests.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,17 @@ jobs:
1919
# https://dev.azure.com/dnceng/internal/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId=20&path=Publish-Build-Assets
2020
# Required for MaestroAppClientId, MaestroStagingAppClientId
2121
- group: Publish-Build-Assets
22+
# https://dev.azure.com/dnceng/internal/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId=202&path=Arcade-Services-Scenario-Tests
23+
# Required for dotnet-bot-maestro-auth-test-content-rw
24+
- group: Arcade-Services-Scenario-Tests
2225
- ${{ if parameters.isProd }}:
23-
- group: MaestroProd KeyVault
2426
- name: MaestroTestEndpoints
2527
value: https://maestro-prod.westus2.cloudapp.azure.com,https://maestro.dot.net
2628
- name: ScenarioTestSubscription
2729
value: "Darc: Maestro Production"
2830
- name: MaestroAppId
2931
value: $(MaestroAppClientId)
3032
- ${{ else }}:
31-
- group: MaestroInt KeyVault
3233
- name: MaestroTestEndpoints
3334
value: https://maestro-int.westus2.cloudapp.azure.com,https://maestro.int-dot.net
3435
- name: ScenarioTestSubscription
@@ -119,7 +120,7 @@ jobs:
119120
env:
120121
MAESTRO_BASEURIS: ${{ variables.MaestroTestEndpoints }}
121122
MAESTRO_TOKEN: $(GetAuthInfo.Token)
122-
GITHUB_TOKEN: $(maestro-scenario-test-github-token)
123+
GITHUB_TOKEN: $(dotnet-bot-maestro-auth-test-content-rw)
123124
AZDO_TOKEN: $(AzdoToken)
124125
DARC_PACKAGE_SOURCE: $(Pipeline.Workspace)\PackageArtifacts
125126
DARC_DIR: $(Build.SourcesDirectory)\darc

eng/templates/stages/deploy.yaml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ parameters:
22
- name: isProd
33
type: boolean
44

5-
# --- Secret Variable group requirements ---
6-
# maestro-scenario-test-github-token
7-
85
stages:
96
- template: /eng/templates/stages/secret-validation.yml@self
107
parameters:
@@ -35,7 +32,9 @@ stages:
3532

3633
variables:
3734
- ${{ if parameters.isProd }}:
38-
- group: MaestroProd KeyVault
35+
# https://dev.azure.com/dnceng/internal/_library?itemType=VariableGroups&view=VariableGroupView&variableGroupId=202&path=Arcade-Services-Scenario-Tests
36+
# Required for dotnet-bot-arcade-services-content-write
37+
- group: Arcade-Services-Release
3938
- name: PublishProfile
4039
value: Prod
4140
- name: Subscription
@@ -45,7 +44,6 @@ stages:
4544
- name: BarMigrationSubscription
4645
value: BarMigrationProd
4746
- ${{ else }}:
48-
- group: MaestroInt KeyVault
4947
- name: PublishProfile
5048
value: Int
5149
- name: Subscription
@@ -131,7 +129,7 @@ stages:
131129
--repo dotnet/arcade-services
132130
displayName: Create GitHub release
133131
env:
134-
GH_TOKEN: $(BotAccount-dotnet-bot-repo-PAT)
132+
GH_TOKEN: $(dotnet-bot-arcade-services-content-write)
135133
continueOnError: true
136134
137135
- stage: validateDeployment

src/ProductConstructionService/ProductConstructionService.BarViz/Components/DependencyGrid.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
<TemplateColumn Title="Commit" Align="Align.Start" Width="6rem">
1818
<a href="@(context.CommitLink ?? "javascript:void(0)")" target="_blank">@context.CommitShort</a>
1919
</TemplateColumn>
20-
<PropertyColumn Title="Age" Property="@(r => r.AgeDays)" Sortable="true" Align="Align.End" Width="5rem" />
21-
<PropertyColumn Title="Staleness" Property="@(r => r.BuildStaleness)" Align="Align.Start" Width="8rem" />
20+
<PropertyColumn Title="Age (days)" Property="@(r => r.AgeDays)" Sortable="true" Align="Align.End" Width="5rem" />
21+
<PropertyColumn Title="Newer builds?" Property="@(r => r.BuildStaleness)" Align="Align.Start" Width="8rem" />
2222
<TemplateColumn Sortable="false" Align="Align.Start" Title="Build Number" Width="8rem">
2323
<a href="@context.BuildUrl" target="_blank">
2424
<FluentBadge Appearance="Appearance.Accent">@context.BuildNumber</FluentBadge>

src/ProductConstructionService/ProductConstructionService.BarViz/Layout/MainLayout.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<FluentLayout>
77
<FluentHeader Class="siteheader">
8-
Maestro++
8+
<FluentAnchor Href="/" Appearance="Appearance.Outline" Style="font-weight: bold">Maestro++</FluentAnchor>
99

1010
<FluentSpacer />
1111

src/ProductConstructionService/ProductConstructionService.BarViz/Pages/Build.razor

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
@using ProductConstructionService.BarViz.Components;
77
@inject IProductConstructionServiceApi PcsApi
88

9+
<PageTitle>@Channel</PageTitle>
10+
911
@if (_build == null)
1012
{
1113
<PageLoadingStatus StatusText="Loading build info ..." />
1214
}
1315
else
1416
{
1517
<FluentStack VerticalGap="20" Orientation="Orientation.Vertical">
18+
<FluentLabel Typo="Typography.H2">
19+
@Channel
20+
</FluentLabel>
1621

1722
<ErrorBoundary>
1823
<BuildInfo BuildId="@BuildId" Repository="@Repo" ChannelId="ChannelId" />
@@ -44,13 +49,16 @@ else
4449

4550
private string? Repo { get; set; }
4651

52+
private string? Channel { get; set; }
53+
4754
private ProductConstructionService.Client.Models.Build? _build;
4855

4956
protected override async Task OnParametersSetAsync()
5057
{
5158
_build = null;
5259

5360
Repo = RepoUrlConverter.SlugToRepoUrl(RepoSlug);
61+
Channel = (await PcsApi.Channels.GetChannelAsync(ChannelId)).Name;
5462

5563
if (BuildId == "latest")
5664
{
@@ -61,5 +69,4 @@ else
6169
_build = await PcsApi.Builds.GetBuildAsync(int.Parse(BuildId!));
6270
}
6371
}
64-
6572
}

src/ProductConstructionService/ProductConstructionService.Cli/Operations/DeploymentOperation.cs

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using Azure;
5-
using Azure.Core;
6-
using Azure.ResourceManager;
75
using Azure.ResourceManager.AppContainers;
86
using Azure.ResourceManager.AppContainers.Models;
97
using Azure.ResourceManager.Resources;
@@ -95,10 +93,17 @@ public async Task<int> RunAsync()
9593
// Wait for the new app revision to become active
9694
var newRevisionActive = await WaitForRevisionToBecomeActive(newRevisionName);
9795

98-
// If the new revision is active, the rollout succeeded, assign a label, and transfer all traffic to it
96+
// If the new revision is active, the rollout succeeded, assign a label, transfer all traffic to it,
97+
// and deactivate the previously running revision
9998
if (newRevisionActive)
10099
{
101100
await AssignLabelAndTransferTraffic(newRevisionName, inactiveRevisionLabel);
101+
102+
if (!string.IsNullOrEmpty(activeRevisionTrafficWeight.RevisionName))
103+
{
104+
await RemoveRevisionLabel(activeRevisionTrafficWeight.RevisionName, activeRevisionTrafficWeight.Label);
105+
await DeactivateRevision(activeRevisionTrafficWeight.RevisionName);
106+
}
102107
}
103108
// If the new revision is not active, deactivate it and get print log link
104109
else
@@ -121,30 +126,33 @@ public async Task<int> RunAsync()
121126
}
122127
}
123128

129+
private async Task RemoveRevisionLabel(string revisionName, string label)
130+
{
131+
var result = await InvokeAzCLI(
132+
["containerapp", "revision", "label", "remove"],
133+
["--label", label]);
134+
result.ThrowIfFailed($"Failed to remove label {label} from revision {revisionName}.");
135+
}
136+
124137
private async Task CleanupRevisionsAsync(IEnumerable<ContainerAppRevisionTrafficWeight> revisionsTrafficWeight)
125138
{
126-
// Cleanup all revision labels
127-
foreach (var revisionTrafficWeight in revisionsTrafficWeight)
139+
IEnumerable<ContainerAppRevisionResource> activeRevisions = _containerApp.GetContainerAppRevisions()
140+
.ToEnumerable()
141+
.Where(revision => revision.Data.IsActive ?? false)
142+
.Where(revision => revision.Data.TrafficWeight != 100);
143+
144+
var revisionsToDeactivate = activeRevisions
145+
.Select(revision => (
146+
revision.Data.Name,
147+
revisionsTrafficWeight.FirstOrDefault(trafficWeight => trafficWeight.RevisionName == revision.Data.Name)?.Label));
148+
149+
foreach (var revision in revisionsToDeactivate)
128150
{
129-
if (!string.IsNullOrEmpty(revisionTrafficWeight.Label))
151+
if (!string.IsNullOrEmpty(revision.Label))
130152
{
131-
var result = await InvokeAzCLI([
132-
"containerapp", "revision", "label", "remove",
133-
],
134-
[
135-
"--label", revisionTrafficWeight.Label
136-
]);
137-
result.ThrowIfFailed($"Failed to remove label {revisionTrafficWeight.Label} from revision {revisionTrafficWeight.RevisionName}. Stderr: {result.StandardError}");
153+
await RemoveRevisionLabel(revision.Name, revision.Label);
138154
}
139-
}
140-
141-
// Now deactivate all revisions in the list
142-
foreach (var revisionTrafficWeight in revisionsTrafficWeight)
143-
{
144-
_containerApp = await _containerApp.GetAsync();
145-
ContainerAppRevisionResource revision = (await _containerApp.GetContainerAppRevisionAsync(revisionTrafficWeight.RevisionName)).Value;
146-
147-
await revision.DeactivateRevisionAsync();
155+
await DeactivateRevision(revision.Name);
148156
}
149157
}
150158

@@ -219,11 +227,16 @@ private async Task AssignLabelAndTransferTraffic(string revisionName, string lab
219227
label);
220228
}
221229

222-
private async Task DeactivateFailedRevisionAndGetLogs(string revisionName)
230+
private async Task DeactivateRevision(string revisionName)
223231
{
224232
var revision = (await _containerApp.GetContainerAppRevisionAsync(revisionName)).Value;
225233
await revision.DeactivateRevisionAsync();
226234
_logger.LogInformation("Deactivated revision {revisionName}", revisionName);
235+
}
236+
237+
private async Task DeactivateFailedRevisionAndGetLogs(string revisionName)
238+
{
239+
await DeactivateRevision(revisionName);
227240

228241
_logger.LogInformation("Check revision logs too see failure reason: {logsUri}", GetLogsUri(revisionName));
229242
}

0 commit comments

Comments
 (0)