dockdev 0.3.8 → 0.4.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: aef435aebef51b0a8aa6535d4f75b6ea8151bd7c474e15547421cc31d7e4dfbc
4
- data.tar.gz: 727d3c78e3c9ae406e4e0dda8c399cced4e86a6c79c4f899d06f626cf365f7d3
3
+ metadata.gz: 190fb93712b6c1bae63281b42de5937866791af1a1d00f1e42b7ebee453436cb
4
+ data.tar.gz: 1b6bd909c27c7ab06f419c3f91846f036dd86548668ba31f0b201acda311fb3b
5
5
  SHA512:
6
- metadata.gz: 81a9a8152486d15d21db57fcaf83a95480bd948c500c28aef61bb3b1f321d5d2d39c5c1d07eb782905ff48b3e763a1db9bf284da78dcc3258d172b6e237684b7
7
- data.tar.gz: 1f9a94feef8e90938743c97623671dcf37ee96153723db7e3f1964c2d0ca08ef8cfbf2bdabc78946c33f37dcbb002a1106c42c6e3b3958a60463e62826876809
6
+ metadata.gz: 2d63a111dc55feab9e5b562a4e8fc75667b0fbc5bc8163a4fd67ef15ad991fa0dbb7919f8614e363a29b0f3f7b9e6a9afe4d6f14bd9567d2a2d829f63a9d4fc2
7
+ data.tar.gz: 36de0b82ec04b79fbf94dff6b375f91eaaee5b676aa9e252a7064f77f96b2c5c81568825dd565936538e900c1d717e85bb4debccb360c2fdfc9eec999f1f4693
data/exe/dockdev CHANGED
@@ -14,6 +14,7 @@ begin
14
14
  Dockdev.with_running_container(contName, command: cmd, root: Dir.getwd)
15
15
  rescue StandardError => ex
16
16
  STDERR.puts ex.message
17
+ STDERR.puts ex.backtrace.join("\n")
17
18
  end
18
19
 
19
20
 
@@ -88,14 +88,20 @@ module Dockdev
88
88
  [@mounts, @ports]
89
89
  end
90
90
 
91
- def process_mount(opts = {}, &block)
92
- mounts, _ = parse_docker_compose
93
- mounts
94
- end
91
+ def apply_context(dockdev_config)
92
+ ddConf = dockdev_config
93
+
94
+ if not ddConf.nil?
95
+ mounts, ports = parse_docker_compose
96
+ mounts.each do |host, docker|
97
+ ddConf.add_mount(host, docker)
98
+ end
99
+ ports.each do |host, docker|
100
+ ddConf.add_port(host, docker)
101
+ end
102
+ end
95
103
 
96
- def process_port(opts = {}, &block)
97
- _, ports = parse_docker_compose
98
- ports
104
+ ddConf
99
105
  end
100
106
 
101
107
  private
@@ -4,6 +4,7 @@ require 'bundler'
4
4
  module Dockdev
5
5
  module Context
6
6
  class Rubygems
7
+ include TR::CondUtils
7
8
 
8
9
  def self.init_path(path)
9
10
  Rubygems.new(path)
@@ -23,15 +24,10 @@ module Dockdev
23
24
  Dir.glob(File.join(@path,"Gemfile"))
24
25
  end
25
26
 
26
- def process_mount(opts = { dir_inside_docker: "/opt" })
27
+ def apply_context(dockdev_config)
28
+ ddConf = dockdev_config
27
29
 
28
- if @mounts.empty?
29
-
30
- dir_inside_docker = opts[:dir_inside_docker]
31
-
32
- script = ["#!/bin/bash"]
33
- #script << "alias be > /dev/null 2>&1 && echo 'alias be=bundle exec' >> ~/.bashrc"
34
- script << "echo 'alias be=\"bundle exec\"' >> ~/.bashrc"
30
+ if not_empty?(ddConf)
35
31
 
36
32
  #
37
33
  # looking at source code
@@ -39,33 +35,91 @@ module Dockdev
39
35
  # seems this is the way to set root for Bundler
40
36
  #
41
37
  ENV['BUNDLE_GEMFILE'] = find_gemfile.first
42
- Bundler.load.dependencies.each do |d|
43
- if not d.source.nil?
44
- src = d.source
45
- if src.path.to_s != "."
46
- pathInsideDocker = File.join(dir_inside_docker, d.name)
47
- @mounts[src.path.expand_path.to_s] = pathInsideDocker
48
- script << "bundle config --global local.#{d.name} #{pathInsideDocker}"
49
- #res[d.name] = src.path.expand_path.to_s
38
+ if not_empty?(ENV['BUNDLE_GEMFILE'])
39
+
40
+ cmd = ["echo 'alias be = \"bundle exec\"' >> /etc/bash.bashrc"]
41
+
42
+ Bundler.load.dependencies.each do |d|
43
+ if not d.source.nil?
44
+ src = d.source
45
+ if src.path.to_s != "."
46
+ pathInsideDocker = File.join(dir_inside_docker, d.name)
47
+ ddConf.add_mount(src.path.expand_path.to_s,pathInsideDocker)
48
+ # following line assumed 'bundle' program already installed inside the image
49
+ cmd << "bundle config --global local.#{d.name} #{pathInsideDocker}"
50
+ end
50
51
  end
