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/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]
|