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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef4b59fb3d6c03246d6b310132556bcac28545c6
4
- data.tar.gz: c4491a84ab7d296609fb4c7f48a6fa54dba79cf4
3
+ metadata.gz: 3bfa69ab3c9cd1fa876a151921a9e04f1af3de8d
4
+ data.tar.gz: 52b50703467b3f9f010733a7681f9739653002c9
5
5
  SHA512:
6
- metadata.gz: df798fc1aa5262eaa5523566be44d690498705a7e641d6b446e9df53c58f586aba6c5bf88436b1b8b32da5274f6b9e357b7b8398689f7a4bfd3e8c7c58e6e8eb
7
- data.tar.gz: 59860a712db9a24d02479794ef8a2f09f0c978c989c8b87843d773b4e2e56da76877c12d9b78f159db2dc1eff7e93a2cf6ac59b523d8b64ab08c7ca548ba7d09
6
+ metadata.gz: a6ff593c34f989c41d76c1f45395ed35b2a44dd289ae1e1fbe9ced7909467315c15a8a3896b712874d5ab4895748c3ab3d46187a8ec6048c0ef82e341c8dcbca
7
+ data.tar.gz: 258db6118fb8c67204521773771497b1e6f51a21731feb41ab6a82a1274fb36b43a426f6dce404bd261f29e3dd522d75b86e052d1341fcd36218e1ea751882b5
@@ -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.container_id_for_tag tag
9
- regex = /^[0-9a-f]+ +#{tag} +/
10
- `docker ps -a`.each_line do |line|
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
@@ -1,3 +1,3 @@
1
1
  module Nutkins
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
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
- prev_image_id = Docker.image_id_for_tag tag
47
-
48
- if Docker.run 'build', '-t', cfg['latest_tag'], '-t', tag, img_dir, stdout: true
49
- image_id = Docker.image_id_for_tag tag
50
- if prev_image_id and image_id != prev_image_id
51
- puts "deleting previous image #{prev_image_id}"
52
- Docker.run "rmi", prev_image_id
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
- raise "issue building docker image for #{img_name}"
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.6.1
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-21 00:00:00.000000000 Z
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