51
52
  end
52
- end
53
53
 
54
- scriptOut = File.join(@path,"to-be-executed-once-inside-docker.sh")
55
- File.open(scriptOut,"w") do |f|
56
- f.write script.join("\n")
54
+ if not_empty?(cmd)
55
+
56
+ script = ["#!/bin/bash"]
57
+ script << "if ! command -v bundle &> /dev/null"
58
+ script << " echo \"Command 'bundle' is available!\""
59
+ script.concat(cmd.collect { |e| " #{e}"})
60
+ script << "then"
61
+ script << " echo \"Command 'bundle' not available\""
62
+ #script << " exit 1"
63
+ script << "fi"
64
+
65
+ File.open("rubygems_init.sh","w") do |f|
66
+ f.write script.join("\n")
67
+ end
68
+
69
+ ddConf.append_Dockerfile("COPY rubygems_init.sh /tmp/rubygems_init.sh")
70
+ ddConf.append_Dockerfile("RUN chmod +x /tmp/rubygems_init.sh && /tmp/rubygems_init.sh")
71
+ end
72
+
57
73
  end
58
- `chmod +x #{scriptOut}`
59
74
 
60
75
  end
61
76
 
62
- @mounts
63
-
77
+ ddConf
64
78
  end
65
79
 
66
- def process_port(opts = {})
67
- @ports
68
- end
80
+ #def process_mount(opts = { dir_inside_docker: "/opt" })
81
+
82
+ # if @mounts.empty?
83
+
84
+ # dir_inside_docker = opts[:dir_inside_docker]
85
+
86
+ # script = ["#!/bin/bash"]
87
+ # #script << "alias be > /dev/null 2>&1 && echo 'alias be=bundle exec' >> ~/.bashrc"
88
+ # script << "echo 'alias be=\"bundle exec\"' >> ~/.bashrc"
89
+
90
+ # #
91
+ # # looking at source code
92
+ # # https://github.com/rubygems/rubygems/blob/master/bundler/lib/bundler/shared_helpers.rb#L246
93
+ # # seems this is the way to set root for Bundler
94
+ # #
95
+ # ENV['BUNDLE_GEMFILE'] = find_gemfile.first
96
+ # Bundler.load.dependencies.each do |d|
97
+ # if not d.source.nil?
98
+ # src = d.source
99
+ # if src.path.to_s != "."
100
+ # pathInsideDocker = File.join(dir_inside_docker, d.name)
101
+ # @mounts[src.path.expand_path.to_s] = pathInsideDocker
102
+ # script << "bundle config --global local.#{d.name} #{pathInsideDocker}"
103
+ # #res[d.name] = src.path.expand_path.to_s
104
+ # end
105
+ # end
106
+ # end
107
+
108
+ # scriptOut = File.join(@path,"to-be-executed-once-inside-docker.sh")
109
+ # File.open(scriptOut,"w") do |f|
110
+ # f.write script.join("\n")
111
+ # end
112
+ # `chmod +x #{scriptOut}`
113
+
114
+ # end
115
+
116
+ # @mounts
117
+
118
+ #end
119
+
120
+ #def process_port(opts = {})
121
+ # @ports
122
+ #end
69
123
 
70
124
  end
71
125
  end
@@ -14,12 +14,26 @@ module Dockdev
14
14
  @ctx[name] = cls
15
15
  end
16
16
 
17
+ def registered_context
18
+ @ctx.keys.freeze
19
+ end
20
+
21
+ def registered_context_by_name(name, path)
22
+ ctx = @ctx[id]
23
+ if not ctx.nil?
24
+ ctx.init_path(path)
25
+ end
26
+
27
+ ctx
28
+ end
29
+
17
30
  def get_context(path)
18
- ctx = []
19
- @ctx.values.each do |v|
31
+ ctx = {}
32
+ @ctx.each do |k, v|
20
33
  vv = v.init_path(path)
21
34
  if vv.is_context?
22
- ctx << vv
35
+ #ctx << vv
36
+ ctx[k] = vv
23
37
  end
24
38
  end
25
39
  ctx
