capistrano-nomad 0.6.4 → 0.7.0
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/Gemfile.lock +1 -1
- data/README.md +52 -9
- data/capistrano-nomad.gemspec +1 -1
- data/lib/capistrano/nomad/helpers/base.rb +4 -0
- data/lib/capistrano/nomad/helpers/docker.rb +48 -21
- data/lib/capistrano/nomad/helpers/dsl.rb +48 -13
- data/lib/capistrano/nomad/helpers/nomad.rb +147 -84
- data/lib/capistrano/nomad/tasks/nomad.rake +1 -1
- 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: d227d6f15f433cd6410884afc5572cf9c7e115a7365e00e244d3b95ca320f148
|
4
|
+
data.tar.gz: a0cdd0e131319cd0f1a17a926afe70619d24550018de9731c96a610b075300c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd4aec6aecd6698d4ed23d4e1a2c3d3e8ded18467c1a4d8daee191e29304e2f91f85ca7d604681dba4ec6cb885308f0fb6985fb4e9d952e459fc7859a3b3917a
|
7
|
+
data.tar.gz: 6009d60f5bd99e35fdb2961f6b9d860ea9db40ac844cebacf87b21ae58381eeefd35022fb58c7a6b88fa74da7e3acff3ee26a0f9a71a92e1b61495102bf53a21
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -37,8 +37,8 @@ Within `deploy.rb`
|
|
37
37
|
set :nomad_jobs_path, "nomad/jobs"
|
38
38
|
set :nomad_var_files_path, "nomad/vars"
|
39
39
|
|
40
|
-
#
|
41
|
-
set :
|
40
|
+
# Make variables available to all template .erb files
|
41
|
+
set :nomad_template_vars, (lambda do
|
42
42
|
{
|
43
43
|
env_name: fetch(:stage).to_sym,
|
44
44
|
domain: fetch(:domain),
|
@@ -46,19 +46,45 @@ set :nomad_erb_vars, (lambda do
|
|
46
46
|
}
|
47
47
|
end)
|
48
48
|
|
49
|
-
#
|
49
|
+
# Make helpers available to all template .erb files
|
50
|
+
nomad_template_helpers do
|
51
|
+
def restart_stanza(interval = "1m")
|
52
|
+
<<-EOF
|
53
|
+
restart {
|
54
|
+
interval = "#{interval}"
|
55
|
+
attempts = 3
|
56
|
+
mode = "delay"
|
57
|
+
}
|
58
|
+
EOF
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Use hosted Docker image
|
63
|
+
nomad_docker_image_type :postgres,
|
64
|
+
alias_digest: "postgres:5.0.0"
|
65
|
+
|
66
|
+
# Use Docker image that will be built locally relative to project and push
|
50
67
|
nomad_docker_image_type :backend,
|
51
68
|
path: "local/path/backend",
|
52
69
|
alias: ->(image_type:) { "gcr.io/axsuul/#{image_type}" },
|
53
70
|
target: "release",
|
54
71
|
build_args: { foo: "bar" }
|
72
|
+
|
73
|
+
# Use Docker image that will be built locally from an absolute path and push
|
55
74
|
nomad_docker_image_type :redis,
|
56
75
|
path: "/absolute/path/redis",
|
57
76
|
alias: "gcr.io/axsuul/redis"
|
58
77
|
|
78
|
+
# Use Docker image that will be built remotely on server
|
79
|
+
nomad_docker_image_type :restic,
|
80
|
+
path: "containers/restic",
|
81
|
+
alias: "my-project/restic:local",
|
82
|
+
strategy: :remote_build
|
83
|
+
|
59
84
|
# Jobs
|
60
|
-
nomad_job :frontend
|
61
85
|
nomad_job :backend, docker_image_types: [:backend], var_files: [:rails]
|
86
|
+
nomad_job :frontend
|
87
|
+
nomad_job :postgres, docker_image_types: [:postgres]
|
62
88
|
nomad_job :redis, docker_image_types: [:redis]
|
63
89
|
nomad_job :"traefik-default", template: :traefik, erb_vars: { role: :default }
|
64
90
|
nomad_job :"traefik-secondary", template: :traefik, erb_vars: { role: :secondary }
|
@@ -68,21 +94,29 @@ nomad_namespace :analytics do
|
|
68
94
|
end
|
69
95
|
```
|
70
96
|
|
71
|
-
Deploy all
|
97
|
+
Deploy all jobs
|
72
98
|
|
73
99
|
```shell
|
74
100
|
cap production nomad:all:deploy
|
75
101
|
```
|
76
102
|
|
77
|
-
Deploy
|
103
|
+
Deploy individual jobs
|
78
104
|
|
79
105
|
```shell
|
80
106
|
cap production nomad:app:deploy
|
81
|
-
cap production nomad:redis:purge
|
82
107
|
cap production nomad:analytics:grafana:deploy
|
83
108
|
```
|
84
109
|
|
85
|
-
|
110
|
+
Manage jobs
|
111
|
+
|
112
|
+
```shell
|
113
|
+
cap production nomad:app:stop
|
114
|
+
cap production nomad:redis:purge
|
115
|
+
cap production nomad:analytics:grafana:restart
|
116
|
+
cap production nomad:postgres:status
|
117
|
+
```
|
118
|
+
|
119
|
+
Open console
|
86
120
|
|
87
121
|
```shell
|
88
122
|
cap production nomad:app:console
|
@@ -90,10 +124,19 @@ cap production nomad:app:console TASK=custom-task-name
|
|
90
124
|
cap production nomad:analytics:grafana:console
|
91
125
|
```
|
92
126
|
|
127
|
+
Display logs
|
128
|
+
|
129
|
+
```shell
|
130
|
+
cap production nomad:app:logs
|
131
|
+
cap production nomad:app:stdout
|
132
|
+
cap production nomad:app:stderr
|
133
|
+
cap production nomad:analytics:grafana:follow
|
134
|
+
```
|
135
|
+
|
93
136
|
Create missing and delete unused namespaces
|
94
137
|
|
95
138
|
```shell
|
96
|
-
cap production nomad:all:
|
139
|
+
cap production nomad:all:modify_namespaces
|
97
140
|
```
|
98
141
|
|
99
142
|
## Development
|
data/capistrano-nomad.gemspec
CHANGED
@@ -21,7 +21,7 @@ end
|
|
21
21
|
def capistrano_nomad_read_docker_image_types_manifest
|
22
22
|
manifest = {}
|
23
23
|
|
24
|
-
|
24
|
+
capistrano_nomad_run_remotely do
|
25
25
|
# Ensure file exists
|
26
26
|
execute("mkdir -p #{shared_path}")
|
27
27
|
execute("touch #{capistrano_nomad_docker_image_types_manifest_path}")
|
@@ -37,7 +37,7 @@ def capistrano_nomad_read_docker_image_types_manifest
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def capistrano_nomad_update_docker_image_types_manifest(image_type, properties = {})
|
40
|
-
|
40
|
+
capistrano_nomad_run_remotely do
|
41
41
|
# Read and update manifest
|
42
42
|
manifest = capistrano_nomad_read_docker_image_types_manifest
|
43
43
|
manifest[image_type] = (manifest[image_type] || {}).merge(properties.stringify_keys)
|
@@ -70,6 +70,9 @@ def capistrano_nomad_build_docker_image_for_type(image_type)
|
|
70
70
|
|
71
71
|
return unless attributes
|
72
72
|
|
73
|
+
# No need to build if there's no path
|
74
|
+
return unless attributes[:path]
|
75
|
+
|
73
76
|
args = [
|
74
77
|
# Ensure images are built for x86_64 which is production env otherwise it will default to local development env
|
75
78
|
# which can be arm64 (Apple Silicon)
|
@@ -87,12 +90,7 @@ def capistrano_nomad_build_docker_image_for_type(image_type)
|
|
87
90
|
args << "--build-arg #{key}=#{value}"
|
88
91
|
end
|
89
92
|
|
90
|
-
|
91
|
-
# If any of these files exist then we're in the middle of rebase so we should interrupt
|
92
|
-
if ["rebase-merge", "rebase-apply"].any? { |f| File.exist?("#{capistrano_nomad_git.dir.path}/.git/#{f}") }
|
93
|
-
raise StandardError, "Still in the middle of git rebase, interrupting docker image build"
|
94
|
-
end
|
95
|
-
|
93
|
+
docker_build_command = lambda do |path|
|
96
94
|
image_alias_args = args.dup
|
97
95
|
|
98
96
|
[capistrano_nomad_build_docker_image_alias(image_type)]
|
@@ -101,31 +99,60 @@ def capistrano_nomad_build_docker_image_for_type(image_type)
|
|
101
99
|
image_alias_args << "--tag #{tag}"
|
102
100
|
end
|
103
101
|
|
104
|
-
|
102
|
+
"docker build #{image_alias_args.join(' ')} #{path}"
|
103
|
+
end
|
104
|
+
|
105
|
+
case attributes[:strategy]
|
106
|
+
|
107
|
+
# We need to build Docker container locally
|
108
|
+
when :local_build, :local_push
|
109
|
+
run_locally do
|
110
|
+
# If any of these files exist then we're in the middle of rebase so we should interrupt
|
111
|
+
if ["rebase-merge", "rebase-apply"].any? { |f| File.exist?("#{capistrano_nomad_git.dir.path}/.git/#{f}") }
|
112
|
+
raise StandardError, "Still in the middle of git rebase, interrupting docker image build"
|
113
|
+
end
|
114
|
+
|
115
|
+
execute(docker_build_command.call(capistrano_nomad_root.join(attributes[:path])))
|
116
|
+
end
|
117
|
+
|
118
|
+
# We need to build Docker container remotely
|
119
|
+
when :remote_build, :remote_push
|
120
|
+
remote_path = Pathname.new(release_path).join(attributes[:path])
|
121
|
+
capistrano_nomad_upload(local_path: attributes[:path], remote_path: remote_path)
|
122
|
+
|
123
|
+
capistrano_nomad_run_remotely do
|
124
|
+
execute(docker_build_command.call(remote_path))
|
125
|
+
end
|
105
126
|
end
|
106
127
|
end
|
107
128
|
|
108
129
|
def capistrano_nomad_push_docker_image_for_type(image_type, is_manifest_updated: true)
|
109
|
-
|
110
|
-
|
130
|
+
attributes = fetch(:nomad_docker_image_types)[image_type]
|
131
|
+
alias_digest = attributes&.dig(:alias_digest)
|
132
|
+
|
133
|
+
return false unless [:local_push, :remote_push].include?(attributes[:strategy])
|
111
134
|
|
112
135
|
run_locally do
|
113
|
-
|
114
|
-
|
136
|
+
# Don't push Docker image if alias digest is already passed in
|
137
|
+
unless alias_digest
|
138
|
+
interaction_handler = CapistranoNomadDockerPushImageInteractionHandler.new
|
139
|
+
image_alias = capistrano_nomad_build_docker_image_alias(image_type)
|
140
|
+
|
141
|
+
# We should not proceed if image cannot be pushed
|
142
|
+
unless execute("docker push #{image_alias}", interaction_handler: interaction_handler)
|
143
|
+
raise StandardError, "Docker image push unsuccessful!"
|
144
|
+
end
|
115
145
|
|
116
|
-
|
117
|
-
unless execute("docker push #{image_alias}", interaction_handler: interaction_handler)
|
118
|
-
raise StandardError, "Docker image push unsuccessful!"
|
119
|
-
end
|
146
|
+
return unless is_manifest_updated
|
120
147
|
|
121
|
-
|
148
|
+
# Has the @sha256:xxxx appended so we have the ability to also target by digest
|
149
|
+
alias_digest = "#{image_alias}@#{interaction_handler.last_digest}"
|
150
|
+
end
|
122
151
|
|
123
152
|
# Update image type manifest
|
124
153
|
capistrano_nomad_update_docker_image_types_manifest(image_type,
|
125
154
|
alias: image_alias,
|
126
|
-
|
127
|
-
# Has the @sha256:xxxx appended so we have the ability to also target by digest
|
128
|
-
alias_digest: "#{image_alias}@#{interaction_handler.last_digest}",
|
155
|
+
alias_digest: alias_digest,
|
129
156
|
)
|
130
157
|
end
|
131
158
|
end
|
@@ -1,6 +1,14 @@
|
|
1
|
+
require "active_support/core_ext/hash"
|
2
|
+
|
1
3
|
def nomad_docker_image_type(image_type, attributes = {})
|
2
4
|
docker_image_types = fetch(:nomad_docker_image_types) || {}
|
3
|
-
docker_image_types[image_type] = attributes
|
5
|
+
docker_image_types[image_type] = attributes.reverse_merge(
|
6
|
+
# In case image doesn't get pushed, this will still be populated
|
7
|
+
alias_digest: attributes[:alias],
|
8
|
+
|
9
|
+
# By default build and push Docker image locally
|
10
|
+
strategy: :local_push,
|
11
|
+
)
|
4
12
|
|
5
13
|
set(:nomad_docker_image_types, docker_image_types)
|
6
14
|
end
|
@@ -50,12 +58,12 @@ def nomad_job(name, attributes = {})
|
|
50
58
|
|
51
59
|
desc "Run #{description_name} job"
|
52
60
|
task :run do
|
53
|
-
capistrano_nomad_run_jobs([name], namespace: namespace)
|
61
|
+
capistrano_nomad_run_jobs([name], namespace: namespace, is_detached: false)
|
54
62
|
end
|
55
63
|
|
56
64
|
desc "Purge and run #{description_name} job again"
|
57
65
|
task :rerun do
|
58
|
-
capistrano_nomad_rerun_jobs([name], namespace: namespace)
|
66
|
+
capistrano_nomad_rerun_jobs([name], namespace: namespace, is_detached: false)
|
59
67
|
end
|
60
68
|
|
61
69
|
desc "Upload and plan #{description_name} job"
|
@@ -65,22 +73,17 @@ def nomad_job(name, attributes = {})
|
|
65
73
|
|
66
74
|
desc "Upload and run #{description_name} job"
|
67
75
|
task :upload_run do
|
68
|
-
capistrano_nomad_upload_run_jobs([name], namespace: namespace)
|
76
|
+
capistrano_nomad_upload_run_jobs([name], namespace: namespace, is_detached: false)
|
69
77
|
end
|
70
78
|
|
71
79
|
desc "Upload and re-run #{description_name} job"
|
72
80
|
task :upload_rerun do
|
73
|
-
capistrano_nomad_upload_rerun_jobs([name], namespace: namespace)
|
81
|
+
capistrano_nomad_upload_rerun_jobs([name], namespace: namespace, is_detached: false)
|
74
82
|
end
|
75
83
|
|
76
84
|
desc "Deploy #{description_name} job"
|
77
85
|
task :deploy do
|
78
|
-
capistrano_nomad_deploy_jobs([name], namespace: namespace)
|
79
|
-
end
|
80
|
-
|
81
|
-
desc "Restart #{description_name} job"
|
82
|
-
task :restart do
|
83
|
-
capistrano_nomad_restart_jobs([name], namespace: namespace)
|
86
|
+
capistrano_nomad_deploy_jobs([name], namespace: namespace, is_detached: false)
|
84
87
|
end
|
85
88
|
|
86
89
|
desc "Stop #{description_name} job"
|
@@ -88,6 +91,11 @@ def nomad_job(name, attributes = {})
|
|
88
91
|
capistrano_nomad_stop_jobs([name], namespace: namespace)
|
89
92
|
end
|
90
93
|
|
94
|
+
desc "Restart #{description_name} job"
|
95
|
+
task :restart do
|
96
|
+
capistrano_nomad_restart_jobs([name], namespace: namespace)
|
97
|
+
end
|
98
|
+
|
91
99
|
desc "Purge #{description_name} job"
|
92
100
|
task :purge do
|
93
101
|
capistrano_nomad_purge_jobs([name], namespace: namespace, is_detached: false)
|
@@ -98,9 +106,32 @@ def nomad_job(name, attributes = {})
|
|
98
106
|
capistrano_nomad_display_job_status(name, namespace: namespace)
|
99
107
|
end
|
100
108
|
|
101
|
-
desc "Open console to #{description_name} job. Specify task
|
109
|
+
desc "Open console to #{description_name} job. Specify task with TASK, command with CMD"
|
102
110
|
task :console do
|
103
|
-
|
111
|
+
command = ENV["CMD"].presence || "/bin/bash"
|
112
|
+
|
113
|
+
capistrano_nomad_exec_within_job(name, command, namespace: namespace, task: ENV["TASK"])
|
114
|
+
end
|
115
|
+
|
116
|
+
desc "Display stdout and stderr of #{description_name} job. Specify task with TASK"
|
117
|
+
task :logs do
|
118
|
+
capistrano_nomad_tail_job_logs(name, namespace: namespace, stdout: true)
|
119
|
+
capistrano_nomad_tail_job_logs(name, namespace: namespace, stderr: true)
|
120
|
+
end
|
121
|
+
|
122
|
+
desc "Display stdout of #{description_name} job. Specify task with TASK"
|
123
|
+
task :stdout do
|
124
|
+
capistrano_nomad_tail_job_logs(name, namespace: namespace, stdout: true)
|
125
|
+
end
|
126
|
+
|
127
|
+
desc "Display stderr of #{description_name} job. Specify task with TASK"
|
128
|
+
task :stderr do
|
129
|
+
capistrano_nomad_tail_job_logs(name, namespace: namespace, stderr: true)
|
130
|
+
end
|
131
|
+
|
132
|
+
desc "Follow logs of #{description_name} job. Specify task with TASK"
|
133
|
+
task :follow do
|
134
|
+
capistrano_nomad_display_job_logs(name, namespace: namespace, f: true)
|
104
135
|
end
|
105
136
|
end
|
106
137
|
end
|
@@ -116,3 +147,7 @@ def nomad_job(name, attributes = {})
|
|
116
147
|
end
|
117
148
|
end
|
118
149
|
end
|
150
|
+
|
151
|
+
def nomad_template_helpers(&block)
|
152
|
+
CapistranoNomadErbNamespace.class_eval(&block)
|
153
|
+
end
|
@@ -10,16 +10,6 @@ class CapistranoNomadErbNamespace
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
# Use default value passed in unless `nomad_job_task_cpu_resource` is set
|
14
|
-
def build_nomad_job_task_cpu_resource(default:)
|
15
|
-
nomad_job_task_cpu_resource == "null" ? default : nomad_job_task_cpu_resource
|
16
|
-
end
|
17
|
-
|
18
|
-
# Use default value passed in unless `nomad_job_task_memory_resource` is set
|
19
|
-
def build_nomad_job_task_memory_resource(default:)
|
20
|
-
nomad_job_task_memory_resource == "null" ? default : nomad_job_task_memory_resource
|
21
|
-
end
|
22
|
-
|
23
13
|
# rubocop:disable Style/MissingRespondToMissing
|
24
14
|
def method_missing(name, *args)
|
25
15
|
instance_variable = "@#{name}"
|
@@ -106,7 +96,7 @@ def capistrano_nomad_run_nomad_command(kind, *args)
|
|
106
96
|
end
|
107
97
|
|
108
98
|
def capistrano_nomad_execute_nomad_command(*args)
|
109
|
-
|
99
|
+
capistrano_nomad_run_remotely do |host|
|
110
100
|
run_interactively(host) do
|
111
101
|
capistrano_nomad_run_nomad_command(:execute, *args)
|
112
102
|
end
|
@@ -116,32 +106,59 @@ end
|
|
116
106
|
def capistrano_nomad_capture_nomad_command(*args)
|
117
107
|
output = nil
|
118
108
|
|
119
|
-
|
109
|
+
capistrano_nomad_run_remotely do
|
120
110
|
output = capistrano_nomad_run_nomad_command(:capture, *args)
|
121
111
|
end
|
122
112
|
|
123
113
|
output
|
124
114
|
end
|
125
115
|
|
126
|
-
def
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
116
|
+
def capistrano_nomad_find_job_task_details(name, namespace: nil, task: nil)
|
117
|
+
task = task.presence || name
|
118
|
+
|
119
|
+
# Find alloc id that contains task
|
120
|
+
allocs_output = capistrano_nomad_capture_nomad_command(
|
121
|
+
:job,
|
122
|
+
:allocs,
|
123
|
+
{ namespace: namespace, t: "'{{range .}}{{ .ID }},{{ .TaskGroup }}|{{end}}'" },
|
124
|
+
name,
|
125
|
+
)
|
126
|
+
alloc_id = allocs_output.split("|").map { |s| s.split(",") }.find { |_, t| t == task.to_s }&.first
|
127
|
+
|
128
|
+
# Can't continue if we can't choose an alloc id
|
129
|
+
return unless alloc_id
|
130
|
+
|
131
|
+
tasks_output = capistrano_nomad_capture_nomad_command(
|
132
|
+
:alloc,
|
133
|
+
:status,
|
134
|
+
{ namespace: namespace, t: "'{{range $key, $value := .TaskStates}}{{ $key }},{{ .State }}|{{end}}'" },
|
135
|
+
alloc_id,
|
136
|
+
)
|
137
|
+
tasks_by_score = tasks_output.split("|").each_with_object({}) do |task_output, hash|
|
138
|
+
task, state = task_output.split(",")
|
139
|
+
|
140
|
+
score = 0
|
141
|
+
score += 5 if state == "running"
|
142
|
+
score += 5 unless task.match?(/connect-proxy/)
|
143
|
+
|
144
|
+
hash[task] = score
|
145
|
+
end
|
146
|
+
task = tasks_by_score.max_by { |_, v| v }.first
|
138
147
|
|
139
|
-
|
148
|
+
{
|
149
|
+
alloc_id: alloc_id,
|
150
|
+
name: task,
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
def capistrano_nomad_exec_within_job(name, command, namespace: nil, task: nil)
|
155
|
+
capistrano_nomad_run_remotely do
|
156
|
+
if (task_details = capistrano_nomad_find_job_task_details(name, namespace: namespace, task: task))
|
140
157
|
capistrano_nomad_execute_nomad_command(
|
141
158
|
:alloc,
|
142
159
|
:exec,
|
143
|
-
{ namespace: namespace, task:
|
144
|
-
alloc_id,
|
160
|
+
{ namespace: namespace, task: task_details[:name] },
|
161
|
+
task_details[:alloc_id],
|
145
162
|
command,
|
146
163
|
)
|
147
164
|
else
|
@@ -157,40 +174,55 @@ def capistrano_nomad_exec_within_job(name, command, namespace: nil, task: nil)
|
|
157
174
|
end
|
158
175
|
end
|
159
176
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
177
|
+
def capistrano_nomad_upload(local_path:, remote_path:, erb_vars: {})
|
178
|
+
# If directory upload everything within the directory
|
179
|
+
if File.directory?(local_path)
|
180
|
+
Dir.glob("#{local_path}/*").each do |path|
|
181
|
+
capistrano_nomad_upload(local_path: path, remote_path: "#{remote_path}/#{File.basename(path)}")
|
182
|
+
end
|
183
|
+
else
|
184
|
+
io =
|
185
|
+
if File.extname(local_path) == ".erb"
|
186
|
+
docker_image_types = fetch(:nomad_docker_image_types)
|
187
|
+
docker_image_types_manifest = capistrano_nomad_read_docker_image_types_manifest
|
188
|
+
|
189
|
+
# Merge manifest into image types
|
190
|
+
docker_image_types_manifest.each do |manifest_image_type, manifest_attributes|
|
191
|
+
docker_image_types[manifest_image_type]&.merge!(manifest_attributes) || {}
|
192
|
+
end
|
193
|
+
|
194
|
+
# Parse manifest files using ERB
|
195
|
+
erb = ERB.new(File.open(local_path).read, trim_mode: "-")
|
196
|
+
|
197
|
+
final_erb_vars = {
|
198
|
+
git_commit_id: fetch(:current_revision) || capistrano_nomad_git_commit_id,
|
199
|
+
docker_image_types: docker_image_types,
|
200
|
+
}
|
201
|
+
|
202
|
+
# Add global ERB vars
|
203
|
+
final_erb_vars.merge!(fetch(:nomad_template_vars) || {})
|
204
|
+
|
205
|
+
# Add job-specific ERB vars
|
206
|
+
final_erb_vars.merge!(erb_vars)
|
207
|
+
|
208
|
+
# We use a custom namespace class so that we can include helper methods into the namespace to make them available
|
209
|
+
# for template to access
|
210
|
+
namespace = CapistranoNomadErbNamespace.new(
|
211
|
+
context: self,
|
212
|
+
vars: final_erb_vars,
|
213
|
+
)
|
214
|
+
|
215
|
+
StringIO.new(erb.result(namespace.instance_eval { binding }))
|
216
|
+
else
|
217
|
+
File.open(local_path)
|
218
|
+
end
|
179
219
|
|
180
|
-
|
181
|
-
|
220
|
+
capistrano_nomad_run_remotely do
|
221
|
+
# Ensure parent directory exists
|
222
|
+
execute(:mkdir, "-p", File.dirname(remote_path))
|
182
223
|
|
183
|
-
|
184
|
-
|
185
|
-
namespace = CapistranoNomadErbNamespace.new(
|
186
|
-
context: self,
|
187
|
-
vars: final_erb_vars,
|
188
|
-
)
|
189
|
-
erb_io = StringIO.new(erb.result(namespace.instance_eval { binding }))
|
190
|
-
|
191
|
-
on(roles(:manager)) do
|
192
|
-
execute(:mkdir, "-p", File.dirname(remote_path))
|
193
|
-
upload!(erb_io, remote_path)
|
224
|
+
upload!(io, remote_path)
|
225
|
+
end
|
194
226
|
end
|
195
227
|
end
|
196
228
|
|
@@ -236,7 +268,7 @@ def capistrano_nomad_upload_jobs(names, *args)
|
|
236
268
|
uniq_var_files = names.map { |n| capistrano_nomad_fetch_job_var_files(n, *args) }.flatten.uniq
|
237
269
|
|
238
270
|
uniq_var_files.each do |var_file|
|
239
|
-
|
271
|
+
capistrano_nomad_upload(
|
240
272
|
local_path: capistrano_nomad_build_local_var_file_path(var_file, *args),
|
241
273
|
remote_path: capistrano_nomad_build_release_var_file_path(var_file, *args),
|
242
274
|
)
|
@@ -252,7 +284,7 @@ def capistrano_nomad_upload_jobs(names, *args)
|
|
252
284
|
# Can set a custom template instead
|
253
285
|
file_basename = nomad_job_options[:template] || name
|
254
286
|
|
255
|
-
|
287
|
+
capistrano_nomad_upload(
|
256
288
|
local_path: capistrano_nomad_build_local_job_path(file_basename, *args),
|
257
289
|
remote_path: capistrano_nomad_build_release_job_path(name, *args),
|
258
290
|
erb_vars: erb_vars,
|
@@ -269,11 +301,11 @@ def capistrano_nomad_plan_jobs(names, *args)
|
|
269
301
|
end
|
270
302
|
end
|
271
303
|
|
272
|
-
def capistrano_nomad_run_jobs(names, namespace: nil)
|
304
|
+
def capistrano_nomad_run_jobs(names, namespace: nil, is_detached: true)
|
273
305
|
names.each do |name|
|
274
306
|
run_options = {
|
275
307
|
namespace: namespace,
|
276
|
-
detach:
|
308
|
+
detach: is_detached,
|
277
309
|
|
278
310
|
# Don't reset counts since they may have been scaled
|
279
311
|
preserve_counts: true,
|
@@ -292,44 +324,52 @@ def capistrano_nomad_run_jobs(names, namespace: nil)
|
|
292
324
|
end
|
293
325
|
|
294
326
|
# Remove job and run again
|
295
|
-
def capistrano_nomad_rerun_jobs(names)
|
327
|
+
def capistrano_nomad_rerun_jobs(names, **options)
|
328
|
+
general_options = options.slice!(:is_detached)
|
329
|
+
|
296
330
|
names.each do |name|
|
297
331
|
# Wait for jobs to be purged before running again
|
298
|
-
capistrano_nomad_purge_jobs([name], is_detached: false)
|
332
|
+
capistrano_nomad_purge_jobs([name], **general_options.merge(is_detached: false))
|
299
333
|
|
300
|
-
capistrano_nomad_run_jobs([name])
|
334
|
+
capistrano_nomad_run_jobs([name], **general_options.merge(options))
|
301
335
|
end
|
302
336
|
end
|
303
337
|
|
304
|
-
def capistrano_nomad_upload_plan_jobs(names,
|
305
|
-
capistrano_nomad_upload_jobs(names,
|
306
|
-
capistrano_nomad_plan_jobs(names,
|
338
|
+
def capistrano_nomad_upload_plan_jobs(names, **options)
|
339
|
+
capistrano_nomad_upload_jobs(names, **options)
|
340
|
+
capistrano_nomad_plan_jobs(names, **options)
|
307
341
|
end
|
308
342
|
|
309
|
-
def capistrano_nomad_upload_run_jobs(names,
|
310
|
-
|
311
|
-
|
343
|
+
def capistrano_nomad_upload_run_jobs(names, **options)
|
344
|
+
general_options = options.slice!(:is_detached)
|
345
|
+
|
346
|
+
capistrano_nomad_upload_jobs(names, **general_options)
|
347
|
+
capistrano_nomad_run_jobs(names, **general_options.merge(options))
|
312
348
|
end
|
313
349
|
|
314
|
-
def capistrano_nomad_upload_rerun_jobs(names,
|
315
|
-
|
316
|
-
|
350
|
+
def capistrano_nomad_upload_rerun_jobs(names, **options)
|
351
|
+
general_options = options.slice!(:is_detached)
|
352
|
+
|
353
|
+
capistrano_nomad_upload_jobs(names, **general_options)
|
354
|
+
capistrano_nomad_rerun_jobs(names, **general_options.merge(options))
|
317
355
|
end
|
318
356
|
|
319
|
-
def capistrano_nomad_deploy_jobs(names,
|
320
|
-
|
321
|
-
|
357
|
+
def capistrano_nomad_deploy_jobs(names, **options)
|
358
|
+
general_options = options.slice!(:is_detached)
|
359
|
+
|
360
|
+
capistrano_nomad_assemble_jobs_docker_images(names, **general_options)
|
361
|
+
capistrano_nomad_upload_run_jobs(names, **general_options.merge(options))
|
322
362
|
end
|
323
363
|
|
324
|
-
def capistrano_nomad_stop_jobs(names,
|
364
|
+
def capistrano_nomad_stop_jobs(names, **options)
|
325
365
|
names.each do |name|
|
326
|
-
capistrano_nomad_execute_nomad_command(:job, :stop,
|
366
|
+
capistrano_nomad_execute_nomad_command(:job, :stop, options, name)
|
327
367
|
end
|
328
368
|
end
|
329
369
|
|
330
|
-
def capistrano_nomad_restart_jobs(names,
|
370
|
+
def capistrano_nomad_restart_jobs(names, **options)
|
331
371
|
names.each do |name|
|
332
|
-
capistrano_nomad_execute_nomad_command(:job, :restart,
|
372
|
+
capistrano_nomad_execute_nomad_command(:job, :restart, options, name)
|
333
373
|
end
|
334
374
|
end
|
335
375
|
|
@@ -339,6 +379,29 @@ def capistrano_nomad_purge_jobs(names, namespace: nil, is_detached: true)
|
|
339
379
|
end
|
340
380
|
end
|
341
381
|
|
342
|
-
def capistrano_nomad_display_job_status(name,
|
343
|
-
capistrano_nomad_execute_nomad_command(:status,
|
382
|
+
def capistrano_nomad_display_job_status(name, **options)
|
383
|
+
capistrano_nomad_execute_nomad_command(:status, options, name)
|
384
|
+
end
|
385
|
+
|
386
|
+
def capistrano_nomad_display_job_logs(name, namespace: nil, **options)
|
387
|
+
if (task_details = capistrano_nomad_find_job_task_details(name, namespace: namespace, task: ENV["TASK"]))
|
388
|
+
capistrano_nomad_execute_nomad_command(
|
389
|
+
:alloc,
|
390
|
+
:logs,
|
391
|
+
options.merge(namespace: namespace, task: task_details[:name]),
|
392
|
+
task_details[:alloc_id],
|
393
|
+
)
|
394
|
+
else
|
395
|
+
# If task can't be determined choose a random allocation
|
396
|
+
capistrano_nomad_execute_nomad_command(
|
397
|
+
:alloc,
|
398
|
+
:logs,
|
399
|
+
options.merge(namespace: namespace, job: true),
|
400
|
+
name,
|
401
|
+
)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def capistrano_nomad_tail_job_logs(*args, **options)
|
406
|
+
capistrano_nomad_display_job_logs(*args, **options.merge(tail: true, n: 50))
|
344
407
|
end
|
@@ -55,7 +55,7 @@ namespace :nomad do
|
|
55
55
|
end
|
56
56
|
|
57
57
|
desc "Create missing and remove unused namespaces"
|
58
|
-
task :
|
58
|
+
task :modify_namespaces do
|
59
59
|
output = capistrano_nomad_capture_nomad_command(:namespace, :list, t: "'{{range .}}{{ .Name }}|{{end}}'")
|
60
60
|
current_namespaces = output.split("|").compact.map(&:to_sym)
|
61
61
|
desired_namespaces = fetch(:nomad_jobs).keys
|
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.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Hu
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|