aggkit 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- 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/consul.rb
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/locker.rb
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/merger.rb
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/terminator.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
STDOUT.sync = true
|
6
|
+
STDERR.sync = true
|
7
|
+
|
8
|
+
@opts = {
|
9
|
+
code: 0,
|
10
|
+
sleep: 1,
|
11
|
+
term_code: 0
|
12
|
+
}
|
13
|
+
|
14
|
+
def log(msg)
|
15
|
+
puts "[terminator]: #{msg}"
|
16
|
+
end
|
17
|
+
|
18
|
+
log "started: #{ARGV.inspect}"
|
19
|
+
|
20
|
+
|
21
|
+
parser = OptionParser.new do |o|
|
22
|
+
o.banner = 'Usage: term.rb [options]'
|
23
|
+
|
24
|
+
o.on("--exit code=#{@opts[:code]}", 'set exit code') do |code|
|
25
|
+
@opts[:code] = code.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
o.on("--sleep sec=#{@opts[:sleep]}", 'Sleep before exit') do |sec|
|
29
|
+
@opts[:sleep] = sec.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
o.on('--term', 'SIGTERM self') do
|
33
|
+
@opts[:term] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
o.on("--term-code=#{@opts[:term_code]}", 'exit code when SIGTERM catched') do |code|
|
37
|
+
@opts[:term_code] = code.to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
o.on('--kill', 'SIGKILL self') do
|
41
|
+
@opts[:kill] = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
parser.parse!
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
%w[INT TERM].each do |sig|
|
50
|
+
trap(sig) do
|
51
|
+
log "signal: #{sig}. exit: #{@opts[:term_code]}"
|
52
|
+
exit(@opts[:term_code])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
log 'sleep...'
|
57
|
+
sleep @opts[:sleep]
|
58
|
+
|
59
|
+
log 'go'
|
60
|
+
if @opts[:kill]
|
61
|
+
log 'kill self'
|
62
|
+
::Process.kill('KILL', $PROCESS_ID)
|
63
|
+
end
|
64
|
+
|
65
|
+
if @opts[:term]
|
66
|
+
log 'term self'
|
67
|
+
::Process.kill('TERM', $PROCESS_ID)
|
68
|
+
end
|
69
|
+
|
70
|
+
log "normal exit with: #{@opts[:code]}"
|
71
|
+
exit @opts[:code]
|