@@ -0,0 +1,309 @@
1
+
2
+ require 'toolrack'
3
+
4
+ module Dockdev
5
+
6
+
7
+ ## Support multiple Dockerfile different configurations
8
+ ## Default config is the default Dockerfile file name
9
+ #class Configs
10
+ # include TR::CondUtils
11
+
12
+ # def initialize
13
+ # @configs = { default: Config.new }
14
+ # end
15
+
16
+ # def config_names
17
+ # @configs.keys.freeze
18
+ # end
19
+
20
+ # def default_config_name
21
+ # :default
22
+ # end
23
+
24
+ # def default_config
25
+ # @configs[:default]
26
+ # end
27
+
28
+ # def config(name)
29
+ # indx = File.basename(name)
30
+ # indx = :default if indx == "Dockerfile"
31
+
32
+ # conf = @configs[indx]
33
+ # if conf.nil?
34
+ # conf = Config.new
35
+ # @configs[indx] = conf
36
+ # end
37
+ # conf
38
+ # end
39
+
40
+ # def is_config_exist?(name)
41
+ # not_empty?(config(name))
42
+ # end
43
+
44
+ # private
45
+ # def method_missing(mtd, *args, &block)
46
+ # default_config.send(mtd, *args, &block)
47
+ # end
48
+ #end # end class Configs
49
+
50
+ # Content of the config file
51
+ # which shall configure the to be run docker instance
52
+ # Hence mostly the accessors/readers are related to docker configuration items
53
+ class Config
54
+ include TR::CondUtils
55
+
56
+ # for image
57
+ attr_accessor :workdir
58
+
59
+ # for container
60
+ attr_reader :mounts, :ports
61
+
62
+ # for image
63
+ attr_reader :dockerfile_entries
64
+
65
+ # since context activation is automated:
66
+ # 1. skip_context shall skip all found automated activated context
67
+ # 2. activate_context shall add those context which is not found by automated discovery
68
+ attr_reader :skip_context, :activate_context
69
+ def initialize(val = {})
70
+ @mounts = val[:mounts] || {}
71
+ @ports = val[:ports] || {}
72
+ @dockerfile_entries = val[:dockerfile_entries] || []
73
+ @workdir = val[:workdir] || "/opt"
74
+ @skip_context = val[:skip_context] || []
75
+ @activate_context = val[:activate_context] || []
76
+ end
77
+
78
+
79
+ # Convert internal value into hash to be written to file to get rid of the object
80
+ # encoding in the yaml file
81
+ def to_storage
82
+ { mounts: @mounts, ports: @ports, dockerfile_entries: @dockerfile_entries, workdir: @workdir, skip_context: @skip_context, activate_context: @activate_context }
83
+ end
84
+
85
+
86
+ # Add mount mapping of host => docker follows docker-cli which follows
87
+ # docker cli -v format
88
+ #
89
+ # @param on_host [String] path on host
90
+ # @param on_docker [String] path on docker
91
+ # @param opts [Hash] options for the mount spec definition. Value keys including:
92
+ # @option opts [Symbol] :duplicated_entry_policy :error (default) raise error if the on_host is duplicated/already defined
93
+ # @option opts [Symbol] :duplicated_entry_policy :warn_replace raise warning if the on_host is duplicated/already defined and new entry shall replace the old entry
94
+ # @option opts [Symbol] :duplicated_entry_policy :warn_discard raise warning if the on_host is duplicated/already defined and new entry is discarded
95
+ def add_mount(on_host, on_docker, opts = { duplicated_entry_policy: :error })
96
+ if not_empty?(on_host) and not_empty?(on_docker)
97
+ if @mounts.keys.include?(on_host)
98
+ policy = opts[:duplicated_entry_policy] || :error
99
+ case policy
100
+ when :warn_replace
101
+ logger.warn "on_host '#{on_host}' was mapped to '#{@mounts[on_host]}'. It shall be replaced with '#{on_docker}'"
102
+ @mounts[on_host] = on_docker
103
+
104
+ when :warn_discard
105
+ logger.warn "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'. New value on_docker is ignored."
106
+
107
+ else
108
+ # default policy always raise error
109
+ raise Error, "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'"
110
+ end
111
+ else
112
+ @mounts[on_host] = on_docker
113
+ end
114
+ else
115
+ logger.debug "add_mount unsuccessful = on_host : #{on_host} / on_docker : #{on_docker}"
116
+ raise Error, "on_host mount entry cannot be empty" if is_empty?(on_host)
117
+ raise Error, "on_docker mount entry cannot be empty" if is_empty?(on_docker)
118
+ end
119
+ end
120
+
121
+
122
+ # Add port mapping of host => docker follows docker-cli which follows
123
+ # docker cli -p format
124
+ #
125
+ # @param on_host [String] port on host
126
+ # @param on_docker [String] port on docker
127
+ # @param opts [Hash] options for the port spec definition. Value keys including:
128
+ # @option opts [Symbol] :duplicated_entry_policy :error (default) raise error if the on_host is duplicated/already defined
129
+ # @option opts [Symbol] :duplicated_entry_policy :warn_replace raise warning if the on_host is duplicated/already defined and new entry shall replace the old entry
130
+ # @option opts [Symbol] :duplicated_entry_policy :warn_discard raise warning if the on_host is duplicated/already defined and new entry is discarded
131
+ def add_port(on_host, on_docker, opts = { duplicated_entry_policy: :error })
132
+ if not_empty?(on_host) and not_empty?(on_docker)
133
+ if @ports.keys.include?(on_host)
134
+ policy = opts[:duplicated_entry_policy] || :error
135
+ case policy
136
+ when :warn_replace
137
+ logger.warn "on_host '#{on_host}' was mapped to '#{@mounts[on_host]}'. It shall be replaced with '#{on_docker}'"
138
+ @ports[on_host] = on_docker
139
+
140
+ when :warn_discard
141
+ logger.warn "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'. New value on_docker is ignored."
142
+
143
+ else
144
+ # default policy always raise error
145
+ raise Error, "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'"
146
+ end
147
+ else
148
+ @ports[on_host] = on_docker
149
+ end
150
+ else
151
+ logger.debug "add_port unsuccessful = on_host : #{on_host} / on_docker : #{on_docker}"
152
+ raise Error, "on_host port entry cannot be empty" if is_empty?(on_host)
153
+ raise Error, "on_docker port entry cannot be empty" if is_empty?(on_docker)
154
+ end
155
+
156
+ end
157
+
158
+ # Any instruction to be appended into Dockerfile.
159
+ # Note this did not presume the entry is RUN, COPY or anyting.
160
+ # A full valid Dockerfile entry has to be provided here
161
+ #
162
+ # @param st [String] Full valid Dockerfile line to be embed into Dockerfile
163
+ def append_Dockerfile(st)
164
+ logger.debug "Appending : #{st}"
165
+ @dockerfile_entries << st
166
+ end
167
+
168
+ def is_context_should_skip?(name)
169
+ @skip_context.include?(name)
170
+ end
171
+
172
+
173
+ def manual_activated_context
174
+ @activate_context.freeze
175
+ end
176
+
177
+
178
+ private
179
+ def logger
180
+ Dockdev.logger(:config)
181
+ end
182
+
183
+ def has_additional_entries?
184
+ not @dockerfile_entries.empty?
185
+ end
186
+ end
187
+
188
+ # Managing the config
189
+ class DockdevConfig
190
+ include TR::CondUtils
191
+
192
+ DOCDEV_CONFIG_FILE = "dockdev-config"
193
+
194
+ def self.load(root = Dir.getwd, &block)
195
+ confFile = Dir.glob(File.join(root,"#{DOCDEV_CONFIG_FILE}.*")).grep(/.yml|.yaml$/)
196
+ if confFile.length > 1
197
+ block.call(:found_more, contFile)
198
+ elsif confFile.length == 0
199
+ block.call(:not_found)
200
+ else
201
+ block.call(:found, confFile.first)
202
+ end
203
+ end
204
+
205
+ def initialize(conf = nil)
206
+ logger.debug "Given to initialize : #{conf}"
207
+ if not_empty?(conf)
208
+ @config = parse(conf)
209
+ else
210
+ @config = Config.new
211
+ end
212
+ end
213
+
214
+
215
+ def save(root = Dir.getwd)
216
+ path = File.join(root,"#{DOCDEV_CONFIG_FILE}.yml")
217
+ File.open(path,"w") do |f|
218
+ f.write YAML.dump(@config.to_storage)
219
+ end
220
+ path
221
+ end
222
+
223
+
224
+ # Build the docker image by embedding additional entries into the Dockerfile.
225
+ # This likely will need a temporary file to be created and it should be managed by
226
+ # this operation.
227
+ #
228
+ # @param [Dockdev::Image] image instance
229
+ # @param [String] path to selected Dockerfile
230
+ def build_image(image, dockerfile_path, opts = { root: Dir.getwd }, &block)
231
+
232
+ if has_additional_entries?
233
+
234
+ logger.debug "dockdev_config has additional entry for Dockerfile. Merging additional entries into Dockerfile"
235
+
236
+ root = opts[:root] || Dir.getwd
237
+ # make it static name so:
238
+ # 1. No file removal is required
239
+ # 2. Allow debug on generated Dockerfile
240
+ # 3. Replace Dockerfile on each run and only single Dockerfile is left on system
241
+ # 4. Allow this temporary to be added to .gitignore
242
+ tmpFile = File.join(root, "#{File.basename(dockerfile_path)}-dockdev")
243
+ File.open(tmpFile,"w") do |f|
244
+ found = false
245
+ File.open(dockerfile_path,"r").each_line do |line|
246
+ # detecting the CMD line if there is any
247
+ if line =~ /^CMD/
248
+ found = true
249
+ # here we append the lines
250
+ dockerfile_entries.each do |al|
251
+ f.puts al
252
+ end
253
+ f.write line
254
+
255
+ else
256
+ f.write line
257
+
258
+ end
259
+ end
260
+
261
+ if not found
262
+ @dockerfile_entries.each do |al|
263
+ f.puts al
264
+ end
265
+ end
266
+
267
+ end
268
+
269
+ image.build(tmpFile)
270
+
271
+ else
272
+ logger.debug "dockdev_config has no additional entry for Dockerfile. Proceed to build found Dockerfile"
273
+ image.build(dockerfile_path)
274
+ end
275
+
276
+ end
277
+
278
+
279
+ private
280
+ def parse(conf)
281
+ logger.debug "Given to parse : #{conf}"
282
+ cont = File.read(conf)
283
+ val = YAML.unsafe_load(cont)
284
+ Config.new(val)
285
+ end
286
+
287
+ def method_missing(mtd, *args, &block)
288
+ logger.debug "method_missing '#{mtd}' / #{args}"
289
+ @config.send(mtd, *args, &block)
290
+ end
291
+
292
+ def logger
293
+ Dockdev.logger(:dockdev_config)
294
+ end
295
+
296
+ end
297
+ end
298
+
299
+
300
+ if $0 == __FILE__
301
+
302
+ require_relative '../dockdev'
303
+
304
+ c = Dockdev::DockdevConfig.new
305
+ c.add_mount("/Users/chris/01.Workspaces/02.Code-Factory/08-Workspace/docker-cli","/opt/docker-cli")
306
+ c.save
307
+ end
308
+
309
+
data/lib/dockdev/image.rb CHANGED
@@ -1,10 +1,6 @@
1
1
 
