Skip to content

Commit e95fc6f

Browse files
committed
[installer] Add upgrade test werft flag
1 parent c657f8d commit e95fc6f

File tree

13 files changed

+407
-149
lines changed

13 files changed

+407
-149
lines changed

.werft/build.ts

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,64 @@
1-
import * as fs from 'fs';
2-
import { SpanStatusCode } from '@opentelemetry/api';
3-
import { Werft } from './util/werft';
4-
import { reportBuildFailureInSlack } from './util/slack';
5-
import * as Tracing from './observability/tracing'
6-
import * as VM from './vm/vm'
7-
import { buildAndPublish } from './jobs/build/build-and-publish';
8-
import { validateChanges } from './jobs/build/validate-changes';
9-
import { prepare } from './jobs/build/prepare';
10-
import { deployToPreviewEnvironment } from './jobs/build/deploy-to-preview-environment';
11-
import { triggerIntegrationTests } from './jobs/build/trigger-integration-tests';
12-
import { jobConfig } from './jobs/build/job-config';
13-
import { typecheckWerftJobs } from './jobs/build/typecheck-werft-jobs';
1+
import * as fs from "fs";
2+
import { SpanStatusCode } from "@opentelemetry/api";
3+
import { Werft } from "./util/werft";
4+
import { reportBuildFailureInSlack } from "./util/slack";
5+
import * as Tracing from "./observability/tracing";
6+
import * as VM from "./vm/vm";
7+
import { buildAndPublish } from "./jobs/build/build-and-publish";
8+
import { validateChanges } from "./jobs/build/validate-changes";
9+
import { prepare } from "./jobs/build/prepare";
10+
import { deployToPreviewEnvironment } from "./jobs/build/deploy-to-preview-environment";
11+
import { triggerIntegrationTests } from "./jobs/build/trigger-integration-tests";
12+
import { triggerUpgradeTests } from "./jobs/build/self-hosted-upgrade-tests";
13+
import { jobConfig } from "./jobs/build/job-config";
14+
import { typecheckWerftJobs } from "./jobs/build/typecheck-werft-jobs";
1415

1516
// Will be set once tracing has been initialized
16-
let werft: Werft
17-
const context: any = JSON.parse(fs.readFileSync('context.json').toString());
17+
let werft: Werft;
18+
const context: any = JSON.parse(fs.readFileSync("context.json").toString());
1819

1920
Tracing.initialize()
2021
.then(() => {
21-
werft = new Werft("build")
22+
werft = new Werft("build");
2223
})
2324
.then(() => run(context))
2425
.catch((err) => {
2526
werft.rootSpan.setStatus({
2627
code: SpanStatusCode.ERROR,
27-
message: err
28-
})
28+
message: err,
29+
});
2930

30-
console.log('Error', err)
31+
console.log("Error", err);
3132

3233
if (context.Repository.ref === "refs/heads/main") {
3334
reportBuildFailureInSlack(context, err).catch((error: Error) => {
34-
console.error("Failed to send message to Slack", error)
35+
console.error("Failed to send message to Slack", error);
3536
});
3637
}
3738

3839
// Explicitly not using process.exit as we need to flush tracing, see tracing.js
39-
process.exitCode = 1
40+
process.exitCode = 1;
4041
})
4142
.finally(() => {
42-
werft.phase("Stop kubectl port forwards", "Stopping kubectl port forwards")
43-
VM.stopKubectlPortForwards()
43+
werft.phase("Stop kubectl port forwards", "Stopping kubectl port forwards");
44+
VM.stopKubectlPortForwards();
4445

45-
werft.phase("Flushing telemetry", "Flushing telemetry before stopping job")
46-
werft.endAllSpans()
47-
})
46+
werft.phase("Flushing telemetry", "Flushing telemetry before stopping job");
47+
werft.endAllSpans();
48+
});
4849

