capistrano-nomad 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|