moby-derp 0.7.2 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1d04c23ce729679e9e1cd34fa7fbb63dac8672a2212cbed442cdaef698ac20e
4
- data.tar.gz: f7cfa0d0fa9f21e6f02766669cd2a763614828a75e60ba6b35146c9c2b3d5877
3
+ metadata.gz: f655f86d8be3982dcf24c11734361071cb040093e22ca89fb59bfb7ac786a8ad
4
+ data.tar.gz: 39a54635e4cebed9f54a2eaeb5124e0b62435a582b3ef3d52e018530c9683efb
5
5
  SHA512:
6
- metadata.gz: 877ef1db2ccb0f5b32cdb2bca422edc2bb3995fa650d65fad0fa581fc89c5ae4b3873759f2da706700c0a3a2b8b8392cc315659d4cf58b480efd5fcc1e91fc18
7
- data.tar.gz: bf4261f7c31c01628df29e43cc42b84346525802225999e790e0268c6246b2730f9716dc087c8270cde96a75730a345276cb176e78a5d38603517532fd9c07ee
6
+ metadata.gz: d3209066a78a89998bd549568d65345e8ec86c26f31b1860084cfe4d1325f1e8a8388c392915a701ff54fe1949c572de4d2ac7ec2efd2285670a1ba789eba4c5
7
+ data.tar.gz: b964a1d45ebb633437fb29e656ae48e9743cda5bc768605098289b10a0a12a13ec5c6ed142397a14bc08b6b0c3bb94bef4255f0b9f911863abb0db9a04e17774
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request: {}
8
+
9
+ jobs:
10
+ build:
11
+ name: "moby-derp-test"
12
+ runs-on: ${{ matrix.os }}
13
+ timeout-minutes: 60
14
+
15
+ strategy:
16
+ fail-fast: false
17
+
18
+ matrix:
19
+ os: [ubuntu-latest]
20
+ ruby: ["2.7"]
21
+
22
+ steps:
23
+ - uses: actions/checkout@master
24
+ with:
25
+ fetch-depth: 1
26
+
27
+ - name: Setup ruby
28
+ uses: actions/setup-ruby@v1
29
+ with:
30
+ ruby-version: ${{ matrix.ruby }}
31
+
32
+ - name: Setup bundler
33
+ run: |
34
+ gem install bundler -v 2.1.4 --no-doc
35
+
36
+ - name: Setup gems
37
+ run: bundle install --jobs 4
38
+
39
+ - name: RSpec
40
+ run: bundle exec rspec
data/README.md CHANGED
@@ -124,6 +124,12 @@ The keys are:
124
124
  "localhost"; so pointing to your local caching resolver using `127.0.0.1`
125
125
  will not end in happiness and puppies.
126
126
 
127
+ * **`host_hostname`**: the hostname to use when generating the container
128
+ hostname. This might be useful, for example, if moby-derp is running
129
+ in a container by itself, with a hostname that reflects its management
130
+ function, but the containers that are managed conceptually belong to
131
+ the host.
132
+
127
133
  If you wish to modify the location of the `moby-derp` system-wide configuration
128
134
  file, you can do so by setting the `MOBY_DERP_SYSTEM_CONFIG_FILE` environment
129
135
  variable. Note, however, that it is a terrible idea to let ordinary users control
@@ -169,7 +175,8 @@ caveats:
169
175
  # Security
170
176
 
171
177
  This section discusses the security model and guarantees of `moby-derp`. It
172
- isn't necessary to simply use `moby-derp` in most circumstances.
178
+ isn't necessary to understand this section if you simply want to use
179
+ `moby-derp` in most circumstances.
173
180
 
174
181
  The fundamental principle of `moby-derp` is that users are given control over
175
182
  a certain portion of the container and filesystem namespace, by virtue of their
@@ -233,7 +240,7 @@ See [`CONTRIBUTING.md`](CONTRIBUTING.md).
233
240
  Unless otherwise stated, everything in this repo is covered by the following
234
241
  copyright notice:
235
242
 
236
- Copyright (C) 2019 Matt Palmer <matt@hezmatt.org>
243
+ Copyright (C) 2019, 2023, 2024 Matt Palmer <matt@hezmatt.org>
237
244
 
238
245
  This program is free software: you can redistribute it and/or modify it
239
246
  under the terms of the GNU General Public License version 3, as
data/example.yml CHANGED
@@ -48,24 +48,60 @@ containers:
48
48
  #
49
49
  command: '--foo=bar --wombat'
50
50
 
51
+ # Occasionally a container image will have defined an entrypoint that isn't
52
+ # appropriate for your situation. Although fixing the image is the preferable
53
+ # option, if that isn't feasible, you can instead override the image's
54
+ # entrypoint here.
55
+ entrypoint: '/usr/local/bin/alternate-init'
56
+
51
57
  # The 12-factor app concept says that all configuration should be passed
