aggkit 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +20 -0
  3. data/.gitignore +109 -0
  4. data/.gitlab-ci.yml +66 -0
  5. data/.rspec +4 -0
  6. data/.rubocop.yml +98 -0
  7. data/.travis.yml +30 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +55 -0
  10. data/README.md +96 -0
  11. data/aggkit.gemspec +38 -0
  12. data/bin/agg +167 -0
  13. data/bin/aggconsul +222 -0
  14. data/bin/agglock +71 -0
  15. data/bin/aggmerge +118 -0
  16. data/bin/aggwait +262 -0
  17. data/bin/consul.rb +222 -0
  18. data/bin/locker.rb +71 -0
  19. data/bin/merger.rb +118 -0
  20. data/bin/terminator.rb +71 -0
  21. data/bin/waiter.rb +262 -0
  22. data/docker/Dockerfile +112 -0
  23. data/docker/docker-compose.yml +12 -0
  24. data/docker/down.sh +4 -0
  25. data/docker/run_tests.sh +23 -0
  26. data/lib/aggkit/childprocess/abstract_io.rb +38 -0
  27. data/lib/aggkit/childprocess/abstract_process.rb +194 -0
  28. data/lib/aggkit/childprocess/errors.rb +28 -0
  29. data/lib/aggkit/childprocess/jruby/io.rb +17 -0
  30. data/lib/aggkit/childprocess/jruby/process.rb +161 -0
  31. data/lib/aggkit/childprocess/jruby/pump.rb +55 -0
  32. data/lib/aggkit/childprocess/jruby.rb +58 -0
  33. data/lib/aggkit/childprocess/tools/generator.rb +148 -0
  34. data/lib/aggkit/childprocess/unix/fork_exec_process.rb +72 -0
  35. data/lib/aggkit/childprocess/unix/io.rb +22 -0
  36. data/lib/aggkit/childprocess/unix/lib.rb +188 -0
  37. data/lib/aggkit/childprocess/unix/platform/i386-linux.rb +14 -0
  38. data/lib/aggkit/childprocess/unix/platform/i386-solaris.rb +13 -0
  39. data/lib/aggkit/childprocess/unix/platform/x86_64-linux.rb +14 -0
  40. data/lib/aggkit/childprocess/unix/platform/x86_64-macosx.rb +13 -0
  41. data/lib/aggkit/childprocess/unix/posix_spawn_process.rb +135 -0
  42. data/lib/aggkit/childprocess/unix/process.rb +91 -0
  43. data/lib/aggkit/childprocess/unix.rb +11 -0
  44. data/lib/aggkit/childprocess/version.rb +5 -0
  45. data/lib/aggkit/childprocess/windows/handle.rb +93 -0
  46. data/lib/aggkit/childprocess/windows/io.rb +25 -0
  47. data/lib/aggkit/childprocess/windows/lib.rb +418 -0
  48. data/lib/aggkit/childprocess/windows/process.rb +132 -0
  49. data/lib/aggkit/childprocess/windows/process_builder.rb +177 -0
  50. data/lib/aggkit/childprocess/windows/structs.rb +151 -0
  51. data/lib/aggkit/childprocess/windows.rb +35 -0
  52. data/lib/aggkit/childprocess.rb +213 -0
  53. data/lib/aggkit/env.rb +219 -0
  54. data/lib/aggkit/runner.rb +80 -0
  55. data/lib/aggkit/version.rb +5 -0
  56. data/lib/aggkit/watcher.rb +239 -0
  57. data/lib/aggkit.rb +15 -0
  58. 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]