dockdev 0.3.8 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/dockdev +9 -3
- data/lib/dockdev/container.rb +1 -0
- data/lib/dockdev/context/docker-compose.rb +13 -7
- data/lib/dockdev/context/rubygems.rb +80 -26
- data/lib/dockdev/context.rb +17 -3
- data/lib/dockdev/dockdev_config.rb +311 -0
- data/lib/dockdev/image.rb +3 -1
- data/lib/dockdev/version.rb +1 -1
- data/lib/dockdev/workspace.rb +18 -2
- data/lib/dockdev.rb +196 -34
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc457c18386138fb748fcc182da23a0a53613bcdfad90288ff2e4f553fe81ab7
|
4
|
+
data.tar.gz: 6a9d639a8a4a09f76c3948cead657f50688e42c0aaada6f45222dd766b45e947
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa1e22aafc5d128eef30ab82e3b5545d616efda9c30eccc5e85a414aa0638b260be020d3d4d376cceeca0197417f3b9f61098c7fbd607f68576e218ce00e40aa
|
7
|
+
data.tar.gz: 677f6dcc88a354185760d1abed3064f8292352ae9f6efb08c4493f1ac42f45eccdb25e70e5ea4b71708aa2cd458ce42de6d3704edcbc658b812f7d257a58e768
|
data/exe/dockdev
CHANGED
@@ -7,13 +7,19 @@
|
|
7
7
|
|
8
8
|
require_relative '../lib/dockdev'
|
9
9
|
|
10
|
-
contName = ARGV.first || File.basename(Dir.getwd)
|
11
|
-
cmd = ARGV[1]
|
10
|
+
#contName = ARGV.first || File.basename(Dir.getwd)
|
11
|
+
#cmd = ARGV[1]
|
12
|
+
#input = ARGV[1]
|
13
|
+
|
14
|
+
uconf = Dockdev::UserConfig.new(ARGV.first)
|
15
|
+
contName = uconf.container_name || File.basename(Dir.getwd)
|
12
16
|
|
13
17
|
begin
|
14
|
-
Dockdev.with_running_container(contName, command: cmd, root: Dir.getwd)
|
18
|
+
#Dockdev.with_running_container(contName, command: cmd, root: Dir.getwd)
|
19
|
+
Dockdev.with_running_container(contName, user_config: uconf, root: Dir.getwd)
|
15
20
|
rescue StandardError => ex
|
16
21
|
STDERR.puts ex.message
|
22
|
+
STDERR.puts ex.backtrace.join("\n")
|
17
23
|
end
|
18
24
|
|
19
25
|
|
data/lib/dockdev/container.rb
CHANGED
@@ -88,14 +88,20 @@ module Dockdev
|
|
88
88
|
[@mounts, @ports]
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
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
|
27
|
+
def apply_context(dockdev_config)
|
28
|
+
ddConf = dockdev_config
|
27
29
|
|
28
|
-
if
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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(ddConf.workdir, 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
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
63
|
-
|
77
|
+
ddConf
|
64
78
|
end
|
65
79
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
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
|
data/lib/dockdev/context.rb
CHANGED
@@ -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.
|
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,311 @@
|
|
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
|
+
attr_accessor :network
|
62
|
+
|
63
|
+
# for image
|
64
|
+
attr_reader :dockerfile_entries
|
65
|
+
|
66
|
+
# since context activation is automated:
|
67
|
+
# 1. skip_context shall skip all found automated activated context
|
68
|
+
# 2. activate_context shall add those context which is not found by automated discovery
|
69
|
+
attr_reader :skip_context, :activate_context
|
70
|
+
def initialize(val = {})
|
71
|
+
@mounts = val[:mounts] || {}
|
72
|
+
@ports = val[:ports] || {}
|
73
|
+
@network = val[:network] || nil
|
74
|
+
@dockerfile_entries = val[:dockerfile_entries] || []
|
75
|
+
@workdir = val[:workdir] || "/opt"
|
76
|
+
@skip_context = val[:skip_context] || []
|
77
|
+
@activate_context = val[:activate_context] || []
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# Convert internal value into hash to be written to file to get rid of the object
|
82
|
+
# encoding in the yaml file
|
83
|
+
def to_storage
|
84
|
+
{ mounts: @mounts, ports: @ports, dockerfile_entries: @dockerfile_entries, workdir: @workdir, skip_context: @skip_context, activate_context: @activate_context }
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# Add mount mapping of host => docker follows docker-cli which follows
|
89
|
+
# docker cli -v format
|
90
|
+
#
|
91
|
+
# @param on_host [String] path on host
|
92
|
+
# @param on_docker [String] path on docker
|
93
|
+
# @param opts [Hash] options for the mount spec definition. Value keys including:
|
94
|
+
# @option opts [Symbol] :duplicated_entry_policy :error (default) raise error if the on_host is duplicated/already defined
|
95
|
+
# @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
|
96
|
+
# @option opts [Symbol] :duplicated_entry_policy :warn_discard raise warning if the on_host is duplicated/already defined and new entry is discarded
|
97
|
+
def add_mount(on_host, on_docker, opts = { duplicated_entry_policy: :error })
|
98
|
+
if not_empty?(on_host) and not_empty?(on_docker)
|
99
|
+
if @mounts.keys.include?(on_host)
|
100
|
+
policy = opts[:duplicated_entry_policy] || :error
|
101
|
+
case policy
|
102
|
+
when :warn_replace
|
103
|
+
logger.warn "on_host '#{on_host}' was mapped to '#{@mounts[on_host]}'. It shall be replaced with '#{on_docker}'"
|
104
|
+
@mounts[on_host] = on_docker
|
105
|
+
|
106
|
+
when :warn_discard
|
107
|
+
logger.warn "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'. New value on_docker is ignored."
|
108
|
+
|
109
|
+
else
|
110
|
+
# default policy always raise error
|
111
|
+
raise Error, "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'"
|
112
|
+
end
|
113
|
+
else
|
114
|
+
@mounts[on_host] = on_docker
|
115
|
+
end
|
116
|
+
else
|
117
|
+
logger.debug "add_mount unsuccessful = on_host : #{on_host} / on_docker : #{on_docker}"
|
118
|
+
raise Error, "on_host mount entry cannot be empty" if is_empty?(on_host)
|
119
|
+
raise Error, "on_docker mount entry cannot be empty" if is_empty?(on_docker)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Add port mapping of host => docker follows docker-cli which follows
|
125
|
+
# docker cli -p format
|
126
|
+
#
|
127
|
+
# @param on_host [String] port on host
|
128
|
+
# @param on_docker [String] port on docker
|
129
|
+
# @param opts [Hash] options for the port spec definition. Value keys including:
|
130
|
+
# @option opts [Symbol] :duplicated_entry_policy :error (default) raise error if the on_host is duplicated/already defined
|
131
|
+
# @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
|
132
|
+
# @option opts [Symbol] :duplicated_entry_policy :warn_discard raise warning if the on_host is duplicated/already defined and new entry is discarded
|
133
|
+
def add_port(on_host, on_docker, opts = { duplicated_entry_policy: :error })
|
134
|
+
if not_empty?(on_host) and not_empty?(on_docker)
|
135
|
+
if @ports.keys.include?(on_host)
|
136
|
+
policy = opts[:duplicated_entry_policy] || :error
|
137
|
+
case policy
|
138
|
+
when :warn_replace
|
139
|
+
logger.warn "on_host '#{on_host}' was mapped to '#{@mounts[on_host]}'. It shall be replaced with '#{on_docker}'"
|
140
|
+
@ports[on_host] = on_docker
|
141
|
+
|
142
|
+
when :warn_discard
|
143
|
+
logger.warn "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'. New value on_docker is ignored."
|
144
|
+
|
145
|
+
else
|
146
|
+
# default policy always raise error
|
147
|
+
raise Error, "on_host '#{on_host}' already mapped to '#{@mounts[on_host]}'"
|
148
|
+
end
|
149
|
+
else
|
150
|
+
@ports[on_host] = on_docker
|
151
|
+
end
|
152
|
+
else
|
153
|
+
logger.debug "add_port unsuccessful = on_host : #{on_host} / on_docker : #{on_docker}"
|
154
|
+
raise Error, "on_host port entry cannot be empty" if is_empty?(on_host)
|
155
|
+
raise Error, "on_docker port entry cannot be empty" if is_empty?(on_docker)
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
# Any instruction to be appended into Dockerfile.
|
161
|
+
# Note this did not presume the entry is RUN, COPY or anyting.
|
162
|
+
# A full valid Dockerfile entry has to be provided here
|
163
|
+
#
|
164
|
+
# @param st [String] Full valid Dockerfile line to be embed into Dockerfile
|
165
|
+
def append_Dockerfile(st)
|
166
|
+
logger.debug "Appending : #{st}"
|
167
|
+
@dockerfile_entries << st
|
168
|
+
end
|
169
|
+
|
170
|
+
def is_context_should_skip?(name)
|
171
|
+
@skip_context.include?(name)
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
def manual_activated_context
|
176
|
+
@activate_context.freeze
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
private
|
181
|
+
def logger
|
182
|
+
Dockdev.logger(:config)
|
183
|
+
end
|
184
|
+
|
185
|
+
def has_additional_entries?
|
186
|
+
not @dockerfile_entries.empty?
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Managing the config
|
191
|
+
class DockdevConfig
|
192
|
+
include TR::CondUtils
|
193
|
+
|
194
|
+
DOCDEV_CONFIG_FILE = "dockdev-config"
|
195
|
+
|
196
|
+
def self.load(root = Dir.getwd, &block)
|
197
|
+
confFile = Dir.glob(File.join(root,"#{DOCDEV_CONFIG_FILE}.*")).grep(/.yml|.yaml$/)
|
198
|
+
if confFile.length > 1
|
199
|
+
block.call(:found_more, contFile)
|
200
|
+
elsif confFile.length == 0
|
201
|
+
block.call(:not_found)
|
202
|
+
else
|
203
|
+
block.call(:found, confFile.first)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def initialize(conf = nil)
|
208
|
+
logger.debug "Given to initialize : #{conf}"
|
209
|
+
if not_empty?(conf)
|
210
|
+
@config = parse(conf)
|
211
|
+
else
|
212
|
+
@config = Config.new
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
def save(root = Dir.getwd)
|
218
|
+
path = File.join(root,"#{DOCDEV_CONFIG_FILE}.yml")
|
219
|
+
File.open(path,"w") do |f|
|
220
|
+
f.write YAML.dump(@config.to_storage)
|
221
|
+
end
|
222
|
+
path
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
# Build the docker image by embedding additional entries into the Dockerfile.
|
227
|
+
# This likely will need a temporary file to be created and it should be managed by
|
228
|
+
# this operation.
|
229
|
+
#
|
230
|
+
# @param [Dockdev::Image] image instance
|
231
|
+
# @param [String] path to selected Dockerfile
|
232
|
+
def build_image(image, dockerfile_path, opts = { root: Dir.getwd }, &block)
|
233
|
+
|
234
|
+
if has_additional_entries?
|
235
|
+
|
236
|
+
logger.debug "dockdev_config has additional entry for Dockerfile. Merging additional entries into Dockerfile"
|
237
|
+
|
238
|
+
root = opts[:root] || Dir.getwd
|
239
|
+
# make it static name so:
|
240
|
+
# 1. No file removal is required
|
241
|
+
# 2. Allow debug on generated Dockerfile
|
242
|
+
# 3. Replace Dockerfile on each run and only single Dockerfile is left on system
|
243
|
+
# 4. Allow this temporary to be added to .gitignore
|
244
|
+
tmpFile = File.join(root, "#{File.basename(dockerfile_path)}-dockdev")
|
245
|
+
File.open(tmpFile,"w") do |f|
|
246
|
+
found = false
|
247
|
+
File.open(dockerfile_path,"r").each_line do |line|
|
248
|
+
# detecting the CMD line if there is any
|
249
|
+
if line =~ /^CMD/
|
250
|
+
found = true
|
251
|
+
# here we append the lines
|
252
|
+
dockerfile_entries.each do |al|
|
253
|
+
f.puts al
|
254
|
+
end
|
255
|
+
f.write line
|
256
|
+
|
257
|
+
else
|
258
|
+
f.write line
|
259
|
+
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
if not found
|
264
|
+
@dockerfile_entries.each do |al|
|
265
|
+
f.puts al
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
image.build(tmpFile)
|
272
|
+
|
273
|
+
else
|
274
|
+
logger.debug "dockdev_config has no additional entry for Dockerfile. Proceed to build found Dockerfile"
|
275
|
+
image.build(dockerfile_path)
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
private
|
282
|
+
def parse(conf)
|
283
|
+
logger.debug "Given to parse : #{conf}"
|
284
|
+
cont = File.read(conf)
|
285
|
+
val = YAML.unsafe_load(cont)
|
286
|
+
Config.new(val)
|
287
|
+
end
|
288
|
+
|
289
|
+
def method_missing(mtd, *args, &block)
|
290
|
+
logger.debug "method_missing '#{mtd}' / #{args}"
|
291
|
+
@config.send(mtd, *args, &block)
|
292
|
+
end
|
293
|
+
|
294
|
+
def logger
|
295
|
+
Dockdev.logger(:dockdev_config)
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
if $0 == __FILE__
|
303
|
+
|
304
|
+
require_relative '../dockdev'
|
305
|
+
|
306
|
+
c = Dockdev::DockdevConfig.new
|
307
|
+
c.add_mount("/Users/chris/01.Workspaces/02.Code-Factory/08-Workspace/docker-cli","/opt/docker-cli")
|
308
|
+
c.save
|
309
|
+
end
|
310
|
+
|
311
|
+
|
data/lib/dockdev/image.rb
CHANGED
@@ -53,10 +53,12 @@ module Dockdev
|
|
53
53
|
dockerfile: dockerfilePath
|
54
54
|
}
|
55
55
|
optss.merge!(opts)
|
56
|
-
@cmd_fact.build_image(@image_name, optss).run
|
56
|
+
res = @cmd_fact.build_image(@image_name, optss).run
|
57
57
|
|
58
58
|
FileUtils.rm(generated_dockerfile) if File.exist?(generated_dockerfile) and not is_keep_generated_dockerfile?
|
59
59
|
|
60
|
+
res
|
61
|
+
|
60
62
|
end
|
61
63
|
|
62
64
|
def destroy
|
data/lib/dockdev/version.rb
CHANGED
data/lib/dockdev/workspace.rb
CHANGED
@@ -12,11 +12,27 @@ module Dockdev
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def has_dockerfile?
|
15
|
-
|
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
|
-
|
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,217 @@ 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
|
+
|
18
|
+
require_relative 'dockdev/user_config'
|
19
|
+
|
14
20
|
module Dockdev
|
15
21
|
include TR::CondUtils
|
16
22
|
|
17
23
|
class Error < StandardError; end
|
18
24
|
# Your code goes here...
|
19
25
|
|
26
|
+
# main entry points to start docker
|
20
27
|
def self.with_running_container(contName, opts = {})
|
21
28
|
|
29
|
+
pmt = TTY::Prompt.new
|
22
30
|
root = opts[:root]
|
23
|
-
cmd = opts[:command]
|
31
|
+
cmd = opts[:command] || ""
|
24
32
|
|
25
|
-
|
26
|
-
|
33
|
+
ddConf = load_config(root)
|
34
|
+
|
35
|
+
user_config = opts[:user_config]
|
27
36
|
|
28
37
|
cont = Container.new(contName)
|
29
38
|
if cont.has_container?
|
39
|
+
|
40
|
+
logger.debug "Container '#{contName}' already exist. Just run the container"
|
30
41
|
if cont.running?
|
31
42
|
cont.attach_and_exec(command: cmd)
|
32
43
|
else
|
33
44
|
cont.start_with_command(command: cmd)
|
34
45
|
end
|
46
|
+
|
35
47
|
else
|
48
|
+
|
49
|
+
logger.debug "Container '#{contName}' does not exist. Creating the container"
|
50
|
+
|
36
51
|
img = Image.new(contName)
|
37
52
|
ws = opts[:workspace] || root
|
38
53
|
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
54
|
|
46
|
-
|
55
|
+
# root directory is mounted by default
|
56
|
+
ddConf.add_mount(root, File.join(ddConf.workdir,File.basename(root)))
|
47
57
|
|
48
|
-
|
49
|
-
|
58
|
+
if user_config.has_key?(:network)
|
59
|
+
ddConf.network = user_config.network
|
60
|
+
end
|
61
|
+
|
62
|
+
ctx = Dockdev::Context::ContextManager.instance.get_context(root)
|
63
|
+
logger.debug("Found context : #{ctx}")
|
64
|
+
|
65
|
+
ctx.each do |name, cctx|
|
50
66
|
|
51
|
-
|
67
|
+
if ddConf.is_context_should_skip?(name)
|
68
|
+
logger.debug "Context '#{name}' is asked to be skipped"
|
69
|
+
next
|
52
70
|
end
|
53
71
|
|
54
|
-
|
55
|
-
|
72
|
+
logger.debug "Appying context '#{name}' "
|
73
|
+
# here allow context to add additional Dockerfile entries, mounts and ports
|
74
|
+
ddConf = cctx.apply_context(ddConf)
|
75
|
+
end
|
56
76
|
|
57
|
-
|
77
|
+
ddConf.manual_activated_context.each do |cctx|
|
78
|
+
mctx = Dockdev::Context::ContextManager.instance.registered_context_by_name(cctx)
|
79
|
+
logger.debug "Executing manual activated context : #{mctx}"
|
80
|
+
ddConf = mctx.apply_context(ddConf) if not mctx.nil?
|
81
|
+
end
|
58
82
|
|
59
|
-
|
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?
|
83
|
+
if not img.has_image?
|
67
84
|
|
68
|
-
|
85
|
+
if wss.has_dockerfile?
|
69
86
|
|
70
|
-
|
71
|
-
port.merge!(prt) if not prt.empty?
|
87
|
+
if wss.has_multiple_dockerfiles?
|
72
88
|
|
73
|
-
|
74
|
-
|
89
|
+
selDockerFile = pmt.select("Please select one of the Dockerfile to proceed : ") do |m|
|
90
|
+
wss.dockerfiles.each do |df|
|
91
|
+
m.choice File.basename(df), df
|
92
|
+
end
|
93
|
+
end
|
75
94
|
|
76
|
-
|
77
|
-
|
95
|
+
else
|
96
|
+
selDockerFile = wss.dockerfile
|
97
|
+
end
|
78
98
|
|
79
|
-
|
99
|
+
# Delegated to config file to allow
|
100
|
+
# config file to embed addtional entries (if there is any) and proceed to build it.
|
101
|
+
# During the process it is very likey a temporary Dockerfile shall be created since
|
102
|
+
# docker cli works on file basis and this temporary file need to be managed after the process.
|
103
|
+
# Hence this makes more sense to let the config handle all those inside the operation
|
104
|
+
res = ddConf.build_image(img, selDockerFile, root: root)
|
105
|
+
raise Error, "Image failed to be built. Error was : #{res.err_stream}" if res.failed?
|
106
|
+
STDOUT.puts "\n Image '#{contName}' built successfully\n\n".green
|
107
|
+
#img.build(wss.dockerfile)
|
80
108
|
|
81
|
-
|
82
|
-
|
109
|
+
else
|
110
|
+
raise Error, "\n No image and no Dockerfile found to build the image. Operation aborted. \n\n".red
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# image already exist!
|
116
|
+
# Since reach here means container doesn't exist yet.
|
117
|
+
# Proceed to create container
|
118
|
+
|
119
|
+
param = { command: cmd, mounts: ddConf.mounts, ports: ddConf.ports }
|
120
|
+
if not_empty?(ddConf.network)
|
121
|
+
cmd_fact = Docker::Cli::CommandFactory.new
|
122
|
+
res = cmd_fact.create_network(ddConf.network).run
|
123
|
+
if res.success? and not res.is_out_stream_empty?
|
124
|
+
param[:network] = ddConf.network
|
125
|
+
else
|
126
|
+
err = res.err_stream
|
127
|
+
if err =~ /already exist/
|
128
|
+
param[:network] = ddConf.network
|
129
|
+
else
|
130
|
+
raise Error, "\n Failed to create network. Error was : #{res.err_stream}\n"
|
131
|
+
end
|
132
|
+
end
|
83
133
|
end
|
134
|
+
img.new_container(cont.name, param)
|
135
|
+
|
136
|
+
#if img.has_image?
|
137
|
+
#
|
138
|
+
# # has image but no container
|
139
|
+
# ctx.each do |cctx|
|
140
|
+
#
|
141
|
+
# ddConf = cctx.apply_context(ddConf)
|
142
|
+
|
143
|
+
# #cctx.process_mount(dir_inside_docker: ddConf.workdir).each do |host,docker|
|
144
|
+
# # logger.debug "Mount points by context '#{cctx}' : #{host} => #{docker}"
|
145
|
+
# # ddConf.add_mount(host, docker)
|
146
|
+
# #end
|
147
|
+
|
148
|
+
# #cctx.process_port.each do |host, docker|
|
149
|
+
# # logger.debug "Ports mapping by context '#{cctx}' : #{host} => #{docker}"
|
150
|
+
# # ddConf.add_port(host, docker)
|
151
|
+
# #end
|
152
|
+
|
153
|
+
# #mnts = cctx.process_mount(dir_inside_docker: ddConf.workdir)
|
154
|
+
# #logger.debug "Mount points by context : #{mnts}"
|
155
|
+
|
156
|
+
# #mount.merge!(mnts) if not mnts.empty?
|
157
|
+
|
158
|
+
# #prt = cctx.process_port
|
159
|
+
# #port.merge!(prt) if not prt.empty?
|
160
|
+
|
161
|
+
# #logger.debug "Ports by context #{cctx} : #{prt}"
|
162
|
+
# end
|
163
|
+
|
164
|
+
# #param = { command: cmd, mounts: mount }
|
165
|
+
# #param[:ports] = port if not port.empty?
|
166
|
+
# param = { command: cmd, mounts: ddConf.mounts, ports: ddConf.ports }
|
167
|
+
|
168
|
+
# img.new_container(cont.name, param)
|
169
|
+
|
170
|
+
#elsif wss.has_dockerfile?
|
171
|
+
|
172
|
+
# logger.debug "Dockerfile '#{wss.dockerfile}' found. Proceed building the image."
|
173
|
+
|
174
|
+
# # Delegated to config file to allow
|
175
|
+
# # config file to embed addtional entries (if there is any) and proceed to build it.
|
176
|
+
# # During the process it is very likey a temporary Dockerfile shall be created since
|
177
|
+
# # docker cli works on file basis and this temporary file need to be managed after the process.
|
178
|
+
# # Hence this makes more sense to let the config handle all those inside the operation
|
179
|
+
# ddConf.build_image(img, wss, root: root)
|
180
|
+
# #img.build(wss.dockerfile)
|
181
|
+
#
|
182
|
+
# ddConf.add_mount(root, File.join(ddConf.workdir,File.basename(root)))
|
183
|
+
# #mount = { root => File.join(ddConf.workdir,File.basename(root)) }
|
184
|
+
# #port = {}
|
185
|
+
# ctx.each do |cctx|
|
186
|
+
|
187
|
+
# ddConf = cctx.apply_context(ddConf)
|
188
|
+
|
189
|
+
# #cctx.process_mount(dir_inside_docker: ddConf.workdir).each do |host,docker|
|
190
|
+
# # logger.debug "Mount points by context '#{cctx}' : #{host} => #{docker}"
|
191
|
+
# # ddConf.add_mount(host, docker)
|
192
|
+
# #end
|
193
|
+
|
194
|
+
# #cctx.process_port.each do |host, docker|
|
195
|
+
# # logger.debug "Ports mapping by context '#{cctx}' : #{host} => #{docker}"
|
196
|
+
# # ddConf.add_port(host, docker)
|
197
|
+
# #end
|
198
|
+
|
199
|
+
# #mnt = cctx.process_mount(dir_inside_docker: ddConf.workdir)
|
200
|
+
# #mount.merge!(mnt) if not mnt.empty?
|
201
|
+
|
202
|
+
# #logger.debug "Mount points by context #{cctx} : #{mnt}"
|
203
|
+
|
204
|
+
# #prt = cctx.process_port
|
205
|
+
# #port.merge!(prt) if not prt.empty?
|
206
|
+
|
207
|
+
# #logger.debug "Ports by context #{cctx} : #{prt}"
|
208
|
+
# end
|
209
|
+
|
210
|
+
# #param = { command: cmd, mounts: mount }
|
211
|
+
# #param[:ports] = port if not port.empty?
|
212
|
+
# param = { command: cmd, mounts: ddConf.mounts, ports: ddConf.ports }
|
213
|
+
|
214
|
+
# img.new_container(cont.name, param)
|
215
|
+
|
216
|
+
#else
|
217
|
+
# raise Error, "\n No image and no Dockerfile found to build the image found. Operation aborted. \n\n".red
|
218
|
+
#end
|
84
219
|
end
|
85
220
|
end
|
86
221
|
|
@@ -99,6 +234,33 @@ module Dockdev
|
|
99
234
|
|
100
235
|
end
|
101
236
|
|
237
|
+
# detect if the additional configuration file exist
|
238
|
+
def self.load_config(root)
|
239
|
+
|
240
|
+
ddConf = DockdevConfig.new
|
241
|
+
DockdevConfig.load(root) do |ops, *args|
|
242
|
+
case ops
|
243
|
+
when :found_more
|
244
|
+
found = args.first
|
245
|
+
pmt = TTY::Prompt.new
|
246
|
+
selConf = pmt.select("There are more config files found. Please select one of the files below :") do |m|
|
247
|
+
found.each do |f|
|
248
|
+
m.choice Pathname.new(f).relative_path_from(root),f
|
249
|
+
end
|
250
|
+
end
|
251
|
+
ddConf = DockdevConfig.new(selConf)
|
252
|
+
when :found
|
253
|
+
ddConf = DockdevConfig.new(args.first)
|
254
|
+
else
|
255
|
+
logger.debug "Load config got ops : #{ops}"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
logger.debug "Loaded config : #{ddConf}"
|
260
|
+
ddConf
|
261
|
+
|
262
|
+
end
|
263
|
+
|
102
264
|
def self.logger(tag = nil, &block)
|
103
265
|
if @_logger.nil?
|
104
266
|
@_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.
|
4
|
+
version: 0.4.1
|
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-
|
11
|
+
date: 2024-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: teLogger
|
@@ -128,6 +128,7 @@ 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
133
|
- lib/dockdev/user_info.rb
|
133
134
|
- lib/dockdev/version.rb
|
@@ -136,7 +137,7 @@ files:
|
|
136
137
|
homepage: ''
|
137
138
|
licenses: []
|
138
139
|
metadata: {}
|
139
|
-
post_install_message:
|
140
|
+
post_install_message:
|
140
141
|
rdoc_options: []
|
141
142
|
require_paths:
|
142
143
|
- lib
|
@@ -151,8 +152,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
152
|
- !ruby/object:Gem::Version
|
152
153
|
version: '0'
|
153
154
|
requirements: []
|
154
|
-
rubygems_version: 3.5.
|
155
|
-
signing_key:
|
155
|
+
rubygems_version: 3.5.1
|
156
|
+
signing_key:
|
156
157
|
specification_version: 4
|
157
158
|
summary: ''
|
158
159
|
test_files: []
|