github.com/letsencrypt/boulder@v0.20251208.0/cmd/admin/admin.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/jmhodges/clock"
     9  
    10  	"github.com/letsencrypt/boulder/cmd"
    11  	"github.com/letsencrypt/boulder/db"
    12  	"github.com/letsencrypt/boulder/features"
    13  	bgrpc "github.com/letsencrypt/boulder/grpc"
    14  	blog "github.com/letsencrypt/boulder/log"
    15  	rapb "github.com/letsencrypt/boulder/ra/proto"
    16  	"github.com/letsencrypt/boulder/sa"
    17  	sapb "github.com/letsencrypt/boulder/sa/proto"
    18  )
    19  
    20  // admin holds all of the external connections necessary to perform admin
    21  // actions on a boulder deployment.
    22  type admin struct {
    23  	rac   rapb.RegistrationAuthorityClient
    24  	sac   sapb.StorageAuthorityClient
    25  	saroc sapb.StorageAuthorityReadOnlyClient
    26  	// TODO: Remove this and only use sac and saroc to interact with the db.
    27  	// We cannot have true dry-run safety as long as we have a direct dbMap.
    28  	dbMap *db.WrappedMap
    29  
    30  	// TODO: Remove this when the dbMap is removed and the dryRunSAC and dryRunRAC
    31  	// handle all dry-run safety.
    32  	dryRun bool
    33  
    34  	clk clock.Clock
    35  	log blog.Logger
    36  }
    37  
    38  // newAdmin constructs a new admin object on the heap and returns a pointer to
    39  // it.
    40  func newAdmin(configFile string, dryRun bool) (*admin, error) {
    41  	// Unlike most boulder service constructors, this does all of its own config
    42  	// parsing and dependency setup. If this is broken out into its own package
    43  	// (outside the //cmd/ directory) those pieces of setup should stay behind
    44  	// in //cmd/admin/main.go, to match other boulder services.
    45  	var c Config
    46  	err := cmd.ReadConfigFile(configFile, &c)
    47  	if err != nil {
    48  		return nil, fmt.Errorf("parsing config file: %w", err)
    49  	}
    50  
    51  	scope, logger, oTelShutdown := cmd.StatsAndLogging(c.Syslog, c.OpenTelemetry, "")
    52  	defer oTelShutdown(context.Background())
    53  	logger.Info(cmd.VersionString())
    54  
    55  	clk := clock.New()
    56  	features.Set(c.Admin.Features)
    57  
    58  	tlsConfig, err := c.Admin.TLS.Load(scope)
    59  	if err != nil {
    60  		return nil, fmt.Errorf("loading TLS config: %w", err)
    61  	}
    62  
    63  	var rac rapb.RegistrationAuthorityClient = dryRunRAC{log: logger}
    64  	if !dryRun {
    65  		raConn, err := bgrpc.ClientSetup(c.Admin.RAService, tlsConfig, scope, clk)
    66  		if err != nil {
    67  			return nil, fmt.Errorf("creating RA gRPC client: %w", err)
    68  		}
    69  		rac = rapb.NewRegistrationAuthorityClient(raConn)
    70  	}
    71  
    72  	saConn, err := bgrpc.ClientSetup(c.Admin.SAService, tlsConfig, scope, clk)
    73  	if err != nil {
    74  		return nil, fmt.Errorf("creating SA gRPC client: %w", err)
    75  	}
    76  	saroc := sapb.NewStorageAuthorityReadOnlyClient(saConn)
    77  
    78  	var sac sapb.StorageAuthorityClient = dryRunSAC{log: logger}
    79  	if !dryRun {
    80  		sac = sapb.NewStorageAuthorityClient(saConn)
    81  	}
    82  
    83  	dbMap, err := sa.InitWrappedDb(c.Admin.DB, nil, logger)
    84  	if err != nil {
    85  		return nil, fmt.Errorf("creating database connection: %w", err)
    86  	}
    87  
    88  	return &admin{
    89  		rac:    rac,
    90  		sac:    sac,
    91  		saroc:  saroc,
    92  		dbMap:  dbMap,
    93  		dryRun: dryRun,
    94  		clk:    clk,
    95  		log:    logger,
    96  	}, nil
    97  }
    98  
    99  // findActiveInputMethodFlag returns a single key from setInputs with a value of `true`,
   100  // if exactly one exists. Otherwise it returns an error.
   101  func findActiveInputMethodFlag(setInputs map[string]bool) (string, error) {
   102  	var activeFlags []string
   103  	for flag, isSet := range setInputs {
   104  		if isSet {
   105  			activeFlags = append(activeFlags, flag)
   106  		}
   107  	}
   108  
   109  	if len(activeFlags) == 0 {
   110  		return "", errors.New("at least one input method flag must be specified")
   111  	} else if len(activeFlags) > 1 {
   112  		return "", fmt.Errorf("more than one input method flag specified: %v", activeFlags)
   113  	}
   114  
   115  	return activeFlags[0], nil
   116  }