Search in sources :

Example 1 with StepExecution

use of org.jenkinsci.plugins.workflow.steps.StepExecution in project workflow-cps-plugin by jenkinsci.

the class DSL method invokeStep.

/**
 * When {@link #invokeMethod(String, Object)} is calling a {@link StepDescriptor}
 */
protected Object invokeStep(StepDescriptor d, Object args) {
    final NamedArgsAndClosure ps = parseArgs(args, d);
    CpsThread thread = CpsThread.current();
    FlowNode an;
    // TODO: generalize the notion of Step taking over the FlowNode creation.
    boolean hack = d instanceof ParallelStep.DescriptorImpl || d instanceof LoadStep.DescriptorImpl;
    if (ps.body == null && !hack) {
        an = new StepAtomNode(exec, d, thread.head.get());
        // TODO: use CPS call stack to obtain the current call site source location. See JENKINS-23013
        thread.head.setNewHead(an);
    } else {
        an = new StepStartNode(exec, d, thread.head.get());
        thread.head.setNewHead(an);
    }
    final CpsStepContext context = new CpsStepContext(d, thread, handle, an, ps.body);
    Step s;
    boolean sync;
    ClassLoader originalLoader = Thread.currentThread().getContextClassLoader();
    try {
        d.checkContextAvailability(context);
        Thread.currentThread().setContextClassLoader(CpsVmExecutorService.ORIGINAL_CONTEXT_CLASS_LOADER.get());
        s = d.newInstance(ps.namedArgs);
        try {
            // No point storing empty arguments, and ParallelStep is a special case where we can't store its closure arguments
            if (ps.namedArgs != null && !(ps.namedArgs.isEmpty()) && isKeepStepArguments() && !(s instanceof ParallelStep)) {
                // Get the environment variables to find ones that might be credentials bindings
                Computer comp = context.get(Computer.class);
                EnvVars allEnv = new EnvVars(context.get(EnvVars.class));
                if (comp != null && allEnv != null) {
                    allEnv.entrySet().removeAll(comp.getEnvironment().entrySet());
                }
                an.addAction(new ArgumentsActionImpl(ps.namedArgs, allEnv));
            }
        } catch (Exception e) {
            // Avoid breaking execution because we can't store some sort of crazy Step argument
            LOGGER.log(Level.WARNING, "Error storing the arguments for step: " + d.getFunctionName(), e);
        }
        // Persist the node - block start and end nodes do their own persistence.
        CpsFlowExecution.maybeAutoPersistNode(an);
        StepExecution e = s.start(context);
        thread.setStep(e);
        sync = e.start();
    } catch (Exception e) {
        if (e instanceof MissingContextVariableException)
            reportMissingContextVariableException(context, (MissingContextVariableException) e);
        context.onFailure(e);
        s = null;
        sync = true;
    } finally {
        Thread.currentThread().setContextClassLoader(originalLoader);
    }
    if (sync) {
        assert context.bodyInvokers.isEmpty() : "If a step claims synchronous completion, it shouldn't invoke body";
        if (context.getOutcome() == null) {
            context.onFailure(new AssertionError("Step " + s + " claimed to have ended synchronously, but didn't set the result via StepContext.onSuccess/onFailure"));
        }
        thread.setStep(null);
        // we just move on accordingly
        if (an instanceof StepStartNode) {
            // no body invoked, so EndNode follows StartNode immediately.
            thread.head.setNewHead(new StepEndNode(exec, (StepStartNode) an, an));
        }
        thread.head.markIfFail(context.getOutcome());
        return context.replay();
    } else {
        // if it's in progress, suspend it until we get invoked later.
        // when it resumes, the CPS caller behaves as if this method returned with the resume value
        Continuable.suspend(new ThreadTaskImpl(context));
        // so the execution will never reach here.
        throw new AssertionError();
    }
}
Also used : StepStartNode(org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode) LoadStep(org.jenkinsci.plugins.workflow.cps.steps.LoadStep) Step(org.jenkinsci.plugins.workflow.steps.Step) LoadStep(org.jenkinsci.plugins.workflow.cps.steps.LoadStep) ParallelStep(org.jenkinsci.plugins.workflow.cps.steps.ParallelStep) StepEndNode(org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode) StepAtomNode(org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) NoStaplerConstructorException(org.kohsuke.stapler.NoStaplerConstructorException) GroovyRuntimeException(groovy.lang.GroovyRuntimeException) IOException(java.io.IOException) MissingContextVariableException(org.jenkinsci.plugins.workflow.steps.MissingContextVariableException) ParallelStep(org.jenkinsci.plugins.workflow.cps.steps.ParallelStep) EnvVars(hudson.EnvVars) ArgumentsActionImpl(org.jenkinsci.plugins.workflow.cps.actions.ArgumentsActionImpl) Computer(hudson.model.Computer) MissingContextVariableException(org.jenkinsci.plugins.workflow.steps.MissingContextVariableException) FlowNode(org.jenkinsci.plugins.workflow.graph.FlowNode)

