nutkins 0.6.1 → 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/lib/nutkins/docker.rb +17 -3
- data/lib/nutkins/docker_builder.rb +120 -0
- data/lib/nutkins/version.rb +1 -1
- data/lib/nutkins.rb +18 -8
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bfa69ab3c9cd1fa876a151921a9e04f1af3de8d
|
4
|
+
data.tar.gz: 52b50703467b3f9f010733a7681f9739653002c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6ff593c34f989c41d76c1f45395ed35b2a44dd289ae1e1fbe9ced7909467315c15a8a3896b712874d5ab4895748c3ab3d46187a8ec6048c0ef82e341c8dcbca
|
7
|
+
data.tar.gz: 258db6118fb8c67204521773771497b1e6f51a21731feb41ab6a82a1274fb36b43a426f6dce404bd261f29e3dd522d75b86e052d1341fcd36218e1ea751882b5
|
data/lib/nutkins/docker.rb
CHANGED
@@ -5,14 +5,28 @@ module Nutkins::Docker
|
|
5
5
|
self.run_get_stdout 'inspect', '--format="{{.Id}}"', tag
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.
|
9
|
-
|
10
|
-
|
8
|
+
def self.kill_and_remove_container id
|
9
|
+
raise "issue killing existing container" unless Nutkins::Docker.run 'kill', id
|
10
|
+
raise "issue removing existing container" unless Nutkins::Docker.run 'rm', id
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.container_id_for_tag tag, running: false
|
14
|
+
flags = running ? '' : ' -a'
|
15
|
+
regex = /^[0-9a-f]+ +#{Regexp.escape tag} +/
|
16
|
+
`docker ps#{flags}`.each_line do |line|
|
11
17
|
return line.split(' ')[0] if line =~ regex
|
12
18
|
end
|
13
19
|
nil
|
14
20
|
end
|
15
21
|
|
22
|
+
def self.get_short_commit commit
|
23
|
+
if /^sha256:/.match(commit)
|
24
|
+
commit[7...19]
|
25
|
+
else
|
26
|
+
commit
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
16
30
|
def self.container_id_for_name name
|
17
31
|
self.run_get_stdout 'inspect', '--format="{{.Id}}"', name
|
18
32
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require_relative "docker"
|
2
|
+
require "json"
|
3
|
+
require "digest"
|
4
|
+
|
5
|
+
module Nutkins::DockerBuilder
|
6
|
+
def self.build cfg
|
7
|
+
base = cfg["base"]
|
8
|
+
raise "to use build commands you must specify the base image" unless base
|
9
|
+
|
10
|
+
# TODO: build cache from this and use to determine restore point
|
11
|
+
# Nutkins::Docker.run 'inspect', tag, stderr: false
|
12
|
+
|
13
|
+
unless Nutkins::Docker.run 'inspect', base, stderr: false
|
14
|
+
puts "getting base image"
|
15
|
+
Docker.run 'pull', base, stdout: true
|
16
|
+
end
|
17
|
+
|
18
|
+
cont_id = Nutkins::Docker.container_id_for_tag base, running: true
|
19
|
+
if cont_id
|
20
|
+
puts "found existing container #{cont_id}"
|
21
|
+
Nutkins::Docker.kill_and_remove_container cont_id
|
22
|
+
puts "killed and removed existing container"
|
23
|
+
end
|
24
|
+
|
25
|
+
# the base image to start rebuilding from
|
26
|
+
parent_img_id = base
|
27
|
+
cont_id = nil
|
28
|
+
pwd = Dir.pwd
|
29
|
+
begin
|
30
|
+
Dir.chdir cfg["directory"]
|
31
|
+
|
32
|
+
cache_is_dirty = false
|
33
|
+
build_commands = cfg["build"]["commands"]
|
34
|
+
build_commands.each do |build_cmd|
|
35
|
+
cmd = /^\w+/.match(build_cmd).to_s.downcase
|
36
|
+
cmd_args = build_cmd[(cmd.length + 1)..-1].strip
|
37
|
+
|
38
|
+
docker_args = []
|
39
|
+
# the commit_msg is used to look up cache entries, it can be
|
40
|
+
# modified if the command uses dynamic data, e.g. to add checksums
|
41
|
+
commit_msg = nil
|
42
|
+
|
43
|
+
case cmd
|
44
|
+
when "run"
|
45
|
+
cmd_args.gsub! /\n+/, ' '
|
46
|
+
docker_args = ['exec', '%CONT_ID%', cfg['shell'], '-c', cmd_args]
|
47
|
+
commit_msg = cmd + ' ' + cmd_args
|
48
|
+
when "add"
|
49
|
+
*srcs, dest = cmd_args.split ' '
|
50
|
+
srcs = srcs.map { |src| Dir.glob src }.flatten
|
51
|
+
|
52
|
+
docker_args = srcs.map { |src| ['cp', src, '%CONT_ID%' + ':' + dest] }
|
53
|
+
# ensure checksum of each file is embedded into commit_msg
|
54
|
+
# if any file changes the cache is dirtied
|
55
|
+
commit_msg = 'add ' + srcs.map do |src|
|
56
|
+
src + ':' + Digest::MD5.file(src).to_s
|
57
|
+
end.push(dest).join(' ')
|
58
|
+
when "cmd", "entrypoint", "env", "expose", "label", "onbuild", "user", "volume", "workdir"
|
59
|
+
commit_msg = commit_change = build_cmd
|
60
|
+
else
|
61
|
+
raise "unsupported command: #{cmd}"
|
62
|
+
# TODO add metadata flags
|
63
|
+
end
|
64
|
+
|
65
|
+
if (commit_change or docker_args) and commit_msg
|
66
|
+
commit_msg = "#{parent_img_id} -> #{commit_msg}"
|
67
|
+
|
68
|
+
unless cache_is_dirty
|
69
|
+
# searches the commit messages of all images for the one matching the expected
|
70
|
+
# cache entry for the given content
|
71
|
+
all_images = Nutkins::Docker.run_get_stdout('images', '-aq').split("\n")
|
72
|
+
images_meta = JSON.parse(Nutkins::Docker.run_get_stdout('inspect', *all_images))
|
73
|
+
cache_entry = images_meta.find do |image_meta|
|
74
|
+
if image_meta['Comment'] == commit_msg
|
75
|
+
parent_img_id = Nutkins::Docker.get_short_commit(image_meta['Id'])
|
76
|
+
true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
if cache_entry
|
81
|
+
puts "cached: #{commit_msg}"
|
82
|
+
next
|
83
|
+
else
|
84
|
+
puts "starting build container from commit #{parent_img_id}"
|
85
|
+
Nutkins::Docker.run 'run', '-d', parent_img_id, 'sleep', '3600'
|
86
|
+
cont_id = Nutkins::Docker.container_id_for_tag parent_img_id, running: true
|
87
|
+
puts "started build container #{cont_id}"
|
88
|
+
cache_is_dirty = true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
puts "#{cmd}: #{cmd_args}"
|
93
|
+
|
94
|
+
unless docker_args.empty?
|
95
|
+
# docker can be an array of one set of args, or an array of arrays of args
|
96
|
+
docker_args = [ docker_args ] unless docker_args[0].kind_of? Array
|
97
|
+
docker_args.each do |one_docker_args|
|
98
|
+
run_args = one_docker_args.map { |arg| arg.gsub '%CONT_ID%', cont_id }
|
99
|
+
puts "run #{run_args.join ' '}"
|
100
|
+
unless Nutkins::Docker.run *run_args, stdout: true
|
101
|
+
raise "build failed: #{one_docker_args.join ' '}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
commit_args = commit_change ? ['-c', commit_change] : []
|
107
|
+
parent_img_id = Nutkins::Docker.run_get_stdout 'commit', '-m', commit_msg, *commit_args, cont_id
|
108
|
+
raise "could not commit docker image" if parent_img_id.nil?
|
109
|
+
parent_img_id = Nutkins::Docker.get_short_commit parent_img_id
|
110
|
+
else
|
111
|
+
puts "TODO: support cmd #{build_cmd}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
ensure
|
115
|
+
Dir.chdir pwd
|
116
|
+
Nutkins::Docker.kill_and_remove_container cont_id if cont_id
|
117
|
+
puts "killed and removed build container"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/nutkins/version.rb
CHANGED
data/lib/nutkins.rb
CHANGED
@@ -9,6 +9,7 @@ require "net/http"
|
|
9
9
|
module Nutkins ; end
|
10
10
|
|
11
11
|
require "nutkins/docker"
|
12
|
+
require "nutkins/docker_builder"
|
12
13
|
require "nutkins/download"
|
13
14
|
require "nutkins/version"
|
14
15
|
|
@@ -36,6 +37,8 @@ module Nutkins
|
|
36
37
|
raise "directory `#{img_dir}' does not exist" unless Dir.exists? img_dir
|
37
38
|
tag = cfg['tag']
|
38
39
|
|
40
|
+
prev_image_id = Docker.image_id_for_tag tag
|
41
|
+
|
39
42
|
build_cfg = cfg["build"]
|
40
43
|
if build_cfg
|
41
44
|
# download each of the files in the resources section if it doesn't exist
|
@@ -43,16 +46,22 @@ module Nutkins
|
|
43
46
|
Download.download_resources img_dir, resources if resources
|
44
47
|
end
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
if cfg.dig "build", "commands"
|
50
|
+
# if build commands are available use nutkins built-in builder
|
51
|
+
DockerBuilder::build cfg
|
52
|
+
else
|
53
|
+
# fallback to `docker build` which is less good
|
54
|
+
if not Docker.run 'build', '-t', cfg['latest_tag'], '-t', tag, img_dir, stdout: true
|
55
|
+
raise "issue building docker image for #{img_name}"
|
53
56
|
end
|
57
|
+
end
|
58
|
+
|
59
|
+
image_id = Docker.image_id_for_tag tag
|
60
|
+
if prev_image_id and image_id != prev_image_id
|
61
|
+
puts "deleting previous image #{prev_image_id}"
|
62
|
+
Docker.run "rmi", prev_image_id
|
54
63
|
else
|
55
|
-
|
64
|
+
puts "image is identical to cached version"
|
56
65
|
end
|
57
66
|
end
|
58
67
|
|
@@ -286,6 +295,7 @@ module Nutkins
|
|
286
295
|
img_cfg_path = File.join directory, IMG_CONFIG_FILE_NAME
|
287
296
|
img_cfg = File.exists?(img_cfg_path) ? YAML.load_file(img_cfg_path) : {}
|
288
297
|
img_cfg['image'] ||= path if path != '.'
|
298
|
+
img_cfg['shell'] ||= 'sh'
|
289
299
|
img_cfg['directory'] = directory
|
290
300
|
img_cfg["version"] ||= @config.version if @config.version
|
291
301
|
img_cfg['version'] = img_cfg['version'].to_s
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nutkins
|
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 Pike
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- circle.yml
|
87
87
|
- lib/nutkins.rb
|
88
88
|
- lib/nutkins/docker.rb
|
89
|
+
- lib/nutkins/docker_builder.rb
|
89
90
|
- lib/nutkins/download.rb
|
90
91
|
- lib/nutkins/version.rb
|
91
92
|
- nutkins.gemspec
|