Jsonnet libraries that generate GoCD pipelines for getsentry services.
You need go-jsonnet, jsonnet-bundler, and yq to use these libraries in Sentry repos:
brew install go-jsonnet jsonnet-bundler yqInstall the libraries with jsonnet-bundler.
Replace v3.0.10 with the latest tag from the releases page:
jb init
jb install https://github.com/getsentry/gocd-jsonnet.git/libs@v3.0.10To upgrade, change the version in your jsonnetfile.json, then run jb update.
pipedream.render() reads the external variable output-files, so you must pass it on every build:
--ext-code output-files=<true|false>With output-files=true, the output has one file per pipeline:
{
"example.yaml": {
"format_version": 10,
"pipelines": {
"deploy-example": {...}
}
},
"example-us.yaml": {
"format_version": 10,
"pipelines": {
"deploy-example-us": {...}
}
}
}Use it with the jsonnet -m flag to write one file per pipeline, which is easier to review:
jsonnet --ext-code output-files=true -m ./generated-pipelines ./example.jsonnetThe GoCD jsonnet plugin cannot write multiple files.
GoCD is configured with --ext-code output-files=false, which outputs all pipelines in one object:
{
"format_version": 10,
"pipelines": {
"deploy-example": {...},
"deploy-example-us": {...}
}
}A pipedream is the deployment process for a Sentry service that deploys to multiple regions.
pipedream.render(config, pipeline_fn, parallel=false) generates its pipelines.
pipeline_fn(region) returns a GoCD pipeline definition for one region.
Regions are organized into groups in libs/getsentry.libsonnet.
The groups deploy in this order: s4s2, de, us, control, snty-tools, st.
render() generates these pipelines:
deploy-<name>: a trigger pipeline with one manual-approval stage.render()adds it only whenauto_deployisfalse.deploy-<name>-<group>: one pipeline per group. Each region in the group runs as a parallel job.rollback-<name>: generated only when you setrollback.
render() skips a group when all of its regions are excluded.
Group pipelines run one after the other by default.
With parallel=true, every group pipeline starts from the trigger pipeline instead.
Each group pipeline ends with a pipeline-complete stage.
The stage stops a rollback from re-triggering the downstream pipelines.
| Key | Required | Description |
|---|---|---|
name |
Yes | Service name, used in every pipeline name and as the GoCD pipeline group. |
materials |
No | Materials for the trigger pipeline. Required when auto_deploy is false. |
auto_deploy |
No | Defaults to true. Set to false to add the deploy-<name> trigger pipeline. |
auto_pipeline_progression |
No | Defaults to true. Set to false to add ready and wait stages, so each group waits for manual approval. |
exclude_regions |
No | Regions to leave out of the pipedream. |
include_regions |
No | Regions that are excluded by default to opt in to. control and snty-tools are excluded by default. |
rollback |
No | Adds the rollback-<name> pipeline. See Rollback. |
render() fails the build when the pipelines from pipeline_fn break these rules within a group:
- Each stage object has exactly one key. A missing comma between two stages merges them into one object.
- Pipeline attributes other than
stagesandenvironment_variablesare the same for every region. - Stage attributes other than
jobsandenvironment_variablesare the same for every region.
In groups with more than one region, render() appends -<region> to each job name.
It skips the suffix when the job name already ends with -<region>.
You can set environment_variables at the pipeline, stage, or job level in pipeline_fn.
A variable with the same value in every region of a group stays at its original level.
render() moves a region-specific variable down to the job level.
GoCD resolves precedence as job, then stage, then pipeline.
Each group pipeline has a PIPEDREAM_GROUP_REGIONS variable that lists the group's regions.
To deploy to a subset, override PIPEDREAM_GROUP_REGIONS on the Environment Variables tab of "Trigger with options" in GoCD.
For example, deploy-example-st defaults to customer-1,customer-2,customer-7:
| Override value | Result |
|---|---|
| (no override) | All three regions deploy |
customer-1 |
Only customer-1 deploys |
customer-1,customer-2 |
Only customer-1 and customer-2 deploy |
The override works only when these conditions hold:
- The group has more than one region.
- The job's tasks are
scripttasks. - The job sets
SENTRY_REGIONto its region.render()does not setSENTRY_REGIONfor you.
If the override matches none of the group's regions, every job skips and the pipeline succeeds without deploying. Trigger the pipeline again with a valid value.
| Key | Required | Description |
|---|---|---|
material_name |
Yes | The material name that every region pipeline uses, for example getsentry_repo. |
stage |
Yes | The deploy stage that the rollback re-runs. |
elastic_profile_id |
Yes | The elastic agent profile that runs the rollback jobs. |
final_pipeline |
No | The group whose pipeline decides which revisions you can roll back to. Defaults to the last group. |
final_stage |
No | The stage on that pipeline that the rollback material watches. Defaults to its last stage, pipeline-complete. |
render() fails the build when stage or final_stage does not exist on the final pipeline, or when final_pipeline names no generated group.
Set final_stage to an earlier stage, for example deploy-primary, so a revision becomes eligible before the trailing pipeline-complete stage runs.
Set final_pipeline to an earlier group, for example us, when a flaky last group leaves few revisions to roll back to.
A revision from an earlier group may not have deployed to the later groups that the rollback then deploys it to.
The rollback pipeline has three stages:
pause_pipelineswaits for manual approval, then pauses and cancels every pipeline in the pipedream.start_rollbackdeploys the chosen revision by re-runningstageon each group pipeline.incident_resolvedwaits for manual approval, then unpauses and unlocks the pipelines.
local pipedream = import 'github.com/getsentry/gocd-jsonnet/libs/pipedream.libsonnet';
local pipedream_config = {
name: 'example',
materials: {
init_repo: {
git: 'git@github.com:getsentry/init.git',
shallow_clone: true,
branch: 'master',
destination: 'init',
},
},
rollback: {
material_name: 'example_repo',
stage: 'example_stage',
elastic_profile_id: 'example_profile',
},
auto_deploy: false,
auto_pipeline_progression: false,
exclude_regions: [],
};
local sample = {
pipeline(region):: {
materials: {
example_repo: {
git: 'git@github.com:getsentry/example.git',
shallow_clone: true,
branch: 'master',
destination: 'example',
},
},
stages: [
{
example_stage: {
jobs: {
deploy: {
elastic_profile_id: 'example_profile',
environment_variables: { SENTRY_REGION: region },
tasks: [
{ script: './deploy.sh --region=' + region },
],
},
},
},
},
],
},
};
pipedream.render(pipedream_config, sample.pipeline)local gocdtasks = import 'github.com/getsentry/gocd-jsonnet/libs/gocd-tasks.libsonnet';
local tasks = [
// A task that does nothing. GoCD requires at least one task per job.
gocdtasks.noop,
// A script task: { script: "echo 'hello world'" }
gocdtasks.script("echo 'hello world'"),
// A script task from a file
gocdtasks.script(importstr './bash/example.sh'),
];gocdtasks.script() doubles a # at the start of each line, because GoCD treats a single # as a parameter reference.
gocdstages.basic(name, tasks, opts={}) returns a stage with one job.
The stage and the job both use name.
local gocdstages = import 'github.com/getsentry/gocd-jsonnet/libs/gocd-stages.libsonnet';
local gocdtasks = import 'github.com/getsentry/gocd-jsonnet/libs/gocd-tasks.libsonnet';
[
gocdstages.basic('example-stage', [gocdtasks.noop]),
// Waits for manual approval
gocdstages.basic('example-stage', [gocdtasks.noop], { approval: 'manual' }),
// Runs only when the previous stage succeeded
gocdstages.basic('example-stage', [gocdtasks.noop], { approval: 'success' }),
// Skips fetching materials
gocdstages.basic('example-stage', [gocdtasks.noop], { fetch_materials: false }),
]See AGENTS.md for the build and test commands, and docs/releasing.md to release a new version.