52
58
  # via the environment. If that is your bag, then this section will make
53
59
  # you very happy.
54
60
  #
55
61
  environment:
56
- # The environment is specified as a map of variable name to variable
57
- # values. For passing big strings, you may want to get familiar with
58
- # YAML's many string quoting and escaping modes.
62
+ # The environment is specified as a map of environment variable names to
63
+ # environment variable values.
59
64
  #
60
65
  APP_ENV: production
61
- # Embedding your private key like this isn't necessarily a particularly
62
- # good idea, but as an example of where you might need multiline strings,
63
- # it works very well.
66
+
67
+ # Environment variable values must be strings by the time YAML has
68
+ # finished with them. That means that values YAML would normally interpret
69
+ # as other types must be quoted.
70
+ HAS_BUGS: 'false'
71
+ FINAL_ANSWER: '42'
72
+
73
+ # For passing big strings, you may want to get familiar with
74
+ # YAML's many string quoting and escaping modes.
75
+ #
76
+ COMPLEX_CONFIG: |
77
+ {
78
+ "foo": "bar",
79
+ "baz": [1, 1, 2, 3, 5, 8, 13]
80
+ }
81
+
82
+ # Read an environment variable value from a file.
83
+ #
84
+ # The keys are environment variable names, like any other, but the values
85
+ # those environment variables will have are read from the file given as the
86
+ # value of the entry, specified relative to the pod root directory on the host.
87
+ #
88
+ # The intended use case for this feature is for injecting secrets, where the
89
+ # value of the secret is written to a file on the host by some means outside the
90
+ # usual deployment mechanisms (so the data never goes into the repo), but can
91
+ # be accessed at runtime via the environment.
92
+ #
93
+ # Note that if you specify the same environment variable name both in here and
94
+ # in the `environment` map, the results are undefined. So don't do that.
95
+ #
96
+ # These environment files are read when `moby-derp` runs, and so only need be
97
+ # readable by the user that `moby-derp` runs as (typically `root`).
98
+ #
99
+ environment_files:
100
+ # This will set an environment variable named `PRIVATE_KEY` with the value
101
+ # read from `$pod_root/secret/private.key`, which presumably an administrator
102
+ # has previously written.
64
103
  #
65
- PRIVATE_KEY: |
66
- -----BEGIN PRIVATE KEY-----
67
- AAAAsd...
68
- -----END PRIVATE KEY-----
104
+ PRIVATE_KEY: secrets/private.key
69
105
 
70
106
  # Persisting data past the lifetime of a particular container is the job
71
107
  # of mounts. Here you can specify filesystem locations on the host
@@ -19,7 +19,7 @@ module MobyDerp
19
19
 
20
20
  def run
21
21
  container_name = @root_container ? @pod.name : @config.name
22
- @logger.debug(logloc) { "Calculated container name is #{container_name} (@root_container: #{@root_container.inspect}, @config.name: #{@config.name}, @pod.name: #{@pod.name}" }
22
+ @logger.debug(logloc) { "Calculated container name is #{container_name} (@root_container: #{@root_container.inspect}, @config.name: #{@config.name}, @pod.name: #{@pod.name})" }
23
23
 
24
24
  begin
25
25
  existing_container = Docker::Container.get(container_name)
@@ -29,6 +29,12 @@ module MobyDerp
29
29
  if existing_container.info["Config"]["Labels"]["org.hezmatt.moby-derp.config-hash"] == params_hash(container_creation_parameters)
30
30
  # Container is up-to-date
31
31
  @logger.info(logloc) { "Container #{container_name} is up-to-date" }
32
+ @logger.debug(logloc) { "Container #{container_name} has restart=#{@config.restart}, state is #{existing_container.info.dig("State", "Status")}" }
33
+
34
+ if @config.restart == "always" && existing_container.info.dig("State", "Status") != "running"
35
+ existing_container.start
36
+ end
37
+
32
38
  return existing_container.id
33
39
  end
34
40
 
@@ -85,9 +91,11 @@ module MobyDerp
85
91
  def container_creation_parameters
86
92
  {}.tap do |params|
87
93
  if @root_container
94
+ params["Hostname"] = @pod.hostname
88
95
  params["HostConfig"] = {
89
96
  "NetworkMode" => @pod.network_name,
90
97
  "Init" => true,
98
+ "IpcMode" => "shareable"
91
99
  }
92
100
  params["MacAddress"] = container_mac_address
93
101
  if network_uses_ipv6? && user_defined_network?
@@ -113,7 +121,7 @@ module MobyDerp
113
121
  params["HostConfig"]["RestartPolicy"] = parsed_restart_policy
