3

我们有一个有八个节点的 Hudson 集群。当某个特定的代码分支不再处于活动状态时,我们会禁用构建作业,但该作业的工作区仍然会占用所有节点上的空间。

我正在寻找一种跨所有节点触发工作区清理的方法。请注意,我不是在寻找“构建前干净的工作区”解决方案。

4

6 回答 6

4

我试过以下脚本,它适用于单节点,

  def hi = hudson.model.Hudson.instance
             hi.getItems(hudson.model.Job).each {
                job ->
                if(job.isDisabled())
                {
                   println(job.displayName)
                   job.doDoWipeOutWorkspace()
                 }
           }
于 2012-09-04T09:10:40.353 回答
4

您不需要编写插件。您可以编写一个利用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 将自动在您在矩阵作业中指定的所有从属服务器上运行它。

于 2012-07-09T08:21:03.497 回答
3

以下 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")
  }
}
于 2013-08-09T09:39:21.957 回答
1

它有点晚了,但我遇到了同样的问题。我的脚本将检查是否至少有 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)
    }
}
于 2013-10-22T11:59:56.937 回答
1

我最近还想清理我的 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."

不过可能需要一些个人调整。显然,您应该首先注释掉所有删除语句来运行此脚本,并确保在实际运行之前进行备份。

于 2016-10-18T07:29:54.810 回答
0

听起来您正在寻找“禁用构建时删除工作区”解决方案。您可以编写一个 Hudson 插件来执行此操作。这可能是矫枉过正。

如果我必须这样做(我不会这样做,因为我们没有磁盘空间短缺),我会编写一个单元脚本来查找 hudson 目录下的所有禁用作业。作业由 XML 文件表示。然后我会让脚本删除任何匹配的工作区。而且我可能会在 cron 中设置它,以便它每晚或每周运行或任何适合环境的运行。

于 2012-07-09T03:18:07.677 回答