dolphin 0.0.2 → 0.0.3
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/lib/dolphin.rb +4 -180
- data/lib/dolphin/base.rb +167 -0
- data/lib/dolphin/deploy.rb +49 -0
- data/lib/dolphin/lock.rb +29 -0
- data/lib/dolphin/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64b7b1836dabf66d97b69fe469e1f8f4033f79e1
|
4
|
+
data.tar.gz: 83df23c962252ad8e9410219d8a8a69fb0be9229
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1cbff0b4fe5d73c500a6b99b7ef944083407fef7c4670c3d9adc9b24b369aa6a7087223d313a941ebc56fce142071bf5a6c0bebecbed1e2310480b0e8ce255c5
|
7
|
+
data.tar.gz: 7fb173b1c59804e27891accfba0a29e25f4485afaad496b669abaf7080ac54f50d3215013651bd1e97be282623ceb33730e9499c38e3b83d3f28406d30aed0fb
|
data/lib/dolphin.rb
CHANGED
@@ -1,143 +1,9 @@
|
|
1
|
-
require "thor"
|
2
|
-
require 'net/ssh'
|
3
|
-
require 'parallel'
|
4
|
-
|
5
1
|
require_relative "dolphin/version"
|
2
|
+
require_relative "dolphin/base"
|
3
|
+
require_relative "dolphin/lock"
|
4
|
+
require_relative "dolphin/deploy"
|
6
5
|
|
7
6
|
module Dolphin
|
8
|
-
# core functions
|
9
|
-
class Base < Thor
|
10
|
-
include Thor::Actions
|
11
|
-
|
12
|
-
# =============================================================================
|
13
|
-
# class options
|
14
|
-
# =============================================================================
|
15
|
-
|
16
|
-
class_option :env, :aliases => '-e', :type => :string, :default => 'alpha'
|
17
|
-
|
18
|
-
def initialize(args=[], options={}, config={})
|
19
|
-
super(args, options, config)
|
20
|
-
# set up environment
|
21
|
-
env
|
22
|
-
end
|
23
|
-
|
24
|
-
# =============================================================================
|
25
|
-
# private functions
|
26
|
-
# =============================================================================
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
# deployment environment
|
31
|
-
def env
|
32
|
-
# placeholder, to be implemented in each project
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def parse_commands(menu)
|
37
|
-
# Helper method to parse a list of text menu of possible commands,
|
38
|
-
# which may contain empty lines or commented out with #
|
39
|
-
# commands can be separated into groups
|
40
|
-
|
41
|
-
commands = []
|
42
|
-
menu.each do |group|
|
43
|
-
buffer = []
|
44
|
-
group.split(/\r?\n/).each do |line|
|
45
|
-
line = line.strip
|
46
|
-
unless line.empty? or line.start_with?('#') # empty or commented out
|
47
|
-
buffer << line
|
48
|
-
end
|
49
|
-
end
|
50
|
-
commands.push(*buffer)
|
51
|
-
end
|
52
|
-
commands
|
53
|
-
end
|
54
|
-
|
55
|
-
def ssh_connection(server)
|
56
|
-
@sessions[server] ||= begin
|
57
|
-
ssh = Net::SSH.start(server, @user, )
|
58
|
-
at_exit { ssh.close }
|
59
|
-
ssh
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def execute(menu)
|
64
|
-
# execute commands defined in menu
|
65
|
-
commands = parse_commands(menu)
|
66
|
-
puts "#{'*'*10}Executing commands#{'*'*10}\n"
|
67
|
-
commands.each do |command|
|
68
|
-
puts "#{command}\n"
|
69
|
-
end
|
70
|
-
puts "#{'='*60}\n"
|
71
|
-
|
72
|
-
# use Parallel to execute commands on multiple servers in parallel
|
73
|
-
tracks = @servers.size
|
74
|
-
# 3 threads maximum
|
75
|
-
tracks = 3 if tracks > 3
|
76
|
-
# record output to display at the end
|
77
|
-
output = {}
|
78
|
-
|
79
|
-
Parallel.map(@servers, in_threads: tracks) do |server|
|
80
|
-
session = ssh_connection(server)
|
81
|
-
output[server] = [] # output from this server
|
82
|
-
|
83
|
-
channel = session.open_channel do |chan|
|
84
|
-
chan.send_channel_request "shell" do |ch, success|
|
85
|
-
# chan.request_pty do |ch, success|
|
86
|
-
raise "could not start user shell" unless success
|
87
|
-
|
88
|
-
# normal output
|
89
|
-
ch.on_data do |c, data|
|
90
|
-
msg = "[output]: #{data}"
|
91
|
-
puts "#{server} => #{msg}"
|
92
|
-
output[server] << msg
|
93
|
-
end
|
94
|
-
|
95
|
-
# error message
|
96
|
-
ch.on_extended_data do |c, type, data|
|
97
|
-
msg = "[error]: #{data}"
|
98
|
-
puts "#{server} => #{msg}"
|
99
|
-
output[server] << msg
|
100
|
-
end
|
101
|
-
|
102
|
-
# exit code
|
103
|
-
ch.on_request "exit-status" do |c, data|
|
104
|
-
msg = "[exit]: #{data.read_long}\n"
|
105
|
-
puts "#{server} => #{msg}"
|
106
|
-
output[server] << msg
|
107
|
-
end
|
108
|
-
|
109
|
-
# has to explicitly call shell startup script
|
110
|
-
ch.send_data "source ~/.bash_profile\n"
|
111
|
-
|
112
|
-
# pick up ruby
|
113
|
-
ch.send_data "chruby #{@ruby_version}\n"
|
114
|
-
|
115
|
-
# Output each command as if they were entered on the command line
|
116
|
-
commands.each do |command|
|
117
|
-
ch.send_data "#{command}\n"
|
118
|
-
end
|
119
|
-
|
120
|
-
# Remember to exit or we'll hang!
|
121
|
-
ch.send_data "exit\n"
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
# Wait for everything to complete
|
127
|
-
channel.wait
|
128
|
-
end
|
129
|
-
|
130
|
-
# puts output
|
131
|
-
puts "\n#{'*'*10}Results Review#{'*'*10}\n"
|
132
|
-
output.each do |server, data|
|
133
|
-
puts "\n#{'='*60}\n"
|
134
|
-
puts "Executing on [#{server}] =>\n"
|
135
|
-
data.each {|line| puts line}
|
136
|
-
end
|
137
|
-
puts "\n\n"
|
138
|
-
end
|
139
|
-
|
140
|
-
end
|
141
7
|
|
142
8
|
# =============================================================================
|
143
9
|
# Setup
|
@@ -345,49 +211,6 @@ module Dolphin
|
|
345
211
|
|
346
212
|
end
|
347
213
|
|
348
|
-
# =============================================================================
|
349
|
-
# Deploy
|
350
|
-
# =============================================================================
|
351
|
-
class Deploy < Base
|
352
|
-
desc "bundle", "sudo bundle install"
|
353
|
-
def bundle
|
354
|
-
menu = [
|
355
|
-
"
|
356
|
-
cd #{@deploy_dir}
|
357
|
-
sudo bundle install --quiet
|
358
|
-
",
|
359
|
-
]
|
360
|
-
|
361
|
-
execute menu
|
362
|
-
end
|
363
|
-
|
364
|
-
desc "go", "normal deploy procedure"
|
365
|
-
def go
|
366
|
-
# update code
|
367
|
-
invoke "dolphin:git:update"
|
368
|
-
|
369
|
-
# no need to invoke since it is within the same class
|
370
|
-
bundle
|
371
|
-
|
372
|
-
# restart app server
|
373
|
-
invoke "dolphin:puma:restart"
|
374
|
-
end
|
375
|
-
|
376
|
-
desc "try", "normal deploy procedure"
|
377
|
-
def try
|
378
|
-
menu = [
|
379
|
-
"
|
380
|
-
cd #{@deploy_dir}
|
381
|
-
pwd
|
382
|
-
bundle check
|
383
|
-
",
|
384
|
-
]
|
385
|
-
|
386
|
-
execute menu
|
387
|
-
end
|
388
|
-
|
389
|
-
end
|
390
|
-
|
391
214
|
# =============================================================================
|
392
215
|
# Git
|
393
216
|
# =============================================================================
|
@@ -463,6 +286,7 @@ module Dolphin
|
|
463
286
|
register(Puma, 'puma', 'puma', 'Puma related commands')
|
464
287
|
register(Nginx, 'nginx', 'nginx', 'Nginx related commands')
|
465
288
|
register(Git, 'git', 'git', 'Git related commands')
|
289
|
+
register(Lock, 'lock', 'lock', 'Lock resource to avoid simultaneous deployments')
|
466
290
|
end
|
467
291
|
|
468
292
|
end
|
data/lib/dolphin/base.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require "thor"
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'parallel'
|
4
|
+
|
5
|
+
# core functions
|
6
|
+
class Dolphin::Base < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
# =============================================================================
|
10
|
+
# class options
|
11
|
+
# =============================================================================
|
12
|
+
|
13
|
+
class_option :env, :aliases => '-e', :type => :string, :default => 'alpha'
|
14
|
+
|
15
|
+
def initialize(args=[], options={}, config={})
|
16
|
+
super(args, options, config)
|
17
|
+
# set up environment
|
18
|
+
env
|
19
|
+
end
|
20
|
+
|
21
|
+
# =============================================================================
|
22
|
+
# private functions
|
23
|
+
# =============================================================================
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# deployment environment
|
28
|
+
def env
|
29
|
+
# placeholder, to be implemented in each project
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_commands(menu)
|
34
|
+
# Helper method to parse a list of text menu of possible commands,
|
35
|
+
# which may contain empty lines or commented out with #
|
36
|
+
# commands can be separated into groups
|
37
|
+
|
38
|
+
commands = []
|
39
|
+
menu.each do |group|
|
40
|
+
buffer = []
|
41
|
+
group.split(/\r?\n/).each do |line|
|
42
|
+
line = line.strip
|
43
|
+
unless line.empty? or line.start_with?('#') # empty or commented out
|
44
|
+
buffer << line
|
45
|
+
end
|
46
|
+
end
|
47
|
+
commands.push(*buffer)
|
48
|
+
end
|
49
|
+
commands
|
50
|
+
end
|
51
|
+
|
52
|
+
def ssh_connection(server)
|
53
|
+
@sessions[server] ||= begin
|
54
|
+
ssh = Net::SSH.start(server, @user, )
|
55
|
+
at_exit { ssh.close }
|
56
|
+
ssh
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def capture(command, server)
|
61
|
+
# capture output from one target server
|
62
|
+
output = ''
|
63
|
+
session = ssh_connection(server)
|
64
|
+
|
65
|
+
channel = session.open_channel do |chan|
|
66
|
+
chan.exec(command) do |ch, success|
|
67
|
+
|
68
|
+
ch.on_data do |c, data|
|
69
|
+
output << data
|
70
|
+
end
|
71
|
+
|
72
|
+
ch.on_extended_data do |c, type, data|
|
73
|
+
output << data
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
channel.wait
|
80
|
+
output
|
81
|
+
end
|
82
|
+
|
83
|
+
def execute(menu, target_server=nil)
|
84
|
+
# execute commands defined in menu, when :target_server is passed in, only execute on this server
|
85
|
+
commands = parse_commands(menu)
|
86
|
+
puts "#{'*'*10}Executing commands#{'*'*10}\n"
|
87
|
+
commands.each do |command|
|
88
|
+
puts "#{command}\n"
|
89
|
+
end
|
90
|
+
puts "#{'='*60}\n"
|
91
|
+
|
92
|
+
if target_server # solo
|
93
|
+
tracks = 1
|
94
|
+
target = [target_server]
|
95
|
+
else
|
96
|
+
# use Parallel to execute commands on multiple servers in parallel
|
97
|
+
tracks = @servers.size
|
98
|
+
# 3 threads maximum
|
99
|
+
tracks = 3 if tracks > 3
|
100
|
+
target = @servers
|
101
|
+
end
|
102
|
+
|
103
|
+
# record output to display at the end
|
104
|
+
output = {}
|
105
|
+
|
106
|
+
Parallel.map(target, in_threads: tracks) do |server|
|
107
|
+
session = ssh_connection(server)
|
108
|
+
output[server] = [] # output from this server
|
109
|
+
|
110
|
+
channel = session.open_channel do |chan|
|
111
|
+
chan.send_channel_request "shell" do |ch, success|
|
112
|
+
# chan.request_pty do |ch, success|
|
113
|
+
raise "could not start user shell" unless success
|
114
|
+
|
115
|
+
# normal output
|
116
|
+
ch.on_data do |c, data|
|
117
|
+
msg = "[output]: #{data}"
|
118
|
+
puts "#{server} => #{msg}"
|
119
|
+
output[server] << msg
|
120
|
+
end
|
121
|
+
|
122
|
+
# error message
|
123
|
+
ch.on_extended_data do |c, type, data|
|
124
|
+
msg = "[error]: #{data}"
|
125
|
+
puts "#{server} => #{msg}"
|
126
|
+
output[server] << msg
|
127
|
+
end
|
128
|
+
|
129
|
+
# exit code
|
130
|
+
ch.on_request "exit-status" do |c, data|
|
131
|
+
msg = "[exit]: #{data.read_long}\n"
|
132
|
+
puts "#{server} => #{msg}"
|
133
|
+
output[server] << msg
|
134
|
+
end
|
135
|
+
|
136
|
+
# has to explicitly call shell startup script
|
137
|
+
ch.send_data "source ~/.bash_profile\n"
|
138
|
+
|
139
|
+
# pick up ruby
|
140
|
+
ch.send_data "chruby #{@ruby_version}\n"
|
141
|
+
|
142
|
+
# Output each command as if they were entered on the command line
|
143
|
+
commands.each do |command|
|
144
|
+
ch.send_data "#{command}\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Remember to exit or we'll hang!
|
148
|
+
ch.send_data "exit\n"
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Wait for everything to complete
|
154
|
+
channel.wait
|
155
|
+
end
|
156
|
+
|
157
|
+
# puts output
|
158
|
+
puts "\n#{'*'*10}Results Review#{'*'*10}\n"
|
159
|
+
output.each do |server, data|
|
160
|
+
puts "\n#{'='*60}\n"
|
161
|
+
puts "Executing on [#{server}] =>\n"
|
162
|
+
data.each {|line| puts line}
|
163
|
+
end
|
164
|
+
puts "\n\n"
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Deploy tasks
|
2
|
+
class Dolphin::Deploy < Dolphin::Base
|
3
|
+
|
4
|
+
desc "bundle", "sudo bundle install"
|
5
|
+
def bundle
|
6
|
+
menu = [
|
7
|
+
"
|
8
|
+
cd #{@deploy_dir}
|
9
|
+
sudo bundle install --quiet
|
10
|
+
",
|
11
|
+
]
|
12
|
+
|
13
|
+
execute menu
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "go", "normal deploy procedure"
|
17
|
+
def go
|
18
|
+
# check lock
|
19
|
+
invoke "dolphin:lock:check"
|
20
|
+
# put lock
|
21
|
+
invoke "dolphin:lock:create"
|
22
|
+
|
23
|
+
# update code
|
24
|
+
invoke "dolphin:git:update"
|
25
|
+
|
26
|
+
# no need to invoke since it is within the same class
|
27
|
+
bundle
|
28
|
+
|
29
|
+
# restart app server
|
30
|
+
invoke "dolphin:puma:restart"
|
31
|
+
|
32
|
+
# remove lock
|
33
|
+
invoke "dolphin:lock:release"
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "try", "normal deploy procedure"
|
37
|
+
def try
|
38
|
+
menu = [
|
39
|
+
"
|
40
|
+
cd #{@deploy_dir}
|
41
|
+
pwd
|
42
|
+
bundle check
|
43
|
+
",
|
44
|
+
]
|
45
|
+
|
46
|
+
execute menu
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/lib/dolphin/lock.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Use lock to avoid simultaneous deployments
|
2
|
+
class Dolphin::Lock < Dolphin::Base
|
3
|
+
|
4
|
+
desc "check", "Check lock"
|
5
|
+
def check
|
6
|
+
command = "if [ -e #{@lock_file} ]; then cat #{@lock_file}; fi"
|
7
|
+
output = capture(command, @lead_server)
|
8
|
+
if output.empty?
|
9
|
+
puts "OK to proceed"
|
10
|
+
else
|
11
|
+
puts "[output]: #{output}"
|
12
|
+
abort "\e[0;31m A deployment is already in progress\n Please wait for its completion\nOr in case of stale lock, remove #{@lock_file} to unlock \e[0m\n"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "create", "Create lock"
|
17
|
+
def create
|
18
|
+
lock_message = "Deploy started at #{@deploy_date} in progress\n"
|
19
|
+
command = "echo '#{lock_message}' > #{@lock_file}"
|
20
|
+
puts capture(command, @lead_server)
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "release", "Release lock"
|
24
|
+
def release
|
25
|
+
command = "rm -f #{@lock_file}"
|
26
|
+
puts capture(command, @lead_server)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/lib/dolphin/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dolphin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- |
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -95,6 +95,9 @@ files:
|
|
95
95
|
- Rakefile
|
96
96
|
- dolphin.gemspec
|
97
97
|
- lib/dolphin.rb
|
98
|
+
- lib/dolphin/base.rb
|
99
|
+
- lib/dolphin/deploy.rb
|
100
|
+
- lib/dolphin/lock.rb
|
98
101
|
- lib/dolphin/version.rb
|
99
102
|
homepage: ''
|
100
103
|
licenses:
|