我们有一个有八个节点的 Hudson 集群。当某个特定的代码分支不再处于活动状态时,我们会禁用构建作业,但该作业的工作区仍然会占用所有节点上的空间。
我正在寻找一种跨所有节点触发工作区清理的方法。请注意,我不是在寻找“构建前干净的工作区”解决方案。
我们有一个有八个节点的 Hudson 集群。当某个特定的代码分支不再处于活动状态时,我们会禁用构建作业,但该作业的工作区仍然会占用所有节点上的空间。
我正在寻找一种跨所有节点触发工作区清理的方法。请注意,我不是在寻找“构建前干净的工作区”解决方案。
我试过以下脚本,它适用于单节点,
def hi = hudson.model.Hudson.instance
hi.getItems(hudson.model.Job).each {
job ->
if(job.isDisabled())
{
println(job.displayName)
job.doDoWipeOutWorkspace()
}
}
您不需要编写插件。您可以编写一个利用Groovy 插件编写 Groovy 系统脚本的作业。比方说,这项工作每晚都会运行。它将识别禁用的项目并删除它们的工作空间。这是您的脚本将利用的Hudson 模型 API的链接。有一个 Groovy 脚本控制台,http://<hudson-server>/script
对调试非常有用。
这是一个应该对您有直接好处的代码片段。在脚本控制台中运行它并检查输出:
def hi = hudson.model.Hudson.instance
hi.getItems(hudson.model.Job).each {
job ->
println(job.displayName)
println(job.isDisabled())
println(job.workspace)
}
您可能还会发现此答案中的代码片段很有用。他们指的是 Jenkins API,但在这个级别上,我认为 Jenkins 和 Hudson 之间没有区别。
更新:
以下是在多个从站上执行此操作的方法:创建一个在所有从站上运行的多配置作业(也称为“矩阵作业”)。在每个从属服务器上,以下系统 Groovy 脚本将为您提供每个作业在该从属服务器上的工作空间(以及启用/禁用标志):
def hi = hudson.model.Hudson.instance
def thr = Thread.currentThread()
def build = thr?.executable
def node = build.executor.owner.node
hi.getItems(hudson.model.Job).each {
job ->
println("---------")
println(job.displayName)
println(job.isDisabled())
println(node.getWorkspaceFor(job))
}
由于脚本在从站本身上运行,您可以直接从中清除工作区。当然,工作空间可能不存在,但这不是问题。请注意,您只编写一次脚本 - Jenkins 将自动在您在矩阵作业中指定的所有从属服务器上运行它。
以下 Groovy 脚本擦除所有节点上某些作业的工作区。从“Jenkins host”/computer/(master)/script 执行
在 TODO 部分,将作业名称更改为您需要的名称。
import hudson.model.*
// For each job
for (item in Hudson.instance.items)
{
jobName = item.getFullDisplayName()
// check that job is not building
if (!item.isBuilding())
{
// TODO: Modify the following condition to select which jobs to affect
if (jobName == "MyJob")
{
println("Wiping out workspaces of job " + jobName)
customWorkspace = item.getCustomWorkspace()
println("Custom workspace = " + customWorkspace)
for (node in Hudson.getInstance().getNodes())
{
println(" Node: " + node.getDisplayName())
workspacePath = node.getWorkspaceFor(item)
if (workspacePath == null)
{
println(" Could not get workspace path")
}
else
{
if (customWorkspace != null)
{
workspacePath = node.getRootPath().child(customWorkspace)
}
pathAsString = workspacePath.getRemote()
if (workspacePath.exists())
{
workspacePath.deleteRecursive()
println(" Deleted from location " + pathAsString)
}
else
{
println(" Nothing to delete at " + pathAsString)
}
}
}
}
}
else
{
println("Skipping job " + jobName + ", currently building")
}
}
它有点晚了,但我遇到了同样的问题。我的脚本将检查是否至少有 2 GB 空间可用。如果不是这种情况,则节点上的所有工作空间都将被清除以释放空间。
import hudson.FilePath.FileCallable
import hudson.slaves.OfflineCause
for (node in Jenkins.instance.nodes) {
computer = node.toComputer()
if (computer.getChannel() == null) continue
rootPath = node.getRootPath()
size = rootPath.asCallableWith({f, c -> f.getUsableSpace()} as FileCallable).call()
roundedSize = size / (1024 * 1024 * 1024) as int
println("node: " + node.getDisplayName() + ", free space: " + roundedSize + "GB")
if (roundedSize < 2) {
computer.setTemporarilyOffline(true, [toString: {"disk cleanup"}] as OfflineCause)
for (item in Jenkins.instance.items) {
jobName = item.getFullDisplayName()
if (item.isBuilding()) {
println(".. job " + jobName + " is currently running, skipped")
continue
}
println(".. wiping out workspaces of job " + jobName)
workspacePath = node.getWorkspaceFor(item)
if (workspacePath == null) {
println(".... could not get workspace path")
continue
}
println(".... workspace = " + workspacePath)
customWorkspace = item.getCustomWorkspace()
if (customWorkspace != null) {
workspacePath = node.getRootPath().child(customWorkspace)
println(".... custom workspace = " + workspacePath)
}
pathAsString = workspacePath.getRemote()
if (workspacePath.exists()) {
workspacePath.deleteRecursive()
println(".... deleted from location " + pathAsString)
} else {
println(".... nothing to delete at " + pathAsString)
}
}
computer.setTemporarilyOffline(false, null)
}
}
我最近还想清理我的 jenkins 工作区,但有点扭曲:我只想从不再存在的工作中删除工作区。这是因为 jenkins 在删除作业时并没有摆脱工作区,这很烦人。而且我们目前只使用一个主节点,没有单独的节点。
我在某处找到了一个脚本(再也找不到链接了),但为了我们的使用对其进行了一些调整,将其放入 jenkins 作业中,使用“执行系统 Groovy 脚本”构建步骤,每天运行:
import hudson.FilePath
import jenkins.model.Jenkins
import hudson.model.Job
def deleteUnusedWorkspace(FilePath root, String path) {
root.list().sort{child->child.name}.each { child ->
String fullName = path + child.name
def item = Jenkins.instance.getItemByFullName(fullName);
println "Checking '$fullName'"
try{
if (item.class.canonicalName == 'com.cloudbees.hudson.plugins.folder.Folder') {
println "-> going deeper into the folder"
deleteUnusedWorkspace(root.child(child.name), "$fullName/")
} else if (item == null) {
// this code is never reached, non-existing projects generate an exception
println "Deleting (no such job): '$fullName'"
child.deleteRecursive()
} else if (item instanceof Job && !item.isBuildable()) {
// don't remove the workspace for disabled jobs!
//println "Deleting (job disabled): '$fullName'"
//child.deleteRecursive()
}
} catch (Exception exc) {
println " Exception happened: " + exc.message
println " So we delete '" + child + "'!"
child.deleteRecursive()
}
}
}
println "Beginning of cleanup script."
// loop over possible slaves
for (node in Jenkins.instance.nodes) {
println "Processing $node.displayName"
def workspaceRoot = node.rootPath.child("workspace");
deleteUnusedWorkspace(workspaceRoot, "")
}
// do the master itself
deleteUnusedWorkspace(Jenkins.instance.rootPath.child("workspace"), "")
println "Script has completed."
不过可能需要一些个人调整。显然,您应该首先注释掉所有删除语句来运行此脚本,并确保在实际运行之前进行备份。
听起来您正在寻找“禁用构建时删除工作区”解决方案。您可以编写一个 Hudson 插件来执行此操作。这可能是矫枉过正。
如果我必须这样做(我不会这样做,因为我们没有磁盘空间短缺),我会编写一个单元脚本来查找 hudson 目录下的所有禁用作业。作业由 XML 文件表示。然后我会让脚本删除任何匹配的工作区。而且我可能会在 cron 中设置它,以便它每晚或每周运行或任何适合环境的运行。