Skip to content

Commit

Permalink
[werft] Migrate wipe-devstaging from bash to TS and re-use/extend exi…
Browse files Browse the repository at this point in the history
…sting logic
  • Loading branch information
geropl committed Apr 15, 2021
1 parent 67bef6e commit 7f0553f
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .werft/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export async function deployToDev(deploymentConfig: DeploymentConfig, workspaceF

// re-create namespace
try {
wipeAndRecreateNamespace(helmInstallName, namespace, { slice: 'prep' });
await wipeAndRecreateNamespace(helmInstallName, namespace, { slice: 'prep' });
setKubectlContextNamespace(namespace, { slice: 'prep' });
namespaceRecreatedResolve(); // <-- signal for certificate
werft.done('prep');
Expand Down
56 changes: 47 additions & 9 deletions .werft/util/kubectl.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import { ShellString } from 'shelljs';
import { werft, exec, ExecOptions } from './shell';
import { exec, ExecOptions, werft } from './shell';


export const IS_PREVIEW_APP_LABEL: string = "isPreviewApp";

export function setKubectlContextNamespace(namespace, shellOpts) {
[
"kubectl config current-context",
`kubectl config set-context --current --namespace=${namespace}`
].forEach(cmd => exec(cmd, shellOpts));
}

export function wipeAndRecreateNamespace(helmInstallName: string, namespace: string, shellOpts: ExecOptions) {
export async function wipeAndRecreateNamespace(helmInstallName: string, namespace: string, shellOpts: ExecOptions) {
await wipePreviewEnvironment(helmInstallName, namespace, shellOpts);

createNamespace(namespace, shellOpts);
}

export async function wipePreviewEnvironment(helmInstallName: string, namespace: string, shellOpts: ExecOptions) {
// uninstall helm first so that:
// - ws-scaler can't create new ghosts in the meantime
// - ws-manager can't start new probes/workspaces
uninstallHelm(helmInstallName, namespace, shellOpts)

deleteAllWorkspaces(namespace, shellOpts);
await deleteAllUnnamespacedObjects(namespace, shellOpts);

recreateNamespace(namespace, shellOpts);
deleteNamespace(true, namespace, shellOpts);
}

function uninstallHelm(installationName: string, namespace: string, shellOpts: ExecOptions) {
Expand Down Expand Up @@ -53,20 +62,49 @@ function deleteAllWorkspaces(namespace: string, shellOpts: ExecOptions) {
});
}

function recreateNamespace(namespace: string, shellOpts: ExecOptions) {
const result = (exec(`kubectl get namespace ${namespace}`, { ...shellOpts, dontCheckRc: true }) as ShellString);
if (result.code === 0) {
deleteNamespace(true, namespace, shellOpts);
// deleteAllUnnamespacedObjects deletes all unnamespaced objects for the given namespace
async function deleteAllUnnamespacedObjects(namespace: string, shellOpts: ExecOptions): Promise<void> {
const slice = shellOpts.slice || "deleteobjs";

const promisedDeletes: Promise<any>[] = [];
for (const resType of ["clusterrole", "clusterrolebinding", "podsecuritypolicy"]) {
werft.log(slice, `Deleting old ${resType}s...`);
const objs = exec(`kubectl get ${resType} --no-headers -o=custom-columns=:metadata.name`)
.split("\n")
.map(o => o.trim())
.filter(o => o.length > 0)
.filter(o => o.startsWith(`${namespace}-ns-`)); // "{{ .Release.Namespace }}-ns-" is the prefix-pattern we use throughout our helm resources for un-namespaced resources

for (const obj of objs) {
promisedDeletes.push(exec(`kubectl delete ${resType} ${obj}`, { slice, async: true }));
}
}
await Promise.all(promisedDeletes);
}

function createNamespace(namespace: string, shellOpts: ExecOptions) {
// (re-)create namespace
[
`kubectl create namespace ${namespace}`,
`kubectl patch namespace ${namespace} --patch '{"metadata": {"labels": {"isPreviewApp": "true"}}}'`
`kubectl patch namespace ${namespace} --patch '{"metadata": {"labels": {"${IS_PREVIEW_APP_LABEL}": "true"}}}'`
].forEach((cmd) => exec(cmd, shellOpts));
};

function deleteNamespace(wait: boolean, namespace: string, shellOpts: ExecOptions) {
export function listAllPreviewNamespaces(): string[] {
return exec(`kubectl get namespaces -l ${IS_PREVIEW_APP_LABEL}=true -o=custom-columns=:metadata.name`, { silent: true })
.stdout
.split("\n")
.map(o => o.trim())
.filter(o => o.length > 0);
}

export function deleteNamespace(wait: boolean, namespace: string, shellOpts: ExecOptions) {
// check if present
const result = (exec(`kubectl get namespace ${namespace}`, { ...shellOpts, dontCheckRc: true }) as ShellString);
if (result.code !== 0) {
return;
}

const cmd = `kubectl delete namespace ${namespace}`;
exec(cmd, shellOpts);

Expand Down
2 changes: 1 addition & 1 deletion .werft/util/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type ExecResult = {
// exec executes a command and throws an exception if that command exits with a non-zero exit code
export function exec(command: string): shell.ShellString;
export function exec(command: string, options: ExecOptions & { async?: false }): shell.ShellString;
export function exec(command: string, options: ExecOptions & { async: true }): ChildProcess;
export function exec(command: string, options: ExecOptions & { async: true }): Promise<ExecResult>;
export function exec(command: string, options: ExecOptions): shell.ShellString | ChildProcess;
export function exec(cmd: string, options?: ExecOptions): ChildProcess | shell.ShellString | Promise<ExecResult> {
if (options && options.slice) {
Expand Down
23 changes: 23 additions & 0 deletions .werft/wipe-devstaging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { werft } from './util/shell';
import { wipePreviewEnvironment, listAllPreviewNamespaces } from './util/kubectl';


async function wipeDevstaging() {
const namespace_raw = process.env.NAMESPACE;
const namespaces: string[] = [];
if (namespace_raw === "<no value>" || !namespace_raw) {
werft.log('wipe', "Going to wipe all namespaces");
listAllPreviewNamespaces()
.map(ns => namespaces.push(ns));
} else {
werft.log('wipe', `Going to wipe namespace ${namespace_raw}`);
namespaces.push(namespace_raw);
}

for (const namespace of namespaces) {
await wipePreviewEnvironment("gitpod", namespace, { slice: 'wipe' });
}
werft.done('wipe');
}

wipeDevstaging()
20 changes: 4 additions & 16 deletions .werft/wipe-devstaging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,8 @@ pod:
gcloud auth activate-service-account --key-file /mnt/secrets/gcp-sa/service-account.json
gcloud container clusters get-credentials dev --zone europe-west1-b --project gitpod-core-dev
NAMESPACE="{{ .Annotations.namespace }}"
if [ "$NAMESPACE" = "<no value>" ]; then
NAMESPACE=""
echo "Going to wipe all namespaces"
else
echo "Going to wipe namespace '$NAMESPACE'"
fi
export NAMESPACE="{{ .Annotations.namespace }}"
sudo chown -R gitpod:gitpod /workspace
for obj in clusterrole clusterrolebinding podsecuritypolicy namespace; do
werft log phase $obj Deleting old ${obj}...
OBJS_FILE=$(mktemp)
kubectl get $obj --no-headers -o=custom-columns=:metadata.name | grep -e staging -e testing | (grep "$NAMESPACE" || true) | tee "$OBJS_FILE"
if [ $(cat "$OBJS_FILE" | wc -l) -gt 0 ]; then
cat "$OBJS_FILE" | xargs kubectl delete $obj
fi
werft log phase $obj Deleted old ${obj}.
done
npm install shelljs semver ts-node typescript @types/shelljs @types/node @types/semver
npx ts-node .werft/wipe-devstaging.ts

0 comments on commit 7f0553f

Please sign in to comment.