docker-template 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +13 -0
  3. data/LICENSE +13 -0
  4. data/README.md +20 -0
  5. data/Rakefile +20 -0
  6. data/lib/docker/template.rb +86 -0
  7. data/lib/docker/template/alias.rb +28 -0
  8. data/lib/docker/template/ansi.rb +75 -0
  9. data/lib/docker/template/auth.rb +25 -0
  10. data/lib/docker/template/common.rb +126 -0
  11. data/lib/docker/template/config.rb +84 -0
  12. data/lib/docker/template/error.rb +20 -0
  13. data/lib/docker/template/error/bad_exit_status.rb +17 -0
  14. data/lib/docker/template/error/bad_repo_name.rb +15 -0
  15. data/lib/docker/template/error/invalid_repo_type.rb +16 -0
  16. data/lib/docker/template/error/invalid_targz_file.rb +15 -0
  17. data/lib/docker/template/error/no_rootfs_copy_dir.rb +15 -0
  18. data/lib/docker/template/error/no_rootfs_mkimg.rb +15 -0
  19. data/lib/docker/template/error/no_setup_context_found.rb +15 -0
  20. data/lib/docker/template/error/not_implemented.rb +15 -0
  21. data/lib/docker/template/error/repo_not_found.rb +16 -0
  22. data/lib/docker/template/interface.rb +119 -0
  23. data/lib/docker/template/metadata.rb +160 -0
  24. data/lib/docker/template/parser.rb +66 -0
  25. data/lib/docker/template/patches.rb +9 -0
  26. data/lib/docker/template/patches/array.rb +11 -0
  27. data/lib/docker/template/patches/hash.rb +76 -0
  28. data/lib/docker/template/patches/object.rb +9 -0
  29. data/lib/docker/template/patches/pathname.rb +46 -0
  30. data/lib/docker/template/patches/string.rb +9 -0
  31. data/lib/docker/template/repo.rb +180 -0
  32. data/lib/docker/template/rootfs.rb +75 -0
  33. data/lib/docker/template/routable.rb +28 -0
  34. data/lib/docker/template/scratch.rb +139 -0
  35. data/lib/docker/template/simple.rb +51 -0
  36. data/lib/docker/template/stream.rb +62 -0
  37. data/lib/docker/template/templates/rootfs.erb +8 -0
  38. data/lib/docker/template/templates/scratch.erb +7 -0
  39. data/lib/docker/template/util.rb +39 -0
  40. data/lib/docker/template/util/copy.rb +82 -0
  41. data/lib/docker/template/util/data.rb +26 -0
  42. data/lib/docker/template/version.rb +9 -0
  43. metadata +155 -0
