public @CheckForNull Run<?,?> $build() throws IOException { FlowExecutionOwner owner = execution.getOwner(); Queue.Executable qe = owner.getExecutable(); if (qe instanceof Run) { return (Run) qe; } else { return null; } }
void addTo(@Nonnull CpsFlowExecution execution) { GroovyShell shell = trusted ? execution.getTrustedShell() : execution.getShell(); shell.getClassLoader().addURL(url); }
@Override public CpsFlowExecution create(FlowDefinition def, FlowExecutionOwner owner, List<? extends Action> actions) throws IOException { Run original = Run.fromExternalizableId(originRunId); String origScript = null; boolean origSandbox = true; if (original instanceof FlowExecutionOwner.Executable) { FlowExecutionOwner originalOwner = ((FlowExecutionOwner.Executable) original).asFlowExecutionOwner(); if (originalOwner != null) { try { for (FlowCopier copier : ExtensionList.lookup(FlowCopier.class)) { copier.copy(originalOwner, owner); } } catch (InterruptedException x) { throw new IOException("Failed to copy metadata", x); } FlowExecution origExecution = originalOwner.getOrNull(); if (origExecution instanceof CpsFlowExecution) { origScript = ((CpsFlowExecution) origExecution).getScript(); origSandbox = ((CpsFlowExecution) origExecution).isSandbox(); } } } if (origScript != null) { return new CpsFlowExecution(origScript, origSandbox, owner); } else { return null; } }
/* for tests */ CpsThreadDump threadDumpSynchronous() throws InterruptedException, ExecutionException { execution.waitForSuspension(); return execution.getThreadDump(); }
@Override public void writeTo(OutputStream outputStream) throws IOException { PrintWriter pw = new PrintWriter(new OutputStreamWriter(outputStream, Charsets.UTF_8)); for (FlowExecution flow : FlowExecutionList.get()) { if (flow instanceof CpsFlowExecution) { pw.println("Build: " + flow.getOwner().getExecutable()); ((CpsFlowExecution) flow).getThreadDump().print(pw); pw.println("Approximate graph size: " + ((CpsFlowExecution) flow).approximateNodeCount()); pw.println(); } } pw.flush(); } });
/** Need to be careful that internal method names in {@link CpsScript} are not likely identifiers in user scripts. */ @Test public void methodNameClash() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("def build() {20}; def initialize() {10}; def env() {10}; def getShell() {2}; assert build() + initialize() + env() + shell == 42", false); createExecution(flow); exec.start(); while (!exec.isComplete()) { exec.waitForSuspension(); } assertEquals(dumpError(), Result.SUCCESS, exec.getResult()); }
initializeStorage(); // Throws exception and bombs out if we can't load FlowNodes } catch (Exception ex) { LOGGER.log(Level.WARNING, "Error initializing storage and loading nodes, will try to create placeholders for: "+this, ex); createPlaceholderNodes(ex); return; if (isComplete()) { if (done == Boolean.TRUE && !super.isComplete()) { LOGGER.log(Level.INFO, "Completed flow without FlowEndNode: "+this+" heads:"+getHeadsAsString()); saveOwner(); if (canResume()) { loadProgramAsync(getProgramDataFile()); } else { SettableFuture<CpsThreadGroup> p = SettableFuture.create(); programPromise = p; loadProgramFailed(e, p); } finally { if (programPromise == null) {
/** Verifies all the universal post-build cleanup was done, regardless of pass/fail state. */ static void verifyCompletedCleanly(Jenkins j, WorkflowRun run) throws Exception { // Assert that we have the appropriate flow graph entries FlowExecution exec = run.getExecution(); List<FlowNode> heads = exec.getCurrentHeads(); Assert.assertEquals(1, heads.size()); verifyNoTasksRunning(j); Assert.assertEquals(0, exec.getCurrentExecutions(false).get().size()); if (exec instanceof CpsFlowExecution) { CpsFlowExecution cpsFlow = (CpsFlowExecution)exec; assert cpsFlow.getStorage() != null; Assert.assertFalse("Should always be able to retrieve script", StringUtils.isEmpty(cpsFlow.getScript())); Assert.assertNull("We should have no Groovy shell left or that's a memory leak", cpsFlow.getShell()); Assert.assertNull("We should have no Groovy shell left or that's a memory leak", cpsFlow.getTrustedShell()); Assert.assertTrue(cpsFlow.done); assert cpsFlow.isComplete(); assert cpsFlow.heads.size() == 1; Map.Entry<Integer, FlowHead> finalHead = cpsFlow.heads.entrySet().iterator().next(); assert finalHead.getValue().get() instanceof FlowEndNode; Assert.assertEquals(cpsFlow.storage.getNode(finalHead.getValue().get().getId()), finalHead.getValue().get()); } verifyExecutionRemoved(run); }
/** * I should be able to have DSL call into async step and then bring it to the completion. */ @Test public void suspendExecutionAndComeBack() throws Exception { CpsFlowDefinition flow = new CpsFlowDefinition("semaphore 'watch'\nprintln 'Yo'", false); // get this going... createExecution(flow); exec.start(); SemaphoreStep.waitForStart("watch/1", null); assertFalse("Expected the execution to be suspended but it has completed", exec.isComplete()); FlowExecutionOwner owner = exec.getOwner(); exec = roundtripXStream(exec); // poor man's simulation of Jenkins restart exec.onLoad(owner); // now resume workflow execution SemaphoreStep.success("watch/1", null); exec.waitForSuspension(); assertTrue(exec.isComplete()); }
throw new IllegalStateException("JENKINS-50407: no loaded execution"); GroovyShell shell = e.getShell(); if (shell == null) { throw new IllegalStateException("JENKINS-50407: no loaded shell in " + e); GroovyShell trustedShell = e.getTrustedShell(); if (trustedShell == null) { throw new IllegalStateException("JENKINS-50407: no loaded trustedShell in " + e); ScriptApproval.get().accessRejected(x, ApprovalContext.create()); try { e.getOwner().getListener().getLogger().println(x.getMessage() + ". " + ScriptApprovalNote.encodeTo(Messages.SandboxContinuable_ScriptApprovalLink())); } catch (IOException ex) {
public void waitForWorkflowToSuspend(CpsFlowExecution e) throws Exception { e.waitForSuspension(); }
if (ex instanceof Run) { Result res = ((Run) ex).getResult(); setResult(res != null ? res : Result.FAILURE); this.storage = createStorage(); // Empty storage this.heads = new TreeMap<Integer, FlowHead>(); heads.put(head.getId(), head); FlowStartNode start = new FlowStartNode(this, iotaStr()); head.newStartNode(start); FlowNode end = new FlowEndNode(this, iotaStr(), (FlowStartNode) startNodes.pop(), result, getCurrentHeads().toArray(new FlowNode[0])); end.addAction(new ErrorAction(failureReason)); head.setNewHead(end); saveOwner();
/** * Obtains the Groovy compiler to be used for compiling user script * in the CPS-transformed and sandboxed manner. */ private GroovyShell $getShell() { return CpsThreadGroup.current().getExecution().getShell(); }
FlowNode head = new FlowEndNode(this, iotaStr(), (FlowStartNode)startNodes.pop(), result, getCurrentHeads().toArray(new FlowNode[0])); if (outcome.isFailure()) { head.addAction(new ErrorAction(outcome.getAbnormal())); FlowHead first = getFirstHead(); first.setNewHead(head); done = true; // After setting the final head this.getStorage().flush(); } catch (IOException ioe) { LOGGER.log(Level.WARNING, "Error flushing FlowNodeStorage to disk at end of run", ioe);
result = ((FlowInterruptedException) error).getResult(); execution.setResult(result); FlowNode fn = t.head.get(); if (fn != null) { execution.onProgramEnd(o); try { this.execution.saveOwner(); } catch (Exception ex) { LOGGER.log(Level.WARNING, "Error saving execution for "+this.getExecution(), ex); execution.cleanUpHeap(); if (scripts != null) { scripts.clear(); Util.deleteFile(execution.getProgramDataFile()); } catch (IOException x) { LOGGER.log(Level.WARNING, "Failed to delete program.dat in " + execution, x);
private ThreadContext setUp() { CpsFlowExecution execution = cpsThreadGroup.getExecution(); ACL.impersonate(execution.getAuthentication()); CURRENT.set(cpsThreadGroup); cpsThreadGroup.busy = true; Thread t = Thread.currentThread(); ThreadContext context = new ThreadContext(t, execution); t.setName("Running " + execution); assert cpsThreadGroup.getExecution() != null; if (cpsThreadGroup.getExecution().getShell() != null) { assert cpsThreadGroup.getExecution().getShell().getClassLoader() != null; t.setContextClassLoader(cpsThreadGroup.getExecution().getShell().getClassLoader()); } return context; }
/** * @param execution * Instantiated {@link CpsGroovyShell} will be used to load scripts for this execution. */ public CpsGroovyShellFactory(@Nullable CpsFlowExecution execution) { this.execution = execution; this.sandbox = execution!=null && execution.isSandbox(); this.decorators = GroovyShellDecorator.all(); }
static void assertCleanInProgress(WorkflowRun run) throws Exception { Assert.assertTrue(run.isBuilding()); Assert.assertNull(run.getResult()); FlowExecution fe = run.getExecution(); AtomicBoolean hasExecutionInList = new AtomicBoolean(false); FlowExecutionList.get().forEach(f -> { if (fe != null && f == fe) { hasExecutionInList.set(true); } }); if (!hasExecutionInList.get()) { Assert.fail("Build completed but should still show in FlowExecutionList"); } CpsFlowExecution cpsExec = (CpsFlowExecution)fe; Assert.assertFalse(cpsExec.isComplete()); Assert.assertEquals(Boolean.FALSE, cpsExec.done); Assert.assertFalse(cpsExec.getCurrentHeads().get(0) instanceof FlowEndNode); Assert.assertTrue(cpsExec.startNodes != null && !cpsExec.startNodes.isEmpty()); }
/** Verifies all the assumptions about a cleanly finished build. */ static void assertCompletedCleanly(WorkflowRun run) throws Exception { while (run.isBuilding()) { Thread.sleep(100); // TODO seems to be unpredictable } Assert.assertNotNull(run.getResult()); FlowExecution fe = run.getExecution(); FlowExecutionList.get().forEach(f -> { if (fe != null && f == fe) { Assert.fail("FlowExecution still in FlowExecutionList!"); } }); Assert.assertTrue("Queue not empty after completion!", Queue.getInstance().isEmpty()); if (fe instanceof CpsFlowExecution) { CpsFlowExecution cpsExec = (CpsFlowExecution)fe; Assert.assertTrue(cpsExec.isComplete()); Assert.assertEquals(Boolean.TRUE, cpsExec.done); Assert.assertEquals(1, cpsExec.getCurrentHeads().size()); Assert.assertTrue(cpsExec.isComplete()); Assert.assertTrue(cpsExec.getCurrentHeads().get(0) instanceof FlowEndNode); Assert.assertTrue(cpsExec.startNodes == null || cpsExec.startNodes.isEmpty()); while (cpsExec.blocksRestart()) { Thread.sleep(100); // TODO ditto } } else { System.out.println("WARNING: no FlowExecutionForBuild"); } }