以前梁大师搞过一个Hudson的构建节点(slave)workspace清理(详见http://www.51testing.com/?uid-13997-action-viewspace-itemid-809114)。现在Hudson和Jenkins基本上分道扬镳了,最新Hudson版本风格与老版本有很大差异,要想升级用到最新的一些特性非常痛苦。而Jenkins则是由Kohsuke Kawaguchi(原Hudson作者)继续发展,其对版本风格更适合升级,所以需要将一些以前弄好的需求代码迁移到Jenkins上。
Jenkins在删除任务后,本身不会对slave上的workspace进行清理,导致slave磁盘空间占满。目前没有找到任何插件能完成这一工作(插件上也不好搞),于是需要直接修改Jenkins代码。从GitHub上拿到最新的Jenkins代码后,先处理
hudson.model.AbstractProject
protected void performDelete() throws IOException, InterruptedException {
// prevent a new build while a delete operation is in progress
makeDisabled(true);
// note start
/*
FilePath ws = getWorkspace();
if(ws!=null) {
Node on = getLastBuiltOn();
getScm().processWorkspaceBeforeDeletion(this, ws, on);
if(on!=null)
on.getFileSystemProvisioner().discardWorkspace(this,ws);
}
*/
// note end
// modify start
Map<Node, FilePath> nfMap = getAllBuiltOn();
for (Map.Entry<Node, FilePath> entry : nfMap.entrySet()) {
Node n = entry.getKey();
FilePath fp = entry.getValue();
getScm().processWorkspaceBeforeDeletion(this, fp, n);
if (n != null) n.getFileSystemProvisioner().discardWorkspace(this, fp);
}
// modify end
super.performDelete();
}
先注释掉原有代码,基本没用,只是为了ZFSProvisioner扩展使用,Default的discardWorksapce没有做任何操作。增加一个getAllBuiltOn()方法获取到AbstractProject的所有构建节点,AbstractProject本身获取不到构建节点。但AbstractProject是继承于Job的,所以可以在Job中获取节点。
hudson.model.Job
public Map<Node, FilePath> getAllBuiltOn() {
List allBuild = getAllBuild();
Map<Node, FilePath> map = new HashMap<Node, FilePath>();
Set<Node> nodes = new HashSet<Node>();
for (Object o : allBuild) {
AbstractBuild b = (AbstractBuild) o;
Node n = b.getBuiltOn();
if (!nodes.contains(n)) {
FilePath ws = b.getWorkspace();
if (ws != null) {
map.put(n, ws);
nodes.add(n);
}
}
}
return map;
}
@Exported
@QuickSilver
public List getAllBuild() {
SortedMap runs = _getRuns();
List buildList = new ArrayList();
if (runs.isEmpty()) return buildList;
for (Map.Entry entry : runs.entrySet()) {
buildList.add(runs.get(entry.getKey()));
}
return buildList;
}
_getRuns()可以拿到本次Job的运行历史,返回的是Run的子类,AbstractBuild可以拿到slave节点Node和workspace,接下来封装返回即可。接下来是hudson.FileSystemProvisioner.Default内部类对文件的删除,原有代码是没有做任何处理的,所以需要加代码,可以参考ZFSProvisioner的实现。
hudson.FileSystemProvisioner.Default
public void discardWorkspace(AbstractProject<?, ?> project, FilePath ws) throws IOException,InterruptedException {
ws.act(new FileCallable<Void>() {
public Void invoke(File f, VirtualChannel channel) throws IOException {
if (f != null) {
try {
Util.deleteRecursive(f);
} catch (Throwable e) {
// delete failed, eat it.
}
}
return null;
}
});
}
务必将Default实现序列化接口,如果没有删除一个任务会抛出一个异常,异常信息很明显,需要Default类序列化。这里加一个删除任务吃掉任何异常的代码,避免删除文件时出现异常导致任务删除不成功,后继删除操作不能完成(我们有出现需要删除的workspace正好被打开在看问题,结果删除不成功抛了异常,Amon又是一个事件触发的删除,该事件不会第二次来删除,导致Job就残留在Hudson上了)。