jarl 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/jarl +6 -1
- data/lib/jarl/application.rb +35 -2
- data/lib/jarl/base.rb +19 -2
- data/lib/jarl/cli.rb +46 -21
- data/lib/jarl/config.rb +23 -0
- data/lib/jarl/console.rb +11 -4
- data/lib/jarl/docker.rb +36 -5
- data/lib/jarl/jarl_file.rb +10 -5
- data/lib/jarl/version.rb +1 -1
- data/lib/jarl.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 970993e9185345d4b00ebacc7f220e3859530366
|
4
|
+
data.tar.gz: 65c05fe392e5b9b61860141927b71da5556258db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97aa717a7ae7d644ec8fce6a063c8bbf54b7cb4c2db911304236baa57e43162d3f839a903951cdfca4024bb84c9ad5f09cd66553d3dbe7cbabe9402c3767e4ad
|
7
|
+
data.tar.gz: 67c50f92173a2ca9e6a7c5187aaa0354fb9a4fad674937eaeed1e69d11b3f6ca4cd2b128e2175af1b1d5679755a6b01365d405187b8a4857e614409f03c334f1
|
data/bin/jarl
CHANGED
data/lib/jarl/application.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Jarl
|
2
2
|
class Application
|
3
|
-
attr_reader :group, :name, :
|
3
|
+
attr_reader :group, :name, :hostname
|
4
|
+
attr_reader :image, :volumes, :ports, :environment, :entrypoint, :command
|
4
5
|
|
5
6
|
# Create Application object from application definition:
|
6
7
|
#
|
@@ -39,6 +40,8 @@ module Jarl
|
|
39
40
|
if @command && !@command.is_a?(String)
|
40
41
|
fail "Application '#{self}' has invalid 'command' definition, String is expected"
|
41
42
|
end
|
43
|
+
@serial = self.class.next_serial
|
44
|
+
@hostname = @name
|
42
45
|
end
|
43
46
|
|
44
47
|
def full_name
|
@@ -61,6 +64,7 @@ module Jarl
|
|
61
64
|
running_container.stop! if running?
|
62
65
|
Docker.start(
|
63
66
|
name: full_name,
|
67
|
+
hostname: hostname,
|
64
68
|
image: image_name,
|
65
69
|
volumes: volumes,
|
66
70
|
ports: ports,
|
@@ -69,6 +73,23 @@ module Jarl
|
|
69
73
|
)
|
70
74
|
end
|
71
75
|
|
76
|
+
def execute(execute_command, args)
|
77
|
+
Docker.execute(
|
78
|
+
name: full_name,
|
79
|
+
hostname: hostname,
|
80
|
+
image: image_name,
|
81
|
+
volumes: volumes,
|
82
|
+
ports: ports,
|
83
|
+
environment: environment,
|
84
|
+
command: (execute_command || command || '') + ' ' + args.join(' ')
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def ssh
|
89
|
+
fail 'Not a running application' unless running?
|
90
|
+
running_container.open_ssh_session!(Jarl.config.params)
|
91
|
+
end
|
92
|
+
|
72
93
|
def build
|
73
94
|
fail 'Not a file based image' unless image_is_a_path?
|
74
95
|
Docker::Image.new(image_name, image_path).build!
|
@@ -83,11 +104,17 @@ module Jarl
|
|
83
104
|
end
|
84
105
|
|
85
106
|
def show_log(*_args)
|
107
|
+
color_code = Console::Colors::SEQUENCE[@serial % Console::Colors::SEQUENCE.size]
|
86
108
|
return unless running?
|
87
109
|
begin
|
88
110
|
IO.popen "docker logs -f #{running_container.id}", 'r' do |p|
|
89
111
|
str = '<<< STARTED >>>'
|
90
|
-
|
112
|
+
while str
|
113
|
+
str = p.gets
|
114
|
+
str.split("\n").each do |s|
|
115
|
+
puts "#{esc_color color_code, full_name}: #{s}"
|
116
|
+
end
|
117
|
+
end
|
91
118
|
end
|
92
119
|
rescue Interrupt
|
93
120
|
#
|
@@ -97,5 +124,11 @@ module Jarl
|
|
97
124
|
def to_s
|
98
125
|
full_name
|
99
126
|
end
|
127
|
+
|
128
|
+
def self.next_serial
|
129
|
+
@current_serial ||= 0
|
130
|
+
@current_serial += 1
|
131
|
+
@current_serial - 1
|
132
|
+
end
|
100
133
|
end # class Application
|
101
134
|
end # module Jarl
|
data/lib/jarl/base.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
module Jarl
|
2
|
+
DEFAULT_JARL_CONFIG_PATH = '~/.jarl'
|
3
|
+
|
2
4
|
#
|
3
5
|
# Load config and application definitions
|
4
6
|
#
|
5
|
-
def self.load
|
7
|
+
def self.load(options)
|
8
|
+
load_config(options['config'] || DEFAULT_JARL_CONFIG_PATH)
|
9
|
+
# puts "Jarl::CLI.options: #{options}"
|
6
10
|
abort 'Docker is not installed' unless Docker.installed?
|
7
11
|
if Jarl.jarl_files.empty?
|
8
12
|
abort 'No *.jarl files found'
|
@@ -12,6 +16,13 @@ module Jarl
|
|
12
16
|
true
|
13
17
|
end
|
14
18
|
|
19
|
+
#
|
20
|
+
# Returns current config
|
21
|
+
#
|
22
|
+
def self.config
|
23
|
+
@config
|
24
|
+
end
|
25
|
+
|
15
26
|
#
|
16
27
|
# Returns list of found *.jarl files
|
17
28
|
#
|
@@ -24,7 +35,7 @@ module Jarl
|
|
24
35
|
def self.applications
|
25
36
|
@applications ||= jarl_files.map do |jf|
|
26
37
|
begin
|
27
|
-
jf.applications
|
38
|
+
jf.applications
|
28
39
|
rescue StandardError => e
|
29
40
|
abort "Failed to parse application definition in file '#{jf.path}': #{e}"
|
30
41
|
end
|
@@ -39,6 +50,12 @@ module Jarl
|
|
39
50
|
|
40
51
|
private
|
41
52
|
|
53
|
+
def self.load_config(filename)
|
54
|
+
@config = Config.new(filename)
|
55
|
+
rescue StandardError => e
|
56
|
+
raise "Failed to load config file '#{filename}': #{e}"
|
57
|
+
end
|
58
|
+
|
42
59
|
def self.load_jarl_files
|
43
60
|
Pathname.glob(Pathname.pwd + '**' + '*.jarl').map do |p|
|
44
61
|
JarlFile.new(p)
|
data/lib/jarl/cli.rb
CHANGED
@@ -8,20 +8,39 @@ module Jarl
|
|
8
8
|
aliases: '-c',
|
9
9
|
type: :string,
|
10
10
|
desc: 'Use specified config file (defaul: ~/.jarl)'
|
11
|
+
class_option :debug
|
11
12
|
|
12
13
|
#
|
13
|
-
desc '
|
14
|
+
desc 'config', 'Show current configuration'
|
15
|
+
def config
|
16
|
+
Jarl.load(options)
|
17
|
+
puts esc_yellow('config:') + " #{Jarl.config.path}"
|
18
|
+
Jarl.config.params.each do |key, value|
|
19
|
+
puts " #{key}: #{value.inspect}"
|
20
|
+
end
|
21
|
+
puts esc_yellow('jarl files:')
|
22
|
+
Jarl.jarl_files.each do |jarl_file|
|
23
|
+
puts " #{jarl_file.path}"
|
24
|
+
jarl_file.applications.each do |app|
|
25
|
+
puts " #{app.name}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# show_jarl_applications_status(apps)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
desc 'status [NAME]', '(default) Show applications statuses'
|
14
33
|
def status(name = nil)
|
15
|
-
Jarl.load
|
34
|
+
Jarl.load(options)
|
16
35
|
apps = name ? Jarl.find_applications_by(name) : Jarl.applications
|
17
36
|
show_jarl_applications_status(apps)
|
18
37
|
end
|
19
38
|
default_task :status
|
20
39
|
|
21
40
|
#
|
22
|
-
desc 'up [NAME]', '
|
41
|
+
desc 'up [NAME]', 'Start or restart applications'
|
23
42
|
def up(name = nil)
|
24
|
-
Jarl.load
|
43
|
+
Jarl.load(options)
|
25
44
|
apps = name ? Jarl.find_applications_by(name) : Jarl.applications
|
26
45
|
apps.each do |app|
|
27
46
|
puts esc_green(app.running? ? "Restarting '#{app}'..." : "Starting '#{app}'...")
|
@@ -33,7 +52,7 @@ module Jarl
|
|
33
52
|
#
|
34
53
|
desc 'down [NAME]', 'Stop applications'
|
35
54
|
def down(name = nil)
|
36
|
-
Jarl.load
|
55
|
+
Jarl.load(options)
|
37
56
|
apps = name ? Jarl.find_applications_by(name) : Jarl.applications
|
38
57
|
apps.each do |app|
|
39
58
|
next unless app.running?
|
@@ -42,11 +61,29 @@ module Jarl
|
|
42
61
|
end
|
43
62
|
end
|
44
63
|
|
64
|
+
desc 'execute NAME [COMMAND ...]', 'Run a single command against application image'
|
65
|
+
def execute(name, command = nil, *args)
|
66
|
+
Jarl.load(options)
|
67
|
+
app = Jarl.find_applications_by(name).first
|
68
|
+
abort "Failed to find application by name: '#{name}'" unless app
|
69
|
+
app.execute(command, args)
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
desc 'ssh NAME', 'Open SSH session to a running application'
|
74
|
+
def ssh(name)
|
75
|
+
Jarl.load(options)
|
76
|
+
app = Jarl.find_applications_by(name).first
|
77
|
+
abort "Failed to find application by name: '#{name}'" unless app
|
78
|
+
abort "Application is not running: '#{app.full_name}'" unless app.running?
|
79
|
+
app.ssh
|
80
|
+
end
|
81
|
+
|
45
82
|
#
|
46
83
|
option :ip, type: :boolean, desc: 'Show only IPs'
|
47
84
|
desc 'inspect [NAME]', 'Inspect applications'
|
48
85
|
def inspect(name = nil)
|
49
|
-
Jarl.load
|
86
|
+
Jarl.load(options)
|
50
87
|
apps = name ? Jarl.find_applications_by(name) : Jarl.applications
|
51
88
|
apps.each do |app|
|
52
89
|
if options[:ip]
|
@@ -57,9 +94,9 @@ module Jarl
|
|
57
94
|
end
|
58
95
|
end
|
59
96
|
|
60
|
-
desc 'build [NAME]', '
|
97
|
+
desc 'build [NAME]', 'Rebuild docker images for Dockerfile based applications'
|
61
98
|
def build(name = nil)
|
62
|
-
Jarl.load
|
99
|
+
Jarl.load(options)
|
63
100
|
apps = name ? Jarl.find_applications_by(name) : Jarl.applications
|
64
101
|
apps.select(&:image_is_a_path?).each do |app|
|
65
102
|
puts esc_yellow "Building image for '#{app}'..."
|
@@ -69,7 +106,7 @@ module Jarl
|
|
69
106
|
|
70
107
|
desc 'logs [NAME]', 'Show log output for applications'
|
71
108
|
def logs(name = nil)
|
72
|
-
Jarl.load
|
109
|
+
Jarl.load(options)
|
73
110
|
apps = name ? Jarl.find_applications_by(name) : Jarl.applications
|
74
111
|
threads = []
|
75
112
|
apps.select(&:running?).each do |app|
|
@@ -106,7 +143,6 @@ module Jarl
|
|
106
143
|
['UPTIME', 12],
|
107
144
|
['IP', 12],
|
108
145
|
['PORTS']
|
109
|
-
# ['IMAGE']
|
110
146
|
)
|
111
147
|
apps.each do |app|
|
112
148
|
container = app.running_container
|
@@ -116,17 +152,8 @@ module Jarl
|
|
116
152
|
(app.running? ? [seconds_to_human(container.uptime), 12, :yellow] : ['', 12]),
|
117
153
|
(app.running? ? [container.ip, 12, :yellow] : ['', 12]),
|
118
154
|
(app.running? ? [container.ports.map { |p| "#{p[:from]}"}.join(', ')] : [''])
|
119
|
-
|
120
|
-
# (app.image_is_a_path? ? ["path: #{app.image}"] : [app.image])
|
121
155
|
)
|
122
156
|
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
157
|
end
|
131
158
|
|
132
159
|
def show_jarl_application_inspect(app)
|
@@ -143,8 +170,6 @@ module Jarl
|
|
143
170
|
puts " IP: #{esc_yellow container.ip}"
|
144
171
|
puts " ports: #{esc_yellow container.ports.map { |p| "#{p[:from]}->#{p[:to]}"}.join(', ')}"
|
145
172
|
puts " volumes: #{esc_yellow container.params['Volumes']}"
|
146
|
-
# puts " ps: #{esc_yellow container.ps}"
|
147
|
-
# puts " params: #{esc_yellow container.params}"
|
148
173
|
end
|
149
174
|
end # class CLI
|
150
175
|
end # module Jarl
|
data/lib/jarl/config.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Jarl
|
5
|
+
class Config
|
6
|
+
attr_reader :path, :params
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = Pathname.new(path).expand_path
|
10
|
+
load
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
@params[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def load
|
20
|
+
@params = YAML.load File.read(path)
|
21
|
+
end
|
22
|
+
end # class Config
|
23
|
+
end # module Jarl
|
data/lib/jarl/console.rb
CHANGED
@@ -25,34 +25,41 @@ module Console
|
|
25
25
|
end # module Utils
|
26
26
|
|
27
27
|
module Colors
|
28
|
+
SEQUENCE = %w(31 32 33 34 35 36 37 31;1 32;1 33;1 34;1 35;1 36;1 37;1)
|
28
29
|
# Returns ANSI escaped string.
|
29
30
|
#
|
30
31
|
def esc_string(esc, text)
|
31
32
|
esc + text.to_s + "\e[0m"
|
32
33
|
end
|
33
34
|
|
35
|
+
# Returns ANSI escaped string for the colored text.
|
36
|
+
#
|
37
|
+
def esc_color(color_code, text)
|
38
|
+
esc_string "\e[#{color_code}m", text
|
39
|
+
end
|
40
|
+
|
34
41
|
# Returns ANSI escaped string for the red colored text.
|
35
42
|
#
|
36
43
|
def esc_red(text)
|
37
|
-
|
44
|
+
esc_color 31, text
|
38
45
|
end
|
39
46
|
|
40
47
|
# Returns ANSI escaped string for the green colored text.
|
41
48
|
#
|
42
49
|
def esc_green(text)
|
43
|
-
|
50
|
+
esc_color 32, text
|
44
51
|
end
|
45
52
|
|
46
53
|
# Returns ANSI escaped string for the yellow colored text.
|
47
54
|
#
|
48
55
|
def esc_yellow(text)
|
49
|
-
|
56
|
+
esc_color 33, text
|
50
57
|
end
|
51
58
|
|
52
59
|
# Returns ANSI escaped string for the blue colored text.
|
53
60
|
#
|
54
61
|
def esc_blue(text)
|
55
|
-
|
62
|
+
esc_color 34, text
|
56
63
|
end
|
57
64
|
|
58
65
|
# Returns ANSI escaped string for the bold text.
|
data/lib/jarl/docker.rb
CHANGED
@@ -93,6 +93,8 @@ module Docker
|
|
93
93
|
# Start container defined by params
|
94
94
|
#
|
95
95
|
def self.start(params)
|
96
|
+
container_name = params[:name]
|
97
|
+
Docker::Container.clean_containers(container_name)
|
96
98
|
opts = []
|
97
99
|
opts << params[:volumes].map do |v|
|
98
100
|
"-v #{v}"
|
@@ -104,8 +106,30 @@ module Docker
|
|
104
106
|
"-e #{k}=#{v}"
|
105
107
|
end.join(' ')
|
106
108
|
docker_cmd = "docker run -d #{opts.join(' ')} " \
|
107
|
-
" --hostname #{params[:
|
108
|
-
" --name #{
|
109
|
+
" --hostname #{params[:hostname]} " \
|
110
|
+
" --name #{container_name} #{params[:image]} #{params[:command]}"
|
111
|
+
# puts docker_cmd
|
112
|
+
sh docker_cmd
|
113
|
+
end
|
114
|
+
|
115
|
+
# Execute container in foreground, defined by params
|
116
|
+
#
|
117
|
+
def self.execute(params)
|
118
|
+
container_name = "#{params[:name]}.execute"
|
119
|
+
Docker::Container.clean_containers(container_name)
|
120
|
+
opts = []
|
121
|
+
opts << params[:volumes].map do |v|
|
122
|
+
"-v #{v}"
|
123
|
+
end.join(' ')
|
124
|
+
opts << params[:ports].map do |p|
|
125
|
+
"-p #{p}"
|
126
|
+
end.join(' ')
|
127
|
+
opts << params[:environment].map do |k, v|
|
128
|
+
"-e #{k}=#{v}"
|
129
|
+
end.join(' ')
|
130
|
+
docker_cmd = "docker run -t -i --rm #{opts.join(' ')} " \
|
131
|
+
" --hostname #{params[:hostname]} " \
|
132
|
+
" --name #{container_name} #{params[:image]} #{params[:command]}"
|
109
133
|
# puts docker_cmd
|
110
134
|
sh docker_cmd
|
111
135
|
end
|
@@ -195,9 +219,16 @@ module Docker
|
|
195
219
|
end
|
196
220
|
end
|
197
221
|
|
198
|
-
def open_ssh_session!
|
199
|
-
ssh_flags =
|
200
|
-
|
222
|
+
def open_ssh_session!(params = {})
|
223
|
+
ssh_flags = ['-oStrictHostKeyChecking=no']
|
224
|
+
if params['ssh_identity']
|
225
|
+
ssh_flags << "-i #{params['ssh_identity']}" if params['ssh_identity'].is_a?(String)
|
226
|
+
if params['ssh_identity'].is_a?(Array)
|
227
|
+
ssh_flags << params['ssh_identity'].map { |i| "-i #{i}" }
|
228
|
+
end
|
229
|
+
end
|
230
|
+
ssh_user = params['ssh_user'] ? "#{params['ssh_user']}@" : ''
|
231
|
+
sh "ssh #{ssh_flags.join(' ')} #{ssh_user}#{ip}"
|
201
232
|
end
|
202
233
|
|
203
234
|
def stop!
|
data/lib/jarl/jarl_file.rb
CHANGED
@@ -3,18 +3,23 @@ require 'pathname'
|
|
3
3
|
|
4
4
|
module Jarl
|
5
5
|
class JarlFile
|
6
|
-
attr_reader :path, :name
|
6
|
+
attr_reader :path, :name
|
7
7
|
|
8
8
|
def initialize(path)
|
9
|
-
@path = Pathname.new(path)
|
9
|
+
@path = Pathname.new(path).expand_path
|
10
10
|
@name = @path.basename('.jarl')
|
11
|
-
|
11
|
+
end
|
12
|
+
|
13
|
+
def applications
|
14
|
+
@applications ||= load_applications
|
12
15
|
end
|
13
16
|
|
14
17
|
private
|
15
18
|
|
16
|
-
def
|
17
|
-
|
19
|
+
def load_applications
|
20
|
+
YAML.load(File.read(path)).map do |app_name, params|
|
21
|
+
Application.new(name, app_name, params)
|
22
|
+
end
|
18
23
|
end
|
19
24
|
|
20
25
|
end # class JarlFile
|
data/lib/jarl/version.rb
CHANGED
data/lib/jarl.rb
CHANGED
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.
|
4
|
+
version: 0.3.0
|
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-01-
|
11
|
+
date: 2015-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- lib/jarl/application.rb
|
86
86
|
- lib/jarl/base.rb
|
87
87
|
- lib/jarl/cli.rb
|
88
|
+
- lib/jarl/config.rb
|
88
89
|
- lib/jarl/console.rb
|
89
90
|
- lib/jarl/docker.rb
|
90
91
|
- lib/jarl/jarl_file.rb
|