Skip to content

Commit bd31275

Browse files
committed
Add self-hosted upgrade tests
1 parent 227f94f commit bd31275

File tree

7 files changed

+180
-44
lines changed

7 files changed

+180
-44
lines changed

.werft/build.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { validateChanges } from './jobs/build/validate-changes';
99
import { prepare } from './jobs/build/prepare';
1010
import { deployToPreviewEnvironment } from './jobs/build/deploy-to-preview-environment';
1111
import { triggerIntegrationTests } from './jobs/build/trigger-integration-tests';
12+
import { triggerUpgradeTests } from './jobs/build/self-hosted-upgrade-tests';
1213
import { jobConfig } from './jobs/build/job-config';
1314
import { typecheckWerftJobs } from './jobs/build/typecheck-werft-jobs';
1415

@@ -72,4 +73,7 @@ async function run(context: any) {
7273
}
7374

7475
await triggerIntegrationTests(werft, config, context.Owner)
76+
77+
// this will trigger an upgrade test on a self-hosted gitpod instance on a new cluster.
78+
await triggerUpgradeTests(werft, config, context.Owner)
7579
}

.werft/installer-tests.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { Werft } from "./util/werft";
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)
77
const version: string = process.argv.length > 3 ? process.argv[3] : "";
8+
// we are assuming that if a version is provided, this is to test upgrade to the latest
9+
const channel: string = version != "" ? "beta" : "unstable";
810

911
const makefilePath: string = join("install/tests");
1012

