jarl 0.2.0 → 0.3.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 +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
|