From a86d8c4539cd009b655e18bad65713995c5a4c73 Mon Sep 17 00:00:00 2001 From: Daniel Huckins Date: Fri, 19 May 2023 18:11:41 -0400 Subject: [PATCH] agent: Add support for parsing env_template configuration files (#20598) * added exec and env_template config/parsing * add tests * we can reuse ctconfig here * do not create a non-nil map * check defaults * Apply suggestions from code review Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> * convert to list Signed-off-by: Daniel Huckins * convert to list Signed-off-by: Daniel Huckins * sig test Signed-off-by: Daniel Huckins * add failing example Signed-off-by: Daniel Huckins * add test for invalid signal Signed-off-by: Daniel Huckins * Update command/agent/config/config.go * use latest consul-template * fix build * fix test * fix test fixtures * make fmt * test docs * rename file * env var -> environment variable * default to SIGTERM * empty line * explicit naming Signed-off-by: Daniel Huckins * clean typo Signed-off-by: Daniel Huckins * replace $ HOME with /home/username in examples * remove empty line --------- Signed-off-by: Daniel Huckins Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> Co-authored-by: Anton Averchenkov --- command/agent/config/config.go | 155 +++++++++++++++++- command/agent/config/config_test.go | 111 +++++++++++++ ...ad-config-env-templates-invalid-signal.hcl | 26 +++ .../bad-config-env-templates-no-name.hcl | 26 +++ .../config-env-templates-complex.hcl | 33 ++++ .../config-env-templates-simple.hcl | 7 + go.mod | 32 ++-- go.sum | 33 ++++ 8 files changed, 405 insertions(+), 18 deletions(-) create mode 100644 command/agent/config/test-fixtures/bad-config-env-templates-invalid-signal.hcl create mode 100644 command/agent/config/test-fixtures/bad-config-env-templates-no-name.hcl create mode 100644 command/agent/config/test-fixtures/config-env-templates-complex.hcl create mode 100644 command/agent/config/test-fixtures/config-env-templates-simple.hcl diff --git a/command/agent/config/config.go b/command/agent/config/config.go index 3ddb7430e3..0277d6aae8 100644 --- a/command/agent/config/config.go +++ b/command/agent/config/config.go @@ -12,17 +12,21 @@ import ( "os" "path/filepath" "strings" + "syscall" "time" ctconfig "github.com/hashicorp/consul-template/config" + ctsignals "github.com/hashicorp/consul-template/signals" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/ast" + "github.com/mitchellh/mapstructure" + "github.com/hashicorp/vault/command/agentproxyshared" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/internalshared/configutil" - "github.com/mitchellh/mapstructure" + "github.com/hashicorp/vault/sdk/helper/pointerutil" ) // Config is the configuration for Vault Agent. @@ -32,7 +36,7 @@ type Config struct { AutoAuth *AutoAuth `hcl:"auto_auth"` ExitAfterAuth bool `hcl:"exit_after_auth"` Cache *Cache `hcl:"cache"` - APIProxy *APIProxy `hcl:"api_proxy""` + APIProxy *APIProxy `hcl:"api_proxy"` Vault *Vault `hcl:"vault"` TemplateConfig *TemplateConfig `hcl:"template_config"` Templates []*ctconfig.TemplateConfig `hcl:"templates"` @@ -44,6 +48,8 @@ type Config struct { DisableKeepAlivesAPIProxy bool `hcl:"-"` DisableKeepAlivesTemplating bool `hcl:"-"` DisableKeepAlivesAutoAuth bool `hcl:"-"` + Exec *ExecConfig `hcl:"exec,optional"` + EnvTemplates []*ctconfig.TemplateConfig `hcl:"env_template,optional"` } const ( @@ -160,6 +166,12 @@ type TemplateConfig struct { StaticSecretRenderInt time.Duration `hcl:"-"` } +type ExecConfig struct { + Command []string `hcl:"command,attr" mapstructure:"command"` + RestartOnSecretChanges string `hcl:"restart_on_secret_changes,optional" mapstructure:"restart_on_secret_changes"` + RestartStopSignal os.Signal `hcl:"-" mapstructure:"restart_stop_signal"` +} + func NewConfig() *Config { return &Config{ SharedConfig: new(configutil.SharedConfig), @@ -257,6 +269,19 @@ func (c *Config) Merge(c2 *Config) *Config { result.PidFile = c2.PidFile } + result.Exec = c.Exec + if c2.Exec != nil { + result.Exec = c2.Exec + } + + for _, envTmpl := range c.EnvTemplates { + result.EnvTemplates = append(result.EnvTemplates, envTmpl) + } + + for _, envTmpl := range c2.EnvTemplates { + result.EnvTemplates = append(result.EnvTemplates, envTmpl) + } + return result } @@ -491,6 +516,14 @@ func LoadConfigFile(path string) (*Config, error) { return nil, fmt.Errorf("error parsing 'template': %w", err) } + if err := parseExec(result, list); err != nil { + return nil, fmt.Errorf("error parsing 'exec': %w", err) + } + + if err := parseEnvTemplates(result, list); err != nil { + return nil, fmt.Errorf("error parsing 'env_template': %w", err) + } + if result.Cache != nil && result.APIProxy == nil { result.APIProxy = &APIProxy{ UseAutoAuthToken: result.Cache.UseAutoAuthToken, @@ -1038,3 +1071,121 @@ func parseTemplates(result *Config, list *ast.ObjectList) error { result.Templates = tcs return nil } + +func parseExec(result *Config, list *ast.ObjectList) error { + name := "exec" + + execList := list.Filter(name) + if len(execList.Items) == 0 { + return nil + } + + if len(execList.Items) > 1 { + return fmt.Errorf("at most one %q block is allowed", name) + } + + item := execList.Items[0] + var shadow interface{} + if err := hcl.DecodeObject(&shadow, item.Val); err != nil { + return fmt.Errorf("error decoding config: %s", err) + } + + parsed, ok := shadow.(map[string]interface{}) + if !ok { + return errors.New("error converting config") + } + + var execConfig ExecConfig + var md mapstructure.Metadata + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: mapstructure.ComposeDecodeHookFunc( + ctconfig.StringToFileModeFunc(), + ctconfig.StringToWaitDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + mapstructure.StringToTimeDurationHookFunc(), + ctsignals.StringToSignalFunc(), + ), + ErrorUnused: true, + Metadata: &md, + Result: &execConfig, + }) + if err != nil { + return errors.New("mapstructure decoder creation failed") + } + if err := decoder.Decode(parsed); err != nil { + return err + } + + // if the user does not specify a restart signal, default to SIGTERM + if execConfig.RestartStopSignal == nil { + execConfig.RestartStopSignal = syscall.SIGTERM + } + + if execConfig.RestartOnSecretChanges == "" { + execConfig.RestartOnSecretChanges = "always" + } + + result.Exec = &execConfig + return nil +} + +func parseEnvTemplates(result *Config, list *ast.ObjectList) error { + name := "env_template" + + envTemplateList := list.Filter(name) + + if len(envTemplateList.Items) < 1 { + return nil + } + + envTemplates := make([]*ctconfig.TemplateConfig, 0, len(envTemplateList.Items)) + + for _, item := range envTemplateList.Items { + var shadow interface{} + if err := hcl.DecodeObject(&shadow, item.Val); err != nil { + return fmt.Errorf("error decoding config: %s", err) + } + + // Convert to a map and flatten the keys we want to flatten + parsed, ok := shadow.(map[string]any) + if !ok { + return errors.New("error converting config") + } + + var templateConfig ctconfig.TemplateConfig + var md mapstructure.Metadata + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: mapstructure.ComposeDecodeHookFunc( + ctconfig.StringToFileModeFunc(), + ctconfig.StringToWaitDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + mapstructure.StringToTimeDurationHookFunc(), + ctsignals.StringToSignalFunc(), + ), + ErrorUnused: true, + Metadata: &md, + Result: &templateConfig, + }) + if err != nil { + return errors.New("mapstructure decoder creation failed") + } + if err := decoder.Decode(parsed); err != nil { + return err + } + + // parse the keys in the item for the environment variable name + if numberOfKeys := len(item.Keys); numberOfKeys != 1 { + return fmt.Errorf("expected one and only one environment variable name, got %d", numberOfKeys) + } + + // hcl parses this with extra quotes if quoted in config file + environmentVariableName := strings.Trim(item.Keys[0].Token.Text, `"`) + + templateConfig.MapToEnvironmentVariable = pointerutil.StringPtr(environmentVariableName) + + envTemplates = append(envTemplates, &templateConfig) + } + + result.EnvTemplates = envTemplates + return nil +} diff --git a/command/agent/config/config_test.go b/command/agent/config/config_test.go index 2c52925ad6..3a2e567c06 100644 --- a/command/agent/config/config_test.go +++ b/command/agent/config/config_test.go @@ -5,6 +5,7 @@ package config import ( "os" + "syscall" "testing" "time" @@ -13,6 +14,7 @@ import ( "github.com/hashicorp/vault/command/agentproxyshared" "github.com/hashicorp/vault/internalshared/configutil" "github.com/hashicorp/vault/sdk/helper/pointerutil" + "golang.org/x/exp/slices" ) func TestLoadConfigFile_AgentCache(t *testing.T) { @@ -2109,3 +2111,112 @@ func TestLoadConfigFile_Bad_Value_Disable_Keep_Alives(t *testing.T) { t.Fatal("should have error, it didn't") } } + +// TestLoadConfigFile_EnvTemplates loads and validates an env_template config +func TestLoadConfigFile_EnvTemplates(t *testing.T) { + cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-simple.hcl") + if err != nil { + t.Fatalf("error loading config file: %s", err) + } + + expectedKey := "MY_DATABASE_USER" + found := false + for _, envTemplate := range cfg.EnvTemplates { + if *envTemplate.MapToEnvironmentVariable == expectedKey { + found = true + } + } + if !found { + t.Fatalf("expected environment variable name to be populated") + } +} + +// TestLoadConfigFile_EnvTemplateComplex loads and validates an env_template config +func TestLoadConfigFile_EnvTemplateComplex(t *testing.T) { + cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-complex.hcl") + if err != nil { + t.Fatalf("error loading config file: %s", err) + } + expectedKeys := []string{ + "FOO_DATA_LOCK", + "FOO_DATA_PASSWORD", + "FOO_DATA_USER", + } + + envExists := func(key string) bool { + for _, envTmpl := range cfg.EnvTemplates { + if *envTmpl.MapToEnvironmentVariable == key { + return true + } + } + return false + } + + for _, expected := range expectedKeys { + if !envExists(expected) { + t.Fatalf("expected environment variable %s", expected) + } + } +} + +// TestLoadConfigFile_EnvTemplateNoName ensures that env_template with no name triggers an error +func TestLoadConfigFile_EnvTemplateNoName(t *testing.T) { + _, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-no-name.hcl") + if err == nil { + t.Fatalf("expected error") + } +} + +// TestLoadConfigFile_ExecInvalidSignal ensures that an invalid signal triggers an error +func TestLoadConfigFile_ExecInvalidSignal(t *testing.T) { + _, err := LoadConfigFile("./test-fixtures/bad-config-env-templates-invalid-signal.hcl") + if err == nil { + t.Fatalf("expected error") + } +} + +// TestLoadConfigFile_ExecSimple validates the exec section with default parameters +func TestLoadConfigFile_ExecSimple(t *testing.T) { + cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-simple.hcl") + if err != nil { + t.Fatalf("error loading config file: %s", err) + } + + if cfg.Exec == nil { + t.Fatal("expected exec config to be parsed") + } + + expectedCmd := []string{"/path/to/my/app", "arg1", "arg2"} + if !slices.Equal(cfg.Exec.Command, expectedCmd) { + t.Fatal("exec.command does not have expected value") + } + + // check defaults + if cfg.Exec.RestartOnSecretChanges != "always" { + t.Fatalf("expected cfg.Exec.RestartOnSecretChanges to be 'always', got '%s'", cfg.Exec.RestartOnSecretChanges) + } + + if cfg.Exec.RestartStopSignal != syscall.SIGTERM { + t.Fatalf("expected cfg.Exec.RestartStopSignal to be 'syscall.SIGTERM', got '%s'", cfg.Exec.RestartStopSignal) + } +} + +// TestLoadConfigFile_ExecComplex validates the exec section with non-default parameters +func TestLoadConfigFile_ExecComplex(t *testing.T) { + cfg, err := LoadConfigFile("./test-fixtures/config-env-templates-complex.hcl") + if err != nil { + t.Fatalf("error loading config file: %s", err) + } + + if !slices.Equal(cfg.Exec.Command, []string{"env"}) { + t.Fatal("exec.command does not have expected value") + } + + if cfg.Exec.RestartOnSecretChanges != "never" { + t.Fatalf("expected cfg.Exec.RestartOnSecretChanges to be 'never', got %q", cfg.Exec.RestartOnSecretChanges) + } + + if cfg.Exec.RestartStopSignal != syscall.SIGINT { + t.Fatalf("expected cfg.Exec.RestartStopSignal to be 'syscall.SIGINT', got %q", cfg.Exec.RestartStopSignal) + } +} diff --git a/command/agent/config/test-fixtures/bad-config-env-templates-invalid-signal.hcl b/command/agent/config/test-fixtures/bad-config-env-templates-invalid-signal.hcl new file mode 100644 index 0000000000..e8d822d64b --- /dev/null +++ b/command/agent/config/test-fixtures/bad-config-env-templates-invalid-signal.hcl @@ -0,0 +1,26 @@ +auto_auth { + + method { + type = "token_file" + + config { + token_file_path = "/home/username/.vault-token" + } + } +} + +vault { + address = "http://localhost:8200" +} + +env_template "FOO" { + contents = "{{ with secret \"secret/data/foo\" }}{{ .Data.data.lock }}{{ end }}" + error_on_missing_key = false +} + + +exec { + command = ["env"] + restart_on_secret_changes = "never" + restart_stop_signal = "notasignal" +} diff --git a/command/agent/config/test-fixtures/bad-config-env-templates-no-name.hcl b/command/agent/config/test-fixtures/bad-config-env-templates-no-name.hcl new file mode 100644 index 0000000000..f77f20c112 --- /dev/null +++ b/command/agent/config/test-fixtures/bad-config-env-templates-no-name.hcl @@ -0,0 +1,26 @@ +auto_auth { + + method { + type = "token_file" + + config { + token_file_path = "/home/username/.vault-token" + } + } +} + +vault { + address = "http://localhost:8200" +} + +env_template { + contents = "{{ with secret \"secret/data/foo\" }}{{ .Data.data.lock }}{{ end }}" + error_on_missing_key = false +} + + +exec { + command = ["env"] + restart_on_secret_changes = "never" + restart_stop_signal = "SIGTERM" +} diff --git a/command/agent/config/test-fixtures/config-env-templates-complex.hcl b/command/agent/config/test-fixtures/config-env-templates-complex.hcl new file mode 100644 index 0000000000..50328a69de --- /dev/null +++ b/command/agent/config/test-fixtures/config-env-templates-complex.hcl @@ -0,0 +1,33 @@ +auto_auth { + + method { + type = "token_file" + + config { + token_file_path = "/home/username/.vault-token" + } + } +} + +vault { + address = "http://localhost:8200" +} + +env_template "FOO_DATA_LOCK" { + contents = "{{ with secret \"secret/data/foo\" }}{{ .Data.data.lock }}{{ end }}" + error_on_missing_key = false +} +env_template "FOO_DATA_PASSWORD" { + contents = "{{ with secret \"secret/data/foo\" }}{{ .Data.data.password }}{{ end }}" + error_on_missing_key = false +} +env_template "FOO_DATA_USER" { + contents = "{{ with secret \"secret/data/foo\" }}{{ .Data.data.user }}{{ end }}" + error_on_missing_key = false +} + +exec { + command = ["env"] + restart_on_secret_changes = "never" + restart_stop_signal = "SIGINT" +} diff --git a/command/agent/config/test-fixtures/config-env-templates-simple.hcl b/command/agent/config/test-fixtures/config-env-templates-simple.hcl new file mode 100644 index 0000000000..4b5f385be3 --- /dev/null +++ b/command/agent/config/test-fixtures/config-env-templates-simple.hcl @@ -0,0 +1,7 @@ +env_template "MY_DATABASE_USER" { + contents = "{{ with secret \"secret/db-secret\" }}{{ .Data.data.user }}{{ end }}" +} + +exec { + command = ["/path/to/my/app", "arg1", "arg2"] +} \ No newline at end of file diff --git a/go.mod b/go.mod index f028c62ed7..d668fe470f 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/denisenkom/go-mssqldb v0.12.2 github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 github.com/dustin/go-humanize v1.0.0 - github.com/fatih/color v1.13.0 + github.com/fatih/color v1.15.0 github.com/fatih/structs v1.1.0 github.com/favadi/protoc-go-inject-tag v1.3.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 @@ -69,8 +69,8 @@ require ( github.com/google/go-metrics-stackdriver v0.2.0 github.com/google/tink/go v1.6.1 github.com/hashicorp/cap v0.2.1-0.20220727210936-60cd1534e220 - github.com/hashicorp/consul-template v0.30.1-0.20230322153229-821bc07137f5 - github.com/hashicorp/consul/api v1.18.0 + github.com/hashicorp/consul-template v0.32.0 + github.com/hashicorp/consul/api v1.20.0 github.com/hashicorp/errwrap v1.1.0 github.com/hashicorp/eventlogger v0.1.1 github.com/hashicorp/go-cleanhttp v0.5.2 @@ -112,7 +112,7 @@ require ( github.com/hashicorp/hcp-link v0.1.0 github.com/hashicorp/hcp-scada-provider v0.2.1 github.com/hashicorp/hcp-sdk-go v0.23.0 - github.com/hashicorp/nomad/api v0.0.0-20230103221135-ce00d683f9be + github.com/hashicorp/nomad/api v0.0.0-20230519153805-2275a83cbfdf github.com/hashicorp/raft v1.3.10 github.com/hashicorp/raft-autopilot v0.2.0 github.com/hashicorp/raft-boltdb/v2 v2.0.0-20210421194847-a7e34179d62c @@ -155,10 +155,10 @@ require ( github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f github.com/jefferai/jsonx v1.0.0 github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f - github.com/kr/pretty v0.3.0 + github.com/kr/pretty v0.3.1 github.com/kr/text v0.2.0 github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.17 + github.com/mattn/go-isatty v0.0.18 github.com/mholt/archiver/v3 v3.5.1 github.com/michaelklishin/rabbit-hole/v2 v2.12.0 github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a @@ -201,13 +201,13 @@ require ( go.opentelemetry.io/otel/trace v1.14.0 go.uber.org/atomic v1.10.0 go.uber.org/goleak v1.2.1 - golang.org/x/crypto v0.6.0 - golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb - golang.org/x/net v0.8.0 + golang.org/x/crypto v0.9.0 + golang.org/x/exp v0.0.0-20230519143937-03e91628a987 + golang.org/x/net v0.10.0 golang.org/x/oauth2 v0.5.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.6.0 - golang.org/x/term v0.6.0 + golang.org/x/sys v0.8.0 + golang.org/x/term v0.8.0 golang.org/x/tools v0.6.0 google.golang.org/api v0.110.0 google.golang.org/grpc v1.53.0 @@ -254,7 +254,7 @@ require ( github.com/Jeffail/gabs v1.1.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -364,11 +364,11 @@ require ( github.com/hashicorp/mdns v1.0.4 // indirect github.com/hashicorp/net-rpc-msgpackrpc/v2 v2.0.0 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/hashicorp/vault/api/auth/kubernetes v0.3.0 // indirect + github.com/hashicorp/vault/api/auth/kubernetes v0.4.0 // indirect github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.11.0 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -434,7 +434,7 @@ require ( github.com/snowflakedb/gosnowflake v1.6.3 // indirect github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/tencentcloud/tencentcloud-sdk-go v1.0.162 // indirect @@ -461,7 +461,7 @@ require ( go.uber.org/zap v1.19.1 // indirect golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index da81b58e56..f7e90fc6a3 100644 --- a/go.sum +++ b/go.sum @@ -630,6 +630,8 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF0 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= @@ -1165,6 +1167,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/favadi/protoc-go-inject-tag v1.3.0 h1:JPrmsmc/uBShG85uY5xGZIa5WJ0IaNZn6LZhQR9tIQE= @@ -1183,6 +1187,7 @@ github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897/go.mod h1:lgRN6+ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -1622,12 +1627,17 @@ github.com/hashicorp/cap v0.2.1-0.20220727210936-60cd1534e220 h1:Vgv3jG0kicczshK github.com/hashicorp/cap v0.2.1-0.20220727210936-60cd1534e220/go.mod h1:zb3VvIFA0lM2lbmO69NjowV9dJzJnZS89TaM9blXPJA= github.com/hashicorp/consul-template v0.30.1-0.20230322153229-821bc07137f5 h1:Z5Pzj0hUZzjWtaKATeWzTHfsr+onJEwBVe7JBiJiemM= github.com/hashicorp/consul-template v0.30.1-0.20230322153229-821bc07137f5/go.mod h1:KfaZbjtDruE5wsV9fBimP30C6xnF3cHJUX7/AQOFgM4= +github.com/hashicorp/consul-template v0.32.0 h1:VIfKjoJLkBYLgHdLH4mR7RstPc549qqHJiecqPwYTis= +github.com/hashicorp/consul-template v0.32.0/go.mod h1:r9mcCoHVkTeVln7aL4Ky+RfKupOtbEW70i8n9YuEe+w= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.18.0 h1:R7PPNzTCeN6VuQNDwwhZWJvzCtGSrNpJqfb22h3yH9g= github.com/hashicorp/consul/api v1.18.0/go.mod h1:owRRGJ9M5xReDC5nfT8FTJrNAPbT4NM6p/k+d03q2v4= +github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c= github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -1777,6 +1787,8 @@ github.com/hashicorp/net-rpc-msgpackrpc/v2 v2.0.0 h1:kBpVVl1sl3MaSrs97e0+pDQhSrq github.com/hashicorp/net-rpc-msgpackrpc/v2 v2.0.0/go.mod h1:6pdNz0vo0mF0GvhwDG56O3N18qBrAz/XRIcfINfTbwo= github.com/hashicorp/nomad/api v0.0.0-20230103221135-ce00d683f9be h1:bJ/jBA5pt/5OT1oaApx8B5g/nRyohn61Q8TyUp4PoEI= github.com/hashicorp/nomad/api v0.0.0-20230103221135-ce00d683f9be/go.mod h1:EM/2XaEwHziSB4NdWZ6MfE65TcvgWwVawOUBT8kVRqE= +github.com/hashicorp/nomad/api v0.0.0-20230519153805-2275a83cbfdf h1:cKXVf1UJqwdkGiTF3idqCOLApAql0310OSmJxeiaMWg= +github.com/hashicorp/nomad/api v0.0.0-20230519153805-2275a83cbfdf/go.mod h1:rb38DqjaaIfhJRiLeCAGgIt+wV7o78rB+liyFE3mVzE= github.com/hashicorp/raft v1.0.1/go.mod h1:DVSAWItjLjTOkVbSpWQ0j0kUADIvDaCtBxIcbNAQLkI= github.com/hashicorp/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= @@ -1872,6 +1884,8 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb v1.7.6/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= @@ -2030,6 +2044,8 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= @@ -2100,6 +2116,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -2501,6 +2519,7 @@ github.com/sethvargo/go-limiter v0.7.1/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiw github.com/shirou/gopsutil/v3 v3.22.6 h1:FnHOFOh+cYAM0C30P+zysPISzlknLC5Z1G4EAElznfQ= github.com/shirou/gopsutil/v3 v3.22.6/go.mod h1:EdIubSnZhbAvBS1yJ7Xi+AShB/hxwLHOMz4MCYz7yMs= github.com/shoenig/test v0.5.2 h1:ELZ7qZ/6CPrT71PXrSe2TFzLs4/cGCqqU5lZ5RhZ+B8= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= @@ -2544,6 +2563,8 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= @@ -2880,6 +2901,8 @@ golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2897,6 +2920,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230519143937-03e91628a987 h1:3xJIFvzUFbu4ls0BTBYcgbCGhA63eAOEMxIHugyXJqA= +golang.org/x/exp v0.0.0-20230519143937-03e91628a987/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE= golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= @@ -3039,6 +3064,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -3251,6 +3278,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3264,6 +3293,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -3281,6 +3312,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=