@@ -37,48 +37,98 @@ async function deletePreviewEnvironments() {
37
37
}
38
38
werft . done ( "prep" )
39
39
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
-
47
40
werft . phase ( "Fetching previews" ) ;
48
41
let previews : string [ ]
49
42
try {
50
43
previews = listAllPreviewNamespaces ( CORE_DEV_KUBECONFIG_PATH , { } ) ;
51
- previews . forEach ( previewNs => werft . log ( "Fetching previews" , previewNs ) )
44
+ previews . forEach ( previewNs => werft . log ( "Fetching previews" , previewNs ) ) ;
52
45
} catch ( err ) {
53
46
werft . fail ( "Fetching previews" , err )
54
47
}
55
48
werft . done ( "Fetching previews" ) ;
56
49
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
+
57
61
werft . phase ( "deleting previews" )
58
62
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" )
64
78
} catch ( err ) {
65
79
werft . fail ( "deleting previews" , err )
66
80
}
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
+
68
125
}
69
126
70
127
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' ) ;
72
129
}
73
130
74
131
function expectedNamespaceFromBranch ( branch : string ) : string {
75
132
const previewName = previewNameFromBranchName ( branch )
76
133
return `staging-${ previewName } `
77
134
}
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