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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +30 -4
  3. data/LICENSE +1 -1
  4. data/README.md +79 -14
  5. data/Rakefile +115 -38
  6. data/bin/docker-template +24 -10
  7. data/comp/bin +9 -0
  8. data/comp/list +83 -0
  9. data/comp/list.pak +0 -0
  10. data/lib/docker/template.rb +47 -61
  11. data/lib/docker/template/builder.rb +302 -0
  12. data/lib/docker/template/cache.rb +71 -0
  13. data/lib/docker/template/cli.rb +125 -0
  14. data/lib/docker/template/error.rb +120 -11
  15. data/lib/docker/template/logger.rb +128 -0
  16. data/lib/docker/template/metadata.rb +566 -103
  17. data/lib/docker/template/normal.rb +46 -0
  18. data/lib/docker/template/notify.rb +44 -0
  19. data/lib/docker/template/parser.rb +48 -38
  20. data/lib/docker/template/repo.rb +131 -97
  21. data/lib/docker/template/rootfs.rb +51 -41
  22. data/lib/docker/template/scratch.rb +96 -66
  23. data/lib/docker/template/version.rb +4 -2
  24. data/lib/erb/context.rb +29 -0
  25. data/shas.yml +11 -0
  26. data/templates/rootfs.erb +5 -0
  27. data/templates/rootfs/alpine.erb +71 -0
  28. data/templates/rootfs/ubuntu.erb +76 -0
  29. data/{lib/docker/template/templates → templates}/scratch.erb +0 -1
  30. metadata +64 -50
  31. data/lib/docker/template/alias.rb +0 -28
  32. data/lib/docker/template/ansi.rb +0 -85
  33. data/lib/docker/template/auth.rb +0 -25
  34. data/lib/docker/template/common.rb +0 -130
  35. data/lib/docker/template/config.rb +0 -80
  36. data/lib/docker/template/error/bad_exit_status.rb +0 -17
  37. data/lib/docker/template/error/bad_repo_name.rb +0 -15
  38. data/lib/docker/template/error/invalid_repo_type.rb +0 -16
  39. data/lib/docker/template/error/invalid_targz_file.rb +0 -15
  40. data/lib/docker/template/error/no_rootfs_copy_dir.rb +0 -15
  41. data/lib/docker/template/error/no_rootfs_mkimg.rb +0 -15
  42. data/lib/docker/template/error/no_setup_context_found.rb +0 -15
  43. data/lib/docker/template/error/not_implemented.rb +0 -15
  44. data/lib/docker/template/error/repo_not_found.rb +0 -16
  45. data/lib/docker/template/interface.rb +0 -118
  46. data/lib/docker/template/patches.rb +0 -9
  47. data/lib/docker/template/patches/array.rb +0 -11
  48. data/lib/docker/template/patches/hash.rb +0 -71
  49. data/lib/docker/template/patches/object.rb +0 -9
  50. data/lib/docker/template/patches/pathname.rb +0 -46
  51. data/lib/docker/template/patches/string.rb +0 -9
  52. data/lib/docker/template/routable.rb +0 -28
  53. data/lib/docker/template/simple.rb +0 -49
  54. data/lib/docker/template/stream.rb +0 -63
  55. data/lib/docker/template/templates/rootfs.erb +0 -8
  56. data/lib/docker/template/util.rb +0 -54
  57. data/lib/docker/template/util/copy.rb +0 -77
  58. data/lib/docker/template/util/data.rb +0 -26
Binary file
@@ -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 "forwardable"
10
- require "json"
11
- require "erb"
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
- autoload :Util, "docker/template/util"
19
- autoload :Config, "docker/template/config"
20
- autoload :Ansi, "docker/template/ansi"
21
- autoload :Parser, "docker/template/parser"
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 :Common, "docker/template/common"
30
- autoload :Rootfs, "docker/template/rootfs"
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 :Simple, "docker/template/simple"
38
+ autoload :Rootfs, "docker/template/rootfs"
39
+ autoload :Cache, "docker/template/cache"
33
40
  autoload :Alias, "docker/template/alias"
34
- autoload :Auth, "docker/template/auth"
41
+ autoload :CLI, "docker/template/cli"
35
42
 
36
- def repo_is_root?
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
- Pathname.new(Dir.pwd)
47
+ Pathutil.new(Dir.pwd)
51
48
  end
52
49
  end
53
50
 
54
- # The location of the standard repos/ dir, you can change this by adding
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
- path = File.expand_path("../../", __dir__)
78
- Pathname.new(path)
55
+ Pathutil.new("../../").expand_path(
56
+ __dir__
57
+ )
79
58
  end
80
59
  end
81
60
 
82
- # Provides the templates directory so you can quickly pull a template
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("lib/docker/template/templates")
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 = Util::Data.new(data)
95
- template = template_root.join("#{name}.erb").read
96
- ERB.new(template).result(data._binding)
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