// Package bootstrap assembles the standalone proxy-checker process. Checker // execution is intentionally isolated from Gateway request handling and from // all Redis/PostgreSQL access. package bootstrap import ( "bytes" "context" "errors" "fmt" "reflect" "strings" "google.golang.org/grpc" "google.golang.org/grpc/credentials" controlplanev1 "proxy-pool/gen/controlplane/v1" "proxy-pool/internal/checker/controlplane" "proxy-pool/internal/checker/probe" "proxy-pool/internal/config" "proxy-pool/internal/controlplane/clienttransport" "proxy-pool/internal/domain/workerruntime" ) var ( ErrInvalidOptions = errors.New("invalid checker bootstrap options") ErrStartup = errors.New("checker startup failed") ) type Options struct { ConfigPath string Resolver config.Resolver ControlPlaneAddress string CheckerID string InstanceID string MaxInFlight int SupportedLevels []controlplanev1.CheckLevel GRPCTransport credentials.TransportCredentials } func Run(ctx context.Context, options Options) error { if err := validateOptions(ctx, options); err != nil { return err } configuration, err := loadConfiguration(ctx, options.ConfigPath, options.Resolver) if err != nil { return fmt.Errorf("%w: load configuration: %w", ErrStartup, err) } if !configuration.ControlPlane.Enabled { return errors.Join(ErrInvalidOptions, errors.New("controlPlane must be enabled")) } transport, err := clienttransport.New( configuration.ControlPlane, options.ControlPlaneAddress, configuration.ControlPlane.CheckerTLS, options.GRPCTransport, "checkerTLS", ) if err != nil { return fmt.Errorf("%w: %w", ErrInvalidOptions, err) } connection, err := grpc.NewClient(options.ControlPlaneAddress, grpc.WithTransportCredentials(transport), grpc.WithDefaultCallOptions( grpc.MaxCallRecvMsgSize(configuration.ControlPlane.MaxMessageBytes), grpc.MaxCallSendMsgSize(configuration.ControlPlane.MaxMessageBytes), ), ) if err != nil { return fmt.Errorf("%w: dial control plane: %w", ErrStartup, err) } defer connection.Close() client := controlplane.NewGeneratedClient(controlplanev1.NewCheckerControlPlaneClient(connection)) runner, err := controlplane.NewRunner(client, probe.NewExecutor(), controlplane.Options{ CheckerID: options.CheckerID, InstanceID: options.InstanceID, MaxInFlight: options.MaxInFlight, SupportedLevels: append([]controlplanev1.CheckLevel(nil), options.SupportedLevels...), ReportBatchSize: options.MaxInFlight, }) if err != nil { return fmt.Errorf("%w: build checker runner: %w", ErrStartup, err) } return runner.Run(ctx) } func validateOptions(ctx context.Context, options Options) error { if ctx == nil || strings.TrimSpace(options.ConfigPath) != options.ConfigPath || options.ConfigPath == "" || nilInterface(options.Resolver) || !clienttransport.ValidDialAddress(options.ControlPlaneAddress) || !workerruntime.ValidIdentifier(options.CheckerID) || !workerruntime.ValidIdentifier(options.InstanceID) || options.MaxInFlight <= 0 || len(options.SupportedLevels) == 0 { return ErrInvalidOptions } return nil } func loadConfiguration(ctx context.Context, path string, resolver config.Resolver) (*config.Config, error) { if ctx == nil || strings.TrimSpace(path) != path || path == "" || nilInterface(resolver) { return nil, ErrInvalidOptions } if err := ctx.Err(); err != nil { return nil, err } content, err := resolver.ReadFile(path) if err != nil { return nil, fmt.Errorf("read configuration %q: %w", path, err) } configuration, err := config.LoadResolved(bytes.NewReader(content), resolver) if err != nil { return nil, fmt.Errorf("decode configuration %q: %w", path, err) } return configuration, nil } func nilInterface(value any) bool { if value == nil { return true } reflected := reflect.ValueOf(value) switch reflected.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: return reflected.IsNil() default: return false } }