@@ -0,0 +1,28 @@
1
+ # Frozen-string-literal: true
2
+ # Copyright: 2015 Jordon Bedwell - Apache v2.0 License
3
+ # Encoding: utf-8
4
+
5
+ module Docker
6
+ module Template
7
+ module Routable
8
+ def route_to_hash(methods, hash, alt_key = nil)
9
+ methods = [methods] unless methods.is_a?(Array)
10
+ methods.each do |method|
11
+ class_eval <<-STR, __FILE__, __LINE__
12
+ def #{method}
13
+ #{alt_key ? "#{hash}['#{alt_key}']" : "#{hash}['#{method}']"}
14
+ end
15
+ STR
16
+ end
17
+ end
18
+
19
+ def route_to_ivar(method, var, bool: false, revbool: false)
20
+ class_eval <<-STR, __FILE__, __LINE__
21
+ def #{method}#{"?" if bool || revbool}
22
+ #{revbool ? "!!!" : "!!" if bool || revbool}#{var}
23
+ end
24
+ STR
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,139 @@
1
+ # Frozen-string-literal: true
2
+ # Copyright: 2015 Jordon Bedwell - Apache v2.0 License
3
+ # Encoding: utf-8
4
+
5
+ module Docker
6
+ module Template
7
+ class Scratch < Common
8
+ attr_reader :rootfs, :img, :repo
9
+ def initialize(repo)
10
+ @repo = repo
11
+ end
12
+
13
+ #
14
+
15
+ def data
16
+ Template.get(:scratch, {
17
+ :maintainer => @repo.metadata["maintainer"],
18
+ :entrypoint => @repo.metadata["entry"].fallback,
19
+ :tar_gz => @tar_gz.basename
20
+ })
21
+ end
22
+
23
+ #
24
+
25
+ def unlink(img: false)
26
+ @copy.rmtree if @copy && @copy.directory?
27
+ @img.delete "force" => true if @img && img
28
+ @context.rmtree if @context && @context.directory?
29
+ @tar_gz.unlink if @tar_gz && @tar_gz.file?
30
+ end
31
+
32
+ #
33
+
34
+ private
35
+ def setup_context
36
+ @context = @repo.tmpdir
37
+ @tar_gz = @repo.tmpfile "archive", ".tar.gz", root: @context
38
+ @copy = @repo.tmpdir "copy"
39
+ copy_dockerfile
40
+ end
41
+
42
+ #
43
+
44
+ private
45
+ def build_rootfs
46
+ @rootfs ||= begin
47
+ self.class.rootfs_for(@repo)
48
+ end
49
+ end
50
+
51
+ # Caches and builds the master rootfs for repos, this is cached
52
+ # on the class because it could be used many times in a single build
53
+ # so we make sure to keep it around so you don't have tons of
54
+ # replication going about slowing down all of the builds.
55
+
56
+ def self.rootfs_for(repo)
57
+ (@rootfs ||= {})[repo.name] ||= begin
58
+ Rootfs.new(repo).tap(&:build)
59
+ end
60
+ end
61
+
62
+ #
63
+
64
+ private
65
+ def build_context
66
+ build_rootfs
67
+
68
+ output_given = false
69
+ img = Container.create(create_args)
70
+ img.start(start_args).attach do |type, str|
71
+ type == :stdout ? $stdout.print(str) : $stderr.print(Ansi.red(str))
72
+ output_given = true
73
+ end
74
+
75
+ # NOTE: Sometimes the instance exists too quickly for attach to even
76
+ # work, through the remote API, so we need to detect those situations
77
+ # and stream the logs after it's exited if we have given no output,
78
+ # we want you to always get the output that was given.
79
+
80
+ if !output_given
81
+ img.streaming_logs "stdout" => true, "stderr" => true do |type, str|
82
+ type == :stdout ? $stdout.print(str) : $stderr.print(Ansi.red(str))
83
+ end
84
+ end
85
+
86
+ if (status = img.json["State"]["ExitCode"]) != 0
87
+ raise Error::BadExitStatus, status
88
+ end
89
+ ensure
90
+ if img
91
+ img.stop rescue nil
92
+ img.delete
93
+ end
94
+ end
95
+
96
+ #
97
+
98
+ def verify_context
99
+ unless @tar_gz.size > 0
100
+ raise Error::InvalidTargzFile, @tar_gz
101
+ end
102
+ end
103
+
104
+ #
105
+
106
+ private
107
+ def copy_dockerfile
108
+ data = self.data % @tar_gz.basename
109
+ dockerfile = @context.join("Dockerfile")
110
+ dockerfile.write(data)
111
+ end
112
+
113
+ #
114
+
115
+ private
116
+ def create_args
117
+ {
118
+ "Env" => @repo.to_env_hash(tar_gz: @tar_gz, copy_dir: @copy).to_env_ary,
119
+ "Name" => ["rootfs", @repo.name, @repo.tag, "image"].join("-"),
120
+ "Image" => @rootfs.img.id,
121
+ "Volumes" => {
122
+ @tar_gz.to_s => {}, @copy.to_s => {}
123
+ }
124
+ }
125
+ end
126
+
127
+ #
128
+
129
+ private
130
+ def start_args
131
+ {
132
+ "Binds" => [
133
+ "#{@copy.to_s}:#{@copy.to_s}:ro", "#{@tar_gz.to_s}:#{@tar_gz.to_s}"
134
+ ]
135
+ }
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,51 @@
1
+ # Frozen-string-literal: true
2
+ # Copyright: 2015 Jordon Bedwell - Apache v2.0 License
3
+ # Encoding: utf-8
4
+
5
+ module Docker
6
+ module Template
7
+ class Simple < Common
8
+ attr_reader :repo
9
+ def initialize(repo)
10
+ @repo = repo
11
+ end
12
+
13
+ #
14
+
15
+ def sync
16
+ copy_build_and_verify unless @context
17
+ Util.create_dockerhub_context( \
18
+ self, @context)
19
+ end
20
+
21
+ #
22
+
23
+ def unlink(img: false, sync: true)
24
+ self.sync if sync && @repo.syncable?
25
+ @img.delete "force" => true if @img && img
26
+ @context.rmtree if @context && \
27
+ @context.directory?
28
+ end
29
+
30
+ #
31
+
32
+ def setup_context
33
+ @context = @repo.tmpdir
34
+ @copy = @context.join("copy")
35
+ copy_dockerfile
36
+ @copy.mkdir
37
+ end
38
+
39
+ #
40
+
41
+ private
42
+ def copy_dockerfile
43
+ dockerfile = @repo.root.join("Dockerfile").read
44
+ data = Util::Data.new(:metadata => @repo.metadata)
45
+ data = ERB.new(dockerfile).result(data._binding)
46
+ context = @context.join("Dockerfile")
47
+ context.write(data)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,62 @@
1
+ # Frozen-string-literal: true
2
+ # Copyright: 2015 Jordon Bedwell - Apache v2.0 License
3
+ # Encoding: utf-8
4
+
5
+ module Docker
6
+ module Template
7
+ class Stream
8
+ def initialize
9
+ @lines = {}
10
+ end
11
+
12
+ #
13
+
14
+ def log(part)
15
+ stream = JSON.parse(part)
16
+ return progress_bar(stream) if stream.any_keys?("progress", "progressDetail")
17
+ return $stdout.puts stream["status"] || stream["stream"] if stream.any_keys?("status", "stream")
18
+ return progress_error(stream) if stream.any_keys?("errorDetail", "error")
19
+
20
+ warn Ansi.red("Unhandled stream message")
21
+ $stderr.puts Ansi.red("Please file a bug ticket.")
22
+ $stdout.puts part
23
+ end
24
+
25
+ #
26
+
27
+ def progress_error(stream)
28
+ abort Ansi.red((stream["error"] || stream["errorDetail"]["message"]).capitalize)
29
+ end
30
+
31
+ #
32
+
33
+ private
34
+ def progress_bar(stream)
35
+ id = stream["id"]
36
+ unless id
37
+ return
38
+ end
39
+
40
+ before, diff = progress_diff(id)
41
+ $stdout.print before if before
42
+ str = stream["progress"] || stream["status"]
43
+ str = "%s: %s\r" % [id, str]
44
+
45
+ $stdout.print(Ansi.jump(str, both: diff))
46
+ end
47
+
48
+ #
49
+
50
+ private
51
+ def progress_diff(id)
52
+ if @lines.has_key?(id)
53
+ return nil, @lines.size - @lines[id]
54
+ end
55
+
56
+ @lines[id] = @lines.size
57
+ before = "\n" unless @lines.size == 1
58
+ return before, 0
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,8 @@
1
+ FROM <%= @rootfs_base_img %>
2
+ RUN apt-get update && apt-get install --no-install-recommends -yf \
3
+ xz-utils git ca-certificates wget
4
+
5
+ COPY copy /
6
+ ENTRYPOINT [ \
7
+ "/usr/local/bin/mkimg" \
8
+ ]
@@ -0,0 +1,7 @@
1
+ FROM scratch
2
+ MAINTAINER <%= @maintainer %>
3
+ ENV TERM=xterm
4
+ ADD <%= @tar_gz %> /
5
+ <% if @entrypoint %>
6
+ ENTRYPOINT ["<%= @entrypoint %>"]
7
+ <% end %>
@@ -0,0 +1,39 @@
1
+ # Frozen-string-literal: true
2
+ # Copyright: 2015 Jordon Bedwell - Apache v2.0 License
3
+ # Encoding: utf-8
4
+
5
+ module Docker
6
+ module Template
7
+ module Util module_function
8
+ autoload :Copy, "docker/template/util/copy"
9
+ autoload :Data, "docker/template/util/data"
10
+
11
+ def notify_alias(aliased)
12
+ repo = aliased.repo
13
+ parent_repo = aliased.parent_repo
14
+ $stdout.puts Ansi.green("Aliasing #{repo} -> #{parent_repo}")
15
+ end
16
+
17
+ #
18
+
19
+ def notify_build(repo, rootfs: false)
20
+ img = rootfs ? repo.to_rootfs_s : repo.to_s
21
+ $stdout.puts Ansi.green("Building: #{img}")
22
+ end
23
+
24
+ #
25
+
26
+ def create_dockerhub_context(builder, context)
27
+ tags = builder.repo.root.join("tags")
28
+ readme = builder.repo.root.children.select { |val| val.to_s =~ /readme/i }.first
29
+ context = tags.join(builder.repo.aliased) if builder.aliased?
30
+ dir = tags.join(builder.repo.tag)
31
+
32
+ FileUtils.mkdir_p dir
33
+ $stdout.puts Ansi.yellow("Storing a Docker context for #{builder.repo}")
34
+ Util::Copy.directory(context, dir)
35
+ Util::Copy.file(readme, dir)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,82 @@
1
+ # Frozen-string-literal: true
2
+ # Copyright: 2015 Jordon Bedwell - Apache v2.0 License
3
+ # Encoding: utf-8
4
+
5
+ module Docker
6
+ module Template
7
+ module Util
8
+ class Copy
9
+ def initialize(from, to)
10
+ @root = Template.root.realpath
11
+ @repos_root = Template.repos_root.realpath
12
+ @from = from.to_pathname
13
+ @to = to.to_pathname
14
+ end
15
+
16
+ #
17
+
18
+ def self.directory(from, to)
19
+ if from && to && File.exist?(from)
20
+ new(from, to).directory
21
+ end
22
+ end
23
+
24
+ # Copy a directory checking for symlinks and resolving them (only
25
+ # at the top level) if they are in the repos root, the root or the from
26
+ # path. The reason we check all three is because repos might be a
27
+ # symlink that resolves out of path so we need to allow symlinks
28
+ # from it. The same for the copy folder.
29
+
30
+ def directory
31
+ FileUtils.cp_r(@from.children, @to, :dereference_root => false)
32
+ @from.all_children.select(&:symlink?).each do |path|
33
+ resolved = path.realpath
34
+ pth = path.relative_path_from(@from)
35
+ pth = @to.join(path)
36
+
37
+ unless in_path?(resolved)
38
+ raise Errno::EPERM, "#{pth} not in #{@root}"
39
+ end
40
+
41
+ FileUtils.rm_r(pth)
42
+ FileUtils.cp_r(resolved, pth, {
43
+ :dereference_root => false
44
+ })
45
+ end
46
+ end
47
+
48
+ #
49
+
50
+ def self.file(from, to)
51
+ if to && from && File.exist?(from)
52
+ new(from, to).file
53
+ end
54
+ end
55
+
56
+ #
57
+
58
+ def file
59
+ if !@from.symlink?
60
+ return FileUtils.cp(@from, @to)
61
+ end
62
+
63
+ resolved = @from.realpath
64
+ allowed = resolved.in_path?(@root)
65
+ raise Errno::EPERM, "#{resolved} not in #{@root}." unless allowed
66
+ FileUtils.cp(resolved, @to)
67
+ end
68
+
69
+ # Check to see if the path falls within the users roots, while
70
+ # getting the real path of the from root just incase it, itself is
71
+ # a symlink, we don't want it to fail because of that.
72
+
73
+ private
74
+ def in_path?(resolved)
75
+ resolved.in_path?(@repos_root) || \
76
+ resolved.in_path?(@from.realpath) || \
77
+ resolved.in_path?(@root)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,26 @@
1
+ # Frozen-string-literal: true
2
+ # Copyright: 2015 Jordon Bedwell - Apache v2.0 License
3
+ # Encoding: utf-8
4
+
5
+ module Docker
6
+ module Template
7
+ module Util
8
+
9
+ # Provides a way to encapsulate data for ERB processing so that we
10
+ # don't get full unfettered access to the entire binding from within
11
+ # our ERB processing context. Nobody wants that.
12
+
13
+ class Data
14
+ def initialize(vars)
15
+ vars.each do |key, val|
16
+ instance_variable_set("@#{key}", val)
17
+ end
18
+ end
19
+
20
+ def _binding
21
+ return binding
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end