aggkit 0.2.5
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 +7 -0
- data/.dockerignore +20 -0
- data/.gitignore +109 -0
- data/.gitlab-ci.yml +66 -0
- data/.rspec +4 -0
- data/.rubocop.yml +98 -0
- data/.travis.yml +30 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +55 -0
- data/README.md +96 -0
- data/aggkit.gemspec +38 -0
- data/bin/agg +167 -0
- data/bin/aggconsul +222 -0
- data/bin/agglock +71 -0
- data/bin/aggmerge +118 -0
- data/bin/aggwait +262 -0
- data/bin/consul.rb +222 -0
- data/bin/locker.rb +71 -0
- data/bin/merger.rb +118 -0
- data/bin/terminator.rb +71 -0
- data/bin/waiter.rb +262 -0
- data/docker/Dockerfile +112 -0
- data/docker/docker-compose.yml +12 -0
- data/docker/down.sh +4 -0
- data/docker/run_tests.sh +23 -0
- data/lib/aggkit/childprocess/abstract_io.rb +38 -0
- data/lib/aggkit/childprocess/abstract_process.rb +194 -0
- data/lib/aggkit/childprocess/errors.rb +28 -0
- data/lib/aggkit/childprocess/jruby/io.rb +17 -0
- data/lib/aggkit/childprocess/jruby/process.rb +161 -0
- data/lib/aggkit/childprocess/jruby/pump.rb +55 -0
- data/lib/aggkit/childprocess/jruby.rb +58 -0
- data/lib/aggkit/childprocess/tools/generator.rb +148 -0
- data/lib/aggkit/childprocess/unix/fork_exec_process.rb +72 -0
- data/lib/aggkit/childprocess/unix/io.rb +22 -0
- data/lib/aggkit/childprocess/unix/lib.rb +188 -0
- data/lib/aggkit/childprocess/unix/platform/i386-linux.rb +14 -0
- data/lib/aggkit/childprocess/unix/platform/i386-solaris.rb +13 -0
- data/lib/aggkit/childprocess/unix/platform/x86_64-linux.rb +14 -0
- data/lib/aggkit/childprocess/unix/platform/x86_64-macosx.rb +13 -0
- data/lib/aggkit/childprocess/unix/posix_spawn_process.rb +135 -0
- data/lib/aggkit/childprocess/unix/process.rb +91 -0
- data/lib/aggkit/childprocess/unix.rb +11 -0
- data/lib/aggkit/childprocess/version.rb +5 -0
- data/lib/aggkit/childprocess/windows/handle.rb +93 -0
- data/lib/aggkit/childprocess/windows/io.rb +25 -0
- data/lib/aggkit/childprocess/windows/lib.rb +418 -0
- data/lib/aggkit/childprocess/windows/process.rb +132 -0
- data/lib/aggkit/childprocess/windows/process_builder.rb +177 -0
- data/lib/aggkit/childprocess/windows/structs.rb +151 -0
- data/lib/aggkit/childprocess/windows.rb +35 -0
- data/lib/aggkit/childprocess.rb +213 -0
- data/lib/aggkit/env.rb +219 -0
- data/lib/aggkit/runner.rb +80 -0
- data/lib/aggkit/version.rb +5 -0
- data/lib/aggkit/watcher.rb +239 -0
- data/lib/aggkit.rb +15 -0
- metadata +196 -0
data/bin/aggconsul
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'diplomat'
|
4
|
+
require 'optparse'
|
5
|
+
require 'English'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
STDOUT.sync = true
|
9
|
+
STDERR.sync = true
|
10
|
+
|
11
|
+
@opts = {}
|
12
|
+
|
13
|
+
@opts[:exec] = (begin
|
14
|
+
ARGV.join(' ').split(' -- ')[1].strip
|
15
|
+
rescue StandardError
|
16
|
+
nil
|
17
|
+
end)
|
18
|
+
|
19
|
+
parser = OptionParser.new do |o|
|
20
|
+
o.banner = 'Usage: consul.rb [options] -- exec'
|
21
|
+
|
22
|
+
o.on('--consul url', 'Set up a custom Consul URL') do |url|
|
23
|
+
Diplomat.configure do |config|
|
24
|
+
config.url = url.strip
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
o.on('--token token', 'Connect into consul with custom access token (ACL)') do |token|
|
29
|
+
Diplomat.configure do |config|
|
30
|
+
config.acl_token = token.strip
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
o.on('--init [service]', 'Initialize Consul services from config') do |service|
|
35
|
+
@opts[:service] = service
|
36
|
+
@opts[:init] = true
|
37
|
+
end
|
38
|
+
|
39
|
+
o.on('--config file', 'Read service configulation from file') do |file|
|
40
|
+
@opts[:config] = file.strip
|
41
|
+
end
|
42
|
+
|
43
|
+
o.on('--upload', 'Upload files to variables') do
|
44
|
+
@opts[:upload] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
o.on('--show [service]', 'Show service configulation from Consul') do |service|
|
48
|
+
@opts[:service] = service
|
49
|
+
@opts[:show] = true
|
50
|
+
end
|
51
|
+
|
52
|
+
o.on('--override', 'override existed keys') do
|
53
|
+
@opts[:override] = true
|
54
|
+
end
|
55
|
+
|
56
|
+
o.on('-d', '--dereference', 'dereference consul values in form of "consul://key/subkey"') do
|
57
|
+
@opts[:dereference] = true
|
58
|
+
end
|
59
|
+
|
60
|
+
o.on('--env prefix', 'export KV values from prefix as env varaibles') do |prefix|
|
61
|
+
@opts[:env] = (prefix + '/').gsub('//', '/')
|
62
|
+
end
|
63
|
+
|
64
|
+
o.on('--export', 'add export to --env output') do
|
65
|
+
@opts[:export] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
o.on('--pristine', "not include the parent processes' environment when exec child process") do
|
69
|
+
@opts[:pristine] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
o.on('--put path:value', 'put value to path') do |path|
|
73
|
+
@opts[:put] = path.strip
|
74
|
+
end
|
75
|
+
|
76
|
+
o.on('--get path', 'get value from') do |path|
|
77
|
+
@opts[:get] = path.strip
|
78
|
+
end
|
79
|
+
end
|
80
|
+
parser.parse!
|
81
|
+
|
82
|
+
def die(message)
|
83
|
+
STDERR.puts "Error: #{message}"
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
|
87
|
+
def key_to_consul(key)
|
88
|
+
key.downcase.gsub(/[^0-9a-z]/i, '_')
|
89
|
+
end
|
90
|
+
|
91
|
+
def key_to_env(key)
|
92
|
+
key.upcase.gsub(/[^0-9a-z]/i, '_')
|
93
|
+
end
|
94
|
+
|
95
|
+
def dereferenced_value(value)
|
96
|
+
if @opts[:dereference] && value && value[/^consul:\/\//]
|
97
|
+
reference_path = value.gsub(/^consul:\/\//, '')
|
98
|
+
dereferenced_value(Diplomat::Kv.get(reference_path))
|
99
|
+
else
|
100
|
+
value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
if config = @opts[:config]
|
105
|
+
@opts[:config] = YAML.load(config == '-' ? STDIN.read : File.read(config))
|
106
|
+
end
|
107
|
+
|
108
|
+
if @opts[:init]
|
109
|
+
raise OptionParser::MissingArgument.new('config') unless @opts[:config]
|
110
|
+
|
111
|
+
services = if service = @opts[:service]
|
112
|
+
{
|
113
|
+
service => @opts[:config][service]
|
114
|
+
}
|
115
|
+
else
|
116
|
+
@opts[:config]
|
117
|
+
end
|
118
|
+
|
119
|
+
services.each_pair do |service, config|
|
120
|
+
next unless config
|
121
|
+
next if service[/^\./] # skip hidden keys
|
122
|
+
|
123
|
+
path = "services/env/#{service}"
|
124
|
+
config.each_pair do |env, item|
|
125
|
+
key = "#{path}/#{key_to_consul(env)}"
|
126
|
+
value = if @opts[:upload] && item['file']
|
127
|
+
File.read(item['file'])
|
128
|
+
else
|
129
|
+
item['value'] || item['default'] || item['file']
|
130
|
+
end
|
131
|
+
|
132
|
+
empty = begin
|
133
|
+
Diplomat::Kv.get(key)
|
134
|
+
false
|
135
|
+
rescue Diplomat::KeyNotFound
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
Diplomat::Kv.put(key, value.to_s.strip) || die("Can't put #{key} to Consul") if empty || @opts[:override]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
exit 0
|
144
|
+
end
|
145
|
+
|
146
|
+
if @opts[:show]
|
147
|
+
config = {}
|
148
|
+
|
149
|
+
path = if service = @opts[:service]
|
150
|
+
"services/env/#{service}/"
|
151
|
+
else
|
152
|
+
'services/env/'
|
153
|
+
end
|
154
|
+
|
155
|
+
answer = Diplomat::Kv.get(path, recurse: true, convert_to_hash: true) || die("Can't get #{path} from Consul")
|
156
|
+
answer['services']['env'].each_pair do |service, env|
|
157
|
+
cfg = config[service] ||= {}
|
158
|
+
|
159
|
+
env.each_pair do |key, value|
|
160
|
+
value = dereferenced_value(value)
|
161
|
+
|
162
|
+
cfg[key_to_env(key)] = {
|
163
|
+
env: key_to_env(key),
|
164
|
+
value: value
|
165
|
+
}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
STDOUT.puts JSON.parse(config.to_json).to_yaml
|
170
|
+
|
171
|
+
exit 0
|
172
|
+
end
|
173
|
+
|
174
|
+
if put = @opts[:put]
|
175
|
+
path, *value = put.split(':').map(&:strip)
|
176
|
+
value = value.join(':')
|
177
|
+
value = File.read(value) if @opts[:upload] && value && File.exist?(value)
|
178
|
+
|
179
|
+
Diplomat::Kv.put(path, value.to_s.strip) || die("Can't put #{path} to Consul")
|
180
|
+
|
181
|
+
exit 0
|
182
|
+
end
|
183
|
+
|
184
|
+
if path = @opts[:get]
|
185
|
+
value = dereferenced_value(Diplomat::Kv.get(path))
|
186
|
+
|
187
|
+
STDOUT.puts value.to_s.strip
|
188
|
+
|
189
|
+
exit 0
|
190
|
+
end
|
191
|
+
|
192
|
+
if prefix = @opts[:env]
|
193
|
+
keys = begin
|
194
|
+
Diplomat::Kv.get(prefix, keys: true)
|
195
|
+
rescue Diplomat::KeyNotFound => e
|
196
|
+
[]
|
197
|
+
rescue StandardError
|
198
|
+
die("Can't get keys at #{prefix} from Consul")
|
199
|
+
end
|
200
|
+
|
201
|
+
env = keys.reduce({}) do |e, key|
|
202
|
+
value = dereferenced_value(Diplomat::Kv.get(key))
|
203
|
+
|
204
|
+
e.merge(key_to_env(key.gsub(prefix, '')) => value)
|
205
|
+
end
|
206
|
+
|
207
|
+
if cmd = @opts[:exec]
|
208
|
+
env = ENV.to_h.merge(env) unless @opts[:pristine]
|
209
|
+
|
210
|
+
exec(env, cmd, unsetenv_others: true)
|
211
|
+
else
|
212
|
+
env.each_pair do |k, v|
|
213
|
+
STDOUT.puts "#{@opts[:export] ? 'export ' : ''}#{k}=\"#{v}\""
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
exit 0
|
219
|
+
end
|
220
|
+
|
221
|
+
STDOUT.puts parser.help
|
222
|
+
exit 1
|
data/bin/agglock
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'diplomat'
|
5
|
+
|
6
|
+
@opts = {
|
7
|
+
url: 'http://localhost:8500',
|
8
|
+
timeout: 10,
|
9
|
+
ttl: 30 * 60
|
10
|
+
}
|
11
|
+
|
12
|
+
parser = OptionParser.new do |o|
|
13
|
+
o.banner = 'Usage: locker.rb [options]'
|
14
|
+
|
15
|
+
o.on("--consul url=#{@opts[:url]}", 'Set up a custom Consul URL') do |url|
|
16
|
+
Diplomat.configure do |config|
|
17
|
+
config.url = url.strip
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
o.on('--lock resource', 'resource name to lock with Consul') do |resource|
|
22
|
+
@opts[:lock] = resource.strip
|
23
|
+
end
|
24
|
+
|
25
|
+
o.on("--ttl seconds=#{@opts[:ttl]}", 'TTL to set when session created') do |seconds|
|
26
|
+
@opts[:ttl] = Integer(seconds.strip)
|
27
|
+
end
|
28
|
+
|
29
|
+
o.on('--unlock session', 'session name from previous call lock') do |session|
|
30
|
+
@opts[:unlock] = session.strip
|
31
|
+
end
|
32
|
+
|
33
|
+
o.on("--timeout seconds=#{@opts[:timeout]}", 'timeout to wait lock') do |seconds|
|
34
|
+
@opts[:timeout] = Integer(seconds.strip)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
parser.parse!
|
38
|
+
|
39
|
+
|
40
|
+
def lock(session, locker, timeout)
|
41
|
+
Timeout.timeout(timeout) do
|
42
|
+
return Diplomat::Lock.wait_to_acquire("resource/#{locker[:resource]}/lock", session, locker.to_json, 10)
|
43
|
+
end
|
44
|
+
rescue Timeout::Error => e
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
if resource = @opts[:lock]
|
50
|
+
locker = {
|
51
|
+
Name: "#{resource}_locker_#{rand(999_999)}",
|
52
|
+
Behavior: 'delete',
|
53
|
+
TTL: "#{@opts[:ttl]}s",
|
54
|
+
resource: resource
|
55
|
+
}
|
56
|
+
sessionid = Diplomat::Session.create(locker)
|
57
|
+
|
58
|
+
if lock(sessionid, locker, @opts[:timeout])
|
59
|
+
puts sessionid
|
60
|
+
exit 0
|
61
|
+
else
|
62
|
+
STDERR.puts "Failed to lock resource: #{resource}"
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
elsif session = @opts[:unlock]
|
67
|
+
Diplomat::Session.destroy(session)
|
68
|
+
else
|
69
|
+
STDERR.puts parser.help
|
70
|
+
exit 1
|
71
|
+
end
|
data/bin/aggmerge
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
|
7
|
+
unless ENV['COMPOSE_FILE']
|
8
|
+
STDERR.puts 'COMPOSE_FILE environment must point to one or more files'
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
class Hash
|
15
|
+
|
16
|
+
def deep_dup
|
17
|
+
Marshal.load(Marshal.dump(self))
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class Array
|
23
|
+
|
24
|
+
def deep_dup
|
25
|
+
Marshal.load(Marshal.dump(self))
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
def extend_hash(first, second)
|
33
|
+
raise ArgumentError.new('First and second args equal nil') if [first, second].all? &:nil?
|
34
|
+
return second if first.nil?
|
35
|
+
return first if second.nil?
|
36
|
+
|
37
|
+
first.each_pair do |fk, fv|
|
38
|
+
next unless second.key?(fk)
|
39
|
+
|
40
|
+
sv = second[fk]
|
41
|
+
raise "Types of values not match(#{fv.class}, #{sv.class})" if fv.class != sv.class
|
42
|
+
|
43
|
+
# Специальный случай потому что command не мерджится а заменяется
|
44
|
+
if fk == 'command'
|
45
|
+
first[fk] = sv
|
46
|
+
elsif fv.is_a? Hash
|
47
|
+
extend_hash(fv, sv)
|
48
|
+
elsif fv.is_a? Array
|
49
|
+
fv |= sv
|
50
|
+
first[fk] = fv
|
51
|
+
else
|
52
|
+
first[fk] = sv
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
second.each_pair do |sk, sv|
|
57
|
+
next if first.key?(sk)
|
58
|
+
|
59
|
+
first[sk] = sv
|
60
|
+
end
|
61
|
+
|
62
|
+
first
|
63
|
+
end
|
64
|
+
|
65
|
+
def process_compose_hash(yml, dirname, parent = {})
|
66
|
+
(yml['services'] || {}).each_pair do |name, service|
|
67
|
+
next unless ext = service['extends']
|
68
|
+
base = if ext.is_a? String
|
69
|
+
template = yml['services'][ext]
|
70
|
+
parent_service = (parent['services'] || {})[ext] || {}
|
71
|
+
extend_hash(parent_service.deep_dup, template)
|
72
|
+
elsif file = ext['file']
|
73
|
+
ENV.each_pair do |k, v|
|
74
|
+
file.gsub!("$#{k}", v)
|
75
|
+
file.gsub!("${#{k}}", v)
|
76
|
+
end
|
77
|
+
|
78
|
+
file_to_load = if File.exist?(dirname + '/' + file)
|
79
|
+
dirname + '/' + file
|
80
|
+
else
|
81
|
+
file
|
82
|
+
end
|
83
|
+
|
84
|
+
tmp = process_compose_hash(YAML.load(File.read(file_to_load)), File.dirname(file_to_load), service)
|
85
|
+
|
86
|
+
begin
|
87
|
+
(tmp['services'][ext['service']] || {})
|
88
|
+
rescue StandardError
|
89
|
+
{}
|
90
|
+
end
|
91
|
+
else
|
92
|
+
yml['services'][ext['service']]
|
93
|
+
end.deep_dup
|
94
|
+
|
95
|
+
service.delete 'extends'
|
96
|
+
|
97
|
+
yml['services'][name] = extend_hash(base, service)
|
98
|
+
end
|
99
|
+
yml
|
100
|
+
end
|
101
|
+
|
102
|
+
if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
|
103
|
+
result = ENV['COMPOSE_FILE'].split(':').reduce({}) do |parent, file|
|
104
|
+
yml = process_compose_hash(YAML.load(File.read(file)), File.dirname(file), parent)
|
105
|
+
if yml['version'] && parent['version'] && yml['version'] != parent['version']
|
106
|
+
raise "version mismatch: #{file}"
|
107
|
+
end
|
108
|
+
ret = extend_hash(parent.deep_dup, yml)
|
109
|
+
ret
|
110
|
+
end
|
111
|
+
|
112
|
+
if ARGV[0].nil? || ARGV[0].strip == '-'
|
113
|
+
puts YAML.dump(result)
|
114
|
+
else
|
115
|
+
File.write(ARGV[0].strip, YAML.dump(result))
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
data/bin/aggwait
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'English'
|
6
|
+
require 'openssl'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
STDOUT.sync = true
|
11
|
+
STDERR.sync = true
|
12
|
+
|
13
|
+
TIMEOUT = 15
|
14
|
+
INTERVAL = 3
|
15
|
+
|
16
|
+
@opts = {}
|
17
|
+
|
18
|
+
@opts[:exec] = (begin
|
19
|
+
ARGV.join(' ').split(' -- ')[1].strip
|
20
|
+
rescue StandardError
|
21
|
+
nil
|
22
|
+
end)
|
23
|
+
@opts[:timeout] = TIMEOUT
|
24
|
+
@opts[:interval] = INTERVAL
|
25
|
+
@opts[:consul_addr] = 'http://localhost:8500'
|
26
|
+
@opts[:consul_service_count] = 1
|
27
|
+
|
28
|
+
|
29
|
+
OptionParser.new do |o|
|
30
|
+
o.banner = 'Usage: waiter.rb [options] -- exec'
|
31
|
+
|
32
|
+
o.on('--tcp host:port', 'Wait for tcp accepts on host:port') do |addr|
|
33
|
+
host, port = addr.split(':')
|
34
|
+
@opts[:tcp] = addr.strip
|
35
|
+
@opts[:host] = host.strip
|
36
|
+
@opts[:port] = port.strip
|
37
|
+
end
|
38
|
+
|
39
|
+
o.on('--db dbname', 'Wait for PG database exists. Using --tcp to conenct PG') do |db|
|
40
|
+
@opts[:db] = db.strip
|
41
|
+
end
|
42
|
+
|
43
|
+
o.on('--tb tablename', 'Wait for PG table exists. Using --tcp to conenct PG') do |tb|
|
44
|
+
@opts[:tb] = tb.strip
|
45
|
+
end
|
46
|
+
|
47
|
+
o.on('-f', '--file filename', 'Wait for file exists.') do |file|
|
48
|
+
@opts[:file] = file.strip
|
49
|
+
end
|
50
|
+
|
51
|
+
o.on("--consul-addr addr=#{@opts[:consul_addr]}", 'HTTP addres to connect to consul') do |addr|
|
52
|
+
@opts[:consul_addr] = addr.strip
|
53
|
+
end
|
54
|
+
|
55
|
+
o.on('--consul', 'Wait for local consul agent to be ready') do
|
56
|
+
@opts[:consul] = true
|
57
|
+
end
|
58
|
+
|
59
|
+
o.on('--consul-service service', 'Wait for service appear in consul') do |service|
|
60
|
+
@opts[:consul_service] = service
|
61
|
+
end
|
62
|
+
|
63
|
+
o.on("--consul-service-count count=#{@opts[:consul_service_count]}", 'Wait for this count of service appear in consul') do |count|
|
64
|
+
@opts[:consul_service_count] = count.to_i
|
65
|
+
end
|
66
|
+
|
67
|
+
o.on('--consul-tag tag', 'Filter consul service by tag') do |tag|
|
68
|
+
@opts[:consul_tag] = tag.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
o.on('--user user', 'username') do |user|
|
72
|
+
@opts[:user] = user.strip
|
73
|
+
end
|
74
|
+
|
75
|
+
o.on('--pass pass', 'password') do |pass|
|
76
|
+
@opts[:pass] = pass.strip
|
77
|
+
end
|
78
|
+
|
79
|
+
o.on('-t', '--timeout secs=15', 'Total timeout') do |timeout|
|
80
|
+
@opts[:timeout] = timeout.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
o.on('-i', '--interval secs=2', 'Interval between attempts') do |interval|
|
84
|
+
@opts[:interval] = interval.to_i
|
85
|
+
end
|
86
|
+
|
87
|
+
o.on('-q', '--quiet', 'Do not output any status messages') do
|
88
|
+
@opts[:quiet] = true
|
89
|
+
end
|
90
|
+
end.parse!
|
91
|
+
|
92
|
+
def log(message)
|
93
|
+
puts message unless @opts[:quiet]
|
94
|
+
end
|
95
|
+
|
96
|
+
@opts[:timeout] = @opts[:timeout].to_i
|
97
|
+
@opts[:interval] = @opts[:interval].to_i
|
98
|
+
|
99
|
+
if @opts[:db]
|
100
|
+
@pg = {}
|
101
|
+
@pg[:db] = "-d #{@opts[:db]}"
|
102
|
+
@pg[:user] = "-U #{@opts[:user]}" if @opts[:user]
|
103
|
+
@pg[:pass] = if @opts[:pass] && !@opts[:pass].empty?
|
104
|
+
"PGPASSWORD=#{@opts[:pass]}"
|
105
|
+
else
|
106
|
+
''
|
107
|
+
end
|
108
|
+
|
109
|
+
@pg[:host] = "-h #{@opts[:host]}" if @opts[:host]
|
110
|
+
@pg[:port] = "-p #{@opts[:port]}" if @opts[:port]
|
111
|
+
|
112
|
+
@pg[:tb] = @opts[:tb] if @opts[:tb]
|
113
|
+
end
|
114
|
+
|
115
|
+
def wait_for(timeout)
|
116
|
+
starttime = Time.now
|
117
|
+
loop do
|
118
|
+
success = yield
|
119
|
+
|
120
|
+
return success if success
|
121
|
+
|
122
|
+
return false if (Time.now - starttime) > timeout
|
123
|
+
sleep @opts[:interval]
|
124
|
+
end
|
125
|
+
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
129
|
+
def complete!(success)
|
130
|
+
if success
|
131
|
+
if @opts[:exec]
|
132
|
+
exec @opts[:exec]
|
133
|
+
else
|
134
|
+
exit 0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
STDERR.puts 'Operation timed out'
|
139
|
+
exit 1
|
140
|
+
end
|
141
|
+
|
142
|
+
def wait_for_consul
|
143
|
+
log('Waiting for consul...')
|
144
|
+
ret = wait_for @opts[:timeout] do
|
145
|
+
cmd = "consul operator raft list-peers -http-addr=#{@opts[:consul_addr]} > /dev/null 2>&1"
|
146
|
+
system(cmd)
|
147
|
+
$?.success?
|
148
|
+
end
|
149
|
+
|
150
|
+
yield(ret)
|
151
|
+
end
|
152
|
+
|
153
|
+
def wait_for_consul_service(service)
|
154
|
+
log("Waiting for consul service #{service}...")
|
155
|
+
ret = wait_for @opts[:timeout] do
|
156
|
+
cmd = "curl -s #{@opts[:consul_addr]}/v1/health/service/#{service}?passing"
|
157
|
+
cmd += "&tag=#{@opts[:consul_tag]}" if @opts[:consul_tag]
|
158
|
+
result = `#{cmd}`
|
159
|
+
result = begin
|
160
|
+
JSON.parse(result)
|
161
|
+
rescue StandardError
|
162
|
+
[]
|
163
|
+
end
|
164
|
+
result.count >= @opts[:consul_service_count]
|
165
|
+
end
|
166
|
+
|
167
|
+
yield(ret)
|
168
|
+
end
|
169
|
+
|
170
|
+
def wait_for_tcp
|
171
|
+
log("Waiting for TCP: #{@opts[:host]}:#{@opts[:port]}...")
|
172
|
+
ret = wait_for @opts[:timeout] do
|
173
|
+
cmd = "nc -z #{@opts[:host]} #{@opts[:port]} > /dev/null 2>&1"
|
174
|
+
system(cmd)
|
175
|
+
$?.success?
|
176
|
+
end
|
177
|
+
|
178
|
+
yield(ret)
|
179
|
+
end
|
180
|
+
|
181
|
+
def wait_for_db
|
182
|
+
log("Waiting for DB: pg://#{@opts[:user]}:#{@opts[:pass]}@#{@opts[:host]}:#{@opts[:port]}/#{@opts[:db]}...")
|
183
|
+
ret = wait_for @opts[:timeout] do
|
184
|
+
cmd = "#{@pg[:pass]} psql -lqt #{@pg[:user]} #{@pg[:host]} #{@pg[:port]} #{@pg[:db]} 2>/dev/null | cut -d \\| -f 1 | grep -qw #{@opts[:db]} > /dev/null 2>&1"
|
185
|
+
system(cmd)
|
186
|
+
$?.success?
|
187
|
+
end
|
188
|
+
|
189
|
+
yield(ret)
|
190
|
+
end
|
191
|
+
|
192
|
+
def wait_for_tb
|
193
|
+
log("Waiting for TABLE: pg://#{@opts[:user]}:#{@opts[:pass]}@#{@opts[:host]}:#{@opts[:port]}/#{@opts[:db]}##{@opts[:tb]}...")
|
194
|
+
ret = wait_for @opts[:timeout] do
|
195
|
+
cmd = "echo \"\\dt\" | psql -qt #{@pg[:user]} #{@pg[:pass]} #{@pg[:host]} #{@pg[:port]} #{@pg[:db]} 2>/dev/null | cut -d \\| -f 2 | grep -qw #{@pg[:tb]} > /dev/null 2>&1"
|
196
|
+
system(cmd)
|
197
|
+
$?.success?
|
198
|
+
end
|
199
|
+
|
200
|
+
yield(ret)
|
201
|
+
end
|
202
|
+
|
203
|
+
def wait_for_file(file = @opts[:file], timeout = @opts[:timeout])
|
204
|
+
log("Waiting for FILE: #{file}")
|
205
|
+
ret = wait_for timeout do
|
206
|
+
File.exist? file
|
207
|
+
end
|
208
|
+
yield(ret)
|
209
|
+
end
|
210
|
+
|
211
|
+
if @opts[:tb]
|
212
|
+
wait_for_tcp do |success|
|
213
|
+
if success
|
214
|
+
wait_for_db do |success|
|
215
|
+
if success
|
216
|
+
wait_for_tb do |success|
|
217
|
+
complete!(success)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
complete!(false)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
if @opts[:db]
|
228
|
+
wait_for_tcp do |success|
|
229
|
+
if success
|
230
|
+
wait_for_db do |success|
|
231
|
+
complete!(success)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
complete!(false)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
if @opts[:consul]
|
241
|
+
wait_for_consul do |success|
|
242
|
+
complete!(success)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
if @opts[:consul_service]
|
247
|
+
wait_for_consul_service(@opts[:consul_service]) do |success|
|
248
|
+
complete!(success)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
if @opts[:tcp]
|
253
|
+
wait_for_tcp do |success|
|
254
|
+
complete!(success)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
if @opts[:file]
|
259
|
+
wait_for_file(@opts[:file], @opts[:timeout]) do |success|
|
260
|
+
complete!(success)
|
261
|
+
end
|
262
|
+
end
|