2
2
  require 'docker/cli'
3
3
 
4
- require 'securerandom'
5
-
6
- require_relative 'user_info'
7
-
8
4
  module Dockdev
9
5
  class Image
10
6
  include TR::CondUtils
@@ -24,39 +20,22 @@ module Dockdev
24
20
  end
25
21
 
26
22
  def new_container(cont_name, opts = {})
27
-
28
23
  optss = {
29
24
  interactive: true,
30
25
  tty: true,
31
- container_name: cont_name,
32
- match_user: TR::RTUtils.on_linux?
26
+ container_name: cont_name
33
27
  }
34
28
  optss.merge!(opts)
35
-
36
29
  @cmd_fact.create_container_from_image(@image_name, optss).run
37
-
38
30
  end
39
31
 
40
32
  def build(dockerfile, opts = {})
41
-
42
- dockerfilePath = dockerfile
43
- if is_transfer_user?(opts)
44
- cont = append_transfer_user_in_dockerfile(dockerfile)
45
- dockerfilePath = generated_dockerfile
46
- File.open(dockerfilePath, "w") do |f|
47
- f.write cont
48
- end
49
- end
50
-
51
33
  optss = {
52
34
  context_root: opts[:root],
53
- dockerfile: dockerfilePath
35
+ dockerfile: dockerfile
54
36
  }