114
122
  params["HostConfig"]["Mounts"] = merged_mounts.map { |mount| mount_structure(mount) }
115
123
 
116
- params["Env"] = @pod.common_environment.merge(@config.environment).map { |k, v| "#{k}=#{v}" }
124
+ params["Env"] = @pod.common_environment.merge(read_environment_files).merge(@config.environment).map { |k, v| "#{k}=#{v}" }
117
125
  params["Volumes"] = {}
118
126
 
119
127
  params["name"] = @root_container ? @pod.name : @config.name
@@ -123,6 +131,10 @@ module MobyDerp
123
131
  params["StopSignal"] = @config.stop_signal
124
132
  params["StopTimeout"] = @config.stop_timeout
125
133
 
134
+ if !@config.entrypoint.nil?
135
+ params["Entrypoint"] = [@config.entrypoint]
136
+ end
137
+
126
138
  if @config.user
127
139
  params["User"] = @config.user
128
140
  end
@@ -165,6 +177,12 @@ module MobyDerp
165
177
  end
166
178
  end
167
179
 
180
+ def read_environment_files
181
+ @config.environment_files.map do |var, filename|
182
+ [var, File.read(File.join(@pod.mount_root, filename))]
183
+ end.to_h
184
+ end
185
+
168
186
  def hash_labelled(params)
169
187
  params.tap do |params|
170
188
  config_hash = params_hash(params)
@@ -7,8 +7,8 @@ require "shellwords"
7
7
 
8
8
  module MobyDerp
9
9
  class ContainerConfig
10
- attr_reader :name, :image, :update_image, :command, :environment, :mounts,
11
- :labels, :readonly, :stop_signal, :stop_timeout, :user, :restart, :limits,
10
+ attr_reader :name, :image, :update_image, :command, :entrypoint, :environment, :environment_files,
11
+ :mounts, :labels, :readonly, :stop_signal, :stop_timeout, :user, :restart, :limits,
12
12
  :startup_health_check
13
13
 
14
14
  def initialize(system_config:,
@@ -17,7 +17,9 @@ module MobyDerp
17
17
  image:,
18
18
  update_image: true,
19
19
  command: [],
20
+ entrypoint: nil,
20
21
  environment: {},
22
+ environment_files: {},
21
23
  mounts: [],
22
24
  labels: {},
23
25
  readonly: false,
@@ -30,14 +32,16 @@ module MobyDerp
30
32
  )
31
33
  @system_config, @pod_config, @name, @image = system_config, pod_config, "#{pod_config.name}.#{container_name}", image
32
34
 
33
- @update_image, @command, @environment, @mounts, @labels = update_image, command, environment, mounts, labels
35
+ @update_image, @command, @entrypoint, @environment, @environment_files, @mounts, @labels = update_image, command, entrypoint, environment, environment_files, mounts, labels
34
36
  @readonly, @stop_signal, @stop_timeout, @user, @restart = readonly, stop_signal, stop_timeout, user, restart
35
37
  @limits, @startup_health_check = limits, startup_health_check
36
38
 
37
39
  validate_image
38
40
  validate_update_image
39
41
  validate_command
42
+ validate_entrypoint
40
43
  validate_environment
44
+ validate_environment_files
41
45
  validate_mounts
42
46
  validate_labels
43
47
  validate_readonly
@@ -88,6 +92,13 @@ module MobyDerp
88
92
  end
89
93
  end
90
94
 
95
+ def validate_entrypoint
96
+ unless @entrypoint.nil? || @entrypoint.is_a?(String)
97
+ raise ConfigurationError,
98
+ "entrypoint must be a string"
99
+ end
100
+ end
101
+
91
102
  def validate_environment
92
103
  validate_hash(:environment)
93
104
 
@@ -97,6 +108,27 @@ module MobyDerp
97
108
  end
98
109
  end
99
110
 
111
+ def validate_environment_files
112
+ validate_hash(:environment_files)
113
+
114
+ if (bad_vars = @environment_files.keys.select { |k| k =~ /=/ }) != []
115
+ raise ConfigurationError,
116
+ "environment variable names cannot include equals signs: #{bad_vars.inspect}"
117
+ end
118
+
119
+ @environment_files.each do |k, v|
120
+ if v =~ %r{(\A|/)\.\.($|/)}
121
+ raise ConfigurationError,
122
+ "path traversal detected in #{k} -- this ain't a Fortinet, mate!"
123
+ end
124
+
125
+ if v =~ %r{\A(/|~)}
126
+ raise ConfigurationError,
127
+ "environment file for #{k} can only be a relative path"
128
+ end
129
+ end
130
+ end
131
+
100
132
  def validate_mounts
101
133
  unless @mounts.is_a?(Array)
102
134
  raise ConfigurationError,
