FileSystemProvisioner.java 8.6 KB
Newer Older
K
kohsuke 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
24 25 26 27 28 29
package hudson;

import hudson.matrix.MatrixBuild;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Computer;
30
import hudson.model.Describable;
31
import hudson.model.Job;
32
import hudson.model.TaskListener;
K
kohsuke 已提交
33
import hudson.model.Node;
34
import hudson.model.listeners.RunListener;
35
import hudson.util.DescriptorList;
36
import hudson.scm.SCM;
37

38
import java.io.BufferedOutputStream;
39
import java.io.File;
40 41 42
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
43 44 45 46 47 48 49 50

/**
 * Prepares and provisions workspaces for {@link AbstractProject}s.
 *
 * <p>
 *
 *
 * <p>
K
kohsuke 已提交
51
 * STILL A WORK IN PROGRESS. SUBJECT TO CHANGE! DO NOT EXTEND.
52 53
 *
 * TODO: is this per {@link Computer}? Per {@link Job}?
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
 *   -> probably per slave.
 *
 * <h2>Design Problems</h2>
 * <ol>
 * <li>
 * Garbage collection of snapshots. When do we discard snapshots?
 * In one use case, it would be convenient to keep the snapshot of the
 * last promoted/successful build. So we need to define a mechanism
 * to veto GC of snapshot? like an interface that Action can implement?
 *
 * Snapshot should be obtained per user's direction. That would be a good
 * moment for the user to specify the retention policy.
 *
 * <li>
 * Configuration mechanism. Should we auto-detect FileSystemProvisioner
 * per OS? (but for example, zfs support would require the root access.)
 * People probably needs to be able to disable this feature, which means
 * one more configuration option. It's especially tricky because
 * during the configuration we don't know the OS type.
 *
 * OTOH special slave type like the ones for network.com grid can
 * hide this.
 * </ol>
 *
 *
 * <h2>Recap</h2>
 *
 * To recap,
 *
 * - when a slave connects, we auto-detect the file system provisioner.
 *   (for example, ZFS FSP would check the slave root user prop
 *   and/or attempt to "pfexec zfs create" and take over.)
 *
 * - the user may configure jobs for snapshot collection, along with
 *   the retention policy.
 *
K
kohsuke 已提交
90 91 92
 * - keep workspace snapshots that correspond to the permalinks
 *   In ZFS, use a user property to remember the build and the job.
 *
93
 * Can't the 2nd step happen automatically, when someone else depends on
94 95
 * the workspace snapshot of the upstream? Yes, by using {@link RunListener}.
 * So this becomes like a special SCM type.
96
 *
K
kohsuke 已提交
97 98
 *
 *
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
 * <h2>Design take 2</h2>
 * <p>
 * The first piece of this is the custom {@link SCM}, which inherits the
 * workspace of another job. When this executes, it picks up
 * {@link WorkspaceSnapshot} from the other job and use it to obtain the workspace.
 *
 * <p>
 * Then there's {@link RunListener}, which creates a snapshot if
 * someone else is interested in using a snapshot later.
 *
 * <h3>TODOs</h3>
 * <ul>
 * <li>
 * Garbage collection of workspace snapshots. 
 *
 * </ul>
115 116 117 118
 *
 * @author Kohsuke Kawaguchi
 * @since 1.235
 */
