docker-cli 0.3.1 → 0.5.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.
@@ -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(" ")}"
@@ -106,6 +109,10 @@ module Docker
106
109
  Command.new(cmd)
107
110
  end
108
111
 
112
+ #
113
+ # Create container from image directly
114
+ # e.g. > docker run -it <image> "/bin/bash"
115
+ #
109
116
  def create_container_from_image(image, opts = { })
110
117
  opts = {} if opts.nil?
111
118
  cmd = []
@@ -113,13 +120,21 @@ module Docker
113
120
  cmd << "run"
114
121
  cmd << "-i" if opts[:interactive] == true
115
122
  cmd << "-t" if opts[:tty] == true
123
+ cmd << "-d" if opts[:detached] == true
124
+ cmd << "--rm" if opts[:del] == true
116
125
  if not (opts[:container_name].nil? or opts[:container_name].empty?)
117
- cmd << "--name #{opts[:container_name]}"
126
+ cmd << "--name \"#{opts[:container_name]}\""
118
127
  end
119
128
 
120
129
  cmd << process_mount(opts)
121
130
  cmd << process_port(opts)
122
131
 
132
+ if opts[:match_user] == true
133
+ ui = UserInfo.user_info
134
+ gi = UserInfo.group_info
135
+ cmd << "-u #{ui[:uid]}:#{gi[:gid]}"
136
+ end
137
+
123
138
  cmd << image
124
139
 
125
140
  if not_empty?(opts[:command])
@@ -128,7 +143,7 @@ module Docker
128
143
 
129
144
  interactive = false
130
145
  interactive = true if opts[:interactive] or opts[:tty]
131
-
146
+
132
147
  logger.debug "Run Container from Image : #{cmd.join(" ")}"
133
148
  Command.new(cmd, (interactive ? true : false))
134
149
  end # run_container_from_image
@@ -220,17 +235,17 @@ module Docker
220
235
 
221
236
 
222
237
  private
223
- # expecting :mounts => [{ "/dir/local" => "/dir/inside/docker" }]
238
+ # expecting :mounts => { "/dir/local" => "/dir/inside/docker" }
224
239
  def process_mount(opts)
225
- if not (opts[:mount].nil? or opts[:mount].empty?)
226
- m = opts[:mount]
227
- m = [m] if not m.is_a?(Array)
228
- res = []
229
- m.each do |mm|
230
- host = mm.keys.first
231
- res << "-v #{host}:#{mm[host]}"
240
+ if not_empty?(opts[:mounts]) #not (opts[:mounts].nil? or opts[:mounts].empty?)
241
+ m = opts[:mounts]
242
+ if m.is_a?(Hash)
243
+ res = []
244
+ m.each do |local, docker|
245
+ res << "-v #{local}:#{docker}"
246
+ end
247
+ res.join(" ")
232
248
  end
233
- res.join(" ")
234
249
  else
235
250
  ""
236
251
  end
@@ -238,13 +253,20 @@ module Docker
238
253
  end # process_mount
239
254
 
240
255
  def process_port(opts)
241
- if not (opts[:ports].nil? or opts[:ports].empty?)
256
+ if not_empty?(opts[:ports]) #not (opts[:ports].nil? or opts[:ports].empty?)
242
257
  po = opts[:ports]
243
- po = [po] if not po.is_a?(Array)
244
258
  res = []
245
- po.each do |host,docker|
246
- res << "-p #{host}:#{docker}"
259
+ if po.is_a?(Hash)
260
+ po.each do |host, docker|
261
+ res << "-p #{host}:#{docker}"
262
+ end
247
263
  end
264
+ #po = [po] if not po.is_a?(Array)
265
+ #po.each do |e|
266
+ # # 1st is port on host
267
+ # # 2nd is port inside container
268
+ # res << "-p #{e.keys.first}:#{e.values.first}"
269
+ #end
248
270
  res.join(" ")
249
271
  else