data/lib/moby_derp/pod.rb CHANGED
@@ -38,9 +38,9 @@ module MobyDerp
38
38
  begin
39
39
  MobyDerp::Container.new(pod: self, container_config: cfg).run
40
40
  rescue MobyDerp::ContainerError => ex
41
- raise MobyDerp::ContainerError,
42
- "error while running container #{cfg.name}: #{ex.message}",
43
- ex.backtrace
41
+ @logger.error(logloc) {
42
+ (["Error while running container #{cfg.name}: #{ex.message}"] + ex.backtrace.map { |l| " #{l}" }).join("\n")
43
+ }
44
44
  end
45
45
  end
46
46
  end
@@ -57,8 +57,8 @@ module MobyDerp
57
57
  @containers = @config.fetch("containers")
58
58
  validate_containers
59
59
 
60
- @logger.debug(logloc) { "Hostname is #{Socket.gethostname}" }
61
- @hostname = @config.fetch("hostname", "#{@name.gsub("_", "-")}-#{Socket.gethostname}")
60
+ @logger.debug(logloc) { "Hostname is #{@system_config.host_hostname}" }
61
+ @hostname = @config.fetch("hostname", "#{@name.gsub("_", "-")}-#{@system_config.host_hostname}")
62
62
 
63
63
  @common_environment = @config.fetch("common_environment", {})
64
64
  @common_labels = @config.fetch("common_labels", {})
@@ -1,9 +1,11 @@
1
1
  require_relative "./config_file"
2
2
 
3
+ require "socket"
4
+
3
5
  module MobyDerp
4
6
  class SystemConfig < ConfigFile
5
7
  attr_reader :mount_root, :port_whitelist, :network_name, :use_host_resolv_conf,
6
- :cpu_count, :cpu_bits
8
+ :cpu_count, :cpu_bits, :host_hostname
7
9
 
8
10
  def initialize(config_data_or_filename, moby_info, logger)
9
11
  @logger = logger
@@ -17,6 +19,7 @@ module MobyDerp
17
19
  raise ArgumentError, "Unsupported type for config_data_or_filename parameter"
18
20
  end
19
21
 
22
+ @host_hostname = @config["host_hostname"] || Socket.gethostname
20
23
  @mount_root = @config["mount_root"]
21
24
  @port_whitelist = stringify_keys(@config["port_whitelist"] || {})
22
25
  @network_name = @config["network_name"] || "bridge"
@@ -46,11 +49,6 @@ module MobyDerp
46
49
  raise ConfigurationError,
47
50
  "use_host_resolv_conf must be true or false"
48
51
  end
49
-
50
- unless File.directory?(@mount_root)
51
- raise ConfigurationError,
52
- "mount_root #{@mount_root} must exist and be a directory"
53
- end
54
52
  end
55
53
 
56
54
  private
@@ -0,0 +1,24 @@
1
+ load test_helper
2
+
3
+ @test "Restart always" {
4
+ config_file <<-'EOF'
5
+ containers:
6
+ bob:
7
+ image: busybox:latest
8
+ command: sleep 600
9
+ restart: always
10
+ common_labels:
11
+ moby-derp-smoke-test: ayup
12
+ EOF
13
+
14
+ run $MOBY_DERP_BIN $TEST_CONFIG_FILE
15
+
16
+ [ "$status" = "0" ]
17
+
18
+ docker stop mdst.bob
19
+ run $MOBY_DERP_BIN $TEST_CONFIG_FILE
20
+
21
+ echo $output
22
+ container_running "mdst"
23
+ container_running "mdst.bob"
24
+ }
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.7.2
4
+ version: 0.8.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: 2020-07-11 00:00:00.000000000 Z
11
+ date: 2024-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docker-api
@@ -186,6 +186,7 @@ executables:
186
186
  extensions: []
187
187
  extra_rdoc_files: []
188
188
  files:
189
+ - ".github/workflows/ci.yml"
189
190
  - ".gitignore"
190
191
  - ".travis.yml"
191
192
  - ".yardopts"
@@ -210,6 +211,7 @@ files:
210
211
  - smoke_tests/exposed.bats
211
212
  - smoke_tests/minimal.bats
212
213
  - smoke_tests/no_file.bats
214
+ - smoke_tests/restart_always.bats
213
215
  - smoke_tests/root_labels.bats
214
216
  - smoke_tests/test_helper.bash
215
217
  homepage: http://github.com/mpalmer/moby-derp
@@ -230,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
232
  - !ruby/object:Gem::Version
231
233
  version: '0'
232
234
  requirements: []
233
- rubygems_version: 3.0.3
235
+ rubygems_version: 3.2.5
234
236
  signing_key:
235
237
  specification_version: 4
236
238
  summary: A simple management system for a pod of moby containers