runsible 0.1.2.1 → 0.1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bin/runsible +1 -1
- data/lib/runsible.rb +92 -68
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f31a8f06a0427b8f580a9240bc5698bc87e1b439
|
4
|
+
data.tar.gz: bf3daedd7b2e648992ced1ea3ec8140f83e9f30c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c43fd82babf3dd5630fa2d1e69b82ff141a33ac23781b670a51d50e893e5313f8ad45305de6f9cd779176e720cf047e9c25866b8b66e59a5eebd6e79665b230
|
7
|
+
data.tar.gz: 539a72b37cab94eba245b16fdd5d67bbd31ade5cecadd84cd10f34a5457a29b3a3eca3b4085c1ec5c8b3f865fef52cd515ace50f45e5ee92a27cd6e455617823
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3.1
|
data/bin/runsible
CHANGED
data/lib/runsible.rb
CHANGED
@@ -18,8 +18,8 @@ end
|
|
18
18
|
# STDOUT and STDERR
|
19
19
|
#
|
20
20
|
module Runsible
|
21
|
-
SSH_CNX_TIMEOUT = 10
|
22
21
|
class Error < RuntimeError; end
|
22
|
+
SSH_CNX_TIMEOUT = 10
|
23
23
|
|
24
24
|
SETTINGS = {
|
25
25
|
user: ENV['USER'],
|
@@ -29,6 +29,10 @@ module Runsible
|
|
29
29
|
vars: [],
|
30
30
|
}
|
31
31
|
|
32
|
+
#
|
33
|
+
# Utility stuff
|
34
|
+
#
|
35
|
+
|
32
36
|
def self.default_settings
|
33
37
|
hsh = {}
|
34
38
|
SETTINGS.each { |sym, v|
|
@@ -48,32 +52,58 @@ module Runsible
|
|
48
52
|
exit 1
|
49
53
|
end
|
50
54
|
|
51
|
-
def self.
|
52
|
-
|
53
|
-
|
55
|
+
def self.alert(topic, message, settings)
|
56
|
+
backend = settings['alerts'] && settings['alerts']['backend']
|
57
|
+
case backend
|
58
|
+
when 'disabled', nil, false
|
59
|
+
return
|
60
|
+
when 'email'
|
61
|
+
Pony.mail(to: settings.fetch(:address),
|
62
|
+
from: 'runsible@spoon',
|
63
|
+
subject: topic,
|
64
|
+
body: message)
|
65
|
+
when 'kafka', 'slack'
|
66
|
+
# TODO
|
67
|
+
raise Error, "unsupported backend: #{backend.inspect}"
|
68
|
+
else
|
69
|
+
raise Error, "unknown backend: #{backend.inspect}"
|
70
|
+
end
|
54
71
|
end
|
55
72
|
|
56
|
-
def self.
|
57
|
-
|
58
|
-
|
73
|
+
def self.warn(msg)
|
74
|
+
$stdout.puts msg
|
75
|
+
$stderr.puts msg
|
76
|
+
end
|
59
77
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
78
|
+
def self.die!(msg, settings)
|
79
|
+
self.warn(msg)
|
80
|
+
self.alert("runsible:fatal:#{Process.pid}", msg, settings)
|
81
|
+
exit 1
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
#
|
86
|
+
# CLI or bin/runsible stuff
|
87
|
+
#
|
88
|
+
|
89
|
+
# bin/runsible entry point
|
90
|
+
def self.spoon(ssh_options = Hash.new)
|
91
|
+
opts = Runsible.slop_parse
|
92
|
+
yaml = self.extract_yaml(opts)
|
93
|
+
settings = Runsible.default_settings.merge(yaml['settings'] || Hash.new)
|
94
|
+
settings = self.merge(opts, settings)
|
95
|
+
self.ssh_runlist(settings, yaml['runlist'] || Array.new, ssh_options, yaml)
|
66
96
|
end
|
67
97
|
|
68
98
|
def self.slop_parse
|
69
|
-
d = SETTINGS
|
99
|
+
d = SETTINGS # display defaults
|
70
100
|
Slop.parse do |o|
|
71
101
|
o.banner = "usage: runsible [options] yaml_file"
|
72
102
|
o.on '-h', '--help' do
|
73
103
|
puts o
|
74
104
|
exit 0
|
75
105
|
end
|
76
|
-
o.on '-v', '--version', 'show
|
106
|
+
o.on '-v', '--version', 'show runsible version' do
|
77
107
|
puts Runsible.version
|
78
108
|
exit 0
|
79
109
|
end
|
@@ -89,43 +119,30 @@ module Runsible
|
|
89
119
|
end
|
90
120
|
end
|
91
121
|
|
92
|
-
def self.
|
93
|
-
|
94
|
-
|
95
|
-
}
|
96
|
-
settings['alerts'] = {} if opts.silent?
|
97
|
-
settings
|
98
|
-
end
|
122
|
+
def self.extract_yaml(opts)
|
123
|
+
yaml_filename = opts.arguments.shift
|
124
|
+
self.usage(opts, "yaml_file is required") if yaml_filename.nil?
|
99
125
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
return
|
105
|
-
when 'email'
|
106
|
-
Pony.mail(to: settings.fetch(:address),
|
107
|
-
from: 'runsible@spoon',
|
108
|
-
subject: topic,
|
109
|
-
body: message)
|
110
|
-
when 'kafka', 'slack'
|
111
|
-
# TODO
|
112
|
-
raise Error, "unsupported backend: #{backend.inspect}"
|
113
|
-
else
|
114
|
-
raise Error, "unknown backend: #{backend.inspect}"
|
126
|
+
begin
|
127
|
+
yaml = YAML.load_file(yaml_filename)
|
128
|
+
rescue RuntimeError => e
|
129
|
+
Runsible.usage(opts, "could not load yaml_file\n#{e}")
|
115
130
|
end
|
131
|
+
yaml
|
116
132
|
end
|
117
133
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
end
|
134
|
+
#
|
135
|
+
# Library stuff
|
136
|
+
#
|
122
137
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
self.ssh_runlist(
|
138
|
+
# run a YAML file without any consideration for command line options
|
139
|
+
def self.run_yaml(yaml_filename, ssh_options = Hash.new)
|
140
|
+
yaml = YAML.load_file(yaml_filename)
|
141
|
+
self.ssh_runlist(self.default_settings, yaml['runlist'] || Array.new,
|
142
|
+
ssh_options, yaml)
|
127
143
|
end
|
128
144
|
|
145
|
+
# initiate ssh connection, perform the runlist
|
129
146
|
def self.ssh_runlist(settings, runlist, ssh_options, yaml)
|
130
147
|
ssh_options[:forward_agent] ||= true
|
131
148
|
ssh_options[:port] ||= settings.fetch('port')
|
@@ -137,12 +154,6 @@ module Runsible
|
|
137
154
|
}
|
138
155
|
end
|
139
156
|
|
140
|
-
def self.die!(msg, settings)
|
141
|
-
self.warn(msg)
|
142
|
-
self.alert("runsible:fatal:#{Process.pid}", msg, settings)
|
143
|
-
exit 1
|
144
|
-
end
|
145
|
-
|
146
157
|
# execute runlist with failure handling, retries, alerting, etc.
|
147
158
|
def self.exec_runlist(ssh, runlist, settings, yaml = Hash.new)
|
148
159
|
runlist.each { |run|
|
@@ -177,6 +188,26 @@ module Runsible
|
|
177
188
|
}
|
178
189
|
end
|
179
190
|
|
191
|
+
# retry several times, rescuing CommandFailure
|
192
|
+
# raises on SSH channel exec failure and CommandFailure on final retry
|
193
|
+
def self.exec_retry(ssh, cmd, retries)
|
194
|
+
self.banner_wrap(cmd) {
|
195
|
+
success = false
|
196
|
+
retries.times { |i|
|
197
|
+
begin
|
198
|
+
success = self.exec(ssh, cmd)
|
199
|
+
break
|
200
|
+
rescue CommandFailure => e
|
201
|
+
$stdout.puts "#{e}; retrying shortly..."
|
202
|
+
$stderr.puts e
|
203
|
+
sleep 2
|
204
|
+
end
|
205
|
+
}
|
206
|
+
# the final retry, may blow up
|
207
|
+
success or self.exec(ssh, cmd)
|
208
|
+
}
|
209
|
+
end
|
210
|
+
|
180
211
|
# prints remote STDOUT to local STDOUT, likewise for STDERR
|
181
212
|
# raises on SSH channel exec failure or nonzero exit status
|
182
213
|
def self.exec(ssh, cmd)
|
@@ -199,24 +230,17 @@ module Runsible
|
|
199
230
|
exit_code == 0 or raise(CommandFailure, "[exit #{exit_code}] #{cmd}")
|
200
231
|
end
|
201
232
|
|
202
|
-
|
203
|
-
#
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
break
|
211
|
-
rescue CommandFailure => e
|
212
|
-
$stdout.puts "#{e}; retrying shortly..."
|
213
|
-
$stderr.puts e
|
214
|
-
sleep 2
|
215
|
-
end
|
216
|
-
}
|
217
|
-
# the final retry, may blow up
|
218
|
-
success or self.exec(ssh, cmd)
|
233
|
+
|
234
|
+
#
|
235
|
+
# Necessities
|
236
|
+
#
|
237
|
+
|
238
|
+
def self.merge(opts, settings)
|
239
|
+
Runsible::SETTINGS.keys.each { |sym|
|
240
|
+
settings[sym.to_s] = opts[sym] if opts[sym]
|
219
241
|
}
|
242
|
+
settings['alerts'] = {} if opts.silent?
|
243
|
+
settings
|
220
244
|
end
|
221
245
|
|
222
246
|
def self.begin_banner(msg)
|