dockdev 0.3.8 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
-