capistrano-nomad 0.16.2 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +10 -1
- data/capistrano-nomad.gemspec +1 -1
- data/lib/capistrano/nomad/helpers/dsl.rb +5 -0
- data/lib/capistrano/nomad/helpers/nomad.rb +21 -10
- data/test/upload_plan_test.rb +65 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 56224de9c341668aba37b58d2b0b404decfce7ee5f488364c325319fea262572
|
|
4
|
+
data.tar.gz: 49275ee4444defa39b4de8e6d193342d63b8a1edd08deb6f4946451c8c4b9cf2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 10e0218ac88de63102cb0af504e7eec43985535516d6fcd91c5404224003b4a652e500bef48895707f4532ca83ebac128aef143d6b38571e490eca632bac91c2
|
|
7
|
+
data.tar.gz: 7b53eee7196177a352c757527911ccfb601c8075918f49c8a3557cfc61da571fb707341802affb138fbcb903b13c92e09a619431a6f657ee2bc651ddced4df5f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.16.3]
|
|
4
|
+
|
|
5
|
+
- Add a generated `:force` task to launch a periodic child through the configured manager host and Nomad token
|
|
6
|
+
- Reject non-periodic and disabled periodic jobs before submitting a force request
|
|
7
|
+
- Fail the Capistrano command when job inspection or periodic force fails
|
|
8
|
+
|
|
3
9
|
## [0.16.2]
|
|
4
10
|
|
|
5
11
|
- Fail `upload_plan` and other plan operations when Nomad rejects a job or cannot determine the plan
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -145,6 +145,15 @@ cap production nomad:analytics:grafana:restart
|
|
|
145
145
|
cap production nomad:postgres:status
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
+
Force a periodic job without uploading, deploying, or restarting its parent:
|
|
149
|
+
|
|
150
|
+
```shell
|
|
151
|
+
cap production nomad:maintenance:force
|
|
152
|
+
cap production nomad:analytics:archive:force
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The per-job `:force` task checks the deployed job before running `nomad job periodic force`. Non-periodic and disabled periodic jobs fail with a clear error. Inspection and force failures produce a nonzero Capistrano exit. The task uses the configured manager host and `:nomad_token`, like other job actions.
|
|
156
|
+
|
|
148
157
|
Tasks can apply across all namespaces or be filtered by namespaces or tags
|
|
149
158
|
|
|
150
159
|
```shell
|
|
@@ -213,7 +222,7 @@ cap production nomad:version
|
|
|
213
222
|
|
|
214
223
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
215
224
|
|
|
216
|
-
Run `bundle exec rake test` to test the generated Capistrano `upload_plan`
|
|
225
|
+
Run `bundle exec rake test` to test the generated Capistrano `upload_plan` and `force` tasks through the CLI. The tests use local transport and controlled Nomad exit codes. Force coverage checks the job, namespace, token, rejected requests, and non-periodic jobs. To also check rejection of invalid HCL with a real Nomad binary, run `NOMAD_REAL_BINARY=/path/to/nomad bundle exec rake test`.
|
|
217
226
|
|
|
218
227
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version in `capistrano-nomad.gemspec`, run `bundle install`, and update `CHANGELOG.md`. Run `bundle exec rake test` and commit the release changes. Then run `bundle exec rake release`, which creates the version tag, pushes the release commit and tag, and publishes the gem to [RubyGems](https://rubygems.org).
|
|
219
228
|
|
data/capistrano-nomad.gemspec
CHANGED
|
@@ -91,6 +91,11 @@ def nomad_job(name, attributes = {})
|
|
|
91
91
|
capistrano_nomad_run_jobs([name], namespace: namespace, is_detached: capistrano_nomad_job_detached_overridden?)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
+
desc("Force a new periodic child of #{description_name} job")
|
|
95
|
+
task(:force) do
|
|
96
|
+
capistrano_nomad_force_job(name, namespace: namespace)
|
|
97
|
+
end
|
|
98
|
+
|
|
94
99
|
desc("Purge and run #{description_name} job again")
|
|
95
100
|
task(:rerun) do
|
|
96
101
|
capistrano_nomad_rerun_jobs([name], namespace: namespace, is_detached: capistrano_nomad_job_detached_overridden?)
|
|
@@ -142,7 +142,7 @@ def capistrano_nomad_plan_command?(args)
|
|
|
142
142
|
args.first.to_s == "plan" || args.take(2).map(&:to_s) == ["job", "plan"]
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
-
def capistrano_nomad_run_nomad_command(kind, *args)
|
|
145
|
+
def capistrano_nomad_run_nomad_command(kind, *args, raise_on_non_zero_exit: false)
|
|
146
146
|
converted_args = args.each_with_object([]) do |arg, collection|
|
|
147
147
|
# If hash then convert it as options
|
|
148
148
|
if arg.is_a?(Hash)
|
|
@@ -173,30 +173,30 @@ def capistrano_nomad_run_nomad_command(kind, *args)
|
|
|
173
173
|
# Nomad returns 1 for a successful plan with changes; all other nonzero exits are errors
|
|
174
174
|
public_send(kind, :nomad, *converted_args, '; status=$?; if [ "$status" -eq 1 ]; then exit 0; else exit "$status"; fi', raise_on_non_zero_exit: true)
|
|
175
175
|
else
|
|
176
|
-
#
|
|
177
|
-
public_send(kind, :nomad, *converted_args, raise_on_non_zero_exit:
|
|
176
|
+
# Preserve unchecked execution unless the caller requires failures to propagate
|
|
177
|
+
public_send(kind, :nomad, *converted_args, raise_on_non_zero_exit: raise_on_non_zero_exit)
|
|
178
178
|
end
|
|
179
179
|
end
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
-
def capistrano_nomad_execute_nomad_command(*args, **options)
|
|
182
|
+
def capistrano_nomad_execute_nomad_command(*args, raise_on_non_zero_exit: false, **options)
|
|
183
183
|
capistrano_nomad_run_remotely do |host|
|
|
184
|
-
if capistrano_nomad_plan_command?(args)
|
|
185
|
-
# Use
|
|
186
|
-
capistrano_nomad_run_nomad_command(:execute, *args,
|
|
184
|
+
if capistrano_nomad_plan_command?(args) || raise_on_non_zero_exit
|
|
185
|
+
# Use checked execution when failures must propagate because the interactive backend does not raise
|
|
186
|
+
capistrano_nomad_run_nomad_command(:execute, *args, options, raise_on_non_zero_exit: raise_on_non_zero_exit)
|
|
187
187
|
else
|
|
188
188
|
run_interactively(host) do
|
|
189
|
-
capistrano_nomad_run_nomad_command(:execute, *args,
|
|
189
|
+
capistrano_nomad_run_nomad_command(:execute, *args, options)
|
|
190
190
|
end
|
|
191
191
|
end
|
|
192
192
|
end
|
|
193
193
|
end
|
|
194
194
|
|
|
195
|
-
def capistrano_nomad_capture_nomad_command(*args, **options)
|
|
195
|
+
def capistrano_nomad_capture_nomad_command(*args, raise_on_non_zero_exit: false, **options)
|
|
196
196
|
output = nil
|
|
197
197
|
|
|
198
198
|
capistrano_nomad_run_remotely do
|
|
199
|
-
output = capistrano_nomad_run_nomad_command(:capture, *args,
|
|
199
|
+
output = capistrano_nomad_run_nomad_command(:capture, *args, options, raise_on_non_zero_exit: raise_on_non_zero_exit)
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
output
|
|
@@ -634,6 +634,17 @@ def capistrano_nomad_restart_jobs(names, **options)
|
|
|
634
634
|
end
|
|
635
635
|
end
|
|
636
636
|
|
|
637
|
+
def capistrano_nomad_force_job(name, **options)
|
|
638
|
+
capistrano_nomad_ensure_options!(options)
|
|
639
|
+
job = JSON.parse(capistrano_nomad_capture_nomad_command(:job, :inspect, options, name, raise_on_non_zero_exit: true))
|
|
640
|
+
|
|
641
|
+
unless job.dig("Periodic", "Enabled")
|
|
642
|
+
raise ArgumentError, "Job #{options[:namespace]}/#{name} is not an enabled periodic job"
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
capistrano_nomad_execute_nomad_command(:job, :periodic, :force, options, name, raise_on_non_zero_exit: true)
|
|
646
|
+
end
|
|
647
|
+
|
|
637
648
|
def capistrano_nomad_start_jobs(names, **options)
|
|
638
649
|
capistrano_nomad_ensure_options!(options)
|
|
639
650
|
|
data/test/upload_plan_test.rb
CHANGED
|
@@ -23,6 +23,10 @@ class UploadPlanTest < Minitest::Test
|
|
|
23
23
|
set :nomad_docker_image_types, {}
|
|
24
24
|
SSHKit.config.command_map[:nomad] = #{"#{root}/bin/nomad".inspect}
|
|
25
25
|
nomad_job :example
|
|
26
|
+
set :nomad_token, "force-test-token"
|
|
27
|
+
nomad_namespace :spleencheese do
|
|
28
|
+
nomad_job :"myanonamouse-maintenance"
|
|
29
|
+
end
|
|
26
30
|
RUBY
|
|
27
31
|
File.write("#{root}/config/deploy/test.rb", <<~RUBY)
|
|
28
32
|
server "localhost", roles: [:manager]
|
|
@@ -36,6 +40,21 @@ class UploadPlanTest < Minitest::Test
|
|
|
36
40
|
FileUtils.chmod(0755, "#{root}/bin/ssh")
|
|
37
41
|
File.write("#{root}/bin/nomad", <<~SH)
|
|
38
42
|
#!/bin/sh
|
|
43
|
+
if [ "$1" = job ]; then
|
|
44
|
+
[ "$NOMAD_TOKEN" = force-test-token ] || exit 98
|
|
45
|
+
printf '%s\n' "$*" >> #{root}/commands
|
|
46
|
+
if [ "$2" = inspect ]; then
|
|
47
|
+
printf '%s\n' "$NOMAD_TEST_JOB"
|
|
48
|
+
exit "${NOMAD_TEST_INSPECT_EXIT:-0}"
|
|
49
|
+
fi
|
|
50
|
+
[ "$2 $3" = 'periodic force' ] || exit 99
|
|
51
|
+
if [ "$NOMAD_TEST_EXIT" = 0 ]; then
|
|
52
|
+
echo 'Created periodic child'
|
|
53
|
+
else
|
|
54
|
+
echo 'Periodic force rejected' >&2
|
|
55
|
+
fi
|
|
56
|
+
exit "$NOMAD_TEST_EXIT"
|
|
57
|
+
fi
|
|
39
58
|
[ "$1" = plan ] || exit 99
|
|
40
59
|
cat "$2"
|
|
41
60
|
case "$NOMAD_TEST_EXIT" in
|
|
@@ -65,6 +84,52 @@ class UploadPlanTest < Minitest::Test
|
|
|
65
84
|
end
|
|
66
85
|
end
|
|
67
86
|
|
|
87
|
+
[0, 1, 2].each do |exit_code|
|
|
88
|
+
File.write("#{root}/commands", "")
|
|
89
|
+
output, status = Open3.capture2e(
|
|
90
|
+
{ "NOMAD_TEST_EXIT" => exit_code.to_s, "NOMAD_TEST_JOB" => '{"Periodic":{"Enabled":true}}' },
|
|
91
|
+
"bundle", "exec", "cap", "test", "nomad:spleencheese:myanonamouse-maintenance:force",
|
|
92
|
+
chdir: root,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert_equal("job inspect -namespace=spleencheese myanonamouse-maintenance\njob periodic force -namespace=spleencheese myanonamouse-maintenance\n", File.read("#{root}/commands"), output)
|
|
96
|
+
assert_equal(exit_code.zero?, status.success?, output)
|
|
97
|
+
assert_includes(output, exit_code.zero? ? "Created periodic child" : "Periodic force rejected")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
['{"Periodic":null}', '{"Periodic":{"Enabled":false}}'].each do |job|
|
|
101
|
+
File.write("#{root}/commands", "")
|
|
102
|
+
output, status = Open3.capture2e(
|
|
103
|
+
{ "NOMAD_TEST_JOB" => job },
|
|
104
|
+
"bundle", "exec", "cap", "test", "nomad:example:force",
|
|
105
|
+
chdir: root,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
refute(status.success?, output)
|
|
109
|
+
assert_includes(output, "Job default/example is not an enabled periodic job")
|
|
110
|
+
assert_equal("job inspect -namespace=default example\n", File.read("#{root}/commands"), output)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
File.write("#{root}/commands", "")
|
|
114
|
+
output, status = Open3.capture2e(
|
|
115
|
+
{ "NOMAD_TEST_JOB" => '{"Periodic":{"Enabled":true}}', "NOMAD_TEST_INSPECT_EXIT" => "1" },
|
|
116
|
+
"bundle", "exec", "cap", "test", "nomad:example:force",
|
|
117
|
+
chdir: root,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
refute(status.success?, output)
|
|
121
|
+
assert_equal("job inspect -namespace=default example\n", File.read("#{root}/commands"), output)
|
|
122
|
+
|
|
123
|
+
File.write("#{root}/commands", "")
|
|
124
|
+
output, status = Open3.capture2e(
|
|
125
|
+
{ "NOMAD_TEST_JOB" => '{"Periodic":{"Enabled":true}}', "NOMAD_TEST_EXIT" => "0" },
|
|
126
|
+
"bundle", "exec", "cap", "test", "nomad:example:force",
|
|
127
|
+
chdir: root,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
assert(status.success?, output)
|
|
131
|
+
assert_equal("job inspect -namespace=default example\njob periodic force -namespace=default example\n", File.read("#{root}/commands"), output)
|
|
132
|
+
|
|
68
133
|
if ENV["NOMAD_REAL_BINARY"]
|
|
69
134
|
FileUtils.cp(ENV.fetch("NOMAD_REAL_BINARY"), "#{root}/bin/nomad")
|
|
70
135
|
output, status = Open3.capture2e("bundle", "exec", "cap", "test", "nomad:example:upload_plan", chdir: root)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: capistrano-nomad
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.16.
|
|
4
|
+
version: 0.16.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Hu
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|