119
public abstract class FileSystemProvisioner implements ExtensionPoint, Describable<FileSystemProvisioner> {
120 121 122 123 124 125 126
    /**
     * Called very early in the build (before a build places any files
     * in the workspace, such as SCM checkout) to provision a workspace
     * for the build.
     *
     * <p>
     * This method can prepare the underlying file system in preparation
127
     * for the later {@link #snapshot(AbstractBuild, FilePath, TaskListener)}.
128
     *
129
     * TODO : the method needs to be able to see the snapshot would
130 131
     * be later needed. In fact, perhaps we should only call this method
     * when Hudson knows that a snapshot is later needed?
132 133 134 135
     *
     * @param ws
     *      New workspace should be prepared in this location. This is the same value as
     *      {@code build.getProject().getWorkspace()} but passed separately for convenience.
136
     */
137
    public abstract void prepareWorkspace(AbstractBuild<?,?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException;
138

139 140 141 142 143
    /**
     * When a project is deleted, this method is called to undo the effect of
     * {@link #prepareWorkspace(AbstractBuild, FilePath, TaskListener)}.
     */
    public abstract void discardWorkspace(AbstractProject<?,?> project, TaskListener listener) throws IOException, InterruptedException;
144

145
//    public abstract void moveWorkspace(AbstractProject<?,?> project, File oldWorkspace, File newWorkspace) throws IOException;
146 147 148 149 150 151 152 153 154 155

    /**
     * Obtains the snapshot of the workspace of the given build.
     *
     * <p>
     * The state of the build when this method is invoked depends on
     * the project type. Most would call this at the end of the build,
     * but for example {@link MatrixBuild} would call this after
     * SCM check out so that the state of the fresh workspace
     * can be then propagated to elsewhere.
156 157 158 159 160 161 162 163 164 165 166 167 168 169
     *
     * <p>
     * If the implementation of this method needs to store data in a file system,
     * do so under {@link AbstractBuild#getRootDir()}, since the lifecycle of
     * the snapshot is tied to the life cycle of a build.
     *
     * @param ws
     *      New workspace should be prepared in this location. This is the same value as
     *      {@code build.getProject().getWorkspace()} but passed separately for convenience.
     */
    public abstract WorkspaceSnapshot snapshot(AbstractBuild<?,?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException;

    public abstract FileSystemProvisionerDescriptor getDescriptor();

K
kohsuke 已提交
170 171 172 173 174 175 176 177 178
    /**
     * TODO: eventually move this to {@link Node} since it needs to be configurable
     * per node, but as of now kept here to avoid interfering with the production code.
     */
    public static FileSystemProvisioner get(Node node) {
        return DEFAULT;
    }


179 180
    /**
     * A list of available file system provider types.
181
     */
182 183
    public static final DescriptorList<FileSystemProvisioner> LIST = new DescriptorList<FileSystemProvisioner>();

184 185 186 187
    /**
     * Default implementation.
     */
    public static final FileSystemProvisioner DEFAULT = new Default();
188 189 190 191 192 193

    /**
     * Default implementation that doesn't rely on any file system specific capability,
     * and thus can be used anywhere that Hudson runs.
     */
    public static final class Default extends FileSystemProvisioner {
194 195
        private Default() {}

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
        public void prepareWorkspace(AbstractBuild<?, ?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException {
            ws.mkdirs();
        }

        public void discardWorkspace(AbstractProject<?, ?> project, TaskListener listener) throws IOException, InterruptedException {
            project.getWorkspace().deleteRecursive();
        }

        /**
         * Creates a tar ball.
         */
        public WorkspaceSnapshot snapshot(AbstractBuild<?, ?> build, FilePath ws, TaskListener listener) throws IOException, InterruptedException {
            File wss = new File(build.getRootDir(),"workspace.zip");
            OutputStream os = new BufferedOutputStream(new FileOutputStream(wss));
            try {
                ws.createZipArchive(os);
            } finally {
                os.close();
            }
            return new WorkspaceSnapshotImpl();
        }

        public FileSystemProvisionerDescriptor getDescriptor() {
            return null;
        }
221

222 223 224 225 226 227 228
        public static final class WorkspaceSnapshotImpl extends WorkspaceSnapshot {
            public void restoreTo(AbstractBuild<?,?> owner, FilePath dst, TaskListener listener) throws IOException, InterruptedException {
                File wss = new File(owner.getRootDir(),"workspace.zip");
                new FilePath(wss).unzip(dst);
            }
        }
    }
229
}