docker-template 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|