jarl 0.7.0 → 0.8.1

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
  SHA1:
3
- metadata.gz: 5236af50f9ae03530ad885c513aa7f2cab5ec001
4
- data.tar.gz: c054de648fe8d3d61af434f4c7eae774ed9513b9
3
+ metadata.gz: 83b74b296a0d7b717781e0e95afc0229fccf6806
4
+ data.tar.gz: ad1000a7b31104b2f4c89850acec71e59cd52a12
5
5
  SHA512:
6
- metadata.gz: 96e8031a848011187b457d2e2eb0399df81ae93395f418014c243937073f380b7eeb497f7e94a80fc9426fbfb724e7607ef4a34f7dfe288c50dbf12fcb6a41bf
7
- data.tar.gz: 1a125e6dab7a71f8300e63f37ac496421949e0f97dab6df46c381680cf3c9fcb086c359addff5cd8d89a80d7366d00ee7b7410ecaf62304d13eda999885cfbbf
6
+ metadata.gz: 40284867d9621630fa84494ede01af179a92474c61ac1fe723358a55c0115848a056cbba18bc41fc5aa1a3ceb308f48e3219add98ba5695798fe9fc9e80816b1
7
+ data.tar.gz: f4d2e5ae060dbcfff10601956dac755c706972d3099009017aa78acd762ffb6d28493225a32812d0d05480dc7cb6c240198a526b1a2d29d410dd466289d33b0b
@@ -1,7 +1,7 @@
1
1
  module Jarl
2
2
  class Application
3
3
  attr_reader :group, :name, :hostname, :base_path
4
- attr_reader :image, :volumes, :ports, :environment, :entrypoint, :command
4
+ attr_reader :image, :scale, :volumes, :ports, :environment, :entrypoint, :command
5
5
  attr_reader :serial
6
6
 
7
7
  # Create Application object from application definition:
@@ -22,6 +22,10 @@ module Jarl
22
22
  unless @image.is_a?(String)
23
23
  fail "Application '#{self}' has invalid 'image' definition, String is expected"
24
24
  end
25
+ @scale = params['scale'] || 1
26
+ unless @scale.is_a?(Integer) && @scale > 0
27
+ fail "Application '#{self}' has invalid 'scale' definition, Integer > 0 is expected"
28
+ end
25
29
  @volumes = params['volumes'] || []
26
30
  unless @volumes.is_a?(Array)
27
31
  fail "Application '#{self}' has invalid 'volumes' definition, Array is expected"
@@ -59,18 +63,42 @@ module Jarl
59
63
  end
60
64
 
61
65
  def running?
62
- instances.size > 0
66
+ instances.any?(&:running?)
63
67
  end
64
68
 
65
69
  def instances
