Class CpsFlowExecution

  • All Implemented Interfaces:
    org.jenkinsci.plugins.workflow.flow.BlockableResume, org.jenkinsci.plugins.workflow.graph.FlowActionStorage, org.jenkinsci.plugins.workflow.graph.GraphLookupView

    public class CpsFlowExecution
    extends org.jenkinsci.plugins.workflow.flow.FlowExecution
    implements org.jenkinsci.plugins.workflow.flow.BlockableResume
    FlowExecution implemented with Groovy CPS.

    State Transition

    CpsFlowExecution goes through the following states:

    
                                        +----------------------+
                                        |                      |
                                        v                      |
     PERSISTED --> PREPARING --> SUSPENDED --> RUNNABLE --> RUNNING --> COMPLETE
                                                 ^
                                                 |
                                               INITIAL
     
    INITIAL
    When a new CpsFlowExecution is created, it starts from here. When start() method is called, we get one thread scheduled, and we arrive at RUNNABLE state.
    PERSISTED
    CpsFlowExecution is on disk with its owner, for example in build.xml of the workflow run. Nothing exists in memory. For example, Jenkins is not running. Transition from this into PREPARING is triggered outside our control by XStream using CpsFlowExecution.ConverterImpl to unmarshal CpsFlowExecution. FlowExecution.onLoad() is called at the end, and we arrive at the PREPARING state.
    PREPARING
    CpsFlowExecution is in memory, but CpsThreadGroup isn't. We are trying to restore all the ephemeral pickles that are necessary to get workflow going again. programPromise represents a promise of completing this state. PickleResolver keeps track of this, and when it's all done, we arrive at SUSPENDED state.
    SUSPENDED
    CpsThreadGroup is in memory, but all CpsThreads are not runnable, which means they are waiting for some conditions to trigger (such as a completion of a shell script that's executing, human approval, etc). CpsFlowExecution and CpsThreadGroup are safe to persist. When a condition is met, CpsThread.resume(Outcome) is called, and that thread becomes runnable, and we move to the RUNNABLE state.
    RUNNABLE
    Some of CpsThreads are runnable, but we aren't actually running. The conditions that triggered CpsThread is captured in CpsThread.resumeValue. As we get into this state, CpsThreadGroup.scheduleRun() should be called to schedule the execution. CpsFlowExecution and CpsThreadGroup are safe to persist in this state, just like in the SUSPENDED state. When CpsThreadGroup.runner allocated a real Java thread to the execution, we move to the RUNNING state.
    RUNNING
    A thread is inside CpsThreadGroup.run() and is actively mutating the object graph inside the script. This state continues until no threads are runnable any more. Only one thread executes CpsThreadGroup.run(). In this state, CpsFlowExecution still need to be persistable (because generally we don't get to control when it is persisted), but CpsThreadGroup isn't safe to persist. When the Java thread leaves CpsThreadGroup.run(), we move to the SUSPENDED state.
    COMPLETE
    All the CpsThreads have terminated and there's nothing more to execute, and there's no more events to wait. The result is finalized and there's no further state change.
    Author:
    Kohsuke Kawaguchi
    • Field Detail

      • OPTIMIZE_STORAGE_UPON_COMPLETION

        public static boolean OPTIMIZE_STORAGE_UPON_COMPLETION
        If true, then when the execution completes, we migrate the flow node storage from SimpleXStreamFlowNodeStorage to BulkFlowNodeStorage.
      • programPromise

        public transient volatile com.google.common.util.concurrent.ListenableFuture<CpsThreadGroup> programPromise
        Loading of the program is asynchronous because it requires us to re-obtain stateful objects. This object represents a Future for filling in CpsThreadGroup. TODO: provide a mechanism to diagnose how far along this process is.
        See Also:
        runInCpsVmThread(FutureCallback)
    • Constructor Detail

      • CpsFlowExecution

        public CpsFlowExecution​(@NonNull
                                String script,
                                boolean sandbox,
                                @NonNull
                                org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner,
                                @CheckForNull
                                org.jenkinsci.plugins.workflow.flow.FlowDurabilityHint durabilityHint)
                         throws IOException
        Throws:
        IOException
      • CpsFlowExecution

        public CpsFlowExecution​(String script,
                                boolean sandbox,
                                org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner)
                         throws IOException
        Throws:
        IOException
    • Method Detail

      • isResumeBlocked

        public boolean isResumeBlocked()
        If true, pipeline is forbidden to resume even if it can.
        Specified by:
        isResumeBlocked in interface org.jenkinsci.plugins.workflow.flow.BlockableResume
      • setResumeBlocked

        public void setResumeBlocked​(boolean resumeBlocked)
        Specified by:
        setResumeBlocked in interface org.jenkinsci.plugins.workflow.flow.BlockableResume
      • getShell

        public groovy.lang.GroovyShell getShell()
        Returns a groovy compiler used to load the script.
        See Also:
        "doc/classloader.md", GroovyShell.getClassLoader()
      • getTrustedShell

        public groovy.lang.GroovyShell getTrustedShell()
        Returns a groovy compiler used to load the trusted script.
        See Also:
        "doc/classloader.md"
      • getStorage

        public org.jenkinsci.plugins.workflow.support.storage.FlowNodeStorage getStorage()
      • getScript

        public String getScript()
      • isSandbox

        public boolean isSandbox()
        True if executing with groovy-sandbox, false if executing with approval.
      • getOwner

        public org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner getOwner()
        Specified by:
        getOwner in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • start

        public void start()
                   throws IOException
        Specified by:
        start in class org.jenkinsci.plugins.workflow.flow.FlowExecution
        Throws:
        IOException
      • iotaStr

        @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class)
        public String iotaStr()
        Assigns a new ID.
      • iota

        @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class)
        public int iota()
      • canResume

        public boolean canResume()
        If true, we are allowed to resume the build because resume is enabled AND we shut down cleanly.
      • onLoad

        public void onLoad​(org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner owner)
                    throws IOException
        Overrides:
        onLoad in class org.jenkinsci.plugins.workflow.flow.FlowExecution
        Throws:
        IOException
      • loadProgramAsync

        public void loadProgramAsync​(File programDataFile)
        Deserializes CpsThreadGroup from getProgramDataFile() if necessary. This moves us into the PREPARING state.
        Parameters:
        programDataFile -
      • afterStepExecutionsResumed

        protected void afterStepExecutionsResumed()
        Overrides:
        afterStepExecutionsResumed in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • blocksRestart

        public boolean blocksRestart()
        See JENKINS-22941 for why this exists.
        Overrides:
        blocksRestart in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • getFlowHead

        @CheckForNull
        public org.jenkinsci.plugins.workflow.cps.FlowHead getFlowHead​(int id)
      • getCurrentHeads

        public List<org.jenkinsci.plugins.workflow.graph.FlowNode> getCurrentHeads()
        Specified by:
        getCurrentHeads in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • getCurrentExecutions

        public com.google.common.util.concurrent.ListenableFuture<List<org.jenkinsci.plugins.workflow.steps.StepExecution>> getCurrentExecutions​(boolean innerMostOnly)
        Overrides:
        getCurrentExecutions in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • getThreadDump

        public CpsThreadDump getThreadDump()
        Synchronously obtain the current state of the workflow program.

        The workflow can be already completed, or it can still be running.

      • isCurrentHead

        public boolean isCurrentHead​(org.jenkinsci.plugins.workflow.graph.FlowNode n)
        Specified by:
        isCurrentHead in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • addListener

        public void addListener​(org.jenkinsci.plugins.workflow.flow.GraphListener listener)
        Specified by:
        addListener in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • removeListener

        public void removeListener​(org.jenkinsci.plugins.workflow.flow.GraphListener listener)
        Overrides:
        removeListener in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • getNode

        public org.jenkinsci.plugins.workflow.graph.FlowNode getNode​(String id)
                                                              throws IOException
        Specified by:
        getNode in class org.jenkinsci.plugins.workflow.flow.FlowExecution
        Throws:
        IOException
      • setResult

        public void setResult​(Result v)
      • getResult

        public Result getResult()
      • loadActions

        public List<Action> loadActions​(org.jenkinsci.plugins.workflow.graph.FlowNode node)
                                 throws IOException
        Specified by:
        loadActions in interface org.jenkinsci.plugins.workflow.graph.FlowActionStorage
        Throws:
        IOException
      • saveActions

        public void saveActions​(org.jenkinsci.plugins.workflow.graph.FlowNode node,
                                List<Action> actions)
                         throws IOException
        Specified by:
        saveActions in interface org.jenkinsci.plugins.workflow.graph.FlowActionStorage
        Throws:
        IOException
      • maybeAutoPersistNode

        public static void maybeAutoPersistNode​(@NonNull
                                                org.jenkinsci.plugins.workflow.graph.FlowNode node)
        Invoke me to toggle autopersist back on for steps that delay it.
      • isComplete

        public boolean isComplete()
        Overrides:
        isComplete in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • getAuthentication

        public Authentication getAuthentication()
        Specified by:
        getAuthentication in class org.jenkinsci.plugins.workflow.flow.FlowExecution
      • getNextScriptName

        @Restricted(org.kohsuke.accmod.restrictions.NoExternalUse.class)
        public String getNextScriptName​(String path)
        Finds the expected next loaded script name, like Script1.
        Parameters:
        path - a file path being loaded (currently ignored)
      • isDoneFlagSet

        public boolean isDoneFlagSet()
        Has the execution been marked done - note that legacy builds may not have that flag persisted, in which case we look for a single FlowEndNode head (see: isComplete() and FlowExecution.isComplete())
      • isPaused

        public boolean isPaused()
      • pause

        public void pause​(boolean v)
                   throws IOException
        Pause or unpause the execution.
        Parameters:
        v - true to pause, false to unpause.
        Throws:
        IOException
      • suspendAll

        @Restricted(org.kohsuke.accmod.restrictions.DoNotUse.class)
        @Terminator(attains="FlowExecutionList.EXECUTIONS_SUSPENDED")
        public static void suspendAll()