nutkins 0.9.0 → 0.10.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/bin/nutkins +13 -13
- data/lib/hash_dig.rb +14 -0
- data/lib/nutkins/docker_builder.rb +16 -14
- data/lib/nutkins/version.rb +1 -1
- data/lib/nutkins.rb +74 -41
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e73d665ef1da7e2713d6d7ab469d963f50969c39
|
4
|
+
data.tar.gz: 02399c0c0dca4636432ec49625978433b9e3f994
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d0cc75a3a1d4b93d7ffc50ebed8233e69d3c17577cabffe1f96e94a4aee7ac94432b683e479b5fdd83f11f390bd6c2003ce699d5dac4ed259429b54c4e6b140
|
7
|
+
data.tar.gz: eca9bb3a1db88257cf7813e0eb62fe2cab4c5ec7b41067ac4d691f010b9de3ac9a778f505ec1370c441a3ec4559a9e5faff6e9d0c92a7ac7eaba3630498f1256
|
data/bin/nutkins
CHANGED
@@ -24,24 +24,24 @@ module Nutkins::Command
|
|
24
24
|
|
25
25
|
op.on '-p', '--project dir', 'override path to project', 'project_dir'
|
26
26
|
|
27
|
-
op.subcommand 'build,b *
|
27
|
+
op.subcommand 'build,b *paths', 'build docker image[s] from nutkin.yamls/dockerfiles'
|
28
28
|
|
29
|
-
op.subcommand 'create,c *
|
29
|
+
op.subcommand 'create,c *paths', 'create container from image' do |subop|
|
30
30
|
subop.on '-r', '--reuse', 'reuse previously built image', 'reuse'
|
31
31
|
subop.on '-p', '--preserve', 'preserve existing container', 'preserve'
|
32
32
|
end
|
33
33
|
|
34
|
-
op.subcommand 'delete,d *
|
34
|
+
op.subcommand 'delete,d *paths', 'delete container corresponding to image'
|
35
35
|
op.subcommand 'delete-all', 'delete containers corresponding to all images'
|
36
36
|
|
37
|
-
op.subcommand 'run,r
|
37
|
+
op.subcommand 'run,r path', 'run created container' do |subop|
|
38
38
|
subop.on '-r', '--reuse', 'reuse previously created container', 'reuse'
|
39
39
|
subop.on '-s', '--shell', 'add bash shell to console', 'shell'
|
40
40
|
end
|
41
41
|
|
42
|
-
op.subcommand 'exec,e
|
42
|
+
op.subcommand 'exec,e path *cmd', 'execute a command in a running container'
|
43
43
|
op.subcommand 'build-secret,B path', 'build secret files/volumes'
|
44
|
-
op.subcommand 'extract-secrets,X [*
|
44
|
+
op.subcommand 'extract-secrets,X [*paths]', 'extract secret files/volumes'
|
45
45
|
|
46
46
|
op.subcommand 'start-etcd', 'start container running etcd'
|
47
47
|
op.subcommand 'stop-etcd', 'stop container running etcd'
|
@@ -62,23 +62,23 @@ module Nutkins::Command
|
|
62
62
|
|
63
63
|
case command
|
64
64
|
when 'build'
|
65
|
-
config.
|
65
|
+
config.paths.each &nutkins.method(:build)
|
66
66
|
when 'create'
|
67
|
-
config.
|
68
|
-
nutkins.create
|
67
|
+
config.paths.each do |path|
|
68
|
+
nutkins.create path, preserve: config.preserve, reuse: config.reuse
|
69
69
|
end
|
70
70
|
when 'delete'
|
71
|
-
config.
|
71
|
+
config.paths.each &nutkins.method(:delete)
|
72
72
|
when 'delete-all'
|
73
73
|
nutkins.delete_all
|
74
74
|
when 'run'
|
75
|
-
nutkins.run config.
|
75
|
+
nutkins.run config.path, reuse: config.reuse, shell: config.shell
|
76
76
|
when 'exec'
|
77
|
-
nutkins.exec config.
|
77
|
+
nutkins.exec config.path, config.cmd
|
78
78
|
when 'build-secret'
|
79
79
|
nutkins.build_secret config.path
|
80
80
|
when 'extract-secrets'
|
81
|
-
nutkins.extract_secrets config.
|
81
|
+
nutkins.extract_secrets config.paths
|
82
82
|
when 'start-etcd'
|
83
83
|
nutkins.start_etcd_container
|
84
84
|
when 'stop-etcd'
|
data/lib/hash_dig.rb
ADDED
@@ -2,21 +2,23 @@ require_relative "docker"
|
|
2
2
|
require "json"
|
3
3
|
require "digest"
|
4
4
|
|
5
|
-
module Nutkins::
|
5
|
+
module Nutkins::Docker::Builder
|
6
|
+
Docker = Nutkins::Docker
|
7
|
+
|
6
8
|
def self.build cfg
|
7
9
|
base = cfg["base"]
|
8
10
|
raise "to use build commands you must specify the base image" unless base
|
9
11
|
|
10
12
|
# TODO: build cache from this and use to determine restore point
|
11
|
-
#
|
13
|
+
# Docker.run 'inspect', tag, stderr: false
|
12
14
|
|
13
|
-
unless
|
15
|
+
unless Docker.run 'inspect', base, stderr: false
|
14
16
|
puts "getting base image"
|
15
17
|
Docker.run 'pull', base, stdout: true
|
16
18
|
end
|
17
19
|
|
18
20
|
# the base image to start rebuilding from
|
19
|
-
parent_img_id =
|
21
|
+
parent_img_id = Docker.get_short_commit Docker.container_id_for_name(base)
|
20
22
|
pwd = Dir.pwd
|
21
23
|
begin
|
22
24
|
Dir.chdir cfg["directory"]
|
@@ -80,7 +82,7 @@ module Nutkins::DockerBuilder
|
|
80
82
|
|
81
83
|
if run_args
|
82
84
|
puts "run #{run_args}"
|
83
|
-
unless
|
85
|
+
unless Docker.run 'run', parent_img_id, *run_shell_cmd, stdout: true
|
84
86
|
raise "run failed: #{run_args}"
|
85
87
|
end
|
86
88
|
|
@@ -88,18 +90,18 @@ module Nutkins::DockerBuilder
|
|
88
90
|
begin
|
89
91
|
if add_files
|
90
92
|
add_files.each do |src|
|
91
|
-
if not
|
93
|
+
if not Docker.run 'cp', src, "#{cont_id}:#{add_files_dest}"
|
92
94
|
raise "could not copy #{src} to #{cont_id}:#{add_files_dest}"
|
93
95
|
end
|
94
96
|
end
|
95
97
|
end
|
96
98
|
|
97
99
|
commit_args = env_args ? ['-c', env_args] : []
|
98
|
-
parent_img_id =
|
100
|
+
parent_img_id = Docker.run_get_stdout 'commit', *commit_args, cont_id
|
99
101
|
raise "could not commit docker image" if parent_img_id.nil?
|
100
|
-
parent_img_id =
|
102
|
+
parent_img_id = Docker.get_short_commit parent_img_id
|
101
103
|
ensure
|
102
|
-
if not
|
104
|
+
if not Docker.run 'rm', cont_id
|
103
105
|
puts "could not remove build container #{cont_id}"
|
104
106
|
end
|
105
107
|
end
|
@@ -112,16 +114,16 @@ module Nutkins::DockerBuilder
|
|
112
114
|
Dir.chdir pwd
|
113
115
|
end
|
114
116
|
|
115
|
-
|
117
|
+
Docker.run 'tag', parent_img_id, cfg['tag']
|
116
118
|
end
|
117
119
|
|
118
120
|
def self.find_cached_img_id parent_img_id, command
|
119
|
-
all_images =
|
120
|
-
images_meta = JSON.parse(
|
121
|
+
all_images = Docker.run_get_stdout('images', '-aq').split("\n")
|
122
|
+
images_meta = JSON.parse(Docker.run_get_stdout('inspect', *all_images))
|
121
123
|
images_meta.each do |image_meta|
|
122
124
|
if image_meta.dig('ContainerConfig', 'Cmd') == command and
|
123
|
-
|
124
|
-
return
|
125
|
+
Docker.get_short_commit(image_meta['Parent']) == parent_img_id
|
126
|
+
return Docker.get_short_commit(image_meta['Id'])
|
125
127
|
end
|
126
128
|
end
|
127
129
|
nil
|
data/lib/nutkins/version.rb
CHANGED
data/lib/nutkins.rb
CHANGED
@@ -13,6 +13,8 @@ require "nutkins/docker_builder"
|
|
13
13
|
require "nutkins/download"
|
14
14
|
require "nutkins/version"
|
15
15
|
|
16
|
+
require_relative "hash_dig"
|
17
|
+
|
16
18
|
# Must be somedomain.net instead of somedomain.net/, otherwise, it will throw exception.
|
17
19
|
module Nutkins
|
18
20
|
CONFIG_FILE_NAME = 'nutkins.yaml'
|
@@ -22,6 +24,9 @@ module Nutkins
|
|
22
24
|
|
23
25
|
class CloudManager
|
24
26
|
def initialize(project_dir: nil)
|
27
|
+
@img_configs = {}
|
28
|
+
# when an image is built true is stored against it's name to avoid building it again
|
29
|
+
@built = {}
|
25
30
|
@project_root = project_dir || Dir.pwd
|
26
31
|
cfg_path = File.join(@project_root, CONFIG_FILE_NAME)
|
27
32
|
if File.exists? cfg_path
|
@@ -31,12 +36,23 @@ module Nutkins
|
|
31
36
|
end
|
32
37
|
end
|
33
38
|
|
34
|
-
def build
|
35
|
-
cfg = get_image_config
|
36
|
-
img_dir =
|
39
|
+
def build path
|
40
|
+
cfg = get_image_config path
|
41
|
+
img_dir = cfg['directory']
|
42
|
+
img_name = cfg['image']
|
43
|
+
return if @built[img_name]
|
44
|
+
|
37
45
|
raise "directory `#{img_dir}' does not exist" unless Dir.exists? img_dir
|
38
46
|
tag = cfg['tag']
|
39
47
|
|
48
|
+
# TODO: flag to suppress building base image?
|
49
|
+
base = cfg['base']
|
50
|
+
unless @built[base]
|
51
|
+
if image_in_project? base
|
52
|
+
puts "building parent of #{img_name}: #{base}"
|
53
|
+
build base
|
54
|
+
end
|
55
|
+
end
|
40
56
|
prev_image_id = Docker.image_id_for_tag tag
|
41
57
|
|
42
58
|
build_cfg = cfg["build"]
|
@@ -48,11 +64,11 @@ module Nutkins
|
|
48
64
|
|
49
65
|
if cfg.dig "build", "commands"
|
50
66
|
# if build commands are available use nutkins built-in builder
|
51
|
-
|
67
|
+
Docker::Builder::build cfg
|
52
68
|
else
|
53
69
|
# fallback to `docker build` which is less good
|
54
70
|
if not Docker.run 'build', '-t', cfg['latest_tag'], '-t', tag, img_dir, stdout: true
|
55
|
-
raise "issue building docker image for #{
|
71
|
+
raise "issue building docker image for #{path}"
|
56
72
|
end
|
57
73
|
end
|
58
74
|
|
@@ -69,18 +85,19 @@ module Nutkins
|
|
69
85
|
else
|
70
86
|
puts "no image exists for image... what went wrong?"
|
71
87
|
end
|
88
|
+
@built[img_name] = true
|
72
89
|
end
|
73
90
|
|
74
|
-
def create
|
91
|
+
def create path, preserve: false, docker_args: [], reuse: false
|
75
92
|
flags = []
|
76
|
-
cfg = get_image_config
|
93
|
+
cfg = get_image_config path
|
77
94
|
create_cfg = cfg["create"]
|
78
95
|
if create_cfg
|
79
96
|
(create_cfg["ports"] or []).each do |port|
|
80
97
|
flags.push '-p', "#{port}:#{port}"
|
81
98
|
end
|
82
99
|
|
83
|
-
img_dir =
|
100
|
+
img_dir = cfg['directory']
|
84
101
|
(create_cfg["volumes"] or []).each do |volume|
|
85
102
|
src, dest = volume.split ' -> '
|
86
103
|
src_dir = File.absolute_path File.join(img_dir, VOLUMES_PATH, src)
|
@@ -108,12 +125,12 @@ module Nutkins
|
|
108
125
|
Docker.run "rm", prev_container_id
|
109
126
|
prev_container_id = nil
|
110
127
|
end
|
111
|
-
build
|
128
|
+
build path
|
112
129
|
end
|
113
130
|
|
114
131
|
puts "creating new docker image"
|
115
132
|
unless Docker.run "create", "-it", *flags, tag, *docker_args
|
116
|
-
raise "failed to create `#{
|
133
|
+
raise "failed to create `#{path}' container"
|
117
134
|
end
|
118
135
|
|
119
136
|
unless preserve
|
@@ -124,16 +141,18 @@ module Nutkins
|
|
124
141
|
end
|
125
142
|
end
|
126
143
|
|
127
|
-
puts "created `#{
|
144
|
+
puts "created `#{path}' container"
|
128
145
|
end
|
129
146
|
|
130
|
-
def run
|
131
|
-
cfg = get_image_config
|
147
|
+
def run path, reuse: false, shell: false
|
148
|
+
cfg = get_image_config path
|
132
149
|
tag = cfg['tag']
|
133
150
|
create_args = []
|
134
151
|
if shell
|
135
152
|
raise '--shell and --reuse arguments are incompatible' if reuse
|
136
153
|
|
154
|
+
# TODO: fix crash when image doesn't exist yet... the tag isn't
|
155
|
+
# there to be inspected yet
|
137
156
|
# TODO: test for smell-baron
|
138
157
|
create_args = JSON.parse(`docker inspect #{tag}`)[0]["Config"]["Cmd"]
|
139
158
|
|
@@ -148,21 +167,20 @@ module Nutkins
|
|
148
167
|
|
149
168
|
id = reuse && Docker.container_id_for_tag(tag)
|
150
169
|
unless id
|
151
|
-
create
|
170
|
+
create path, docker_args: create_args
|
152
171
|
id = Docker.container_id_for_tag tag
|
153
|
-
raise "couldn't create container to run `#{
|
172
|
+
raise "couldn't create container to run `#{path}'" unless id
|
154
173
|
end
|
155
174
|
|
156
175
|
Kernel.exec "docker", "start", "-ai", id
|
157
176
|
end
|
158
177
|
|
159
|
-
def delete
|
160
|
-
cfg = get_image_config
|
178
|
+
def delete path
|
179
|
+
cfg = get_image_config path
|
161
180
|
tag = cfg['tag']
|
162
181
|
container_id = Docker.container_id_for_tag tag
|
163
182
|
raise "no container to delete" if container_id.nil?
|
164
183
|
puts "deleting container #{container_id}"
|
165
|
-
# TODO: also delete :latest
|
166
184
|
Docker.run "rm", container_id
|
167
185
|
end
|
168
186
|
|
@@ -186,13 +204,15 @@ module Nutkins
|
|
186
204
|
File.unlink secret if path_is_dir
|
187
205
|
end
|
188
206
|
|
189
|
-
def extract_secrets
|
190
|
-
if
|
191
|
-
|
207
|
+
def extract_secrets img_dirs
|
208
|
+
if img_dirs.empty?
|
209
|
+
img_dirs = get_all_img_dirs
|
210
|
+
# there may be secrets in the root even if there is no image build there
|
211
|
+
img_dirs.push '.' unless img_dirs.include? '.'
|
192
212
|
end
|
193
213
|
|
194
|
-
|
195
|
-
get_secrets(
|
214
|
+
img_dirs.each do |img_dir|
|
215
|
+
get_secrets(img_dir).each do |secret|
|
196
216
|
loop do
|
197
217
|
puts "enter passphrase for #{secret}"
|
198
218
|
break if system 'gpg', secret
|
@@ -207,10 +227,11 @@ module Nutkins
|
|
207
227
|
end
|
208
228
|
end
|
209
229
|
|
210
|
-
def exec
|
211
|
-
puts "TODO: exec #{
|
230
|
+
def exec path, *cmd
|
231
|
+
puts "TODO: exec #{path}: #{cmd.join ' '}"
|
212
232
|
end
|
213
233
|
|
234
|
+
# TODO: move this stuff into another file
|
214
235
|
def start_etcd_container
|
215
236
|
name = get_etcd_container_name
|
216
237
|
return unless name
|
@@ -232,14 +253,15 @@ module Nutkins
|
|
232
253
|
'-advertise-client-urls', "http://#{gateway}:#{ETCD_PORT}",
|
233
254
|
'-listen-client-urls', "http://0.0.0.0:#{ETCD_PORT}"
|
234
255
|
|
235
|
-
|
236
|
-
configs =
|
256
|
+
img_dirs = get_all_img_dirs
|
257
|
+
configs = img_dirs.map &method(:get_image_config)
|
237
258
|
etcd_store = {}
|
238
259
|
configs.each do |config|
|
239
260
|
etcd_store.merge! config['etcd']['data'] if config.dig('etcd', 'data')
|
240
261
|
|
241
|
-
|
242
|
-
|
262
|
+
etcd_files = config.dig('etcd', 'files')
|
263
|
+
if etcd_files
|
264
|
+
etcd_files.each do |file|
|
243
265
|
etcd_data_path = File.join config['directory'], file
|
244
266
|
begin
|
245
267
|
etcd_store.merge! YAML.load_file(etcd_data_path)
|
@@ -296,19 +318,30 @@ module Nutkins
|
|
296
318
|
repository && "nutkins-etcd-#{repository}"
|
297
319
|
end
|
298
320
|
|
321
|
+
# path should be "." or a single element path referencing the project root
|
299
322
|
def get_image_config path
|
323
|
+
cached = @img_configs[path]
|
324
|
+
return cached if cached
|
325
|
+
|
300
326
|
directory = get_project_dir(path)
|
301
327
|
img_cfg_path = File.join directory, IMG_CONFIG_FILE_NAME
|
302
|
-
|
328
|
+
raise "missing #{img_cfg_path}" unless File.exists?(img_cfg_path)
|
329
|
+
img_cfg = YAML.load_file(img_cfg_path)
|
303
330
|
img_cfg['image'] ||= path if path != '.'
|
331
|
+
raise "#{img_cfg_path} must contain 'image' entry" unless img_cfg['image']
|
332
|
+
|
304
333
|
img_cfg['shell'] ||= '/bin/sh'
|
334
|
+
img_cfg['path'] ||= img_cfg_path
|
305
335
|
img_cfg['directory'] = directory
|
306
336
|
img_cfg["version"] ||= @config.version if @config.version
|
307
337
|
img_cfg['version'] = img_cfg['version'].to_s
|
308
|
-
raise
|
338
|
+
raise "#{img_cfg_path} must contain 'version' entry" unless img_cfg.has_key? 'version'
|
309
339
|
img_cfg['latest_tag'] = get_tag img_cfg
|
310
340
|
img_cfg['tag'] = img_cfg['latest_tag'] + ':' + img_cfg['version']
|
311
|
-
|
341
|
+
|
342
|
+
base = img_cfg['base']
|
343
|
+
raise "#{img_cfg_path} must include `base` field" unless base
|
344
|
+
@img_configs[path] = img_cfg
|
312
345
|
end
|
313
346
|
|
314
347
|
def get_project_dir path
|
@@ -316,10 +349,6 @@ module Nutkins
|
|
316
349
|
end
|
317
350
|
|
318
351
|
def get_tag img_cfg
|
319
|
-
unless img_cfg.has_key? "image"
|
320
|
-
raise "nutkins.yaml should contain `image' entry for this command"
|
321
|
-
end
|
322
|
-
|
323
352
|
repository = img_cfg['repository'] || @config.repository
|
324
353
|
if repository.nil?
|
325
354
|
raise "nutkins.yaml or nutkin.yaml should contain `repository' entry for this command"
|
@@ -327,15 +356,19 @@ module Nutkins
|
|
327
356
|
repository + '/' + img_cfg['image']
|
328
357
|
end
|
329
358
|
|
330
|
-
def
|
331
|
-
Dir.glob("#{@project_root}
|
359
|
+
def get_all_img_dirs
|
360
|
+
Dir.glob("#{@project_root}{,/*}/nutkin.yaml").map do |path|
|
332
361
|
File.basename File.dirname(path)
|
333
362
|
end
|
334
363
|
end
|
335
364
|
|
336
|
-
|
337
|
-
|
338
|
-
|
365
|
+
def image_in_project? image_name
|
366
|
+
get_all_img_dirs.map(&method(:get_image_config)).find do |cfg|
|
367
|
+
cfg['image'] == image_name
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def get_secrets img_dir
|
339
372
|
Dir.glob("#{img_dir}/{volumes,secrets}/*.gpg")
|
340
373
|
end
|
341
374
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nutkins
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Pike
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- bin/nutkins
|
85
85
|
- bin/setup
|
86
86
|
- circle.yml
|
87
|
+
- lib/hash_dig.rb
|
87
88
|
- lib/nutkins.rb
|
88
89
|
- lib/nutkins/docker.rb
|
89
90
|
- lib/nutkins/docker_builder.rb
|