Example 2 with StepExecution

use of org.jenkinsci.plugins.workflow.steps.StepExecution in project workflow-cps-plugin by jenkinsci.

the class CpsBodyExecution method getCurrentExecutions.

@Override
public Collection<StepExecution> getCurrentExecutions() {
    CpsThread t;
    synchronized (this) {
        t = thread;
        if (t == null) {
            return Collections.emptySet();
        }
    }
    final SettableFuture<Collection<StepExecution>> result = SettableFuture.create();
    t.getExecution().runInCpsVmThread(new FutureCallback<CpsThreadGroup>() {

        @Override
        public void onSuccess(CpsThreadGroup g) {
            try {
                List<StepExecution> executions = new ArrayList<>();
                // cf. trick in CpsFlowExecution.getCurrentExecutions(true)
                Map<FlowHead, CpsThread> m = new LinkedHashMap<>();
                for (CpsThread t : g.threads.values()) {
                    m.put(t.head, t);
                }
                for (CpsThread t : m.values()) {
                    // TODO seems cumbersome to have to go through the flow graph to find out whether a head is a descendant of ours, yet FlowHead does not seem to retain a parent field
                    LinearBlockHoppingScanner scanner = new LinearBlockHoppingScanner();
                    scanner.setup(t.head.get());
                    for (FlowNode node : scanner) {
                        if (node.getId().equals(startNodeId)) {
                            // this head is inside this body execution
                            StepExecution execution = t.getStep();
                            if (execution != null) {
                                executions.add(execution);
                            }
                            break;
                        }
                    }
                }
                result.set(executions);
            } catch (Exception x) {
                result.setException(x);
            }
        }

        @Override
        public void onFailure(Throwable t) {
            result.setException(t);
        }
    });
    try {
        return result.get(1, TimeUnit.MINUTES);
    } catch (ExecutionException | InterruptedException | TimeoutException x) {
        // TODO access to CpsThreadGroup.threads must be restricted to the CPS VM thread, but the API signature does not allow us to return a ListenableFuture or throw checked exceptions
        throw new RuntimeException(x);
    }
}
Also used : LinearBlockHoppingScanner(org.jenkinsci.plugins.workflow.graphanalysis.LinearBlockHoppingScanner) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) FlowInterruptedException(org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) TimeoutException(java.util.concurrent.TimeoutException) IOException(java.io.IOException) FlowInterruptedException(org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) ExecutionException(java.util.concurrent.ExecutionException) Collection(java.util.Collection) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List) ExecutionException(java.util.concurrent.ExecutionException) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) FlowNode(org.jenkinsci.plugins.workflow.graph.FlowNode) TimeoutException(java.util.concurrent.TimeoutException)

Example 3 with StepExecution

use of org.jenkinsci.plugins.workflow.steps.StepExecution in project selenium_java by sergueik.

the class Audit2DbStepTests method testStartWithStepContextReturnsAudit2DbExecution.

@Test
public void testStartWithStepContextReturnsAudit2DbExecution() throws Exception {
    StepContext context = Mockito.mock(StepContext.class);
    Audit2DbStep step = new Audit2DbStep();
    StepExecution execution = step.start(context);
    assertNotNull("Null StepExecution returned.", execution);
    assertTrue("Expected Audit2DbExecution", Audit2DbExecution.class.isInstance(execution));
    assertThat("StepContext should be injected into execution.", execution, hasProperty("context", sameInstance(context)));
    assertThat("Step should be injected into execution.", execution, hasProperty("step", sameInstance(step)));
}
Also used : StepContext(org.jenkinsci.plugins.workflow.steps.StepContext) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) Test(org.junit.Test)

