Skip to content

Inject Pipeline Primitives

Pipeline Primitives

Pipeline Primitives are objects that can be instantiated and "injected" into a JTE pipeline's execution to create concrete implementations of resources used in a Pipeline Template.

Each primitive extends TemplatePrimitive which is a Serializable GlobalVariable to ensure they can be persisted and discovered.

Primitives That Execute Pipeline Code

Pipeline Primitives that execute pipeline code, such as Library Steps (StepWrapper) or Stages (Stage), should create a separate class in the resources directory and override the getValue(..) method such that what's actually returned to the pipeline is a CPS-transformed class.

TemplatePrimitiveInjector

Each Pipeline Primitive has a corresponding TemplatePrimitiveInjector that's responsible for parsing the Aggregated Pipeline Configuration and populating a TemplatePrimitiveNamespace holding the created TemplatePrimitves.

Class Structure

classDiagram
  class TemplatePrimitiveInjector{
    <<abstract>>
    orchestrate(CpsFlowExecution exec, PipelineConfigurationObject config)$
    +validateConfiguration(CpsFlowExecution exec, PipelineConfigurationObject config)
    +injectPrimitives(CpsFlowExecution exec, PipelineConfigurationObject config) TemplatePrimitiveNamespace
    +validatePrimitives(CpsFlowExecution exec, PipelineConfigurationObject config, TemplatePrimitiveCollector collector)
  }
  class TemplatePrimitiveNamespace{
    +String name
    +List~TemplatePrimitive~ primitives
    +TemplatePrimitiveNamespace parent
    +getProperty(String property) TemplatePrimitive
    +methodMissing(String methodName, Object args) TemplatePrimitive
  }
  TemplatePrimitiveInjector "0" ..> "many"TemplatePrimitiveNamespace : produces
  class TemplatePrimitiveCollector{
    +List~TemplatePrimitiveNamespace~ namespaces
  }
  TemplatePrimitiveNamespace --o TemplatePrimitiveCollector
  class TemplatePrimitiveProvider{
    +forRun(Run run) List~GlobalVariable~
  }
  TemplatePrimitiveProvider .. TemplatePrimitiveCollector : inner class
  class TemplatePrimitive{
    +String name
    +TemplatePrimitiveNamespace parent
    +getValue(CpsScript script) TemplatePrimitive
  }
  TemplatePrimitive ..> TemplatePrimitiveNamespace : populates

Functional Interface

There are three phases to Pipeline Primitive Injection that are mapped to three methods a TemplatePrimitiveInjector can override.

Phase Description
validateConfiguration Receives the Aggregated Pipeline Configuration so that syntax validation can occur
injectPrimitives Where injectors should instantiate Pipeline Primitives based on the Aggregated Pipeline Configuration
validatePrimitives Where validations should occur on the Pipeline Primitives that have been instantiated

Pipeline Primitive Resolution

These TemplatePrimitiveNamespaces are collected in the TemplatePrimitiveCollector, which is persisted as an InvisibleAction on the Pipeline Run.

TemplatePrimitiveCollector, then, has a subclass called TemplatePrimitiveProvider that extends GlobalVariableSet.

When a Jenkins pipeline tries to resolve a method (in CpsScript.invokeMethod()) it will check for the GlobalVariables available to the run and resolve the Pipeline Primitives that have been created.

flowchart LR
  script[Jenkins Pipeline]
  subgraph CpsScript
  invokeMethod
  end
  script-->|invoke|CpsScript
  subgraph GlobalVariable
  byName --> forRun
  forRun --> GlobalVariableSet
  end
  invokeMethod --> GlobalVariable
  subgraph JTE
  TemplatePrimitiveProvider --> TemplatePrimitiveCollector
  TemplatePrimitiveCollector --> TemplatePrimitiveNamespace
  TemplatePrimitiveNamespace --> TemplatePrimitive
  end
  GlobalVariableSet-->JTE

Injector Sequencing

Each TemplatePrimitiveCollector can annotate the three methods of Pipeline Primitive Injection with a @RunAfter annotation that accepts the TemplatePrimitiveInjectors that this injector should run after.

This approach has been easier to handle than ordinals for sequencing the injectors.

When TemplatePrimitiveInjector.orchestrate() is called, each phase of Pipeline Primitive Injection is called in sequence.

For each phase, a Directed Acyclic Graph of TemplatePrimitiveInjectors is created based on the dependencies defined by @RunAfter annotations.

The injectors are then execution in the order determined by the graph.

For example, for the injectPrimitives phase, the graph looks like:

flowchart LR
  root --> ApplicationEnvironmentInjector
  root --> KeywordInjector
  root --> LibraryStepInjector
  LibraryStepInjector --> DefaultStepInjector
  DefaultStepInjector --> TemplateMethodInjector
  TemplateMethodInjector --> StageInjector
Back to top