capistrano-nomad 0.11.1 → 0.12.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c21ac8313227691d585855b23b06182c72b04000c04f1b74d04825e446febb2
4
- data.tar.gz: 19e9c17ef14d2342bd849f03ef4e3515d04f5c61d02ded52edd656bc08c0acd8
3
+ metadata.gz: 6d34d3ae527de70b6bd8168b0054e92cd52d098fed4d21180b1fb1f1bbd01d04
4
+ data.tar.gz: e48dc773cd3a8433c507e97ae5299cf96255afcb1d3a07386635bd28d9697303
5
5
  SHA512:
6
- metadata.gz: ecd8ecae5c1e062469e6e308ca68fa09b913edd635c05bc5b1cb80331a7e5ec550f626168a103df2b48f1f465bd846b9054b01f245029c968fc97aa052b8186a
7
- data.tar.gz: 9a58048d208327c150c81800eb0f200220494ae48a656c8bb293aaf55b1799d90a8668b09b1ab7be69c7e48c899295a0e64873e2928198ac644276f582205c78
6
+ metadata.gz: '078c8ca031513bc8b0f0a0cba6e1e390611981c873b5cab880025a10cf5ef898edd3034c4ee94e287f442d25e3fc049d3cec4be3a0588fc6c7bf33e8ed88f391'
7
+ data.tar.gz: 35ce5309245c29dd3f5363bce2bbd0638c9810d32993ce5d68bfad886fff22302d4be1b45757bf7562c47b9f8f41c1abb349e5d7a244eb56eb91eb2428de7b64
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- capistrano-nomad (0.11.1)
4
+ capistrano-nomad (0.12.1)
5
5
  activesupport (<= 7.0.8)
6
6
  byebug
7
7
  capistrano (~> 3.0)
data/README.md CHANGED
@@ -37,7 +37,7 @@ Within `deploy.rb`
37
37
  set :nomad_jobs_path, "nomad/jobs"
38
38
  set :nomad_var_files_path, "nomad/vars"
39
39
 
40
- # Determines base URL to use when opening job in web UI
40
+ # Determines base URL to use when opening job in web UI
41
41
  set :nomad_ui_url, "http://localhost:4646"
42
42
 
43
43
  # Make variables available to all template .erb files
@@ -162,6 +162,14 @@ cap production nomad:app:stderr
162
162
  cap production nomad:analytics:grafana:follow
163
163
  ```
164
164
 
165
+ Revert jobs
166
+
167
+ ```shell
168
+ cap production nomad:app:revert
169
+ cap production nomad:app:revert VERSION=4
170
+ cap production nomad:app:revert DOCKER_IMAGE=v1.4.4
171
+ ```
172
+
165
173
  Open job in web UI
166
174
 
167
175
  ```shell
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "capistrano-nomad"
5
- spec.version = "0.11.1"
5
+ spec.version = "0.12.1"
6
6
  spec.authors = ["James Hu"]
7
7
 
