mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-18 12:37:05 +02:00
Fixes were applied automatically. Import ordering might be questionable, but it's strict: * stdlib * other packages * same package imports Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
208 lines
6.0 KiB
Go
208 lines
6.0 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package factory
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
|
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
|
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
|
"google.golang.org/grpc"
|
|
|
|
grpclog "github.com/talos-systems/talos/pkg/grpc/middleware/log"
|
|
)
|
|
|
|
// Registrator describes the set of methods required in order for a concrete
|
|
// type to be used with the Listen function.
|
|
type Registrator interface {
|
|
Register(*grpc.Server)
|
|
}
|
|
|
|
// Options is the functional options struct.
|
|
type Options struct {
|
|
Port int
|
|
SocketPath string
|
|
Network string
|
|
Config *tls.Config
|
|
LogPrefix string
|
|
LogDestination io.Writer
|
|
ServerOptions []grpc.ServerOption
|
|
StreamInterceptors []grpc.StreamServerInterceptor
|
|
UnaryInterceptors []grpc.UnaryServerInterceptor
|
|
}
|
|
|
|
// Option is the functional option func.
|
|
type Option func(*Options)
|
|
|
|
// Port sets the listen port of the server.
|
|
func Port(o int) Option {
|
|
return func(args *Options) {
|
|
args.Port = o
|
|
}
|
|
}
|
|
|
|
// SocketPath sets the listen unix file socket path of the server.
|
|
func SocketPath(o string) Option {
|
|
return func(args *Options) {
|
|
args.SocketPath = o
|
|
}
|
|
}
|
|
|
|
// Network sets the network type of the listener.
|
|
func Network(o string) Option {
|
|
return func(args *Options) {
|
|
args.Network = o
|
|
}
|
|
}
|
|
|
|
// Config sets the listen port of the server.
|
|
func Config(o *tls.Config) Option {
|
|
return func(args *Options) {
|
|
args.Config = o
|
|
}
|
|
}
|
|
|
|
// ServerOptions appends to the gRPC server options of the server.
|
|
func ServerOptions(o ...grpc.ServerOption) Option {
|
|
return func(args *Options) {
|
|
args.ServerOptions = append(args.ServerOptions, o...)
|
|
}
|
|
}
|
|
|
|
// WithStreamInterceptor appends to the list of gRPC server stream interceptors.
|
|
func WithStreamInterceptor(i grpc.StreamServerInterceptor) Option {
|
|
return func(args *Options) {
|
|
args.StreamInterceptors = append(args.StreamInterceptors, i)
|
|
}
|
|
}
|
|
|
|
// WithUnaryInterceptor appends to the list of gRPC server unary interceptors.
|
|
func WithUnaryInterceptor(i grpc.UnaryServerInterceptor) Option {
|
|
return func(args *Options) {
|
|
args.UnaryInterceptors = append(args.UnaryInterceptors, i)
|
|
}
|
|
}
|
|
|
|
// WithLog sets up request logging to specified destination.
|
|
func WithLog(prefix string, w io.Writer) Option {
|
|
return func(args *Options) {
|
|
args.LogPrefix = prefix
|
|
args.LogDestination = w
|
|
}
|
|
}
|
|
|
|
// WithDefaultLog sets up request logging to default destination.
|
|
func WithDefaultLog() Option {
|
|
return func(args *Options) {
|
|
args.LogDestination = log.Writer()
|
|
}
|
|
}
|
|
|
|
// NewDefaultOptions initializes the Options struct with default values.
|
|
func NewDefaultOptions(setters ...Option) *Options {
|
|
opts := &Options{
|
|
Network: "tcp",
|
|
SocketPath: "/run/factory/factory.sock",
|
|
}
|
|
|
|
for _, setter := range setters {
|
|
setter(opts)
|
|
}
|
|
|
|
if opts.LogDestination != nil {
|
|
logger := log.New(opts.LogDestination, opts.LogPrefix, log.Flags())
|
|
|
|
logMiddleware := grpclog.NewMiddleware(logger)
|
|
|
|
// Logging is installed as the first middleware so that request in the form it was received,
|
|
// and status sent on the wire is logged (error/success). It also tracks whole duration of the
|
|
// request, including other middleware overhead.
|
|
opts.UnaryInterceptors = append([]grpc.UnaryServerInterceptor{logMiddleware.UnaryInterceptor()}, opts.UnaryInterceptors...)
|
|
opts.StreamInterceptors = append([]grpc.StreamServerInterceptor{logMiddleware.StreamInterceptor()}, opts.StreamInterceptors...)
|
|
}
|
|
|
|
// Install default recovery interceptors.
|
|
// Recovery is installed as the last middleware in the chain so that earlier middlewares in the chain
|
|
// have a chance to process the error (e.g. logging middleware).
|
|
opts.StreamInterceptors = append(opts.StreamInterceptors, grpc_recovery.StreamServerInterceptor())
|
|
opts.UnaryInterceptors = append(opts.UnaryInterceptors, grpc_recovery.UnaryServerInterceptor())
|
|
|
|
opts.ServerOptions = append(opts.ServerOptions,
|
|
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(opts.StreamInterceptors...)),
|
|
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(opts.UnaryInterceptors...)),
|
|
)
|
|
|
|
return opts
|
|
}
|
|
|
|
// NewServer builds grpc server and binds it to the Registrator.
|
|
func NewServer(r Registrator, setters ...Option) *grpc.Server {
|
|
opts := NewDefaultOptions(setters...)
|
|
|
|
server := grpc.NewServer(opts.ServerOptions...)
|
|
r.Register(server)
|
|
|
|
return server
|
|
}
|
|
|
|
// NewListener builds listener for grpc server.
|
|
func NewListener(setters ...Option) (net.Listener, error) {
|
|
opts := NewDefaultOptions(setters...)
|
|
|
|
if opts.Network == "tcp" && opts.Port == 0 {
|
|
return nil, errors.New("a port is required for TCP listener")
|
|
}
|
|
|
|
var address string
|
|
|
|
switch opts.Network {
|
|
case "unix":
|
|
address = opts.SocketPath
|
|
|
|
// Unlink the address or we will get the error:
|
|
// bind: address already in use.
|
|
if _, err := os.Stat(address); err == nil {
|
|
if err := os.Remove(address); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Make any dirs on the path to the listening socket.
|
|
if err := os.MkdirAll(filepath.Dir(address), 0o700); err != nil {
|
|
return nil, fmt.Errorf("error creating containing directory for the file socket; %w", err)
|
|
}
|
|
case "tcp":
|
|
address = ":" + strconv.Itoa(opts.Port)
|
|
default:
|
|
return nil, fmt.Errorf("unknown network: %s", opts.Network)
|
|
}
|
|
|
|
return net.Listen(opts.Network, address)
|
|
}
|
|
|
|
// ListenAndServe configures TLS for mutual authtentication by loading the CA into a
|
|
// CertPool and configuring the server's policy for TLS Client Authentication.
|
|
// Once TLS is configured, the gRPC options are built to make use of the TLS
|
|
// configuration and the receiver (Server) is registered to the gRPC server.
|
|
// Finally the gRPC server is started.
|
|
func ListenAndServe(r Registrator, setters ...Option) (err error) {
|
|
server := NewServer(r, setters...)
|
|
|
|
listener, err := NewListener(setters...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return server.Serve(listener)
|
|
}
|