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
@@ -0,0 +1,14 @@
1
+ module Aggkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 76,
5
+ :posix_spawnattr_t => 336,
6
+ :sigset_t => 128
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ POSIX_SPAWN_USEVFORK = 64
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Aggkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 4,
5
+ :posix_spawnattr_t => 4,
6
+ :sigset_t => 16
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Aggkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 80,
5
+ :posix_spawnattr_t => 336,
6
+ :sigset_t => 128
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ POSIX_SPAWN_USEVFORK = 64
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Aggkit
2
+ module ChildProcess::Unix::Platform
3
+ SIZEOF = {
4
+ :posix_spawn_file_actions_t => 8,
5
+ :posix_spawnattr_t => 8,
6
+ :sigset_t => 4
7
+ }
8
+ POSIX_SPAWN_RESETIDS = 1
9
+ POSIX_SPAWN_SETPGROUP = 2
10
+ POSIX_SPAWN_SETSIGDEF = 4
11
+ POSIX_SPAWN_SETSIGMASK = 8
12
+ end
13
+ end
@@ -0,0 +1,135 @@
1
+ require 'thread'
2
+
3
+ module Aggkit
4
+ module ChildProcess
5
+ module Unix
6
+ class PosixSpawnProcess < Process
7
+ private
8
+
9
+ @@cwd_lock = Mutex.new
10
+
11
+ def launch_process
12
+ pid_ptr = FFI::MemoryPointer.new(:pid_t)
13
+ actions = Lib::FileActions.new
14
+ attrs = Lib::Attrs.new
15
+
16
+ if io.stdout
17
+ actions.add_dup fileno_for(io.stdout), fileno_for(STDOUT)
18
+ else
19
+ actions.add_open fileno_for(STDOUT), "/dev/null", File::WRONLY, 0644
20
+ end
21
+
22
+ if io.stderr
23
+ actions.add_dup fileno_for(io.stderr), fileno_for(STDERR)
24
+ else
25
+ actions.add_open fileno_for(STDERR), "/dev/null", File::WRONLY, 0644
26
+ end
27
+
28
+ if duplex?
29
+ reader, writer = ::IO.pipe
30
+ actions.add_dup fileno_for(reader), fileno_for(STDIN)
31
+ actions.add_close fileno_for(writer)
32
+ end
33
+
34
+ attrs.pgroup = 0 if leader?
35
+ attrs.flags |= Platform::POSIX_SPAWN_USEVFORK if defined? Platform::POSIX_SPAWN_USEVFORK
36
+
37
+ # wrap in helper classes in order to avoid GC'ed pointers
38
+ argv = Argv.new(@args)
39
+ envp = Envp.new(ENV.to_hash.merge(@environment))
40
+
41
+ ret = 0
42
+ @@cwd_lock.synchronize do
43
+ Dir.chdir(@cwd || Dir.pwd) do
44
+ if ChildProcess.jruby?
45
+ # on JRuby, the current working directory is for some reason not inherited.
46
+ # We'll work around it by making a chdir call through FFI.
47
+ # TODO: report this to JRuby
48
+ Lib.chdir Dir.pwd
49
+ end
50
+
51
+ ret = Lib.posix_spawnp(
52
+ pid_ptr,
53
+ @args.first, # TODO: not sure this matches exec() behaviour
54
+ actions,
55
+ attrs,
56
+ argv,
57
+ envp
58
+ )
59
+ end
60
+ end
61
+
62
+ if duplex?
63
+ io._stdin = writer
64
+ reader.close
65
+ end
66
+
67
+ actions.free
68
+ attrs.free
69
+
70
+ if ret != 0
71
+ raise LaunchError, "#{Lib.strerror(ret)} (#{ret})"
72
+ end
73
+
74
+ @pid = pid_ptr.read_int
75
+ ::Process.detach(@pid) if detach?
76
+ end
77
+
78
+ if ChildProcess.jruby?
79
+ def fileno_for(obj)
80
+ ChildProcess::JRuby.posix_fileno_for(obj)
81
+ end
82
+ else
83
+ def fileno_for(obj)
84
+ obj.fileno
85
+ end
86
+ end
87
+
88
+ class Argv
89
+ def initialize(args)
90
+ @ptrs = args.map do |e|
91
+ if e.include?("\0")
92
+ raise ArgumentError, "argument cannot contain null bytes: #{e.inspect}"
93
+ end
94
+
95
+ FFI::MemoryPointer.from_string(e.to_s)
96
+ end
97
+
98
+ @ptrs << FFI::Pointer.new(0)
99
+ end
100
+
101
+ def to_ptr
102
+ argv = FFI::MemoryPointer.new(:pointer, @ptrs.size)
103
+ argv.put_array_of_pointer(0, @ptrs)
104
+
105
+ argv
106
+ end
107
+ end # Argv
108
+
109
+ class Envp
110
+ def initialize(env)
111
+ @ptrs = env.map do |key, val|
112
+ next if val.nil?
113
+
114
+ if key =~ /=|\0/ || val.include?("\0")
115
+ raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
116
+ end
117
+
118
+ FFI::MemoryPointer.from_string("#{key}=#{val}")
119
+ end.compact
120
+
121
+ @ptrs << FFI::Pointer.new(0)
122
+ end
123
+
124
+ def to_ptr
125
+ env = FFI::MemoryPointer.new(:pointer, @ptrs.size)
126
+ env.put_array_of_pointer(0, @ptrs)
127
+
128
+ env
129
+ end
130
+ end # Envp
131
+
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,91 @@
1
+ module Aggkit
2
+ module ChildProcess
3
+ module Unix
4
+ class Process < AbstractProcess
5
+ attr_reader :pid
6
+
7
+ def io
8
+ @io ||= Unix::IO.new
9
+ end
10
+
11
+ def stop(timeout = 3)
12
+ assert_started
13
+ send_term
14
+
15
+ begin
16
+ return poll_for_exit(timeout)
17
+ rescue TimeoutError
18
+ # try next
19
+ end
20
+
21
+ send_kill
22
+ wait
23
+ rescue Errno::ECHILD, Errno::ESRCH
24
+ # handle race condition where process dies between timeout
25
+ # and send_kill
26
+ true
27
+ end
28
+
29
+ def exited?
30
+ return true if @exit_code
31
+
32
+ assert_started
33
+ pid, status = ::Process.waitpid2(_pid, ::Process::WNOHANG | ::Process::WUNTRACED)
34
+ pid = nil if pid == 0 # may happen on jruby
35
+
36
+ log(:pid => pid, :status => status)
37
+
38
+ if pid
39
+ set_exit_code(status)
40
+ end
41
+
42
+ !!pid
43
+ rescue Errno::ECHILD
44
+ # may be thrown for detached processes
45
+ true
46
+ end
47
+
48
+ def wait
49
+ assert_started
50
+
51
+ if exited?
52
+ exit_code
53
+ else
54
+ _, status = ::Process.waitpid2 _pid
55
+ set_exit_code(status)
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def send_term
62
+ send_signal 'TERM'
63
+ end
64
+
65
+ def send_kill
66
+ send_signal 'KILL'
67
+ end
68
+
69
+ def send_signal(sig)
70
+ assert_started
71
+
72
+ log "sending #{sig}"
73
+ ::Process.kill sig, _pid
74
+ end
75
+
76
+ def set_exit_code(status)
77
+ @exit_code = status.exitstatus || status.termsig
78
+ end
79
+
80
+ def _pid
81
+ if leader?
82
+ -@pid # negative pid == process group
83
+ else
84
+ @pid
85
+ end
86
+ end
87
+
88
+ end # Process
89
+ end # Unix
90
+ end # ChildProcess
91
+ end
@@ -0,0 +1,11 @@
1
+ module Aggkit
2
+ module ChildProcess
3
+ module Unix
4
+ end
5
+ end
6
+ end
7
+
8
+ require "aggkit/childprocess/unix/io"
9
+ require "aggkit/childprocess/unix/process"
10
+ require "aggkit/childprocess/unix/fork_exec_process"
11
+ # PosixSpawnProcess + ffi is required on demand.
@@ -0,0 +1,5 @@
1
+ module Aggkit
2
+ module ChildProcess
3
+ VERSION = '0.9.0'
4
+ end
5
+ end
@@ -0,0 +1,93 @@
1
+ module Aggkit
2
+ module ChildProcess
3
+ module Windows
4
+ class Handle
5
+
6
+ class << self
7
+ private :new
8
+
9
+ def open(pid, access = PROCESS_ALL_ACCESS)
10
+ handle = Lib.open_process(access, false, pid)
11
+
12
+ if handle.null?
13
+ raise Error, Lib.last_error_message
14
+ end
15
+
16
+ h = new(handle, pid)
17
+ return h unless block_given?
18
+
19
+ begin
20
+ yield h
21
+ ensure
22
+ h.close
23
+ end
24
+ end
25
+ end
26
+
27
+ attr_reader :pointer
28
+
29
+ def initialize(pointer, pid)
30
+ unless pointer.kind_of?(FFI::Pointer)
31
+ raise TypeError, "invalid handle: #{pointer.inspect}"
32
+ end
33
+
34
+ if pointer.null?
35
+ raise ArgumentError, "handle is null: #{pointer.inspect}"
36
+ end
37
+
38
+ @pid = pid
39
+ @pointer = pointer
40
+ @closed = false
41
+ end
42
+
43
+ def exit_code
44
+ code_pointer = FFI::MemoryPointer.new :ulong
45
+ ok = Lib.get_exit_code(@pointer, code_pointer)
46
+
47
+ if ok
48
+ code_pointer.get_ulong(0)
49
+ else
50
+ close
51
+ raise Error, Lib.last_error_message
52
+ end
53
+ end
54
+
55
+ def send(signal)
56
+ case signal
57
+ when 0
58
+ exit_code == PROCESS_STILL_ALIVE
59
+ when WIN_SIGINT
60
+ Lib.generate_console_ctrl_event(CTRL_C_EVENT, @pid)
61
+ when WIN_SIGBREAK
62
+ Lib.generate_console_ctrl_event(CTRL_BREAK_EVENT, @pid)
63
+ when WIN_SIGKILL
64
+ ok = Lib.terminate_process(@pointer, @pid)
65
+ Lib.check_error ok
66
+ else
67
+ thread_id = FFI::MemoryPointer.new(:ulong)
68
+ module_handle = Lib.get_module_handle("kernel32")
69
+ proc_address = Lib.get_proc_address(module_handle, "ExitProcess")
70
+
71
+ thread = Lib.create_remote_thread(@pointer, 0, 0, proc_address, 0, 0, thread_id)
72
+ check_error thread
73
+
74
+ Lib.wait_for_single_object(thread, 5)
75
+ true
76
+ end
77
+ end
78
+
79
+ def close
80
+ return if @closed
81
+
82
+ Lib.close_handle(@pointer)
83
+ @closed = true
84
+ end
85
+
86
+ def wait(milliseconds = nil)
87
+ Lib.wait_for_single_object(@pointer, milliseconds || INFINITE)
88
+ end
89
+
90
+ end # Handle
91
+ end # Windows
92
+ end # ChildProcess
93
+ end
@@ -0,0 +1,25 @@
1
+ module Aggkit
2
+ module ChildProcess
3
+ module Windows
4
+ class IO < AbstractIO
5
+ private
6
+
7
+ def check_type(io)
8
+ return if has_fileno?(io)
9
+ return if has_to_io?(io)
10
+
11
+ raise ArgumentError, "#{io.inspect}:#{io.class} must have :fileno or :to_io"
12
+ end
13
+
14
+ def has_fileno?(io)
15
+ io.respond_to?(:fileno) && io.fileno
16
+ end
17
+
18
+ def has_to_io?(io)
19
+ io.respond_to?(:to_io) && io.to_io.kind_of?(::IO)
20
+ end
21
+
22
+ end # IO
23
+ end # Windows
24
+ end # ChildProcess
25
+ end