Skip to content

Commit afa2bd1

Browse files
mads-hartmannroboquat
authored andcommitted
Allow for branch name lengths of 45
This relaxes the branch name restriction from 20 characters to 45. This is achieved by constructing the preview name based on the first 10 characters of the branch name and then the first 10 characters of the hashed value of the branch name. All places that (to my knowledge) rely on the preview name has been updated. Fixes gitpod-io/ops#1252
1 parent c10b2c9 commit afa2bd1

12 files changed

+94
-25
lines changed

Diff for: .werft/delete-preview-environments/delete-preview-environments-cron.ts

+20-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as Tracing from '../observability/tracing';
33
import { SpanStatusCode } from '@opentelemetry/api';
44
import { wipePreviewEnvironmentAndNamespace, helmInstallName, listAllPreviewNamespaces } from '../util/kubectl';
55
import { exec } from '../util/shell';
6+
import { previewNameFromBranchName } from '../util/preview';
67

78
// Will be set once tracing has been initialized
89
let werft: Werft
@@ -12,12 +13,14 @@ Tracing.initialize()
1213
werft = new Werft("delete-preview-environment-cron")
1314
})
1415
.then(() => deletePreviewEnvironments())
15-
.then(() => werft.endAllSpans())
1616
.catch((err) => {
1717
werft.rootSpan.setStatus({
1818
code: SpanStatusCode.ERROR,
1919
message: err
2020
})
21+
})
22+
.finally(() => {
23+
werft.phase("Flushing telemetry", "Flushing telemetry before stopping job")
2124
werft.endAllSpans()
2225
})
2326

@@ -35,25 +38,28 @@ async function deletePreviewEnvironments() {
3538

3639
werft.phase("Fetching branches");
3740
const branches = getAllBranches();
38-
const expectedPreviewEnvironmentNamespaces = new Set(branches.map(branch => parseBranch(branch)));
41+
// During the transition from the old preview names to the new ones we have to check for the existence of both the old or new
42+
// preview name patterns before it is safe to delete a namespace.
43+
const expectedPreviewEnvironmentNamespaces = new Set(branches.flatMap(branch => [parseBranch(branch), expectedNamespaceFromBranch(branch)]));
3944
werft.done("Fetching branches");
4045

4146
werft.phase("Fetching previews");
42-
let previews
47+
let previews: string[]
4348
try {
4449
previews = listAllPreviewNamespaces({});
50+
previews.forEach(previewNs => werft.log("Fetching previews", previewNs))
4551
} catch (err) {
4652
werft.fail("Fetching previews", err)
4753
}
4854
werft.done("Fetching previews");
4955

50-
51-
werft.phase("Mapping previews => branches")
52-
const previewsToDelete = previews.filter(ns => !expectedPreviewEnvironmentNamespaces.has(ns))
53-
5456
werft.phase("deleting previews")
5557
try {
56-
previewsToDelete.forEach(preview => wipePreviewEnvironmentAndNamespace(helmInstallName, preview, { slice: `Deleting preview ${preview}` }));
58+
const previewsToDelete = previews.filter(ns => !expectedPreviewEnvironmentNamespaces.has(ns))
59+
// Trigger namespace deletion in parallel
60+
const promises = previewsToDelete.map(preview => wipePreviewEnvironmentAndNamespace(helmInstallName, preview, { slice: `Deleting preview ${preview}` }));
61+
// But wait for all of them to finish before (or one of them to fail) before we continue
62+
await Promise.all(promises)
5763
} catch (err) {
5864
werft.fail("deleting previews", err)
5965
}
@@ -64,9 +70,14 @@ function getAllBranches(): string[] {
6470
return exec(`git branch -r | grep -v '\\->' | sed "s,\\x1B\\[[0-9;]*[a-zA-Z],,g" | while read remote; do echo "\${remote#origin/}"; done`).stdout.trim().split('\n');
6571
}
6672

73+
function expectedNamespaceFromBranch(branch: string): string {
74+
const previewName = previewNameFromBranchName(branch)
75+
return `staging-${previewName}`
76+
}
77+
6778
function parseBranch(branch: string): string {
6879
const prefix = 'staging-';
6980
const parsedBranch = branch.normalize().split("/").join("-");
7081

7182
return prefix + parsedBranch;
72-
}
83+
}

