nutkins 0.6.1 → 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 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