4950
async function run(context: any) {
50-
const config = jobConfig(werft, context)
51+
const config = jobConfig(werft, context);
5152

52-
await validateChanges(werft, config)
53-
await prepare(werft, config)
54-
await typecheckWerftJobs(werft)
55-
await buildAndPublish(werft, config)
53+
await validateChanges(werft, config);
54+
await prepare(werft, config);
55+
if (config.withUpgradeTests) {
56+
// this will trigger an upgrade test on a self-hosted gitpod instance on a new cluster.
57+
await triggerUpgradeTests(werft, config, context.Owner);
58+
return;
59+
}
60+
await typecheckWerftJobs(werft);
61+
await buildAndPublish(werft, config);
5662

5763
if (!config.withPreview || config.publishRelease) {
5864
werft.phase("deploy", "not deploying");
@@ -61,15 +67,15 @@ async function run(context: any) {
6167
}
6268

6369
try {
64-
await deployToPreviewEnvironment(werft, config)
70+
await deployToPreviewEnvironment(werft, config);
6571
} catch (e) {
6672
// We currently don't support concurrent deployments to the same preview environment.
6773
// Until we do we don't want errors to mark the main build as failed.
6874
if (config.mainBuild) {
69-
return
75+
return;
7076
}
71-
throw e
77+
throw e;
7278
}
7379

74-
await triggerIntegrationTests(werft, config, context.Owner)
80+
await triggerIntegrationTests(werft, config, context.Owner);
7581
}

.werft/installer-tests.ts

Lines changed: 104 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { Werft } from "./util/werft";
44

55
const testConfig: string = process.argv.length > 2 ? process.argv[2] : "STANDARD_K3S_TEST";
66
// we can provide the version of the gitpod to install (eg: 2022.4.2)
7-
const version: string = process.argv.length > 3 ? process.argv[3] : "";
7+
// "-" is the default value which will install the latest version
8+
const version: string = process.argv.length > 3 ? process.argv[3] : "-";
9+
10+
const channel: string = process.argv.length > 4 ? process.argv[4] : "unstable";
811

912
const makefilePath: string = join("install/tests");
1013

@@ -16,6 +19,78 @@ interface InfraConfig {
1619
description: string;
1720
}
1821

22+
interface TestConfig {
23+
DESCRIPTION: string;
24+
PHASES: string[];
25+
CLOUD: string;
26+
}
27+
28+
// Each of the TEST_CONFIGURATIONS define an integration test end-to-end
29+
// It should be a combination of multiple INFRA_PHASES, order of PHASES slice is important
30+
const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
31+
STANDARD_GKE_TEST: {
32+
CLOUD: "gcp",
33+
DESCRIPTION: "Deploy Gitpod on GKE, with managed DNS, and run integration tests",
34+
PHASES: [
35+
"STANDARD_GKE_CLUSTER",
36+
"CERT_MANAGER",
37+
"GCP_MANAGED_DNS",
38+
"GENERATE_KOTS_CONFIG",
39+
"INSTALL_GITPOD",
40+
"CHECK_INSTALLATION",
41+
"RUN_INTEGRATION_TESTS",
42+
"RESULTS",
43+
"DESTROY",
44+
],
45+
},
46+
STANDARD_GKE_UPGRADE_TEST: {
47+
CLOUD: "gcp",
48+
DESCRIPTION: `Deploy Gitpod on GKE, and test upgrade from ${version} to latest version`,
49+
PHASES: [
50+
"STANDARD_GKE_CLUSTER",
51+
"CERT_MANAGER",
52+
"GCP_MANAGED_DNS",
53+
"GENERATE_KOTS_CONFIG",
54+
"INSTALL_GITPOD",
55+
"CHECK_INSTALLATION",
56+
"KOTS_UPGRADE",
57+
"CHECK_INSTALLATION",
58+
"DESTROY",
59+
],
60+
},
61+
STANDARD_K3S_TEST: {
62+
CLOUD: "gcp", // the cloud provider is still GCP
63+
DESCRIPTION:
64+
"Deploy Gitpod on a K3s cluster, created on a GCP instance," +
65+
" with managed DNS and run integrations tests",
66+
PHASES: [
67+
"STANDARD_K3S_CLUSTER_ON_GCP",
68+
"CERT_MANAGER",
69+
"GENERATE_KOTS_CONFIG",
70+
"INSTALL_GITPOD",
71+
"CHECK_INSTALLATION",
72+
"RUN_INTEGRATION_TESTS",
73+
"RESULTS",
74+
"DESTROY",
75+
],
76+
},
77+
STANDARD_K3S_PREVIEW: {
78+
CLOUD: "gcp",
79+
DESCRIPTION: "Create a SH Gitpod preview environment on a K3s cluster, created on a GCP instance",
80+
PHASES: [
81+
"STANDARD_K3S_CLUSTER_ON_GCP",
82+
"CERT_MANAGER",
83+
"GENERATE_KOTS_CONFIG",
84+
"INSTALL_GITPOD",
85+
"CHECK_INSTALLATION",
86+
"RESULTS",
87+
],
88+
},
89+
};
90+
91+
const config: TestConfig = TEST_CONFIGURATIONS[testConfig];
92+
const cloud: string = config.CLOUD;
93+
1994
// `INFRA_PHASES` describe the phases that can be mixed
2095
// and matched to form a test configuration
2196
// Each phase should contain a `makeTarget` which
@@ -41,14 +116,22 @@ const INFRA_PHASES: { [name: string]: InfraConfig } = {
41116
makeTarget: "managed-dns",
42117
description: "Sets up external-dns & cloudDNS config",
43118
},
119+
GENERATE_KOTS_CONFIG: {
120+
phase: "generate-kots-config",
121+
makeTarget: `generate-kots-config storage=${randomize("storage", cloud)} registry=${randomize(
122+
"registry",
123+
cloud,
124+
)} db=${randomize("db", cloud)}`,
125+
description: `Generate KOTS Config file`,
126+
},
44127
INSTALL_GITPOD_IGNORE_PREFLIGHTS: {
45128
phase: "install-gitpod-without-preflights",
46-
makeTarget: `kots-install channel=unstable version=${version} preflights=false`, // this is a bit of a hack, for now we pass params like this
129+
makeTarget: `kots-install channel=${channel} version=${version} preflights=false`, // this is a bit of a hack, for now we pass params like this
47130
description: "Install gitpod using kots community edition without preflights",
48131
},
49132
INSTALL_GITPOD: {
50133
phase: "install-gitpod",
51-
makeTarget: `kots-install channel=unstable version=${version} preflights=true`,
134+
makeTarget: `kots-install channel=${channel} version=${version} preflights=true`,
52135
description: "Install gitpod using kots community edition",
53136
},
54137
CHECK_INSTALLATION: {
@@ -57,6 +140,11 @@ const INFRA_PHASES: { [name: string]: InfraConfig } = {
57140
makeTarget: "check-gitpod-installation",
58141
description: "Check gitpod installation",
59142
},
143+
KOTS_UPGRADE: {
144+
phase: "kots-upgrade",
145+
makeTarget: "kots-upgrade",
146+
description: "Upgrade Gitpod installation to latest version using KOTS CLI",
147+
},
60148
RUN_INTEGRATION_TESTS: {
61149
phase: "run-integration-tests",
62150
makeTarget: "run-tests",
@@ -74,56 +162,6 @@ const INFRA_PHASES: { [name: string]: InfraConfig } = {
74162
},
75163
};
76164

77-
interface TestConfig {
78-
DESCRIPTION: string;
79-
PHASES: string[];
80-
}
81-
82-
// Each of the TEST_CONFIGURATIONS define an integration test end-to-end
83-
// It should be a combination of multiple INFRA_PHASES, order of PHASES slice is important
84-
const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
85-
STANDARD_GKE_TEST: {
86-
DESCRIPTION: "Deploy Gitpod on GKE, with managed DNS, and run integration tests",
87-
PHASES: [
88-
"STANDARD_GKE_CLUSTER",
89-
"CERT_MANAGER",
90-
"GCP_MANAGED_DNS",
91-
"INSTALL_GITPOD",
92-
"CHECK_INSTALLATION",
93-
"RUN_INTEGRATION_TESTS",
94-
"RESULTS",
95-
"DESTROY",
96-
],
97-
},
98-
STANDARD_K3S_TEST: {
99-
DESCRIPTION:
100-
"Deploy Gitpod on a K3s cluster, created on a GCP instance," +
101-
" with managed DNS and run integrations tests",
102-
PHASES: [
103-
"STANDARD_K3S_CLUSTER_ON_GCP",
104-
"CERT_MANAGER",
105-
"INSTALL_GITPOD_IGNORE_PREFLIGHTS",
106-
"CHECK_INSTALLATION",
107-
"RUN_INTEGRATION_TESTS",
108-
"RESULTS",
109-
"DESTROY",
110-
],
111-
},
112-
STANDARD_K3S_PREVIEW: {
113-
DESCRIPTION: "Create a SH Gitpod preview environment on a K3s cluster, created on a GCP instance",
114-
PHASES: [
115-
"STANDARD_K3S_CLUSTER_ON_GCP",
116-
"GCP_MANAGED_DNS",
117-
"INSTALL_GITPOD_IGNORE_PREFLIGHTS",
118-
"CHECK_INSTALLATION",
119-
"RESULTS",
120-
],
121-
},
122-
};
123-
124-
// TODO better way to clean up
125-
const config: TestConfig = TEST_CONFIGURATIONS[testConfig];
126-
127165
if (config === undefined) {
128166
console.log(`Unknown configuration specified: "${testConfig}", Exiting...`);
129167
process.exit(1);
@@ -135,12 +173,6 @@ installerTests(TEST_CONFIGURATIONS[testConfig]).catch((err) => {
135173
process.exit(1);
136174
});
137175

138-
function getKubeconfig() {
139-
const ret = exec(`make -C ${makefilePath} get-kubeconfig`);
140-
const filename = ret.stdout.toString().split("\n").slice(1, -1);
141-
exec(`echo ${filename}`);
142-
}
143-
144176
export async function installerTests(config: TestConfig) {
145177
console.log(config.DESCRIPTION);
146178
for (let phase of config.PHASES) {
@@ -155,9 +187,13 @@ export async function installerTests(config: TestConfig) {
155187
}
156188

157189
function callMakeTargets(phase: string, description: string, makeTarget: string) {
158-
werft.phase(phase, description);
190+
werft.phase(phase, `${description}`);
191+
werft.log(phase, `calling ${makeTarget}`);
159192

160-
const response = exec(`make -C ${makefilePath} ${makeTarget}`, { slice: "call-make-target", dontCheckRc: true });
193+
const response = exec(`make -C ${makefilePath} ${makeTarget}`, {
194+
slice: "call-make-target",
195+
dontCheckRc: true,
196+
});
161197

162198
if (response.code) {
163199
console.error(`Error: ${response.stderr}`);
@@ -170,6 +206,13 @@ function callMakeTargets(phase: string, description: string, makeTarget: string)
170206
return response.code;
171207
}
172208

209+
function randomize(resource: string, platform: string): string {
210+
// in the follow-up PR we will add `${platform}-${resource}` as an option here to
211+
// test against resource dependencies(storage, db, registry) for each cloud platform
212+
const options = ["incluster"];
213+
return options[Math.floor(Math.random() * options.length)];
214+
}
215+
173216
function cleanup() {
174217
const phase = "destroy-infrastructure";
175218
werft.phase(phase, "Destroying all the created resources");

0 commit comments

Comments
 (0)