250
272
  ""
@@ -253,15 +275,17 @@ module Docker
253
275
  end
254
276
 
255
277
  def logger
256
- if @logger.nil?
257
- @logger = TeLogger::Tlogger.new
258
- @logger.tag = :docker_cmd
259
- end
260
- @logger
278
+ Cli.logger(:cmdFact)
261
279
  end
262
280
 
263
- def parse_container_list(out, &block)
264
-
281
+ def build_add_user_script
282
+ path = File.join(File.dirname(__FILE__),"..","..","..","scripts","create_user.sh.erb")
283
+ if File.exist?(path)
284
+ ui = UserInfo.user_info
285
+ gi = UserInfo.group_info
286
+
287
+ 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] })
288
+ end
265
289
  end
266
290
 
267
291
  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
@@ -0,0 +1,205 @@
1
+
2
+ require_relative 'command_factory'
3
+
4
+ module Docker
5
+ module Cli
6
+ class DockerImage
7
+ include TR::CondUtils
8
+
9
+ def self.set_master_format(format)
10
+ @_doc_mformat = format
11
+ end
12
+ def self.master_format
13
+ if @_doc_mformat.nil?
14
+ @_doc_mformat = "{{.Repository}}#{self.separator}{{.ID}}#{self.separator}{{.Tag}}"
15
+ end
16
+ @_doc_mformat.freeze
17
+ end
18
+
19
+ def self.set_inspect_format(format)
20
+ @_dec_iformat = format
21
+ end
22
+ def self.inspect_format
23
+ if @_doc_iformat.nil?
24
+ @_doc_iformat = "{{.Created}}#{self.separator}{{.Size}}"
25
+ end
26
+ @_doc_iformat.freeze
27
+ end
28
+
29
+ def self.set_separator(sep)
30
+ @_doc_sep = sep
31
+ end
32
+ def self.separator
33
+ if @_doc_sep.nil?
34
+ @_doc_sep = ";"
35
+ end
36
+ @_doc_sep.freeze
37
+ end
38
+
39
+ def self.images
40
+ logger.warn "loading images from system is an expensive operation. Please implement some cache at aplication level to increase its efficiency"
41
+ cmd = []
42
+ cmd << Cli.docker_exe
43
+ cmd << "images"
44
+ cmd << "--format"
45
+ cmd << "\"#{self.master_format}\""
46
+
47
+ rres = []
48
+ res = Command.new(cmd).run
49
+ if res.successful?
50
+ res.each_line do |l|
51
+ sp = l.strip.split(self.separator)
52
+ rres << { name: sp[0], id: sp[1], tag: sp[2] }
53
+ end
54
+ else
55
+ logger.warn "Command '#{cmd.join(" ")}' failed.\n Error was : #{res.err_stream}"
56
+ end
57
+
58
+ fres = []
59
+ rres.each do |r|
60
+ ccmd = []
61
+ ccmd << cmd[0]
62
+ ccmd << "inspect"
63
+ ccmd << "-f"
64
+ ccmd << "'{{.ID}}#{self.separator}{{.Created}}#{self.separator}{{.Size}}'"
65
+ ccmd << r[:name]
66
+
67
+ rres = Command.new(ccmd).run
68
+ if rres.successful?
69
+ rres.each_line do |l|
70
+ sp = l.strip.split(self.separator)
71
+ rid = sp[0]
72
+ r[:image_id] = rid.split(":")[1]
73
+ r[:created] = sp[1]
74
+ r[:size] = sp[2]
75
+ r[:runtime] = true
76
+ fres << DockerImage.new(r[:name], r)
77
+ end
78
+ else
79
+ logger.warn "Command '#{ccmd.join(" ")}' failed.\n Error was : #{rres.err_stream}"
80
+ end
81
+ end
82
+
83
+ fres
84
+ end
85
+
86
+ def self.image(image)
87
+ cmd = []
88
+ cmd << Cli.docker_exe
89
+ cmd << "images"
90
+ cmd << "--format"
91
+ cmd << "\"#{self.master_format}\""
92
+ # image and tag is in single unit
93
+ # e.g.:
94
+ # jruby:9.2-jdk11
95
+ # jruby:9
96
+ # jruby:9.3.0.0-jdk11
97
+ cmd << image
98
+
99
+ rres = []
100
+ res = Command.new(cmd).run
101
+ if res.successful?
102
+ # single image could have many lines because they have
103
+ # different tags!
104
+ res.each_line do |l|
105
+ sp = l.strip.split(self.separator)
106
+ rres << { name: sp[0], id: sp[1], tag: sp[2] }
107
+ end
108
+ else
109
+ logger.warn "Command '#{cmd.join(" ")}' failed.\n Error was : #{res.err_stream}"
110
+ end
111
+
112
+ fres = []
113
+ rres.each do |r|
114
+ ccmd = []
115
+ ccmd << cmd[0]
116
+ ccmd << "inspect"
117
+ ccmd << "-f"
118
+ ccmd << "'{{.ID}}#{self.separator}{{.Created}}#{self.separator}{{.Size}}'"
119
+ ccmd << r[:name]
120
+
121
+ rres = Command.new(ccmd).run
122
+ if rres.successful?
123
+ rres.each_line do |l|
124
+ sp = l.strip.split(self.separator)
125
+ rid = sp[0]
126
+ r[:image_id] = rid.split(":")[1]
127
+ r[:created] = sp[1]
128
+ r[:size] = sp[2]
129
+ r[:runtime] = true
130
+ fres << DockerImage.new(r[:name], r)
131
+ end
132
+ else
133
+ logger.warn "Command '#{ccmd.join(" ")}' failed.\n Error was : #{rres.err_stream}"
134
+ end
135
+ end
136
+
137
+ fres
138
+ end
139
+
140
+ def self.build(imageName, dockerfile, context = ".")
141
+ command.build_image(imageName, dockerfile: dockerfile, context_root: context).run
142
+ end
143
+
144
+
145
+ attr_reader :name, :tag, :size, :created, :image_id, :sid
146
+
147
+ def initialize(name, opts = {})
148
+ @name = name
149
+ if not_empty?(opts)
150
+ @sid = opts[:sid]
151
+ @image_id = opts[:image_id]
152
+ @tag = opts[:tag]
153
+ @size = opts[:size]
154
+ @created = opts[:created]
155
+ @runtime = opts[:runtime]
156
+ end
157
+
158
+ @runtime = false if is_empty?(@runtime) or not_bool?(@runtime)
159
+
160
+ end
161
+
162
+ def is_runtime_image?
163
+ @runtime
164
+ end
165
+
166
+ def has_containers?
167
+ DockerRunLog.instance.image_has_containers?(@name)
168
+ end
169
+
170
+ def containers
171
+ if @_cont.nil?
172
+ # here assuming every run from system will be logged
173
+ # but what about the one not using the system?
174
+ @_cont = DockerRunLog.instance.image_containers(@name).collect { |e| DockerContainer.new(e[:container], e) }
175
+ end
176
+ @_cont
177
+ end
178
+
179
+ def delete!
180
+ if not_empty?(@tag)
181
+ command.delete_image(@name, @tag, opts)
182
+ else
183
+ raise IndefiniteOption, "Delete image cannot proceed because there might be more than single instance of the image. Please provide a tag for definite selection for deletion."
184
+ end
185
+ end
186
+
187
+ private
188
+ def self.command
189
+ if @_cmd.nil?
190
+ @_cmd = CommandFactory.new
191
+ end
192
+ @_cmd
193
+ end
194
+
195
+ def command
196
+ self.class.command
197
+ end
198
+
199
+ def self.logger
200
+ Cli.logger(:docker_image)
201
+ end
202
+
203
+ end
204
+ end
205
+ end