@CheckForNull protected Result getExecutionResult(@Nonnull WorkflowRun r) { FlowExecution execution = r.getExecution(); if (execution instanceof CpsFlowExecution) { return ((CpsFlowExecution) execution).getResult(); } else { return r.getResult(); } }
@Nonnull protected final Result combineResults(@Nonnull WorkflowRun run) { Result execResult = getExecutionResult(run); Result prevResult = run.getResult(); if (prevResult == null) { prevResult = Result.SUCCESS; } if (execResult == null) { return Result.SUCCESS; } else { return execResult.combine(prevResult); } }
@Override public void onCompleted(WorkflowRun workflowRun, @Nonnull TaskListener listener) { super.onCompleted(workflowRun, listener); // Note: run.duration is zero in onCompleted(), do the substraction in this listener Result result = workflowRun.getResult(); if (result == null) { result = Result.SUCCESS; // FIXME more elegant handling } globalPipelineMavenConfig.getDao().updateBuildOnCompletion( workflowRun.getParent().getFullName(), workflowRun.getNumber(), result.ordinal, workflowRun.getStartTimeInMillis(), Math.max(System.currentTimeMillis() - workflowRun.getStartTimeInMillis(), 0)); // @see HUDSON-5844 } }
/** * Main entry point invoked by the setup module */ public int run(Bootstrap bootstrap) throws Exception { Jenkins j = Jenkins.getInstance(); WorkflowJob w = j.createProject(WorkflowJob.class, "job"); w.addProperty(new DurabilityHintJobProperty(FlowDurabilityHint.PERFORMANCE_OPTIMIZED)); w.setDefinition(new CpsScmFlowDefinition( new FileSystemSCM(bootstrap.jenkinsfile.getParent()), bootstrap.jenkinsfile.getName())); QueueTaskFuture<WorkflowRun> f = w.scheduleBuild2(0, new SetJenkinsfileLocation(bootstrap.jenkinsfile)); b = f.getStartCondition().get(); writeLogTo(System.out); f.get(); // wait for the completion return b.getResult().ordinal; }
/** Waits until the build to resume or die. */ static void waitForBuildToResumeOrFail(WorkflowRun run) throws Exception { CpsFlowExecution execution = (CpsFlowExecution)(run.getExecution()); long nanoStartTime = System.nanoTime(); while (true) { if (!run.isBuilding()) { return; } long currentTime = System.nanoTime(); if (TimeUnit.SECONDS.convert(currentTime-nanoStartTime, TimeUnit.NANOSECONDS) > 10) { StringBuilder builder = new StringBuilder(); builder.append("Run result: "+run.getResult()); builder.append(" and execution != null:"+run.getExecution() != null+" "); FlowExecution exec = run.getExecution(); if (exec instanceof CpsFlowExecution) { CpsFlowExecution cpsFlow = (CpsFlowExecution)exec; builder.append(", FlowExecution is paused: "+cpsFlow.isPaused()) .append(", FlowExecution is complete: "+cpsFlow.isComplete()) .append(", FlowExecution result: "+cpsFlow.getResult()) .append(", FlowExecution PersistedClean: "+cpsFlow.persistedClean).append('\n'); } throw new TimeoutException("Build didn't resume or fail in a timely fashion. "+builder.toString()); } Thread.sleep(100L); } }
/** * Main entry point invoked by the setup module */ public int run(Bootstrap bootstrap) throws Exception { Jenkins j = Jenkins.getInstance(); WorkflowJob w = j.createProject(WorkflowJob.class, "job"); w.addProperty(new DurabilityHintJobProperty(FlowDurabilityHint.PERFORMANCE_OPTIMIZED)); w.setDefinition(new CpsScmFlowDefinition( new FileSystemSCM(bootstrap.jenkinsfile.getParent()), bootstrap.jenkinsfile.getName())); QueueTaskFuture<WorkflowRun> f = w.scheduleBuild2(0, new SetJenkinsfileLocation(bootstrap.jenkinsfile)); b = f.getStartCondition().get(); writeLogTo(System.out); f.get(); // wait for the completion return b.getResult().ordinal; }
/** Execution bombed out due to some sort of irrecoverable persistence issue. */ static void assertNulledExecution(WorkflowRun run) throws Exception { if (run.isBuilding()) { System.out.println("Run initially building, going to wait a second to see if it finishes, run="+run); Thread.sleep(1000); } Assert.assertFalse(run.isBuilding()); Assert.assertNotNull(run.getResult()); FlowExecution fe = run.getExecution(); Assert.assertNull(fe); }
@Override public void evaluate() throws Throwable { WorkflowRun run = story.j.jenkins.getItemByFullName(jobName, WorkflowJob.class).getLastBuild(); assert run.isBuilding(); assert run.getResult() != Result.FAILURE; Thread.sleep(35000); // Step completes if (run.getExecution() instanceof CpsFlowExecution) { CpsFlowExecution exec = (CpsFlowExecution)run.getExecution(); assert exec.persistedClean == null; } } });
static void assertResultMatchExecutionAndRun(WorkflowRun run, Result[] executionAndBuildResult) throws Exception { Assert.assertEquals(executionAndBuildResult[0], ((CpsFlowExecution)(run.getExecution())).getResult()); Assert.assertEquals(executionAndBuildResult[1], run.getResult()); }
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()); }
/** Simulates something happening badly during final shutdown, which may cause build to not appear done. */ @Test public void completedFinalFlowNodeNotPersisted() throws Exception { final int[] build = new int[1]; final Result[] executionAndBuildResult = new Result[2]; story.thenWithHardShutdown( j -> { WorkflowRun run = runBasicBuild(j, DEFAULT_JOBNAME, build); String finalId = run.getExecution().getCurrentHeads().get(0).getId(); // Hack but deletes the file from disk CpsFlowExecution cpsExec = (CpsFlowExecution)(run.getExecution()); File f = new File(cpsExec.getStorageDir(), finalId+".xml"); f.delete(); executionAndBuildResult[0] = ((CpsFlowExecution)(run.getExecution())).getResult(); executionAndBuildResult[1] = run.getResult(); }); story.then(j-> { WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCompletedCleanly(run); // assertNulledExecution(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); assertResultMatchExecutionAndRun(run, executionAndBuildResult); }); } /** Perhaps there was a serialization error breaking the FlowGraph persistence for non-durable mode. */
/** Perhaps there was a serialization error breaking the FlowGraph persistence for non-durable mode. */ @Test public void completedNoNodesPersisted() throws Exception { final int[] build = new int[1]; final Result[] executionAndBuildResult = new Result[2]; story.thenWithHardShutdown( j -> { WorkflowRun run = runBasicBuild(j, DEFAULT_JOBNAME, build); FileUtils.deleteDirectory(((CpsFlowExecution)(run.getExecution())).getStorageDir()); executionAndBuildResult[0] = ((CpsFlowExecution)(run.getExecution())).getResult(); executionAndBuildResult[1] = run.getResult(); }); story.then(j-> { WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCompletedCleanly(run); // assertNulledExecution(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); assertResultMatchExecutionAndRun(run, executionAndBuildResult); }); }
/** 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"); } }
/** Simulates case where done flag was not persisted. */ @Test public void completedButWrongDoneStatus() throws Exception { final int[] build = new int[1]; final Result[] executionAndBuildResult = new Result[2]; story.thenWithHardShutdown( j -> { WorkflowRun run = runBasicBuild(j, DEFAULT_JOBNAME, build); String finalId = run.getExecution().getCurrentHeads().get(0).getId(); // Hack but deletes the FlowNodeStorage from disk CpsFlowExecution cpsExec = (CpsFlowExecution)(run.getExecution()); cpsExec.done = false; cpsExec.saveOwner(); executionAndBuildResult[0] = ((CpsFlowExecution)(run.getExecution())).getResult(); executionAndBuildResult[1] = run.getResult(); }); story.then(j-> { WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCompletedCleanly(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); assertResultMatchExecutionAndRun(run, executionAndBuildResult); }); }
@Test @Ignore public void inProgressMaxPerfDirtyShutdown() throws Exception { final int[] build = new int[1]; final String[] finalNodeId = new String[1]; story.thenWithHardShutdown( j -> { runBasicPauseOnInput(j, DEFAULT_JOBNAME, build, FlowDurabilityHint.PERFORMANCE_OPTIMIZED); // SHOULD still save at end via persist-at-shutdown hooks }); story.then( j->{ WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); Thread.sleep(1000); j.waitForCompletion(run); assertCompletedCleanly(run); Assert.assertEquals(Result.FAILURE, run.getResult()); finalNodeId[0] = run.getExecution().getCurrentHeads().get(0).getId(); }); story.then(j-> { WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCompletedCleanly(run); Assert.assertEquals(finalNodeId[0], run.getExecution().getCurrentHeads().get(0).getId()); // JENKINS-50199, verify it doesn't try to resume again }); }
@Test public void inProgressNormal() throws Exception { final int[] build = new int[1]; story.then( j -> { WorkflowRun run = runBasicPauseOnInput(j, DEFAULT_JOBNAME, build); }); story.then( j->{ WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCleanInProgress(run); InputStepExecution exec = getInputStepExecution(run, "pause"); exec.doProceedEmpty(); j.waitForCompletion(run); assertCompletedCleanly(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); }); }
static void verifyFailedCleanly(Jenkins j, WorkflowRun run) throws Exception { if (run.isBuilding()) { // Give the run a little bit of time to see if it can resume or not FlowExecution exec = run.getExecution(); if (exec instanceof CpsFlowExecution) { waitForBuildToResumeOrFail(run); } else { Thread.sleep(4000L); } } if (run.getExecution() instanceof CpsFlowExecution) { CpsFlowExecution cfe = (CpsFlowExecution)(run.getExecution()); assert cfe.isComplete() || (cfe.programPromise != null && cfe.programPromise.isDone()); } assert !run.isBuilding(); if (run.getExecution() instanceof CpsFlowExecution) { Assert.assertEquals(Result.FAILURE, ((CpsFlowExecution) run.getExecution()).getResult()); } Assert.assertEquals(Result.FAILURE, run.getResult()); assert !run.isBuilding(); // TODO verify all blocks cleanly closed out, so Block start and end nodes have same counts and FlowEndNode is last node verifyCompletedCleanly(j, run); }
@Test @Ignore public void inProgressMaxPerfCleanShutdown() throws Exception { final int[] build = new int[1]; story.then( j -> { WorkflowRun run = runBasicPauseOnInput(j, DEFAULT_JOBNAME, build, FlowDurabilityHint.PERFORMANCE_OPTIMIZED); // SHOULD still save at end via persist-at-shutdown hooks }); story.then( j->{ WorkflowJob r = j.jenkins.getItemByFullName(DEFAULT_JOBNAME, WorkflowJob.class); WorkflowRun run = r.getBuildByNumber(build[0]); assertCleanInProgress(run); InputStepExecution exec = getInputStepExecution(run, "pause"); exec.doProceedEmpty(); j.waitForCompletion(run); assertCompletedCleanly(run); Assert.assertEquals(Result.SUCCESS, run.getResult()); }); }
@Test @Issue("JENKINS-48826") public void durabilityHintByBranchProperty() throws Exception { sampleRepo.init(); sampleRepo.write("Jenkinsfile", "echo 'whynot'"); sampleRepo.git("add", "Jenkinsfile"); sampleRepo.git("commit", "--all", "--message=flow"); WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p"); BranchSource bs = new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false)); mp.getSourcesList().add(bs); bs.setStrategy(new DefaultBranchPropertyStrategy(new BranchProperty[]{new DurabilityHintBranchProperty(FlowDurabilityHint.SURVIVABLE_NONATOMIC)})); WorkflowJob p = scheduleAndFindBranchProject(mp, "master"); r.waitUntilNoActivity(); Assert.assertEquals(FlowDurabilityHint.SURVIVABLE_NONATOMIC, DurabilityHintProvider.suggestedFor(p)); WorkflowRun b1 = p.getLastBuild(); Assert.assertEquals(Result.SUCCESS, b1.getResult()); // Ensure when we remove the property, branches see that on the next build bs.setStrategy(new DefaultBranchPropertyStrategy(new BranchProperty[]{})); p = scheduleAndFindBranchProject(mp, "master"); r.waitUntilNoActivity(); Assert.assertEquals(GlobalDefaultFlowDurabilityLevel.getDefaultDurabilityHint(), DurabilityHintProvider.suggestedFor(mp.getItems().iterator().next())); } }
@Test public void durabilityHintByPropertyStep() throws Exception { sampleRepo.init(); sampleRepo.write("Jenkinsfile", "properties([durabilityHint('" + FlowDurabilityHint.SURVIVABLE_NONATOMIC.getName()+"')])\n"+ "echo 'whynot'"); sampleRepo.git("add", "Jenkinsfile"); sampleRepo.git("commit", "--all", "--message=flow"); WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p"); mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", false))); WorkflowJob p = scheduleAndFindBranchProject(mp, "master"); r.waitUntilNoActivity(); WorkflowRun b1 = p.getLastBuild(); Assert.assertEquals(Result.SUCCESS, b1.getResult()); DurabilityHintJobProperty prop = p.getProperty(DurabilityHintJobProperty.class); Assert.assertEquals(FlowDurabilityHint.SURVIVABLE_NONATOMIC, prop.getHint()); }