docker-cli 0.3.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.release_history.yml +2 -0
- data/.rubocop.yml +2 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +24 -27
- data/Rakefile +2 -1
- data/docker-cli.gemspec +5 -2
- data/exe/_gemdocker +225 -0
- data/exe/dc-update +24 -0
- data/exe/gemdocker +52 -0
- data/exe/gemdocker_test +40 -0
- data/lib/docker/cli/command.rb +37 -31
- data/lib/docker/cli/command_factory.rb +62 -27
- data/lib/docker/cli/command_result.rb +16 -0
- data/lib/docker/cli/docker_composer.rb +113 -0
- data/lib/docker/cli/docker_container.rb +216 -0
- data/lib/docker/cli/docker_image.rb +205 -0
- data/lib/docker/cli/docker_run_log.rb +169 -0
- data/lib/docker/cli/dockerfile.rb +62 -0
- data/lib/docker/cli/dockerfile_template/dup_gem_bundler_env.rb +73 -0
- data/lib/docker/cli/dockerfile_template/match_user.rb +38 -0
- data/lib/docker/cli/dockerfile_template.rb +69 -0
- data/lib/docker/cli/gem/bundler_helper.rb +25 -0
- data/lib/docker/cli/image_helper.rb +43 -0
- data/lib/docker/cli/operations/args_parser.rb +53 -0
- data/lib/docker/cli/operations/run.rb +203 -0
- data/lib/docker/cli/operations/run_del.rb +67 -0
- data/lib/docker/cli/operations/run_keep.rb +118 -0
- data/lib/docker/cli/user_info.rb +39 -0
- data/lib/docker/cli/version.rb +1 -1
- data/lib/docker/cli.rb +64 -0
- data/scripts/create_user.sh.erb +10 -0
- metadata +51 -12
@@ -1,12 +1,15 @@
|
|
1
1
|
|
2
|
+
require 'erb'
|
3
|
+
|
2
4
|
require_relative 'command'
|
5
|
+
require_relative 'user_info'
|
3
6
|
|
4
7
|
module Docker
|
5
8
|
module Cli
|
6
9
|
class CommandFactory
|
7
10
|
include TR::CondUtils
|
8
11
|
|
9
|
-
def build_image(name = "", opts = { })
|
12
|
+
def build_image(name = "", opts = { }, &block)
|
10
13
|
|
11
14
|
opts = { } if opts.nil?
|
12
15
|
cmd = []
|
@@ -37,9 +40,9 @@ module Docker
|
|
37
40
|
cmd << "images"
|
38
41
|
cmd << "-q"
|
39
42
|
if not_empty?(tag)
|
40
|
-
cmd << "#{name}:#{tag}"
|
43
|
+
cmd << "\"#{name}:#{tag}\""
|
41
44
|
else
|
42
|
-
cmd << name
|
45
|
+
cmd << "\"#{name}\""
|
43
46
|
end
|
44
47
|
|
45
48
|
logger.debug "Find image: #{cmd.join(" ")}"
|
@@ -79,7 +82,12 @@ module Docker
|
|
79
82
|
cmd << "ps"
|
80
83
|
cmd << "-q"
|
81
84
|
cmd << "-f"
|
82
|
-
|
85
|
+
|
86
|
+
if opts[:exact_name] == true
|
87
|
+
cmd << "name=\"^/#{name}$\""
|
88
|
+
else
|
89
|
+
cmd << "name=\"#{name}\""
|
90
|
+
end
|
83
91
|
|
84
92
|
logger.debug "Find container: #{cmd.join(" ")}"
|
85
93
|
|
@@ -100,12 +108,22 @@ module Docker
|
|
100
108
|
# From little testing seems the command by default already support regex formatting
|
101
109
|
# So can use the regex marker to get exact match
|
102
110
|
# e.g. if want exact match, pass in ^#{name}\z
|
103
|
-
cmd << "name=\"#{name}\""
|
111
|
+
#cmd << "name=\"#{name}\""
|
112
|
+
|
113
|
+
if opts[:exact_name] == true
|
114
|
+
cmd << "name=\"^/#{name}$\""
|
115
|
+
else
|
116
|
+
cmd << "name=\"#{name}\""
|
117
|
+
end
|
104
118
|
|
105
119
|
logger.debug "Find from all container: #{cmd.join(" ")}"
|
106
120
|
Command.new(cmd)
|
107
121
|
end
|
108
122
|
|
123
|
+
#
|
124
|
+
# Create container from image directly
|
125
|
+
# e.g. > docker run -it <image> "/bin/bash"
|
126
|
+
#
|
109
127
|
def create_container_from_image(image, opts = { })
|
110
128
|
opts = {} if opts.nil?
|
111
129
|
cmd = []
|
@@ -113,13 +131,21 @@ module Docker
|
|
113
131
|
cmd << "run"
|
114
132
|
cmd << "-i" if opts[:interactive] == true
|
115
133
|
cmd << "-t" if opts[:tty] == true
|
134
|
+
cmd << "-d" if opts[:detached] == true
|
135
|
+
cmd << "--rm" if opts[:del] == true
|
116
136
|
if not (opts[:container_name].nil? or opts[:container_name].empty?)
|
117
|
-
cmd << "--name #{opts[:container_name]}"
|
137
|
+
cmd << "--name \"#{opts[:container_name]}\""
|
118
138
|
end
|
119
139
|
|
120
140
|
cmd << process_mount(opts)
|
121
141
|
cmd << process_port(opts)
|
122
142
|
|
143
|
+
if opts[:match_user] == true
|
144
|
+
ui = UserInfo.user_info
|
145
|
+
gi = UserInfo.group_info
|
146
|
+
cmd << "-u #{ui[:uid]}:#{gi[:gid]}"
|
147
|
+
end
|
148
|
+
|
123
149
|
cmd << image
|
124
150
|
|
125
151
|
if not_empty?(opts[:command])
|
@@ -128,7 +154,7 @@ module Docker
|
|
128
154
|
|
129
155
|
interactive = false
|
130
156
|
interactive = true if opts[:interactive] or opts[:tty]
|
131
|
-
|
157
|
+
|
132
158
|
logger.debug "Run Container from Image : #{cmd.join(" ")}"
|
133
159
|
Command.new(cmd, (interactive ? true : false))
|
134
160
|
end # run_container_from_image
|
@@ -220,17 +246,17 @@ module Docker
|
|
220
246
|
|
221
247
|
|
222
248
|
private
|
223
|
-
# expecting :mounts =>
|
249
|
+
# expecting :mounts => { "/dir/local" => "/dir/inside/docker" }
|
224
250
|
def process_mount(opts)
|
225
|
-
if not (opts[:
|
226
|
-
m = opts[:
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
251
|
+
if not_empty?(opts[:mounts]) #not (opts[:mounts].nil? or opts[:mounts].empty?)
|
252
|
+
m = opts[:mounts]
|
253
|
+
if m.is_a?(Hash)
|
254
|
+
res = []
|
255
|
+
m.each do |local, docker|
|
256
|
+
res << "-v #{local}:#{docker}"
|
257
|
+
end
|
258
|
+
res.join(" ")
|
232
259
|
end
|
233
|
-
res.join(" ")
|
234
260
|
else
|
235
261
|
""
|
236
262
|
end
|
@@ -238,13 +264,20 @@ module Docker
|
|
238
264
|
end # process_mount
|
239
265
|
|
240
266
|
def process_port(opts)
|
241
|
-
if not (opts[:ports].nil? or opts[:ports].empty?)
|
267
|
+
if not_empty?(opts[:ports]) #not (opts[:ports].nil? or opts[:ports].empty?)
|
242
268
|
po = opts[:ports]
|
243
|
-
po = [po] if not po.is_a?(Array)
|
244
269
|
res = []
|
245
|
-
po.
|
246
|
-
|
270
|
+
if po.is_a?(Hash)
|
271
|
+
po.each do |host, docker|
|
272
|
+
res << "-p #{host}:#{docker}"
|
273
|
+
end
|
247
274
|
end
|
275
|
+
#po = [po] if not po.is_a?(Array)
|
276
|
+
#po.each do |e|
|
277
|
+
# # 1st is port on host
|
278
|
+
# # 2nd is port inside container
|
279
|
+
# res << "-p #{e.keys.first}:#{e.values.first}"
|
280
|
+
#end
|
248
281
|
res.join(" ")
|
249
282
|
else
|
250
283
|
""
|
@@ -253,15 +286,17 @@ module Docker
|
|
253
286
|
end
|
254
287
|
|
255
288
|
def logger
|
256
|
-
|
257
|
-
@logger = TeLogger::Tlogger.new
|
258
|
-
@logger.tag = :docker_cmd
|
259
|
-
end
|
260
|
-
@logger
|
289
|
+
Cli.logger(:cmdFact)
|
261
290
|
end
|
262
291
|
|
263
|
-
def
|
264
|
-
|
292
|
+
def build_add_user_script
|
293
|
+
path = File.join(File.dirname(__FILE__),"..","..","..","scripts","create_user.sh.erb")
|
294
|
+
if File.exist?(path)
|
295
|
+
ui = UserInfo.user_info
|
296
|
+
gi = UserInfo.group_info
|
297
|
+
|
298
|
+
ERB.new(File.read(path)).result_with_hash({ user_group_id: gi[:gid], user_group_name: gi[:group_name], user_id: ui[:uid], user_login: ui[:login] })
|
299
|
+
end
|
265
300
|
end
|
266
301
|
|
267
302
|
end
|
@@ -19,6 +19,10 @@ module Docker
|
|
19
19
|
is_empty?(@out)
|
20
20
|
end
|
21
21
|
|
22
|
+
def is_err_stream_empty?
|
23
|
+
is_empty?(@err)
|
24
|
+
end
|
25
|
+
|
22
26
|
def out_stream
|
23
27
|
@out.join("\n")
|
24
28
|
end
|
@@ -35,6 +39,18 @@ module Docker
|
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
42
|
+
def success?
|
43
|
+
not failed?
|
44
|
+
end
|
45
|
+
alias_method :successful?, :success?
|
46
|
+
|
47
|
+
def each_line(&block)
|
48
|
+
out_stream.each_line(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def lines
|
52
|
+
out_stream.lines
|
53
|
+
end
|
38
54
|
end
|
39
55
|
|
40
56
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
require_relative 'user_info'
|
5
|
+
|
6
|
+
module Docker
|
7
|
+
module Cli
|
8
|
+
class DockerComposer
|
9
|
+
include TR::CondUtils
|
10
|
+
include TR::ArgUtils
|
11
|
+
|
12
|
+
arg_spec do
|
13
|
+
|
14
|
+
callback :pre_processing do |a|
|
15
|
+
parse_input(a)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_input(argv, &block)
|
21
|
+
@dcPath = argv.first
|
22
|
+
@dcPath = "./docker-compose.yml" if is_empty?(@dcPath)
|
23
|
+
@dcPath = "./docker-compose.yaml" if not File.exist?(@dcPath)
|
24
|
+
raise RuntimeException, "docker-compose.[yml,yaml] not found. Please provide the docker-compose.yml file to load" if not File.exist?(@dcPath)
|
25
|
+
|
26
|
+
@outPath = argv[1]
|
27
|
+
@outPath = "#{@dcPath}-gen.yml" if is_empty?(@outPath)
|
28
|
+
|
29
|
+
process_dc
|
30
|
+
|
31
|
+
[true, []]
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_dc
|
35
|
+
if File.exist?(@dcPath)
|
36
|
+
|
37
|
+
cont = YAML.safe_load(File.read(@dcPath))
|
38
|
+
|
39
|
+
if not_empty?(cont["services"])
|
40
|
+
|
41
|
+
cont["services"].each do |servName, conf|
|
42
|
+
|
43
|
+
# if user key is empty, match with current uid and gid
|
44
|
+
if conf.keys.include?("user") and is_empty?(conf["user"])
|
45
|
+
conf["user"] = "#{user_info[:uid]}:#{user_group_info[:gid]}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# add to volumes if there is development gems found
|
49
|
+
if not_empty?(conf["volumes"])
|
50
|
+
vol = conf["volumes"]
|
51
|
+
|
52
|
+
if vol.include?("devgems")
|
53
|
+
|
54
|
+
vol.delete("devgems")
|
55
|
+
logger.debug "Volume after delete : #{vol.inspect}"
|
56
|
+
|
57
|
+
devGems = Cli.find_dev_gems
|
58
|
+
logger.debug " Found #{devGems.length} devgems"
|
59
|
+
if devGems.length > 0
|
60
|
+
if @parse_argv_block
|
61
|
+
@mount_root = @parse_argv_block.call(:prompt_docker_mount_root)
|
62
|
+
else
|
63
|
+
raise RuntimeException, "Please provide a block to prompt for missing parameters"
|
64
|
+
end
|
65
|
+
|
66
|
+
devGems.each do |name,path|
|
67
|
+
vol << "#{path}:#{File.join(@mount_root, name)}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
reCont = cont.to_yaml
|
79
|
+
reCont = reCont.gsub("---","")
|
80
|
+
File.open(@outPath, "w") do |f|
|
81
|
+
f.write reCont
|
82
|
+
end
|
83
|
+
|
84
|
+
else
|
85
|
+
raise RuntimeException, "Given docker compose file '#{@dcPath}' does not exist"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def user_info
|
91
|
+
if @_user_info.nil?
|
92
|
+
@_user_info = UserInfo.user_info
|
93
|
+
end
|
94
|
+
@_user_info
|
95
|
+
end
|
96
|
+
|
97
|
+
def user_group_info
|
98
|
+
if @_userg_info.nil?
|
99
|
+
@_userg_info = UserInfo.group_info
|
100
|
+
end
|
101
|
+
@_userg_info
|
102
|
+
end
|
103
|
+
|
104
|
+
def logger
|
105
|
+
if @_logger.nil?
|
106
|
+
@_logger = Cli.logger(:docomp)
|
107
|
+
end
|
108
|
+
@_logger
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
|
2
|
+
require_relative 'command_factory'
|
3
|
+
|
4
|
+
module Docker
|
5
|
+
module Cli
|
6
|
+
|
7
|
+
# only use during creation of new container
|
8
|
+
class ContainerProfile
|
9
|
+
include TR::CondUtils
|
10
|
+
|
11
|
+
attr_accessor :run_command, :image_name
|
12
|
+
def initialize
|
13
|
+
@interactive = true
|
14
|
+
@run_detached = false
|
15
|
+
@remove_after_run = false
|
16
|
+
@match_user = false
|
17
|
+
@run_command = "/bin/bash"
|
18
|
+
@mounts = {}
|
19
|
+
@ports = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def mount_points
|
23
|
+
@mounts.freeze
|
24
|
+
end
|
25
|
+
def add_mount_point(host, inside_docker)
|
26
|
+
if not_empty?(host) and not_empty?(inside_docker)
|
27
|
+
@mounts[host] = inside_docker
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def ports
|
32
|
+
@ports.freeze
|
33
|
+
end
|
34
|
+
def add_port_mapping(host, docker)
|
35
|
+
if not_empty?(host) and not_empty?(docker)
|
36
|
+
@ports[host] = docker
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_interactive?
|
41
|
+
@interactive
|
42
|
+
end
|
43
|
+
def interactive=(val)
|
44
|
+
@interactive = val
|
45
|
+
end
|
46
|
+
|
47
|
+
def is_run_detached?
|
48
|
+
@run_detached
|
49
|
+
end
|
50
|
+
def run_detached=(val)
|
51
|
+
@run_detached = val
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove_after_run?
|
55
|
+
@remove_after_run
|
56
|
+
end
|
57
|
+
def remove_after_run=(val)
|
58
|
+
@remove_after_run = val
|
59
|
+
end
|
60
|
+
|
61
|
+
def is_match_user?
|
62
|
+
@match_user
|
63
|
+
end
|
64
|
+
def match_user=(val)
|
65
|
+
@match_user = val
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_hash
|
69
|
+
# this returns a hash that match expectation of input in CommandFactory.create_container_from_image
|
70
|
+
{ interactive: @interactive, tty: @interactive, detached: @run_detached, del: @remove_after_run, mounts: @mounts, ports: @ports, match_user: @match_user, command: @run_command }
|
71
|
+
end
|
72
|
+
|
73
|
+
end # class NewContainerProfile
|
74
|
+
|
75
|
+
class DockerContainer
|
76
|
+
include TR::CondUtils
|
77
|
+
|
78
|
+
#def self.is_exists?(name)
|
79
|
+
# res = command.find_from_all_container(name, opts).run
|
80
|
+
# raise CommandFailed, "Command to check if container exist failed. Error was : #{res.err_stream}" if not res.successful?
|
81
|
+
# not res.is_out_stream_empty?
|
82
|
+
#end
|
83
|
+
|
84
|
+
#def self.create(image, opts = {}, &block)
|
85
|
+
# command.create_container_from_image(image, opts).run
|
86
|
+
#end
|
87
|
+
|
88
|
+
#def self.prep_container(image, opts = {})
|
89
|
+
# # render the create_user script
|
90
|
+
# res = build_add_user_script
|
91
|
+
# # system always mount current dir inside docker
|
92
|
+
# dest = File.join(opts[:mount_local],"create_user.sh")
|
93
|
+
# File.open(dest,"w") do |f|
|
94
|
+
# f.write res
|
95
|
+
# end
|
96
|
+
# `chmod +x #{dest}`
|
97
|
+
|
98
|
+
# # create non interactive session to create the user & group first
|
99
|
+
# opts[:command] = "#{File.join(opts[:mount_docker],"create_user.sh")}"
|
100
|
+
# opts[:detached] = true
|
101
|
+
# command.create_container_from_image(image, opts).run
|
102
|
+
#end
|
103
|
+
|
104
|
+
def self.containers_of_image_from_history(img, opts = { })
|
105
|
+
cont = DockerRunLog.instance.containers(img)
|
106
|
+
res = {}
|
107
|
+
if cont.length == 1
|
108
|
+
c = cont.first
|
109
|
+
res[c[:container]] = c
|
110
|
+
else
|
111
|
+
cont.each do |c|
|
112
|
+
res[c[:container]] = "#{c[:container]} [Last Access : #{Time.at(c[:last_run])}] - Config : #{c[:argv]}"
|
113
|
+
end
|
114
|
+
res
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#def self.containers(image = nil)
|
119
|
+
#
|
120
|
+
#end
|
121
|
+
|
122
|
+
#def self.container(name)
|
123
|
+
#
|
124
|
+
#end
|
125
|
+
|
126
|
+
attr_reader :name
|
127
|
+
|
128
|
+
def initialize(name, history = nil)
|
129
|
+
@name = name
|
130
|
+
@history = history
|
131
|
+
end
|
132
|
+
|
133
|
+
def is_exist?
|
134
|
+
res = command.find_from_all_container(@name).run
|
135
|
+
raise CommandFailed, "Command to check if container exist failed. Error was : #{res.err_stream}" if not res.successful?
|
136
|
+
not res.is_out_stream_empty?
|
137
|
+
end
|
138
|
+
|
139
|
+
def create(container_profile)
|
140
|
+
raise CommandFailed, " Image name is not given to create the container '#{@name}'" if is_empty?(container_profile.image_name)
|
141
|
+
opts = container_profile.to_hash
|
142
|
+
opts[:container_name] = @name
|
143
|
+
command.create_container_from_image(container_profile.image_name, opts).run
|
144
|
+
end
|
145
|
+
|
146
|
+
def name_for_display
|
147
|
+
if @history.nil?
|
148
|
+
@name
|
149
|
+
else
|
150
|
+
"#{@history[:container]} [Last Access : #{Time.at(@history[:last_run])}] - Config : #{@history[:argv]}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def run_before?
|
155
|
+
not @history.nil?
|
156
|
+
end
|
157
|
+
|
158
|
+
def history
|
159
|
+
if not @history.nil?
|
160
|
+
@history.freeze
|
161
|
+
else
|
162
|
+
@history = {}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def is_running?
|
167
|
+
res = command.find_running_container(@name).run
|
168
|
+
raise CommandFailed, "Command to find running container failed. Error was : #{res.err_stream}" if not res.successful?
|
169
|
+
not res.is_out_stream_empty?
|
170
|
+
end
|
171
|
+
|
172
|
+
def start(&block)
|
173
|
+
command.start_container(@name).run
|
174
|
+
end
|
175
|
+
|
176
|
+
def stop(&block)
|
177
|
+
command.stop_container(@name).run
|
178
|
+
end
|
179
|
+
|
180
|
+
def attach(&block)
|
181
|
+
command.attach_container(@name).run
|
182
|
+
end
|
183
|
+
|
184
|
+
def delete!(&block)
|
185
|
+
command.delete_container(@name).run
|
186
|
+
end
|
187
|
+
|
188
|
+
def run(cmd, opts = {}, &block)
|
189
|
+
command.run_command_in_running_container(@name, cmd, opts).run
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
def self.command
|
194
|
+
if @_cmd.nil?
|
195
|
+
@_cmd = CommandFactory.new
|
196
|
+
end
|
197
|
+
@_cmd
|
198
|
+
end
|
199
|
+
|
200
|
+
def command
|
201
|
+
self.class.command
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.build_add_user_script
|
205
|
+
path = File.join(File.dirname(__FILE__),"..","..","..","scripts","create_user.sh.erb")
|
206
|
+
if File.exist?(path)
|
207
|
+
ui = UserInfo.user_info
|
208
|
+
gi = UserInfo.group_info
|
209
|
+
|
210
|
+
ERB.new(File.read(path)).result_with_hash({ user_group_id: gi[:gid], user_group_name: gi[:group_name], user_id: ui[:uid], user_login: ui[:login] })
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|