docker-template 0.2.0 → 0.3.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 +4 -4
- data/Gemfile +30 -4
- data/LICENSE +1 -1
- data/README.md +79 -14
- data/Rakefile +115 -38
- data/bin/docker-template +24 -10
- data/comp/bin +9 -0
- data/comp/list +83 -0
- data/comp/list.pak +0 -0
- data/lib/docker/template.rb +47 -61
- data/lib/docker/template/builder.rb +302 -0
- data/lib/docker/template/cache.rb +71 -0
- data/lib/docker/template/cli.rb +125 -0
- data/lib/docker/template/error.rb +120 -11
- data/lib/docker/template/logger.rb +128 -0
- data/lib/docker/template/metadata.rb +566 -103
- data/lib/docker/template/normal.rb +46 -0
- data/lib/docker/template/notify.rb +44 -0
- data/lib/docker/template/parser.rb +48 -38
- data/lib/docker/template/repo.rb +131 -97
- data/lib/docker/template/rootfs.rb +51 -41
- data/lib/docker/template/scratch.rb +96 -66
- data/lib/docker/template/version.rb +4 -2
- data/lib/erb/context.rb +29 -0
- data/shas.yml +11 -0
- data/templates/rootfs.erb +5 -0
- data/templates/rootfs/alpine.erb +71 -0
- data/templates/rootfs/ubuntu.erb +76 -0
- data/{lib/docker/template/templates → templates}/scratch.erb +0 -1
- metadata +64 -50
- data/lib/docker/template/alias.rb +0 -28
- data/lib/docker/template/ansi.rb +0 -85
- data/lib/docker/template/auth.rb +0 -25
- data/lib/docker/template/common.rb +0 -130
- data/lib/docker/template/config.rb +0 -80
- data/lib/docker/template/error/bad_exit_status.rb +0 -17
- data/lib/docker/template/error/bad_repo_name.rb +0 -15
- data/lib/docker/template/error/invalid_repo_type.rb +0 -16
- data/lib/docker/template/error/invalid_targz_file.rb +0 -15
- data/lib/docker/template/error/no_rootfs_copy_dir.rb +0 -15
- data/lib/docker/template/error/no_rootfs_mkimg.rb +0 -15
- data/lib/docker/template/error/no_setup_context_found.rb +0 -15
- data/lib/docker/template/error/not_implemented.rb +0 -15
- data/lib/docker/template/error/repo_not_found.rb +0 -16
- data/lib/docker/template/interface.rb +0 -118
- data/lib/docker/template/patches.rb +0 -9
- data/lib/docker/template/patches/array.rb +0 -11
- data/lib/docker/template/patches/hash.rb +0 -71
- data/lib/docker/template/patches/object.rb +0 -9
- data/lib/docker/template/patches/pathname.rb +0 -46
- data/lib/docker/template/patches/string.rb +0 -9
- data/lib/docker/template/routable.rb +0 -28
- data/lib/docker/template/simple.rb +0 -49
- data/lib/docker/template/stream.rb +0 -63
- data/lib/docker/template/templates/rootfs.erb +0 -8
- data/lib/docker/template/util.rb +0 -54
- data/lib/docker/template/util/copy.rb +0 -77
- data/lib/docker/template/util/data.rb +0 -26
data/comp/list.pak
ADDED
Binary file
|
data/lib/docker/template.rb
CHANGED
@@ -1,99 +1,85 @@
|
|
1
|
+
# ----------------------------------------------------------------------------
|
1
2
|
# Frozen-string-literal: true
|
2
|
-
# Copyright: 2015 Jordon Bedwell - Apache v2.0 License
|
3
|
+
# Copyright: 2015 - 2016 Jordon Bedwell - Apache v2.0 License
|
3
4
|
# Encoding: utf-8
|
4
|
-
|
5
|
-
require "docker/template/version"
|
6
|
-
require "docker/template/patches"
|
5
|
+
# ----------------------------------------------------------------------------
|
7
6
|
|
8
7
|
require "docker"
|
9
|
-
require "
|
10
|
-
require "
|
11
|
-
require "
|
8
|
+
require "extras/all"
|
9
|
+
require "erb/context"
|
10
|
+
require "forwardable/extended"
|
11
|
+
require "simple/ansi"
|
12
|
+
require "pathutil"
|
12
13
|
require "set"
|
13
14
|
|
15
|
+
# ----------------------------------------------------------------------------
|
16
|
+
|
17
|
+
Excon.defaults[ :read_timeout] = 1440
|
18
|
+
Excon.defaults[:write_timeout] = 1440
|
19
|
+
|
20
|
+
# ----------------------------------------------------------------------------
|
21
|
+
|
14
22
|
module Docker
|
15
23
|
module Template
|
16
24
|
module_function
|
17
25
|
|
18
|
-
|
19
|
-
|
20
|
-
autoload :
|
21
|
-
autoload :
|
22
|
-
autoload :Routable, "docker/template/routable"
|
23
|
-
autoload :Interface, "docker/template/interface"
|
24
|
-
autoload :Metadata, "docker/template/metadata"
|
25
|
-
autoload :Stream, "docker/template/stream"
|
26
|
-
autoload :Safe, "docker/template/safe"
|
26
|
+
# ------------------------------------------------------------------------
|
27
|
+
|
28
|
+
autoload :Notify, "docker/template/notify"
|
29
|
+
autoload :Utils, "docker/template/utils"
|
27
30
|
autoload :Repo, "docker/template/repo"
|
28
31
|
autoload :Error, "docker/template/error"
|
29
|
-
autoload :
|
30
|
-
autoload :
|
32
|
+
autoload :Logger, "docker/template/logger"
|
33
|
+
autoload :Normal, "docker/template/normal"
|
34
|
+
autoload :Parser, "docker/template/parser"
|
35
|
+
autoload :Builder, "docker/template/builder"
|
36
|
+
autoload :Metadata, "docker/template/metadata"
|
31
37
|
autoload :Scratch, "docker/template/scratch"
|
32
|
-
autoload :
|
38
|
+
autoload :Rootfs, "docker/template/rootfs"
|
39
|
+
autoload :Cache, "docker/template/cache"
|
33
40
|
autoload :Alias, "docker/template/alias"
|
34
|
-
autoload :
|
41
|
+
autoload :CLI, "docker/template/cli"
|
35
42
|
|
36
|
-
|
37
|
-
root.join("copy").exist? && !root.join("../..", config["repos_dir"]).exist?
|
38
|
-
end
|
39
|
-
|
40
|
-
def config
|
41
|
-
@config ||= begin
|
42
|
-
Config.new
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
#
|
43
|
+
# ------------------------------------------------------------------------
|
47
44
|
|
48
45
|
def root
|
49
46
|
@root ||= begin
|
50
|
-
|
47
|
+
Pathutil.new(Dir.pwd)
|
51
48
|
end
|
52
49
|
end
|
53
50
|
|
54
|
-
#
|
55
|
-
# `repos_dir` into your configuration file. I'm not saying it has to be but
|
56
|
-
# it should probably be relative rather than absolute, ther are no
|
57
|
-
# guarantees that an absolute path will work.
|
58
|
-
|
59
|
-
def repos_root
|
60
|
-
@repos_root ||= begin
|
61
|
-
root.join(config["repos_dir"])
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
#
|
66
|
-
|
67
|
-
def repo_root_for(name)
|
68
|
-
repo_is_root?? root : repos_root.join(name)
|
69
|
-
end
|
70
|
-
|
71
|
-
# Provides the root to Docker template, wherever it is installed so that
|
72
|
-
# we can do things, mostly ignore files for the profiler. Otherwise it's
|
73
|
-
# not really used, it's just an encapsulator.
|
51
|
+
# ------------------------------------------------------------------------
|
74
52
|
|
75
53
|
def gem_root
|
76
54
|
@gem_root ||= begin
|
77
|
-
|
78
|
-
|
55
|
+
Pathutil.new("../../").expand_path(
|
56
|
+
__dir__
|
57
|
+
)
|
79
58
|
end
|
80
59
|
end
|
81
60
|
|
82
|
-
#
|
83
|
-
# from our templates and use it if you wish to.
|
61
|
+
# ------------------------------------------------------------------------
|
84
62
|
|
85
63
|
def template_root
|
86
64
|
@template_root ||= begin
|
87
|
-
gem_root.join("
|
65
|
+
gem_root.join("templates")
|
88
66
|
end
|
89
67
|
end
|
90
68
|
|
91
|
-
#
|
69
|
+
# ------------------------------------------------------------------------
|
70
|
+
# Pull a `template` from the `template_root` to parse it's data.
|
71
|
+
# TODO: Rename this get template!
|
72
|
+
# ------------------------------------------------------------------------
|
92
73
|
|
93
74
|
def get(name, data = {})
|
94
|
-
data =
|
95
|
-
template = template_root.join("#{name}.erb").read
|
96
|
-
|
75
|
+
data = ERB::Context.new(data)
|
76
|
+
template = template_root.join("#{name}.erb").read unless name.is_a?(Pathutil)
|
77
|
+
template = name.read if name.is_a?(Pathutil)
|
78
|
+
template = ERB.new(template)
|
79
|
+
|
80
|
+
return template.result(
|
81
|
+
data._binding
|
82
|
+
)
|
97
83
|
end
|
98
84
|
end
|
99
85
|
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
# ----------------------------------------------------------------------------
|
2
|
+
# Frozen-string-literal: true
|
3
|
+
# Copyright: 2015 - 2016 Jordon Bedwell - Apache v2.0 License
|
4
|
+
# Encoding: utf-8
|
5
|
+
# ----------------------------------------------------------------------------
|
6
|
+
|
7
|
+
module Docker
|
8
|
+
module Template
|
9
|
+
class Builder
|
10
|
+
extend Forwardable::Extended
|
11
|
+
|
12
|
+
# ----------------------------------------------------------------------
|
13
|
+
|
14
|
+
attr_reader :repo
|
15
|
+
attr_reader :context
|
16
|
+
attr_reader :img
|
17
|
+
|
18
|
+
# ----------------------------------------------------------------------
|
19
|
+
|
20
|
+
ALIAS_SETUP = [:cache_context]
|
21
|
+
SETUP = [:setup_context, :copy_global, :copy_all,
|
22
|
+
:copy_group, :copy_tag, :copy_cleanup, :build_context,
|
23
|
+
:verify_context, :cache_context].freeze
|
24
|
+
|
25
|
+
# ----------------------------------------------------------------------
|
26
|
+
|
27
|
+
def initialize(repo)
|
28
|
+
@repo = repo
|
29
|
+
end
|
30
|
+
|
31
|
+
# ----------------------------------------------------------------------
|
32
|
+
# Checks to see if this repository is an alias. This happens when the
|
33
|
+
# user has alised data inside of their configuration file. At this point
|
34
|
+
# we will not only copy the parent's data but the aliased data.
|
35
|
+
# ----------------------------------------------------------------------
|
36
|
+
|
37
|
+
def alias?
|
38
|
+
!@repo.complex_alias? && @repo.alias? && !rootfs?
|
39
|
+
end
|
40
|
+
|
41
|
+
# ----------------------------------------------------------------------
|
42
|
+
|
43
|
+
def rootfs?
|
44
|
+
is_a?(
|
45
|
+
Rootfs
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
# ----------------------------------------------------------------------
|
50
|
+
|
51
|
+
def normal?
|
52
|
+
@repo.type == "normal" \
|
53
|
+
&& !rootfs?
|
54
|
+
end
|
55
|
+
|
56
|
+
# ----------------------------------------------------------------------
|
57
|
+
|
58
|
+
def scratch?
|
59
|
+
@repo.type == "scratch" \
|
60
|
+
&& !rootfs?
|
61
|
+
end
|
62
|
+
|
63
|
+
# ----------------------------------------------------------------------
|
64
|
+
|
65
|
+
def aliased_img
|
66
|
+
return unless alias?
|
67
|
+
@aliased_img ||= Docker::Image.get(
|
68
|
+
aliased_repo.to_s
|
69
|
+
)
|
70
|
+
|
71
|
+
rescue Docker::Error::NotFoundError
|
72
|
+
if alias?
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# ----------------------------------------------------------------------
|
78
|
+
|
79
|
+
def push
|
80
|
+
return if rootfs? || !@repo.pushable?
|
81
|
+
Notify.push self
|
82
|
+
auth!
|
83
|
+
|
84
|
+
img = @img || Image.get(@repo.to_s)
|
85
|
+
img.push nil, :repo_tag => \
|
86
|
+
@repo.to_s, &Logger.new.method(:api)
|
87
|
+
|
88
|
+
rescue Docker::Error::NotFoundError
|
89
|
+
$stderr.puts Simple::Ansi.red(
|
90
|
+
"Image does not exist, unpushable."
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
# ----------------------------------------------------------------------
|
95
|
+
|
96
|
+
def build
|
97
|
+
Simple::Ansi.clear if @repo.buildable?
|
98
|
+
return build_alias if alias?
|
99
|
+
setup
|
100
|
+
|
101
|
+
if @repo.buildable?
|
102
|
+
Notify.build(@repo, {
|
103
|
+
:rootfs => rootfs?
|
104
|
+
})
|
105
|
+
|
106
|
+
chdir_build
|
107
|
+
end
|
108
|
+
|
109
|
+
push
|
110
|
+
rescue SystemExit => exit_
|
111
|
+
teardown :img => true
|
112
|
+
raise exit_
|
113
|
+
ensure
|
114
|
+
if !rootfs?
|
115
|
+
teardown else teardown({
|
116
|
+
:img => false
|
117
|
+
})
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# ----------------------------------------------------------------------
|
122
|
+
# This method is a default reference. It is called when the image is
|
123
|
+
# done building or when there is an error and we need to clean up some
|
124
|
+
# stuff before exiting, use it... please.
|
125
|
+
# ----------------------------------------------------------------------
|
126
|
+
|
127
|
+
def teardown(*_)
|
128
|
+
$stderr.puts Ansi.red(
|
129
|
+
"#{__method__}: Not Implemented."
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
# ----------------------------------------------------------------------
|
134
|
+
|
135
|
+
private
|
136
|
+
def build_alias
|
137
|
+
alias_setup
|
138
|
+
|
139
|
+
if @repo.buildable?
|
140
|
+
aliased = self.class.new(aliased_repo)
|
141
|
+
aliased.build unless aliased_img
|
142
|
+
Notify.alias(self)
|
143
|
+
|
144
|
+
aliased_img.tag(
|
145
|
+
@repo.to_tag_h
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
push
|
150
|
+
end
|
151
|
+
|
152
|
+
# ----------------------------------------------------------------------
|
153
|
+
# The prebuild happens when a user has "setup_context", which typically
|
154
|
+
# only happens with scratch, which will prebuild it's rootfs image so
|
155
|
+
# it can get to building it's actual image.
|
156
|
+
# ----------------------------------------------------------------------
|
157
|
+
|
158
|
+
private
|
159
|
+
def setup
|
160
|
+
unless respond_to?(:setup_context, true)
|
161
|
+
raise Error::NoSetupContext
|
162
|
+
end
|
163
|
+
|
164
|
+
SETUP.map do |val|
|
165
|
+
if respond_to?(val, true)
|
166
|
+
send(val)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# ----------------------------------------------------------------------
|
172
|
+
|
173
|
+
private
|
174
|
+
def alias_setup
|
175
|
+
ALIAS_SETUP.map do |m|
|
176
|
+
if respond_to?(m, true)
|
177
|
+
send(m)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# ----------------------------------------------------------------------
|
183
|
+
|
184
|
+
private
|
185
|
+
def chdir_build
|
186
|
+
@context.chdir do
|
187
|
+
logger = Logger.new(self).method(:api)
|
188
|
+
opts = { :t => @repo.to_s(rootfs: rootfs?) }
|
189
|
+
$stderr.puts Simple::Ansi.yellow("TTY not supported: Ignored.") if @repo.metadata["tty"]
|
190
|
+
@img = Docker::Image.build_from_dir(".", opts, &logger)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# ----------------------------------------------------------------------
|
195
|
+
|
196
|
+
private
|
197
|
+
def cache_context
|
198
|
+
if repo.cacheable?
|
199
|
+
$stderr.puts Simple::Ansi.red(
|
200
|
+
"Context caching not supported"
|
201
|
+
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# ----------------------------------------------------------------------
|
206
|
+
# The root can have it's own global copy directory shared across all
|
207
|
+
# repos in your repo container dir so this encapsulates those.
|
208
|
+
# ----------------------------------------------------------------------
|
209
|
+
|
210
|
+
private
|
211
|
+
def copy_global
|
212
|
+
unless rootfs?
|
213
|
+
dir = Template.root.join(
|
214
|
+
@repo.metadata["copy_dir"]
|
215
|
+
)
|
216
|
+
|
217
|
+
if dir.exist?
|
218
|
+
then dir.safe_copy(
|
219
|
+
@copy, :root => Template.root
|
220
|
+
)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# ----------------------------------------------------------------------
|
226
|
+
|
227
|
+
private
|
228
|
+
def copy_tag
|
229
|
+
unless rootfs?
|
230
|
+
dir = @repo.copy_dir("tag", @repo.tag)
|
231
|
+
|
232
|
+
if dir.exist?
|
233
|
+
then dir.safe_copy(
|
234
|
+
@copy, :root => Template.root
|
235
|
+
)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# ----------------------------------------------------------------------
|
241
|
+
|
242
|
+
private
|
243
|
+
def copy_group
|
244
|
+
build_group = @repo.metadata["tags"][
|
245
|
+
@repo.tag
|
246
|
+
]
|
247
|
+
|
248
|
+
unless rootfs? || !build_group
|
249
|
+
dir = @repo.copy_dir("group", build_group)
|
250
|
+
|
251
|
+
if dir.exist?
|
252
|
+
then dir.safe_copy(
|
253
|
+
@copy, :root => Template.root
|
254
|
+
)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# ----------------------------------------------------------------------
|
260
|
+
|
261
|
+
private
|
262
|
+
def copy_all
|
263
|
+
unless rootfs?
|
264
|
+
dir = @repo.copy_dir("all")
|
265
|
+
|
266
|
+
if dir.exist?
|
267
|
+
then dir.safe_copy(
|
268
|
+
@copy, :root => Template.root
|
269
|
+
)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# ----------------------------------------------------------------------
|
275
|
+
|
276
|
+
private
|
277
|
+
def auth!
|
278
|
+
credentials = Pathutil.new("~/.docker/config.json").expand_path.read_json
|
279
|
+
return if credentials.empty?
|
280
|
+
|
281
|
+
credentials["auths"].each do |server, info|
|
282
|
+
user, pass = Base64.decode64(info["auth"]).split(
|
283
|
+
":", 2
|
284
|
+
)
|
285
|
+
|
286
|
+
Docker.authenticate!({
|
287
|
+
"username" => user,
|
288
|
+
"serveraddress" => server,
|
289
|
+
"email" => info["email"],
|
290
|
+
"password" => pass
|
291
|
+
})
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# ----------------------------------------------------------------------
|
296
|
+
|
297
|
+
rb_delegate :aliased_repo, {
|
298
|
+
:to => :repo, :alias_of => :aliased
|
299
|
+
}
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# ----------------------------------------------------------------------------
|
2
|
+
# Frozen-string-literal: true
|
3
|
+
# Copyright: 2015 - 2016 Jordon Bedwell - Apache v2.0 License
|
4
|
+
# Encoding: utf-8
|
5
|
+
# ----------------------------------------------------------------------------
|
6
|
+
|
7
|
+
module Docker
|
8
|
+
module Template
|
9
|
+
module Cache
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# ----------------------------------------------------------------------
|
13
|
+
# Cache the context into the cache directory.
|
14
|
+
# ----------------------------------------------------------------------
|
15
|
+
|
16
|
+
def context(builder, context)
|
17
|
+
if builder.alias? && builder.aliased_repo.cache_dir.exist?
|
18
|
+
parent_cache_dir = builder.aliased_repo.cache_dir
|
19
|
+
$stderr.puts Simple::Ansi.yellow("Copying #{builder.aliased_repo} context to #{builder.repo}")
|
20
|
+
cache_dir = builder.repo.cache_dir
|
21
|
+
|
22
|
+
parent_cache_dir.cp_r(cache_dir.tap(
|
23
|
+
&:rm_rf
|
24
|
+
))
|
25
|
+
elsif context
|
26
|
+
builder.repo.cache_dir.rm_rf
|
27
|
+
$stderr.puts Simple::Ansi.yellow("Copying context for #{builder.repo}")
|
28
|
+
cache_dir = builder.repo.cache_dir
|
29
|
+
cache_dir.parent.mkdir_p
|
30
|
+
|
31
|
+
readme(builder)
|
32
|
+
context.cp_r(cache_dir.tap(
|
33
|
+
&:rm_rf
|
34
|
+
))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# ----------------------------------------------------------------------
|
39
|
+
# Cleanup the context caches, removing the caches we no longer need.
|
40
|
+
# ----------------------------------------------------------------------
|
41
|
+
|
42
|
+
def cleanup(repo)
|
43
|
+
repo.cache_dir.parent.children.each do |file|
|
44
|
+
unless repo.metadata.tags.include?(file.basename)
|
45
|
+
$stdout.puts Simple::Ansi.yellow("Removing %s." % [
|
46
|
+
file.relative_path_from(Template.root)
|
47
|
+
])
|
48
|
+
|
49
|
+
file.rm_rf
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# ----------------------------------------------------------------------
|
55
|
+
# Note: We normally expect but do not require you to have a README.
|
56
|
+
# Search for and copy the readme if available.
|
57
|
+
# ----------------------------------------------------------------------
|
58
|
+
|
59
|
+
def readme(builder)
|
60
|
+
file = builder.repo.root.children.find do |val|
|
61
|
+
val =~ /readme/i
|
62
|
+
end
|
63
|
+
|
64
|
+
return unless file
|
65
|
+
file.safe_copy(builder.repo.cache_dir, {
|
66
|
+
:root => file.parent
|
67
|
+
})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|