pytty 0.4.0 → 0.4.1

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