Backend: Split Ci::PipelineTriggerService in 2 separate services
Current State
The Ci::PipelineTriggerService
today has 2 main responsibilities:
- if the service is called with a Trigger Token, it creates a new pipeline and associates the trigger request
- if the service is called with a Job Token, it creates a downstream pipeline
Problem
-
Ci::PipelineTriggerService
has 2 different responsibilities that could go separate ways instead. - The way new pipelines are created are very different
- Creating a pipeline from a Job Token is extremely similar to what
Ci::CreateDownstreamPipelineService
does. The main difference is that the latter service takes in input abridge
job. This also highlights that the creation of a pipeline with Job Token is a missed opportunity to generalize this behavior. -
Ci::CreateDownstreamPipelineService
introduces some limits in the size of pipeline hierarchy. These limits are not taken in consideration when using Job Token inCi::PipelineTriggerService
. -
Ci::PipelineTriggerService
also deals with logic that is coupled to the HTTP/delivery layer and not really business logic: e.g. settingpayload_variable
and application context metadata
Solution
-
Split the service in 2 - This will highlight the difference between triggering a pipeline (run a pipeline as-is) vs creating a downstream pipeline (which links the new pipeline to the source job/bridge).
-
Reuse
Ci::PipelineTriggerService
purely for pipeline triggers with trigger token. -
Reuse
Ci::CreateDownstreamPipelineService
to take care of the strategy using job token. -
Move remaining logic up into the Grape endpoint
Proposal
This is an idea of what the end result could look like:
post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
# ...
token = params[:token].to_s
token_result = Ci::Trigger.find_by_token(token) || Ci::AuthJobFinder.new(token: token).execute
not_found! unless token_result
param_variables = params[:variables].to_h.map { |k, v| { key: k, value: v } }
payload_variable = { key: 'TRIGGER_PEYLOAD', value: params.except(:token).to_json, variable_type: file }
variables = param_variables + [payload_variable]
case token_result
when Ci::Trigger
# set context data
result = Ci::PipelineTriggerService.new(...).execute(token_result, variables: variables)
when Ci::Build
# set context data
result = Ci::CreateDownstreamPipelineService.new(...).execute(token_result, variables: variables)
end
# ...
end
Tradeoffs:
- while this pushes some responsibilities up into the Grape endpoint it also highlights that we use the same endpoint for 2 different use-cases. Before this was hidden inside the
PipelineTriggerService
.
Edited by Mark Nuzzo