File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\model\WorkspaceCleanupThread.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.model;
25
26 import hudson.FilePath;
27 import hudson.Util;
28 import hudson.Extension;
29 import jenkins.model.Jenkins;
30
31 import java.io.File;
32 import java.io.FileFilter;
33 import java.io.IOException;
34 import java.io.Serializable;
35 import java.util.Date;
36 import java.util.List;
37 import java.util.logging.Logger;
38
39 /**
40 * Clean up old left-over workspaces from slaves.
41 *
42 * @author Kohsuke Kawaguchi
43 */
44 @Extension
45 public class WorkspaceCleanupThread extends AsyncPeriodicWork {
46 public WorkspaceCleanupThread() {
47 super("Workspace clean-up");
48 }
49
50 public long getRecurrencePeriod() {
51 return DAY;
52 }
53
54 public static void invoke() {
55 Jenkins.getInstance().getExtensionList(AsyncPeriodicWork.class).get(WorkspaceCleanupThread.class).run();
56 }
57
58 // so that this can be easily accessed from sub-routine.
59 private TaskListener listener;
60
61 protected void execute(TaskListener listener) throws InterruptedException, IOException {
62 try {
63 if(disabled) {
64 LOGGER.fine("Disabled. Skipping execution");
65 return;
66 }
67
68 this.listener = listener;
69
70 Jenkins h = Jenkins.getInstance();
71 for (Node n : h.getNodes())
72 if (n instanceof Slave) process((Slave)n);
73
74 process(h);
75 } finally {
76 this.listener = null;
77 }
78 }
79
80 private void process(Jenkins h) throws IOException, InterruptedException {
81 File jobs = new File(h.getRootDir(), "jobs");
82 File[] dirs = jobs.listFiles(DIR_FILTER);
83 if(dirs==null) return;
84 for (File dir : dirs) {
85 FilePath ws = new FilePath(new File(dir, "workspace"));
86 if(shouldBeDeleted(dir.getName(),ws,h)) {
87 delete(ws);
88 }
89 }
90 }
91
92 private boolean shouldBeDeleted(String workspaceDirectoryName, FilePath dir, Node n) throws IOException, InterruptedException {
93 // TODO: the use of remoting is not optimal.
94 // One remoting can execute "exists", "lastModified", and "delete" all at once.
95 TopLevelItem item = Jenkins.getInstance().getItem(workspaceDirectoryName);
96
97 if(!dir.exists())
98 return false;
99
100 // if younger than a month, keep it
101 long now = new Date().getTime();
102 if(dir.lastModified() + 30 * DAY > now) {
103 LOGGER.fine("Directory "+dir+" is only "+ Util.getTimeSpanString(now-dir.lastModified())+" old, so not deleting");
104 return false;
105 }
106
107 // Could mean that directory doesn't belong to a job. But can also mean that it's a custom workspace belonging to a job.
108 // So better leave it alone - will still be deleted after 30 days - until we have a proper check for custom workspaces.
109 // TODO: implement proper check for custom workspaces.
110 // TODO: If we do the above, could also be good to add checkbox that lets users configure a workspace to never be auto-cleaned.
111 if(item==null) {
112 return false;
113 }
114
115 if (item instanceof AbstractProject<?,?>) {
116 AbstractProject<?,?> p = (AbstractProject<?,?>) item;
117 Node lb = p.getLastBuiltOn();
118 LOGGER.finer("Directory "+dir+" is last built on "+lb);
119 if(lb!=null && lb.equals(n)) {
120 // this is the active workspace. keep it.
121 LOGGER.fine("Directory "+dir+" is the last workspace for "+p);
122 return false;
123 }
124
125 if(!p.getScm().processWorkspaceBeforeDeletion(p,dir,n)) {
126 LOGGER.fine("Directory deletion of "+dir+" is vetoed by SCM");
127 return false;
128 }
129 }
130
131 LOGGER.finer("Going to delete directory "+dir);
132 return true;
133 }
134
135 private void process(Slave s) throws InterruptedException {
136 listener.getLogger().println("Scanning "+s.getNodeName());
137
138 try {
139 FilePath path = s.getWorkspaceRoot();
140 if(path==null) return;
141
142 List<FilePath> dirs = path.list(DIR_FILTER);
143 if(dirs ==null) return;
144 for (FilePath dir : dirs) {
145 if(shouldBeDeleted(dir.getName(),dir,s))
146 delete(dir);
147 }
148 } catch (IOException e) {
149 e.printStackTrace(listener.error("Failed on "+s.getNodeName()));
150 }
151 }
152
153 private void delete(FilePath dir) throws InterruptedException {
154 try {
155 listener.getLogger().println("Deleting "+dir);
156 dir.deleteRecursive();
157 } catch (IOException e) {
158 e.printStackTrace(listener.error("Failed to delete "+dir));
159 }
160 }
161
162
163 private static class DirectoryFilter implements FileFilter, Serializable {
164 public boolean accept(File f) {
165 return f.isDirectory();
166 }
167 private static final long serialVersionUID = 1L;
168 }
169 private static final FileFilter DIR_FILTER = new DirectoryFilter();
170
171 private static final long DAY = 1000*60*60*24;
172
173 private static final Logger LOGGER = Logger.getLogger(WorkspaceCleanupThread.class.getName());
174
175 /**
176 * Can be used to disable workspace clean up.
177 */
178 public static final boolean disabled = Boolean.getBoolean(WorkspaceCleanupThread.class.getName()+".disabled");
179 }
180