55
37
  optss.merge!(opts)
56
38
  @cmd_fact.build_image(@image_name, optss).run
57
-
58
- FileUtils.rm(generated_dockerfile) if File.exist?(generated_dockerfile) and not is_keep_generated_dockerfile?
59
-
60
39
  end
61
40
 
62
41
  def destroy
@@ -68,67 +47,5 @@ module Dockdev
68
47
  end
69
48
  end
70
49
 
71
-
72
- private
73
- def logger
74
- Dockdev.logger(:dockdev_image)
75
- end
76
-
77
- def is_keep_generated_dockerfile?
78
- v = ENV["DOCKDEV_KEEP_GENERATED_DOCKERFILE"]
79
- is_empty?(v) ? false : (v.downcase == "true") ? true : false
80
- end
81
-
82
- def generated_dockerfile
83
- "Dockerfile-dockdev"
84
- end
85
-
86
- def is_transfer_user?(opts = {})
87
- if TR::RTUtils.on_linux?
88
- true
89
- else
90
- (opts[:match_user].nil? || not_bool?(opts[:match_user])) ? false : opts[:match_user]
91
- end
92
- end
93
-
94
- def append_transfer_user_in_dockerfile(file)
95
- if File.exist?(file)
96
- logger.debug "Append transfer user in dockerfile '#{file}'"
97
- res = []
98
- cont = File.read(file)
99
- indx = cont =~ /CMD/
100
- if indx != nil
101
-
102
- res << cont[0...indx]
103
- res << transfer_user_command
104
- res << cont[indx..-1]
105
-
106
- else
107
-
108
- res << cont
109
- res << transfer_user_command
110
-
111
- end
112
-
113
- res.join
114
-
115
- else
116
- ""
117
- end
118
- end
119
-
120
- def transfer_user_command
121
-
122
- ui = UserInfo.user_info
123
- gi = UserInfo.group_info
124
-
125
- res = []
126
- res << "RUN apt-get update && apt-get install -y sudo"
127
- res << "RUN groupadd -f -g #{gi[:gid]} #{gi[:group_name]} && useradd -u #{ui[:uid]} -g #{gi[:gid]} -m #{ui[:login]} && usermod -aG sudo #{ui[:login]} && echo '#{ui[:login]} ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers"
128
- res << "USER #{ui[:login]}"
129
- res.join("\n")
130
-
131
- end
132
-
133
50
  end
