Skip to content

Commit 6a8283a

Browse files
author
Wulf Thimm
committed
enhance heuristics to delete preview-environments
1 parent bb21036 commit 6a8283a

File tree

1 file changed

+72
-22
lines changed

1 file changed

+72
-22
lines changed

.werft/platform-delete-preview-environments-cron.ts

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,48 +37,98 @@ async function deletePreviewEnvironments() {
3737
}
3838
werft.done("prep")
3939

40-
werft.phase("Fetching branches");
41-
const branches = getAllBranches();
42-
// 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
43-
// preview name patterns before it is safe to delete a namespace.
44-
const expectedPreviewEnvironmentNamespaces = new Set(branches.flatMap(branch => [parseBranch(branch), expectedNamespaceFromBranch(branch)]));
45-
werft.done("Fetching branches");
46-
4740
werft.phase("Fetching previews");
4841
let previews: string[]
4942
try {
5043
previews = listAllPreviewNamespaces(CORE_DEV_KUBECONFIG_PATH, {});
51-
previews.forEach(previewNs => werft.log("Fetching previews", previewNs))
44+
previews.forEach(previewNs => werft.log("Fetching previews", previewNs));
5245
} catch (err) {
5346
werft.fail("Fetching previews", err)
5447
}
5548
werft.done("Fetching previews");
5649

50+
werft.phase("Fetching outdated branches");
51+
const branches = getAllBranches();
52+
const outdatedPreviews = new Set(branches
53+
.filter(branch => {
54+
const lastCommit = exec(`git log ${branch} --since=$(date +%Y-%m-%d -d "5 days ago")`, { silent: true })
55+
return lastCommit.length < 1
56+
})
57+
.map(branch => expectedNamespaceFromBranch(branch.replace('origin\/','').trim())))
58+
59+
const expectedPreviewEnvironmentNamespaces = new Set(branches.map(branch => expectedNamespaceFromBranch(branch)));
60+
5761
werft.phase("deleting previews")
5862
try {
59-
const previewsToDelete = previews.filter(ns => !expectedPreviewEnvironmentNamespaces.has(ns))
60-
// Trigger namespace deletion in parallel
61-
const promises = previewsToDelete.map(preview => wipePreviewEnvironmentAndNamespace(helmInstallName, preview, CORE_DEV_KUBECONFIG_PATH, { slice: `Deleting preview ${preview}` }));
62-
// But wait for all of them to finish before (or one of them to fail) before we continue
63-
await Promise.all(promises)
63+
const deleteDueToMissingBranch = previews.filter(ns => !expectedPreviewEnvironmentNamespaces.has(ns))
64+
const deleteDueToNoCommitActivity = previews.filter(ns => outdatedPreviews.has(ns))
65+
const deleteDueToNoDBActivity = previews.filter(ns => checkActivity(ns))
66+
const previewsToDelete = new Set([...deleteDueToMissingBranch, ...deleteDueToNoCommitActivity, ...deleteDueToNoDBActivity])
67+
68+
const dryRun = true
69+
if (dryRun) {
70+
previewsToDelete.forEach(preview => werft.log("Would have deleted preview environment ", preview))
71+
}
72+
else {
73+
previewsToDelete.forEach(preview => werft.log("deleting preview", preview))
74+
// const promises = previewsToDelete.map(preview => wipePreviewEnvironmentAndNamespace(helmInstallName, preview, CORE_DEV_KUBECONFIG_PATH, { slice: `Deleting preview ${preview}` }));
75+
// await Promise.all(promises)
76+
}
77+
werft.done("deleting previews")
6478
} catch (err) {
6579
werft.fail("deleting previews", err)
6680
}
67-
werft.done("deleting previews")
81+
}
82+
83+
84+
function checkActivity(previewNS) {
85+
86+
const statusNS = exec(`KUBECONFIG=${CORE_DEV_KUBECONFIG_PATH} kubectl get ns ${previewNS} -o jsonpath='{.status.phase}'`, { silent: true})
87+
88+
if ( statusNS == "Active") {
89+
90+
const emptyNS = exec(`KUBECONFIG=${CORE_DEV_KUBECONFIG_PATH} kubectl get pods -n ${previewNS} -o jsonpath='{.items.*}'`, { silent: true})
91+
92+
if ( emptyNS.length < 1 )
93+
return;
94+
95+
const statusDB = exec(`KUBECONFIG=${CORE_DEV_KUBECONFIG_PATH} kubectl get pods mysql-0 -n ${previewNS} -o jsonpath='{.status.phase}'`, { silent: true})
96+
const statusDbContainer = exec(`KUBECONFIG=${CORE_DEV_KUBECONFIG_PATH} kubectl get pods mysql-0 -n ${previewNS} -o jsonpath='{.status.containerStatuses.*.ready}'`, { silent: true})
97+
98+
if (statusDB.code == 0 && statusDB == "Running" && statusDbContainer != "false") {
99+
100+
const connectionToDb = `KUBECONFIG=${CORE_DEV_KUBECONFIG_PATH} kubectl get secret db-password -n ${previewNS} -o jsonpath='{.data.mysql-root-password}' | base64 -d | mysql --host=db.${previewNS}.svc.cluster.local --port=3306 --user=root --database=gitpod -s -N -p`
101+
102+
const latestInstanceTimeout = 24
103+
const latestInstance = exec(`${connectionToDb} --execute="SELECT creationTime FROM d_b_workspace_instance WHERE creationTime > DATE_SUB(NOW(), INTERVAL '${latestInstanceTimeout}' HOUR) LIMIT 1"`, { silent: true })
104+
105+
const latestUserTimeout = 24
106+
const latestUser= exec(`${connectionToDb} --execute="SELECT creationDate FROM d_b_user WHERE creationDate > DATE_SUB(NOW(), INTERVAL '${latestUserTimeout}' HOUR) LIMIT 1"`, { silent: true })
107+
108+
const lastModifiedTimeout = 24
109+
const lastModified= exec(`${connectionToDb} --execute="SELECT _lastModified FROM d_b_user WHERE _lastModified > DATE_SUB(NOW(), INTERVAL '${lastModifiedTimeout}' HOUR) LIMIT 1"`, { silent: true })
110+
111+
const heartbeatTimeout = 24
112+
const heartbeat= exec(`${connectionToDb} --execute="SELECT lastSeen FROM d_b_workspace_instance_user WHERE lastSeen > DATE_SUB(NOW(), INTERVAL '${heartbeatTimeout}' HOUR) LIMIT 1"`, { silent: true })
113+
114+
if ( (heartbeat.length < 1) &&
115+
(latestInstance.length < 1) &&
116+
(latestUser.length < 1) &&
117+
(lastModified.length < 1) ) {
118+
return true;
119+
} else {
120+
return false;
121+
}
122+
}
123+
}
124+
68125
}
69126

70127
function getAllBranches(): string[] {
71-
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');
128+
return exec(`git branch -r | grep -v '\\->' | sed "s,\\x1B\\[[0-9;]*[a-zA-Z],,g"`).stdout.trim().split('\n');
72129
}
73130

74131
function expectedNamespaceFromBranch(branch: string): string {
75132
const previewName = previewNameFromBranchName(branch)
76133
return `staging-${previewName}`
77134
}
78-
79-
function parseBranch(branch: string): string {
80-
const prefix = 'staging-';
81-
const parsedBranch = branch.normalize().split("/").join("-");
82-
83-
return prefix + parsedBranch;
84-
}

0 commit comments

Comments
 (0)