moby-derp 0.3.3 → 0.7.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/README.md +3 -3
- data/example.yml +27 -0
- data/lib/moby_derp/config_file.rb +2 -2
- data/lib/moby_derp/container.rb +35 -2
- data/lib/moby_derp/container_config.rb +57 -5
- data/lib/moby_derp/error.rb +4 -0
- data/lib/moby_derp/freedom_patches/docker/credential.rb +91 -0
- data/lib/{freedom_patches → moby_derp/freedom_patches}/docker/image.rb +0 -0
- data/lib/moby_derp/pod.rb +22 -0
- data/lib/moby_derp/pod_config.rb +31 -6
- data/lib/moby_derp/system_config.rb +9 -4
- data/moby-derp.gemspec +0 -1
- data/smoke_tests/exposed.bats +28 -0
- data/smoke_tests/root_labels.bats +27 -0
- data/smoke_tests/test_helper.bash +1 -1
- metadata +7 -19
- data/lib/moby_derp/moby_info.rb +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c2890cb17a2175bed1a51fce4f28ac8e898555b62c28f626ff7d87bd26b21ec
|
|
4
|
+
data.tar.gz: c49fe1673ac2380e964c7b37b5605c18a52b6f0ec095e6ffba8c345afcf179c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2d2cae1b30c7c75a021adfb5c40b5cf1a658828383236e0852fec61c79e5ddf4ff50bdecff8fa1319b3afc35624c03d79410a2505385ffc9ebaefb4121f25ea
|
|
7
|
+
data.tar.gz: f51fce566d449b97f8a938722f58484668d1e3b220e95f53aee4788ef9f72d2f2f54073cbb55770546e92304e42d36006e1ce67b524da36f81cea6cde9482aa6
|
data/README.md
CHANGED
|
@@ -134,7 +134,7 @@ wrapper script, like this:
|
|
|
134
134
|
|
|
135
135
|
set -e
|
|
136
136
|
|
|
137
|
-
MOBY_DERP_SYSTEM_CONFIG_FILE=/opt/srv/etc/moby-derp/moby-derp.yaml
|
|
137
|
+
export MOBY_DERP_SYSTEM_CONFIG_FILE=/opt/srv/etc/moby-derp/moby-derp.yaml
|
|
138
138
|
|
|
139
139
|
exec /usr/local/bin/moby-derp "$@"
|
|
140
140
|
|
|
@@ -185,7 +185,7 @@ to `moby-derp`. This means that, yes, different users need to use different
|
|
|
185
185
|
filenames. The benefit of this is that the `sudo` configuration becomes a lot
|
|
186
186
|
easier to audit -- the pod name is right there.
|
|
187
187
|
|
|
188
|
-
This means that no matter a user does, they cannot have any effect on any
|
|
188
|
+
This means that no matter what a user does, they cannot have any effect on any
|
|
189
189
|
container which is not named for the pod they're manipulating. There are also
|
|
190
190
|
safety valves around `moby-derp`-managed containers being labelled as such, so
|
|
191
191
|
that in the event that someone does inadvertently name a container in such a
|
|
@@ -220,7 +220,7 @@ still publish to ephemeral ports (using the `:containerPort` syntax, or
|
|
|
220
220
|
`publish_all: true`) if they wish.
|
|
221
221
|
|
|
222
222
|
If a pod *does* need to bind to a specific host port, then that pod/port pair
|
|
223
|
-
should be whitelisted in the system configuration file.
|
|
223
|
+
should be whitelisted in the [system configuration file](#system-configuration).
|
|
224
224
|
|
|
225
225
|
|
|
226
226
|
# Contributing
|
data/example.yml
CHANGED
|
@@ -213,6 +213,33 @@ containers:
|
|
|
213
213
|
ulimit-rttime: 15:16
|
|
214
214
|
ulimit-stack: 17:18
|
|
215
215
|
|
|
216
|
+
# If you're a bit suss as to whether or not one of your containers will
|
|
217
|
+
# successfully start up, you can use the following section to define a
|
|
218
|
+
# start-time health checking regime.
|
|
219
|
+
#
|
|
220
|
+
# How it works is that the defined command is run in the container (using
|
|
221
|
+
# `exec`), and if-and-when that command returns a `0` exit status, the
|
|
222
|
+
# container is considered to be healthy and we're done. If the command
|
|
223
|
+
# returns a non-zero exit status, we wait for `interval` seconds and then
|
|
224
|
+
# retry. If the command executes `attempts` times without receiving a `0`
|
|
225
|
+
# exit status, the container is considered "failed", and no further
|
|
226
|
+
# containers in the pod will be processed, and the `moby-derp` execution
|
|
227
|
+
# will itself exit with a non-zero status.
|
|
228
|
+
startup_health_check:
|
|
229
|
+
# The command to run inside the container via `exec`. You can specify
|
|
230
|
+
# this as a string, or as an array of strings if you prefer to avoid
|
|
231
|
+
# shell quoting hell.
|
|
232
|
+
command: '/usr/local/bin/r-u-ok'
|
|
233
|
+
|
|
234
|
+
# How many seconds to wait between invocations of the command, when
|
|
235
|
+
# it fails. Can be any non-negative number. Defaults to 3.
|
|
236
|
+
interval: 3
|
|
237
|
+
|
|
238
|
+
# How many times to attempt to execute the health-check command before
|
|
239
|
+
# declaring the container hopelessly busticated, and aborting the
|
|
240
|
+
# `moby-derp` run. Must be a positive integer. Defaults to 10.
|
|
241
|
+
attempts: 10
|
|
242
|
+
|
|
216
243
|
# SECTION 2: POD-LEVEL CONFIGURATION
|
|
217
244
|
#
|
|
218
245
|
# The remainder of the configuration items in this file correspond to settings
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require_relative "./error"
|
|
2
2
|
require_relative "./logging_helpers"
|
|
3
3
|
|
|
4
|
-
require "
|
|
4
|
+
require "yaml"
|
|
5
5
|
|
|
6
6
|
module MobyDerp
|
|
7
7
|
class ConfigFile
|
|
@@ -12,7 +12,7 @@ module MobyDerp
|
|
|
12
12
|
def initialize(filename)
|
|
13
13
|
begin
|
|
14
14
|
@logger.debug(logloc) { "Reading configuration file #{filename}" }
|
|
15
|
-
@config =
|
|
15
|
+
@config = YAML.safe_load(File.read(filename))
|
|
16
16
|
rescue Errno::ENOENT
|
|
17
17
|
raise ConfigurationError,
|
|
18
18
|
"file does not exist"
|
data/lib/moby_derp/container.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "docker-api"
|
|
|
5
5
|
require "ipaddr"
|
|
6
6
|
require "json/canonicalization"
|
|
7
7
|
|
|
8
|
+
require_relative "./freedom_patches/docker/credential"
|
|
9
|
+
|
|
8
10
|
module MobyDerp
|
|
9
11
|
class Container
|
|
10
12
|
include LoggingHelpers
|
|
@@ -43,7 +45,35 @@ module MobyDerp
|
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
begin
|
|
46
|
-
Docker::Container.create(hash_labelled(container_creation_parameters))
|
|
48
|
+
c = Docker::Container.create(hash_labelled(container_creation_parameters))
|
|
49
|
+
c.start!.object_id
|
|
50
|
+
|
|
51
|
+
if @config.startup_health_check
|
|
52
|
+
attempts = @config.startup_health_check[:attempts]
|
|
53
|
+
|
|
54
|
+
while attempts > 0
|
|
55
|
+
stdout, stderr, exitstatus = c.exec(@config.startup_health_check[:command])
|
|
56
|
+
if exitstatus > 0
|
|
57
|
+
stdout_lines = stdout.empty? ? [] : ["stdout:"] + stdout.join("\n").split("\n").map { |l| " #{l}" }
|
|
58
|
+
stderr_lines = stderr.empty? ? [] : ["stderr:"] + stderr.join("\n").split("\n").map { |l| " #{l}" }
|
|
59
|
+
output_lines = stdout_lines + stderr_lines
|
|
60
|
+
@logger.warn(logloc) { "Startup health check failed on #{container_name} with status #{exitstatus}." + (output_lines.empty? ? "" : ([" Output:"] + output_lines.join("\n "))) }
|
|
61
|
+
|
|
62
|
+
attempts -= 1
|
|
63
|
+
sleep @config.startup_health_check[:interval]
|
|
64
|
+
else
|
|
65
|
+
@logger.info(logloc) { "Startup health check passed." }
|
|
66
|
+
break
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if attempts == 0
|
|
71
|
+
raise MobyDerp::StartupHealthCheckError,
|
|
72
|
+
"Container #{container_name} has failed the startup health check command #{@config.startup_health_check[:attempts]} times. Aborting."
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
c.id
|
|
47
77
|
rescue Docker::Error::ClientError => ex
|
|
48
78
|
raise MobyDerp::ContainerError,
|
|
49
79
|
"moby daemon returned error: #{ex.message}"
|
|
@@ -71,6 +101,7 @@ module MobyDerp
|
|
|
71
101
|
}
|
|
72
102
|
}
|
|
73
103
|
end
|
|
104
|
+
params["ExposedPorts"] = Hash[@pod.expose.map { |ex| [ex, {}] }]
|
|
74
105
|
else
|
|
75
106
|
params["HostConfig"] = {
|
|
76
107
|
"NetworkMode" => "container:#{@pod.root_container_id}",
|
|
@@ -126,7 +157,9 @@ module MobyDerp
|
|
|
126
157
|
params["Labels"] = @pod.common_labels.merge(@config.labels)
|
|
127
158
|
params["Labels"]["org.hezmatt.moby-derp.pod-name"] = @pod.name
|
|
128
159
|
|
|
129
|
-
|
|
160
|
+
if @root_container
|
|
161
|
+
params["Labels"] = @pod.root_labels.merge(params["Labels"])
|
|
162
|
+
else
|
|
130
163
|
params["Labels"]["org.hezmatt.moby-derp.root-container-id"] = @pod.root_container_id
|
|
131
164
|
end
|
|
132
165
|
end
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
require_relative "
|
|
1
|
+
require_relative "./freedom_patches/docker/image"
|
|
2
2
|
require_relative "./error"
|
|
3
3
|
require_relative "./mount"
|
|
4
4
|
|
|
5
5
|
require "docker-api"
|
|
6
|
+
require "shellwords"
|
|
6
7
|
|
|
7
8
|
module MobyDerp
|
|
8
9
|
class ContainerConfig
|
|
9
10
|
attr_reader :name, :image, :update_image, :command, :environment, :mounts,
|
|
10
|
-
:labels, :readonly, :stop_signal, :stop_timeout, :user, :restart, :limits
|
|
11
|
+
:labels, :readonly, :stop_signal, :stop_timeout, :user, :restart, :limits,
|
|
12
|
+
:startup_health_check
|
|
11
13
|
|
|
12
14
|
def initialize(system_config:,
|
|
13
15
|
pod_config:,
|
|
@@ -23,13 +25,14 @@ module MobyDerp
|
|
|
23
25
|
stop_timeout: 10,
|
|
24
26
|
user: nil,
|
|
25
27
|
restart: "no",
|
|
26
|
-
limits: {}
|
|
28
|
+
limits: {},
|
|
29
|
+
startup_health_check: nil
|
|
27
30
|
)
|
|
28
31
|
@system_config, @pod_config, @name, @image = system_config, pod_config, "#{pod_config.name}.#{container_name}", image
|
|
29
32
|
|
|
30
33
|
@update_image, @command, @environment, @mounts, @labels = update_image, command, environment, mounts, labels
|
|
31
34
|
@readonly, @stop_signal, @stop_timeout, @user, @restart = readonly, stop_signal, stop_timeout, user, restart
|
|
32
|
-
@limits = limits
|
|
35
|
+
@limits, @startup_health_check = limits, startup_health_check
|
|
33
36
|
|
|
34
37
|
validate_image
|
|
35
38
|
validate_update_image
|
|
@@ -43,6 +46,7 @@ module MobyDerp
|
|
|
43
46
|
validate_user
|
|
44
47
|
validate_restart
|
|
45
48
|
validate_limits
|
|
49
|
+
validate_startup_health_check
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
private
|
|
@@ -66,7 +70,10 @@ module MobyDerp
|
|
|
66
70
|
def validate_command
|
|
67
71
|
case @command
|
|
68
72
|
when String
|
|
69
|
-
|
|
73
|
+
# Despite the Docker Engine API spec saying you can pass a string,
|
|
74
|
+
# if you do it doesn't get parsed into arguments... so that's pretty
|
|
75
|
+
# fucking useless.
|
|
76
|
+
@command = Shellwords.split(@command)
|
|
70
77
|
when Array
|
|
71
78
|
unless @command.all? { |c| String === c }
|
|
72
79
|
raise ConfigurationError, "all elements of the command array must be strings"
|
|
@@ -327,6 +334,51 @@ module MobyDerp
|
|
|
327
334
|
end
|
|
328
335
|
end
|
|
329
336
|
|
|
337
|
+
def validate_startup_health_check
|
|
338
|
+
if @startup_health_check.nil?
|
|
339
|
+
# This is fine
|
|
340
|
+
return
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
unless @startup_health_check.is_a?(Hash)
|
|
344
|
+
raise ConfigurationError,
|
|
345
|
+
"startup_health_check must be a hash"
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
case @startup_health_check[:command]
|
|
349
|
+
when String
|
|
350
|
+
@startup_health_check[:command] = Shellwords.split(@startup_health_check[:command])
|
|
351
|
+
when Array
|
|
352
|
+
unless @startup_health_check[:command].all? { |c| String === c }
|
|
353
|
+
raise ConfigurationError, "all elements of the health check command array must be strings"
|
|
354
|
+
end
|
|
355
|
+
when NilClass
|
|
356
|
+
raise ConfigurationError, "health check command must be specified"
|
|
357
|
+
else
|
|
358
|
+
raise ConfigurationError,
|
|
359
|
+
"health check command must be string or array of strings"
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
@startup_health_check[:interval] ||= 3
|
|
363
|
+
@startup_health_check[:attempts] ||= 10
|
|
364
|
+
|
|
365
|
+
unless Numeric === @startup_health_check[:interval]
|
|
366
|
+
raise ConfigurationError, "startup health check interval must be a number"
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
if @startup_health_check[:interval] < 0
|
|
370
|
+
raise ConfigurationError, "startup health check interval cannot be negative"
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
unless Integer === @startup_health_check[:attempts]
|
|
374
|
+
raise ConfigurationError, "startup health check attempt count must be an integer"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
if @startup_health_check[:attempts] < 1
|
|
378
|
+
raise ConfigurationError, "startup health check attempt count must be a positive integer"
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
330
382
|
def validate_boolean(name)
|
|
331
383
|
v = instance_variable_get(:"@#{name}")
|
|
332
384
|
unless v == true || v == false
|
data/lib/moby_derp/error.rb
CHANGED
|
@@ -9,6 +9,10 @@ module MobyDerp
|
|
|
9
9
|
# Indicates there was a problem manipulating a live container
|
|
10
10
|
class ContainerError < Error; end
|
|
11
11
|
|
|
12
|
+
# Raised when the startup health check has failed spectacularly for
|
|
13
|
+
# a container
|
|
14
|
+
class StartupHealthCheckError < Error; end
|
|
15
|
+
|
|
12
16
|
# Only appears when an inviolable assertion is invalid, and indicates
|
|
13
17
|
# there is a bug in the code
|
|
14
18
|
class BugError < Error; end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "open3"
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
module Docker
|
|
7
|
+
module Credential
|
|
8
|
+
#:nocov:
|
|
9
|
+
def self.for(ref)
|
|
10
|
+
image_cred(ref)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def self.image_cred(ref)
|
|
16
|
+
cred_helper = hunt_for_image_domain_cred(ref, docker_config.fetch("credHelpers", {}))
|
|
17
|
+
|
|
18
|
+
if cred_helper
|
|
19
|
+
out, rv = Open3.capture2e("docker-credential-#{cred_helper}", "get", stdin_data: image_domain(ref))
|
|
20
|
+
|
|
21
|
+
if rv.exitstatus == 0
|
|
22
|
+
cred_data = JSON.parse(out)
|
|
23
|
+
|
|
24
|
+
{ username: cred_data["Username"], password: cred_data["Secret"], serveraddress: image_domain(ref) }
|
|
25
|
+
else
|
|
26
|
+
raise RuntimeError, "Credential helper docker-credential-#{cred_helper} exited with #{rv.exitstatus}: #{out}"
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
cred = hunt_for_image_domain_cred(ref, docker_config.fetch("auths", {}))
|
|
30
|
+
|
|
31
|
+
if cred
|
|
32
|
+
user, pass = JSON.parse(cred.fetch("auth", "null"))&.unpack("m")&.first&.split(":", 2)
|
|
33
|
+
|
|
34
|
+
if user && pass
|
|
35
|
+
{ username: user, password: pass, serveraddress: image_domain(ref) }
|
|
36
|
+
else
|
|
37
|
+
{}
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
{}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.hunt_for_image_domain_cred(ref, section)
|
|
46
|
+
section.find do |k, v|
|
|
47
|
+
if k =~ /:\/\//
|
|
48
|
+
# Doin' it URL style
|
|
49
|
+
URI(k).host == image_domain(ref)
|
|
50
|
+
else
|
|
51
|
+
k == image_domain(ref)
|
|
52
|
+
end
|
|
53
|
+
end&.last
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.image_domain(ref)
|
|
57
|
+
if match_data = ref.match(Docker::Image::IMAGE_REFERENCE)
|
|
58
|
+
if match_data[1] =~ /[.:]/
|
|
59
|
+
match_data[1].gsub(/\/\z/, '')
|
|
60
|
+
else
|
|
61
|
+
"index.docker.io"
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
raise ArgumentError, "Could not parse image ref #{ref.inspect}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.docker_config
|
|
69
|
+
if (f = Pathname.new(ENV.fetch("DOCKER_CONFIG", "~/.docker")).expand_path.join("config.json")).exist?
|
|
70
|
+
JSON.parse(f.read)
|
|
71
|
+
else
|
|
72
|
+
{}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
module ImageClassMixin
|
|
77
|
+
def create(opts = {}, creds = nil, conn = Docker.connection, &block)
|
|
78
|
+
if creds.nil?
|
|
79
|
+
image = opts["fromImage"] || opts[:fromImage]
|
|
80
|
+
|
|
81
|
+
creds = Docker::Credential.for(image)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
super(opts, creds, conn, &block)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
#:nocov:
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
Docker::Image.singleton_class.prepend(Docker::Credential::ImageClassMixin)
|
|
File without changes
|
data/lib/moby_derp/pod.rb
CHANGED
|
@@ -18,6 +18,20 @@ module MobyDerp
|
|
|
18
18
|
|
|
19
19
|
@logger.debug(logloc) { "Root container ID is #{@root_container_id}" }
|
|
20
20
|
|
|
21
|
+
desired_container_names = @config.containers.map(&:name)
|
|
22
|
+
|
|
23
|
+
Docker::Container.all(all: true).each do |c|
|
|
24
|
+
c_name = c.info["Names"].first.sub(/^\//, '')
|
|
25
|
+
|
|
26
|
+
if c.info["Labels"]["org.hezmatt.moby-derp.pod-name"] == name &&
|
|
27
|
+
!c.info["Labels"]["org.hezmatt.moby-derp.root-container-id"].nil? &&
|
|
28
|
+
!desired_container_names.include?(c_name.split(".", 2).last)
|
|
29
|
+
@logger.info(logloc) { "Removing stale container #{c_name}" }
|
|
30
|
+
c.stop
|
|
31
|
+
c.delete
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
21
35
|
@config.containers.each do |cfg|
|
|
22
36
|
@logger.info(logloc) { "Checking container #{cfg.name}" }
|
|
23
37
|
|
|
@@ -48,6 +62,10 @@ module MobyDerp
|
|
|
48
62
|
@config.common_labels
|
|
49
63
|
end
|
|
50
64
|
|
|
65
|
+
def root_labels
|
|
66
|
+
@config.root_labels
|
|
67
|
+
end
|
|
68
|
+
|
|
51
69
|
def common_environment
|
|
52
70
|
@config.common_environment
|
|
53
71
|
end
|
|
@@ -68,6 +86,10 @@ module MobyDerp
|
|
|
68
86
|
@config.hostname
|
|
69
87
|
end
|
|
70
88
|
|
|
89
|
+
def expose
|
|
90
|
+
@config.expose
|
|
91
|
+
end
|
|
92
|
+
|
|
71
93
|
private
|
|
72
94
|
|
|
73
95
|
def root_container
|
data/lib/moby_derp/pod_config.rb
CHANGED
|
@@ -3,13 +3,24 @@ require_relative "./container_config"
|
|
|
3
3
|
require_relative "./logging_helpers"
|
|
4
4
|
require_relative "./mount"
|
|
5
5
|
|
|
6
|
-
require "safe_yaml"
|
|
7
6
|
require "socket"
|
|
8
7
|
|
|
9
8
|
module MobyDerp
|
|
10
9
|
class PodConfig < ConfigFile
|
|
11
10
|
include LoggingHelpers
|
|
12
11
|
|
|
12
|
+
VALID_CONFIG_KEYS = %w{
|
|
13
|
+
containers
|
|
14
|
+
hostname
|
|
15
|
+
common_environment
|
|
16
|
+
common_labels
|
|
17
|
+
root_labels
|
|
18
|
+
common_mounts
|
|
19
|
+
expose
|
|
20
|
+
publish
|
|
21
|
+
publish_all
|
|
22
|
+
}
|
|
23
|
+
|
|
13
24
|
attr_reader :name,
|
|
14
25
|
:containers,
|
|
15
26
|
:hostname,
|
|
@@ -21,8 +32,8 @@ module MobyDerp
|
|
|
21
32
|
:publish,
|
|
22
33
|
:publish_all,
|
|
23
34
|
:mount_root,
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
:system_config,
|
|
36
|
+
:logger
|
|
26
37
|
|
|
27
38
|
def initialize(filename, system_config)
|
|
28
39
|
@logger = system_config.logger
|
|
@@ -34,6 +45,10 @@ module MobyDerp
|
|
|
34
45
|
@name = File.basename(filename, ".*")
|
|
35
46
|
validate_name
|
|
36
47
|
|
|
48
|
+
unless (bad_keys = @config.keys - VALID_CONFIG_KEYS).empty?
|
|
49
|
+
raise ConfigurationError,
|
|
50
|
+
"Invalid pod configuration key(s): #{bad_keys.inspect}"
|
|
51
|
+
end
|
|
37
52
|
|
|
38
53
|
unless @config.has_key?("containers")
|
|
39
54
|
raise ConfigurationError,
|
|
@@ -100,6 +115,11 @@ module MobyDerp
|
|
|
100
115
|
raise ConfigurationError,
|
|
101
116
|
"container name #{name.inspect} is invalid (must contain only alphanumerics, underscores, and hyphens)"
|
|
102
117
|
end
|
|
118
|
+
|
|
119
|
+
unless data.is_a?(Hash)
|
|
120
|
+
raise ConfigurationError,
|
|
121
|
+
"container data must be a hash"
|
|
122
|
+
end
|
|
103
123
|
end
|
|
104
124
|
|
|
105
125
|
begin
|
|
@@ -181,18 +201,23 @@ module MobyDerp
|
|
|
181
201
|
"expose must be an array"
|
|
182
202
|
end
|
|
183
203
|
|
|
184
|
-
@expose.map!
|
|
185
|
-
|
|
186
|
-
@expose.each do |e|
|
|
204
|
+
@expose.map! do |e|
|
|
205
|
+
e = e.to_s
|
|
187
206
|
unless e.is_a?(String) && e =~ %r{\A\d+(/(tcp|udp))?\z}
|
|
188
207
|
raise ConfigurationError,
|
|
189
208
|
"exposed ports must be integers, with an optional protocol specifier (got #{e.inspect})"
|
|
190
209
|
end
|
|
191
210
|
|
|
211
|
+
if $1.nil?
|
|
212
|
+
e += "/tcp"
|
|
213
|
+
end
|
|
214
|
+
|
|
192
215
|
if e.to_i < 1 || e.to_i > 65535
|
|
193
216
|
raise ConfigurationError,
|
|
194
217
|
"exposed port #{e} is out of range (expected 1-65535)"
|
|
195
218
|
end
|
|
219
|
+
|
|
220
|
+
e
|
|
196
221
|
end
|
|
197
222
|
end
|
|
198
223
|
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
require_relative "./config_file"
|
|
2
2
|
|
|
3
|
-
require "safe_yaml"
|
|
4
|
-
|
|
5
3
|
module MobyDerp
|
|
6
4
|
class SystemConfig < ConfigFile
|
|
7
5
|
attr_reader :mount_root, :port_whitelist, :network_name, :use_host_resolv_conf,
|
|
8
6
|
:cpu_count, :cpu_bits
|
|
9
7
|
|
|
10
|
-
def initialize(
|
|
8
|
+
def initialize(config_data_or_filename, moby_info, logger)
|
|
11
9
|
@logger = logger
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
case config_data_or_filename
|
|
12
|
+
when String
|
|
13
|
+
super(config_data_or_filename)
|
|
14
|
+
when Hash
|
|
15
|
+
@config = stringify_keys(config_data_or_filename)
|
|
16
|
+
else
|
|
17
|
+
raise ArgumentError, "Unsupported type for config_data_or_filename parameter"
|
|
18
|
+
end
|
|
14
19
|
|
|
15
20
|
@mount_root = @config["mount_root"]
|
|
16
21
|
@port_whitelist = stringify_keys(@config["port_whitelist"] || {})
|
data/moby-derp.gemspec
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
load test_helper
|
|
2
|
+
|
|
3
|
+
@test "Exposed ports" {
|
|
4
|
+
config_file <<-'EOF'
|
|
5
|
+
expose:
|
|
6
|
+
- 80
|
|
7
|
+
- "53/udp"
|
|
8
|
+
containers:
|
|
9
|
+
bob:
|
|
10
|
+
image: busybox:latest
|
|
11
|
+
command: sleep 600
|
|
12
|
+
common_labels:
|
|
13
|
+
moby-derp-smoke-test: ayup
|
|
14
|
+
EOF
|
|
15
|
+
|
|
16
|
+
run $MOBY_DERP_BIN $TEST_CONFIG_FILE
|
|
17
|
+
|
|
18
|
+
echo "status: $status"
|
|
19
|
+
echo "output: $output"
|
|
20
|
+
|
|
21
|
+
[ "$status" = "0" ]
|
|
22
|
+
container_running "mdst"
|
|
23
|
+
container_running "mdst.bob"
|
|
24
|
+
|
|
25
|
+
docker inspect mdst --format='{{.Config.ExposedPorts}}'
|
|
26
|
+
docker inspect mdst --format='{{.Config.ExposedPorts}}' | grep 'map.*53/udp:{}'
|
|
27
|
+
docker inspect mdst --format='{{.Config.ExposedPorts}}' | grep 'map.*80/tcp:{}'
|
|
28
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
load test_helper
|
|
2
|
+
|
|
3
|
+
@test "Core labels" {
|
|
4
|
+
config_file <<-'EOF'
|
|
5
|
+
root_labels:
|
|
6
|
+
foo: booblee
|
|
7
|
+
containers:
|
|
8
|
+
bob:
|
|
9
|
+
image: busybox:latest
|
|
10
|
+
command: sleep 1
|
|
11
|
+
common_labels:
|
|
12
|
+
moby-derp-smoke-test: ayup
|
|
13
|
+
EOF
|
|
14
|
+
|
|
15
|
+
run $MOBY_DERP_BIN $TEST_CONFIG_FILE
|
|
16
|
+
|
|
17
|
+
echo "status: $status"
|
|
18
|
+
echo "output: $output"
|
|
19
|
+
|
|
20
|
+
[ "$status" = "0" ]
|
|
21
|
+
container_running "mdst"
|
|
22
|
+
container_running "mdst.bob"
|
|
23
|
+
|
|
24
|
+
docker inspect mdst --format='{{.Config.Labels}}'
|
|
25
|
+
docker inspect mdst --format='{{.Config.Labels}}' | grep 'map.*foo:booblee'
|
|
26
|
+
! docker inspect mdst.bob --format='{{.Config.Labels}}' | grep 'map.*foo:booblee'
|
|
27
|
+
}
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moby-derp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matt Palmer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-06-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: docker-api
|
|
@@ -38,20 +38,6 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: safe_yaml
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
41
|
- !ruby/object:Gem::Dependency
|
|
56
42
|
name: bundler
|
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -208,20 +194,22 @@ files:
|
|
|
208
194
|
- README.md
|
|
209
195
|
- bin/moby-derp
|
|
210
196
|
- example.yml
|
|
211
|
-
- lib/freedom_patches/docker/image.rb
|
|
212
197
|
- lib/moby_derp/config_file.rb
|
|
213
198
|
- lib/moby_derp/container.rb
|
|
214
199
|
- lib/moby_derp/container_config.rb
|
|
215
200
|
- lib/moby_derp/error.rb
|
|
201
|
+
- lib/moby_derp/freedom_patches/docker/credential.rb
|
|
202
|
+
- lib/moby_derp/freedom_patches/docker/image.rb
|
|
216
203
|
- lib/moby_derp/logging_helpers.rb
|
|
217
|
-
- lib/moby_derp/moby_info.rb
|
|
218
204
|
- lib/moby_derp/mount.rb
|
|
219
205
|
- lib/moby_derp/pod.rb
|
|
220
206
|
- lib/moby_derp/pod_config.rb
|
|
221
207
|
- lib/moby_derp/system_config.rb
|
|
222
208
|
- moby-derp.gemspec
|
|
209
|
+
- smoke_tests/exposed.bats
|
|
223
210
|
- smoke_tests/minimal.bats
|
|
224
211
|
- smoke_tests/no_file.bats
|
|
212
|
+
- smoke_tests/root_labels.bats
|
|
225
213
|
- smoke_tests/test_helper.bash
|
|
226
214
|
homepage: http://github.com/mpalmer/moby-derp
|
|
227
215
|
licenses: []
|
|
@@ -241,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
241
229
|
- !ruby/object:Gem::Version
|
|
242
230
|
version: '0'
|
|
243
231
|
requirements: []
|
|
244
|
-
rubygems_version: 3.0.
|
|
232
|
+
rubygems_version: 3.0.3
|
|
245
233
|
signing_key:
|
|
246
234
|
specification_version: 4
|
|
247
235
|
summary: A simple management system for a pod of moby containers
|
data/lib/moby_derp/moby_info.rb
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
module MobyDerp
|
|
2
|
-
class MobyInfo
|
|
3
|
-
attr_reader :cpu_count, :cpu_bits
|
|
4
|
-
|
|
5
|
-
def initialize(info)
|
|
6
|
-
@cpu_count = info["NCPU"]
|
|
7
|
-
# As far as I can tell, the only 32-bit platform Moby supports is
|
|
8
|
-
# armhf; if that turns out to be incorrect, amend the list below.
|
|
9
|
-
@cpu_bits = %w{armhf}.include?(info["Architecture"]) ? 32 : 64
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
end
|