dry-dock 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/.dockerignore +6 -0
- data/.pryrc +1 -0
- data/.rspec +2 -0
- data/Dockerfile +69 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +102 -0
- data/LICENSE +22 -0
- data/README.md +75 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/drydock +45 -0
- data/bin/json-test-consumer.rb +11 -0
- data/bin/json-test-producer.rb +25 -0
- data/bin/test-tar-writer-digest.rb +27 -0
- data/dry-dock.gemspec +135 -0
- data/examples/ruby-dsl.rb +14 -0
- data/examples/ruby-node-app-dsl.rb +128 -0
- data/examples/test-dsl.rb +9 -0
- data/examples/test.rb +46 -0
- data/lib/drydock/cli_flags.rb +46 -0
- data/lib/drydock/container_config.rb +75 -0
- data/lib/drydock/docker_api_patch.rb +176 -0
- data/lib/drydock/drydock.rb +65 -0
- data/lib/drydock/errors.rb +6 -0
- data/lib/drydock/file_manager.rb +26 -0
- data/lib/drydock/formatters.rb +13 -0
- data/lib/drydock/ignorefile_definition.rb +61 -0
- data/lib/drydock/image_repository.rb +50 -0
- data/lib/drydock/logger.rb +61 -0
- data/lib/drydock/object_caches/base.rb +24 -0
- data/lib/drydock/object_caches/filesystem_cache.rb +88 -0
- data/lib/drydock/object_caches/in_memory_cache.rb +52 -0
- data/lib/drydock/object_caches/no_cache.rb +38 -0
- data/lib/drydock/phase.rb +50 -0
- data/lib/drydock/phase_chain.rb +233 -0
- data/lib/drydock/plugins/apk.rb +31 -0
- data/lib/drydock/plugins/base.rb +15 -0
- data/lib/drydock/plugins/npm.rb +16 -0
- data/lib/drydock/plugins/package_manager.rb +30 -0
- data/lib/drydock/plugins/rubygems.rb +30 -0
- data/lib/drydock/project.rb +427 -0
- data/lib/drydock/runtime_options.rb +79 -0
- data/lib/drydock/stream_monitor.rb +54 -0
- data/lib/drydock/tar_writer.rb +36 -0
- data/lib/drydock.rb +35 -0
- data/spec/assets/MANIFEST +4 -0
- data/spec/assets/hello-world.txt +1 -0
- data/spec/assets/sample.tar +0 -0
- data/spec/assets/test.sh +3 -0
- data/spec/drydock/cli_flags_spec.rb +38 -0
- data/spec/drydock/container_config_spec.rb +230 -0
- data/spec/drydock/docker_api_patch_spec.rb +103 -0
- data/spec/drydock/drydock_spec.rb +25 -0
- data/spec/drydock/file_manager_spec.rb +53 -0
- data/spec/drydock/formatters_spec.rb +26 -0
- data/spec/drydock/ignorefile_definition_spec.rb +123 -0
- data/spec/drydock/image_repository_spec.rb +54 -0
- data/spec/drydock/object_caches/base_spec.rb +28 -0
- data/spec/drydock/object_caches/filesystem_cache_spec.rb +48 -0
- data/spec/drydock/object_caches/no_cache_spec.rb +62 -0
- data/spec/drydock/phase_chain_spec.rb +118 -0
- data/spec/drydock/phase_spec.rb +67 -0
- data/spec/drydock/plugins/apk_spec.rb +49 -0
- data/spec/drydock/plugins/base_spec.rb +13 -0
- data/spec/drydock/plugins/npm_spec.rb +26 -0
- data/spec/drydock/plugins/package_manager_spec.rb +12 -0
- data/spec/drydock/plugins/rubygems_spec.rb +53 -0
- data/spec/drydock/project_import_spec.rb +39 -0
- data/spec/drydock/project_spec.rb +156 -0
- data/spec/drydock/runtime_options_spec.rb +31 -0
- data/spec/drydock/stream_monitor_spec.rb +41 -0
- data/spec/drydock/tar_writer_spec.rb +27 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/shared_examples/base_class.rb +3 -0
- data/spec/support/shared_examples/container_config.rb +12 -0
- data/spec/support/shared_examples/drydockfile.rb +6 -0
- metadata +223 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
class Logger < ::Logger
|
6
|
+
|
7
|
+
def add(severity, message = nil, progname = nil, &block)
|
8
|
+
annotation = nil
|
9
|
+
indent = 0
|
10
|
+
|
11
|
+
if message.nil?
|
12
|
+
if block_given?
|
13
|
+
message = yield
|
14
|
+
else
|
15
|
+
message = progname
|
16
|
+
progname = @progname
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if message.respond_to?(:key?)
|
21
|
+
indent = message[:indent].to_i + 1
|
22
|
+
annotation = message.fetch(:annotation, '-->')
|
23
|
+
if message.key?(:message)
|
24
|
+
messages = Array(message[:message])
|
25
|
+
elsif message.key?(:messages)
|
26
|
+
messages = Array(message[:messages])
|
27
|
+
end
|
28
|
+
else
|
29
|
+
messages = [message]
|
30
|
+
end
|
31
|
+
|
32
|
+
annotation << " " if annotation
|
33
|
+
indentation = ' ' * indent
|
34
|
+
|
35
|
+
messages.each do |m|
|
36
|
+
m.to_s.split(/\n/).each do |line|
|
37
|
+
if annotation
|
38
|
+
super(severity, "#{indentation}#{annotation} #{line}", progname)
|
39
|
+
else
|
40
|
+
super(severity, line, progname)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
alias log add
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class Formatter < ::Logger::Formatter
|
51
|
+
|
52
|
+
def call(severity, time, program, message)
|
53
|
+
"%s [%s] %s\n" % [
|
54
|
+
severity.slice(0, 1),
|
55
|
+
time.strftime('%H:%M:%S'),
|
56
|
+
msg2str(message)
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module Drydock
|
3
|
+
module ObjectCaches
|
4
|
+
class Base
|
5
|
+
|
6
|
+
def clear
|
7
|
+
raise NotImplementedError, '#clear must be overridden in the subclass'
|
8
|
+
end
|
9
|
+
|
10
|
+
def fetch(key, &blk)
|
11
|
+
raise NotImplementedError, '#fetch must be overridden in the subclass'
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(key, &blk)
|
15
|
+
raise NotImplementedError, '#get must be overridden in the subclass'
|
16
|
+
end
|
17
|
+
|
18
|
+
def set(key, value = nil, &blk)
|
19
|
+
raise NotImplementedError, '#set must be overridden in the subclass'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
module ObjectCaches
|
6
|
+
class FilesystemCache < Base
|
7
|
+
|
8
|
+
def initialize(dir = "~/.drydock")
|
9
|
+
@dir = File.expand_path(File.join(dir.to_s, 'cache'))
|
10
|
+
FileUtils.mkdir_p(@dir)
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
begin
|
15
|
+
FileUtils.remove_entry(@dir)
|
16
|
+
true
|
17
|
+
rescue => e
|
18
|
+
Drydock.logger.error("Cannot clear #{self.class} at #{@dir.inspect}: #{e}")
|
19
|
+
return false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch(key, &blk)
|
24
|
+
filename = build_path(key)
|
25
|
+
|
26
|
+
if File.exist?(filename)
|
27
|
+
File.read(filename)
|
28
|
+
else
|
29
|
+
dirname = File.dirname(filename)
|
30
|
+
FileUtils.mkdir_p(dirname)
|
31
|
+
|
32
|
+
blk.call.tap do |contents|
|
33
|
+
File.open(filename, 'w') do |file|
|
34
|
+
file.write contents
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get(key, &blk)
|
41
|
+
filename = build_path(key)
|
42
|
+
if File.exist?(filename)
|
43
|
+
if blk.nil?
|
44
|
+
File.read(filename)
|
45
|
+
else
|
46
|
+
File.open(filename) do |file|
|
47
|
+
blk.call file
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def key?(key)
|
56
|
+
File.exist?(build_path(key))
|
57
|
+
end
|
58
|
+
|
59
|
+
def set(key, value = nil, &blk)
|
60
|
+
filename = build_path(key)
|
61
|
+
dirname = File.dirname(filename)
|
62
|
+
FileUtils.mkdir_p(dirname)
|
63
|
+
|
64
|
+
File.open(filename, 'w') do |file|
|
65
|
+
if blk.nil?
|
66
|
+
file.write value
|
67
|
+
else
|
68
|
+
blk.call file
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
attr_reader :dir
|
77
|
+
|
78
|
+
def build_path(key)
|
79
|
+
digest = Digest::SHA2.hexdigest(key)
|
80
|
+
subdir1 = digest.slice(0, 2)
|
81
|
+
subdir2 = digest.slice(2, 2)
|
82
|
+
filename = digest.slice(4, digest.length - 4)
|
83
|
+
File.join(dir, subdir1, subdir2, filename)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
module ObjectCaches
|
6
|
+
class InMemoryCache < Base
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@mem = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def clear
|
13
|
+
@mem.clear
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch(key, &blk)
|
18
|
+
@mem.fetch(key, &blk)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(key, &blk)
|
22
|
+
if @mem.key?(key)
|
23
|
+
if blk.nil?
|
24
|
+
@mem[key]
|
25
|
+
else
|
26
|
+
blk.call(StringIO.new(@mem[key]))
|
27
|
+
end
|
28
|
+
else
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def key?(key)
|
34
|
+
@mem.key?(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set(key, value = nil, &blk)
|
38
|
+
if blk
|
39
|
+
buffer = StringIO.new
|
40
|
+
blk.call buffer
|
41
|
+
buffer.rewind
|
42
|
+
@mem[key] = buffer.string
|
43
|
+
else
|
44
|
+
@mem[key] = value
|
45
|
+
end
|
46
|
+
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
module ObjectCaches
|
6
|
+
class NoCache < Base
|
7
|
+
|
8
|
+
def clear
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch(key, &blk)
|
13
|
+
blk.call
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(key, &blk)
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def key?(key)
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def set(key, value = nil, &blk)
|
25
|
+
if blk
|
26
|
+
File.open('/dev/null', 'w') do |file|
|
27
|
+
blk.call file
|
28
|
+
end
|
29
|
+
else
|
30
|
+
# :noop:
|
31
|
+
end
|
32
|
+
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
module Drydock
|
3
|
+
class Phase < Struct.new(:source_image, :build_container, :result_image)
|
4
|
+
|
5
|
+
alias_method :build, :build_container
|
6
|
+
alias_method :build=, :build_container=
|
7
|
+
|
8
|
+
alias_method :result, :result_image
|
9
|
+
alias_method :result=, :result_image=
|
10
|
+
|
11
|
+
alias_method :source, :source_image
|
12
|
+
alias_method :source=, :source_image=
|
13
|
+
|
14
|
+
def self.from(hsh)
|
15
|
+
h = hsh.to_h
|
16
|
+
extra_keys = h.keys - members
|
17
|
+
raise ArgumentError, "unknown options: #{extra_keys.join(', ')}" unless extra_keys.empty?
|
18
|
+
new(*h.values_at(*members))
|
19
|
+
end
|
20
|
+
|
21
|
+
def built?
|
22
|
+
!cached?
|
23
|
+
end
|
24
|
+
|
25
|
+
def cached?
|
26
|
+
build_container.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
def destroy!(force: false)
|
30
|
+
if result_image
|
31
|
+
begin
|
32
|
+
result_image.remove(force: force)
|
33
|
+
rescue Docker::Error::NotFoundError => e
|
34
|
+
# Ignore, because the image could have been deleted by another phase in
|
35
|
+
# another derived chain.
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
build_container.remove(force: force) if built?
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def finalize!(force: false)
|
44
|
+
return self unless built?
|
45
|
+
build_container.remove(force: force)
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
|
2
|
+
module Drydock
|
3
|
+
class PhaseChain
|
4
|
+
extend Forwardable
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def_delegators :@chain, :<<, :at, :empty?, :last, :length, :push, :size
|
8
|
+
|
9
|
+
def self.build_commit_opts(opts = {})
|
10
|
+
{}.tap do |commit|
|
11
|
+
if opts.key?(:command)
|
12
|
+
commit['run'] = {
|
13
|
+
Cmd: opts[:command]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
commit[:author] = opts.fetch(:author, '') if opts.key?(:author)
|
18
|
+
commit[:comment] = opts.fetch(:comment, '') if opts.key?(:comment)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.build_container_opts(image_id, cmd, opts = {})
|
23
|
+
cmd = ['/bin/sh', '-c', cmd.to_s] unless cmd.is_a?(Array)
|
24
|
+
|
25
|
+
ContainerConfig.from(
|
26
|
+
Cmd: cmd,
|
27
|
+
Tty: opts.fetch(:tty, false),
|
28
|
+
Image: image_id
|
29
|
+
).tap do |cc|
|
30
|
+
env = Array(opts[:env])
|
31
|
+
cc[:Env].push(*env) unless env.empty?
|
32
|
+
|
33
|
+
if opts.key?(:expose)
|
34
|
+
cc[:ExposedPorts] ||= {}
|
35
|
+
opts[:expose].each do |port|
|
36
|
+
cc[:ExposedPorts][port] = {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
(cc[:OnBuild] ||= []).push(opts[:on_build]) if opts.key?(:on_build)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.build_pull_opts(repo, tag = nil)
|
45
|
+
if tag
|
46
|
+
{fromImage: repo, tag: tag}
|
47
|
+
else
|
48
|
+
{fromImage: repo}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.create_container(cfg, timeout: nil, &blk)
|
53
|
+
timeout ||= Excon.defaults[:read_timeout]
|
54
|
+
|
55
|
+
Docker::Container.create(cfg).tap do |c|
|
56
|
+
t = Thread.new do
|
57
|
+
begin
|
58
|
+
c.attach(stream: true, stdout: true, stderr: true) do |stream, chunk|
|
59
|
+
case stream
|
60
|
+
when :stdout
|
61
|
+
Drydock.logger.info(message: chunk, annotation: '(O)')
|
62
|
+
when :stderr
|
63
|
+
Drydock.logger.info(message: chunk, annotation: '(E)')
|
64
|
+
else
|
65
|
+
Drydock.logger.info(message: chunk, annotation: '(?)')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
rescue Docker::Error::TimeoutError
|
69
|
+
Drydock.logger.warn(message: "Lost connection to stream; retrying")
|
70
|
+
retry
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
c.start
|
75
|
+
|
76
|
+
blk.call(c) if blk
|
77
|
+
|
78
|
+
c.wait(timeout)
|
79
|
+
t.join
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.from_repo(repo, tag = 'latest')
|
84
|
+
new(Docker::Image.create(build_pull_opts(repo, tag)))
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize(from, parent = nil)
|
88
|
+
@chain = []
|
89
|
+
@from = from
|
90
|
+
@parent = parent
|
91
|
+
@children = []
|
92
|
+
|
93
|
+
@ephemeral_containers = []
|
94
|
+
|
95
|
+
if parent
|
96
|
+
parent.children << self
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def children
|
101
|
+
@children
|
102
|
+
end
|
103
|
+
|
104
|
+
def containers
|
105
|
+
map(&:build_container)
|
106
|
+
end
|
107
|
+
|
108
|
+
def depth
|
109
|
+
@parent ? @parent.depth + 1 : 1
|
110
|
+
end
|
111
|
+
|
112
|
+
def derive
|
113
|
+
self.class.new(last_image, self)
|
114
|
+
end
|
115
|
+
|
116
|
+
def destroy!(force: false)
|
117
|
+
return self if frozen?
|
118
|
+
children.reverse_each { |c| c.destroy!(force: force) } if children
|
119
|
+
ephemeral_containers.map { |c| c.remove(force: force) }
|
120
|
+
|
121
|
+
reverse_each { |c| c.destroy!(force: force) }
|
122
|
+
freeze
|
123
|
+
end
|
124
|
+
|
125
|
+
def each(&blk)
|
126
|
+
@chain.each(&blk)
|
127
|
+
end
|
128
|
+
|
129
|
+
def ephemeral_containers
|
130
|
+
@ephemeral_containers
|
131
|
+
end
|
132
|
+
|
133
|
+
def finalize!(force: false)
|
134
|
+
return self if frozen?
|
135
|
+
|
136
|
+
children.map { |c| c.finalize!(force: force) } if children
|
137
|
+
ephemeral_containers.map { |c| c.remove(force: force) }
|
138
|
+
|
139
|
+
Drydock.logger.info("##{serial}: Final image ID is #{last_image.id}") unless empty?
|
140
|
+
map { |p| p.finalize!(force: force) }
|
141
|
+
freeze
|
142
|
+
end
|
143
|
+
|
144
|
+
def images
|
145
|
+
[root_image] + map(&:result_image)
|
146
|
+
end
|
147
|
+
|
148
|
+
def last_image
|
149
|
+
@chain.last ? @chain.last.result_image : nil
|
150
|
+
end
|
151
|
+
|
152
|
+
def root_image
|
153
|
+
@from
|
154
|
+
end
|
155
|
+
|
156
|
+
def run(cmd, opts = {}, &blk)
|
157
|
+
src_image = last ? last.result_image : @from
|
158
|
+
no_commit = opts.fetch(:no_commit, false)
|
159
|
+
|
160
|
+
no_cache = opts.fetch(:no_cache, false)
|
161
|
+
no_cache = true if no_commit
|
162
|
+
|
163
|
+
build_config = self.class.build_container_opts(src_image.id, cmd, opts)
|
164
|
+
Drydock.logger.info(build_config.inspect)
|
165
|
+
cached_image = ImageRepository.find_by_config(build_config)
|
166
|
+
|
167
|
+
if cached_image && !no_cache
|
168
|
+
Drydock.logger.info(message: "Using cached image ID #{cached_image.id.slice(0, 12)}")
|
169
|
+
|
170
|
+
if no_commit
|
171
|
+
Drydock.logger.info(message: "Skipping commit phase")
|
172
|
+
else
|
173
|
+
self << Phase.from(
|
174
|
+
source_image: src_image,
|
175
|
+
result_image: cached_image
|
176
|
+
)
|
177
|
+
end
|
178
|
+
else
|
179
|
+
if cached_image && no_commit
|
180
|
+
Drydock.logger.info(message: "Found cached image ID #{cached_image.id.slice(0, 12)}, but skipping due to :no_commit")
|
181
|
+
elsif cached_image && no_cache
|
182
|
+
Drydock.logger.info(message: "Found cached image ID #{cached_image.id.slice(0, 12)}, but skipping due to :no_cache")
|
183
|
+
end
|
184
|
+
|
185
|
+
container = self.class.create_container(build_config)
|
186
|
+
yield container if block_given?
|
187
|
+
|
188
|
+
if no_commit
|
189
|
+
Drydock.logger.info(message: "Skipping commit phase")
|
190
|
+
ephemeral_containers << container
|
191
|
+
else
|
192
|
+
if opts.key?(:command)
|
193
|
+
Drydock.logger.info("Command override: #{opts[:command].inspect}")
|
194
|
+
else
|
195
|
+
src_image.refresh!
|
196
|
+
if src_image.info && src_image.info.key?('Config')
|
197
|
+
src_image_config = src_image.info['Config']
|
198
|
+
opts[:command] = src_image_config['Cmd'] if src_image_config.key?('Cmd')
|
199
|
+
end
|
200
|
+
|
201
|
+
Drydock.logger.info("Command retrieval: #{opts[:command].inspect}")
|
202
|
+
Drydock.logger.info("Source image info: #{src_image.info.class} #{src_image.info.inspect}")
|
203
|
+
Drydock.logger.info("Source image config: #{src_image.info['Config'].inspect}")
|
204
|
+
end
|
205
|
+
|
206
|
+
commit_config = self.class.build_commit_opts(opts)
|
207
|
+
Drydock.logger.info(opts.inspect)
|
208
|
+
Drydock.logger.info(commit_config.inspect)
|
209
|
+
|
210
|
+
result = container.commit(commit_config)
|
211
|
+
Drydock.logger.info(message: "Committed image ID #{result.id.slice(0, 12)}")
|
212
|
+
|
213
|
+
self << Phase.from(
|
214
|
+
source_image: src_image,
|
215
|
+
build_container: container,
|
216
|
+
result_image: result
|
217
|
+
)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
self
|
222
|
+
end
|
223
|
+
|
224
|
+
def serial
|
225
|
+
@parent ? "#{@parent.serial}.#{@parent.children.index(self) + 1}.#{size + 1}" : "#{size + 1}"
|
226
|
+
end
|
227
|
+
|
228
|
+
def tag(repo, tag = 'latest', force: false)
|
229
|
+
last_image.tag(repo: repo, tag: tag, force: force)
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
require_relative 'package_manager'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
module Plugins
|
6
|
+
class APK < PackageManager
|
7
|
+
|
8
|
+
def add(*pkgs)
|
9
|
+
opts = pkgs.last.is_a?(Hash) ? pkgs.pop : {}
|
10
|
+
project.run "apk add #{pkgs.join(' ')}", opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def clean
|
14
|
+
project.run "rm -rf /var/cache/apk/*"
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove(*pkgs)
|
18
|
+
project.run "apk del #{pkgs.join(' ')}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def update
|
22
|
+
project.run "apk update"
|
23
|
+
end
|
24
|
+
|
25
|
+
def upgrade
|
26
|
+
project.run "apk upgrade"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
module Plugins
|
6
|
+
class NPM < Base
|
7
|
+
|
8
|
+
def install(*pkgs)
|
9
|
+
opts = pkgs.last.is_a?(Hash) ? pkgs.pop : {}
|
10
|
+
flags = CliFlags.new(opts)
|
11
|
+
project.run("npm install #{flags}#{pkgs.join(' ')}")
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
module Plugins
|
6
|
+
class PackageManager < Base
|
7
|
+
|
8
|
+
def add(*pkgs)
|
9
|
+
raise NotImplementedError, '#add must be overridde in the subclass'
|
10
|
+
end
|
11
|
+
|
12
|
+
def clean
|
13
|
+
raise NotImplementedError, '#clean must be overridde in the subclass'
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove(*pkgs)
|
17
|
+
raise NotImplementedError, '#remove must be overridde in the subclass'
|
18
|
+
end
|
19
|
+
|
20
|
+
def update
|
21
|
+
raise NotImplementedError, '#update must be overridde in the subclass'
|
22
|
+
end
|
23
|
+
|
24
|
+
def upgrade
|
25
|
+
raise NotImplementedError, '#upgrade must be overridde in the subclass'
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Drydock
|
5
|
+
module Plugins
|
6
|
+
class Rubygems < Base
|
7
|
+
|
8
|
+
def add_source(uri)
|
9
|
+
project.run("gem sources --add #{uri}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def install(pkg, opts = {})
|
13
|
+
timeout = opts.delete(:timeout) || 120
|
14
|
+
flags = CliFlags.new(opts)
|
15
|
+
project.run("gem install #{pkg} #{flags}", timeout: timeout)
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove_source(uri)
|
19
|
+
project.run("gem sources --remove #{uri}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_system(opts = {})
|
23
|
+
timeout = opts.delete(:timeout) || 300
|
24
|
+
flags = CliFlags.new(opts)
|
25
|
+
project.run("gem update --system #{flags}", timeout: timeout)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|