-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Allow for branch name lengths of 45 #8692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ import * as Tracing from '../observability/tracing'; | |
import { SpanStatusCode } from '@opentelemetry/api'; | ||
import { wipePreviewEnvironmentAndNamespace, helmInstallName, listAllPreviewNamespaces } from '../util/kubectl'; | ||
import { exec } from '../util/shell'; | ||
import { previewNameFromBranchName } from '../util/preview'; | ||
|
||
// Will be set once tracing has been initialized | ||
let werft: Werft | ||
|
@@ -12,12 +13,14 @@ Tracing.initialize() | |
werft = new Werft("delete-preview-environment-cron") | ||
}) | ||
.then(() => deletePreviewEnvironments()) | ||
.then(() => werft.endAllSpans()) | ||
.catch((err) => { | ||
werft.rootSpan.setStatus({ | ||
code: SpanStatusCode.ERROR, | ||
message: err | ||
}) | ||
}) | ||
.finally(() => { | ||
werft.phase("Flushing telemetry", "Flushing telemetry before stopping job") | ||
werft.endAllSpans() | ||
}) | ||
|
||
|
@@ -35,25 +38,28 @@ async function deletePreviewEnvironments() { | |
|
||
werft.phase("Fetching branches"); | ||
const branches = getAllBranches(); | ||
const expectedPreviewEnvironmentNamespaces = new Set(branches.map(branch => parseBranch(branch))); | ||
// 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 | ||
// preview name patterns before it is safe to delete a namespace. | ||
const expectedPreviewEnvironmentNamespaces = new Set(branches.flatMap(branch => [parseBranch(branch), expectedNamespaceFromBranch(branch)])); | ||
Comment on lines
+41
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice safety check! That didn't cross my mind when I tried to implement this |
||
werft.done("Fetching branches"); | ||
|
||
werft.phase("Fetching previews"); | ||
let previews | ||
let previews: string[] | ||
try { | ||
previews = listAllPreviewNamespaces({}); | ||
previews.forEach(previewNs => werft.log("Fetching previews", previewNs)) | ||
} catch (err) { | ||
werft.fail("Fetching previews", err) | ||
} | ||
werft.done("Fetching previews"); | ||
|
||
|
||
werft.phase("Mapping previews => branches") | ||
const previewsToDelete = previews.filter(ns => !expectedPreviewEnvironmentNamespaces.has(ns)) | ||
|
||
werft.phase("deleting previews") | ||
try { | ||
previewsToDelete.forEach(preview => wipePreviewEnvironmentAndNamespace(helmInstallName, preview, { slice: `Deleting preview ${preview}` })); | ||
const previewsToDelete = previews.filter(ns => !expectedPreviewEnvironmentNamespaces.has(ns)) | ||
// Trigger namespace deletion in parallel | ||
const promises = previewsToDelete.map(preview => wipePreviewEnvironmentAndNamespace(helmInstallName, preview, { slice: `Deleting preview ${preview}` })); | ||
// But wait for all of them to finish before (or one of them to fail) before we continue | ||
await Promise.all(promises) | ||
} catch (err) { | ||
werft.fail("deleting previews", err) | ||
} | ||
|
@@ -64,9 +70,14 @@ function getAllBranches(): string[] { | |
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'); | ||
} | ||
|
||
function expectedNamespaceFromBranch(branch: string): string { | ||
const previewName = previewNameFromBranchName(branch) | ||
return `staging-${previewName}` | ||
} | ||
|
||
function parseBranch(branch: string): string { | ||
const prefix = 'staging-'; | ||
const parsedBranch = branch.normalize().split("/").join("-"); | ||
|
||
return prefix + parsedBranch; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { createHash } from "crypto"; | ||
|
||
/** | ||
* Based on the current branch name this will compute the name of the associated | ||
* preview environment. | ||
* | ||
* NOTE: This needs to produce the same result as the function in dev/preview/util/preview-name-from-branch.sh | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid drift, could we just call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer not to for the following reasons:
|
||
*/ | ||
export function previewNameFromBranchName(branchName: string): string { | ||
// Due to various limitations we have to ensure that we only use 20 characters | ||
// for the preview environment name. | ||
// | ||
// If the branch name is 20 characters or less we just use it. | ||
// | ||
// Otherwise: | ||
// | ||
// We use the first 10 chars of the sanitized branch name | ||
// and then the 10 first chars of the hash of the sanitized branch name | ||
// | ||
// That means collisions can happen. If they do, two jobs would try to deploy to the same | ||
// environment. | ||
// | ||
// see https://github.com/gitpod-io/ops/issues/1252 for details. | ||
const sanitizedBranchName = branchName.replace(/^refs\/heads\//, "").toLocaleLowerCase().replace(/[^-a-z0-9]/g, "-") | ||
|
||
if (sanitizedBranchName.length <= 20) { | ||
return sanitizedBranchName | ||
} | ||
|
||
const hashed = createHash('sha256').update(sanitizedBranchName).digest('hex') | ||
return `${sanitizedBranchName.substring(0, 10)}${hashed.substring(0,10)}` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#!/usr/bin/env bash | ||
|
||
# | ||
# Based on the name of the current branch this will compute the name of the associated | ||
# preview environment. | ||
# | ||
# NOTE: This needs to produce the same result as the function in .werft/util/preview.ts | ||
# See the file for implementation notes. | ||
# | ||
function preview-name-from-branch { | ||
branch_name=$(git symbolic-ref HEAD 2>&1) || error "Cannot get current branch" | ||
sanitizedd_branch_name=$(echo "$branch_name" | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }') | ||
length=$(echo -n "$sanitizedd_branch_name" | wc -c) | ||
|
||
if [ "$length" -gt 20 ]; then | ||
hashed=$(echo -n "${sanitizedd_branch_name}" | sha256sum) | ||
echo "${sanitizedd_branch_name:0:10}${hashed:0:10}" | ||
else | ||
echo "${sanitizedd_branch_name}" | ||
fi | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
#!/bin/bash | ||
|
||
branch=$(git symbolic-ref HEAD 2>&1) || error "cannot set kubectl namespace: no branch" | ||
source ./dev/preview/util/preview-name-from-branch.sh | ||
|
||
currentContext=$(kubectl config current-context 2>&1) || error "cannot set kubectl namespace: no current context" | ||
namespace=staging-$(echo "$branch" | awk '{ sub(/^refs\/heads\//, ""); $0 = tolower($0); gsub(/[^-a-z0-9]/, "-"); print }') | ||
namespace="staging-$(preview-name-from-branch)" | ||
|
||
echo "Setting kubectl namespace: $namespace" | ||
kubectl config set-context "$currentContext" --namespace "$namespace" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure, this isn't related to the proposed change, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this was a tiny improvement we should make it the other jobs as well☺️ Right now the log lines about flushing traces etc. are show up in whatever the phase was the last one in the job. With this change we introduce a new phase for the flushing so the logs become easier to read :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it was moved to "finally" as we want to flush in both the error and success cases :)