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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 449a17a0c2d92d72ceddac8091af43e21e16d730d063458b58411385cd659fa2
4
- data.tar.gz: db5e5b5446c3d18b16d886fcd5409380fcd49ed1f6d120bdc7f29f3e6740146a
3
+ metadata.gz: bdb07fa81c4837ef64e8bffa9abaed4abdb6b99caeff476a7d4b11b2b36be8f1
4
+ data.tar.gz: 979f05736d9f1ab607edf33371c6a3f1ffadcdd60ca05c8d6d57af2a401e1808
5
5
  SHA512:
6
- metadata.gz: cc1f6302b922e313c85327d3348d116905d2f889260695f680c946eedcc769c84bf510a3b0439ed499e3f8528f2c8c8bde89cce0a5bb2e29a600ab04987a7631
7
- data.tar.gz: 59553a444c16d811403ce3e288aaf44d454ed1bb100896b4b6a30645664554b6004dfc8e748ee66d81227ce7792a1a43c4c58d6d63a549f43a2ea45b879e2811
6
+ metadata.gz: 691018ead4ca0970d5ba99006e2931aa7da6b6d116adf6e2d041282c22d24f67985a0eb581659d82bb991c8f64e22b31f9bee6733b02de4fb54ca70187f0f791
7
+ data.tar.gz: 5c9c8a5cbea5e16a36332b3aab35f02472322b474d075bf923dd671c6d56f49a78677c6d77370f77eb80388ae1cfa0ffb8148232178433b201cd4e3da010e625
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pytty (0.4.0)
4
+ pytty (0.4.1)
5
5
  async-websocket
6
6
  clamp (~> 1.3)
7
7
  falcon
data/build/linux.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env sh
2
2
  set -e
3
3
 
4
- version=${TRAVIS_TAG#"v"}
4
+ version=${1#"v"}
5
5
  package="tmp/pyttyd-linux-amd64-${version}"
6
6
  rubyc -o "$package" -d /tmp/pytty-build --make-args="-j16 --silent" pyttyd
7
7
  ./"$package" --version
@@ -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
- $stdin.raw!
12
- $stdin.echo = false
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
@@ -18,7 +18,7 @@ module Pytty
18
18
  }.to_json
19
19
 
20
20
  response = internet.post("#{Pytty::Client.host_url}/v1/yield", headers, [body])
21
- JSON.parse(response.body.read)
21
+ [response, JSON.parse(response.body.read)]
22
22
  end
23
23
  end
24
24
  end
@@ -1,15 +1,17 @@
1
1
  require "clamp"
2
2
 
3
3
  require_relative "../common/cli/version_command"
4
- require_relative "cli/run_command"
5
- require_relative "cli/stream_command"
6
- require_relative "cli/yield_command"
4
+
7
5
  require_relative "cli/ps_command"
8
- require_relative "cli/kill_command"
9
- require_relative "cli/rm_command"
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"
@@ -16,7 +16,7 @@ module Pytty
16
16
  end.wait
17
17
 
18
18
  unless quiet?
19
- puts "id\trunning\tcmd"
19
+ puts "id\trunning\tstatus\tcmd"
20
20
  puts "-"*40
21
21
  end
22
22
  for process_yield_json in process_yield_jsons do
@@ -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
- json = Pytty::Client::Api::Yield.run id: name, cmd: cmd_list, env: {}
20
- process_yield = Pytty::Client::ProcessYield.from_json json
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
- process_yield = Async.run do
16
- json = Pytty::Client::Api::Yield.run cmd: cmd_list, id: name, env: {}
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
- puts process_yield.id
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
- p ex
47
+ puts "----"
48
+ p ["attach", ex]
48
49
  ensure
49
- puts "closing body"
50
+ puts "closing attach: #{req.object_id}"
50
51
  body.close
51
52
  end
52
53
 
@@ -18,8 +18,8 @@ module Pytty
18
18
  bound_endpoint = Async::IO::SharedEndpoint.bound(endpoint)
19
19
 
20
20
  server = Falcon::Server.new(app, bound_endpoint, endpoint.protocol, endpoint.scheme)
21
- puts "serving at #{url}"
22
21
  server.run
22
+ puts "serving at #{url}"
23
23
  end
24
24
  end
25
25
  end
@@ -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
- Pytty::Daemon::Api::Server.run url: url_parts.join("")
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
- process_yield = Pytty::Daemon::ProcessYield.new cmd, id: id, env: env
21
- Pytty::Daemon.yields[process_yield.id] = process_yield
22
- Pytty::Daemon.dump
23
-
24
- [200, process_yield]
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
- p ["exited", @cmd, "status", @status]
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
- sig_upcased = sig.upcase
113
- p ["signaling", sig_upcased, "to", @pid]
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
@@ -1,3 +1,3 @@
1
1
  module Pytty
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1"
3
3
  end
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.0
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
-