jarl 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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