docker-template 0.1.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 +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
|