...

Source file src/github.com/letsencrypt/boulder/test/load-generator/main.go

Documentation: github.com/letsencrypt/boulder/test/load-generator

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"flag"
     7  	"fmt"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/letsencrypt/boulder/cmd"
    14  )
    15  
    16  type Config struct {
    17  	// Execution plan parameters
    18  	Plan struct {
    19  		Actions   []string // things to do
    20  		Rate      int64    // requests / s
    21  		RateDelta string   // requests / s^2
    22  		Runtime   string   // how long to run for
    23  	}
    24  	ExternalState     string   // path to file to load/save registrations etc to/from
    25  	DontSaveState     bool     // don't save changes to external state
    26  	DirectoryURL      string   // ACME server directory URL
    27  	DomainBase        string   // base domain name to create authorizations for
    28  	HTTPOneAddrs      []string // addresses to listen for http-01 validation requests on
    29  	TLSALPNOneAddrs   []string // addresses to listen for tls-alpn-01 validation requests on
    30  	DNSAddrs          []string // addresses to listen for DNS requests on
    31  	FakeDNS           string   // IPv6 address to use for all DNS A requests
    32  	RealIP            string   // value of the Real-IP header to use when bypassing CDN
    33  	RegEmail          string   // email to use in registrations
    34  	Results           string   // path to save metrics to
    35  	MaxRegs           int      // maximum number of registrations to create
    36  	MaxNamesPerCert   int      // maximum number of names on one certificate/order
    37  	ChallengeStrategy string   // challenge selection strategy ("random", "http-01", "dns-01", "tls-alpn-01")
    38  	RevokeChance      float32  // chance of revoking certificate after issuance, between 0.0 and 1.0
    39  }
    40  
    41  func main() {
    42  	configPath := flag.String("config", "", "Path to configuration file for load-generator")
    43  	resultsPath := flag.String("results", "", "Path to latency results file")
    44  	rateArg := flag.Int("rate", 0, "")
    45  	runtimeArg := flag.String("runtime", "", "")
    46  	deltaArg := flag.String("delta", "", "")
    47  	flag.Parse()
    48  
    49  	if *configPath == "" {
    50  		fmt.Fprintf(os.Stderr, "-config argument must not be empty\n")
    51  		os.Exit(1)
    52  	}
    53  
    54  	configBytes, err := os.ReadFile(*configPath)
    55  	if err != nil {
    56  		fmt.Fprintf(os.Stderr, "Failed to read load-generator config file %q: %s\n", *configPath, err)
    57  		os.Exit(1)
    58  	}
    59  	var config Config
    60  	err = json.Unmarshal(configBytes, &config)
    61  	if err != nil {
    62  		fmt.Fprintf(os.Stderr, "Failed to parse load-generator config file: %s\n", err)
    63  		os.Exit(1)
    64  	}
    65  
    66  	if *resultsPath != "" {
    67  		config.Results = *resultsPath
    68  	}
    69  	if *rateArg != 0 {
    70  		config.Plan.Rate = int64(*rateArg)
    71  	}
    72  	if *runtimeArg != "" {
    73  		config.Plan.Runtime = *runtimeArg
    74  	}
    75  	if *deltaArg != "" {
    76  		config.Plan.RateDelta = *deltaArg
    77  	}
    78  
    79  	s, err := New(
    80  		config.DirectoryURL,
    81  		config.DomainBase,
    82  		config.RealIP,
    83  		config.MaxRegs,
    84  		config.MaxNamesPerCert,
    85  		config.Results,
    86  		config.RegEmail,
    87  		config.Plan.Actions,
    88  		config.ChallengeStrategy,
    89  		config.RevokeChance,
    90  	)
    91  	cmd.FailOnError(err, "Failed to create load generator")
    92  
    93  	if config.ExternalState != "" {
    94  		err = s.Restore(config.ExternalState)
    95  		cmd.FailOnError(err, "Failed to load registration snapshot")
    96  	}
    97  
    98  	runtime, err := time.ParseDuration(config.Plan.Runtime)
    99  	cmd.FailOnError(err, "Failed to parse plan runtime")
   100  
   101  	var delta *RateDelta
   102  	if config.Plan.RateDelta != "" {
   103  		parts := strings.Split(config.Plan.RateDelta, "/")
   104  		if len(parts) != 2 {
   105  			fmt.Fprintf(os.Stderr, "RateDelta is malformed")
   106  			os.Exit(1)
   107  		}
   108  		rate, err := strconv.Atoi(parts[0])
   109  		cmd.FailOnError(err, "Failed to parse increase portion of RateDelta")
   110  		period, err := time.ParseDuration(parts[1])
   111  		cmd.FailOnError(err, "Failed to parse period portion of RateDelta")
   112  		delta = &RateDelta{Inc: int64(rate), Period: period}
   113  	}
   114  
   115  	if len(config.HTTPOneAddrs) == 0 &&
   116  		len(config.TLSALPNOneAddrs) == 0 &&
   117  		len(config.DNSAddrs) == 0 {
   118  		cmd.Fail("There must be at least one bind address in " +
   119  			"HTTPOneAddrs, TLSALPNOneAddrs or DNSAddrs\n")
   120  	}
   121  
   122  	ctx, cancel := context.WithCancel(context.Background())
   123  	go cmd.CatchSignals(cancel)
   124  
   125  	err = s.Run(
   126  		ctx,
   127  		config.HTTPOneAddrs,
   128  		config.TLSALPNOneAddrs,
   129  		config.DNSAddrs,
   130  		config.FakeDNS,
   131  		Plan{
   132  			Runtime: runtime,
   133  			Rate:    config.Plan.Rate,
   134  			Delta:   delta,
   135  		})
   136  	cmd.FailOnError(err, "Failed to run load generator")
   137  
   138  	if config.ExternalState != "" && !config.DontSaveState {
   139  		err = s.Snapshot(config.ExternalState)
   140  		cmd.FailOnError(err, "Failed to save registration snapshot")
   141  	}
   142  
   143  	fmt.Println("[+] All done, bye bye ^_^")
   144  }
   145  

View as plain text