134
51
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dockdev
4
- VERSION = "0.3.8"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -12,11 +12,27 @@ module Dockdev
12
12
  end
13
13
 
14
14
  def has_dockerfile?
15
- Dir.glob(File.join(@root,"Dockerfile")).length > 0
15
+ found_dockerfile_count > 0
16
+ end
17
+
18
+ def found_dockerfile_count
19
+ dockerfiles.length
20
+ end
21
+
22
+ def has_multiple_dockerfiles?
23
+ found_dockerfile_count > 1
24
+ end
25
+
26
+ def dockerfiles
27
+ Dir.glob(File.join(@root,"Dockerfile*"))
16
28
  end
17
29
 
18
30
  def dockerfile
19
- Dir.glob(File.join(@root,"Dockerfile")).first
31
+ if has_dockerfile?
32
+ dockerfiles.first
33
+ else
34
+ nil
35
+ end
20
36
  end
21
37
 
22
38
  def has_docker_compose?
data/lib/dockdev.rb CHANGED
@@ -5,82 +5,195 @@ require 'toolrack'
5
5
  require 'docker/cli'
6
6
  require 'colorize'
7
7
 
8
+ require 'tty/prompt'
9
+
8
10
  require_relative "dockdev/version"
9
11
 
10
12
  require_relative 'dockdev/workspace'
11
13
  require_relative 'dockdev/image'
12
14
  require_relative 'dockdev/container'
13
15
 
16
+ require_relative 'dockdev/dockdev_config'
17
+
14
18
  module Dockdev
15
19
  include TR::CondUtils
16
20
 
17
21
  class Error < StandardError; end
18
22
  # Your code goes here...
19
23
 
24
+ # main entry points to start docker
20
25
  def self.with_running_container(contName, opts = {})
21
26
 
27
+ pmt = TTY::Prompt.new
22
28
  root = opts[:root]
23
29
  cmd = opts[:command]
24
30
 
25
- ctx = Dockdev::Context::ContextManager.instance.get_context(root)
26
- logger.debug("Found context : #{ctx}")
31
+ ddConf = load_config(root)
27
32
 
28
33
  cont = Container.new(contName)
29
34
  if cont.has_container?
35
+
36
+ logger.debug "Container '#{contName}' already exist. Just run the container"
30
37
  if cont.running?
31
38
  cont.attach_and_exec(command: cmd)
32
39
  else
33
40
  cont.start_with_command(command: cmd)
34
41
  end
42
+
35
43
  else
44
+
45
+ logger.debug "Container '#{contName}' does not exist. Creating the container"
46
+
36
47
  img = Image.new(contName)
37
48
  ws = opts[:workspace] || root
38
49
  wss = Workspace.new(ws)
39
- if img.has_image?
40
- mount = { root => File.join("/opt",File.basename(root)) }
41
- port = {}
42
- ctx.each do |cctx|
43
- mnts = cctx.process_mount(dir_inside_docker: "/opt")
44
- logger.debug "Mount points by context : #{mnts}"
45
50
 
46
- mount.merge!(mnts) if not mnts.empty?
51
+ # root directory is mounted by default
52
+ ddConf.add_mount(root, File.join(ddConf.workdir,File.basename(root)))
53
+
54
+ ctx = Dockdev::Context::ContextManager.instance.get_context(root)
55
+ logger.debug("Found context : #{ctx}")
47
56
 
48
- prt = cctx.process_port
49
- port.merge!(prt) if not prt.empty?
57
+ ctx.each do |name, cctx|
50
58
 
51
- logger.debug "Ports by context #{cctx} : #{prt}"
59
+ if ddConf.is_context_should_skip?(name)
60
+ logger.debug "Context '#{name}' is asked to be skipped"
61
+ next
52
62
  end
53
63
 
54
- param = { command: cmd, mounts: mount }
55
- param[:ports] = port if not port.empty?
64
+ logger.debug "Appying context '#{name}' "
65
+ # here allow context to add additional Dockerfile entries, mounts and ports
66
+ ddConf = cctx.apply_context(ddConf)
67
+ end
56
68
 
57
- img.new_container(cont.name, param)
69
+ ddConf.manual_activated_context.each do |cctx|
70
+ mctx = Dockdev::Context::ContextManager.instance.registered_context_by_name(cctx)
71
+ logger.debug "Executing manual activated context : #{mctx}"
72
+ ddConf = mctx.apply_context(ddConf) if not mctx.nil?
73
+ end
58
74
 