@@ -43,12 +45,12 @@ const INFRA_PHASES: { [name: string]: InfraConfig } = {
4345
},
4446
INSTALL_GITPOD_IGNORE_PREFLIGHTS: {
4547
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
48+
makeTarget: `kots-install channel=${channel} version=${version} preflights=false`, // this is a bit of a hack, for now we pass params like this
4749
description: "Install gitpod using kots community edition without preflights",
4850
},
4951
INSTALL_GITPOD: {
5052
phase: "install-gitpod",
51-
makeTarget: `kots-install channel=unstable version=${version} preflights=true`,
53+
makeTarget: `kots-install channel=${channel} version=${version} preflights=true`,
5254
description: "Install gitpod using kots community edition",
5355
},
5456
CHECK_INSTALLATION: {
@@ -57,6 +59,11 @@ const INFRA_PHASES: { [name: string]: InfraConfig } = {
5759
makeTarget: "check-gitpod-installation",
5860
description: "Check gitpod installation",
5961
},
62+
KOTS_UPGRADE: {
63+
phase: "kots-upgrade",
64+
makeTarget: "kots-uprgade",
65+
description: "Upgrade Gitpod installation to latest version using KOTS CLI",
66+
},
6067
RUN_INTEGRATION_TESTS: {
6168
phase: "run-integration-tests",
6269
makeTarget: "run-tests",
@@ -95,6 +102,19 @@ const TEST_CONFIGURATIONS: { [name: string]: TestConfig } = {
95102
"DESTROY",
96103
],
97104
},
105+
STANDARD_GKE_UPGRADE_TEST: {
106+
DESCRIPTION: `Deploy Gitpod on GKE, and test upgrade from ${version} to latest version`,
107+
PHASES: [
108+
"STANDARD_GKE_CLUSTER",
109+
"CERT_MANAGER",
110+
"GCP_MANAGED_DNS",
111+
"INSTALL_GITPOD",
112+
"CHECK_INSTALLATION",
113+
"KOTS_UPGRADE",
114+
"CHECK_INSTALLATION",
115+
"DESTROY",
116+
],
117+
},
98118
STANDARD_K3S_TEST: {
99119
DESCRIPTION:
100120
"Deploy Gitpod on a K3s cluster, created on a GCP instance," +

.werft/jobs/build/job-config.ts

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,34 @@ import { Werft } from "../../util/werft";
33
import { previewNameFromBranchName } from "../../util/preview";
44

55
export interface JobConfig {
6-
analytics: string
6+
analytics: string;
77
buildConfig: any;
8-
cleanSlateDeployment: boolean
9-
coverageOutput: string
8+
cleanSlateDeployment: boolean;
9+
coverageOutput: string;
1010
dontTest: boolean;
1111
dynamicCPULimits: boolean;
12-
installEELicense: boolean
13-
localAppVersion: string
12+
installEELicense: boolean;
13+
localAppVersion: string;
1414
mainBuild: boolean;
1515
noPreview: boolean;
1616
publishRelease: boolean;
17-
publishToJBMarketplace: string
18-
publishToNpm: string
17+
publishToJBMarketplace: string;
18+
publishToNpm: string;
1919
publishToKots: boolean;
20-
retag: string
20+
retag: string;
2121
storage: string;
2222
version: string;
23-
withContrib: boolean
23+
withContrib: boolean;
2424
withIntegrationTests: boolean;
25-
withObservability: boolean
26-
withPayment: boolean
27-
withVM: boolean
25+
withUpgradeTests: boolean;
26+
fromVersion: string;
27+
withObservability: boolean;
28+
withPayment: boolean;
29+
withVM: boolean;
2830
workspaceFeatureFlags: string[];
29-
previewEnvironment: PreviewEnvironmentConfig,
30-
repository: Repository
31-
observability: Observability
31+
previewEnvironment: PreviewEnvironmentConfig;
32+
repository: Repository;
33+
observability: Observability;
3234
}
3335

3436
export interface PreviewEnvironmentConfig {
@@ -45,14 +47,14 @@ export interface Repository {
4547

4648
export interface Observability {
4749
// The branch of gitpod-io/observability to use
48-
branch: string
50+
branch: string;
4951
}
5052

5153
export function jobConfig(werft: Werft, context: any): JobConfig {
52-
const sliceId = 'Parsing job configuration'
53-
werft.phase('Job configuration')
54-
werft.log(sliceId , "Parsing the job configuration")
55-
const version = parseVersion(context)
54+
const sliceId = "Parsing job configuration";
55+
werft.phase("Job configuration");
56+
werft.log(sliceId, "Parsing the job configuration");
57+
const version = parseVersion(context);
5658
const repo = `${context.Repository.host}/${context.Repository.owner}/${context.Repository.repo}`;
5759
const mainBuild = repo === "github.com/gitpod-io/gitpod" && context.Repository.ref.includes("refs/heads/main");
5860

@@ -64,7 +66,7 @@ export function jobConfig(werft: Werft, context: any): JobConfig {
6466
if (!raw) {
6567
return [];
6668
}
67-
return raw.split(",").map(e => e.trim());
69+
return raw.split(",").map((e) => e.trim());
6870
})();
6971

7072
const coverageOutput = exec("mktemp -d", { silent: true }).stdout.trim();
@@ -76,13 +78,15 @@ export function jobConfig(werft: Werft, context: any): JobConfig {
7678
const noPreview = ("no-preview" in buildConfig && buildConfig["no-preview"] !== "false") || publishRelease;
7779
const storage = buildConfig["storage"] || "";
7880
const withIntegrationTests = "with-integration-tests" in buildConfig && !mainBuild;
81+
const withUpgradeTests = "with-upgrade-tests" in buildConfig && !mainBuild;
82+
const fromVersion = withUpgradeTests ? buildConfig["from-version"] : "";
7983
const publishToNpm = "publish-to-npm" in buildConfig || mainBuild;
8084
const publishToJBMarketplace = "publish-to-jb-marketplace" in buildConfig || mainBuild;
8185
const publishToKots = "publish-to-kots" in buildConfig;
8286
const analytics = buildConfig["analytics"];
83-
const localAppVersion = mainBuild || ("with-localapp-version" in buildConfig) ? version : "unknown";
84-
const retag = ("with-retag" in buildConfig) ? "" : "--dont-retag";
85-
const cleanSlateDeployment = mainBuild || ("with-clean-slate-deployment" in buildConfig);
87+
const localAppVersion = mainBuild || "with-localapp-version" in buildConfig ? version : "unknown";
88+
const retag = "with-retag" in buildConfig ? "" : "--dont-retag";
89+
const cleanSlateDeployment = mainBuild || "with-clean-slate-deployment" in buildConfig;
8690
const installEELicense = !("without-ee-license" in buildConfig) || mainBuild;
8791
const withPayment = "with-payment" in buildConfig && !mainBuild;
8892
const withObservability = "with-observability" in buildConfig && !mainBuild;
@@ -91,24 +95,24 @@ export function jobConfig(werft: Werft, context: any): JobConfig {
9195
repo: context.Repository.repo,
9296
ref: context.Repository.ref,
9397
branch: context.Repository.ref,
94-
}
98+
};
9599
const refsPrefix = "refs/heads/";
96100
if (repository.branch.startsWith(refsPrefix)) {
97101
repository.branch = repository.branch.substring(refsPrefix.length);
98102
}
99103
const withoutVM = "without-vm" in buildConfig;
100104
const withVM = !withoutVM || mainBuild;
101105

102-
const previewName = previewNameFromBranchName(repository.branch)
106+
const previewName = previewNameFromBranchName(repository.branch);
103107
const previewEnvironmentNamespace = withVM ? `default` : `staging-${previewName}`;
104108
const previewEnvironment = {
105109
destname: previewName,
106-
namespace: previewEnvironmentNamespace
107-
}
110+
namespace: previewEnvironmentNamespace,
111+
};
108112

109113
const observability: Observability = {
110-
branch: context.Annotations.withObservabilityBranch || "main"
111-
}
114+
branch: context.Annotations.withObservabilityBranch || "main",
115+
};
112116

113117
const jobConfig = {
114118
analytics,
@@ -137,19 +141,21 @@ export function jobConfig(werft: Werft, context: any): JobConfig {
137141
withPayment,
138142
withVM,
139143
workspaceFeatureFlags,
140-
}
144+
};
141145

142146
werft.log("job config", JSON.stringify(jobConfig));
143-
const globalAttributes = Object.fromEntries(Object.entries(jobConfig).map((kv) => {
144-
const [key, value] = kv
145-
return [`werft.job.config.${key}`, value]
146-
}))
147-
globalAttributes['werft.job.config.branch'] = context.Repository.ref
148-
werft.addAttributes(globalAttributes)
147+
const globalAttributes = Object.fromEntries(
148+
Object.entries(jobConfig).map((kv) => {
149+
const [key, value] = kv;
150+
return [`werft.job.config.${key}`, value];
151+
}),
152+
);
153+
globalAttributes["werft.job.config.branch"] = context.Repository.ref;
154+
werft.addAttributes(globalAttributes);
149155

150-
werft.done(sliceId)
156+
werft.done(sliceId);
151157

152-
return jobConfig
158+
return jobConfig;
153159
}
154160

155161
function parseVersion(context: any) {
@@ -163,5 +169,5 @@ function parseVersion(context: any) {
163169
if (version.substr(0, PREFIX_TO_STRIP.length) === PREFIX_TO_STRIP) {
164170
version = version.substr(PREFIX_TO_STRIP.length);
165171
}
166-
return version
172+
return version;
167173
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { exec } from "../../util/shell";
2+
import { Werft } from "../../util/werft";
3+
import { JobConfig } from "./job-config";
4+
5+
const phases = {
6+
TRIGGER_UPGRADE_TESTS: "trigger self-hosted upgrade tests",
7+
};
8+
9+
/**
10+
* Trigger self hosted upgrade tests
11+
*/
12+
export async function triggerUpgradeTests(werft: Werft, config: JobConfig, username: string) {
13+
werft.phase(phases.TRIGGER_UPGRADE_TESTS, "Trigger upgrade tests on self-hosted gitpod");
14+
15+
if (!config.withUpgradeTests || !config.fromVersion) {
16+
werft.log(phases.TRIGGER_UPGRADE_TESTS, "Skipped upgrade tests");
17+
werft.done(phases.TRIGGER_UPGRADE_TESTS);
18+
return;
19+
}
20+
21+
try {
22+
const annotation = `-a fromVersion=${config.fromVersion}`;
23+
exec(`werft run --remote-job-path .werft/run-sh-upgrade-tests-gke.yaml ${annotation} github`, {
24+
slice: phases.TRIGGER_UPGRADE_TESTS,
25+
}).trim();
26+
27+
werft.done(phases.TRIGGER_UPGRADE_TESTS);
28+
} catch (err) {
29+
if (!config.mainBuild) {
30+
werft.fail(phases.TRIGGER_UPGRADE_TESTS, err);
31+
}
32+
exec("exit 0");
33+
}
34+
}

.werft/run-sh-upgrade-tests-gke.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# debug using `werft run github -f -s .werft/installer-tests.ts -j .werft/run-sh-upgrade-tests-gke.yaml -a debug=true`
2+
pod:
3+
serviceAccount: werft
4+
affinity:
5+
nodeAffinity:
6+
requiredDuringSchedulingIgnoredDuringExecution:
7+
nodeSelectorTerms:
8+
- matchExpressions:
9+
- key: dev/workload
10+
operator: In
11+
values:
12+
- "builds"
13+
securityContext:
14+
runAsUser: 0
15+
volumes:
16+
- name: sh-playground-sa-perm
17+
secret:
18+
secretName: sh-playground-sa-perm
19+
- name: sh-playground-dns-perm
20+
secret:
21+
secretName: sh-playground-dns-perm
22+
containers:
23+
- name: nightly-test
24+
image: eu.gcr.io/gitpod-core-dev/dev/dev-environment:cw-werft-cred.0
25+
workingDir: /workspace
26+
imagePullPolicy: Always
27+
volumeMounts:
28+
- name: sh-playground-sa-perm
29+
mountPath: /mnt/secrets/sh-playground-sa-perm
30+
- name: sh-playground-dns-perm # this sa is used for the DNS management
31+
mountPath: /mnt/secrets/sh-playground-dns-perm
32+
env:
33+
- name: WERFT_HOST
34+
value: "werft.werft.svc.cluster.local:7777"
35+
- name: GOOGLE_APPLICATION_CREDENTIALS
36+
value: "/mnt/secrets/sh-playground-sa-perm/sh-sa.json"
37+
- name: TF_VAR_sa_creds
38+
value: "/mnt/secrets/sh-playground-sa-perm/sh-sa.json"
39+
- name: TF_VAR_dns_sa_creds
40+
value: "/mnt/secrets/sh-playground-dns-perm/sh-dns-sa.json"
41+
- name: WERFT_K8S_NAMESPACE
42+
value: "werft"
43+
- name: WERFT_K8S_LABEL
44+
value: "component=werft"
45+
- name: NODENAME
46+
valueFrom:
47+
fieldRef:
48+
fieldPath: spec.nodeName
49+
command:
50+
- bash
51+
- -c
52+
- |
53+
sleep 1
54+
set -Eeuo pipefail
55+
56+
sudo chown -R gitpod:gitpod /workspace
57+
sudo apt update && apt install gettext-base
58+
59+
export TF_VAR_TEST_ID=$(echo $RANDOM | md5sum | head -c 5; echo)
60+
61+
(cd .werft && yarn install && mv node_modules ..) | werft log slice prep
62+
printf '{{ toJson . }}' > context.json
63+
64+
FROM_VERSION="{{ .Annotations.fromVersion }}"
65+
npx ts-node .werft/installer-tests.ts "STANDARD_GKE_UPGRADE_TEST" ${FROM_VERSION}
66+
# The bit below makes this a cron job
67+
# plugins:
68+
# cron: "15 4 * * *"

install/infra/terraform/gke/main.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ resource "google_container_cluster" "gitpod-cluster" {
4848
resource_limits {
4949
resource_type = "cpu"
5050
minimum = 2
51-
maximum = 8
51+
maximum = 16
5252
}
5353

5454
resource_limits {
@@ -58,7 +58,7 @@ resource "google_container_cluster" "gitpod-cluster" {
5858
}
5959
}
6060

61-
node_version = var.kubernetes_version
61+
min_master_version = var.kubernetes_version
6262
# the default nodepool is used as the services nodepool
6363
remove_default_node_pool = false
6464
node_config {

install/tests/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ check-gitpod-installation: delete-cm-setup check-kots-app check-env-sub-domain
9292
run-tests:
9393
./tests.sh ${KUBECONFIG}
9494

95+
kots-uprgade:
96+
@echo "Upgrade gitpod KOTS app to latest"
97+
kubectl kots upstream upgrade --kubeconfig=${KUBECONFIG} gitpod -n gitpod --deploy
98+
9599
cleanup:
96100
terraform workspace select $(TF_VAR_TEST_ID)
97101
which ${KUBECONFIG} && terraform destroy -target=module.externaldns -var kubeconfig=${KUBECONFIG} --auto-approve || echo "No kubeconfig file"

0 commit comments

Comments
 (0)