Example 4 with StepExecution

use of org.jenkinsci.plugins.workflow.steps.StepExecution in project workflow-cps-plugin by jenkinsci.

the class CpsStepContext method scheduleNextRun.

/**
 * When this step context has completed execution (successful or otherwise), plan the next action.
 */
private void scheduleNextRun() {
    if (syncMode) {
        // plan the next action.
        return;
    }
    try {
        final FlowNode n = getNode();
        final CpsFlowExecution flow = getFlowExecution();
        final List<FlowNode> parents = new ArrayList<FlowNode>();
        for (int head : bodyHeads) {
            FlowHead flowHead = flow.getFlowHead(head);
            if (flowHead != null) {
                parents.add(flowHead.get());
            } else {
                LOGGER.log(Level.WARNING, "Could not find flow head #{0}", head);
            }
        }
        flow.runInCpsVmThread(new FutureCallback<CpsThreadGroup>() {

            @CpsVmThreadOnly
            @Override
            public void onSuccess(CpsThreadGroup g) {
                g.unexport(body);
                body = null;
                CpsThread thread = getThread(g);
                if (thread != null) {
                    CpsThread nit = thread.getNextInner();
                    if (nit != null) {
                        // can't mark this done until the inner thread is done.
                        // defer the processing until the inner thread is done
                        nit.addCompletionHandler(new ScheduleNextRun());
                        if (getOutcome().isFailure()) {
                            // if the step with a currently running body reported a failure,
                            // make some effort to try to interrupt the running body
                            StepExecution s = nit.getStep();
                            if (s != null) {
                                // TODO: ideally this needs to work like interrupt, in that
                                // if s==null the next StepExecution gets interrupted when it happen
                                FlowInterruptedException cause = new FlowInterruptedException(Result.FAILURE, new BodyFailed());
                                cause.initCause(getOutcome().getAbnormal());
                                try {
                                    // TODO JENKINS-26148/JENKINS-34637 this is probably wrong: should interrupt the innermost execution
                                    // (the “next” one could be block-scoped, and we would want to interrupt all parallel heads)
                                    s.stop(cause);
                                } catch (Exception e) {
                                    LOGGER.log(Level.WARNING, "Failed to stop the body execution in response to the failure of the parent");
                                }
                            }
                        }
                        return;
                    }
                    if (n instanceof StepStartNode) {
                        // if there's no body to invoke, we want the current thread to be the sole head
                        if (parents.isEmpty())
                            parents.add(thread.head.get());
                        // clear all the subsumed heads that are joining. thread that owns parents.get(0) lives on
                        for (int i = 1; i < parents.size(); i++) g.getExecution().subsumeHead(parents.get(i));
                        StepEndNode en = new StepEndNode(flow, (StepStartNode) n, parents);
                        thread.head.setNewHead(en);
                        CpsFlowExecution.maybeAutoPersistNode(en);
                    }
                    thread.head.markIfFail(getOutcome());
                    thread.setStep(null);
                    thread.resume(getOutcome());
                }
            }

            /**
             * Program state failed to load.
             */
            @Override
            public void onFailure(Throwable t) {
            }
        });
    } catch (IOException x) {
        LOGGER.log(Level.FINE, null, x);
    }
}
Also used : StepStartNode(org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode) ArrayList(java.util.ArrayList) FlowInterruptedException(org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) StepEndNode(org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode) IOException(java.io.IOException) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) InvokerInvocationException(org.codehaus.groovy.runtime.InvokerInvocationException) IOException(java.io.IOException) FlowInterruptedException(org.jenkinsci.plugins.workflow.steps.FlowInterruptedException) ExecutionException(java.util.concurrent.ExecutionException) FlowNode(org.jenkinsci.plugins.workflow.graph.FlowNode)

Example 5 with StepExecution

use of org.jenkinsci.plugins.workflow.steps.StepExecution in project workflow-cps-plugin by jenkinsci.

the class CpsBodyExecutionTest method currentExecutions.

