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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5f658f6a2817ddf675c13d89d831c744dd27fcf
4
- data.tar.gz: 087abc1978c7461efc0e2b2154b321ad4c0c1285
3
+ metadata.gz: 970993e9185345d4b00ebacc7f220e3859530366
4
+ data.tar.gz: 65c05fe392e5b9b61860141927b71da5556258db
5
5
  SHA512:
6
- metadata.gz: abfa4bd3f766e2d50352076b32ab0d1e56151fddd19ea8c3c769e44d971e11eecc0148c6187c6077a0fff02b77befaa1e7567507467a1f40cf1a2ed2e2513735
7
- data.tar.gz: 1f397a352b7a36204e4ad6888235b7bf0b34d0d267b1084fbbd0d4bb6fc562bc22dd0078e92365d6308f9c2832216c655bd9431317694315317b06be772d5256
6
+ metadata.gz: 97aa717a7ae7d644ec8fce6a063c8bbf54b7cb4c2db911304236baa57e43162d3f839a903951cdfca4024bb84c9ad5f09cd66553d3dbe7cbabe9402c3767e4ad
7
+ data.tar.gz: 67c50f92173a2ca9e6a7c5187aaa0354fb9a4fad674937eaeed1e69d11b3f6ca4cd2b128e2175af1b1d5679755a6b01365d405187b8a4857e614409f03c334f1
data/bin/jarl CHANGED
@@ -2,4 +2,9 @@
2
2
 
3
3
  require_relative '../lib/jarl'
4
4
 
5
- Jarl::CLI.start
5
+ begin
6
+ Jarl::CLI.start
7
+ rescue StandardError => e
8
+ puts esc_red "FATAL: #{e}"
9
+ puts e.backtrace.join("\n") if ARGV.include?('--debug')
10
+ end
@@ -1,6 +1,7 @@
1
1
  module Jarl
2
2
  class Application
3
- attr_reader :group, :name, :image, :volumes, :ports, :environment, :entrypoint, :command
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
- puts "#{esc_yellow full_name}: #{str}" while str = p.gets
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.map { |app_name, params| Application.new(jf.name, app_name, params) }
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 'status [NAME]', 'Show applications statuses'
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]', '(Re)start applications'
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]', '(Re)build docker images'
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
@@ -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
- esc_string "\e[31m", text
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
- esc_string "\e[32m", text
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
- esc_string "\e[33m", text
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
- esc_string "\e[34m", text
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[:name]} " \
108
- " --name #{params[:name]} #{params[:image]} #{params[:command]}"
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 = "-oStrictHostKeyChecking=no -i #{INSECURE_KEY_PATH}"
200
- sh "ssh #{ssh_flags} root@#{ip}"
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!
@@ -3,18 +3,23 @@ require 'pathname'
3
3
 
4
4
  module Jarl
5
5
  class JarlFile
6
- attr_reader :path, :name, :applications
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
- load
11
+ end
12
+
13
+ def applications
14
+ @applications ||= load_applications
12
15
  end
13
16
 
14
17
  private
15
18
 
16
- def load
17
- @applications = YAML.load File.read(path)
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
@@ -1,3 +1,3 @@
1
1
  module Jarl
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/jarl.rb CHANGED
@@ -5,6 +5,7 @@
5
5
  require 'jarl/base'
6
6
  require 'jarl/version'
7
7
  require 'jarl/cli'
8
+ require 'jarl/config'
8
9
  require 'jarl/application'
9
10
  require 'jarl/jarl_file'
10
11
  require 'jarl/docker'
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.2.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-03 00:00:00.000000000 Z
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