66
- Docker.containers_running.select { |c| c.name =~ /^#{full_name}(\.\d+)?$/ }.map do |c|
67
- Instance.new(self, c)
70
+ running_instances + stopped_instances
71
+ end
72
+
73
+ def running_containers
74
+ @running_containers ||= Docker.containers_running.select do |c|
75
+ c.name =~ /^#{full_name}(\.\d+)?$/
68
76
  end
69
77
  end
70
78
 
71
- # Start +scale+ instances of the application
79
+ def running_instances
80
+ @running_instances ||= running_containers.map do |c|
81
+ n = c.name.sub(/^#{full_name}\.?/, '')
82
+ n = n.empty? ? nil : n.to_i
83
+ Instance.new(self, n, c)
84
+ end
85
+ end
86
+
87
+ def stopped_instances
88
+ return @stopped_instances if @stopped_instances
89
+ n_range = scale == 1 ? [nil] : (1..scale).to_a
90
+ @stopped_instances = n_range.reject do |n|
91
+ running_instances.map(&:n).include?(n)
92
+ end
93
+ @stopped_instances = @stopped_instances.map do |n|
94
+ Instance.new(self, n, nil)
95
+ end
96
+ @stopped_instances
97
+ end
98
+
99
+ # Start instances of the application
72
100
  #
73
- def start(scale = 1)
101
+ def start
74
102
  instances.map(&:stop!) if running?
75
103
  if scale > 1
76
104
  scale.times { |n| Instance.start(self, n + 1) }
@@ -129,35 +157,60 @@ module Jarl
129
157
  # Application::Instance represents a single running instance of the application
130
158
  #
131
159
  class Instance
132
- attr_reader :application, :container
133
- def initialize(application, container)
160
+ attr_reader :application, :n, :container
161
+
162
+ # @param application [Application]
163
+ # @param n [nil,Integer] Instance number, nil if an instance is the primary instance
164
+ # @param container [nil,Docker::Container] Running container, if present
165
+ #
166
+ def initialize(application, n, container)
134
167
  @application = application
168
+ @n = n
135
169
  @container = container
136
170
  end
137
171
 
172
+ def running?
173
+ !@container.nil?
174
+ end
175
+
176
+ def full_name
177
+ n ? "#{application.full_name}.#{n}" : application.full_name
178
+ end
179
+
180
+ def hostname
181
+ n ? "#{application.hostname}-#{n}" : application.hostname
182
+ end
183
+
138
184
  def stop!
185
+ return unless running?
139
186
  @container.stop!
140
187
  end
141
188
 
142
- def name
143
- @container.name
144
- end
189
+ def start!
190
+ return if running?
191
+ # puts esc_yellow("Starting #{application.name}.#{n}") if n
145
192
 
146
- def n
147
- suffix = @container.name.split('.').last
148
- suffix =~ /^\d+$/ ? suffix : nil
193
+ Docker.start(
194
+ name: full_name,
195
+ hostname: hostname,
196
+ image: application.image_name,
197
+ volumes: application.volumes,
198
+ ports: application.ports,
199
+ environment: application.environment,
200
+ command: application.command
201
+ )
149
202
  end
150
203
 
151
204
  def tail_log(*_args)
205
+ return unless running?
152
206
  color_code = Console::Colors::SEQUENCE[application.serial % Console::Colors::SEQUENCE.size]
153
207
  begin
154
- instance_no = n ? ".#{n}" : ''
155
208
  IO.popen "docker logs -f #{container.id}", 'r' do |p|
156
209
  str = '<<< STARTED >>>'
157
210
  while str
158
211
  str = p.gets
159
212
  str.split("\n").each do |s|
160
- puts "#{esc_color color_code, application.full_name}#{instance_no}: #{s}"
213
+ puts "#{esc_color color_code, full_name}: #{s}"
161
214
  end
162
215
  end
163
216
  end
@@ -167,20 +220,9 @@ module Jarl
167
220
  end
168
221
 
169
222
  def ssh(command = nil)
223
+ fail "Failed to open SSH, no running container for '#{full_name}'" unless running?
170
224
  container.open_ssh_session!(Jarl.config.params, command)
171
225
  end
172
-
173
- def self.start(application, n = nil)
174
- Docker.start(
175
- name: (n ? "#{application.full_name}.#{n}" : application.full_name),
176
- hostname: (n ? "#{application.hostname}-#{n}" : application.hostname),
177
- image: application.image_name,
178
- volumes: application.volumes,
179
- ports: application.ports,
180
- environment: application.environment,
181
- command: application.command
182
- )
183
- end
184
226
  end # class Instance
185
227
  end # class Application
186
228
  end # module Jarl
data/lib/jarl/base.rb CHANGED
@@ -42,10 +42,49 @@ module Jarl
42
42
  end.flatten
43
43
  end
44
44
 
45
+ # Returns list of running application instances
46
+ #
47
+ def self.application_instances
48
+ @application_instances ||= applications.map(&:instances).flatten
49
+ end
50
+
45
51
  # Returns list of applications matching the pattern
46
52
  #
47
- def self.find_applications_by(pattern)
48
- applications.select { |a| a.full_name.index(pattern) }
53
+ # @param patterns [nil] Match all application instances
54
+ # @param patterns [String] Match application instances by single pattern
55
+ # @param patterns [Arary<String>] Match application instances by all patterns
56
+ #
57
+ def self.find_applications_by(patterns)
58
+ find_application_instances_by(patterns).map(&:application).uniq
59
+ end
60
+
61
+ # Returns list of running application instances matching the pattern(s)
62
+ #
63
+ # @param patterns [nil] Match all application instances
64
+ # @param patterns [String] Match application instances by single pattern
65
+ # @param patterns [Arary<String>] Match application instances by all patterns
66
+ #
67
+ def self.find_application_instances_by(patterns)
68
+ patterns = nil if patterns && patterns.empty?
69
+ case patterns
70
+ when nil
71
+ application_instances
72
+ when String
73
+ application_instances.select { |i| i.full_name.index(patterns) }
74
+ when Array
75
+ application_instances.select { |i| patterns.any? { |p| i.full_name.index(p) } }
76
+ else
77
+ fail "Unexpected #{patterns.class} passed as argument to .find_application_instances_by"
78
+ end
79
+ end
80
+
81
+ #
82
+ #
83
+ def self.reload!
84
+ @jarl_files = nil
85
+ @applications = nil
86
+ @application_instances = nil
87
+ Docker.reload!
49
88
  end
50
89
 
51
90
  private
data/lib/jarl/cli.rb CHANGED
@@ -25,52 +25,58 @@ module Jarl
25
25
  puts " #{app.name}"
26
26
  end
27
27
  end
28
- # show_jarl_applications_status(apps)
29
28
  end
30
29
 
31
30
  #
32
- desc 'status [NAME]', '(default) Show applications statuses'
33
- def status(name = nil)
31
+ desc 'status [NAME ...]', '(default) Show applications statuses'
32
+ def status(*names)
34
33
  Jarl.load(options)
35
- apps = name ? Jarl.find_applications_by(name) : Jarl.applications
36
- show_jarl_applications_status(apps)
34
+ ais = Jarl.find_application_instances_by(names)
35
+ show_jarl_application_instances_status(ais)
37
36
  end
38
37
  default_task :status
39
38
 
40
- #
41
- option :scale,
42
- type: :numeric,
43
- default: 1,
44
- desc: 'Start several instances'
45
- desc 'up [NAME]', 'Start or restart applications'
46
- def up(name = nil)
39
+ desc 'up [NAME ...]', 'Start or restart applications'
40
+ def up(*names)
47
41
  Jarl.load(options)
48
- apps = name ? Jarl.find_applications_by(name) : Jarl.applications
49
- apps.each do |app|
50
- puts esc_green(app.running? ? "Restarting '#{app}'..." : "Starting '#{app}'...")
51
- app.start(options[:scale])
42
+ if Jarl.find_application_instances_by(names).any?(&:running?)
43
+ down(*names)
44
+ Jarl.reload!
45
+ end
46
+ ais = Jarl.find_application_instances_by(names)
47
+ ais.each do |i|
48
+ puts esc_green "Starting '#{i.full_name}'..."
49
+ i.start!
52
50
  end
51
+ # apps = name ? Jarl.find_applications_by(name) : Jarl.applications
52
+ # apps.each do |app|
53
+ # puts esc_green(app.running? ? "Restarting '#{app}'..." : "Starting '#{app}'...")
54
+ # app.start
55
+ # end
53
56
  # show_jarl_applications_status(apps)
54
57
  end
55
58
 
56
59
  #
57
- desc 'down [NAME]', 'Stop applications'
58
- def down(name = nil)
60
+ desc 'down [NAME ...]', 'Stop applications'
61
+ def down(*names)
59
62
  Jarl.load(options)
60
- apps = name ? Jarl.find_applications_by(name) : Jarl.applications
61
- apps.each do |app|
62
- next unless app.running?
63
- app.instances.each do |i|
64
- puts esc_yellow "Stopping '#{i.name}'..."
65
- i.stop!
66
- end
63
+ ais = Jarl.find_application_instances_by(names).select(&:running?)
64
+ puts esc_yellow 'No running applications found' unless ais.size > 0
65
+ ais.each do |i|
66
+ puts esc_yellow "Stopping '#{i.full_name}'..."
67
+ i.stop!
67
68
  end
68
69
  end
69
70
 
70
71
  desc 'execute NAME [COMMAND ...]', 'Run a single command against application image'
71
72
  def execute(name, command = nil, *args)
72
73
  Jarl.load(options)
73
- app = Jarl.find_applications_by(name).first
74
+ apps = Jarl.find_applications_by(name)
75
+ if apps.size > 1
76
+ puts esc_yellow 'More than one application matches the pattern, using the first one:'
77
+ puts esc_yellow " #{apps.map(&:full_name).join(', ')}"
78
+ end
79
+ app = apps.first
74
80
  abort "Failed to find application by name: '#{name}'" unless app
75
81
  app.execute(command, args)
76
82
  end
@@ -79,7 +85,13 @@ module Jarl
79
85
  desc 'ssh NAME [COMMAND ...]', 'Open an interactive SSH session to a running application or execute a command and exit'
80
86
  def ssh(name, command = nil, *args)
81
87
  Jarl.load(options)
82
- app = Jarl.find_applications_by(name).first
88
+ ais = Jarl.find_application_instances_by(name).select(&:running?)
89
+ abort 'No running applications found' unless ais.size > 0
90
+ if ais.size > 1
91
+ puts esc_yellow 'More than one running application instance matches the pattern, using the first one:'
92
+ puts esc_yellow " #{ais.map(&:full_name).join(', ')}"
93
+ end
94
+ app = ais.first
83
95
  abort "Failed to find application by name: '#{name}'" unless app
84
96
  abort "Application is not running: '#{app.full_name}'" unless app.running?
85
97
  app.ssh([command] + args)
@@ -87,45 +99,46 @@ module Jarl
87
99
 
88
100
  #
89
101
  option :ip, type: :boolean, desc: 'Show only IPs'
90
- desc 'inspect [NAME]', 'Inspect applications'
91
- def inspect(name = nil)
102
+ desc 'inspect [NAME ...]', 'Inspect running application instances'
103
+ def inspect(*names)
92
104
  Jarl.load(options)
93
- apps = name ? Jarl.find_applications_by(name) : Jarl.applications
94
- apps.each do |app|
105
+ ais = Jarl.find_application_instances_by(names).select(&:running?)
106
+ abort 'No running applications found' unless ais.size > 0
107
+ ais.each do |ai|
95
108
  if options[:ip]
96
- puts app.instances.map(&:container).map(&:ip) if app.running?
109
+ puts ai.container.ip
97
110
  else
98
- show_jarl_application_inspect(app)
111
+ show_jarl_application_instance_inspect(ai)
99
112
  end
100
113
  end
101
114
  end
102
115
 
103
- desc 'build [NAME]', 'Rebuild docker images for Dockerfile based applications'
104
- def build(name = nil)
116
+ desc 'build [NAME ...]', 'Rebuild docker images for Dockerfile based applications'
117
+ def build(*names)
105
118
  Jarl.load(options)
106
- apps = name ? Jarl.find_applications_by(name) : Jarl.applications
119
+ apps = Jarl.find_applications_by(names)
107
120
  apps.select(&:image_is_a_path?).each do |app|
108
121
  puts esc_yellow "Building image for '#{app}'..."
109
122
  app.build
110
123
  end
111
124
  end
112
125
 
113
- desc 'pull [NAME]', 'Pull docker images for regestry based applications'
114
- def pull(name = nil)
126
+ desc 'pull [NAME ...]', 'Pull docker images for regestry based applications'
127
+ def pull(*names)
115
128
  Jarl.load(options)
116
- apps = name ? Jarl.find_applications_by(name) : Jarl.applications
129
+ apps = Jarl.find_applications_by(names)
117
130
  apps.select(&:image_is_a_registry_path?).each do |app|
118
131
  puts esc_yellow "Pulling image '#{app}'..."
119
132
  app.pull
120
133
  end
121
134
  end
122
135
 
123
- desc 'logs [NAME]', 'Show log output for applications'
124
- def logs(name = nil)
136
+ desc 'logs [NAME ...]', 'Show log output for applications'
137
+ def logs(*names)
125
138
  Jarl.load(options)
126
- apps = name ? Jarl.find_applications_by(name) : Jarl.applications
139
+ ais = Jarl.find_application_instances_by(names)
127
140
  threads = []
128
- apps.select(&:running?).map(&:instances).flatten.each do |instance|
141
+ ais.select(&:running?).flatten.each do |instance|
129
142
  threads << Thread.new { instance.tail_log }
130
143
  end
131
144
 
@@ -148,14 +161,12 @@ module Jarl
148
161
  "Jarl v#{Jarl::VERSION}"
149
162
  end
150
163
 
151
- def show_jarl_applications_status(apps)
152
- if apps.size == 0
164
+ def show_jarl_application_instances_status(ais)
165
+ if ais.size == 0
153
166
  puts esc_yellow 'No applications found'
154
167
  return
155
168
  end
156
- full_names = apps.map do |app|
157
- app.running? ? app.instances.map { |i| i.container.name } : app.full_name
158
- end.flatten
169
+ full_names = ais.map(&:full_name)
159
170
  max_full_name_len = [full_names.map(&:size).max, 12].max
160
171
  puts esc_format(
161
172
  ['APPLICATION', max_full_name_len],
@@ -164,20 +175,18 @@ module Jarl
164
175
  ['IP', 12],
165
176
  ['PORTS']
166
177
  )
167
- apps.each do |app|
168
- if app.running?
169
- app.instances.each do |i|
170
- puts esc_format(
171
- [i.container.name, max_full_name_len, :yellow],
172
- [i.container.id, 12, :green],
173
- [seconds_to_human(i.container.uptime), 12, :yellow],
174
- [i.container.ip, 12, :yellow],
175
- [i.container.ports.map { |p| "#{p[:from]}" }.join(', ')]
176
- )
177
- end
178
+ ais.each do |i|
179
+ if i.running?
180
+ puts esc_format(
181
+ [i.container.name, max_full_name_len, :yellow],
182
+ [i.container.id, 12, :green],
183
+ [seconds_to_human(i.container.uptime), 12, :yellow],
184
+ [i.container.ip, 12, :yellow],
185
+ [i.container.ports.map { |p| "#{p[:from]}" }.join(', ')]
186
+ )
178
187
  else
179
188
  puts esc_format(
180
- [app.full_name, max_full_name_len, :yellow],
189
+ [i.full_name, max_full_name_len, :yellow],
181
190
  ['no', 12],
182
191
  ['', 12],
183
192
  ['', 12],
@@ -187,21 +196,21 @@ module Jarl
187
196
  end
188
197
  end
189
198
 
190
- def show_jarl_application_inspect(app)
191
- unless app.running?
192
- puts esc_yellow("#{app.full_name}: ") + 'down'
199
+ def show_jarl_application_instance_inspect(ai)
200
+ unless ai.running?
201
+ puts esc_yellow("#{ai.full_name}: ") + 'off'
193
202
  return
194
203
  end
195
- app.instances.map(&:container).each do |container|
196
- puts esc_yellow "#{container.name}: " + esc_green(container.id)
197
- puts " app: #{esc_yellow app.full_name}"
198
- puts " image: #{esc_yellow container.image}"
199
- puts " full ID: #{esc_yellow container.long_id}"
200
- puts " uptime: #{esc_yellow seconds_to_human container.uptime}"
201
- puts " IP: #{esc_yellow container.ip}"
202
- puts " ports: #{esc_yellow container.ports.map { |p| "#{p[:from]}->#{p[:to]}"}.join(', ')}"
203
- puts " volumes: #{esc_yellow container.params['Volumes']}"
204
- end
204
+ app = ai.application
205
+ container = ai.container
206
+ puts esc_yellow "#{container.name}: " + esc_green(container.id)
207
+ puts " app: #{esc_yellow app.full_name}"
208
+ puts " image: #{esc_yellow container.image}"
209
+ puts " full ID: #{esc_yellow container.long_id}"
210
+ puts " uptime: #{esc_yellow seconds_to_human container.uptime}"
211
+ puts " IP: #{esc_yellow container.ip}"
212
+ puts " ports: #{esc_yellow container.ports.map { |p| "#{p[:from]}->#{p[:to]}"}.join(', ')}"
213
+ puts " volumes: #{esc_yellow container.volumes}"
205
214
  end
206
215
  end # class CLI
207
216
  end # module Jarl
data/lib/jarl/docker.rb CHANGED
@@ -64,9 +64,17 @@ module Docker
64
64
  @docker_containers_running
65
65
  end
66
66
 
67
+ # Returns `docker ps` info inspector instance
68
+ #
69
+ def self.ps
70
+ @docker_ps ||= Ps.new
71
+ end
72
+
73
+
67
74
  # Reloads lists of images and containers
68
75
  #
69
76
  def self.reload!
77
+ @docker_ps = nil
70
78
  @docker_images = nil
71
79
  @docker_images_used = nil
72
80
  @docker_containers_running = nil
@@ -134,6 +142,52 @@ module Docker
134
142
  sh docker_cmd
135
143
  end
136
144
 
145
+ # `docker ps` info inspector
146
+ #
147
+ class Ps
148
+ FIELDS = [
149
+ 'CONTAINER ID', 'IMAGE', 'COMMAND', 'CREATED', 'STATUS', 'PORTS', 'NAMES'
150
+ ]
151
+
152
+ attr_reader :entries
153
+
154
+ def initialize
155
+ lines = `docker ps`.split("\n")
156
+ @headers = lines.shift
157
+ @data = lines
158
+ @entries = lines.map do |line|
159
+ FIELDS.map { |name| [name, field(line, name)] }.to_h
160
+ end
161
+ end
162
+
163
+ def field_i_start(name)
164
+ i_start = @headers.index(name)
165
+ fail "Failed to find the field #{name} in docker ps" unless i_start
166
+ i_start
167
+ end
168
+
169
+ # @return [nil,Integer] right index for field or -1 if the field is rightmost
170
+ #
171
+ def field_i_end(name)
172
+ i_start = field_i_start(name)
173
+ r_headers = @headers[i_start..-1].sub(/^#{name}/, '')
174
+ m = r_headers.match(/^(\s+)/)
175
+ m ? i_start + name.size + m[1].size - 1 : -1
176
+ end
177
+
178
+ def field_i_range(name)
179
+ field_i_start(name)..field_i_end(name)
180
+ end
181
+
182
+ def field(line, name)
183
+ line[field_i_range(name)].strip
184
+ end
185
+
186
+ def [](id)
187
+ entries.find { |e| id == e['CONTAINER ID'] }
188
+ end
189
+ end
190
+
137
191
  # Image
138
192
  #
139
193
  class Image
@@ -205,24 +259,54 @@ module Docker
205
259
  # Container
206
260
  #
207
261
  class Container
208
- attr_accessor :id, :long_id, :name, :image, :image_id, :ip, :ps, :ports, :params
262
+ attr_accessor :id
209
263
 
210
264
  def initialize(id)
211
265
  @id = id
212
- @ps = `docker ps | grep #{id}`
213
- container_inspect = `docker inspect #{id}`
214
- @params = JSON.parse(container_inspect).first
215
- @long_id = params['Id']
216
- @name = params['Name'].gsub('/', '')
217
- @image = params['Config']['Image']
218
- @image_id = params['Image']
219
- @ip = params['NetworkSettings']['IPAddress']
220
- port_maps = params['NetworkSettings']['Ports']
221
- @ports = port_maps.keys.map do |port|
266
+ end
267
+
268
+ def ps
269
+ Docker.ps[id] or fail "Failed to find ps info for #{id}"
270
+ end
271
+
272
+ def container_inspect
273
+ return @container_inspect if @container_inspect
274
+ # puts "Loading inspect for: #{ps}"
275
+ @container_inspect ||= JSON.parse(`docker inspect #{id}`).first
276
+ end
277
+
278
+ def long_id
279
+ container_inspect['Id']
280
+ end
281
+
282
+ def name
283
+ ps['NAMES']
284
+ # container_inspect['Name'].gsub('/', '')
285
+ end
286
+
287
+ def image
288
+ container_inspect['Config']['Image']
289
+ end
290
+
291
+ def image_id
292
+ container_inspect['Image']
293
+ end
294
+
295
+ def ip
296
+ container_inspect['NetworkSettings']['IPAddress']
297
+ end
298
+
299
+ def ports
300
+ port_maps = container_inspect['NetworkSettings']['Ports']
301
+ port_maps.keys.map do |port|
222
302
  { from: port, to: port_maps[port] }
223
303
  end
224
304
  end
225
305
 
306
+ def volumes
307
+ container_inspect['Volumes']
308
+ end
309
+
226
310
  def open_ssh_session!(params = {}, command = nil)
227
311
  ssh_flags = ['-oStrictHostKeyChecking=no']
228
312
  if params['ssh_identity']
@@ -246,8 +330,8 @@ module Docker
246
330
  end
247
331
 
248
332
  def uptime
249
- params['State'] && params['State']['StartedAt'] &&
250
- Time.now.utc - DateTime.parse(params['State']['StartedAt']).to_time.utc
333
+ container_inspect['State'] && container_inspect['State']['StartedAt'] &&
334
+ Time.now.utc - DateTime.parse(container_inspect['State']['StartedAt']).to_time.utc
251
335
  end
252
336
 
253
337
  def self.clean_containers(*names)
data/lib/jarl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jarl
2
- VERSION = '0.7.0'
2
+ VERSION = '0.8.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jarl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Kukushkin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-15 00:00:00.000000000 Z
11
+ date: 2015-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
111
  version: '0'
112
112
  requirements: []
113
113
  rubyforge_project:
114
- rubygems_version: 2.2.2
114
+ rubygems_version: 2.4.5
115
115
  signing_key:
116
116
  specification_version: 4
117
117
  summary: Jarl, docker container management tool