github.com/letsencrypt/boulder@v0.20251208.0/test/health-checker/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "os" 8 "strings" 9 "time" 10 11 "github.com/jmhodges/clock" 12 healthpb "google.golang.org/grpc/health/grpc_health_v1" 13 14 "github.com/letsencrypt/boulder/cmd" 15 bgrpc "github.com/letsencrypt/boulder/grpc" 16 "github.com/letsencrypt/boulder/metrics" 17 ) 18 19 type config struct { 20 GRPC *cmd.GRPCClientConfig 21 TLS *cmd.TLSConfig 22 } 23 24 func main() { 25 defer cmd.AuditPanic() 26 27 // Flag and config parsing and validation. 28 configFile := flag.String("config", "", "Path to the TLS configuration file") 29 serverAddr := flag.String("addr", "", "Address of the gRPC server to check") 30 hostOverride := flag.String("host-override", "", "Hostname to use for TLS certificate validation") 31 flag.Parse() 32 if *configFile == "" { 33 flag.Usage() 34 os.Exit(1) 35 } 36 37 var c config 38 err := cmd.ReadConfigFile(*configFile, &c) 39 cmd.FailOnError(err, "failed to read json config") 40 41 if c.GRPC.ServerAddress == "" && *serverAddr == "" { 42 cmd.Fail("must specify either -addr flag or client.ServerAddress config") 43 } else if c.GRPC.ServerAddress != "" && *serverAddr != "" { 44 cmd.Fail("cannot specify both -addr flag and client.ServerAddress config") 45 } else if c.GRPC.ServerAddress == "" { 46 c.GRPC.ServerAddress = *serverAddr 47 } 48 49 tlsConfig, err := c.TLS.Load(metrics.NoopRegisterer) 50 cmd.FailOnError(err, "failed to load TLS credentials") 51 52 if *hostOverride != "" { 53 c.GRPC.HostOverride = *hostOverride 54 } 55 56 // GRPC connection prerequisites. 57 clk := clock.New() 58 59 // Health check retry and timeout. 60 ticker := time.NewTicker(100 * time.Millisecond) 61 ctx, cancel := context.WithTimeout(context.Background(), 10*c.GRPC.Timeout.Duration) 62 defer cancel() 63 64 for { 65 select { 66 case <-ticker.C: 67 _, hostOverride, err := c.GRPC.MakeTargetAndHostOverride() 68 cmd.FailOnError(err, "") 69 70 // Set the hostOverride to match the dNSName in the server certificate. 71 c.GRPC.HostOverride = strings.Replace(hostOverride, ".service.consul", ".boulder", 1) 72 fmt.Fprintf(os.Stderr, "health checking %s (%s)\n", c.GRPC.HostOverride, *serverAddr) 73 74 // Set up the GRPC connection. 75 conn, err := bgrpc.ClientSetup(c.GRPC, tlsConfig, metrics.NoopRegisterer, clk) 76 cmd.FailOnError(err, "failed to connect to service") 77 client := healthpb.NewHealthClient(conn) 78 ctx2, cancel2 := context.WithTimeout(ctx, c.GRPC.Timeout.Duration) 79 defer cancel2() 80 81 // Make the health check. 82 req := &healthpb.HealthCheckRequest{ 83 Service: "", 84 } 85 resp, err := client.Check(ctx2, req) 86 if err != nil { 87 if strings.Contains(err.Error(), "authentication handshake failed") { 88 cmd.Fail(fmt.Sprintf("health checking %s (%s): %s\n", c.GRPC.HostOverride, *serverAddr, err)) 89 } 90 fmt.Fprintf(os.Stderr, "health checking %s (%s): %s\n", c.GRPC.HostOverride, *serverAddr, err) 91 } else if resp.Status == healthpb.HealthCheckResponse_SERVING { 92 return 93 } else { 94 cmd.Fail(fmt.Sprintf("service %s failed health check with status %s", *serverAddr, resp.Status)) 95 } 96 97 case <-ctx.Done(): 98 cmd.Fail(fmt.Sprintf("timed out waiting for %s health check", *serverAddr)) 99 } 100 } 101 }