From 73f44145cd7bd5b8a2f2e8d1bc8b9e13ae5bf080 Mon Sep 17 00:00:00 2001 From: K4L1Ma Date: Wed, 19 Jun 2024 11:41:19 +0200 Subject: [PATCH] Remove Reflection and Added Generics --- .github/workflows/go.yml | 2 +- README.md | 3 +- bootstrap.go | 231 ++++++++++++++++++++------------------ bootstrap_test.go | 73 ++++++------ example_test.go | 65 +++++++---- go.mod | 40 ++++++- go.sum | 236 ++++++++++++--------------------------- 7 files changed, 305 insertions(+), 345 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 08d4d24..cd7b318 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.17 + go-version: 1.21 - name: Build run: go build -v ./... diff --git a/README.md b/README.md index 051cff8..bc026d6 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,11 @@ Bootstrap package for building Services in Go, Handle Signaling and Config comin func main() { - kernel := snout.Kernel{ + kernel := snout.Kernel[Config]{ RunE: Run, } kernelBootstrap := kernel.Bootstrap( context.Background(), - new(Config), ) if err := kernelBootstrap.Initialize(); err != nil { if err != context.Canceled { diff --git a/bootstrap.go b/bootstrap.go index e3a6f26..a44d880 100644 --- a/bootstrap.go +++ b/bootstrap.go @@ -2,7 +2,10 @@ package snout import ( "context" + "errors" "fmt" + "log/slog" + "os" "os/signal" "reflect" "strings" @@ -17,76 +20,124 @@ import ( "github.com/spf13/viper" ) -type Kernel struct { - RunE interface{} +var logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true})) + +// ServiceConfig is a generic type for service configuration. +type ServiceConfig any + +// Kernel represents a service kernel with a run function. +type Kernel[T ServiceConfig] struct { + RunE func(ctx context.Context, cfg T) error } -type env struct { +// Env represents the environment configuration. +type Env struct { VarFile string VarsPrefix string } -type kernelOptions struct { +// KernelOptions contains options for configuring the kernel. +type KernelOptions struct { ServiceName string - Env env + Env Env } -func newKernelOptions() *kernelOptions { - return &kernelOptions{ +// Options is a function type for configuring KernelOptions. +type Options func(kernel *KernelOptions) + +// NewKernelOptions returns a new instance of KernelOptions with default values. +func NewKernelOptions() *KernelOptions { + return &KernelOptions{ ServiceName: "", - Env: env{ + Env: Env{ VarFile: ".", VarsPrefix: "", }, } } -// WithServiceName creates a profile based on the service name to look up for envVar files +// WithServiceName sets the service name in KernelOptions. func WithServiceName(name string) Options { - return func(kernel *kernelOptions) { + return func(kernel *KernelOptions) { kernel.ServiceName = name } } -// WithEnvVarPrefix strips any prefix from os EnvVars to map it into Config struct. +// WithEnvVarPrefix sets the environment variable prefix in KernelOptions. func WithEnvVarPrefix(prefix string) Options { - return func(kernel *kernelOptions) { + return func(kernel *KernelOptions) { kernel.Env.VarsPrefix = prefix } } -// WithEnvVarFolderLocation Specify where to look up form the env var file. +// WithEnvVarFolderLocation sets the folder location for environment variable files in KernelOptions. func WithEnvVarFolderLocation(folderLocation string) Options { - return func(kernel *kernelOptions) { + return func(kernel *KernelOptions) { kernel.Env.VarFile = folderLocation } } -type Options func(kernel *kernelOptions) - -// Bootstrap service creating a Ctx with Signalling and fetching EnvVars from -// env, yaml or json file, or straight from envVars from the OS. -func (k *Kernel) Bootstrap(ctx context.Context, cfg interface{}, opts ...Options) kernelBootstrap { - krnlOpt := newKernelOptions() - for _, o := range opts { - o(krnlOpt) +// Bootstrap initializes the kernel with given options, setting up context and fetching configuration. +func (k *Kernel[T]) Bootstrap(ctx context.Context, opts ...Options) KernelBootstrap[T] { + kernelOpts := NewKernelOptions() + for _, opt := range opts { + opt(kernelOpts) } - ctx = signallingContext(ctx) + ctx = setUpSignalHandling(ctx) + cfg := k.fetchVars(kernelOpts) - k.varFetching(cfg, krnlOpt) + return KernelBootstrap[T]{ctx, cfg, k.RunE} +} - return kernelBootstrap{ctx, cfg, k.RunE} +// KernelBootstrap holds the context, configuration, and run function for the kernel. +type KernelBootstrap[T ServiceConfig] struct { + context context.Context + cfg T + runE func(ctx context.Context, cfg T) error } -func (k Kernel) varFetching(cfg interface{}, options *kernelOptions) { +// Initialize validates the configuration and runs the kernel. +func (kb KernelBootstrap[T]) Initialize() (err error) { + validate := validator.New() + + if err = validate.Struct(kb.cfg); err != nil { + return fmt.Errorf("%w: %s", ErrValidation, err.Error()) + } + + defer func() { + if r := recover(); r != nil { + switch pErr := r.(type) { + case string: + err = fmt.Errorf("%w: %s", ErrPanic, pErr) + case error: + err = fmt.Errorf("%w: %w", ErrPanic, pErr) + default: + err = fmt.Errorf("%w: %+v", ErrPanic, pErr) + } + } + }() + + return kb.runE(kb.context, kb.cfg) +} + +// ErrPanic is an error indicating a panic occurred. +var ErrPanic = errors.New("panic") + +// ErrValidation is an error indicating a validation failure. +var ErrValidation = errors.New("validation error") + +// fetchVars fetches the configuration using Viper from environment variables and configuration files. +func (k *Kernel[T]) fetchVars(options *KernelOptions) T { + var cfg T + viper.SetEnvPrefix(options.Env.VarsPrefix) viper.AutomaticEnv() viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) flagSet := pflag.NewFlagSet(options.ServiceName, pflag.ContinueOnError) - if err := gpflag.ParseTo(cfg, flagSet, sflags.FlagDivider("."), sflags.FlagTag("snout")); err != nil { + if err := gpflag.ParseTo(&cfg, flagSet, sflags.FlagDivider("."), sflags.FlagTag("snout")); err != nil { panic(err) } @@ -98,47 +149,57 @@ func (k Kernel) varFetching(cfg interface{}, options *kernelOptions) { viper.AddConfigPath(options.Env.VarFile) if err := viper.ReadInConfig(); err == nil { - fmt.Printf("Using config file: %s \n", viper.ConfigFileUsed()) + logger.Info("Using config file", slog.String("config file", viper.ConfigFileUsed())) } - setDefaultValues(reflect.TypeOf(cfg).Elem(), "") + setDefaultValues(reflect.TypeOf(&cfg).Elem(), "") - if err := viper.Unmarshal(cfg, unmarshalWithStructTag("snout")); err != nil { + if err := viper.Unmarshal(&cfg, unmarshalWithStructTag("snout")); err != nil { panic(err) } -} -func setDefaultValues(p reflect.Type, path string) { - for i := 0; i < p.NumField(); i++ { - field := p.Field(i) + return cfg +} - PathMap := map[bool]string{ - true: strings.ToUpper(fmt.Sprintf("%s.%s", path, field.Tag.Get("snout"))), - false: strings.ToUpper(field.Tag.Get("snout")), - } +// setUpSignalHandling sets up a context with signal notifications. +func setUpSignalHandling(ctx context.Context) context.Context { + ctx, _ = signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT) - finalPath := PathMap[path != ""] + return ctx +} - var typ reflect.Type +// setDefaultValues sets default values recursively for configuration fields. +func setDefaultValues(t reflect.Type, path string) { + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + finalPath := constructFinalPath(path, field) - switch field.Type.Kind() { - case reflect.Ptr: - typ = field.Type.Elem() - default: - typ = field.Type + if field.Type.Kind() == reflect.Struct { + setDefaultValues(field.Type, finalPath) + } else { + setDefaultValue(finalPath, field) } + } +} - if typ.Kind() != reflect.Struct { - get := field.Tag.Get("default") - viper.SetDefault(finalPath, get) +// constructFinalPath constructs the final path for a field based on its tag and the current path. +func constructFinalPath(path string, field reflect.StructField) string { + tag := field.Tag.Get("snout") + if path != "" { + return strings.ToUpper(fmt.Sprintf("%s.%s", path, tag)) + } - continue - } + return strings.ToUpper(tag) +} - setDefaultValues(typ, finalPath) +// setDefaultValue sets the default value for a field in Viper. +func setDefaultValue(finalPath string, field reflect.StructField) { + if defaultValue := field.Tag.Get("default"); defaultValue != "" { + viper.SetDefault(finalPath, defaultValue) } } +// unmarshalWithStructTag sets the struct tag for unmarshaling configuration. func unmarshalWithStructTag(tag string) viper.DecoderConfigOption { return func(config *mapstructure.DecoderConfig) { config.TagName = tag @@ -146,72 +207,20 @@ func unmarshalWithStructTag(tag string) viper.DecoderConfigOption { } } +// customUnMarshallerHookFunc is a custom unmarshal function for handling time.Duration. func customUnMarshallerHookFunc(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if t.String() == "time.Duration" && f.Kind() == reflect.String { - var s string + var ( + stringDuration string + ok bool + ) - if s = data.(string); s == "" { - s = "0s" + if stringDuration, ok = data.(string); stringDuration == "" && ok { + stringDuration = "0s" } - return time.ParseDuration(s) + return time.ParseDuration(stringDuration) } return data, nil } - -func signallingContext(ctx context.Context) context.Context { - ctx, _ = signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT) - - return ctx -} - -type kernelBootstrap struct { - context context.Context - cfg interface{} - runE interface{} -} - -var ErrPanic = fmt.Errorf("panic") -var ErrValidation = fmt.Errorf("validation error") - -// Initialize Runs the Bootstrapped service -func (kb kernelBootstrap) Initialize() (err error) { - validate := validator.New() - - if err = validate.Struct(kb.cfg); err != nil { - return fmt.Errorf("%w:%s", ErrValidation, err.Error()) - } - - typeOf := reflect.TypeOf(kb.runE) - if typeOf.Kind() != reflect.Func { - return fmt.Errorf("%s is not a reflect.Func", reflect.TypeOf(kb.runE)) - } - - defer func() { - if r := recover(); r != nil { - switch pErr := r.(type) { - case string: - err = fmt.Errorf("%w:%s", ErrPanic, pErr) - case error: - err = fmt.Errorf("%w:%v", ErrPanic, pErr) - default: - err = fmt.Errorf("%w:%+v", ErrPanic, pErr) - } - return - } - }() - - var In []reflect.Value - In = append(In, reflect.ValueOf(kb.context)) - In = append(In, reflect.ValueOf(kb.cfg).Elem()) - - call := reflect.ValueOf(kb.runE).Call(In) - - err, ok := call[0].Interface().(error) - if !ok { - return nil - } - - return err -} diff --git a/bootstrap_test.go b/bootstrap_test.go index 5c7d4d5..4a88600 100644 --- a/bootstrap_test.go +++ b/bootstrap_test.go @@ -3,11 +3,11 @@ package snout_test import ( "context" "fmt" + "github.com/chiguirez/snout/v3" "os" "testing" "time" - "github.com/chiguirez/snout/v2" "github.com/stretchr/testify/suite" ) @@ -17,6 +17,7 @@ type snoutSuite struct { func TestSnout(t *testing.T) { suite.Run(t, new(snoutSuite)) + t.Parallel() } func (s *snoutSuite) TestDefaultTags() { @@ -35,21 +36,22 @@ func (s *snoutSuite) TestDefaultTags() { s.Run("When Kernel Initialized", func() { cfgChan := make(chan stubConfig, 1) - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(_ context.Context, config stubConfig) error { cfgChan <- config + return nil }} - _ = kernel.Bootstrap(context.TODO(), new(stubConfig)).Initialize() + _ = kernel.Bootstrap(context.TODO()).Initialize() s.Run("Then all values are present", func() { config := <-cfgChan s.Require().Equal("a", config.A) s.Require().Equal(1, config.B) - s.Require().Equal(true, config.C) + s.Require().True(config.C) s.Require().Equal("da", *config.D.A) s.Require().Equal(3.1415, *config.D.B) - s.Require().Equal(false, *config.D.C) + s.Require().False(*config.D.C) }) }) }) @@ -69,16 +71,15 @@ func (s *snoutSuite) TestErrPanic() { } s.Run("When Kernel Initialized", func() { - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(context.Context, stubConfig) error { panic(fmt.Errorf("/!\\")) }} - err := kernel.Bootstrap(context.TODO(), new(stubConfig)).Initialize() + err := kernel.Bootstrap(context.TODO()).Initialize() s.Run("Then all values are present", func() { s.Require().Error(err) s.Require().ErrorIs(err, snout.ErrPanic) - }) }) }) @@ -98,16 +99,15 @@ func (s *snoutSuite) TestStringPanic() { } s.Run("When Kernel Initialized", func() { - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(context.Context, stubConfig) error { panic("/!\\") }} - err := kernel.Bootstrap(context.TODO(), new(stubConfig)).Initialize() + err := kernel.Bootstrap(context.TODO()).Initialize() s.Run("Then all values are present", func() { s.Require().Error(err) s.Require().ErrorIs(err, snout.ErrPanic) - }) }) }) @@ -127,16 +127,15 @@ func (s *snoutSuite) TestAnyPanic() { } s.Run("When Kernel Initialized", func() { - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(context.Context, stubConfig) error { panic(false) }} - err := kernel.Bootstrap(context.TODO(), new(stubConfig)).Initialize() + err := kernel.Bootstrap(context.TODO()).Initialize() s.Run("Then all values are present", func() { s.Require().Error(err) s.Require().ErrorIs(err, snout.ErrPanic) - }) }) }) @@ -159,14 +158,14 @@ func (s *snoutSuite) TestENVFile() { s.Run("When Kernel is Initialized", func() { cfgChan := make(chan stubConfig, 1) - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(_ context.Context, config stubConfig) error { cfgChan <- config + return nil }} _ = kernel.Bootstrap( context.TODO(), - new(stubConfig), snout.WithServiceName("ENV"), snout.WithEnvVarFolderLocation("./testdata/"), ).Initialize() @@ -175,10 +174,10 @@ func (s *snoutSuite) TestENVFile() { config := <-cfgChan s.Require().Equal("a", config.A) s.Require().Equal(1, config.B) - s.Require().Equal(true, config.C) + s.Require().True(config.C) s.Require().Equal("da", *config.D.A) s.Require().Equal(3.1415, *config.D.B) - s.Require().Equal(false, *config.D.C) + s.Require().False(*config.D.C) s.Require().Equal(30*time.Minute, config.E) }) }) @@ -202,14 +201,14 @@ func (s *snoutSuite) TestYAMLFile() { s.Run("When Kernel is Initialized", func() { cfgChan := make(chan stubConfig, 1) - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(_ context.Context, config stubConfig) error { cfgChan <- config + return nil }} _ = kernel.Bootstrap( context.TODO(), - new(stubConfig), snout.WithServiceName("YAML"), snout.WithEnvVarFolderLocation("./testdata/"), ).Initialize() @@ -218,10 +217,10 @@ func (s *snoutSuite) TestYAMLFile() { config := <-cfgChan s.Require().Equal("a", config.A) s.Require().Equal(1, config.B) - s.Require().Equal(true, config.C) + s.Require().True(config.C) s.Require().Equal("da", *config.D.A) s.Require().Equal(3.1415, *config.D.B) - s.Require().Equal(false, *config.D.C) + s.Require().False(*config.D.C) s.Require().Equal(30*time.Minute, config.E) }) }) @@ -245,14 +244,14 @@ func (s *snoutSuite) TestJSONFile() { s.Run("When Kernel is Initialized", func() { cfgChan := make(chan stubConfig, 1) - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(_ context.Context, config stubConfig) error { cfgChan <- config + return nil }} err := kernel.Bootstrap( context.TODO(), - new(stubConfig), snout.WithServiceName("JSON"), snout.WithEnvVarFolderLocation("./testdata/"), ).Initialize() @@ -263,10 +262,10 @@ func (s *snoutSuite) TestJSONFile() { config := <-cfgChan s.Require().Equal("a", config.A) s.Require().Equal(1, config.B) - s.Require().Equal(true, config.C) + s.Require().True(config.C) s.Require().Equal("da", *config.D.A) s.Require().Equal(3.1415, *config.D.B) - s.Require().Equal(false, *config.D.C) + s.Require().False(*config.D.C) s.Require().Equal(30*time.Minute, config.E) }) }) @@ -296,21 +295,22 @@ func (s *snoutSuite) TestEnvVars() { s.Run("When Kernel is Initialized with Prefix", func() { cfgChan := make(chan stubConfig, 1) - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(_ context.Context, config stubConfig) error { cfgChan <- config + return nil }} - _ = kernel.Bootstrap(context.TODO(), new(stubConfig), snout.WithEnvVarPrefix("APP")).Initialize() + _ = kernel.Bootstrap(context.TODO(), snout.WithEnvVarPrefix("APP")).Initialize() s.Run("Then all values are present", func() { config := <-cfgChan s.Require().Equal("a", config.A) s.Require().Equal(1, config.B) - s.Require().Equal(true, config.C) + s.Require().True(config.C) s.Require().Equal("da", *config.D.A) s.Require().Equal(3.1415, *config.D.B) - s.Require().Equal(false, *config.D.C) + s.Require().False(*config.D.C) }) }) }) @@ -337,11 +337,11 @@ func (s *snoutSuite) TestConfigValidationFail() { _ = os.Setenv("APP_D_C", "false") s.Run("When Kernel is Initialized with Prefix", func() { - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(context.Context, stubConfig) error { return nil }} - err := kernel.Bootstrap(context.TODO(), new(stubConfig), snout.WithEnvVarPrefix("APP")).Initialize() + err := kernel.Bootstrap(context.TODO(), snout.WithEnvVarPrefix("APP")).Initialize() s.Run("Then all values are present", func() { s.Require().Error(err) @@ -374,22 +374,23 @@ func (s *snoutSuite) TestConfigValidation() { s.Run("When Kernel is Initialized with Prefix", func() { cfgChan := make(chan stubConfig, 1) - kernel := snout.Kernel{RunE: func(ctx context.Context, config stubConfig) error { + kernel := snout.Kernel[stubConfig]{RunE: func(_ context.Context, config stubConfig) error { cfgChan <- config + return nil }} - err := kernel.Bootstrap(context.TODO(), new(stubConfig), snout.WithEnvVarPrefix("APP")).Initialize() + err := kernel.Bootstrap(context.TODO(), snout.WithEnvVarPrefix("APP")).Initialize() s.Require().NoError(err) s.Run("Then all values are present", func() { config := <-cfgChan s.Require().Equal("a", config.A) s.Require().Equal(1, config.B) - s.Require().Equal(true, config.C) + s.Require().True(config.C) s.Require().Equal("da@da.da", *config.D.A) s.Require().Equal(3.1415, *config.D.B) - s.Require().Equal(false, *config.D.C) + s.Require().False(*config.D.C) }) }) }) diff --git a/example_test.go b/example_test.go index b21ad01..15862ee 100644 --- a/example_test.go +++ b/example_test.go @@ -2,11 +2,13 @@ package snout_test import ( "context" + "errors" + "fmt" - "github.com/chiguirez/snout/v2" + "github.com/chiguirez/snout/v3" ) -func ExampleSnout() { +func ExampleKernel_Bootstrap() { // Create a config struct and map using snout tags, env, json, yaml files could be used as well as envVars to // as data source to deserialize into the config struct type Config struct { @@ -16,28 +18,33 @@ func ExampleSnout() { Topic string `snout:"topic"` } `snout:"kafka"` App struct { - //... + // ... } `snout:"app"` } - Run := func(ctx context.Context, config Config) { + Run := func(context.Context, Config) error { // wire your app all together using config struct + fmt.Println("App Initialized") + + return nil } // Create your kernel struct with the function expecting a context and your config struct - kernel := snout.Kernel{ + kernel := snout.Kernel[Config]{ RunE: Run, } // Pass a pointer to config to the kernel for it to be able to deserialize - kernelBootstrap := kernel.Bootstrap(context.Background(), new(Config)) + kernelBootstrap := kernel.Bootstrap(context.Background()) // Initialize your app and handle any error coming from it if err := kernelBootstrap.Initialize(); err != nil { - if err != context.Canceled { + if !errors.Is(err, context.Canceled) { panic(err) } } + + // Output: App Initialized } func ExampleWithEnvVarFolderLocation() { @@ -50,28 +57,33 @@ func ExampleWithEnvVarFolderLocation() { Topic string `snout:"topic"` } `snout:"kafka"` App struct { - //... + // ... } `snout:"app"` } - Run := func(ctx context.Context, config Config) { + Run := func(context.Context, Config) error { // wire your app all together using config struct + fmt.Println("App Initialized with Config from Folder") + + return nil } // Create your kernel struct with the function expecting a context and your config struct - kernel := snout.Kernel{ + kernel := snout.Kernel[Config]{ RunE: Run, } // Pass a pointer to config to the kernel for it to be able to deserialize - kernelBootstrap := kernel.Bootstrap(context.Background(), new(Config), snout.WithEnvVarFolderLocation("/etc/config/")) + kernelBootstrap := kernel.Bootstrap(context.Background(), snout.WithEnvVarFolderLocation("/etc/config/")) // Initialize your app and handle any error coming from it if err := kernelBootstrap.Initialize(); err != nil { - if err != context.Canceled { + if !errors.Is(err, context.Canceled) { panic(err) } } + + // Output: App Initialized with Config from Folder } func ExampleWithEnvVarPrefix() { @@ -84,28 +96,33 @@ func ExampleWithEnvVarPrefix() { Topic string `snout:"topic"` } `snout:"kafka"` App struct { - //... + // ... } `snout:"app"` } - Run := func(ctx context.Context, config Config) { + Run := func(context.Context, Config) error { // wire your app all together using config struct + fmt.Println("App Initialized with EnvVar Prefix") + + return nil } // Create your kernel struct with the function expecting a context and your config struct - kernel := snout.Kernel{ + kernel := snout.Kernel[Config]{ RunE: Run, } // Pass a pointer to config to the kernel for it to be able to deserialize - kernelBootstrap := kernel.Bootstrap(context.Background(), new(Config), snout.WithEnvVarPrefix("APP")) + kernelBootstrap := kernel.Bootstrap(context.Background(), snout.WithEnvVarPrefix("APP")) // Initialize your app and handle any error coming from it if err := kernelBootstrap.Initialize(); err != nil { - if err != context.Canceled { + if !errors.Is(err, context.Canceled) { panic(err) } } + + // Output: App Initialized with EnvVar Prefix } func ExampleWithServiceName() { @@ -118,30 +135,34 @@ func ExampleWithServiceName() { Topic string `snout:"topic"` } `snout:"kafka"` App struct { - //... + // ... } `snout:"app"` } - Run := func(ctx context.Context, config Config) { + Run := func(context.Context, Config) error { // wire your app all together using config struct + fmt.Println("App Initialized with Service Name") + + return nil } // Create your kernel struct with the function expecting a context and your config struct - kernel := snout.Kernel{ + kernel := snout.Kernel[Config]{ RunE: Run, } // Pass a pointer to config to the kernel for it to be able to deserialize kernelBootstrap := kernel.Bootstrap( context.Background(), - new(Config), snout.WithServiceName("MyCustomServiceName"), ) // Initialize your app and handle any error coming from it if err := kernelBootstrap.Initialize(); err != nil { - if err != context.Canceled { + if !errors.Is(err, context.Canceled) { panic(err) } } + + // Output: App Initialized with Service Name } diff --git a/go.mod b/go.mod index 5fac0ee..8629cee 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,40 @@ -module github.com/chiguirez/snout/v2 +module github.com/chiguirez/snout/v3 require ( - github.com/go-playground/validator/v10 v10.10.0 - github.com/mitchellh/mapstructure v1.1.2 + github.com/go-playground/validator/v10 v10.22.0 + github.com/mitchellh/mapstructure v1.5.0 github.com/octago/sflags v0.2.0 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.5.0 - github.com/stretchr/testify v1.7.0 + github.com/spf13/viper v1.19.0 + github.com/stretchr/testify v1.9.0 ) -go 1.16 +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +go 1.18 diff --git a/go.sum b/go.sum index b5e08ce..08a11ac 100644 --- a/go.sum +++ b/go.sum @@ -1,180 +1,82 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= +github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/octago/sflags v0.2.0 h1:XceYzkRXGAHa/lSFmKLcaxSrsh4MTuOMQdIGsUD0wlk= github.com/octago/sflags v0.2.0/go.mod h1:G0bjdxh4qPRycF74a2B8pU36iTp9QHGx0w0dFZXPt80= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= -github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=