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  }