/** * Allocates a workspace by adding some variation to the given base to make it unique. * * <p> * This method doesn't block prolonged amount of time. Whenever a desired workspace * is in use, the unique variation is added. */ public synchronized Lease allocate(@Nonnull FilePath base) throws InterruptedException { return allocate(base,new Object()); }
/** * Just record that this workspace is being used, without paying any attention to the synchronization support. */ public synchronized Lease record(@Nonnull FilePath p) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "recorded " + p, new Throwable("from " + this)); } Entry old = inUse.put(p, new Entry(p, false)); if (old!=null) throw new AssertionError("Tried to record a workspace already owned: "+old); return lease(p); }
/** * See {@link #allocate(FilePath)} * * @param context * Threads that share the same context can re-acquire the same lock (which will just increment the lock count.) * This allows related executors to share the same workspace. */ public synchronized Lease allocate(@Nonnull FilePath base, Object context) throws InterruptedException { for (int i=1; ; i++) { FilePath candidate = i==1 ? base : base.withSuffix(COMBINATOR+i); Entry e = inUse.get(candidate); if(e!=null && !e.quick && e.context!=context) continue; return acquire(candidate,false,context); } }
private PollingResult pollWithWorkspace(TaskListener listener, SCM scm, R lb, @Nonnull FilePath ws, WorkspaceList l) throws InterruptedException, IOException { // if doing non-concurrent build, acquire a workspace in a way that causes builds to block for this workspace. // this prevents multiple workspaces of the same job --- the behavior of Hudson < 1.319. // // OTOH, if a concurrent build is chosen, the user is willing to create a multiple workspace, // so better throughput is achieved over time (modulo the initial cost of creating that many workspaces) // by having multiple workspaces Node node = lb.getBuiltOn(); Launcher launcher = ws.createLauncher(listener).decorateByEnv(getEnvironment(node,listener)); WorkspaceList.Lease lease = l.acquire(ws, !concurrentBuild); try { String nodeName = node != null ? node.getSelfLabel().getName() : "[node_unavailable]"; listener.getLogger().println("Polling SCM changes on " + nodeName); LOGGER.fine("Polling SCM changes of " + getName()); if (pollingBaseline==null) // see NOTE-NO-BASELINE above calcPollingBaseline(lb,launcher,listener); PollingResult r = scm.poll(this, launcher, ws, listener, pollingBaseline); pollingBaseline = r.remote; return r; } finally { lease.release(); } }
/** * Sets up an environment logged in to the specified Docker registry. * @param dockerExecutable as in {@link DockerTool#getExecutable}, with a 1.8+ client */ public KeyMaterialFactory newKeyMaterialFactory(@Nonnull URL endpoint, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) throws InterruptedException, IOException { if (!USE_CUSTOM_LOGIN) { try { // see UsernamePasswordDockerRegistryTokenSource for example String usernameColonPassword = new String(Base64.getDecoder().decode(token), StandardCharsets.UTF_8); int colon = usernameColonPassword.indexOf(':'); if (colon > 0) { return new RegistryKeyMaterialFactory(usernameColonPassword.substring(0, colon), usernameColonPassword.substring(colon + 1), endpoint, launcher, env, listener, dockerExecutable). contextualize(new KeyMaterialContext(WorkspaceList.tempDir(workspace))); } } catch (IllegalArgumentException x) { // not Base64-encoded } listener.getLogger().println("Warning: authentication token does not look like a username:password; falling back to direct manipulation of Docker configuration files"); } return newKeyMaterialFactory(endpoint, workspace.getChannel(), launcher, listener); }
static void doRetrieve(String name, boolean changelog, @Nonnull SCM scm, FilePath target, Run<?, ?> run, TaskListener listener) throws Exception { // Adapted from CpsScmFlowDefinition: SCMStep delegate = new GenericSCMStep(scm); delegate.setPoll(false); // TODO we have no API for determining if a given SCMHead is branch-like or tag-like; would we want to turn on polling if the former? delegate.setChangelog(changelog); FilePath dir; Node node = Jenkins.getActiveInstance(); if (run.getParent() instanceof TopLevelItem) { FilePath baseWorkspace = node.getWorkspaceFor((TopLevelItem) run.getParent()); if (baseWorkspace == null) { throw new IOException(node.getDisplayName() + " may be offline"); } dir = baseWorkspace.withSuffix(getFilePathSuffix() + "libs").child(name); } else { // should not happen, but just in case: throw new AbortException("Cannot check out in non-top-level build"); } Computer computer = node.toComputer(); if (computer == null) { throw new IOException(node.getDisplayName() + " may be offline"); } try (WorkspaceList.Lease lease = computer.getWorkspaceList().allocate(dir)) { delegate.checkout(run, lease.path, listener, node.createLauncher(listener)); // Cannot add WorkspaceActionImpl to private CpsFlowExecution.flowStartNodeActions; do we care? // Copy sources with relevant files from the checkout: lease.path.copyRecursiveTo("src/**/*.groovy,vars/*.groovy,vars/*.txt,resources/", null, target); } }
dir = new FilePath(owner.getRootDir()); delegate.setChangelog(true); FilePath acquiredDir; try (WorkspaceList.Lease lease = computer.getWorkspaceList().acquire(dir)) { for (int retryCount = Jenkins.get().getScmCheckoutRetryCount(); retryCount >= 0; retryCount--) { try { FilePath scriptFile = dir.child(expandedScriptPath); if (!scriptFile.absolutize().getRemote().replace('\\', '/').startsWith(dir.absolutize().getRemote().replace('\\', '/') + '/')) { // TODO JENKINS-26838 throw new IOException(scriptFile + " is not inside " + dir);
throw new IllegalStateException(node + " is offline"); lease = computer.getWorkspaceList().allocate(baseWorkspace); } else { FilePath rootPath = node.getRootPath(); throw new IllegalStateException(node + " is offline"); FilePath baseWorkspace = rootPath.child(dir); lease = computer.getWorkspaceList().allocate(baseWorkspace); .withContexts( EnvironmentExpander.merge(getContext().get(EnvironmentExpander.class), EnvironmentExpander.constant(Collections.singletonMap("WORKSPACE", workspace.getRemote()))), workspace) .withCallback(new Callback(lease))
if (ws == null || !ws.exists()) { WorkspaceList.Lease lease = l.acquire(ws, !concurrentBuild); Launcher launcher = ws.createLauncher(listener); try { LOGGER.fine("Polling SCM changes of " + getName());
@SuppressFBWarnings(value="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification="TODO 1.653+ switch to Jenkins.getInstanceOrNull") @Override protected WorkspaceList.Lease tryResolve() throws InterruptedException { Jenkins j = Jenkins.getInstance(); if (j == null) { return null; } // FilePathUtils.find not useful here since we need c anyway, and cannot easily return a tuple // (could call toComputer on result but then we iterate computers twice, a possible race condition) Computer c = j.getComputer(slave); if (c == null) { return null; } VirtualChannel ch = c.getChannel(); if (ch == null) { return null; } FilePath fp = new FilePath(ch, path); // Since there is no equivalent to Lock.tryLock for WorkspaceList (.record would work but throws AssertionError and swaps the holder): WorkspaceList.Lease lease = c.getWorkspaceList().allocate(fp); if (lease.path.equals(fp)) { return lease; } else { // @2 or other variant, not what we expected to be able to lock without contention lease.release(); throw new IllegalStateException("JENKINS-37121: something already locked " + fp); } } @Override public String toString() {
continue; workspace = new FilePath(c.getChannel(), co.workspace); launcher = workspace.createLauncher(listener).decorateByEnv(getEnvironment(c.getNode(), listener)); lease = c.getWorkspaceList().acquire(workspace, !isConcurrentBuild()); } else { workspace = null;
listener.getLogger().println("Deleting " + ws + " on " + node.getDisplayName()); try { ws.deleteRecursive(); WorkspaceList.tempDir(ws).deleteRecursive(); } catch (IOException x) { Functions.printStackTrace(x, listener.error("Failed to delete " + ws + " on " + node.getDisplayName()));
/** * Allocates a workspace by adding some variation to the given base to make it unique. */ public synchronized Lease allocate(FilePath base) throws InterruptedException { for (int i=1; ; i++) { //Workspace suffix was changed from @ to _, because of some issues with SCMs. //see http://issues.hudson-ci.org/browse/HUDSON-4791 FilePath candidate = i==1 ? base : base.withSuffix(WORKSPACE_NAME_SUFFIX + i); Entry e = inUse.get(candidate); if(e!=null && !e.quick) continue; return acquire(candidate); } }
throw new IllegalStateException(node + " is offline"); WorkspaceList.Lease lease = computer.getWorkspaceList().allocate(p); FilePath workspace = lease.path; FlowNode flowNode = context.get(FlowNode.class);
/** * Just record that this workspace is being used, without paying any * attention to the sycnhronization support. */ public synchronized Lease record(FilePath p) { log("recorded " + p); Entry old = inUse.put(p, new Entry(p, false)); if (old != null) { throw new AssertionError("Tried to record a workspace already owned: " + old); } return lease(p); }
/** * Acquires the given workspace. If necessary, this method blocks until it's * made available. * * @return The same {@link FilePath} as given to this method. */ public synchronized Lease acquire(FilePath p) throws InterruptedException { return acquire(p, false); }