59
- elsif wss.has_dockerfile?
60
- img.build(wss.dockerfile)
61
-
62
- mount = { root => File.join("/opt",File.basename(root)) }
63
- port = {}
64
- ctx.each do |cctx|
65
- mnt = cctx.process_mount(dir_inside_docker: "/opt")
66
- mount.merge!(mnt) if not mnt.empty?
75
+ if not img.has_image?
67
76
 
68
- logger.debug "Mount points by context #{cctx} : #{mnt}"
77
+ if wss.has_dockerfile?
69
78
 
70
- prt = cctx.process_port
71
- port.merge!(prt) if not prt.empty?
79
+ if wss.has_multiple_dockerfiles?
72
80
 
73
- logger.debug "Ports by context #{cctx} : #{prt}"
74
- end
81
+ selDockerFile = pmt.select("Please select one of the Dockerfile to proceed : ") do |m|
82
+ wss.dockerfiles.each do |df|
83
+ m.choice File.basename(df), df
84
+ end
85
+ end
75
86
 
76
- param = { command: cmd, mounts: mount }
77
- param[:ports] = port if not port.empty?
87
+ else
88
+ selDockerFile = wss.dockerfile
89
+ end
78
90
 
79
- img.new_container(cont.name, param)
91
+ # Delegated to config file to allow
92
+ # config file to embed addtional entries (if there is any) and proceed to build it.
93
+ # During the process it is very likey a temporary Dockerfile shall be created since
94
+ # docker cli works on file basis and this temporary file need to be managed after the process.
95
+ # Hence this makes more sense to let the config handle all those inside the operation
96
+ res = ddConf.build_image(img, selDockerFile, root: root)
97
+ raise Error, "Image failed to be built. Error was : #{res.err_stream}" if res.failed?
98
+ STDOUT.puts "\n Image '#{contName}' built successfully\n\n".green
99
+ #img.build(wss.dockerfile)
80
100
 
81
- else
82
- raise Error, "\n No image and no Dockerfile found to build the image found. Operation aborted. \n\n".red
101
+ else
102
+ raise Error, "\n No image and no Dockerfile found to build the image. Operation aborted. \n\n".red
103
+
104
+ end
83
105
  end
106
+
107
+ # image already exist!
108
+ # Since reach here means container doesn't exist yet.
109
+ # Proceed to create container
110
+
111
+ param = { command: cmd, mounts: ddConf.mounts, ports: ddConf.ports }
112
+ img.new_container(cont.name, param)
113
+
114
+ #if img.has_image?
115
+ #
116
+ # # has image but no container
117
+ # ctx.each do |cctx|
118
+ #
119
+ # ddConf = cctx.apply_context(ddConf)
120
+
121
+ # #cctx.process_mount(dir_inside_docker: ddConf.workdir).each do |host,docker|
122
+ # # logger.debug "Mount points by context '#{cctx}' : #{host} => #{docker}"
123
+ # # ddConf.add_mount(host, docker)
124
+ # #end
125
+
126
+ # #cctx.process_port.each do |host, docker|
127
+ # # logger.debug "Ports mapping by context '#{cctx}' : #{host} => #{docker}"
128
+ # # ddConf.add_port(host, docker)
129
+ # #end
130
+
131
+ # #mnts = cctx.process_mount(dir_inside_docker: ddConf.workdir)
132
+ # #logger.debug "Mount points by context : #{mnts}"
133
+
134
+ # #mount.merge!(mnts) if not mnts.empty?
135
+
136
+ # #prt = cctx.process_port
137
+ # #port.merge!(prt) if not prt.empty?
138
+
139
+ # #logger.debug "Ports by context #{cctx} : #{prt}"
140
+ # end
141
+
142
+ # #param = { command: cmd, mounts: mount }
143
+ # #param[:ports] = port if not port.empty?
144
+ # param = { command: cmd, mounts: ddConf.mounts, ports: ddConf.ports }
145
+
146
+ # img.new_container(cont.name, param)
147
+
148
+ #elsif wss.has_dockerfile?
149
+
150
+ # logger.debug "Dockerfile '#{wss.dockerfile}' found. Proceed building the image."
151
+
152
+ # # Delegated to config file to allow
153
+ # # config file to embed addtional entries (if there is any) and proceed to build it.
154
+ # # During the process it is very likey a temporary Dockerfile shall be created since
155
+ # # docker cli works on file basis and this temporary file need to be managed after the process.
156
+ # # Hence this makes more sense to let the config handle all those inside the operation
157
+ # ddConf.build_image(img, wss, root: root)
158
+ # #img.build(wss.dockerfile)
159
+ #
160
+ # ddConf.add_mount(root, File.join(ddConf.workdir,File.basename(root)))
161
+ # #mount = { root => File.join(ddConf.workdir,File.basename(root)) }
162
+ # #port = {}
163
+ # ctx.each do |cctx|
164
+
165
+ # ddConf = cctx.apply_context(ddConf)
166
+
167
+ # #cctx.process_mount(dir_inside_docker: ddConf.workdir).each do |host,docker|
168
+ # # logger.debug "Mount points by context '#{cctx}' : #{host} => #{docker}"
169
+ # # ddConf.add_mount(host, docker)
170
+ # #end
171
+
172
+ # #cctx.process_port.each do |host, docker|
173
+ # # logger.debug "Ports mapping by context '#{cctx}' : #{host} => #{docker}"
174
+ # # ddConf.add_port(host, docker)
175
+ # #end
176
+
177
+ # #mnt = cctx.process_mount(dir_inside_docker: ddConf.workdir)
178
+ # #mount.merge!(mnt) if not mnt.empty?
179
+
180
+ # #logger.debug "Mount points by context #{cctx} : #{mnt}"
181
+
182
+ # #prt = cctx.process_port
183
+ # #port.merge!(prt) if not prt.empty?
184
+
185
+ # #logger.debug "Ports by context #{cctx} : #{prt}"
186
+ # end
187
+
188
+ # #param = { command: cmd, mounts: mount }
189
+ # #param[:ports] = port if not port.empty?
190
+ # param = { command: cmd, mounts: ddConf.mounts, ports: ddConf.ports }
191
+
192
+ # img.new_container(cont.name, param)
193
+
194
+ #else
195
+ # raise Error, "\n No image and no Dockerfile found to build the image found. Operation aborted. \n\n".red
196
+ #end
84
197
  end
