data/exec_standard_entrypoint.rb.erb (106 lines of code) (raw):
# 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