@Issue("JENKINS-34637")
@Test
public void currentExecutions() throws Exception {
    WorkflowJob p = jenkins.createProject(WorkflowJob.class);
    p.setDefinition(new CpsFlowDefinition("parallel main: {retainsBody {parallel a: {retainsBody {semaphore 'a'}}, b: {retainsBody {semaphore 'b'}}}}, aside: {semaphore 'c'}", true));
    WorkflowRun b = p.scheduleBuild2(0).waitForStart();
    SemaphoreStep.waitForStart("a/1", b);
    SemaphoreStep.waitForStart("b/1", b);
    SemaphoreStep.waitForStart("c/1", b);
    final RetainsBodyStep.Execution[] execs = new RetainsBodyStep.Execution[3];
    StepExecution.applyAll(RetainsBodyStep.Execution.class, new Function<RetainsBodyStep.Execution, Void>() {

        @Override
        public Void apply(RetainsBodyStep.Execution exec) {
            execs[exec.count] = exec;
            return null;
        }
    }).get();
    assertNotNull(execs[0]);
    assertNotNull(execs[1]);
    assertNotNull(execs[2]);
    final Set<SemaphoreStep.Execution> semaphores = new HashSet<>();
    StepExecution.applyAll(SemaphoreStep.Execution.class, new Function<SemaphoreStep.Execution, Void>() {

        @Override
        public Void apply(SemaphoreStep.Execution exec) {
            if (exec.getStatus().matches("waiting on [ab]/1")) {
                semaphores.add(exec);
            }
            return null;
        }
    }).get();
    assertThat(semaphores, Matchers.<SemaphoreStep.Execution>iterableWithSize(2));
    // A or B, does not matter
    Collection<StepExecution> currentExecutions1 = execs[1].body.getCurrentExecutions();
    assertThat(/* irritatingly, iterableWithSize does not show the collection in its mismatch message */
    currentExecutions1.toString(), currentExecutions1, Matchers.<StepExecution>iterableWithSize(1));
    Collection<StepExecution> currentExecutions2 = execs[2].body.getCurrentExecutions();
    assertThat(currentExecutions2, Matchers.<StepExecution>iterableWithSize(1));
    assertEquals(semaphores, Sets.union(Sets.newLinkedHashSet(currentExecutions1), Sets.newLinkedHashSet(currentExecutions2)));
    // the top-level one
    assertEquals(semaphores, Sets.newLinkedHashSet(execs[0].body.getCurrentExecutions()));
    execs[0].body.cancel();
    SemaphoreStep.success("c/1", null);
    jenkins.assertBuildStatus(Result.ABORTED, jenkins.waitForCompletion(b));
}
Also used : Function(com.google.common.base.Function) BodyExecution(org.jenkinsci.plugins.workflow.steps.BodyExecution) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) SemaphoreStep(org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep) WorkflowJob(org.jenkinsci.plugins.workflow.job.WorkflowJob) StepExecution(org.jenkinsci.plugins.workflow.steps.StepExecution) WorkflowRun(org.jenkinsci.plugins.workflow.job.WorkflowRun) HashSet(java.util.HashSet) Issue(org.jvnet.hudson.test.Issue) Test(org.junit.Test)

Aggregations

StepExecution (org.jenkinsci.plugins.workflow.steps.StepExecution)6 IOException (java.io.IOException)3 ArrayList (java.util.ArrayList)3 FlowNode (org.jenkinsci.plugins.workflow.graph.FlowNode)3 ExecutionException (java.util.concurrent.ExecutionException)2 StepEndNode (org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode)2 StepStartNode (org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode)2 FlowInterruptedException (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException)2 Test (org.junit.Test)2 Function (com.google.common.base.Function)1 ImmutableList (com.google.common.collect.ImmutableList)1 GroovyRuntimeException (groovy.lang.GroovyRuntimeException)1 EnvVars (hudson.EnvVars)1 Computer (hudson.model.Computer)1 Collection (java.util.Collection)1 HashSet (java.util.HashSet)1 LinkedHashMap (java.util.LinkedHashMap)1 List (java.util.List)1 Map (java.util.Map)1 TimeoutException (java.util.concurrent.TimeoutException)1