85
198
  end
86
199
 
@@ -99,6 +212,33 @@ module Dockdev
99
212
 
100
213
  end
101
214
 
215
+ # detect if the additional configuration file exist
216
+ def self.load_config(root)
217
+
218
+ ddConf = DockdevConfig.new
219
+ DockdevConfig.load(root) do |ops, *args|
220
+ case ops
221
+ when :found_more
222
+ found = args.first
223
+ pmt = TTY::Prompt.new
224
+ selConf = pmt.select("There are more config files found. Please select one of the files below :") do |m|
225
+ found.each do |f|
226
+ m.choice Pathname.new(f).relative_path_from(root),f
227
+ end
228
+ end
229
+ ddConf = DockdevConfig.new(selConf)
230
+ when :found
231
+ ddConf = DockdevConfig.new(args.first)
232
+ else
233
+ logger.debug "Load config got ops : #{ops}"
234
+ end
235
+ end
236
+
237
+ logger.debug "Loaded config : #{ddConf}"
238
+ ddConf
239
+
240
+ end
241
+
102
242
  def self.logger(tag = nil, &block)
103
243
  if @_logger.nil?
104
244
  @_logger = TeLogger::Tlogger.new(STDOUT)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dockdev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-17 00:00:00.000000000 Z
11
+ date: 2024-01-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: teLogger
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.5.2
47
+ version: 0.5.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.5.2
54
+ version: 0.5.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: tty-prompt
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -128,15 +128,15 @@ files:
128
128
  - lib/dockdev/context.rb
129
129
  - lib/dockdev/context/docker-compose.rb
130
130
  - lib/dockdev/context/rubygems.rb
131
+ - lib/dockdev/dockdev_config.rb
131
132
  - lib/dockdev/image.rb
132
- - lib/dockdev/user_info.rb
133
133
  - lib/dockdev/version.rb
134
134
  - lib/dockdev/workspace.rb
135
135
  - sig/dockdev.rbs
136
136
  homepage: ''
137
137
  licenses: []
138
138
  metadata: {}
139
- post_install_message:
139
+ post_install_message:
140
140
  rdoc_options: []
141
141
  require_paths:
142
142
  - lib
@@ -151,8 +151,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
153
  requirements: []
154
- rubygems_version: 3.5.3
155
- signing_key:
154
+ rubygems_version: 3.5.1
155
+ signing_key:
156
156
  specification_version: 4
157
157
  summary: ''
158
158
  test_files: []
@@ -1,36 +0,0 @@
1
-
2
- require 'etc'
3
-
4
- module Dockdev
5
- module UserInfo
6
- include TR::CondUtils
7
-
8
- def self.user_info(login = nil)
9
- login = Etc.getlogin if is_empty?(login)
10
- res = { login: login }
11
- begin
12
- res[:uid] = Etc.getpwnam(login).uid
13
- rescue Exception => ex
14
- res[:uid] = nil
15
- end
16
- res
17
- end
18
-
19
- def self.group_info(login = nil)
20
- login = Etc.getlogin if is_empty?(login)
21
- res = { }
22
- begin
23
- gnm = Etc.getgrnam(login)
24
- res[:group_name] = gnm.name
25
- res[:gid] = gnm.gid
26
- rescue Exception => ex
27
- p ex
28
- res[:group_name] = ""
29
- res[:gid] = nil
30
- end
31
- res
32
- end
33
-
34
- end
35
- end
36
-