Forked from antonpiatek/gist:c74991f7ec54126fc91dd66a290d7520
Created
July 21, 2023 10:56
-
-
Save diorcety/c0655671889013035d4f1f14707603f3 to your computer and use it in GitHub Desktop.
A jenkins script to clean up workspaces on slaves
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// find old workspace folders on any node (master/slave) | |
// currently only look for deleted branches of multibranch (pipeline?) jobs, and remove them | |
// also finds builds older than 90 days. Set flags below to true to actually do deletes | |
import hudson.model.*; | |
import hudson.util.*; | |
import jenkins.model.*; | |
import hudson.FilePath; | |
import hudson.FilePath.FileCallable; | |
import hudson.slaves.OfflineCause; | |
import hudson.node_monitors.*; | |
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject; | |
import org.jenkinsci.plugins.workflow.job.WorkflowJob; | |
//Configurabke options | |
deleteStaleMultiBranchJobs = false | |
deleteOldJobs = false | |
jobMaxAge = 90 | |
tick = "\u2713" | |
trash = "\u267B" | |
jobMap = Jenkins.instance.itemMap | |
def padJob(def jobname){ | |
return "${jobname}".padRight(50,".") | |
} | |
def deleteWorkflowBranchJob(def subdir){ | |
if(deleteStaleMultiBranchJobs){ | |
subdir.deleteRecursive() | |
return "${trash} removed" | |
}else{ | |
return "${trash} DELETE DISABLED, change 'deleteStaleMultiBranchJobs' to enable" | |
} | |
} | |
def lookupJob(def lookup, def parent){ | |
//some jobs have workspaces ending in @tmp, ignore the @tmp part | |
if(lookup ==~ /.*@tmp$/){ | |
lookup = lookup -~ /@tmp$/ | |
} | |
if(lookup ==~ /.*@script$/){ | |
lookup = lookup -~ /@script$/ | |
} | |
if(lookup ==~ /.*@\d$/){ | |
lookup = lookup -~ /@\d$/ | |
} | |
def job; | |
if(parent != null){ | |
job = parent.getItem(lookup) | |
}else{ | |
job = jobMap[lookup] | |
} | |
return job; | |
} | |
def purgeAncientJob(def job, def dir){ | |
//TODO: Should we have a list of protected job names? i.e. master or releases/* | |
//TODO: job.isBuilding() ? | |
//TODO factor out an use for any build type | |
def buildAgeSec = (System.currentTimeMillis() - job.getLastBuild().getTimeInMillis()).intdiv(1000) | |
def buildAgeDays = buildAgeSec.intdiv(3600).intdiv(24) | |
if(buildAgeDays > jobMaxAge ){ | |
if(deleteOldJobs){ | |
println padJob(".... $dir.name")+" ${trash} removed" | |
dir.deleteRecursive() | |
}else{ | |
println padJob(".... $dir.name")+" ${trash} DELETE DISABLED, change 'deleteOldJobs' to delete ${buildAgeDays} days old build" | |
} | |
} else{ | |
println padJob(".... $dir.name")+" ${tick} ${job.getLastBuild().getTimestampString()} " | |
} | |
} | |
def cleanupNode(def ws){ | |
for(dir in ws.listDirectories()){ | |
def job = lookupJob(dir.name,null) | |
if(job != null){ | |
if(job instanceof WorkflowMultiBranchProject){ | |
println padJob(".. $dir.name")+" existing multibranch pipeline job" | |
for(subdir in dir.listDirectories()){ | |
def subjob = lookupJob(subdir.name, job) | |
if(subjob != null){ | |
purgeAncientJob(subjob, subdir) | |
}else{ | |
println padJob(".... $subdir.name")+" "+deleteWorkflowBranchJob(subdir) | |
} | |
} | |
}else if(job instanceof WorkflowJob){ | |
println padJob(".. $dir.name")+" existing pipeline job ${tick}" | |
}else{ | |
println padJob(".. $dir.name")+" job of type ${job}" | |
} | |
}else{ | |
println padJob(".. $dir.name")+" UNKNOWN JOB, not automatically deleting" | |
} | |
println() | |
} | |
} | |
def processNode(def node){ | |
def computer = node.toComputer() | |
if (computer == null || computer.channel == null){ | |
println "[$node.displayName] OFFLINE" | |
}else{ | |
def rootPath = node.getRootPath() | |
def diskMon = DiskSpaceMonitor.DESCRIPTOR.get(computer) | |
if(diskMon == null){ | |
println "[$node.displayName] CAN'T CALCULATE SIZE" | |
}else{ | |
def size = DiskSpaceMonitor.DESCRIPTOR.get(computer).size | |
def roundedSize = size.div(1024 * 1024 * 1024) as int | |
println "[$node.displayName] $node.workspaceRoot free space: ${roundedSize} GB" | |
} | |
try{ | |
//computer.setTemporarilyOffline(true, new hudson.slaves.OfflineCause.ByCLI("disk cleanup")) | |
def ws = node.getWorkspaceRoot() | |
cleanupNode(ws) | |
}finally{ | |
//computer.setTemporarilyOffline(false, null) | |
} | |
} | |
println("\n") | |
} | |
def main(){ | |
def nodes = Jenkins.instance.nodes | |
for (node in nodes) { | |
processNode(node) | |
} | |
println "[MASTER]" | |
cleanupNode(new FilePath(Jenkins.instance.rootPath,'workspace')) | |
} | |
println() | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment