# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require "webrick" require "monitor" require "json" SECRET = <%= secret %> COMMAND = Array(<%= command %>) port = Integer ENV["PORT"] server = ::WEBrick::HTTPServer.new Port: port Status = ::Struct.new :out_lines, :err_lines, :exit_status, :pid, :start_time $status = Status.new [], [], nil, nil, nil $status.extend ::MonitorMixin def do_start $status.synchronize do return unless $status.pid.nil? end $stdout.puts "Executing: #{COMMAND.inspect}" $stdout.flush rout, wout = ::IO.pipe rerr, werr = ::IO.pipe ::Thread.new do rout.each_line do |line| $status.synchronize { $status.out_lines << line } $stdout.puts line $stdout.flush end end ::Thread.new do rerr.each_line do |line| $status.synchronize { $status.err_lines << line } $stderr.puts line $stderr.flush end end start_time = Time.now.to_i pid = ::Process.spawn *COMMAND, err: werr, out: wout werr.close wout.close $status.synchronize do $status.pid = pid $status.start_time = start_time end ::Thread.new do _pid, status = ::Process.wait2 pid $status.synchronize do $status.exit_status = status.exitstatus end end end def get_status outpos: 0, errpos: 0 outlines, errlines, status, start_time = $status.synchronize do [ $status.out_lines[outpos..-1], $status.err_lines[errpos..-1], $status.exit_status, $status.start_time ] end { "outpos" => outpos + outlines.size, "errpos" => errpos + errlines.size, "outlines" => outlines, "errlines" => errlines, "status" => status, "time" => Time.now.to_i - start_time } end server.mount_proc "/_ah/start" do |req, res| do_start end server.mount_proc "/#{SECRET}" do |req, res| do_start status = get_status outpos: req.query["outpos"].to_i, errpos: req.query["errpos"].to_i res.body = JSON.dump status end server.mount_proc "/#{SECRET}/kill" do |req, res| unless req.request_method == "POST" res.status = 404 return end $status.synchronize do if $status.pid.nil? res.status = 404 return end ::Process.kill "SIGTERM", $status.pid end end begin server.start ensure server.shutdown end