145 lines
5.0 KiB
Ruby

# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
require "optparse"
module VagrantPlugins
module CommandServe
autoload :Broker, Vagrant.source_root.join("plugins/commands/serve/broker").to_s
autoload :Client, Vagrant.source_root.join("plugins/commands/serve/client").to_s
autoload :Mappers, Vagrant.source_root.join("plugins/commands/serve/mappers").to_s
autoload :Service, Vagrant.source_root.join("plugins/commands/serve/service").to_s
autoload :Type, Vagrant.source_root.join("plugins/commands/serve/type").to_s
autoload :Util, Vagrant.source_root.join("plugins/commands/serve/util").to_s
autoload :SDK, Vagrant.source_root.join("plugins/commands/serve/constants").to_s
autoload :SRV, Vagrant.source_root.join("plugins/commands/serve/constants").to_s
autoload :Empty, Vagrant.source_root.join("plugins/commands/serve/constants").to_s
class << self
attr_accessor :broker
attr_accessor :server
attr_reader :cache
# Loads the required dependencies for this command. This is isolated
# into a method so that the dependencies can be loaded just in time when
# the command is actually executed.
def load_dependencies!
return if @dependencies_loaded
require 'grpc'
require 'grpc/health/checker'
require 'grpc/health/v1/health_services_pb'
# Add conversion patches
require Vagrant.source_root.join("plugins/commands/serve/util/direct_conversions.rb").to_s
# Mark dependencies as loaded
@dependencies_loaded = true
end
end
@cache = Util::Cacher.new
class Command < Vagrant.plugin("2", :command)
DEFAULT_BIND = "localhost"
DEFAULT_PORT_RANGE = 40000..50000
include Util::HasLogger
def self.synopsis
"start Vagrant server"
end
def execute
# Load dependencies before we start
CommandServe.load_dependencies!
options = {
bind: DEFAULT_BIND,
min_port: DEFAULT_PORT_RANGE.first,
max_port: DEFAULT_PORT_RANGE.last,
}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant serve"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("--bind ADDR", "Bind to specific address. Default: #{DEFAULT_BIND}") do |addr|
options[:bind] = addr
end
o.on("--min-port PORT", "Minimum port number to use. Default: #{DEFAULT_PORT_RANGE.first}") do |port|
options[:min_port] = port
end
o.on("--max-port PORT", "Maximum port number to use. Default: #{DEFAULT_PORT_RANGE.last}") do |port|
options[:max_port] = port
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
ports = options[:min_port].to_i .. options[:max_port].to_i
serve(options[:bind], ports)
end
private
def serve(bind_addr = "localhost", ports = DEFAULT_PORT_RANGE)
logger.info("Starting Vagrant GRPC service addr=#{bind_addr.inspect} ports=#{ports.inspect}")
s = GRPC::RpcServer.new
port = nil
ports.each do |p|
begin
port = s.add_http2_port("#{bind_addr}:#{p}", :this_port_is_insecure)
break
rescue RuntimeError
# Assuming port in use, trying next
end
end
raise "Failed to bind GRPC server listener" if port.nil?
health_checker = Grpc::Health::Checker.new
broker = Broker.new(bind: bind_addr, ports: ports)
CommandServe.broker = broker
logger.debug("vagrant grpc broker started for grpc service addr=#{bind_addr} ports=#{ports.inspect}")
[Broker::Streamer,
Service::CommandService,
Service::CommunicatorService,
Service::ConfigService,
Service::GuestService,
Service::HostService,
Service::InternalService,
Service::ProviderService,
Service::ProvisionerService,
Service::PushService,
Service::SyncedFolderService,
].each do |service_klass|
service = service_klass.new(broker: broker)
s.handle(service)
health_checker.add_status(service_klass,
Grpc::Health::V1::HealthCheckResponse::ServingStatus::SERVING)
logger.debug("enabled Vagrant GRPC service: #{service_klass.name.split('::').last}")
end
s.handle(health_checker)
logger.debug("writing connection informatation to stdout for go-plugin")
STDOUT.puts "1|1|tcp|#{bind_addr}:#{port}|grpc"
STDOUT.flush
logger.info("Vagrant GRPC service is now running addr=#{bind_addr.inspect} port=#{port.inspect}")
VagrantPlugins::CommandServe.server = s
s.run_till_terminated_or_interrupted(['EXIT', 'HUP', 'INT', 'QUIT', 'ABRT'])
0
ensure
VagrantPlugins::CommandServe.server = nil
logger.info("Vagrant GRPC service shut down")
end
end
end
end