From 97e02d84f9c984e22ad74c8d42a0e91f28ec3854 Mon Sep 17 00:00:00 2001 From: Mathilde Gilles Date: Mon, 30 Nov 2020 21:46:49 +0100 Subject: [PATCH] Allow to pass haproxy defaults and global parameters as flag To give better control to users in fine tuning haproxy when using connect, give the ability to pass command line flags to modify haproxy's configuration. The flags are in the form {type}.{name}={value} and can be repeated. Examples: * -haproxy-param "global.tune.bufsize=33792" * -haproxy-param "default.retries=1" --- flags.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++ flags_test.go | 31 +++++++++++++++++++++++++++ haproxy/config.go | 35 +++++++++++++++++++++---------- haproxy/haproxy.go | 2 +- haproxy/options.go | 26 +++++++++++++++++++++++ main.go | 9 ++++++++ 6 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 flags.go create mode 100644 flags_test.go diff --git a/flags.go b/flags.go new file mode 100644 index 0000000..8ba7f1a --- /dev/null +++ b/flags.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/haproxytech/haproxy-consul-connect/haproxy" +) + +type stringSliceFlag []string + +func (i *stringSliceFlag) String() string { + return "" +} + +func (i *stringSliceFlag) Set(value string) error { + *i = append(*i, value) + return nil +} + +func makeHAProxyParams(flags stringSliceFlag) (haproxy.HAProxyParams, error) { + params := haproxy.HAProxyParams{ + Defaults: map[string]string{}, + Globals: map[string]string{}, + } + + for _, flag := range flags { + parts := strings.Split(flag, "=") + if len(parts) != 2 { + return params, fmt.Errorf("bad haproxy-param flag %s, expected {type}.{name}={value}", flag) + } + + dot := strings.IndexByte(parts[0], '.') + if dot == -1 { + return params, fmt.Errorf("bad haproxy-param flag %s, expected {type}.{name}={value}", flag) + } + + var t map[string]string + switch parts[0][:dot] { + case "defaults": + t = params.Defaults + case "global": + t = params.Globals + default: + return params, fmt.Errorf("bad haproxy-param flag %s, param type must be `defaults` or `global`", flag) + } + + t[parts[0][dot+1:]] = parts[1] + } + + return params, nil +} diff --git a/flags_test.go b/flags_test.go new file mode 100644 index 0000000..fa6371e --- /dev/null +++ b/flags_test.go @@ -0,0 +1,31 @@ +package main + +import ( + "testing" + + "github.com/haproxytech/haproxy-consul-connect/haproxy" + "github.com/stretchr/testify/require" +) + +func TestMakeHAProxyParams(t *testing.T) { + flags := stringSliceFlag{ + "defaults.test.with.dots=3", + "defaults.another=abdc", + "global.with.spaces=hey I have spaces", + "global.with.dots=hey.I.have.dots", + } + + r, err := makeHAProxyParams(flags) + require.NoError(t, err) + + require.Equal(t, haproxy.HAProxyParams{ + Defaults: map[string]string{ + "test.with.dots": "3", + "another": "abdc", + }, + Globals: map[string]string{ + "with.spaces": "hey I have spaces", + "with.dots": "hey.I.have.dots", + }, + }, r) +} diff --git a/haproxy/config.go b/haproxy/config.go index f62b8d2..988a2a3 100644 --- a/haproxy/config.go +++ b/haproxy/config.go @@ -3,6 +3,7 @@ package haproxy import ( "crypto/rand" "encoding/base64" + "fmt" "io/ioutil" "os" "path" @@ -14,18 +15,32 @@ import ( log "github.com/sirupsen/logrus" ) +var defaultsHAProxyParams = HAProxyParams{ + Globals: map[string]string{ + "stats": "timeout 2m", + "tune.ssl.default-dh-param": "1024", + "nbproc": "1", + "nbthread": fmt.Sprint(runtime.GOMAXPROCS(0)), + "ulimit-n": "65536", + "maxconn": "32000", + }, + Defaults: map[string]string{ + "http-reuse": "always", + }, +} + const baseCfgTmpl = ` global master-worker stats socket {{.SocketPath}} mode 600 level admin expose-fd listeners - stats timeout 2m - tune.ssl.default-dh-param 1024 - nbproc 1 - nbthread {{.NbThread}} - ulimit-n 65536 + {{ range $k, $v := .HAProxyParams.Globals}} + {{$k}} {{$v}} + {{ end }} defaults - http-reuse always + {{ range $k, $v := .HAProxyParams.Defaults}} + {{$k}} {{$v}} + {{ end }} userlist controller user {{.DataplaneUser}} insecure-password {{.DataplanePass}} @@ -53,11 +68,10 @@ spoe-message check-intentions ` type baseParams struct { - NbThread int SocketPath string DataplaneUser string DataplanePass string - LogsPath string + HAProxyParams HAProxyParams } type haConfig struct { @@ -73,7 +87,7 @@ type haConfig struct { LogsSock string } -func newHaConfig(baseDir string, sd *lib.Shutdown) (*haConfig, error) { +func newHaConfig(baseDir string, params HAProxyParams, sd *lib.Shutdown) (*haConfig, error) { cfg := &haConfig{} sd.Add(1) @@ -119,11 +133,10 @@ func newHaConfig(baseDir string, sd *lib.Shutdown) (*haConfig, error) { cfg.DataplaneUser = "hapeoxy" err = tmpl.Execute(cfgFile, baseParams{ - NbThread: runtime.GOMAXPROCS(0), SocketPath: cfg.StatsSock, - LogsPath: cfg.LogsSock, DataplaneUser: cfg.DataplaneUser, DataplanePass: cfg.DataplanePass, + HAProxyParams: defaultsHAProxyParams.With(params), }) if err != nil { sd.Done() diff --git a/haproxy/haproxy.go b/haproxy/haproxy.go index fd4b625..d39df23 100644 --- a/haproxy/haproxy.go +++ b/haproxy/haproxy.go @@ -47,7 +47,7 @@ func New(consulClient *api.Client, cfg chan consul.Config, opts Options) *HAProx } func (h *HAProxy) Run(sd *lib.Shutdown) error { - hc, err := newHaConfig(h.opts.ConfigBaseDir, sd) + hc, err := newHaConfig(h.opts.ConfigBaseDir, h.opts.HAProxyParams, sd) if err != nil { return err } diff --git a/haproxy/options.go b/haproxy/options.go index e2a365f..9905f41 100644 --- a/haproxy/options.go +++ b/haproxy/options.go @@ -1,5 +1,30 @@ package haproxy +type HAProxyParams struct { + Defaults map[string]string + Globals map[string]string +} + +func (p HAProxyParams) With(other HAProxyParams) HAProxyParams { + new := HAProxyParams{ + Defaults: map[string]string{}, + Globals: map[string]string{}, + } + for k, v := range p.Defaults { + new.Defaults[k] = v + } + for k, v := range other.Defaults { + new.Defaults[k] = v + } + for k, v := range p.Globals { + new.Globals[k] = v + } + for k, v := range other.Globals { + new.Globals[k] = v + } + return new +} + type Options struct { HAProxyBin string DataplaneBin string @@ -9,4 +34,5 @@ type Options struct { StatsListenAddr string StatsRegisterService bool LogRequests bool + HAProxyParams HAProxyParams } diff --git a/main.go b/main.go index b5b0867..8bb1c69 100644 --- a/main.go +++ b/main.go @@ -60,6 +60,9 @@ func validateRequirements(dataplaneBin, haproxyBin string) error { } func main() { + haproxyParamsFlag := stringSliceFlag{} + + flag.Var(&haproxyParamsFlag, "haproxy-param", "Global or defaults Haproxy config parameter to set in config. Can be specified multiple times. Must be of the form `defaults.name=value` or `global.name=value`") versionFlag := flag.Bool("version", false, "Show version and exit") logLevel := flag.String("log-level", "INFO", "Log level") consulAddr := flag.String("http-addr", "127.0.0.1:8500", "Consul agent address") @@ -128,6 +131,11 @@ func main() { log.Fatalf("Please specify -sidecar-for or -sidecar-for-tag") } + haproxyParams, err := makeHAProxyParams(haproxyParamsFlag) + if err != nil { + log.Fatal(err) + } + consulLogger := &consulLogger{} watcher := consul.New(serviceID, consulClient, consulLogger) go func() { @@ -145,6 +153,7 @@ func main() { StatsListenAddr: *statsListenAddr, StatsRegisterService: *statsServiceRegister, LogRequests: ll == log.TraceLevel, + HAProxyParams: haproxyParams, }) sd.Add(1) go func() {