8
8
  spec.summary = "Capistrano plugin for deploying and managing Nomad jobs"
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  # Specify which files should be added to the gem when it is released.
19
19
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
20
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
- `git ls-files -z`.split("\x0").reject do |f|
21
+ %x(git ls-files -z).split("\x0").reject do |f|
22
22
  (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
23
23
  end
24
24
  end
@@ -27,13 +27,13 @@ Gem::Specification.new do |spec|
27
27
  spec.require_paths = ["lib"]
28
28
 
29
29
  # Uncomment to register a new dependency of your gem
30
- spec.add_dependency "activesupport", "<= 7.0.8"
31
- spec.add_dependency "byebug"
32
- spec.add_dependency "capistrano", "~> 3.0"
33
- spec.add_dependency "git"
34
- spec.add_dependency "rake", "~> 13.0"
35
- spec.add_dependency "sshkit"
36
- spec.add_dependency "sshkit-interactive"
30
+ spec.add_dependency("activesupport", "<= 7.0.8")
31
+ spec.add_dependency("byebug")
32
+ spec.add_dependency("capistrano", "~> 3.0")
33
+ spec.add_dependency("git")
34
+ spec.add_dependency("rake", "~> 13.0")
35
+ spec.add_dependency("sshkit")
36
+ spec.add_dependency("sshkit-interactive")
37
37
 
38
38
  # For more information and examples about making a new gem, check out our
39
39
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -44,7 +44,7 @@ def nomad_job(name, attributes = {})
44
44
  attributes[:tags] ||= []
45
45
 
46
46
  # Tags added to namespace should be added to all jobs within
47
- if (nomad_namespace_options = capistrano_nomad_fetch_namespace_options(@nomad_namespace))
47
+ if (nomad_namespace_options = capistrano_nomad_fetch_namespace_options(namespace: @nomad_namespace))
48
48
  attributes[:tags] += nomad_namespace_options[:tags] || []
49
49
  end
50
50
 
@@ -59,107 +59,116 @@ def nomad_job(name, attributes = {})
59
59
  description_name << name.to_s
60
60
 
61
61
  namespace(name) do
62
- desc "Build #{description_name} job Docker images"
63
- task :build do
62
+ desc("Build #{description_name} job Docker images")
63
+ task(:build) do
64
64
  capistrano_nomad_build_jobs_docker_images([name], namespace: namespace)
65
65
  end
66
66
 
67
- desc "Push #{description_name} job Docker images"
68
- task :push do
67
+ desc("Push #{description_name} job Docker images")
68
+ task(:push) do
69
69
  capistrano_nomad_push_jobs_docker_images([name], namespace: namespace)
70
70
  end
71
71
 
72
- desc "Build and push #{description_name} job Docker images"
73
- task :assemble do
72
+ desc("Build and push #{description_name} job Docker images")
73
+ task(:assemble) do
74
74
  capistrano_nomad_build_jobs_docker_images([name], namespace: namespace)
75
75
  capistrano_nomad_push_jobs_docker_images([name], namespace: namespace)
76
76
  end
77
77
 
78
- desc "Upload #{description_name} job and related files"
79
- task :upload do
78
+ desc("Upload #{description_name} job and related files")
79
+ task(:upload) do
80
80
  capistrano_nomad_upload_jobs([name], namespace: namespace)
81
81
  end
82
82
 
83
- desc "Run #{description_name} job"
84
- task :run do
83
+ desc("Run #{description_name} job")
84
+ task(:run) do
85
85
  capistrano_nomad_run_jobs([name], namespace: namespace, is_detached: false)
86
86
  end
87
87
 
88
- desc "Purge and run #{description_name} job again"
89
- task :rerun do
88
+ desc("Purge and run #{description_name} job again")
89
+ task(:rerun) do
90
90
  capistrano_nomad_rerun_jobs([name], namespace: namespace, is_detached: false)
91
91
  end
92
92
 
93
- desc "Upload and plan #{description_name} job"
94
- task :upload_plan do
93
+ desc("Upload and plan #{description_name} job")
94
+ task(:upload_plan) do
95
95
  capistrano_nomad_upload_plan_jobs([name], namespace: namespace)
96
96
  end
97
97
 
98
- desc "Upload and run #{description_name} job"
99
- task :upload_run do
98
+ desc("Upload and run #{description_name} job")
99
+ task(:upload_run) do
100
100
  capistrano_nomad_upload_run_jobs([name], namespace: namespace, is_detached: false)
101
101
  end
102
102
 
103
- desc "Upload and re-run #{description_name} job"
104
- task :upload_rerun do
103
+ desc("Upload and re-run #{description_name} job")
104
+ task(:upload_rerun) do
105
105
  capistrano_nomad_upload_rerun_jobs([name], namespace: namespace, is_detached: false)
106
106
  end
107
107
 
108
- desc "Deploy #{description_name} job"
109
- task :deploy do
108
+ desc("Deploy #{description_name} job")
109
+ task(:deploy) do
110
110
  capistrano_nomad_deploy_jobs([name], namespace: namespace, is_detached: false)
111
111
  end
112
112
 
113
- desc "Stop #{description_name} job"
114
- task :stop do
113
+ desc("Stop #{description_name} job")
114
+ task(:stop) do
115
115
  capistrano_nomad_stop_jobs([name], namespace: namespace)
116
116
  end
117
117
 
118
- desc "Restart #{description_name} job"
119
- task :restart do
118
+ desc("Restart #{description_name} job")
119
+ task(:restart) do
120
120
  capistrano_nomad_restart_jobs([name], namespace: namespace)
121
121
  end
122
122
 
123
- desc "Purge #{description_name} job"
124
- task :purge do
123
+ desc("Revert #{description_name} job. Specify version with VERSION. Specify targeting tasks with docker image with DOCKER_IMAGE. If none specified, it will revert to previous version")
124
+ task(:revert) do
125
+ capistrano_nomad_revert_jobs([name],
126
+ namespace: namespace,
127
+ version: ENV["VERSION"],
128
+ docker_image: ENV["DOCKER_IMAGE"],
129
+ )
130
+ end
131
+
132
+ desc("Purge #{description_name} job")
133
+ task(:purge) do
125
134
  capistrano_nomad_purge_jobs([name], namespace: namespace, is_detached: false)
126
135
  end
127
136
 
128
- desc "Display status of #{description_name} job"
129
- task :status do
137
+ desc("Display status of #{description_name} job")
138
+ task(:status) do
130
139
  capistrano_nomad_display_job_status(name, namespace: namespace)
131
140
  end
132
141
 
133
- desc "Open console to #{description_name} job. Specify task with TASK, command with CMD"
134
- task :console do
142
+ desc("Open console to #{description_name} job. Specify task with TASK, command with CMD")
143
+ task(:console) do
135
144
  command = ENV["CMD"].presence || "/bin/sh"
136
145
 
137
146
  capistrano_nomad_exec_within_job(name, command, namespace: namespace, task: ENV["TASK"])
138
147
  end
139
148
 
140
- desc "Display stdout and stderr of #{description_name} job. Specify task with TASK"
141
- task :logs do
149
+ desc("Display stdout and stderr of #{description_name} job. Specify task with TASK")
150
+ task(:logs) do
142
151
  capistrano_nomad_tail_job_logs(name, namespace: namespace, stdout: true)
143
152
  capistrano_nomad_tail_job_logs(name, namespace: namespace, stderr: true)
144
153
  end
145
154
 
146
- desc "Display stdout of #{description_name} job. Specify task with TASK"
147
- task :stdout do
155
+ desc("Display stdout of #{description_name} job. Specify task with TASK")
156
+ task(:stdout) do
148
157
  capistrano_nomad_tail_job_logs(name, namespace: namespace, stdout: true)
149
158
  end
150
159
 
151
- desc "Display stderr of #{description_name} job. Specify task with TASK"
152
- task :stderr do
160
+ desc("Display stderr of #{description_name} job. Specify task with TASK")
161
+ task(:stderr) do
153
162
  capistrano_nomad_tail_job_logs(name, namespace: namespace, stderr: true)
154
163
  end
155
164
 
156
- desc "Follow logs of #{description_name} job. Specify task with TASK"
157
- task :follow do
165
+ desc("Follow logs of #{description_name} job. Specify task with TASK")
166
+ task(:follow) do
158
167
  capistrano_nomad_display_job_logs(name, namespace: namespace, f: true)
159
168
  end
160
169
 
161
- desc "Open job in web UI"
162
- task :ui do
170
+ desc("Open job in web UI")
171
+ task(:ui) do
163
172
  capistrano_nomad_open_job_ui(name, namespace: namespace)
164
173
  end
165
174
  end
@@ -29,7 +29,9 @@ def capistrano_nomad_ensure_absolute_path(path)
29
29
  path[0] == "/" ? path : "/#{path}"
30
30
  end
31
31
 
32
- def capistrano_nomad_build_file_path(parent_path, basename, kind: nil, namespace: :default)
32
+ def capistrano_nomad_build_file_path(parent_path, basename, kind: nil, **options)
33
+ capistrano_nomad_ensure_options!(**options)
34
+ namespace = options[:namespace]
33
35
  segments = [parent_path]
34
36
 
35
37
  unless namespace == :default
@@ -41,7 +43,7 @@ def capistrano_nomad_build_file_path(parent_path, basename, kind: nil, namespace
41
43
 
42
44
  # Otherwise path can be overriden of where files belonging to namespace are stored locally
43
45
  else
44
- namespace_options = capistrano_nomad_fetch_namespace_options(namespace)
46
+ namespace_options = capistrano_nomad_fetch_namespace_options(namespace: namespace)
45
47
 
46
48
  segments << (namespace_options[:path] || namespace)
47
49
  end
@@ -132,14 +134,15 @@ def capistrano_nomad_capture_nomad_command(*args, **options)
132
134
  output
133
135
  end
134
136
 
135
- def capistrano_nomad_find_job_task_details(name, namespace: :default, task: nil)
137
+ def capistrano_nomad_find_job_task_details(name, task: nil, **options)
138
+ capistrano_nomad_ensure_options!(**options)
136
139
  task = task.presence || name
137
140
 
138
141
  # Find alloc id that contains task that is also running
139
142
  allocs_output = capistrano_nomad_capture_nomad_command(
140
143
  :job,
141
144
  :allocs,
142
- { namespace: namespace, t: "'{{range .}}{{ .ID }},{{ .ClientStatus }},{{ .TaskGroup }}|{{end}}'" },
145
+ options.merge(t: "'{{range .}}{{ .ID }},{{ .ClientStatus }},{{ .TaskGroup }}|{{end}}'"),
143
146
  name,
144
147
  )
145
148
  alloc_id = allocs_output
@@ -154,7 +157,7 @@ def capistrano_nomad_find_job_task_details(name, namespace: :default, task: nil)
154
157
  tasks_output = capistrano_nomad_capture_nomad_command(
155
158
  :alloc,
156
159
  :status,
157
- { namespace: namespace, t: "'{{range $key, $value := .TaskStates}}{{ $key }},{{ .State }}|{{end}}'" },
160
+ options.merge(t: "'{{range $key, $value := .TaskStates}}{{ $key }},{{ .State }}|{{end}}'"),
158
161
  alloc_id,
159
162
  )
160
163
  tasks_by_score = tasks_output.split("|").each_with_object({}) do |task_output, hash|
@@ -174,13 +177,15 @@ def capistrano_nomad_find_job_task_details(name, namespace: :default, task: nil)
174
177
  }
175
178
  end
176
179
 
177
- def capistrano_nomad_exec_within_job(name, command, namespace: :default, task: nil)
180
+ def capistrano_nomad_exec_within_job(name, command, task: nil, **options)
181
+ capistrano_nomad_ensure_options!(**options)
182
+
178
183
  capistrano_nomad_run_remotely do
179
- if (task_details = capistrano_nomad_find_job_task_details(name, namespace: namespace, task: task))
184
+ if (task_details = capistrano_nomad_find_job_task_details(name, task: task, **options))
180
185
  capistrano_nomad_execute_nomad_command(
181
186
  :alloc,
182
187
  :exec,
183
- { namespace: namespace, task: task_details[:name] },
188
+ options.merge(task: task_details[:name]),
184
189
  task_details[:alloc_id],
185
190
  command,
186
191
  )
@@ -189,7 +194,7 @@ def capistrano_nomad_exec_within_job(name, command, namespace: :default, task: n
189
194
  capistrano_nomad_execute_nomad_command(
190
195
  :alloc,
191
196
  :exec,
192
- { namespace: namespace, job: true },
197
+ options.merge(job: true),
193
198
  task,
194
199
  command,
195
200
  )
@@ -246,19 +251,30 @@ def capistrano_nomad_upload(local_path:, remote_path:, erb_vars: {})
246
251
  end
247
252
  end
248
253
 
249
- def capistrano_nomad_fetch_namespace_options(namespace)
250
- fetch(:nomad_namespaces)&.dig(namespace)
254
+ def capistrano_nomad_ensure_options!(**options)
255
+ options[:namespace] ||= :default
256
+ end
257
+
258
+ def capistrano_nomad_fetch_namespace_options(**options)
259
+ capistrano_nomad_ensure_options!(**options)
260
+
261
+ fetch(:nomad_namespaces)&.dig(options[:namespace])
251
262
  end
252
263
 
253
- def capistrano_nomad_fetch_job_options(name, *args, namespace: :default)
254
- fetch(:nomad_jobs).dig(namespace, name.to_sym, *args)
264
+ def capistrano_nomad_fetch_job_options(name, *args, **options)
265
+ capistrano_nomad_ensure_options!(**options)
266
+
267
+ fetch(:nomad_jobs).dig(options[:namespace], name.to_sym, *args)
255
268
  end
256
269
 
257
270
  def capistrano_nomad_fetch_job_var_files(name, **options)
258
271
  capistrano_nomad_fetch_job_options(name, :var_files, **options) || []
259
272
  end
260
273
 
261
- def capistrano_nomad_fetch_jobs_names_by_namespace(namespace: :default)
274
+ def capistrano_nomad_fetch_jobs_names_by_namespace(**options)
275
+ capistrano_nomad_ensure_options!(**options)
276
+ namespace = options[:namespace]
277
+
262
278
  # Can pass tags via command line (e.g. TAG=foo or TAGS=foo,bar)
263
279
  tags =
264
280
  [ENV["TAG"], ENV["TAGS"]].map do |tag_args|
@@ -281,84 +297,93 @@ def capistrano_nomad_fetch_jobs_names_by_namespace(namespace: :default)
281
297
  end
282
298
  end
283
299
 
284
- def capistrano_nomad_fetch_jobs_docker_image_types(names, namespace: :default)
285
- names.map { |n| fetch(:nomad_jobs).dig(namespace, n.to_sym, :docker_image_types) }.flatten.compact.uniq
300
+ def capistrano_nomad_fetch_jobs_docker_image_types(names, **options)
301
+ capistrano_nomad_ensure_options!(**options)
302
+
303
+ names.map { |n| fetch(:nomad_jobs).dig(options[:namespace], n.to_sym, :docker_image_types) }.flatten.compact.uniq
286
304
  end
287
305
 
288
306
  def capistrano_nomad_define_group_tasks(namespace:)
289
307
  define_tasks = lambda do |nomad_namespace: nil|
290
- desc "Build #{nomad_namespace} job Docker images"
291
- task :build do
308
+ desc("Build #{nomad_namespace} job Docker images")
309
+ task(:build) do
292
310
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
293
311
  capistrano_nomad_push_jobs_docker_images(names, namespace: jobs_namespace)
294
312
  end
295
313
  end
296
314
 
297
- desc "Push #{nomad_namespace} job Docker images"
298
- task :push do
315
+ desc("Push #{nomad_namespace} job Docker images")
316
+ task(:push) do
299
317
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
300
318
  capistrano_nomad_push_jobs_docker_images(names, namespace: jobs_namespace)
301
319
  end
302
320
  end
303
321
 
304
- desc "Build and push #{nomad_namespace} job Docker images"
305
- task :assemble do
322
+ desc("Build and push #{nomad_namespace} job Docker images")
323
+ task(:assemble) do
306
324
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
307
325
  capistrano_nomad_assemble_jobs_docker_images(names, namespace: jobs_namespace)
308
326
  end
309
327
  end
310
328
 
311
- desc "Upload #{nomad_namespace} jobs"
312
- task :upload do
329
+ desc("Upload #{nomad_namespace} jobs")
330
+ task(:upload) do
313
331
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
314
332
  capistrano_nomad_upload_jobs(names, namespace: jobs_namespace)
315
333
  end
316
334
  end
317
335
 
318
- desc "Run #{nomad_namespace} jobs"
319
- task :run do
336
+ desc("Run #{nomad_namespace} jobs")
337
+ task(:run) do
320
338
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
321
339
  capistrano_nomad_run_jobs(names, namespace: jobs_namespace)
322
340
  end
323
341
  end
324
342
 
325
- desc "Upload and run #{nomad_namespace} jobs"
326
- task :upload_run do
343
+ desc("Upload and run #{nomad_namespace} jobs")
344
+ task(:upload_run) do
327
345
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
328
346
  capistrano_nomad_upload_run_jobs(names, namespace: jobs_namespace)
329
347
  end
330
348
  end
331
349
 
332
- desc "Deploy #{nomad_namespace} jobs"
333
- task :deploy do
350
+ desc("Deploy #{nomad_namespace} jobs")
351
+ task(:deploy) do
334
352
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
335
353
  capistrano_nomad_deploy_jobs(names, namespace: jobs_namespace)
336
354
  end
337
355
  end
338
356
 
339
- desc "Rerun #{nomad_namespace} jobs"
340
- task :rerun do
357
+ desc("Rerun #{nomad_namespace} jobs")
358
+ task(:rerun) do
341
359
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
342
360
  capistrano_nomad_rerun_jobs(names, namespace: jobs_namespace)
343
361
  end
344
362
  end
345
363
 
346
- desc "Restart #{nomad_namespace} jobs"
347
- task :restart do
364
+ desc("Restart #{nomad_namespace} jobs")
365
+ task(:restart) do
348
366
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
349
367
  capistrano_nomad_restart_jobs(names, namespace: jobs_namespace)
350
368
  end
351
369
  end
352
370
 
353
- desc "Stop #{nomad_namespace} jobs"
354
- task :stop do
371
+ desc("Revert #{nomad_namespace} jobs")
372
+ task(:revert) do
373
+ capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
374
+ capistrano_nomad_revert_jobs(names, namespace: jobs_namespace)
375
+ end
376
+ end
377
+
378
+ desc("Stop #{nomad_namespace} jobs")
379
+ task(:stop) do
355
380
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
356
381
  capistrano_nomad_stop_jobs(names, namespace: jobs_namespace)
357
382
  end
358
383
  end
359
384
 
360
- desc "Purge #{nomad_namespace} jobs"
361
- task :purge do
385
+ desc("Purge #{nomad_namespace} jobs")
386
+ task(:purge) do
362
387
  capistrano_nomad_fetch_jobs_names_by_namespace(namespace: nomad_namespace).each do |jobs_namespace, names|
363
388
  capistrano_nomad_purge_jobs(names, namespace: jobs_namespace)
364
389
  end
@@ -433,24 +458,26 @@ def capistrano_nomad_plan_jobs(names, **options)
433
458
  end
434
459
  end
435
460
 
436
- def capistrano_nomad_run_jobs(names, namespace: :default, is_detached: true)
461
+ def capistrano_nomad_run_jobs(names, is_detached: true, **options)
462
+ capistrano_nomad_ensure_options!(**options)
463
+
437
464
  names.each do |name|
438
465
  run_options = {
439
- namespace: namespace,
466
+ namespace: options[:namespace],
440
467
  detach: is_detached,
441
468
 
442
469
  # Don't reset counts since they may have been scaled
443
470
  preserve_counts: true,
444
471
  }
445
472
 
446
- capistrano_nomad_fetch_job_var_files(name, namespace: namespace).each do |var_file|
447
- run_options[:var_file] = capistrano_nomad_build_release_var_file_path(var_file, namespace: namespace)
473
+ capistrano_nomad_fetch_job_var_files(name, **options).each do |var_file|
474
+ run_options[:var_file] = capistrano_nomad_build_release_var_file_path(var_file, **options)
448
475
  end
449
476
 
450
477
  capistrano_nomad_execute_nomad_command(
451
478
  :run,
452
479
  run_options,
453
- capistrano_nomad_build_release_job_path(name, namespace: namespace),
480
+ capistrano_nomad_build_release_job_path(name, **options),
454
481
  )
455
482
  end
456
483
  end
@@ -494,6 +521,8 @@ def capistrano_nomad_deploy_jobs(names, **options)
494
521
  end
495
522
 
496
523
  def capistrano_nomad_restart_jobs(names, **options)
524
+ capistrano_nomad_ensure_options!(**options)
525
+
497
526
  names.each do |name|
498
527
  # Automatic yes to prompts. If set, the command automatically restarts multi-region jobs only in the region targeted
499
528
  # by the command, ignores batch errors, and automatically proceeds with the remaining batches without waiting
@@ -502,27 +531,76 @@ def capistrano_nomad_restart_jobs(names, **options)
502
531
  end
503
532
 
504
533
  def capistrano_nomad_stop_jobs(names, **options)
534
+ capistrano_nomad_ensure_options!(**options)
535
+
505
536
  names.each do |name|
506
537
  capistrano_nomad_execute_nomad_command(:job, :stop, options, name)
507
538
  end
508
539
  end
509
540
 
510
- def capistrano_nomad_purge_jobs(names, namespace: :default, is_detached: true)
541
+ def capistrano_nomad_purge_jobs(names, is_detached: true, **options)
542
+ capistrano_nomad_ensure_options!(**options)
543
+
544
+ names.each do |name|
545
+ capistrano_nomad_execute_nomad_command(:stop, options.reverse_merge(purge: true, detach: is_detached), name)
546
+ end
547
+ end
548
+
549
+ def capistrano_nomad_revert_jobs(names, version: nil, docker_image: nil, **options)
550
+ capistrano_nomad_ensure_options!(**options)
551
+ versions_by_job_name = {}
552
+
511
553
  names.each do |name|
512
- capistrano_nomad_execute_nomad_command(:stop, { namespace: namespace, purge: true, detach: is_detached }, name)
554
+ history_output_json = capistrano_nomad_display_job_history(name, **options.reverse_merge(json: true))
555
+ history_output = JSON.parse(history_output_json)
556
+ versions_by_job_name[name] = if version.presence
557
+ version
558
+ elsif docker_image.presence
559
+ # Find job history with matching docker image
560
+ docker_image_job_history = history_output.find do |job_history|
561
+ task_images = job_history.dig("TaskGroups")
562
+ .map { |g| g.dig("Tasks").map { |t| t.dig("Config", "image") } }
563
+ .flatten
564
+ .compact
565
+
566
+ task_images.any? { |image| image.include?(docker_image) }
567
+ end
568
+
569
+ unless docker_image_job_history
570
+ raise ArgumentError, "No job history found for job #{name} with docker image: #{docker_image}"
571
+ end
572
+
573
+ docker_image_job_history.dig("Version")
574
+ # Revert to previous version if nothing specified
575
+ else
576
+ history_output[1].dig("Version")
577
+ end
578
+ end
579
+
580
+ versions_by_job_name.each do |name, version|
581
+ capistrano_nomad_execute_nomad_command(:job, :revert, options, name, version)
513
582
  end
514
583
  end
515
584
 
585
+ def capistrano_nomad_display_job_history(name, **options)
586
+ capistrano_nomad_ensure_options!(**options)
587
+
588
+ capistrano_nomad_capture_nomad_command(:job, :history, options, name)
589
+ end
590
+
516
591
  def capistrano_nomad_display_job_status(name, **options)
592
+ capistrano_nomad_ensure_options!(**options)
593
+
517
594
  capistrano_nomad_execute_nomad_command(:status, options, name)
518
595
  end
519
596
 
520
- def capistrano_nomad_display_job_logs(name, namespace: :default, **options)
521
- if (task_details = capistrano_nomad_find_job_task_details(name, namespace: namespace, task: ENV["TASK"]))
597
+ def capistrano_nomad_display_job_logs(name, **options)
598
+ if (task_details = capistrano_nomad_find_job_task_details(name, **options.slice(:namespace).reverse_merge(task: ENV["TASK"])
599
+ ))
522
600
  capistrano_nomad_execute_nomad_command(
523
601
  :alloc,
524
602
  :logs,
525
- options.merge(namespace: namespace, task: task_details[:name]),
603
+ options.reverse_merge(task: task_details[:name]),
526
604
  task_details[:alloc_id],
527
605
  )
528
606
  else
@@ -530,7 +608,7 @@ def capistrano_nomad_display_job_logs(name, namespace: :default, **options)
530
608
  capistrano_nomad_execute_nomad_command(
531
609
  :alloc,
532
610
  :logs,
533
- options.merge(namespace: namespace, job: true),
611
+ options.reverse_merge(job: true),
534
612
  name,
535
613
  )
536
614
  end
@@ -540,7 +618,7 @@ def capistrano_nomad_tail_job_logs(*args, **options)
540
618
  capistrano_nomad_display_job_logs(*args, **options.merge(tail: true, n: 50))
541
619
  end
542
620
 
543
- def capistrano_nomad_open_job_ui(name, namespace: :default)
621
+ def capistrano_nomad_open_job_ui(name, namespace: nil)
544
622
  run_locally do
545
623
  url = "#{fetch(:nomad_ui_url)}/ui/jobs/#{name}"
546
624
  url += "@#{namespace}" if namespace
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.11.1
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Hu
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-10 00:00:00.000000000 Z
11
+ date: 2025-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport