Skip to content

Commit 6c5aad3

Browse files
Merge pull request #1432 from Shopify/ts/support-explicit-rollback-variables
Allow rollbacks to specify their own variables
1 parent 85442aa commit 6c5aad3

File tree

10 files changed

+128
-8
lines changed

10 files changed

+128
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
* Allow explicit configuration of rollback variables (#1432)
4+
35
# 0.41.1
46

57
* Fix commit statuses (#1424)

README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,38 @@ rollback:
352352
```
353353
<br>
354354

355+
You can also accept custom environment variables defined by the user that triggers the rollback:
356+
357+
**<code>rollback.variables</code>** contains an array of variable definitions.
358+
359+
For example:
360+
361+
```yaml
362+
rollback:
363+
variables:
364+
-
365+
name: RUN_MIGRATIONS
366+
title: Run database migrations on rollback
367+
default: 1
368+
```
369+
<br>
370+
371+
**<code>rollback.variables.select</code>** will turn the input into a `<select>` of values.
372+
373+
For example:
374+
375+
```yaml
376+
rollback:
377+
variables:
378+
-
379+
name: REGION
380+
title: Run a rollback in a given region
381+
select:
382+
- east
383+
- west
384+
- north
385+
```
386+
<br>
355387

356388
**<code>fetch</code>** contains an array of the shell commands that Shipit executes to check the revision of the currently-deployed version. This key defaults to `disabled`.
357389

@@ -362,7 +394,7 @@ fetch:
362394
```
363395

364396
**Note:** Currently, deployments in emergency mode are configured to occur concurrently via [the `build_deploy` method](https://github.com/Shopify/shipit-engine/blob/main/app/models/shipit/stack.rb),
365-
whose `allow_concurrency` keyword argument defaults to `force`, where `force` is true when emergency mode is enabled.
397+
whose `allow_concurrency` keyword argument defaults to `force`, where `force` is true when emergency mode is enabled.
366398
If you'd like to separate these two from one another, override this method as desired in your service.
367399

368400
<h3 id="kubernetes">Kubernetes</h3>

app/controllers/shipit/api/rollbacks_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def create
1515
commit = stack.commits.by_sha(params.sha) || param_error!(:sha, 'Unknown revision')
1616
param_error!(:force, "Can't rollback a locked stack") if !params.force && stack.locked?
1717
deploy = stack.deploys.find_by(until_commit: commit) || param_error!(:sha, 'Cant find associated deploy')
18-
deploy_env = stack.filter_deploy_envs(params.env)
18+
rollback_env = stack.filter_rollback_envs(params.env)
1919

2020
response = nil
2121
if !params.force && stack.active_task?
@@ -25,7 +25,7 @@ def create
2525
active_task.abort!(aborted_by: current_user, rollback_once_aborted_to: deploy, rollback_once_aborted: true)
2626
response = active_task
2727
else
28-
response = deploy.trigger_rollback(current_user, env: deploy_env, force: params.force, lock: params.lock)
28+
response = deploy.trigger_rollback(current_user, env: rollback_env, force: params.force, lock: params.lock)
2929
end
3030

3131
render_resource(response, status: :accepted)

app/controllers/shipit/rollbacks_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def load_deploy
2727
end
2828

2929
def rollback_params
30-
params.require(:rollback).permit(:parent_id, env: @stack.deploy_variables.map(&:name))
30+
params.require(:rollback).permit(:parent_id, env: @stack.rollback_variables.map(&:name))
3131
end
3232
end
3333
end

app/models/shipit/deploy_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ def rollback_steps!
139139
rollback_steps || cant_detect!(:rollback)
140140
end
141141

142+
def rollback_variables
143+
if config('rollback', 'variables').nil?
144+
# For backwards compatibility, fallback to using deploy_variables if no explicit rollback variables are set
145+
deploy_variables
146+
else
147+
Array.wrap(config('rollback', 'variables')).map(&VariableDefinition.method(:new))
148+
end
149+
end
150+
142151
def retries_on_rollback
143152
config('rollback', 'retries') { nil }
144153
end
@@ -166,6 +175,10 @@ def filter_deploy_envs(env)
166175
EnvironmentVariables.with(env).permit(deploy_variables)
167176
end
168177

178+
def filter_rollback_envs(env)
179+
EnvironmentVariables.with(env).permit(rollback_variables)
180+
end
181+
169182
def review_checklist
170183
(config('review', 'checklist') || discover_review_checklist || []).map(&:strip).select(&:present?)
171184
end

app/models/shipit/deploy_spec/file_system.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ def cacheable_config
7878
},
7979
'rollback' => {
8080
'override' => rollback_steps,
81-
'retries' => retries_on_rollback
81+
'retries' => retries_on_rollback,
82+
'variables' => rollback_variables.map(&:to_h)
8283
},
8384
'fetch' => fetch_deployed_revision_steps,
8485
'tasks' => cacheable_tasks

app/models/shipit/rollback.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def report_complete!
3838
complete!
3939
end
4040

41+
def variables
42+
stack.rollback_variables
43+
end
44+
4145
private
4246

4347
def update_release_status

app/models/shipit/stack.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -519,8 +519,9 @@ def self.from_param!(param)
519519
end
520520

521521
delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
522-
:blocking_statuses, :deploy_variables, :filter_task_envs, :filter_deploy_envs,
523-
:maximum_commits_per_deploy, :pause_between_deploys, :retries_on_deploy, :retries_on_rollback,
522+
:blocking_statuses, :deploy_variables, :rollback_variables, :filter_task_envs, :filter_deploy_envs,
523+
:filter_rollback_envs, :maximum_commits_per_deploy, :pause_between_deploys, :retries_on_deploy,
524+
:retries_on_rollback,
524525
to: :cached_deploy_spec
525526

526527
def monitoring?

test/models/deploy_spec_test.rb

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,31 @@ class DeploySpecTest < ActiveSupport::TestCase
243243
assert_equal ["kubernetes-deploy --max-watch-seconds 900 foo bar"], @spec.rollback_steps
244244
end
245245

246+
test "#rollback_variables returns an empty array by default" do
247+
assert_equal [], @spec.rollback_variables
248+
end
249+
250+
test "#rollback_variables returns an array of VariableDefinition instances" do
251+
@spec.stubs(:load_config).returns('rollback' => { 'variables' => [{
252+
'name' => 'SAFETY_DISABLED',
253+
'title' => 'Set to 1 to do dangerous things',
254+
'default' => 0
255+
}] })
256+
257+
assert_equal 1, @spec.rollback_variables.size
258+
variable_definition = @spec.rollback_variables.first
259+
assert_equal 'SAFETY_DISABLED', variable_definition.name
260+
end
261+
262+
test "#rollback_variables falls back to deploy_variables if rollback_variables itself is empty" do
263+
@spec.stubs(:load_config).returns('deploy' => { 'variables' => [{
264+
'name' => 'SAFETY_DISABLED',
265+
'title' => 'Set to 1 to do dangerous things',
266+
'default' => 0
267+
}] })
268+
assert_equal @spec.rollback_variables.map(&:to_h), @spec.deploy_variables.map(&:to_h)
269+
end
270+
246271
test "#discover_task_definitions include a kubernetes restart command if `kubernetes` is present" do
247272
@spec.stubs(:load_config).returns(
248273
'kubernetes' => {
@@ -381,7 +406,8 @@ class DeploySpecTest < ActiveSupport::TestCase
381406
},
382407
'rollback' => {
383408
'override' => nil,
384-
'retries' => nil
409+
'retries' => nil,
410+
'variables' => []
385411
},
386412
'fetch' => nil,
387413
'tasks' => {}

test/models/shipit/deploy_spec/file_system_test.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,34 @@ class FileSystemTest < ActiveSupport::TestCase
9393
assert_not loaded_config.include?(Shipit::DeploySpec::FileSystem::SHIPIT_CONFIG_INHERIT_FROM_KEY)
9494
end
9595

96+
test '#load_config accurately deserializes deploy variables' do
97+
Shipit.expects(:respect_bare_shipit_file?).returns(true).at_least_once
98+
stack = shipit_stacks(:shipit)
99+
deploy_spec = Shipit::DeploySpec::FileSystem.new(Dir.tmpdir, stack)
100+
deploy_spec.expects(:config_file_path).returns("#{Pathname.new(Dir.tmpdir)}/shipit.yml").at_least_once
101+
deploy_spec.expects(:read_config).returns(SafeYAML.load(deploy_spec_with_variables_yaml))
102+
103+
deploy_vars = deploy_spec.deploy_variables
104+
assert_equal 1, deploy_vars.length
105+
106+
assert_equal 'TEST_VAR', deploy_vars[0].name
107+
assert_equal 'deploy_default', deploy_vars[0].default
108+
end
109+
110+
test '#load_config accurately deserializes rollback variables' do
111+
Shipit.expects(:respect_bare_shipit_file?).returns(true).at_least_once
112+
stack = shipit_stacks(:shipit)
113+
deploy_spec = Shipit::DeploySpec::FileSystem.new(Dir.tmpdir, stack)
114+
deploy_spec.expects(:config_file_path).returns("#{Pathname.new(Dir.tmpdir)}/shipit.yml").at_least_once
115+
deploy_spec.expects(:read_config).returns(SafeYAML.load(deploy_spec_with_variables_yaml))
116+
117+
rollback_vars = deploy_spec.rollback_variables
118+
assert_equal 1, rollback_vars.length
119+
120+
assert_equal 'TEST_VAR', rollback_vars[0].name
121+
assert_equal 'rollback_default', rollback_vars[0].default
122+
end
123+
96124
def deploy_spec_yaml
97125
<<~EOYAML
98126
deploy:
@@ -120,6 +148,19 @@ def deploy_spec_missing_deploy_yaml
120148
- production-unrestricted-1234
121149
EOYAML
122150
end
151+
152+
def deploy_spec_with_variables_yaml
153+
<<~EOYAML
154+
deploy:
155+
variables:
156+
- name: TEST_VAR
157+
default: "deploy_default"
158+
rollback:
159+
variables:
160+
- name: TEST_VAR
161+
default: "rollback_default"
162+
EOYAML
163+
end
123164
end
124165
end
125166
end

0 commit comments

Comments
 (0)