docker-template 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +13 -0
- data/LICENSE +13 -0
- data/README.md +20 -0
- data/Rakefile +20 -0
- data/lib/docker/template.rb +86 -0
- data/lib/docker/template/alias.rb +28 -0
- data/lib/docker/template/ansi.rb +75 -0
- data/lib/docker/template/auth.rb +25 -0
- data/lib/docker/template/common.rb +126 -0
- data/lib/docker/template/config.rb +84 -0
- data/lib/docker/template/error.rb +20 -0
- data/lib/docker/template/error/bad_exit_status.rb +17 -0
- data/lib/docker/template/error/bad_repo_name.rb +15 -0
- data/lib/docker/template/error/invalid_repo_type.rb +16 -0
- data/lib/docker/template/error/invalid_targz_file.rb +15 -0
- data/lib/docker/template/error/no_rootfs_copy_dir.rb +15 -0
- data/lib/docker/template/error/no_rootfs_mkimg.rb +15 -0
- data/lib/docker/template/error/no_setup_context_found.rb +15 -0
- data/lib/docker/template/error/not_implemented.rb +15 -0
- data/lib/docker/template/error/repo_not_found.rb +16 -0
- data/lib/docker/template/interface.rb +119 -0
- data/lib/docker/template/metadata.rb +160 -0
- data/lib/docker/template/parser.rb +66 -0
- data/lib/docker/template/patches.rb +9 -0
- data/lib/docker/template/patches/array.rb +11 -0
- data/lib/docker/template/patches/hash.rb +76 -0
- data/lib/docker/template/patches/object.rb +9 -0
- data/lib/docker/template/patches/pathname.rb +46 -0
- data/lib/docker/template/patches/string.rb +9 -0
- data/lib/docker/template/repo.rb +180 -0
- data/lib/docker/template/rootfs.rb +75 -0
- data/lib/docker/template/routable.rb +28 -0
- data/lib/docker/template/scratch.rb +139 -0
- data/lib/docker/template/simple.rb +51 -0
- data/lib/docker/template/stream.rb +62 -0
- data/lib/docker/template/templates/rootfs.erb +8 -0
- data/lib/docker/template/templates/scratch.erb +7 -0
- data/lib/docker/template/util.rb +39 -0
- data/lib/docker/template/util/copy.rb +82 -0
- data/lib/docker/template/util/data.rb +26 -0
- data/lib/docker/template/version.rb +9 -0
- 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,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
|