jarl 0.2.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5f658f6a2817ddf675c13d89d831c744dd27fcf
4
+ data.tar.gz: 087abc1978c7461efc0e2b2154b321ad4c0c1285
5
+ SHA512:
6
+ metadata.gz: abfa4bd3f766e2d50352076b32ab0d1e56151fddd19ea8c3c769e44d971e11eecc0148c6187c6077a0fff02b77befaa1e7567507467a1f40cf1a2ed2e2513735
7
+ data.tar.gz: 1f397a352b7a36204e4ad6888235b7bf0b34d0d267b1084fbbd0d4bb6fc562bc22dd0078e92365d6308f9c2832216c655bd9431317694315317b06be772d5256
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jarl.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Alex Kukushkin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Jarl
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'jarl'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install jarl
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/jarl/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ desc 'Run specs'
6
+ task default: :spec
data/bin/jarl ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/jarl'
4
+
5
+ Jarl::CLI.start
data/jarl.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jarl/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'jarl'
8
+ spec.version = Jarl::VERSION
9
+ spec.authors = ['Alex Kukushkin']
10
+ spec.email = ['alex.kukushkin@cubits.com']
11
+ spec.summary = %q{Jarl, docker container management tool}
12
+ spec.description = %q{Jarl, docker container management tool}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'thor', '~> 0.19'
22
+ spec.add_development_dependency 'bundler', '~> 1.7'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ end
@@ -0,0 +1,101 @@
1
+ module Jarl
2
+ class Application
3
+ attr_reader :group, :name, :image, :volumes, :ports, :environment, :entrypoint, :command
4
+
5
+ # Create Application object from application definition:
6
+ #
7
+ # app_name:
8
+ # image: blabla
9
+ # volumes:
10
+ # - /host/path:/container/path
11
+ # ports:
12
+ # - 1234:5678
13
+ # command: blabla
14
+ #
15
+ def initialize(group, name, params)
16
+ @group = group
17
+ @name = name
18
+ @image = params['image'] || fail("Application '#{self}' has no 'image' defined")
19
+ unless @image.is_a?(String)
20
+ fail "Application '#{self}' has invalid 'image' definition, String is expected"
21
+ end
22
+ @volumes = params['volumes'] || []
23
+ unless @volumes.is_a?(Array)
24
+ fail "Application '#{self}' has invalid 'volumes' definition, Array is expected"
25
+ end
26
+ @ports = params['ports'] || []
27
+ unless @ports.is_a?(Array)
28
+ fail "Application '#{self}' has invalid 'ports' definition, Array is expected"
29
+ end
30
+ @environment = params['environment'] || {}
31
+ unless @environment.is_a?(Hash)
32
+ fail "Application '#{self}' has invalid 'environment' definition, Hash is expected"
33
+ end
34
+ @entrypoint = params['entrypoint']
35
+ if @entrypoint && !@entrypoint.is_a?(String)
36
+ fail "Application '#{self}' has invalid 'entrypoint' definition, String is expected"
37
+ end
38
+ @command = params['command']
39
+ if @command && !@command.is_a?(String)
40
+ fail "Application '#{self}' has invalid 'command' definition, String is expected"
41
+ end
42
+ end
43
+
44
+ def full_name
45
+ "#{group}.#{name}"
46
+ end
47
+
48
+ def image_is_a_path?
49
+ image =~ /^[~\.\/]/
50
+ end
51
+
52
+ def running?
53
+ running_container
54
+ end
55
+
56
+ def running_container
57
+ Docker.containers_running.find { |c| c.name == full_name }
58
+ end
59
+
60
+ def start
61
+ running_container.stop! if running?
62
+ Docker.start(
63
+ name: full_name,
64
+ image: image_name,
65
+ volumes: volumes,
66
+ ports: ports,
67
+ environment: environment,
68
+ command: command
69
+ )
70
+ end
71
+
72
+ def build
73
+ fail 'Not a file based image' unless image_is_a_path?
74
+ Docker::Image.new(image_name, image_path).build!
75
+ end
76
+
77
+ def image_name
78
+ image_is_a_path? ? name : image
79
+ end
80
+
81
+ def image_path
82
+ image_is_a_path? ? image : nil
83
+ end
84
+
85
+ def show_log(*_args)
86
+ return unless running?
87
+ begin
88
+ IO.popen "docker logs -f #{running_container.id}", 'r' do |p|
89
+ str = '<<< STARTED >>>'
90
+ puts "#{esc_yellow full_name}: #{str}" while str = p.gets
91
+ end
92
+ rescue Interrupt
93
+ #
94
+ end
95
+ end
96
+
97
+ def to_s
98
+ full_name
99
+ end
100
+ end # class Application
101
+ end # module Jarl
data/lib/jarl/base.rb ADDED
@@ -0,0 +1,48 @@
1
+ module Jarl
2
+ #
3
+ # Load config and application definitions
4
+ #
5
+ def self.load
6
+ abort 'Docker is not installed' unless Docker.installed?
7
+ if Jarl.jarl_files.empty?
8
+ abort 'No *.jarl files found'
9
+ elsif Jarl.applications.empty?
10
+ abort 'No application definitions found'
11
+ end
12
+ true
13
+ end
14
+
15
+ #
16
+ # Returns list of found *.jarl files
17
+ #
18
+ def self.jarl_files
19
+ @jarl_files ||= load_jarl_files
20
+ end
21
+
22
+ # Returns list of registered applications
23
+ #
24
+ def self.applications
25
+ @applications ||= jarl_files.map do |jf|
26
+ begin
27
+ jf.applications.map { |app_name, params| Application.new(jf.name, app_name, params) }
28
+ rescue StandardError => e
29
+ abort "Failed to parse application definition in file '#{jf.path}': #{e}"
30
+ end
31
+ end.flatten
32
+ end
33
+
34
+ # Returns list of applications matching the pattern
35
+ #
36
+ def self.find_applications_by(pattern)
37
+ applications.select { |a| a.full_name.index(pattern) }
38
+ end
39
+
40
+ private
41
+
42
+ def self.load_jarl_files
43
+ Pathname.glob(Pathname.pwd + '**' + '*.jarl').map do |p|
44
+ JarlFile.new(p)
45
+ end
46
+ end
47
+
48
+ end # module Jarl
data/lib/jarl/cli.rb ADDED
@@ -0,0 +1,150 @@
1
+ require 'thor'
2
+
3
+ module Jarl
4
+ class CLI < Thor
5
+ # global options
6
+ #
7
+ class_option :config,
8
+ aliases: '-c',
9
+ type: :string,
10
+ desc: 'Use specified config file (defaul: ~/.jarl)'
11
+
12
+ #
13
+ desc 'status [NAME]', 'Show applications statuses'
14
+ def status(name = nil)
15
+ Jarl.load
16
+ apps = name ? Jarl.find_applications_by(name) : Jarl.applications
17
+ show_jarl_applications_status(apps)
18
+ end
19
+ default_task :status
20
+
21
+ #
22
+ desc 'up [NAME]', '(Re)start applications'
23
+ def up(name = nil)
24
+ Jarl.load
25
+ apps = name ? Jarl.find_applications_by(name) : Jarl.applications
26
+ apps.each do |app|
27
+ puts esc_green(app.running? ? "Restarting '#{app}'..." : "Starting '#{app}'...")
28
+ app.start
29
+ end
30
+ # show_jarl_applications_status(apps)
31
+ end
32
+
33
+ #
34
+ desc 'down [NAME]', 'Stop applications'
35
+ def down(name = nil)
36
+ Jarl.load
37
+ apps = name ? Jarl.find_applications_by(name) : Jarl.applications
38
+ apps.each do |app|
39
+ next unless app.running?
40
+ puts esc_yellow "Stopping '#{app}'..."
41
+ app.running_container.stop!
42
+ end
43
+ end
44
+
45
+ #
46
+ option :ip, type: :boolean, desc: 'Show only IPs'
47
+ desc 'inspect [NAME]', 'Inspect applications'
48
+ def inspect(name = nil)
49
+ Jarl.load
50
+ apps = name ? Jarl.find_applications_by(name) : Jarl.applications
51
+ apps.each do |app|
52
+ if options[:ip]
53
+ puts app.running_container.ip if app.running?
54
+ else
55
+ show_jarl_application_inspect(app)
56
+ end
57
+ end
58
+ end
59
+
60
+ desc 'build [NAME]', '(Re)build docker images'
61
+ def build(name = nil)
62
+ Jarl.load
63
+ apps = name ? Jarl.find_applications_by(name) : Jarl.applications
64
+ apps.select(&:image_is_a_path?).each do |app|
65
+ puts esc_yellow "Building image for '#{app}'..."
66
+ app.build
67
+ end
68
+ end
69
+
70
+ desc 'logs [NAME]', 'Show log output for applications'
71
+ def logs(name = nil)
72
+ Jarl.load
73
+ apps = name ? Jarl.find_applications_by(name) : Jarl.applications
74
+ threads = []
75
+ apps.select(&:running?).each do |app|
76
+ threads << Thread.new { app.show_log }
77
+ end
78
+ begin
79
+ threads.each(&:join)
80
+ rescue Interrupt
81
+ #
82
+ end
83
+ puts
84
+ end
85
+
86
+ desc 'version', 'Show Jarl version'
87
+ def version
88
+ puts app_name_version
89
+ end
90
+
91
+ private
92
+
93
+ def app_name_version
94
+ "Jarl v#{Jarl::VERSION}"
95
+ end
96
+
97
+ def show_jarl_applications_status(apps)
98
+ if apps.size == 0
99
+ puts esc_yellow 'No applications found'
100
+ return
101
+ end
102
+ max_full_name_len = [apps.map(&:full_name).map(&:size).max, 12].max
103
+ puts esc_format(
104
+ ['APPLICATION', max_full_name_len],
105
+ ['RUNNING', 12],
106
+ ['UPTIME', 12],
107
+ ['IP', 12],
108
+ ['PORTS']
109
+ # ['IMAGE']
110
+ )
111
+ apps.each do |app|
112
+ container = app.running_container
113
+ puts esc_format(
114
+ [app.full_name, max_full_name_len, :yellow],
115
+ (app.running? ? [container.id, 12, :green] : ['no', 12]),
116
+ (app.running? ? [seconds_to_human(container.uptime), 12, :yellow] : ['', 12]),
117
+ (app.running? ? [container.ip, 12, :yellow] : ['', 12]),
118
+ (app.running? ? [container.ports.map { |p| "#{p[:from]}"}.join(', ')] : [''])
119
+
120
+ # (app.image_is_a_path? ? ["path: #{app.image}"] : [app.image])
121
+ )
122
+ end
123
+ # puts "#{app.full_name} (#{app.image})"
124
+ # puts " image: #{app.image}"
125
+ # puts " volumes: #{app.volumes}"
126
+ # puts " ports: #{app.ports}"
127
+ # puts " environment: #{app.environment}"
128
+ # puts " entrypoint: #{app.entrypoint}"
129
+ # puts " command: #{app.command}"
130
+ end
131
+
132
+ def show_jarl_application_inspect(app)
133
+ unless app.running?
134
+ puts esc_yellow("#{app.full_name}: ") + 'down'
135
+ return
136
+ end
137
+ container = app.running_container
138
+ puts esc_yellow "#{app.full_name}: " + esc_green(container.id)
139
+ puts " name: #{esc_yellow container.name}"
140
+ puts " image: #{esc_yellow container.image}"
141
+ puts " full ID: #{esc_yellow container.long_id}"
142
+ puts " uptime: #{esc_yellow seconds_to_human container.uptime}"
143
+ puts " IP: #{esc_yellow container.ip}"
144
+ puts " ports: #{esc_yellow container.ports.map { |p| "#{p[:from]}->#{p[:to]}"}.join(', ')}"
145
+ puts " volumes: #{esc_yellow container.params['Volumes']}"
146
+ # puts " ps: #{esc_yellow container.ps}"
147
+ # puts " params: #{esc_yellow container.params}"
148
+ end
149
+ end # class CLI
150
+ end # module Jarl
@@ -0,0 +1,88 @@
1
+ module Console
2
+ module Shell
3
+ def sh(*args)
4
+ puts "> #{args.join(' ')}"
5
+ system(*args)
6
+ end
7
+ end # module Shell
8
+
9
+ module Utils
10
+ def seconds_to_human(s)
11
+ s = s.round
12
+ m = (s / 60).floor
13
+ s = s % 60
14
+ h = (m / 60).floor
15
+ m = m % 60
16
+ d = (h / 24).floor
17
+ h = h % 24
18
+
19
+ output = "#{s}s"
20
+ output = "#{m}m " + output if m > 0 || h > 0 || d > 0
21
+ output = "#{h}h " + output if h > 0 || d > 0
22
+ output = "#{d}d " + output if d > 0
23
+ output
24
+ end
25
+ end # module Utils
26
+
27
+ module Colors
28
+ # Returns ANSI escaped string.
29
+ #
30
+ def esc_string(esc, text)
31
+ esc + text.to_s + "\e[0m"
32
+ end
33
+
34
+ # Returns ANSI escaped string for the red colored text.
35
+ #
36
+ def esc_red(text)
37
+ esc_string "\e[31m", text
38
+ end
39
+
40
+ # Returns ANSI escaped string for the green colored text.
41
+ #
42
+ def esc_green(text)
43
+ esc_string "\e[32m", text
44
+ end
45
+
46
+ # Returns ANSI escaped string for the yellow colored text.
47
+ #
48
+ def esc_yellow(text)
49
+ esc_string "\e[33m", text
50
+ end
51
+
52
+ # Returns ANSI escaped string for the blue colored text.
53
+ #
54
+ def esc_blue(text)
55
+ esc_string "\e[34m", text
56
+ end
57
+
58
+ # Returns ANSI escaped string for the bold text.
59
+ #
60
+ def esc_bold(text)
61
+ esc_string "\e[01;37m", text
62
+ end
63
+
64
+ # Returns string padded to given number of characters, text can be escaped.
65
+ #
66
+ def esc_pad(text, num, color = nil)
67
+ esc_text = text
68
+ case color
69
+ when :red, :yellow, :green
70
+ esc_text = send :"esc_#{color}", text
71
+ end
72
+ pad = esc_text.size - text.size
73
+ num > 0 ? ("%-#{num + pad}s" % esc_text) : esc_text
74
+ end
75
+
76
+ # Returns string with formatted and padded fields
77
+ # Each arg is an Array:
78
+ # [<text>, [padding_num], [color]]
79
+ #
80
+ def esc_format(*args)
81
+ out = []
82
+ args.each do |arg|
83
+ out << esc_pad(arg[0], arg[1] || 0, arg[2])
84
+ end
85
+ out.join(' ')
86
+ end
87
+ end # module Colors
88
+ end # module Console
@@ -0,0 +1,229 @@
1
+ require 'json'
2
+
3
+ module Jarl
4
+ module Docker
5
+ INSECURE_KEY_PATH = '/home/vagrant/.ssh/insecure_key'
6
+
7
+ # Returns true if Docker is installed
8
+ #
9
+ def self.installed?
10
+ !`which docker`.empty?
11
+ end
12
+
13
+ # Returns available docker images
14
+ #
15
+ def self.images
16
+ @docker_images ||= Pathname.glob(Pathname.new(DOCKER_IMAGES_PATH).join('*/')).sort.map do |path|
17
+ Image.new(path.basename, path)
18
+ end
19
+ end
20
+
21
+ # Returns a list of used images
22
+ #
23
+ def self.images_used
24
+ used_images_list = File.readlines(USED_IMAGES_FILE) rescue ['']
25
+ used_images_list.map!(&:chomp)
26
+ @docker_images_used ||= images.select { |di| used_images_list.include?(di.name) }
27
+ end
28
+
29
+ # Returns a list of unused images
30
+ #
31
+ def self.images_unused
32
+ images - images_used
33
+ end
34
+
35
+ # Adds image to a list of used images
36
+ #
37
+ def self.images_used_add(name)
38
+ list = images_used.map(&:name)
39
+ list << name
40
+ File.open(USED_IMAGES_FILE, 'w') do |f|
41
+ f.puts list.join("\n")
42
+ end
43
+ reload!
44
+ end
45
+
46
+ # Removes image from a list of used images
47
+ #
48
+ def self.images_used_remove(name)
49
+ list = images_used.map(&:name).select { |n| n != name }
50
+ File.open(USED_IMAGES_FILE, 'w') do |f|
51
+ f.puts list.join("\n")
52
+ end
53
+ reload!
54
+ end
55
+
56
+ # Returns a list of running docker containers
57
+ #
58
+ def self.containers_running
59
+ return @docker_containers_running if @docker_containers_running
60
+ container_ids = `docker ps -q`.split("\n")
61
+ @docker_containers_running = container_ids.map do |id|
62
+ Container.new id
63
+ end
64
+ @docker_containers_running
65
+ end
66
+
67
+ # Reloads lists of images and containers
68
+ #
69
+ def self.reload!
70
+ @docker_images = nil
71
+ @docker_images_used = nil
72
+ @docker_containers_running = nil
73
+ end
74
+
75
+ # Cleans unused images
76
+ #
77
+ def self.clean_images
78
+ sh "docker rmi $(docker images -a | grep '^<none>' | awk '{print $3}') >/dev/null 2>&1"
79
+ rescue RuntimeError => e
80
+ # omit conflicting images error reports
81
+ raise unless e.to_s =~ /Command failed/
82
+ end
83
+
84
+ # Cleans stopped containers
85
+ #
86
+ def self.clean_containers
87
+ sh "docker rm $(docker ps -a | grep 'Exited' | awk '{print $1}')"
88
+ rescue RuntimeError => e
89
+ # omit conflicting images error reports
90
+ raise unless e.to_s =~ /Command failed/
91
+ end
92
+
93
+ # Start container defined by params
94
+ #
95
+ def self.start(params)
96
+ opts = []
97
+ opts << params[:volumes].map do |v|
98
+ "-v #{v}"
99
+ end.join(' ')
100
+ opts << params[:ports].map do |p|
101
+ "-p #{p}"
102
+ end.join(' ')
103
+ opts << params[:environment].map do |k, v|
104
+ "-e #{k}=#{v}"
105
+ end.join(' ')
106
+ docker_cmd = "docker run -d #{opts.join(' ')} " \
107
+ " --hostname #{params[:name]} " \
108
+ " --name #{params[:name]} #{params[:image]} #{params[:command]}"
109
+ # puts docker_cmd
110
+ sh docker_cmd
111
+ end
112
+
113
+ # Image
114
+ #
115
+ class Image
116
+ attr_accessor :name, :path
117
+
118
+ def initialize(name, path)
119
+ @name = name.to_s
120
+ @path = path
121
+ end
122
+
123
+ def prebuilt?
124
+ self.class.prebuilt_image_names.include?(name)
125
+ end
126
+
127
+ def running?
128
+ !!running_container
129
+ end
130
+
131
+ def running_container
132
+ Docker.containers_running.select { |c| c.name == name }.first
133
+ end
134
+
135
+ def build!
136
+ sh "docker build -t #{name} #{path}"
137
+ end
138
+
139
+ def start!
140
+ Container.clean_containers(name)
141
+ run_flags = "--name=#{name} --hostname=#{name} -d"
142
+ sh "docker run #{run_flags} #{name}"
143
+ end
144
+
145
+ def stop!
146
+ running_container.stop!
147
+ end
148
+
149
+ def open_ssh_session!
150
+ running_container.open_ssh_session!
151
+ end
152
+
153
+ def use!
154
+ build! unless prebuilt?
155
+ Docker.images_used_add(name)
156
+ end
157
+
158
+ def unuse!
159
+ Docker.images_used_remove(name)
160
+ end
161
+
162
+ def used?
163
+ Docker.images_used.map(&:name).include?(name)
164
+ end
165
+
166
+ def self.prebuilt_image_names
167
+ return @prebuilt_image_names if @prebuilt_image_names
168
+ di = `docker images`
169
+ @prebuilt_image_names = di.split("\n").map do |line|
170
+ m = line.match(/^(\S*)\s*/)
171
+ m[1]
172
+ end
173
+ @prebuilt_image_names
174
+ end
175
+ end # class Image
176
+
177
+ # Container
178
+ #
179
+ class Container
180
+ attr_accessor :id, :long_id, :name, :image, :image_id, :ip, :ps, :ports, :params
181
+
182
+ def initialize(id)
183
+ @id = id
184
+ @ps = `docker ps | grep #{id}`
185
+ container_inspect = `docker inspect #{id}`
186
+ @params = JSON.parse(container_inspect).first
187
+ @long_id = params['Id']
188
+ @name = params['Name'].gsub('/', '')
189
+ @image = params['Config']['Image']
190
+ @image_id = params['Image']
191
+ @ip = params['NetworkSettings']['IPAddress']
192
+ port_maps = params['NetworkSettings']['Ports']
193
+ @ports = port_maps.keys.map do |port|
194
+ { from: port, to: port_maps[port] }
195
+ end
196
+ end
197
+
198
+ def open_ssh_session!
199
+ ssh_flags = "-oStrictHostKeyChecking=no -i #{INSECURE_KEY_PATH}"
200
+ sh "ssh #{ssh_flags} root@#{ip}"
201
+ end
202
+
203
+ def stop!
204
+ sh "docker stop #{name}"
205
+ clean!
206
+ end
207
+
208
+ def clean!
209
+ self.class.clean_containers(name)
210
+ end
211
+
212
+ def uptime
213
+ params['State'] && params['State']['StartedAt'] &&
214
+ Time.now.utc - DateTime.parse(params['State']['StartedAt']).to_time.utc
215
+ end
216
+
217
+ def self.clean_containers(*names)
218
+ names.each do |name|
219
+ begin
220
+ sh "docker rm #{name} >/dev/null 2>&1"
221
+ rescue RuntimeError => e
222
+ # omit reports on missing container
223
+ raise unless e.to_s =~ /Command failed/
224
+ end
225
+ end
226
+ end
227
+ end # class ContainerInfo
228
+ end # module Docker
229
+ end # module Jarl
@@ -0,0 +1,21 @@
1
+ require 'yaml'
2
+ require 'pathname'
3
+
4
+ module Jarl
5
+ class JarlFile
6
+ attr_reader :path, :name, :applications
7
+
8
+ def initialize(path)
9
+ @path = Pathname.new(path)
10
+ @name = @path.basename('.jarl')
11
+ load
12
+ end
13
+
14
+ private
15
+
16
+ def load
17
+ @applications = YAML.load File.read(path)
18
+ end
19
+
20
+ end # class JarlFile
21
+ end # module Jarl
@@ -0,0 +1,3 @@
1
+ module Jarl
2
+ VERSION = '0.2.0'
3
+ end
data/lib/jarl.rb ADDED
@@ -0,0 +1,15 @@
1
+ # require 'rubygems'
2
+ # require 'bundler/setup'
3
+ # Bundler.setup
4
+
5
+ require 'jarl/base'
6
+ require 'jarl/version'
7
+ require 'jarl/cli'
8
+ require 'jarl/application'
9
+ require 'jarl/jarl_file'
10
+ require 'jarl/docker'
11
+ require 'jarl/console'
12
+
13
+ include Console::Colors
14
+ include Console::Shell
15
+ include Console::Utils
@@ -0,0 +1,11 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require_relative '../lib/jarl'
5
+
6
+ RSpec.configure do |config|
7
+ config.expect_with :rspec do |c|
8
+ c.syntax = :expect
9
+ end
10
+ end
11
+
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jarl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Kukushkin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.19'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.19'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Jarl, docker container management tool
70
+ email:
71
+ - alex.kukushkin@cubits.com
72
+ executables:
73
+ - jarl
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/jarl
83
+ - jarl.gemspec
84
+ - lib/jarl.rb
85
+ - lib/jarl/application.rb
86
+ - lib/jarl/base.rb
87
+ - lib/jarl/cli.rb
88
+ - lib/jarl/console.rb
89
+ - lib/jarl/docker.rb
90
+ - lib/jarl/jarl_file.rb
91
+ - lib/jarl/version.rb
92
+ - spec/spec_helper.rb
93
+ homepage: ''
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.2.2
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Jarl, docker container management tool
117
+ test_files:
118
+ - spec/spec_helper.rb