Diff for: .werft/delete-preview-environments/delete-preview-environments.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ Tracing.initialize()
2626
async function deletePreviewEnvironment() {
2727
werft.phase("preparing deletion")
2828
const version = parseVersion();
29+
30+
// TODO: This won't work, we need to compute the namespace using the function in
31+
// .werft/util/preview.ts. As this job isn't executed yet I'm leaving it broken for
32+
// now until we can test what information Werft makes available to the jobs that are
33+
// triggered by branch deletions.
2934
const namespace = `staging-${version.split(".")[0]}`
3035
werft.log("preparing deletion", `Proceeding to delete the ${namespace} namespace`)
3136
werft.done("preparing deletion")
@@ -47,4 +52,4 @@ export function parseVersion() {
4752
version = version.substr(PREFIX_TO_STRIP.length);
4853
}
4954
return version
50-
}
55+
}

Diff for: .werft/jobs/build/job-config.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { config } from "process";
21
import { exec } from "../../util/shell";
32
import { Werft } from "../../util/werft";
3+
import { previewNameFromBranchName } from "../../util/preview";
44

55
export interface JobConfig {
66
analytics: string
@@ -95,10 +95,10 @@ export function jobConfig(werft: Werft, context: any): JobConfig {
9595
}
9696
const withVM = ("with-vm" in buildConfig || repository.branch.includes("with-vm")) && !mainBuild;
9797

98-
const previewDestName = version.split(".")[0];
99-
const previewEnvironmentNamespace = withVM ? `default` : `staging-${previewDestName}`;
98+
const previewName = previewNameFromBranchName(repository.branch)
99+
const previewEnvironmentNamespace = withVM ? `default` : `staging-${previewName}`;
100100
const previewEnvironment = {
101-
destname: previewDestName,
101+
destname: previewName,
102102
namespace: previewEnvironmentNamespace
103103
}
104104

Diff for: .werft/jobs/build/validate-changes.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ export async function validateChanges(werft: Werft, config: JobConfig) {
1313
werft.done('validate-changes');
1414
}
1515

16-
// Branch names cannot be longer than 20 characters.
17-
// We plan to remove this limitation once we move to previews on Harvester VMs.
16+
// Branch names cannot be longer than 45 characters.
17+
//
18+
// The branch name is used as part of the Werft job name. The job name is used for the name of the pod
19+
// and k8s has a limit of 63 characters. We use 13 characters for the "gitpod-build-" prefix and 5
20+
// more for the ".<BUILD NUMBER>" ending. That leaves us 45 characters for the branch name.
21+
// See Werft source https://github.com/csweichel/werft/blob/057cfae0fd7bb1a7b05f89d1b162348378d74e71/pkg/werft/service.go#L376
1822
async function branchNameCheck(werft: Werft, config: JobConfig) {
1923
if (!config.noPreview) {
20-
const maxBranchNameLength = 20;
24+
const maxBranchNameLength = 45;
2125
werft.log("check-branchname", `Checking if branch name is shorter than ${maxBranchNameLength} characters.`)
2226

2327
if (config.previewEnvironment.destname.length > maxBranchNameLength) {

Diff for: .werft/util/preview.ts

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { createHash } from "crypto";
2+
3+
/**
4+
* Based on the current branch name this will compute the name of the associated
5+
* preview environment.
6+
*
7+
* NOTE: This needs to produce the same result as the function in dev/preview/util/preview-name-from-branch.sh
8+
*/
9+
export function previewNameFromBranchName(branchName: string): string {
10+
// Due to various limitations we have to ensure that we only use 20 characters
11+
// for the preview environment name.
12+
//
13+
// We use the first 10 chars of the sanitized branch name
14+
// and then the 10 first chars of the hash of the sanitized branch name
15+
//
16+
// That means collisions can happen. If they do, two jobs would try to deploy to the same
17+
// environment.
18+
//
19+
// see https://github.com/gitpod-io/ops/issues/1252 for details.
20+
const sanitizedBranchName = branchName.replace(/^refs\/heads\//, "").toLocaleLowerCase().replace(/[^-a-z0-9]/g, "-")
21+
const hashed = createHash('sha256').update(sanitizedBranchName).digest('hex')
22+
return `${sanitizedBranchName.substring(0, 10)}${hashed.substring(0,10)}`
23+
}

Diff for: dev/preview/install-k3s-kubeconfig.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
set -euo pipefail
44

5-
VM_NAME="$(git symbolic-ref HEAD 2>&1 | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }')"
5+
source ./dev/preview/util/preview-name-from-branch.sh
6+
7+
VM_NAME="$(preview-name-from-branch)"
68

79
PRIVATE_KEY=$HOME/.ssh/vm_id_rsa
810
PUBLIC_KEY=$HOME/.ssh/vm_id_rsa.pub

Diff for: dev/preview/portforward-monitoring-satellite-core-dev.sh

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
# Exposes Prometheus and Grafana's UI
44
#
55

6-
VM_NAME="$(git symbolic-ref HEAD 2>&1 | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }')"
7-
NAMESPACE="staging-${VM_NAME}"
6+
source ./dev/preview/util/preview-name-from-branch.sh
7+
8+
PREVIEW_NAME="$(preview-name-from-branch)"
9+
NAMESPACE="staging-${PREVIEW_NAME}"
810

911
function log {
1012
echo "[$(date)] $*"

Diff for: dev/preview/portforward-monitoring-satellite-harvester.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
# Exposes Prometheus and Grafana's UI
44
#
55

6-
VM_NAME="$(git symbolic-ref HEAD 2>&1 | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }')"
6+
source ./dev/preview/util/preview-name-from-branch.sh
7+
8+
VM_NAME="$(preview-name-from-branch)"
79
NAMESPACE="preview-${VM_NAME}"
810

911
function log {

Diff for: dev/preview/ssh-proxy-command.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/env bash
22

3-
VM_NAME="$(git symbolic-ref HEAD 2>&1 | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }')"
3+
source ./dev/preview/util/preview-name-from-branch.sh
4+
5+
VM_NAME="$(preview-name-from-branch)"
46
NAMESPACE="preview-${VM_NAME}"
57

68
while getopts n:p: flag

Diff for: dev/preview/ssh-vm.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55

66
set -euo pipefail
77

8-
VM_NAME="$(git symbolic-ref HEAD 2>&1 | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }')"
8+
source ./dev/preview/util/preview-name-from-branch.sh
9+
10+
VM_NAME="$(preview-name-from-branch)"
911
NAMESPACE="preview-${VM_NAME}"
1012

1113
PRIVATE_KEY=$HOME/.ssh/vm_id_rsa

Diff for: dev/preview/util/preview-name-from-branch.sh

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env bash
2+
3+
#
4+
# Based on the name of the current branch this will compute the name of the associated
5+
# preview environment.
6+
#
7+
# NOTE: This needs to produce the same result as the function in .werft/util/preview.ts
8+
# See the file for implementation notes.
9+
#
10+
function preview-name-from-branch {
11+
branch_name=$(git symbolic-ref HEAD 2>&1) || error "Cannot get current branch"
12+
sanitizedd_branch_name=$(echo "$branch_name" | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }')
13+
hashed=$(echo -n "${sanitizedd_branch_name}" | sha256sum)
14+
echo "${sanitizedd_branch_name:0:10}${hashed:0:10}"
15+
}

Diff for: scripts/branch-namespace.sh

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#!/bin/bash
22

3-
branch=$(git symbolic-ref HEAD 2>&1) || error "cannot set kubectl namespace: no branch"
3+
source ./dev/preview/util/preview-name-from-branch.sh
4+
45
currentContext=$(kubectl config current-context 2>&1) || error "cannot set kubectl namespace: no current context"
5-
namespace=staging-$(echo "$branch" | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }')
6+
namespace="staging-$(preview-name-from-branch)"
67

78
echo "Setting kubectl namespace: $namespace"
89
kubectl config set-context "$currentContext" --namespace "$namespace"

0 commit comments

Comments
 (0)