Add CD RolloutStep Model
What does this MR do?
Adds a Cd::RolloutStep model backed by a new cd_rollout_steps table. One row is created per node in a rollout's flow definition tree - including stage containers, not just leaf steps - addressable by a path/parent_path pair (e.g. "0", "0.0", "1"). This gives the flow visualization (and future step-execution workers) a place to read and write per-step execution state as a rollout progresses. States are pending/running/success/failed/skipped/cancelled for ordinary steps, or pending/awaiting_approval/approved/rejected/skipped/cancelled for approval-gate steps - the legal set depends on step_type, enforced by a model validation.
Rows are built and bulk-inserted synchronously when a rollout is created, inside the same transaction that already creates rollout_environments and deployments - not later at rollout start. A new Cd::RolloutSteps::Builder service turns a flow definition's step tree into unsaved Cd::RolloutStep records ready for bulk insert. Cd::ApplicationFlowDefinitions::Document gains a #steps_with_paths method that walks the step tree and returns every node (stage containers included) with its position path and parent path (nil at top level). Cd::Rollouts::CreateService now builds rollout_steps right after rollout_environments, and memoizes its flow-definition YAML parse (into one Document instance) since both steps need it within the same #execute call. Cd::Rollout and Cd::RolloutEnvironment each gain a rollout_steps has_many association.
Changes
- New
cd_rollout_stepstable: organization_id, rollout_id, rollout_environment_id (nullable, FK to cd_rollout_environments), timestamps, started_at, finished_at, state (smallint enum, default pending), path, parent_path, step_type, name, error, params (jsonb, JSON-schema-validated, holds step-specific config e.g. wait seconds, canary service weights) - Unique index on (rollout_id, path); separate FK migrations to organizations, cd_rollouts, cd_rollout_environments (one FK per migration, this codebase's convention)
- New
Cd::RolloutSteps::Builderservice builds unsavedCd::RolloutSteprecords from a flow definition's step tree - New
Cd::ApplicationFlowDefinitions::Document#steps_with_pathsreturns every tree node with its path/parent_path Cd::Rollouts::CreateServicebuilds rollout_steps alongside rollout_environments/deployments in one transaction; memoizes the parsed flowDocumentCd::Rollout/Cd::RolloutEnvironmentgainrollout_stepsassociations- Database dictionary entry
db/docs/cd_rollout_steps.yml, feature categorycontinuous_delivery
One new table, 4 migrations (create + 3 FK migrations). No existing tables or schema are touched, no breaking changes. Gated by the existing ai_native_deploy feature flag on the CdRolloutCreate GraphQL mutation - that flag is not new, not introduced here. This MR is backend/data-model only - nothing exposes rollout_steps via GraphQL or REST yet (the CdRolloutCreate mutation's response shape is unchanged); exposing the step tree in the API is a follow-up. This MR does not change flow-definition validation - an invalid or environment-less flow definition still fails rollout creation the same way as before, via the pre-existing InvalidFlowDefinitionError path.
How to test
1. Seed an organization, application, flow definition, and version set (Rails console)
Feature.enable(:ai_native_deploy)
org = Organizations::Organization.create!(path: "cd-rollout-steps-demo", name: "CD Rollout Steps Demo")
user = User.find_by(username: "root")
Organizations::OrganizationUser.create!(organization: org, user: user, access_level: Gitlab::Access::OWNER)
app = Cd::Application.create!(organization: org, name: "demo-app")
env = Cd::Environment.create!(organization: org, name: "production", tier: :production)
service = Cd::Service.create!(organization: org, application: app, name: "web")
Cd::EnvironmentDriverBinding.create!(organization: org, environment: env,
driver_ref: "argo-rollouts", driver_config: { "cluster_agent_id" => "1" })
flow_yaml = <<~YAML
steps:
- type: com.gitlab.cd.steps.stage
name: production
steps:
- type: com.gitlab.cd.argo.rolling.deploy
environment: production
services:
- name: web
- type: com.gitlab.cd.steps.wait
seconds: 30
YAML
Cd::ApplicationFlowDefinition.create!(application: app, organization_id: org.id, definition: flow_yaml)
artifact_source = Cd::ArtifactSource.create!(organization: org, service: service,
name: "web-image", source_ref: "registry.example.com/web", source_config: {})
version = Cd::Version.create!(organization: org, artifact_source: artifact_source, name: "v1_0_0")
vset = Cd::VersionSet.create!(organization: org, application: app, name: "1.0.0")
Cd::VersionSetEntry.create!(organization: org, version_set: vset, version: version)
puts "organization_gid=#{org.to_global_id}"
puts "version_set_gid=#{vset.to_global_id}"2. Run the CdRolloutCreate mutation (GraphQL Explorer at /-/graphql-explorer)
mutation {
cdRolloutCreate(input: {
organizationId: "<organization_gid from step 1>",
versionSetId: "<version_set_gid from step 1>"
}) {
rollout {
id
iid
state
}
errors
}
}Expected (verified live against a running GDK): the mutation succeeds and returns the created rollout. rollout_steps are not yet exposed in the API, so verify them separately via Rails console.
Sample response (cdRolloutCreate success):
{
"data": {
"cdRolloutCreate": {
"rollout": {
"id": "gid://gitlab/Cd::Rollout/78",
"iid": 1,
"state": "PENDING"
},
"errors": []
}
}
}Sample response (rollout_steps, via rollout.rollout_steps.each { |s| puts [s.path, s.parent_path, s.step_type, s.state, s.name, s.params, s.rollout_environment_id].inspect } in a Rails console):
["0", nil, "com.gitlab.cd.steps.stage", "pending", "production", nil, nil]
["0.0", "0", "com.gitlab.cd.argo.rolling.deploy", "pending", nil, {"services"=>[{"name"=>"web"}]}, 66]
["1", nil, "com.gitlab.cd.steps.wait", "pending", nil, {"seconds"=>30}, nil]The stage container has no environment link, its nested deploy step links to the rollout_environment it targets, the standalone wait step also has no environment link, and all three start pending.
References
https://gitlab.com/gitlab-org/gitlab/-/work_items/610547
Screenshots or screen recordings
No UI change - backend/data-model only.
How to set up and validate locally
- Run the seed script above in a Rails console.
- Run the
cdRolloutCreatemutation in GraphQL Explorer and confirm the rollout is returned. - In a Rails console, confirm
rollout.rollout_steps.countmatches the number of tree nodes in the flow definition and each step ispending. - Note that flow-definition validation is unchanged: an invalid or environment-less flow definition still fails rollout creation the same pre-existing way.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.