pytty 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/build/linux.sh +1 -1
- data/lib/pytty/client/api/attach.rb +11 -7
- data/lib/pytty/client/api/yield.rb +1 -1
- data/lib/pytty/client/cli.rb +7 -5
- data/lib/pytty/client/cli/ps_command.rb +1 -1
- data/lib/pytty/client/cli/root_command.rb +0 -2
- data/lib/pytty/client/cli/run_command.rb +6 -2
- data/lib/pytty/client/cli/yield_command.rb +9 -4
- data/lib/pytty/client/process_yield.rb +5 -2
- data/lib/pytty/daemon.rb +0 -3
- data/lib/pytty/daemon/api/router.rb +6 -5
- data/lib/pytty/daemon/api/server.rb +1 -1
- data/lib/pytty/daemon/cli/serve_command.rb +18 -2
- data/lib/pytty/daemon/components/handler.rb +8 -5
- data/lib/pytty/daemon/process_yield.rb +19 -11
- data/lib/pytty/version.rb +1 -1
- metadata +1 -2
- data/lib/pytty/client/cli/kill_command.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdb07fa81c4837ef64e8bffa9abaed4abdb6b99caeff476a7d4b11b2b36be8f1
|
4
|
+
data.tar.gz: 979f05736d9f1ab607edf33371c6a3f1ffadcdd60ca05c8d6d57af2a401e1808
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 691018ead4ca0970d5ba99006e2931aa7da6b6d116adf6e2d041282c22d24f67985a0eb581659d82bb991c8f64e22b31f9bee6733b02de4fb54ca70187f0f791
|
7
|
+
data.tar.gz: 5c9c8a5cbea5e16a36332b3aab35f02472322b474d075bf923dd671c6d56f49a78677c6d77370f77eb80388ae1cfa0ffb8148232178433b201cd4e3da010e625
|
data/Gemfile.lock
CHANGED
data/build/linux.sh
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "io/console"
|
2
|
+
|
1
3
|
module Pytty
|
2
4
|
module Client
|
3
5
|
module Api
|
@@ -8,17 +10,19 @@ module Pytty
|
|
8
10
|
headers = [['accept', 'application/json']]
|
9
11
|
body = {}.to_json
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
# stdin_body = {
|
14
|
+
# c: '\f'
|
15
|
+
# }.to_json
|
16
|
+
# response = internet.post("#{Pytty::Client.host_url}/v1/stdin/#{id}", headers, [stdin_body])
|
17
|
+
|
18
|
+
if interactive
|
19
|
+
$stdin.raw!
|
20
|
+
$stdin.echo = false
|
21
|
+
end
|
13
22
|
async_stdin = Async::IO::Stream.new(
|
14
23
|
Async::IO::Generic.new($stdin)
|
15
24
|
)
|
16
25
|
|
17
|
-
stdin_body = {
|
18
|
-
c: "\f"
|
19
|
-
}.to_json
|
20
|
-
response = internet.post("#{Pytty::Client.host_url}/v1/stdin/#{id}", headers, [stdin_body])
|
21
|
-
|
22
26
|
detach_sequence_started = false
|
23
27
|
while c = async_stdin.read(1) do
|
24
28
|
detach = false
|
data/lib/pytty/client/cli.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
require "clamp"
|
2
2
|
|
3
3
|
require_relative "../common/cli/version_command"
|
4
|
-
|
5
|
-
require_relative "cli/stream_command"
|
6
|
-
require_relative "cli/yield_command"
|
4
|
+
|
7
5
|
require_relative "cli/ps_command"
|
8
|
-
|
9
|
-
require_relative "cli/
|
6
|
+
|
7
|
+
require_relative "cli/yield_command"
|
10
8
|
require_relative "cli/spawn_command"
|
9
|
+
|
10
|
+
require_relative "cli/rm_command"
|
11
11
|
require_relative "cli/signal_command"
|
12
12
|
require_relative "cli/attach_command"
|
13
13
|
require_relative "cli/stdout_command"
|
14
14
|
|
15
|
+
require_relative "cli/run_command"
|
16
|
+
|
15
17
|
require_relative "cli/root_command"
|
@@ -13,10 +13,8 @@ module Pytty
|
|
13
13
|
|
14
14
|
subcommand ["version"], "Show version information", Pytty::Common::Cli::VersionCommand
|
15
15
|
subcommand ["run"], "run", RunCommand
|
16
|
-
subcommand ["stream"], "stream", StreamCommand
|
17
16
|
subcommand ["yield"], "yield", YieldCommand
|
18
17
|
subcommand ["ps"], "ps", PsCommand
|
19
|
-
subcommand ["kill"], "kill", KillCommand
|
20
18
|
subcommand ["rm"], "rm", RmCommand
|
21
19
|
subcommand ["spawn"], "spawn", SpawnCommand
|
22
20
|
subcommand ["signal"], "signal", SignalCommand
|
@@ -16,8 +16,12 @@ module Pytty
|
|
16
16
|
|
17
17
|
def execute
|
18
18
|
Async.run do |task|
|
19
|
-
|
20
|
-
|
19
|
+
response, body = Pytty::Client::Api::Yield.run id: name, cmd: cmd_list, env: {}
|
20
|
+
unless response.status == 200
|
21
|
+
puts body
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
process_yield = Pytty::Client::ProcessYield.from_json body
|
21
25
|
unless detach?
|
22
26
|
task.async do
|
23
27
|
process_yield.attach interactive: interactive?
|
@@ -12,12 +12,17 @@ module Pytty
|
|
12
12
|
option ["--name"], "NAME", "name"
|
13
13
|
|
14
14
|
def execute
|
15
|
-
|
16
|
-
|
17
|
-
::Pytty::Client::ProcessYield.from_json json
|
15
|
+
response, json = Async.run do
|
16
|
+
Pytty::Client::Api::Yield.run cmd: cmd_list, id: name, env: {}
|
18
17
|
end.wait
|
19
18
|
|
20
|
-
|
19
|
+
if response.status == 200
|
20
|
+
process_yield = ::Pytty::Client::ProcessYield.from_json json
|
21
|
+
puts process_yield.id
|
22
|
+
else
|
23
|
+
puts json
|
24
|
+
end
|
25
|
+
|
21
26
|
end
|
22
27
|
end
|
23
28
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module Pytty
|
2
2
|
module Client
|
3
3
|
class ProcessYield
|
4
|
-
def initialize(id:, cmd:, env:, pid:)
|
4
|
+
def initialize(id:, cmd:, env:, pid:, status:)
|
5
5
|
@cmd = cmd
|
6
6
|
@env = env
|
7
7
|
@pid = pid
|
8
8
|
@id = id
|
9
|
+
@status = status
|
9
10
|
end
|
10
11
|
|
11
12
|
attr_reader :id
|
@@ -15,7 +16,8 @@ module Pytty
|
|
15
16
|
id: json.fetch("id"),
|
16
17
|
cmd: json.fetch("cmd"),
|
17
18
|
env: json.fetch("env"),
|
18
|
-
pid: json.fetch("pid")
|
19
|
+
pid: json.fetch("pid"),
|
20
|
+
status: json.fetch("status")
|
19
21
|
})
|
20
22
|
end
|
21
23
|
def running?
|
@@ -25,6 +27,7 @@ module Pytty
|
|
25
27
|
fields = []
|
26
28
|
fields << @id
|
27
29
|
fields << running?
|
30
|
+
fields << @status
|
28
31
|
fields << @cmd.join(" ")
|
29
32
|
fields.join("\t")
|
30
33
|
end
|
data/lib/pytty/daemon.rb
CHANGED
@@ -21,11 +21,8 @@ module Pytty
|
|
21
21
|
objs.each do |k,obj|
|
22
22
|
process_yield = ProcessYield.new obj["cmd"], id: obj["id"], env: obj["env"]
|
23
23
|
@@yields[obj["id"]] = process_yield
|
24
|
-
print "spawning #{process_yield.cmd} ... "
|
25
24
|
process_yield.spawn if obj["running"]
|
26
|
-
puts "done"
|
27
25
|
end
|
28
|
-
|
29
26
|
end
|
30
27
|
|
31
28
|
def self.pytty_path
|
@@ -29,24 +29,25 @@ module Pytty
|
|
29
29
|
rescue Exception => ex
|
30
30
|
p ex
|
31
31
|
ensure
|
32
|
-
puts "closing body"
|
33
32
|
body.close
|
34
33
|
end
|
35
34
|
|
36
35
|
[200, {'content-type' => 'text/html; charset=utf-8'}, body]
|
37
36
|
when "attach"
|
38
37
|
process_yield = Pytty::Daemon.yields[params["id"]]
|
38
|
+
return [404, {'content-type' => 'text/html; charset=utf-8'}, ["does not exist"]] unless process_yield
|
39
|
+
|
40
|
+
puts "got attach: #{req.object_id}"
|
39
41
|
body = Async::HTTP::Body::Writable.new
|
40
42
|
|
41
43
|
Async::Task.current.async do |task|
|
42
44
|
notification = process_yield.add_stdout body
|
43
|
-
p ["blocking", notification]
|
44
45
|
notification.wait
|
45
|
-
p "got notification"
|
46
46
|
rescue Exception => ex
|
47
|
-
|
47
|
+
puts "----"
|
48
|
+
p ["attach", ex]
|
48
49
|
ensure
|
49
|
-
puts "closing
|
50
|
+
puts "closing attach: #{req.object_id}"
|
50
51
|
body.close
|
51
52
|
end
|
52
53
|
|
@@ -25,9 +25,25 @@ module Pytty
|
|
25
25
|
"1234"
|
26
26
|
end
|
27
27
|
|
28
|
-
Async::Reactor.run do
|
28
|
+
Async::Reactor.run do |task|
|
29
29
|
Pytty::Daemon.load
|
30
|
-
|
30
|
+
server_task = task.async do
|
31
|
+
Pytty::Daemon::Api::Server.run url: url_parts.join("")
|
32
|
+
end
|
33
|
+
|
34
|
+
shutdown = lambda do |signo|
|
35
|
+
puts "\r"
|
36
|
+
puts "Got: #{Signal.signame(signo)}"
|
37
|
+
server_task.stop
|
38
|
+
Pytty::Daemon.yields.each do |id,process_yield|
|
39
|
+
process_yield.signal "kill"
|
40
|
+
puts id
|
41
|
+
end
|
42
|
+
puts "bye."
|
43
|
+
end
|
44
|
+
|
45
|
+
Signal.trap "INT", shutdown
|
46
|
+
Signal.trap "TERM", shutdown
|
31
47
|
end
|
32
48
|
end
|
33
49
|
end
|
@@ -17,11 +17,14 @@ module Pytty
|
|
17
17
|
env = params.dig "env"
|
18
18
|
id = params.dig "id"
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
if Pytty::Daemon.yields[id]
|
21
|
+
[500, "already exists"]
|
22
|
+
else
|
23
|
+
process_yield = Pytty::Daemon::ProcessYield.new cmd, id: id, env: env
|
24
|
+
Pytty::Daemon.yields[process_yield.id] = process_yield
|
25
|
+
Pytty::Daemon.dump
|
26
|
+
[200, process_yield]
|
27
|
+
end
|
25
28
|
else
|
26
29
|
raise "unknown: #{component}"
|
27
30
|
end
|
@@ -44,6 +44,7 @@ module Pytty
|
|
44
44
|
|
45
45
|
def spawn
|
46
46
|
return false if running?
|
47
|
+
@status = nil
|
47
48
|
|
48
49
|
executable, args = @cmd
|
49
50
|
# @env.merge!({
|
@@ -59,8 +60,6 @@ module Pytty
|
|
59
60
|
File.open stdout_path, "r"
|
60
61
|
)
|
61
62
|
Async::Task.current.async do |task|
|
62
|
-
p ["spawn", executable, args, @env]
|
63
|
-
|
64
63
|
real_stdout, real_stdin, pid = PTY.spawn @env, executable, *args
|
65
64
|
@pid = pid
|
66
65
|
async_stdout = Async::IO::Generic.new real_stdout
|
@@ -70,6 +69,7 @@ module Pytty
|
|
70
69
|
while c = @stdin.dequeue do
|
71
70
|
async_stdin.write c
|
72
71
|
end
|
72
|
+
rescue Async::Stop => ex
|
73
73
|
rescue Exception => ex
|
74
74
|
puts "async_stdin.write: #{ex.inspect}"
|
75
75
|
end
|
@@ -78,40 +78,48 @@ module Pytty
|
|
78
78
|
while c = async_stdout.read(1)
|
79
79
|
stdout_appender.write c
|
80
80
|
stdout_appender.flush
|
81
|
+
|
81
82
|
@stdouts.each do |notification, stdout|
|
82
83
|
begin
|
83
84
|
stdout.write c
|
84
|
-
rescue Errno::EPIPE => ex
|
85
|
+
rescue Errno::EPIPE, Errno::EPROTOTYPE => ex
|
85
86
|
notification.signal
|
86
87
|
@stdouts.delete notification
|
87
88
|
end
|
88
89
|
end
|
89
90
|
end
|
91
|
+
rescue Async::Stop
|
92
|
+
signal "kill"
|
93
|
+
rescue Exception => ex
|
94
|
+
p ["async_stdout.read", ex]
|
90
95
|
ensure
|
91
96
|
task_stdin_writer.stop
|
92
97
|
Process.wait(@pid)
|
93
|
-
@status = $?.exitstatus
|
98
|
+
@status = if $?.exitstatus
|
99
|
+
$?.exitstatus
|
100
|
+
else
|
101
|
+
Signal.signame $?.termsig
|
102
|
+
end
|
103
|
+
puts "exited #{@id} with status: #{@status}"
|
104
|
+
|
94
105
|
@pid = nil
|
95
106
|
stdout_appender.close
|
96
107
|
|
97
108
|
@stdouts.each do |notification, stdout|
|
98
|
-
p ["notifying: #{notification}"]
|
99
109
|
notification.signal
|
100
110
|
@stdouts.delete notification
|
101
111
|
end
|
102
|
-
|
112
|
+
Pytty::Daemon.dump
|
103
113
|
end
|
104
114
|
end.wait
|
105
115
|
|
106
|
-
puts "spawned"
|
107
|
-
p ["@stdouts", @stdouts]
|
116
|
+
puts "spawned #{id}"
|
108
117
|
return true
|
109
118
|
end
|
110
119
|
|
111
120
|
def signal(sig)
|
112
|
-
|
113
|
-
|
114
|
-
Process.kill(sig_upcased, @pid)
|
121
|
+
return unless @pid
|
122
|
+
Process.kill(sig.upcase, @pid)
|
115
123
|
end
|
116
124
|
end
|
117
125
|
end
|
data/lib/pytty/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pytty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matti Paksula
|
@@ -197,7 +197,6 @@ files:
|
|
197
197
|
- lib/pytty/client/api/yield.rb
|
198
198
|
- lib/pytty/client/cli.rb
|
199
199
|
- lib/pytty/client/cli/attach_command.rb
|
200
|
-
- lib/pytty/client/cli/kill_command.rb
|
201
200
|
- lib/pytty/client/cli/ps_command.rb
|
202
201
|
- lib/pytty/client/cli/rm_command.rb
|
203
202
|
- lib/pytty/client/cli/root_command.rb
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'async'
|
3
|
-
require 'async/http'
|
4
|
-
require 'async/http/internet'
|
5
|
-
require 'json'
|
6
|
-
|
7
|
-
module Pytty
|
8
|
-
module Client
|
9
|
-
module Cli
|
10
|
-
class KillCommand < Clamp::Command
|
11
|
-
parameter "ID ...", "id"
|
12
|
-
|
13
|
-
def execute
|
14
|
-
Async.run do
|
15
|
-
internet = Async::HTTP::Internet.new
|
16
|
-
headers = [['accept', 'application/json']]
|
17
|
-
body = {
|
18
|
-
signal: "KILL"
|
19
|
-
}.to_json
|
20
|
-
|
21
|
-
for id in id_list do
|
22
|
-
response = internet.post("#{Pytty::Client.host_url}/v1/signal/#{id}", headers, [body])
|
23
|
-
puts response.read
|
24
|
-
end
|
25
|
-
ensure
|
26
|
-
internet.close
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|