1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
package server
import (
"context"
"net"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/oklog/run"
"google.golang.org/grpc"
"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
)
// mockery -all -case underscore -dir ./gen -output ./gen/mocks
// Run initializes and starts the server. This will block until the server
// exits (by cancelling the associated context set with WithContext or due
// to an unrecoverable error).
func Run(opts ...Option) error {
var cfg options
for _, opt := range opts {
opt(&cfg)
}
// Set defaults
if cfg.Context == nil {
cfg.Context = context.Background()
}
if cfg.Logger == nil {
cfg.Logger = hclog.L()
}
cfg.Logger = cfg.Logger.ResetNamed("vagrant.server")
// Setup our run group since we're going to be starting multiple
// goroutines for all the servers that we want to live/die as a group.
var group run.Group
// We first add an actor that just returns when the context ends. This
// will trigger the rest of the group to end since a group will not exit
// until any of its actors exit.
ctx, cancelCtx := context.WithCancel(cfg.Context)
cfg.Context = ctx
group.Add(func() error {
<-ctx.Done()
return ctx.Err()
}, func(error) { cancelCtx() })
// Setup our gRPC server.
if err := grpcInit(&group, &cfg); err != nil {
return err
}
// Setup our HTTP server.
if err := httpInit(&group, &cfg); err != nil {
return err
}
// Run!
return group.Run()
}
// Option configures Run
type Option func(*options)
// options configure a server and are set by users only using the exported
// Option functions.
type options struct {
// Context is the context to use for the server. When this is cancelled,
// the server will be gracefully shutdown.
Context context.Context
// Logger is the logger to use. This will default to hclog.L() if not set.
Logger hclog.Logger
// Service is the backend service implementation to use for the server.
Service vagrant_server.VagrantServer
// Client to connect to single Ruby Server runtime process
RubyVagrant *plugin.Client
// Extra services to enable on the server
GRPCServices []func(*grpc.Server)
// GRPCListener will setup the gRPC server. If this is nil, then a
// random loopback port will be chosen. The gRPC server must run since it
// serves the HTTP endpoints as well.
GRPCListener net.Listener
// HTTPListener will setup the HTTP server. If this is nil, then
// the HTTP-based API will be disabled.
HTTPListener net.Listener
// AuthChecker, if set, activates authentication checking on the server.
AuthChecker AuthChecker
// BrowserUIEnabled determines if the browser UI should be mounted
BrowserUIEnabled bool
grpcServer *grpc.Server
}
// WithContext sets the context for the server. When this context is cancelled,
// the server will be shut down.
func WithContext(ctx context.Context) Option {
return func(opts *options) { opts.Context = ctx }
}
// WithLogger sets the logger.
func WithLogger(log hclog.Logger) Option {
return func(opts *options) { opts.Logger = log }
}
// WithGRPC sets the GRPC listener. This listener must be closed manually
// by the caller. Prior to closing the listener, it is recommended that you
// cancel the context set with WithContext and wait for Run to return.
func WithGRPC(ln net.Listener) Option {
return func(opts *options) { opts.GRPCListener = ln }
}
// WithHTTP sets the HTTP listener. This listener must be closed manually
// by the caller. Prior to closing the listener, it is recommended that you
// cancel the context set with WithContext and wait for Run to return.
func WithHTTP(ln net.Listener) Option {
return func(opts *options) { opts.HTTPListener = ln }
}
// WithImpl sets the service implementation to serve.
func WithImpl(impl vagrant_server.VagrantServer) Option {
return func(opts *options) { opts.Service = impl }
}
// WithAuthentication configures the server to require authentication.
func WithAuthentication(ac AuthChecker) Option {
return func(opts *options) { opts.AuthChecker = ac }
}
// WithBrowserUI configures the server to enable the browser UI.
func WithBrowserUI(enabled bool) Option {
return func(opts *options) { opts.BrowserUIEnabled = enabled }
}
|