From 258473228356477d8d75d0cdbd67afad1665a951 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Mon, 24 Nov 2025 17:44:52 +0000 Subject: [PATCH 01/15] fix: experiment with WASI NOTE: it is a no go - but still valuable insight into sandboxing by the runtime --- cmd/configmanager/configmanager.go | 2 +- configmanager.go | 2 +- generator/generator.go | 32 ++-- generator/generator_test.go | 14 +- generator/generatorvars.go | 20 -- go.mod | 57 +++--- go.sum | 60 ++++++ internal/config/config.go | 47 ++++- internal/plugin/plugin.go | 16 ++ internal/plugin/tester/main.go | 46 +++++ internal/plugin/wasip1.go | 263 +++++++++++++++++++++++++++ internal/plugin/wasip1_test.go | 47 +++++ plugins/awsparams/main.go | 203 +++++++++++++++++++++ plugins/awsparams/paramstore.go | 67 +++++++ plugins/awsparams/paramstore_test.go | 152 ++++++++++++++++ plugins/awssecrets/main.go | 197 ++++++++++++++++++++ plugins/scaffolding.go | 30 +++ 17 files changed, 1173 insertions(+), 82 deletions(-) create mode 100644 internal/plugin/plugin.go create mode 100644 internal/plugin/tester/main.go create mode 100644 internal/plugin/wasip1.go create mode 100644 internal/plugin/wasip1_test.go create mode 100644 plugins/awsparams/main.go create mode 100644 plugins/awsparams/paramstore.go create mode 100644 plugins/awsparams/paramstore_test.go create mode 100644 plugins/awssecrets/main.go create mode 100644 plugins/scaffolding.go diff --git a/cmd/configmanager/configmanager.go b/cmd/configmanager/configmanager.go index 1eed6cb..383d229 100644 --- a/cmd/configmanager/configmanager.go +++ b/cmd/configmanager/configmanager.go @@ -73,7 +73,7 @@ func cmdutilsInit(rootCmd *Root, cmd *cobra.Command, path string) (*cmdutils.Cmd cm := configmanager.New(cmd.Context()) cm.Config.WithTokenSeparator(rootCmd.rootFlags.tokenSeparator).WithOutputPath(path).WithKeySeparator(rootCmd.rootFlags.keySeparator).WithEnvSubst(rootCmd.rootFlags.enableEnvSubst) - gnrtr := generator.NewGenerator(cmd.Context(), func(gv *generator.GenVars) { + gnrtr := generator.New(cmd.Context(), func(gv *generator.Generator) { if rootCmd.rootFlags.verbose { rootCmd.logger.SetLevel(log.DebugLvl) } diff --git a/configmanager.go b/configmanager.go index f3f6134..4da0d1b 100644 --- a/configmanager.go +++ b/configmanager.go @@ -46,7 +46,7 @@ type ConfigManager struct { func New(ctx context.Context) *ConfigManager { cm := &ConfigManager{} cm.Config = config.NewConfig() - cm.generator = generator.NewGenerator(ctx).WithConfig(cm.Config) + cm.generator = generator.New(ctx).WithConfig(cm.Config) cm.logger = log.New(io.Discard) return cm } diff --git a/generator/generator.go b/generator/generator.go index 237c6a6..8dcef19 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -17,32 +17,32 @@ import ( "github.com/DevLabFoundry/configmanager/v3/internal/strategy" ) -// GenVars is the main struct holding the +// Generator is the main struct holding the // strategy patterns iface // any initialised config if overridded with withers // as well as the final outString and the initial rawMap // which wil be passed in a loop into a goroutine to perform the // relevant strategy network calls to the config store implementations -type GenVars struct { +type Generator struct { Logger log.ILogger strategy strategy.StrategyFuncMap ctx context.Context config config.GenVarsConfig } -type Opts func(*GenVars) +type Opts func(*Generator) -// NewGenerator returns a new instance of Generator +// New returns a new instance of Generator // with a default strategy pattern wil be overwritten // during the first run of a found tokens map -func NewGenerator(ctx context.Context, opts ...Opts) *GenVars { +func New(ctx context.Context, opts ...Opts) *Generator { // defaultStrategy := NewDefatultStrategy() - return newGenVars(ctx, opts...) + return new(ctx, opts...) } -func newGenVars(ctx context.Context, opts ...Opts) *GenVars { +func new(ctx context.Context, opts ...Opts) *Generator { conf := config.NewConfig() - g := &GenVars{ + g := &Generator{ Logger: log.New(io.Discard), ctx: ctx, // return using default config @@ -61,13 +61,13 @@ func newGenVars(ctx context.Context, opts ...Opts) *GenVars { // WithStrategyMap // // Adds addtional funcs for storageRetrieval used for testing only -func (c *GenVars) WithStrategyMap(sm strategy.StrategyFuncMap) *GenVars { +func (c *Generator) WithStrategyMap(sm strategy.StrategyFuncMap) *Generator { c.strategy = sm return c } // WithConfig uses custom config -func (c *GenVars) WithConfig(cfg *config.GenVarsConfig) *GenVars { +func (c *Generator) WithConfig(cfg *config.GenVarsConfig) *Generator { // backwards compatibility if cfg != nil { c.config = *cfg @@ -76,13 +76,13 @@ func (c *GenVars) WithConfig(cfg *config.GenVarsConfig) *GenVars { } // WithContext uses caller passed context -func (c *GenVars) WithContext(ctx context.Context) *GenVars { +func (c *Generator) WithContext(ctx context.Context) *Generator { c.ctx = ctx return c } // Config gets Config on the GenVars -func (c *GenVars) Config() *config.GenVarsConfig { +func (c *Generator) Config() *config.GenVarsConfig { return &c.config } @@ -90,7 +90,7 @@ func (c *GenVars) Config() *config.GenVarsConfig { // the standard pattern of a token should follow a path like string // // Called only from a slice of tokens -func (c *GenVars) Generate(tokens []string) (ReplacedToken, error) { +func (c *Generator) Generate(tokens []string) (ReplacedToken, error) { ntm, err := c.DiscoverTokens(strings.Join(tokens, "\n")) if err != nil { @@ -112,7 +112,7 @@ var ErrTokenDiscovery = errors.New("failed to discover tokens") // the standard pattern of a token should follow a path like string // // Called only from a slice of tokens -func (c *GenVars) DiscoverTokens(text string) (NormalizedTokenSafe, error) { +func (c *Generator) DiscoverTokens(text string) (NormalizedTokenSafe, error) { rtm := NewRawTokenConfig() @@ -144,7 +144,7 @@ func IsParsed(v any, trm ReplacedToken) bool { // Captures the response/error in TokenResponse struct // It then denormalizes the NormalizedTokenSafe back to a ReplacedToken map // which stores the values for each token to be returned to the caller -func (c *GenVars) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { +func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { if len(ntm.normalizedTokenMap) < 1 { c.Logger.Debug("no replaceable tokens found in input") return nil, nil @@ -232,7 +232,7 @@ func (n NormalizedTokenSafe) GetMap() map[string]*NormalizedToken { return n.normalizedTokenMap } -func (c *GenVars) NormalizeRawToken(rtm *RawTokenConfig) NormalizedTokenSafe { +func (c *Generator) NormalizeRawToken(rtm *RawTokenConfig) NormalizedTokenSafe { ntm := NormalizedTokenSafe{mu: &sync.Mutex{}, normalizedTokenMap: make(map[string]*NormalizedToken)} for _, r := range rtm.RawTokenMap() { diff --git a/generator/generator_test.go b/generator/generator_test.go index e48e546..d391d86 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -34,7 +34,7 @@ func TestGenerate(t *testing.T) { return m, nil } - g := generator.NewGenerator(context.TODO(), func(gv *generator.GenVars) { + g := generator.New(context.TODO(), func(gv *generator.Generator) { gv.Logger = log.New(&bytes.Buffer{}) }) g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) @@ -54,7 +54,7 @@ func TestGenerate(t *testing.T) { return m, nil } - g := generator.NewGenerator(context.TODO()) + g := generator.New(context.TODO()) g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) @@ -72,7 +72,7 @@ func TestGenerate(t *testing.T) { return m, nil } - g := generator.NewGenerator(context.TODO()) + g := generator.New(context.TODO()) g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token|key1.key2"}) @@ -129,7 +129,7 @@ func TestGenerate_withKeys_lookup(t *testing.T) { } for name, tt := range ttests { t.Run(name, func(t *testing.T) { - g := generator.NewGenerator(context.TODO()) + g := generator.New(context.TODO()) g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: tt.custFunc}) got, err := g.Generate([]string{tt.token}) @@ -175,7 +175,7 @@ func Test_IsParsed(t *testing.T) { func TestGenVars_NormalizeRawToken(t *testing.T) { t.Run("multiple tokens", func(t *testing.T) { - g := generator.NewGenerator(context.TODO()) + g := generator.New(context.TODO()) input := `GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|a @@ -298,7 +298,7 @@ func Test_ConfigManager_DiscoverTokens(t *testing.T) { for name, tt := range ttests { t.Run(name, func(t *testing.T) { config.VarPrefix = map[config.ImplementationPrefix]bool{"AWSPARAMSTR": true} - g := generator.NewGenerator(context.TODO()) + g := generator.New(context.TODO()) g.Config().WithTokenSeparator(tt.separator) gdt, err := g.DiscoverTokens(tt.input) if err != nil { @@ -319,7 +319,7 @@ func Test_ConfigManager_DiscoverTokens(t *testing.T) { } func Test_Generate_EnsureRaceFree(t *testing.T) { - g := generator.NewGenerator(context.TODO()) + g := generator.New(context.TODO()) input := ` fg diff --git a/generator/generatorvars.go b/generator/generatorvars.go index 79a56ae..d2ac986 100644 --- a/generator/generatorvars.go +++ b/generator/generatorvars.go @@ -43,26 +43,6 @@ func (rtm *RawTokenConfig) RawTokenMap() map[string]*config.ParsedTokenConfig { return rtm.tokenMap } -// type tokenMapSafe struct { -// mu *sync.Mutex -// tokenMap ReplacedToken -// } - -// func (tms *tokenMapSafe) getTokenMap() ReplacedToken { -// tms.mu.Lock() -// defer tms.mu.Unlock() -// return tms.tokenMap -// } - -// func (tms *tokenMapSafe) addKeyVal(key *config.ParsedTokenConfig, val string) { -// tms.mu.Lock() -// defer tms.mu.Unlock() -// // NOTE: still use the metadata in the key -// // there could be different versions / labels for the same token and hence different values -// // However the JSONpath look up -// tms.tokenMap[key.String()] = keySeparatorLookup(key, val) -// } - // keySeparatorLookup checks if the key contains // keySeparator character // If it does contain one then it tries to parse diff --git a/go.mod b/go.mod index 1d37d5a..bf22cb0 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,18 @@ module github.com/DevLabFoundry/configmanager/v3 -go 1.25.3 +go 1.25.4 require ( cloud.google.com/go/secretmanager v1.16.0 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0 - github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.0 + github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 - github.com/aws/aws-sdk-go-v2 v1.39.6 - github.com/aws/aws-sdk-go-v2/config v1.31.17 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.11 - github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4 + github.com/aws/aws-sdk-go-v2 v1.40.0 + github.com/aws/aws-sdk-go-v2/config v1.32.0 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1 + github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3 github.com/go-test/deep v1.1.1 github.com/googleapis/gax-go/v2 v2.15.0 github.com/hashicorp/vault/api v1.22.0 @@ -22,6 +22,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +require github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect + require ( cloud.google.com/go/auth v0.17.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect @@ -32,16 +34,16 @@ require ( github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/a8m/envsubst v1.4.3 github.com/aws/aws-sdk-go v1.55.8 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.18.21 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect github.com/aws/smithy-go v1.23.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/fatih/color v1.18.0 // indirect @@ -76,23 +78,24 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.10 // indirect + github.com/tetratelabs/wazero v1.10.1 go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect - golang.org/x/crypto v0.43.0 // indirect - golang.org/x/net v0.46.0 // indirect - golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.14.0 // indirect - google.golang.org/api v0.255.0 // indirect - google.golang.org/genproto v0.0.0-20251103181224-f26f9409b101 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect - google.golang.org/grpc v1.76.0 // indirect + google.golang.org/api v0.256.0 // indirect + google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect + google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/go.sum b/go.sum index f40c7f4..a8b3951 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute v1.49.1 h1:KYKIG0+pfpAWaAYayFkE/KPrAVCge0Hu82bPraAmsCk= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= @@ -12,14 +13,20 @@ cloud.google.com/go/secretmanager v1.16.0 h1:19QT7ZsLJ8FSP1k+4esQvuCD7npMJml6hYz cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0 h1:uU4FujKFQAz31AbWOO3INV9qfIanHeIUSsGhRlcJJmg= github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0/go.mod h1:qr3M3Oy6V98VR0c5tCHKUpaeJTRQh6KYzJewRtFWqfc= github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.0 h1:mXlQ+2C8A4KpXTIIYYxgFYqSivjGTBQidq/b0xxZLuk= github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.0/go.mod h1:K//Ck7MUa+r9jpV69WLeWnnju5WJx5120AFsEzvumII= +github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1 h1:j0hhYS006eJ54vusoap0f2NVZ1YY3QnaAEnLM68f0SQ= +github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1/go.mod h1:AdtInaXmK8eYmbjezRWgLz+Qs46nc9Up9GWGwteWNfw= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 h1:/g8S6wk65vfC6m3FIxJ+i5QDyN9JWwXI8Hb0Img10hU= @@ -37,46 +44,75 @@ github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= +github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc= +github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2/config v1.31.17 h1:QFl8lL6RgakNK86vusim14P2k8BFSxjvUkcWLDjgz9Y= github.com/aws/aws-sdk-go-v2/config v1.31.17/go.mod h1:V8P7ILjp/Uef/aX8TjGk6OHZN6IKPM5YW6S78QnRD5c= +github.com/aws/aws-sdk-go-v2/config v1.32.0 h1:T5WWJYnam9SzBLbsVYDu2HscLDe+GU1AUJtfcDAc/vA= +github.com/aws/aws-sdk-go-v2/config v1.32.0/go.mod h1:pSRm/+D3TxBixGMXlgtX4+MPO9VNtEEtiFmNpxksoxw= github.com/aws/aws-sdk-go-v2/credentials v1.18.21 h1:56HGpsgnmD+2/KpG0ikvvR8+3v3COCwaF4r+oWwOeNA= github.com/aws/aws-sdk-go-v2/credentials v1.18.21/go.mod h1:3YELwedmQbw7cXNaII2Wywd+YY58AmLPwX4LzARgmmA= +github.com/aws/aws-sdk-go-v2/credentials v1.19.0 h1:7zm+ez+qEqLaNsCSRaistkvJRJv8sByDOVuCnyHbP7M= +github.com/aws/aws-sdk-go-v2/credentials v1.19.0/go.mod h1:pHKPblrT7hqFGkNLxqoS3FlGoPrQg4hMIa+4asZzBfs= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.11 h1:DouhxUREBjfnNJFp1yNn/p1Gk5pzr1YNixcIOIudI2g= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.11/go.mod h1:QgVIY03/XoQs2iFr0MbQuQ/Tf1RwlkOvuySWMh1wph4= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1 h1:w6a0H79HrHf3lr+zrw+pSzR5B+caiQFAKiNHlrUcnoc= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1/go.mod h1:c6Vg0BRiU7v0MVhHupw90RyL120QBwAMLbDCzptGeMk= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 h1:BDgIUYGEo5TkayOWv/oBLPphWwNm/A91AebUjAu5L5g= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.1/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk= github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4 h1:UmkF0ipNy0Ps6csJl/ZRJ3K+DWe9q0A7LT3xfxoHbgg= github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4/go.mod h1:uNHuYAQazkHqpD+hVomA2+eDSuKJzerno7Fnha6N6/Y= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3 h1:ofiQvKwka2E3T8FXBsU1iWj7Yvk2wd1p4ZCdS6qGiKQ= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3/go.mod h1:+nlWvcgDPQ56mChEBzTC0puAMck+4onOFaHg5cE+Lgg= github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0= github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 h1:U//SlnkE1wOQiIImxzdY5PXat4Wq+8rlfVEw4Y7J8as= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.4/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 h1:MvlNs/f+9eM0mOjD9JzBUbf5jghyTk3p+O9yHMXX94Y= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug= github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 h1:mLlUgHn02ue8whiR4BmxxGJLR2gwU6s6ZzJ5wDamBUs= github.com/aws/aws-sdk-go-v2/service/sts v1.39.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0i9NT+JnqLTOI5nA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 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/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= @@ -200,6 +236,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8= +github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= @@ -219,13 +257,21 @@ go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42s golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -236,23 +282,37 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.255.0 h1:OaF+IbRwOottVCYV2wZan7KUq7UeNUQn1BcPc4K7lE4= google.golang.org/api v0.255.0/go.mod h1:d1/EtvCLdtiWEV4rAEHDHGh2bCnqsWhw+M8y2ECN4a8= +google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= +google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= google.golang.org/genproto v0.0.0-20251103181224-f26f9409b101 h1:MgBTzgUJFAmp2PlyqKJecSpZpjFxkYL3nDUIeH/6Q30= google.golang.org/genproto v0.0.0-20251103181224-f26f9409b101/go.mod h1:bbWg36d7wp3knc0hIlmJAnW5R/CQ2rzpEVb72eH4ex4= +google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba h1:Ze6qXW0j37YCqZdCD2LkzVSxgEWez0cO4NUyd44DiDY= +google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:4FLPzLA8eGAktPOTemJGDgDYRpLYwrNu4u2JtWINhnI= google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM= google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg= +google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= +google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/config/config.go b/internal/config/config.go index d1d05ed..a1898fc 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,6 +5,8 @@ import ( "errors" "fmt" "strings" + + "github.com/DevLabFoundry/configmanager/v3/plugins" ) const ( @@ -171,6 +173,18 @@ func (ptc *ParsedTokenConfig) WithSanitizedToken(v string) { } func (t *ParsedTokenConfig) ParseMetadata(metadataTyp any) error { + // empty map will be parsed as `{}` still resulting in a valid json + // and successful unmarshalling but default value pointer struct + if err := json.Unmarshal(fmt.Appendf(nil, t.parseMetadata()), metadataTyp); err != nil { + // It would very hard to test this since + // we are forcing the key and value to be strings + // return non-filled pointer + return err + } + return nil +} + +func (t *ParsedTokenConfig) parseMetadata() string { // crude json like builder from key/val tags // since we are only ever dealing with a string input // extracted from the token there is little chance panic would occur here @@ -182,16 +196,7 @@ func (t *ParsedTokenConfig) ParseMetadata(metadataTyp any) error { metaMap = append(metaMap, fmt.Sprintf(`"%s":"%s"`, mapKeyVal[0], mapKeyVal[1])) } } - - // empty map will be parsed as `{}` still resulting in a valid json - // and successful unmarshalling but default value pointer struct - if err := json.Unmarshal(fmt.Appendf(nil, `{%s}`, strings.Join(metaMap, ",")), metadataTyp); err != nil { - // It would very hard to test this since - // we are forcing the key and value to be strings - // return non-filled pointer - return err - } - return nil + return fmt.Sprintf(`{%s}`, strings.Join(metaMap, ",")) } // StoreToken returns the sanitized token without: @@ -243,3 +248,25 @@ func (t *ParsedTokenConfig) Prefix() ImplementationPrefix { func (t *ParsedTokenConfig) TokenSeparator() string { return t.tokenSeparator } + +func (t *ParsedTokenConfig) JSONMessagExchange() (*plugins.MessagExchange, error) { + md := map[string]any{} + if err := json.Unmarshal([]byte(t.parseMetadata()), &md); err != nil { + return nil, err + } + + jme := &plugins.MessagExchange{ + Token: t.StoreToken(), + Metadata: md, + } + + return jme, nil +} + +func (t *ParsedTokenConfig) JSONMessagExchangeBytes() ([]byte, error) { + j, err := t.JSONMessagExchange() + if err != nil { + return nil, err + } + return json.Marshal(j) +} diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go new file mode 100644 index 0000000..5eb80c7 --- /dev/null +++ b/internal/plugin/plugin.go @@ -0,0 +1,16 @@ +package plugin + +import "github.com/DevLabFoundry/configmanager/v3/internal/config" + +// Plugin is responsible for managing plugins within configmanager +// +// It includes the following methods +// - fetch plugins from known sources +// - maintains a list of tokens answerable by a specified pluginEngine +type Plugin struct { + Implementations config.ImplementationPrefix + SourcePath string + Version string + fallbackPaths []string + engineInstance *Engine +} diff --git a/internal/plugin/tester/main.go b/internal/plugin/tester/main.go new file mode 100644 index 0000000..b17e481 --- /dev/null +++ b/internal/plugin/tester/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/internal/plugin" +) + +func main() { + inputReader, err := os.Open("/Users/dusannitschneider/git/dnitsch/configmanager/plugins/awsparams/awsparams.wasm") + if err != nil { + log.Fatal(fmt.Errorf("open plugin.wasm: %w", err)) + } + + ctx := context.Background() + + // Load the compiled WASI plugin. + engine, err := plugin.NewEngine(ctx, inputReader) + if err != nil { + log.Fatal(err) + } + defer engine.Close(ctx) + + inst, err := engine.NewApiInstance(ctx) + if err != nil { + log.Fatal(err) + } + defer inst.Close(ctx) + + // os.Setenv("AWS_PROFILE", "anabode_terraform_dev") + // os.Setenv("AWS_REGION", "eu-west-1") + t1, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + t1.WithSanitizedToken("/int-test/pocketbase/admin-pwd") + val, err := inst.TokenValue(ctx, t1) + if err != nil { + log.Fatal(err) + } + + fmt.Println(string(val)) + + os.Exit(0) +} diff --git a/internal/plugin/wasip1.go b/internal/plugin/wasip1.go new file mode 100644 index 0000000..268371f --- /dev/null +++ b/internal/plugin/wasip1.go @@ -0,0 +1,263 @@ +// Package plugin +// provides reactor style module +// we can explore the plugin provided host module +package plugin + +import ( + "context" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" +) + +var ( + ErrMissingMethod = errors.New("missing method on the wasiLib instance") + ErrAllocMemForParam = errors.New("failed to allocate memory for property") + ErrAllocateOutPtrZeroLen = errors.New("allocate returned 0 for output pointer") + ErrMemoryReadFailed = errors.New("mem.Read(out) failed") + ErrEmptyToken = errors.New("token must not be empty") +) + +// ==================== +// Engine & ApiInstance +// ==================== + +type Engine struct { + r wazero.Runtime + compiledModule wazero.CompiledModule +} + +// NewEngine compiles the WASI module once for the lifetime of the program. +func NewEngine(ctx context.Context, ps io.ReadCloser) (*Engine, error) { + r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig()) + if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { + _ = r.Close(ctx) + return nil, fmt.Errorf("instantiate WASI: %w", err) + } + + defer ps.Close() + wasiLib, err := io.ReadAll(ps) + if err != nil { + _ = r.Close(ctx) + return nil, fmt.Errorf("read plugin: %w", err) + } + + cm, err := r.CompileModule(ctx, wasiLib) + if err != nil { + _ = r.Close(ctx) + return nil, fmt.Errorf("compile module: %w", err) + } + + return &Engine{ + r: r, + compiledModule: cm, + }, nil +} + +// Close shuts down the runtime. +func (e *Engine) Close(ctx context.Context) error { + return e.r.Close(ctx) +} + +type ApiInstance struct { + mod api.Module + mem api.Memory + // exported alloc helpers + allocate api.Function + deallocate api.Function + // exported business function + tokenValue api.Function + // scratch output buffers + outPtr uint32 + outCap uint32 + outLenPtr uint32 // 4-byte cell for required length +} + +// NewApiInstance instantiates a fresh module instance. +func (e *Engine) NewApiInstance(ctx context.Context) (*ApiInstance, error) { + mod, err := e.r.InstantiateModule(ctx, e.compiledModule, wazero.NewModuleConfig().WithStartFunctions("_initialize")) + if err != nil { + return nil, fmt.Errorf("instantiate module: %w", err) + } + + inst := &ApiInstance{ + mod: mod, + mem: mod.Memory(), + allocate: mod.ExportedFunction("allocate"), + deallocate: mod.ExportedFunction("deallocate"), + tokenValue: mod.ExportedFunction("strategy_token_value"), + } + + for name, exported := range map[string]api.Function{ + "allocate": inst.allocate, + "deallocate": inst.deallocate, + "strategy_token_value": inst.tokenValue, + } { + if exported == nil { + return nil, fmt.Errorf("%w, method %q not found on exports", ErrMissingMethod, name) + } + } + + return inst, nil +} + +// Close instance (optional). +func (i *ApiInstance) Close(ctx context.Context) { + i.freeScratch(ctx) + _ = i.mod.Close(ctx) +} + +// put allocates module memory and writes bytes into it. +// returns (ptr, size). caller must deallocate(ptr, size). +func (i *ApiInstance) put(ctx context.Context, b []byte) (uint32, uint32, error) { + if len(b) == 0 { + return 0, 0, ErrEmptyToken + } + + res, err := i.allocate.Call(ctx, uint64(len(b))) + if err != nil { + return 0, 0, fmt.Errorf("allocate: %w", err) + } + ptr := uint32(res[0]) + if ptr == 0 { + return 0, 0, fmt.Errorf("allocate returned 0: %w", ErrAllocMemForParam) + } + + if ok := i.mem.Write(ptr, b); !ok { + _, _ = i.deallocate.Call(ctx, uint64(ptr), uint64(len(b))) + return 0, 0, fmt.Errorf("mem.Write failed: %w", ErrAllocMemForParam) + } + + return ptr, uint32(len(b)), nil +} + +// ensureOut ensures the scratch output buffer has at least `need` bytes. +// allocates outLenPtr (4 bytes) once. +func (i *ApiInstance) ensureOut(ctx context.Context, need uint32) error { + // outLenPtr is a 4-byte cell for required length + if i.outLenPtr == 0 { + res, err := i.allocate.Call(ctx, 4) + if err != nil { + return fmt.Errorf("allocate outLenPtr: %w", err) + } + i.outLenPtr = uint32(res[0]) + if i.outLenPtr == 0 { + return ErrAllocateOutPtrZeroLen + } + } + + if need <= i.outCap { + return nil + } + + // grow if needed - free old and alloc new + if i.outPtr != 0 { + _, _ = i.deallocate.Call(ctx, uint64(i.outPtr), uint64(i.outCap)) + i.outPtr, i.outCap = 0, 0 + } + + res, err := i.allocate.Call(ctx, uint64(need)) + if err != nil { + return fmt.Errorf("allocate outPtr: %w", err) + } + i.outPtr, i.outCap = uint32(res[0]), need + if i.outPtr == 0 { + return ErrAllocateOutPtrZeroLen + } + return nil +} + +// freeScratch frees the reusable output buffers (call once per instance). +func (i *ApiInstance) freeScratch(ctx context.Context) { + if i.outPtr != 0 { + _, _ = i.deallocate.Call(ctx, uint64(i.outPtr), uint64(i.outCap)) + i.outPtr, i.outCap = 0, 0 + } + if i.outLenPtr != 0 { + _, _ = i.deallocate.Call(ctx, uint64(i.outLenPtr), 4) + i.outLenPtr = 0 + } +} + +// TokenValue is the nice host-side API: string in, []byte out. +func (i *ApiInstance) TokenValue(ctx context.Context, token *config.ParsedTokenConfig) ([]byte, error) { + if token.StoreToken() == "" { + return nil, ErrEmptyToken + } + tokenBytes, err := token.JSONMessagExchangeBytes() + tokenPtr, tokenLen, err := i.put(ctx, tokenBytes) + if err != nil { + return nil, fmt.Errorf("put input: %w", err) + } + defer i.deallocate.Call(ctx, uint64(tokenPtr), uint64(tokenLen)) + + // start with a smallish buffer; plugin will ask for more if needed + if err := i.ensureOut(ctx, 64); err != nil { + return nil, fmt.Errorf("ensureOut: %w", err) + } + + call := func() (int32, uint32, error) { + res, err := i.tokenValue.Call( + ctx, + uint64(tokenPtr), uint64(tokenLen), // sanitizedToken + uint64(i.outPtr), uint64(i.outCap), // outPtr, outCap + uint64(i.outLenPtr), // outLenPtr + ) + if err != nil { + return 0, 0, fmt.Errorf("call strategy_token_value: %w", err) + } + + lenBytes, ok := i.mem.Read(i.outLenPtr, 4) + if !ok { + return int32(res[0]), 0, ErrMemoryReadFailed + } + + need := binary.LittleEndian.Uint32(lenBytes) + return int32(res[0]), need, nil + } + + rc, need, err := call() + if err != nil { + return nil, err + } + + if rc == plugins.ERR_BUF_TOO_SMALL { + if err := i.ensureOut(ctx, need); err != nil { + return nil, fmt.Errorf("ensureOut resize: %w", err) + } + rc, need, err = call() + if err != nil { + return nil, err + } + } + + if rc != plugins.OK { + switch rc { + case plugins.ERR_INVALID_UTF8: + return nil, errors.New("token value: invalid UTF-8 in input") + case plugins.ERR_EMPTY_INPUT: + return nil, ErrEmptyToken + case plugins.ERR_BUF_TOO_SMALL: + return nil, fmt.Errorf("token value: buffer too small even after resize (need=%d)", need) + default: + return nil, fmt.Errorf("token value: unknown error code %d", rc) + } + } + + out, ok := i.mem.Read(i.outPtr, need) + if !ok { + return nil, ErrMemoryReadFailed + } + + // Detach from wasm memory. + result := make([]byte, need) + copy(result, out) + return result, nil +} diff --git a/internal/plugin/wasip1_test.go b/internal/plugin/wasip1_test.go new file mode 100644 index 0000000..5442eb1 --- /dev/null +++ b/internal/plugin/wasip1_test.go @@ -0,0 +1,47 @@ +package plugin_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/internal/plugin" +) + +func Test_FullFlow(t *testing.T) { + inputReader, err := os.Open("/Users/dusannitschneider/git/dnitsch/configmanager/plugins/awsparams/awsparams.wasm") + if err != nil { + t.Fatal(fmt.Errorf("open plugin.wasm: %w", err)) + } + ctx := context.Background() + + // Load the compiled WASI plugin. + engine, err := plugin.NewEngine(ctx, inputReader) + if err != nil { + t.Fatal(err) + } + defer engine.Close(ctx) + + inst, err := engine.NewApiInstance(ctx) + if err != nil { + t.Fatal(err) + } + defer inst.Close(ctx) + + os.Setenv("AWS_PROFILE", "anabode_terraform_dev") + os.Setenv("AWS_REGION", "eu-west-1") + t1, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + t1.WithSanitizedToken("/int-test/pocketbase/admin-pwd") + val, err := inst.TokenValue(ctx, t1) + if err != nil { + t.Fatal(err) + } + fmt.Printf("TokenValue(\"foo\") => %q\n", string(val)) + + // Zero-length test (should error) + t2, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + _, err = inst.TokenValue(ctx, t2) + fmt.Printf("TokenValue(\"\") error: %v\n", err) +} diff --git a/plugins/awsparams/main.go b/plugins/awsparams/main.go new file mode 100644 index 0000000..4bd4ec6 --- /dev/null +++ b/plugins/awsparams/main.go @@ -0,0 +1,203 @@ +package main + +import ( + "context" + "encoding/binary" + "encoding/json" + "sync" + "unicode/utf8" + "unsafe" + + "github.com/DevLabFoundry/configmanager/v3/plugins" +) + +// ==================== +// Bump allocator +// ==================== + +const heapSize = 64 * 1024 // 64 KiB arena; tune as needed + +type bumpAllocator struct { + mu sync.Mutex + heap []byte + used uint32 +} + +var alloc = bumpAllocator{ + heap: make([]byte, heapSize), +} + +// round allocation up to 8 bytes for basic alignment. +func roundUp(n uint32) uint32 { + const align = 8 + return (n + align - 1) &^ (align - 1) +} + +//go:wasmexport allocate +func Allocate(size uint32) uint32 { + if size == 0 { + return 0 + } + size = roundUp(size) + + alloc.mu.Lock() + defer alloc.mu.Unlock() + + if alloc.used+size > uint32(len(alloc.heap)) { + // Out of memory in our arena. + return 0 + } + + offset := alloc.used + alloc.used += size + + // Return pointer into linear memory for &heap[offset]. + return uint32(uintptr(unsafe.Pointer(&alloc.heap[offset]))) +} + +//go:wasmexport deallocate +func Deallocate(ptr, size uint32) { + // For a simple bump allocator, deallocate is a no-op. + // Memory is reclaimed when the module instance is destroyed. + _ = ptr + _ = size +} + +type Hdr struct { + Data uintptr + Len int + Cap int +} + +// ==================== +// Helpers +// ==================== + +// bytesFromPtrLen reinterprets a (ptr,len) pair in wasm linear memory +// as a Go []byte without copying. +func bytesFromPtrLen(ptr, length uint32) []byte { + if length == 0 { + return nil + } + + hdr := Hdr{ + Data: uintptr(ptr), + Len: int(length), + Cap: int(length), + } + + return *(*[]byte)(unsafe.Pointer(&hdr)) +} + +// ==================== +// strategy_token_value +// ==================== +// +// ABI: +// +// strategy_token_value( +// inPtr, inLen, outPtr, outCap, outLenPtr uint32, +// ) int32 +// +// Host contract: +// - Input bytes are at (inPtr, inLen) +// - Output buffer is [outPtr : outPtr+outCap) +// - outLenPtr points to 4 bytes where we write the required length +// +// Behaviour: +// - If input length == 0 => ERR_EMPTY_INPUT +// - If invalid UTF-8 => ERR_INVALID_UTF8 +// - Always write required length to *outLenPtr (little-endian) +// - If required > outCap => ERR_BUF_TOO_SMALL +// - Else copy into caller buffer and return OK +// +//go:wasmexport strategy_token_value +func StrategyTokenValue(tokenPtr, tokenLen, outPtr, outCap, outLenPtr uint32) int32 { + defer func() { + // Make sure panics don't leak as traps. + if r := recover(); r != nil { + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, 0) + } + } + } + }() + + if tokenLen == 0 { + // Mark required length as 0 and signal error. + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, 0) + } + } + return plugins.ERR_EMPTY_INPUT + } + + tokenBytes := bytesFromPtrLen(tokenPtr, tokenLen) + if !utf8.Valid(tokenBytes) { + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, uint32(len(tokenBytes))) + } + } + return plugins.ERR_INVALID_UTF8 + } + + // --- Business logic (replace with your real token strategy) --- + // unmarshal string into an object + token := &plugins.MessagExchange{} + if err := json.Unmarshal(tokenBytes, token); err != nil { + return plugins.ERR_FAILED_UNMARSHAL_MESSAGE + } + + // logger := log.New(os.Stdout) + // logger.SetLevel(log.DebugLvl) + + store, err := NewParamStore(context.Background()) + if err != nil { + return plugins.ERR_INIT_STORE + } + + outStr, err := store.Value(token) + if err != nil { + return plugins.ERR_FAILED_VALUE_RETRIEVAL + } + + outBytes := []byte(outStr) + // -------------------------------------------------------------- + // BEGIN RETURN Allocation + // -------------------------------------------------------------- + required := uint32(len(outBytes)) + + // Always write required length. + if outLenPtr != 0 { + lenCell := bytesFromPtrLen(outLenPtr, 4) + if len(lenCell) != 4 { + return plugins.ERR_INTERNAL + } + binary.LittleEndian.PutUint32(lenCell, required) + } + + if required > outCap { + return plugins.ERR_BUF_TOO_SMALL + } + + if required == 0 { + return plugins.OK + } + + outSlice := bytesFromPtrLen(outPtr, outCap) + if uint32(len(outSlice)) < required { + return plugins.ERR_INTERNAL + } + + copy(outSlice, outBytes) + return plugins.OK +} + +// main is required for wasip1 +// scaffolds the `_initialize` method +func main() {} + +// GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o awsparams.wasm diff --git a/plugins/awsparams/paramstore.go b/plugins/awsparams/paramstore.go new file mode 100644 index 0000000..9001e53 --- /dev/null +++ b/plugins/awsparams/paramstore.go @@ -0,0 +1,67 @@ +package main + +import ( + "context" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/aws/aws-sdk-go-v2/aws" + awsConf "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ssm" +) + +type paramStoreApi interface { + GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) +} + +type ParamStore struct { + svc paramStoreApi + ctx context.Context + config *ParamStrConfig + token *config.ParsedTokenConfig +} + +type ParamStrConfig struct { + // reserved for potential future use +} + +func NewParamStore(ctx context.Context) (*ParamStore, error) { + cfg, err := awsConf.LoadDefaultConfig(ctx) + if err != nil { + return nil, err + } + c := ssm.NewFromConfig(cfg) + + return &ParamStore{ + svc: c, + ctx: ctx, + }, nil +} + +func (s *ParamStore) WithSvc(svc paramStoreApi) { + s.svc = svc +} + +func (imp *ParamStore) Value(token *plugins.MessagExchange) (string, error) { + // imp.logger.Info("%s", "Concrete implementation ParameterStore") + // imp.logger.Info("ParamStore Token: %s", token.Token) + + input := &ssm.GetParameterInput{ + Name: aws.String(token.Token), + WithDecryption: aws.Bool(true), + } + ctx, cancel := context.WithCancel(imp.ctx) + defer cancel() + + result, err := imp.svc.GetParameter(ctx, input) + if err != nil { + // imp.logger.Error(plugins.ImplementationNetworkErr, config.ParamStorePrefix, err, token) + return "", err + } + + if result.Parameter.Value != nil { + return *result.Parameter.Value, nil + } + // imp.logger.Error("value retrieved but empty for token: %v", imp.token) + return "", nil +} diff --git a/plugins/awsparams/paramstore_test.go b/plugins/awsparams/paramstore_test.go new file mode 100644 index 0000000..84a0d23 --- /dev/null +++ b/plugins/awsparams/paramstore_test.go @@ -0,0 +1,152 @@ +package main_test + +import ( + "context" + "fmt" + "io" + "strings" + "testing" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/internal/store" + "github.com/DevLabFoundry/configmanager/v3/internal/testutils" + "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/aws/aws-sdk-go-v2/service/ssm/types" +) + +type mockParamApi func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) + +func (m mockParamApi) GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { + return m(ctx, params, optFns...) +} + +func awsParamtStoreCommonGetChecker(t *testing.T, params *ssm.GetParameterInput) { + if params.Name == nil { + t.Fatal("expect name to not be nil") + } + + if strings.Contains(*params.Name, "#") { + t.Errorf("incorrectly stripped token separator") + } + + if strings.Contains(*params.Name, string(config.ParamStorePrefix)) { + t.Errorf("incorrectly stripped prefix") + } + + if !*params.WithDecryption { + t.Fatal("expect WithDecryption to not be false") + } +} + +func Test_GetParamStore(t *testing.T) { + var ( + tsuccessParam = "someVal" + // tsuccessObj map[string]string = map[string]string{"AWSPARAMSTR#/token/1": "someVal"} + ) + tests := map[string]struct { + token func() *config.ParsedTokenConfig + expect string + mockClient func(t *testing.T) mockParamApi + }{ + "successVal": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/demo/configmanager" + tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn.WithSanitizedToken("/token/1") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + tsuccessParam, func(t *testing.T) mockParamApi { + return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { + t.Helper() + awsParamtStoreCommonGetChecker(t, params) + return &ssm.GetParameterOutput{ + Parameter: &types.Parameter{Value: &tsuccessParam}, + }, nil + }) + }, + }, + "successVal with keyseparator": { + func() *config.ParsedTokenConfig { + // "AWSPARAMSTR#/token/1|somekey", + tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn.WithSanitizedToken("/token/1") + tkn.WithKeyPath("somekey") + tkn.WithMetadata("") + return tkn + }, + tsuccessParam, func(t *testing.T) mockParamApi { + return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { + t.Helper() + awsParamtStoreCommonGetChecker(t, params) + + if strings.Contains(*params.Name, "|somekey") { + t.Errorf("incorrectly stripped key separator") + } + + return &ssm.GetParameterOutput{ + Parameter: &types.Parameter{Value: &tsuccessParam}, + }, nil + }) + }, + }, + "errored": { + func() *config.ParsedTokenConfig { + // "AWSPARAMSTR#/token/1", + tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn.WithSanitizedToken("/token/1") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + "unable to retrieve", func(t *testing.T) mockParamApi { + return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { + t.Helper() + awsParamtStoreCommonGetChecker(t, params) + return nil, fmt.Errorf("unable to retrieve") + }) + }, + }, + "nil to empty": { + func() *config.ParsedTokenConfig { + // "AWSPARAMSTR#/token/1", + tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn.WithSanitizedToken("/token/1") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + "", func(t *testing.T) mockParamApi { + return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { + t.Helper() + awsParamtStoreCommonGetChecker(t, params) + return &ssm.GetParameterOutput{ + Parameter: &types.Parameter{Value: nil}, + }, nil + }) + }, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + impl, err := store.NewParamStore(context.TODO(), log.New(io.Discard)) + if err != nil { + t.Errorf(testutils.TestPhrase, err.Error(), nil) + } + impl.WithSvc(tt.mockClient(t)) + impl.SetToken(tt.token()) + got, err := impl.Value() + if err != nil { + if err.Error() != tt.expect { + t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) + } + return + } + if got != tt.expect { + t.Errorf(testutils.TestPhrase, got, tt.expect) + } + }) + } +} diff --git a/plugins/awssecrets/main.go b/plugins/awssecrets/main.go new file mode 100644 index 0000000..8a03b12 --- /dev/null +++ b/plugins/awssecrets/main.go @@ -0,0 +1,197 @@ +package main + +import ( + "encoding/binary" + "strings" + "sync" + "unicode/utf8" + "unsafe" + + "github.com/DevLabFoundry/configmanager/v3/plugins" +) + +// ==================== +// Bump allocator +// ==================== + +const heapSize = 64 * 1024 // 64 KiB arena; tune as needed + +type bumpAllocator struct { + mu sync.Mutex + heap []byte + used uint32 +} + +var alloc = bumpAllocator{ + heap: make([]byte, heapSize), +} + +// round allocation up to 8 bytes for basic alignment. +func roundUp(n uint32) uint32 { + const align = 8 + return (n + align - 1) &^ (align - 1) +} + +//go:wasmexport allocate +func Allocate(size uint32) uint32 { + if size == 0 { + return 0 + } + size = roundUp(size) + + alloc.mu.Lock() + defer alloc.mu.Unlock() + + if alloc.used+size > uint32(len(alloc.heap)) { + // Out of memory in our arena. + return 0 + } + + offset := alloc.used + alloc.used += size + + // Return pointer into linear memory for &heap[offset]. + return uint32(uintptr(unsafe.Pointer(&alloc.heap[offset]))) +} + +//go:wasmexport deallocate +func Deallocate(ptr, size uint32) { + // For a simple bump allocator, deallocate is a no-op. + // Memory is reclaimed when the module instance is destroyed. + _ = ptr + _ = size +} + +type Hdr struct { + Data uintptr + Len int + Cap int +} + +// ==================== +// Helpers +// ==================== + +// bytesFromPtrLen reinterprets a (ptr,len) pair in wasm linear memory +// as a Go []byte without copying. +func bytesFromPtrLen(ptr, length uint32) []byte { + if length == 0 { + return nil + } + + hdr := Hdr{ + Data: uintptr(ptr), + Len: int(length), + Cap: int(length), + } + + return *(*[]byte)(unsafe.Pointer(&hdr)) +} + +// ==================== +// strategy_token_value +// ==================== +// +// ABI: +// +// strategy_token_value( +// inPtr, inLen, outPtr, outCap, outLenPtr uint32, +// ) int32 +// +// Host contract: +// - Input bytes are at (inPtr, inLen) +// - Output buffer is [outPtr : outPtr+outCap) +// - outLenPtr points to 4 bytes where we write the required length +// +// Behaviour: +// - If input length == 0 => ERR_EMPTY_INPUT +// - If invalid UTF-8 => ERR_INVALID_UTF8 +// - Always write required length to *outLenPtr (little-endian) +// - If required > outCap => ERR_BUF_TOO_SMALL +// - Else copy into caller buffer and return OK +// +//go:wasmexport strategy_token_value +func StrategyTokenValue(tokenPtr, tokenLen, metadataStrPtr, metadataStrLen, outPtr, outCap, outLenPtr uint32) int32 { + defer func() { + // Make sure panics don't leak as traps. + if r := recover(); r != nil { + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, 0) + } + } + } + }() + + if tokenLen == 0 { + // Mark required length as 0 and signal error. + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, 0) + } + } + return plugins.ERR_EMPTY_INPUT + } + + tokenBytes := bytesFromPtrLen(tokenPtr, tokenLen) + if !utf8.Valid(tokenBytes) { + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, uint32(len(tokenBytes))) + } + } + return plugins.ERR_INVALID_UTF8 + } + + if metadataStrLen > 0 { + metadataStrBytes := bytesFromPtrLen(metadataStrPtr, metadataStrLen) + if !utf8.Valid(metadataStrBytes) { + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(metadataStrPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, uint32(len(tokenBytes))) + } + } + return plugins.ERR_INVALID_UTF8 + } + } + + // --- Business logic (replace with your real token strategy) --- + + inStr := string(tokenBytes) + outStr := "TOKEN_VALUE:" + strings.ToUpper(inStr) + outBytes := []byte(outStr) + // -------------------------------------------------------------- + + required := uint32(len(outBytes)) + + // Always write required length. + if outLenPtr != 0 { + lenCell := bytesFromPtrLen(outLenPtr, 4) + if len(lenCell) != 4 { + return plugins.ERR_INTERNAL + } + binary.LittleEndian.PutUint32(lenCell, required) + } + + if required > outCap { + return plugins.ERR_BUF_TOO_SMALL + } + + if required == 0 { + return plugins.OK + } + + outSlice := bytesFromPtrLen(outPtr, outCap) + if uint32(len(outSlice)) < required { + return plugins.ERR_INTERNAL + } + + copy(outSlice, outBytes) + return plugins.OK +} + +// main is required for wasip1 +// scaffolds the `_instantiate` method +func main() {} + +// GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o awssecrets.wasm diff --git a/plugins/scaffolding.go b/plugins/scaffolding.go new file mode 100644 index 0000000..f4bdce0 --- /dev/null +++ b/plugins/scaffolding.go @@ -0,0 +1,30 @@ +package plugins + +import "errors" + +// Error codes shared with the host. +const ( + OK int32 = 0 + ERR_BUF_TOO_SMALL int32 = -1 + ERR_INVALID_UTF8 int32 = -2 + ERR_EMPTY_INPUT int32 = -3 + ERR_INTERNAL int32 = -4 + ERR_FAILED_UNMARSHAL_MESSAGE int32 = -5 + ERR_INIT_STORE int32 = -6 + ERR_FAILED_VALUE_RETRIEVAL int32 = -7 +) + +const ImplementationNetworkErr string = "implementation %s error: %v for token: %s" + +var ( + ErrRetrieveFailed = errors.New("failed to retrieve config item") + ErrClientInitialization = errors.New("failed to initialize the client") + ErrEmptyResponse = errors.New("value retrieved but empty for token") + ErrServiceCallFailed = errors.New("failed to complete the service call") +) + +type MessagExchange struct { + Token string `json:"token"` + Metadata map[string]any `json:"metadata,omitempty"` + Version string `json:"version"` +} From 6b8689c05de0d39fe45f6e69ac61cba73840308c Mon Sep 17 00:00:00 2001 From: dnitsch Date: Mon, 24 Nov 2025 17:45:55 +0000 Subject: [PATCH 02/15] fix: release container --- .github/workflows/release_container.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_container.yml b/.github/workflows/release_container.yml index 9a1c45d..3ac8556 100644 --- a/.github/workflows/release_container.yml +++ b/.github/workflows/release_container.yml @@ -2,7 +2,7 @@ name: Publish Container on: workflow_run: - workflows: ['Lint and Test'] + workflows: ['CI'] types: - completed branches: From f0582ec9a4e8cdd5cab93c5ea655410ec9030733 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Wed, 26 Nov 2025 09:29:15 +0000 Subject: [PATCH 03/15] fix: interim goplugin from hashicorp --- go.mod | 8 ++++- go.sum | 6 ++++ internal/plugin/plugin.go | 62 ++++++++++++++++++++++++++++++++++++- internal/plugin/provider.go | 62 +++++++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 internal/plugin/provider.go diff --git a/go.mod b/go.mod index bf22cb0..a8074af 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,12 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect +require ( + github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/oklog/run v1.1.0 // indirect +) require ( cloud.google.com/go/auth v0.17.0 // indirect @@ -59,6 +64,7 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-plugin v1.7.0 github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect diff --git a/go.sum b/go.sum index a8b3951..f7638e0 100644 --- a/go.sum +++ b/go.sum @@ -154,6 +154,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= @@ -174,6 +176,8 @@ github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicH github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= github.com/hashicorp/vault/api/auth/aws v0.11.0 h1:lWdUxrzvPotg6idNr62al4w97BgI9xTDdzMCTViNH2s= github.com/hashicorp/vault/api/auth/aws v0.11.0/go.mod h1:PWqdH/xqaudapmnnGP9ip2xbxT/kRW2qEgpqiQff6Gc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= @@ -205,6 +209,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go index 5eb80c7..24c2b48 100644 --- a/internal/plugin/plugin.go +++ b/internal/plugin/plugin.go @@ -1,6 +1,11 @@ package plugin -import "github.com/DevLabFoundry/configmanager/v3/internal/config" +import ( + "net/rpc" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/hashicorp/go-plugin" +) // Plugin is responsible for managing plugins within configmanager // @@ -14,3 +19,58 @@ type Plugin struct { fallbackPaths []string engineInstance *Engine } + +// ValueProvider is the interface that we're exposing as a plugin. +type ValueProvider interface { + Value(token string, metadata string) (string, error) +} + +// Here is an implementation that talks over RPC +type StorePluginRPC struct{ client *rpc.Client } + +func (g *StorePluginRPC) Greet() string { + var resp string + err := g.client.Call("Plugin.Greet", new(interface{}), &resp) + if err != nil { + // You usually want your interfaces to return errors. If they don't, + // there isn't much other choice here. + panic(err) + } + + return resp +} + +// Here is the RPC server that GreeterRPC talks to, conforming to +// the requirements of net/rpc +type GreeterRPCServer struct { + // This is the real implementation + Impl ValueProvider +} + +func (s *GreeterRPCServer) Greet(args interface{}, resp *string) error { + *resp = s.Impl.Value() + return nil +} + +// This is the implementation of plugin.Plugin so we can serve/consume this +// +// This has two methods: Server must return an RPC server for this plugin +// type. We construct a GreeterRPCServer for this. +// +// Client must return an implementation of our interface that communicates +// over an RPC client. We return GreeterRPC for this. +// +// Ignore MuxBroker. That is used to create more multiplexed streams on our +// plugin connection and is a more advanced use case. +type GreeterPlugin struct { + // Impl Injection + Impl ValueProvider +} + +func (p *GreeterPlugin) Server(*plugin.MuxBroker) (interface{}, error) { + return &GreeterRPCServer{Impl: p.Impl}, nil +} + +func (GreeterPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { + return &StorePluginRPC{client: c}, nil +} diff --git a/internal/plugin/provider.go b/internal/plugin/provider.go new file mode 100644 index 0000000..9a9e236 --- /dev/null +++ b/internal/plugin/provider.go @@ -0,0 +1,62 @@ +package plugin + +import ( + "fmt" + "log" + "os" + "os/exec" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/go-plugin/examples/basic/shared" +) + +// handshakeConfigs are used to just do a basic handshake between +// a plugin and host. If the handshake fails, a user friendly error is shown. +// This prevents users from executing bad plugins or executing a plugin +// directory. It is a UX feature, not a security feature. +var handshakeConfig = plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "BASIC_PLUGIN", + MagicCookieValue: "hello", +} + +// pluginMap is the map of plugins we can dispense. +var pluginMap = map[string]plugin.Plugin{ + "greeter": &shared.GreeterPlugin{}, +} + +func Init() { + // Create an hclog.Logger + logger := hclog.New(&hclog.LoggerOptions{ + Name: "plugin", + Output: os.Stdout, + Level: hclog.Debug, + }) + + // We're a host! Start by launching the plugin process. + client := plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: handshakeConfig, + Plugins: pluginMap, + Cmd: exec.Command("./plugin/greeter"), + Logger: logger, + }) + defer client.Kill() + + // Connect via RPC + rpcClient, err := client.Client() + if err != nil { + log.Fatal(err) + } + + // Request the plugin + raw, err := rpcClient.Dispense("greeter") + if err != nil { + log.Fatal(err) + } + + // We should have a Greeter now! This feels like a normal interface + // implementation but is in fact over an RPC connection. + greeter := raw.(shared.Greeter) + fmt.Println(greeter.Greet()) +} From 4f7aeccfd1494de348187a7edc89fb03f774dcb9 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Sat, 29 Nov 2025 11:44:48 +0000 Subject: [PATCH 04/15] fix: interim commit shared lib needs more work --- .gitignore | 1 + buf.gen.yaml | 15 ++ buf.yaml | 9 ++ eirctl.yaml | 34 ++++- go.mod | 32 ++-- go.sum | 219 +++++++++++++++------------ plugins/grpc.go | 35 +++++ plugins/interface.go | 54 +++++++ plugins/proto/token_store.pb.go | 183 ++++++++++++++++++++++ plugins/proto/token_store.proto | 16 ++ plugins/proto/token_store_grpc.pb.go | 107 +++++++++++++ 11 files changed, 588 insertions(+), 117 deletions(-) create mode 100644 buf.gen.yaml create mode 100644 buf.yaml create mode 100644 plugins/grpc.go create mode 100644 plugins/interface.go create mode 100644 plugins/proto/token_store.pb.go create mode 100644 plugins/proto/token_store.proto create mode 100644 plugins/proto/token_store_grpc.pb.go diff --git a/.gitignore b/.gitignore index 573d3c6..81bfbd1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ dist # local testers and local/ +.bin diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000..eb64d58 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,15 @@ +version: v2 +# this ensures we can store the .proto and generated files in the same directory +clean: false +plugins: + - remote: buf.build/protocolbuffers/go + out: plugins/proto + opt: + - paths=source_relative + - remote: buf.build/grpc/go:v1.3.0 + out: plugins/proto + opt: + - paths=source_relative + - require_unimplemented_servers=false +inputs: + - directory: plugins/proto diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000..d055ccd --- /dev/null +++ b/buf.yaml @@ -0,0 +1,9 @@ +version: v2 +modules: + - path: ./plugins/proto +lint: + use: + - STANDARD +breaking: + use: + - FILE diff --git a/eirctl.yaml b/eirctl.yaml index f9e4f9c..7c64937 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -10,10 +10,24 @@ contexts: bash: container: name: mirror.gcr.io/bash:5.0.18-alpine3.22 + buf: + container: + name: docker.io/bufbuild/buf:1.61 + pull_timeout: 0 + entrypoint: /usr/bin/env + + go1xalpine: + container: + name: mirror.gcr.io/golang:1.25-alpine + envfile: + exclude: + - GO + - CXX + - CGO pipelines: unit:test: - - pipeline: test:unit + - pipeline: test:unit env: ROOT_PKG_NAME: github.com/DevLabFoundry @@ -32,6 +46,11 @@ pipelines: - task: go:build:binary depends_on: clean + proto:build: + - task: proto:install + - task: proto:generate + depends_on: proto:install + tasks: show:coverage: description: Opens the current coverage viewer for the the configmanager utility. @@ -93,7 +112,7 @@ tasks: sonar:coverage:prep: context: bash command: - - | + - | sed -i 's|github.com/DevLabFoundry/configmanager/v2/||g' .coverage/out echo "Coverage file first 20 lines after conversion:" head -20 .coverage/out @@ -112,4 +131,15 @@ tasks: - VERSION - REVISION + # currently unused + proto:install: + context: go1xalpine + command: + - GOPATH=$PWD/local/go go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.31.0 + proto:generate: + context: buf + command: + # - PATH=$PATH:$PWD/local/go/bin buf generate + # getting all plugins from the remote registry + - buf generate diff --git a/go.mod b/go.mod index a8074af..4049a5c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/DevLabFoundry/configmanager/v3 go 1.25.4 require ( - cloud.google.com/go/secretmanager v1.16.0 + cloud.google.com/go/secretmanager v1.11.4 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0 @@ -14,7 +14,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1 github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3 github.com/go-test/deep v1.1.1 - github.com/googleapis/gax-go/v2 v2.15.0 + github.com/googleapis/gax-go/v2 v2.12.0 github.com/hashicorp/vault/api v1.22.0 github.com/hashicorp/vault/api/auth/aws v0.11.0 github.com/spf13/cobra v1.10.1 @@ -24,16 +24,16 @@ require ( require ( github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect - github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/oklog/run v1.1.0 // indirect + go.opencensus.io v0.24.0 // indirect ) require ( - cloud.google.com/go/auth v0.17.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect - cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/iam v1.1.5 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect @@ -57,14 +57,14 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect - github.com/google/s2a-go v0.1.9 // indirect + github.com/google/s2a-go v0.1.7 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.7.0 + github.com/hashicorp/go-plugin v1.6.2 github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect @@ -86,7 +86,7 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/tetratelabs/wazero v1.10.1 go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect @@ -98,10 +98,10 @@ require ( golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.14.0 // indirect - google.golang.org/api v0.256.0 // indirect - google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/grpc v1.77.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/api v0.155.0 // indirect + google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/grpc v1.61.2 + google.golang.org/protobuf v1.31.0 ) diff --git a/go.sum b/go.sum index f7638e0..3008268 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,20 @@ -cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= -cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= -cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= -cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= -cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= -cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/compute v1.49.1 h1:KYKIG0+pfpAWaAYayFkE/KPrAVCge0Hu82bPraAmsCk= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= -cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= -cloud.google.com/go/secretmanager v1.16.0 h1:19QT7ZsLJ8FSP1k+4esQvuCD7npMJml6hYzilxVyT+k= -cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= +cloud.google.com/go/secretmanager v1.11.4 h1:krnX9qpG2kR2fJ+u+uNyNo+ACVhplIAS4Pu7u+4gd+k= +cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0 h1:uU4FujKFQAz31AbWOO3INV9qfIanHeIUSsGhRlcJJmg= github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0/go.mod h1:qr3M3Oy6V98VR0c5tCHKUpaeJTRQh6KYzJewRtFWqfc= -github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.0 h1:mXlQ+2C8A4KpXTIIYYxgFYqSivjGTBQidq/b0xxZLuk= -github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.0/go.mod h1:K//Ck7MUa+r9jpV69WLeWnnju5WJx5120AFsEzvumII= github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1 h1:j0hhYS006eJ54vusoap0f2NVZ1YY3QnaAEnLM68f0SQ= github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1/go.mod h1:AdtInaXmK8eYmbjezRWgLz+Qs46nc9Up9GWGwteWNfw= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= @@ -37,84 +27,64 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY= github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU= github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= -github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= -github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc= github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= -github.com/aws/aws-sdk-go-v2/config v1.31.17 h1:QFl8lL6RgakNK86vusim14P2k8BFSxjvUkcWLDjgz9Y= -github.com/aws/aws-sdk-go-v2/config v1.31.17/go.mod h1:V8P7ILjp/Uef/aX8TjGk6OHZN6IKPM5YW6S78QnRD5c= github.com/aws/aws-sdk-go-v2/config v1.32.0 h1:T5WWJYnam9SzBLbsVYDu2HscLDe+GU1AUJtfcDAc/vA= github.com/aws/aws-sdk-go-v2/config v1.32.0/go.mod h1:pSRm/+D3TxBixGMXlgtX4+MPO9VNtEEtiFmNpxksoxw= -github.com/aws/aws-sdk-go-v2/credentials v1.18.21 h1:56HGpsgnmD+2/KpG0ikvvR8+3v3COCwaF4r+oWwOeNA= -github.com/aws/aws-sdk-go-v2/credentials v1.18.21/go.mod h1:3YELwedmQbw7cXNaII2Wywd+YY58AmLPwX4LzARgmmA= github.com/aws/aws-sdk-go-v2/credentials v1.19.0 h1:7zm+ez+qEqLaNsCSRaistkvJRJv8sByDOVuCnyHbP7M= github.com/aws/aws-sdk-go-v2/credentials v1.19.0/go.mod h1:pHKPblrT7hqFGkNLxqoS3FlGoPrQg4hMIa+4asZzBfs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.11 h1:DouhxUREBjfnNJFp1yNn/p1Gk5pzr1YNixcIOIudI2g= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.39.11/go.mod h1:QgVIY03/XoQs2iFr0MbQuQ/Tf1RwlkOvuySWMh1wph4= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1 h1:w6a0H79HrHf3lr+zrw+pSzR5B+caiQFAKiNHlrUcnoc= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1/go.mod h1:c6Vg0BRiU7v0MVhHupw90RyL120QBwAMLbDCzptGeMk= github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 h1:BDgIUYGEo5TkayOWv/oBLPphWwNm/A91AebUjAu5L5g= github.com/aws/aws-sdk-go-v2/service/signin v1.0.1/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk= -github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4 h1:UmkF0ipNy0Ps6csJl/ZRJ3K+DWe9q0A7LT3xfxoHbgg= -github.com/aws/aws-sdk-go-v2/service/ssm v1.66.4/go.mod h1:uNHuYAQazkHqpD+hVomA2+eDSuKJzerno7Fnha6N6/Y= github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3 h1:ofiQvKwka2E3T8FXBsU1iWj7Yvk2wd1p4ZCdS6qGiKQ= github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3/go.mod h1:+nlWvcgDPQ56mChEBzTC0puAMck+4onOFaHg5cE+Lgg= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.1 h1:0JPwLz1J+5lEOfy/g0SURC9cxhbQ1lIMHMa+AHZSzz0= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.1/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 h1:U//SlnkE1wOQiIImxzdY5PXat4Wq+8rlfVEw4Y7J8as= github.com/aws/aws-sdk-go-v2/service/sso v1.30.4/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5 h1:OWs0/j2UYR5LOGi88sD5/lhN6TDLG6SfA7CqsQO9zF0= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.5/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 h1:MvlNs/f+9eM0mOjD9JzBUbf5jghyTk3p+O9yHMXX94Y= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.1 h1:mLlUgHn02ue8whiR4BmxxGJLR2gwU6s6ZzJ5wDamBUs= -github.com/aws/aws-sdk-go-v2/service/sts v1.39.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0i9NT+JnqLTOI5nA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls= -github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 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/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= -github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A= -github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= -github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= -github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= -github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= @@ -132,18 +102,41 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= -github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -154,8 +147,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= -github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= +github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= @@ -180,6 +173,8 @@ github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8 github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -215,10 +210,9 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= @@ -239,15 +233,18 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8= github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= @@ -261,66 +258,88 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689Cbtr go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= -golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.255.0 h1:OaF+IbRwOottVCYV2wZan7KUq7UeNUQn1BcPc4K7lE4= -google.golang.org/api v0.255.0/go.mod h1:d1/EtvCLdtiWEV4rAEHDHGh2bCnqsWhw+M8y2ECN4a8= -google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= -google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= -google.golang.org/genproto v0.0.0-20251103181224-f26f9409b101 h1:MgBTzgUJFAmp2PlyqKJecSpZpjFxkYL3nDUIeH/6Q30= -google.golang.org/genproto v0.0.0-20251103181224-f26f9409b101/go.mod h1:bbWg36d7wp3knc0hIlmJAnW5R/CQ2rzpEVb72eH4ex4= -google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba h1:Ze6qXW0j37YCqZdCD2LkzVSxgEWez0cO4NUyd44DiDY= -google.golang.org/genproto v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:4FLPzLA8eGAktPOTemJGDgDYRpLYwrNu4u2JtWINhnI= -google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM= -google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg= -google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= -google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= -google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= +google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 h1:EWIeHfGuUf00zrVZGEgYFxok7plSAXBGcH7NNdMAWvA= +google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.61.2 h1:TzJay21lXCf7BiNFKl7mSskt5DlkKAumAYTs52SpJeo= +google.golang.org/grpc v1.61.2/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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= @@ -332,3 +351,5 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/plugins/grpc.go b/plugins/grpc.go new file mode 100644 index 0000000..9d7ffc5 --- /dev/null +++ b/plugins/grpc.go @@ -0,0 +1,35 @@ +package plugins + +import ( + "context" + + "github.com/DevLabFoundry/configmanager/v3/plugins/proto" +) + +// GRPCClient is an implementation of KV that talks over RPC. +type GRPCClient struct{ client proto.TokenStoreClient } + +func (m *GRPCClient) Get(key string, metadata []byte) (string, error) { + resp, err := m.client.Value(context.Background(), &proto.TokenValueRequest{ + Token: key, + Metadata: metadata, + }) + if err != nil { + return "", err + } + + return resp.Value, nil +} + +// Here is the gRPC server that GRPCClient talks to. +type GRPCServer struct { + // This is the real implementation + Impl TokenStore +} + +func (m *GRPCServer) Get( + ctx context.Context, + req *proto.TokenValueRequest) (*proto.TokenValueResponse, error) { + v, err := m.Impl.Get(req.) + return &proto.GetResponse{Value: v}, err +} diff --git a/plugins/interface.go b/plugins/interface.go new file mode 100644 index 0000000..ee33294 --- /dev/null +++ b/plugins/interface.go @@ -0,0 +1,54 @@ +package plugins + +import ( + "context" + + "google.golang.org/grpc" + + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/go-plugin/examples/grpc/proto" +) + +// Handshake is a common handshake that is shared by plugin and host. +var Handshake = plugin.HandshakeConfig{ + // This isn't required when using VersionedPlugins + ProtocolVersion: 1, + MagicCookieKey: "BASIC_PLUGIN", + MagicCookieValue: "hello", +} + +// PluginMap is the map of plugins we can dispense. +var PluginMap = map[string]plugin.Plugin{ + "kv_grpc": &TokenStoreGRPCPlugin{}, + // "kv": &KVPlugin{}, +} + +// TokenStore is the interface that we're exposing as a plugin. +type TokenStore interface { + Get(token string, metadata []byte) (string, error) +} + +// This is the implementation of plugin.Plugin so we can serve/consume this. +type TokenStorePlugin struct { + // Concrete implementation, written in Go. This is only used for plugins + // that are written in Go. + Impl TokenStore +} + +// This is the implementation of plugin.GRPCPlugin so we can serve/consume this. +type TokenStoreGRPCPlugin struct { + // GRPCPlugin must still implement the Plugin interface + plugin.Plugin + // Concrete implementation, written in Go. This is only used for plugins + // that are written in Go. + Impl TokenStore +} + +func (p *TokenStoreGRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + proto.RegisterKVServer(s, &GRPCServer{Impl: p.Impl}) + return nil +} + +func (p *TokenStoreGRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &GRPCClient{client: proto(c)}, nil +} diff --git a/plugins/proto/token_store.pb.go b/plugins/proto/token_store.pb.go new file mode 100644 index 0000000..aa4eff0 --- /dev/null +++ b/plugins/proto/token_store.pb.go @@ -0,0 +1,183 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc (unknown) +// source: token_store.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TokenValueRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + Metadata []byte `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TokenValueRequest) Reset() { + *x = TokenValueRequest{} + mi := &file_token_store_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TokenValueRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenValueRequest) ProtoMessage() {} + +func (x *TokenValueRequest) ProtoReflect() protoreflect.Message { + mi := &file_token_store_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenValueRequest.ProtoReflect.Descriptor instead. +func (*TokenValueRequest) Descriptor() ([]byte, []int) { + return file_token_store_proto_rawDescGZIP(), []int{0} +} + +func (x *TokenValueRequest) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *TokenValueRequest) GetMetadata() []byte { + if x != nil { + return x.Metadata + } + return nil +} + +type TokenValueResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TokenValueResponse) Reset() { + *x = TokenValueResponse{} + mi := &file_token_store_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TokenValueResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenValueResponse) ProtoMessage() {} + +func (x *TokenValueResponse) ProtoReflect() protoreflect.Message { + mi := &file_token_store_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenValueResponse.ProtoReflect.Descriptor instead. +func (*TokenValueResponse) Descriptor() ([]byte, []int) { + return file_token_store_proto_rawDescGZIP(), []int{1} +} + +func (x *TokenValueResponse) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +var File_token_store_proto protoreflect.FileDescriptor + +const file_token_store_proto_rawDesc = "" + + "\n" + + "\x11token_store.proto\x12\x05proto\"E\n" + + "\x11TokenValueRequest\x12\x14\n" + + "\x05token\x18\x01 \x01(\tR\x05token\x12\x1a\n" + + "\bmetadata\x18\x02 \x01(\fR\bmetadata\"*\n" + + "\x12TokenValueResponse\x12\x14\n" + + "\x05value\x18\x01 \x01(\tR\x05value2J\n" + + "\n" + + "TokenStore\x12<\n" + + "\x05Value\x12\x18.proto.TokenValueRequest\x1a\x19.proto.TokenValueResponseB\tZ\a./protob\x06proto3" + +var ( + file_token_store_proto_rawDescOnce sync.Once + file_token_store_proto_rawDescData []byte +) + +func file_token_store_proto_rawDescGZIP() []byte { + file_token_store_proto_rawDescOnce.Do(func() { + file_token_store_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_token_store_proto_rawDesc), len(file_token_store_proto_rawDesc))) + }) + return file_token_store_proto_rawDescData +} + +var file_token_store_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_token_store_proto_goTypes = []any{ + (*TokenValueRequest)(nil), // 0: proto.TokenValueRequest + (*TokenValueResponse)(nil), // 1: proto.TokenValueResponse +} +var file_token_store_proto_depIdxs = []int32{ + 0, // 0: proto.TokenStore.Value:input_type -> proto.TokenValueRequest + 1, // 1: proto.TokenStore.Value:output_type -> proto.TokenValueResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_token_store_proto_init() } +func file_token_store_proto_init() { + if File_token_store_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_token_store_proto_rawDesc), len(file_token_store_proto_rawDesc)), + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_token_store_proto_goTypes, + DependencyIndexes: file_token_store_proto_depIdxs, + MessageInfos: file_token_store_proto_msgTypes, + }.Build() + File_token_store_proto = out.File + file_token_store_proto_goTypes = nil + file_token_store_proto_depIdxs = nil +} diff --git a/plugins/proto/token_store.proto b/plugins/proto/token_store.proto new file mode 100644 index 0000000..a22eb02 --- /dev/null +++ b/plugins/proto/token_store.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package proto; +option go_package = "./proto"; + +message TokenValueRequest { + string token = 1; + bytes metadata = 2; +} + +message TokenValueResponse { + string value = 1; +} + +service TokenStore { + rpc Value(TokenValueRequest) returns (TokenValueResponse); +} diff --git a/plugins/proto/token_store_grpc.pb.go b/plugins/proto/token_store_grpc.pb.go new file mode 100644 index 0000000..6ea7faf --- /dev/null +++ b/plugins/proto/token_store_grpc.pb.go @@ -0,0 +1,107 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: token_store.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + TokenStore_Value_FullMethodName = "/proto.TokenStore/Value" +) + +// TokenStoreClient is the client API for TokenStore service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type TokenStoreClient interface { + Value(ctx context.Context, in *TokenValueRequest, opts ...grpc.CallOption) (*TokenValueResponse, error) +} + +type tokenStoreClient struct { + cc grpc.ClientConnInterface +} + +func NewTokenStoreClient(cc grpc.ClientConnInterface) TokenStoreClient { + return &tokenStoreClient{cc} +} + +func (c *tokenStoreClient) Value(ctx context.Context, in *TokenValueRequest, opts ...grpc.CallOption) (*TokenValueResponse, error) { + out := new(TokenValueResponse) + err := c.cc.Invoke(ctx, TokenStore_Value_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TokenStoreServer is the server API for TokenStore service. +// All implementations should embed UnimplementedTokenStoreServer +// for forward compatibility +type TokenStoreServer interface { + Value(context.Context, *TokenValueRequest) (*TokenValueResponse, error) +} + +// UnimplementedTokenStoreServer should be embedded to have forward compatible implementations. +type UnimplementedTokenStoreServer struct { +} + +func (UnimplementedTokenStoreServer) Value(context.Context, *TokenValueRequest) (*TokenValueResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Value not implemented") +} + +// UnsafeTokenStoreServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TokenStoreServer will +// result in compilation errors. +type UnsafeTokenStoreServer interface { + mustEmbedUnimplementedTokenStoreServer() +} + +func RegisterTokenStoreServer(s grpc.ServiceRegistrar, srv TokenStoreServer) { + s.RegisterService(&TokenStore_ServiceDesc, srv) +} + +func _TokenStore_Value_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TokenValueRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TokenStoreServer).Value(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TokenStore_Value_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TokenStoreServer).Value(ctx, req.(*TokenValueRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// TokenStore_ServiceDesc is the grpc.ServiceDesc for TokenStore service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TokenStore_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.TokenStore", + HandlerType: (*TokenStoreServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Value", + Handler: _TokenStore_Value_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "token_store.proto", +} From b1f304641297318a1e046b892135fa68a28971ed Mon Sep 17 00:00:00 2001 From: dnitsch Date: Sun, 30 Nov 2025 10:42:37 +0000 Subject: [PATCH 05/15] fix: plugin architecture working NOTE: needs a lot more polishing --- internal/config/config.go | 50 +- internal/parser/parser_test.go | 179 +++-- internal/plugin/plugin.go | 76 --- internal/plugin/provider.go | 62 -- internal/plugin/tester/main.go | 46 -- internal/plugin/wasip1.go | 263 -------- internal/plugin/wasip1_test.go | 47 -- internal/store/azappconf.go | 112 ---- internal/store/azappconf_test.go | 208 ------ internal/store/azhelpers.go | 37 -- internal/store/azkeyvault.go | 94 --- internal/store/azkeyvault_test.go | 218 ------ internal/store/aztablestorage.go | 126 ---- internal/store/aztablestorage_test.go | 354 ---------- internal/store/gcpsecrets.go | 91 --- internal/store/gcpsecrets_test.go | 184 ------ internal/store/hashivault.go | 172 ----- internal/store/hashivault_test.go | 624 ------------------ internal/store/paramstore_test.go | 152 ----- internal/store/plugin.go | 64 ++ internal/store/plugin_test.go | 34 + internal/store/secretsmanager.go | 93 --- internal/store/secretsmanager_test.go | 154 ----- internal/store/store.go | 2 +- internal/strategy/strategy.go | 42 +- internal/strategy/strategy_test.go | 339 +++++----- plugins/awsparams/paramstore.go | 67 -- .../main.go => awsparamstr/README.md} | 17 +- .../awsparamstr/impl}/paramstore.go | 23 +- .../impl}/paramstore_test.go | 10 +- plugins/awsparamstr/main.go | 34 + plugins/awssecrets/main.go | 197 ------ plugins/grpc.go | 8 +- plugins/interface.go | 22 +- plugins/scaffolding.go | 6 - 35 files changed, 463 insertions(+), 3744 deletions(-) delete mode 100644 internal/plugin/plugin.go delete mode 100644 internal/plugin/provider.go delete mode 100644 internal/plugin/tester/main.go delete mode 100644 internal/plugin/wasip1.go delete mode 100644 internal/plugin/wasip1_test.go delete mode 100644 internal/store/azappconf.go delete mode 100644 internal/store/azappconf_test.go delete mode 100644 internal/store/azhelpers.go delete mode 100644 internal/store/azkeyvault.go delete mode 100644 internal/store/azkeyvault_test.go delete mode 100644 internal/store/aztablestorage.go delete mode 100644 internal/store/aztablestorage_test.go delete mode 100644 internal/store/gcpsecrets.go delete mode 100644 internal/store/gcpsecrets_test.go delete mode 100644 internal/store/hashivault.go delete mode 100644 internal/store/hashivault_test.go delete mode 100644 internal/store/paramstore_test.go create mode 100644 internal/store/plugin.go create mode 100644 internal/store/plugin_test.go delete mode 100644 internal/store/secretsmanager.go delete mode 100644 internal/store/secretsmanager_test.go delete mode 100644 plugins/awsparams/paramstore.go rename plugins/{awsparams/main.go => awsparamstr/README.md} (91%) rename {internal/store => plugins/awsparamstr/impl}/paramstore.go (69%) rename plugins/{awsparams => awsparamstr/impl}/paramstore_test.go (95%) create mode 100644 plugins/awsparamstr/main.go delete mode 100644 plugins/awssecrets/main.go diff --git a/internal/config/config.go b/internal/config/config.go index a1898fc..f7b3713 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,8 +5,6 @@ import ( "errors" "fmt" "strings" - - "github.com/DevLabFoundry/configmanager/v3/plugins" ) const ( @@ -175,7 +173,7 @@ func (ptc *ParsedTokenConfig) WithSanitizedToken(v string) { func (t *ParsedTokenConfig) ParseMetadata(metadataTyp any) error { // empty map will be parsed as `{}` still resulting in a valid json // and successful unmarshalling but default value pointer struct - if err := json.Unmarshal(fmt.Appendf(nil, t.parseMetadata()), metadataTyp); err != nil { + if err := json.Unmarshal(fmt.Appendf(nil, "%s", t.parseMetadata()), metadataTyp); err != nil { // It would very hard to test this since // we are forcing the key and value to be strings // return non-filled pointer @@ -184,21 +182,6 @@ func (t *ParsedTokenConfig) ParseMetadata(metadataTyp any) error { return nil } -func (t *ParsedTokenConfig) parseMetadata() string { - // crude json like builder from key/val tags - // since we are only ever dealing with a string input - // extracted from the token there is little chance panic would occur here - // WATCH THIS SPACE "¯\_(ツ)_/¯" - metaMap := []string{} - for keyVal := range strings.SplitSeq(t.metadataStr, ",") { - mapKeyVal := strings.Split(keyVal, "=") - if len(mapKeyVal) == 2 { - metaMap = append(metaMap, fmt.Sprintf(`"%s":"%s"`, mapKeyVal[0], mapKeyVal[1])) - } - } - return fmt.Sprintf(`{%s}`, strings.Join(metaMap, ",")) -} - // StoreToken returns the sanitized token without: // - metadata // - keySeparator @@ -249,24 +232,17 @@ func (t *ParsedTokenConfig) TokenSeparator() string { return t.tokenSeparator } -func (t *ParsedTokenConfig) JSONMessagExchange() (*plugins.MessagExchange, error) { - md := map[string]any{} - if err := json.Unmarshal([]byte(t.parseMetadata()), &md); err != nil { - return nil, err - } - - jme := &plugins.MessagExchange{ - Token: t.StoreToken(), - Metadata: md, - } - - return jme, nil -} - -func (t *ParsedTokenConfig) JSONMessagExchangeBytes() ([]byte, error) { - j, err := t.JSONMessagExchange() - if err != nil { - return nil, err +func (t *ParsedTokenConfig) parseMetadata() string { + // crude json like builder from key/val tags + // since we are only ever dealing with a string input + // extracted from the token there is little chance panic would occur here + // WATCH THIS SPACE "¯\_(ツ)_/¯" + metaMap := []string{} + for keyVal := range strings.SplitSeq(t.metadataStr, ",") { + mapKeyVal := strings.Split(keyVal, "=") + if len(mapKeyVal) == 2 { + metaMap = append(metaMap, fmt.Sprintf(`"%s":"%s"`, mapKeyVal[0], mapKeyVal[1])) + } } - return json.Marshal(j) + return fmt.Sprintf(`{%s}`, strings.Join(metaMap, ",")) } diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go index 2a97c04..9b93cf5 100644 --- a/internal/parser/parser_test.go +++ b/internal/parser/parser_test.go @@ -9,7 +9,6 @@ import ( "github.com/DevLabFoundry/configmanager/v3/internal/lexer" "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/DevLabFoundry/configmanager/v3/internal/parser" - "github.com/DevLabFoundry/configmanager/v3/internal/store" ) var lexerSource = lexer.Source{FileName: "bar", FullPath: "/foo/bar"} @@ -188,100 +187,100 @@ func Test_Parse_should_pass_with_metadata_end_tag(t *testing.T) { } } -func Test_Parse_ParseMetadata(t *testing.T) { +// func Test_Parse_ParseMetadata(t *testing.T) { - ttests := map[string]struct { - input string - typ *store.SecretsMgrConfig - }{ - "without keysPath": { - `AWSSECRETS:///foo[version=1.2.3]`, - &store.SecretsMgrConfig{}, - }, - "with keysPath": { - `AWSSECRETS:///foo|path.one[version=1.2.3]`, - &store.SecretsMgrConfig{}, - }, - "nestled in text": { - `someQ=AWSPARAMSTR:///path/queryparam|p1[version=1.2.3]&anotherQ`, - &store.SecretsMgrConfig{}, - }, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - lexerSource.Input = tt.input - cfg := config.NewConfig() - l := lexer.New(lexerSource, *cfg) - p := parser.New(l, cfg).WithLogger(log.New(os.Stderr)) - parsed, errs := p.Parse() - if len(errs) > 0 { - t.Fatalf("%v", errs) - } +// ttests := map[string]struct { +// input string +// typ *store.SecretsMgrConfig +// }{ +// "without keysPath": { +// `AWSSECRETS:///foo[version=1.2.3]`, +// &store.SecretsMgrConfig{}, +// }, +// "with keysPath": { +// `AWSSECRETS:///foo|path.one[version=1.2.3]`, +// &store.SecretsMgrConfig{}, +// }, +// "nestled in text": { +// `someQ=AWSPARAMSTR:///path/queryparam|p1[version=1.2.3]&anotherQ`, +// &store.SecretsMgrConfig{}, +// }, +// } +// for name, tt := range ttests { +// t.Run(name, func(t *testing.T) { +// lexerSource.Input = tt.input +// cfg := config.NewConfig() +// l := lexer.New(lexerSource, *cfg) +// p := parser.New(l, cfg).WithLogger(log.New(os.Stderr)) +// parsed, errs := p.Parse() +// if len(errs) > 0 { +// t.Fatalf("%v", errs) +// } - for _, p := range parsed { - if err := p.ParsedToken.ParseMetadata(tt.typ); err != nil { - t.Fatal(err) - } - if tt.typ.Version != "1.2.3" { - t.Errorf("got %v wanted 1.2.3", tt.typ.Version) - } - } - }) - } -} +// for _, p := range parsed { +// if err := p.ParsedToken.ParseMetadata(tt.typ); err != nil { +// t.Fatal(err) +// } +// if tt.typ.Version != "1.2.3" { +// t.Errorf("got %v wanted 1.2.3", tt.typ.Version) +// } +// } +// }) +// } +// } -func Test_Parse_Path_Keys_WithParsedMetadat(t *testing.T) { +// func Test_Parse_Path_Keys_WithParsedMetadat(t *testing.T) { - ttests := map[string]struct { - input string - typ *store.SecretsMgrConfig - wantSanitizedPath string - wantKeyPath string - }{ - "without keysPath": { - `AWSSECRETS:///foo[version=1.2.3]`, - &store.SecretsMgrConfig{}, - "/foo", "", - }, - "with keysPath": { - `AWSSECRETS:///foo|path.one[version=1.2.3]`, - &store.SecretsMgrConfig{}, - "/foo", "path.one", - }, - "nestled in text": { - `someQ=AWSPARAMSTR:///path/queryparam|p1[version=1.2.3]&anotherQ`, - &store.SecretsMgrConfig{}, - "/path/queryparam", "p1", - }, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - lexerSource.Input = tt.input - cfg := config.NewConfig() - l := lexer.New(lexerSource, *cfg) - p := parser.New(l, cfg).WithLogger(log.New(os.Stderr)) - parsed, errs := p.Parse() - if len(errs) > 0 { - t.Fatalf("%v", errs) - } +// ttests := map[string]struct { +// input string +// typ *store.SecretsMgrConfig +// wantSanitizedPath string +// wantKeyPath string +// }{ +// "without keysPath": { +// `AWSSECRETS:///foo[version=1.2.3]`, +// &store.SecretsMgrConfig{}, +// "/foo", "", +// }, +// "with keysPath": { +// `AWSSECRETS:///foo|path.one[version=1.2.3]`, +// &store.SecretsMgrConfig{}, +// "/foo", "path.one", +// }, +// "nestled in text": { +// `someQ=AWSPARAMSTR:///path/queryparam|p1[version=1.2.3]&anotherQ`, +// &store.SecretsMgrConfig{}, +// "/path/queryparam", "p1", +// }, +// } +// for name, tt := range ttests { +// t.Run(name, func(t *testing.T) { +// lexerSource.Input = tt.input +// cfg := config.NewConfig() +// l := lexer.New(lexerSource, *cfg) +// p := parser.New(l, cfg).WithLogger(log.New(os.Stderr)) +// parsed, errs := p.Parse() +// if len(errs) > 0 { +// t.Fatalf("%v", errs) +// } - for _, p := range parsed { - if p.ParsedToken.StoreToken() != tt.wantSanitizedPath { - t.Errorf("got %s want %s", p.ParsedToken.StoreToken(), tt.wantSanitizedPath) - } - if p.ParsedToken.LookupKeys() != tt.wantKeyPath { - t.Errorf("got %s want %s", p.ParsedToken.LookupKeys(), tt.wantKeyPath) - } - if err := p.ParsedToken.ParseMetadata(tt.typ); err != nil { - t.Fatal(err) - } - if tt.typ.Version != "1.2.3" { - t.Errorf("got %v wanted 1.2.3", tt.typ.Version) - } - } - }) - } -} +// for _, p := range parsed { +// if p.ParsedToken.StoreToken() != tt.wantSanitizedPath { +// t.Errorf("got %s want %s", p.ParsedToken.StoreToken(), tt.wantSanitizedPath) +// } +// if p.ParsedToken.LookupKeys() != tt.wantKeyPath { +// t.Errorf("got %s want %s", p.ParsedToken.LookupKeys(), tt.wantKeyPath) +// } +// if err := p.ParsedToken.ParseMetadata(tt.typ); err != nil { +// t.Fatal(err) +// } +// if tt.typ.Version != "1.2.3" { +// t.Errorf("got %v wanted 1.2.3", tt.typ.Version) +// } +// } +// }) +// } +// } func testHelperGenDocBlock(t *testing.T, stmtBlock parser.ConfigManagerTokenBlock, tokenType config.ImplementationPrefix, tokenValue, keysLookupPath string) bool { t.Helper() diff --git a/internal/plugin/plugin.go b/internal/plugin/plugin.go deleted file mode 100644 index 24c2b48..0000000 --- a/internal/plugin/plugin.go +++ /dev/null @@ -1,76 +0,0 @@ -package plugin - -import ( - "net/rpc" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/hashicorp/go-plugin" -) - -// Plugin is responsible for managing plugins within configmanager -// -// It includes the following methods -// - fetch plugins from known sources -// - maintains a list of tokens answerable by a specified pluginEngine -type Plugin struct { - Implementations config.ImplementationPrefix - SourcePath string - Version string - fallbackPaths []string - engineInstance *Engine -} - -// ValueProvider is the interface that we're exposing as a plugin. -type ValueProvider interface { - Value(token string, metadata string) (string, error) -} - -// Here is an implementation that talks over RPC -type StorePluginRPC struct{ client *rpc.Client } - -func (g *StorePluginRPC) Greet() string { - var resp string - err := g.client.Call("Plugin.Greet", new(interface{}), &resp) - if err != nil { - // You usually want your interfaces to return errors. If they don't, - // there isn't much other choice here. - panic(err) - } - - return resp -} - -// Here is the RPC server that GreeterRPC talks to, conforming to -// the requirements of net/rpc -type GreeterRPCServer struct { - // This is the real implementation - Impl ValueProvider -} - -func (s *GreeterRPCServer) Greet(args interface{}, resp *string) error { - *resp = s.Impl.Value() - return nil -} - -// This is the implementation of plugin.Plugin so we can serve/consume this -// -// This has two methods: Server must return an RPC server for this plugin -// type. We construct a GreeterRPCServer for this. -// -// Client must return an implementation of our interface that communicates -// over an RPC client. We return GreeterRPC for this. -// -// Ignore MuxBroker. That is used to create more multiplexed streams on our -// plugin connection and is a more advanced use case. -type GreeterPlugin struct { - // Impl Injection - Impl ValueProvider -} - -func (p *GreeterPlugin) Server(*plugin.MuxBroker) (interface{}, error) { - return &GreeterRPCServer{Impl: p.Impl}, nil -} - -func (GreeterPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &StorePluginRPC{client: c}, nil -} diff --git a/internal/plugin/provider.go b/internal/plugin/provider.go deleted file mode 100644 index 9a9e236..0000000 --- a/internal/plugin/provider.go +++ /dev/null @@ -1,62 +0,0 @@ -package plugin - -import ( - "fmt" - "log" - "os" - "os/exec" - - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/go-plugin" - "github.com/hashicorp/go-plugin/examples/basic/shared" -) - -// handshakeConfigs are used to just do a basic handshake between -// a plugin and host. If the handshake fails, a user friendly error is shown. -// This prevents users from executing bad plugins or executing a plugin -// directory. It is a UX feature, not a security feature. -var handshakeConfig = plugin.HandshakeConfig{ - ProtocolVersion: 1, - MagicCookieKey: "BASIC_PLUGIN", - MagicCookieValue: "hello", -} - -// pluginMap is the map of plugins we can dispense. -var pluginMap = map[string]plugin.Plugin{ - "greeter": &shared.GreeterPlugin{}, -} - -func Init() { - // Create an hclog.Logger - logger := hclog.New(&hclog.LoggerOptions{ - Name: "plugin", - Output: os.Stdout, - Level: hclog.Debug, - }) - - // We're a host! Start by launching the plugin process. - client := plugin.NewClient(&plugin.ClientConfig{ - HandshakeConfig: handshakeConfig, - Plugins: pluginMap, - Cmd: exec.Command("./plugin/greeter"), - Logger: logger, - }) - defer client.Kill() - - // Connect via RPC - rpcClient, err := client.Client() - if err != nil { - log.Fatal(err) - } - - // Request the plugin - raw, err := rpcClient.Dispense("greeter") - if err != nil { - log.Fatal(err) - } - - // We should have a Greeter now! This feels like a normal interface - // implementation but is in fact over an RPC connection. - greeter := raw.(shared.Greeter) - fmt.Println(greeter.Greet()) -} diff --git a/internal/plugin/tester/main.go b/internal/plugin/tester/main.go deleted file mode 100644 index b17e481..0000000 --- a/internal/plugin/tester/main.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "os" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/plugin" -) - -func main() { - inputReader, err := os.Open("/Users/dusannitschneider/git/dnitsch/configmanager/plugins/awsparams/awsparams.wasm") - if err != nil { - log.Fatal(fmt.Errorf("open plugin.wasm: %w", err)) - } - - ctx := context.Background() - - // Load the compiled WASI plugin. - engine, err := plugin.NewEngine(ctx, inputReader) - if err != nil { - log.Fatal(err) - } - defer engine.Close(ctx) - - inst, err := engine.NewApiInstance(ctx) - if err != nil { - log.Fatal(err) - } - defer inst.Close(ctx) - - // os.Setenv("AWS_PROFILE", "anabode_terraform_dev") - // os.Setenv("AWS_REGION", "eu-west-1") - t1, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) - t1.WithSanitizedToken("/int-test/pocketbase/admin-pwd") - val, err := inst.TokenValue(ctx, t1) - if err != nil { - log.Fatal(err) - } - - fmt.Println(string(val)) - - os.Exit(0) -} diff --git a/internal/plugin/wasip1.go b/internal/plugin/wasip1.go deleted file mode 100644 index 268371f..0000000 --- a/internal/plugin/wasip1.go +++ /dev/null @@ -1,263 +0,0 @@ -// Package plugin -// provides reactor style module -// we can explore the plugin provided host module -package plugin - -import ( - "context" - "encoding/binary" - "errors" - "fmt" - "io" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/plugins" - "github.com/tetratelabs/wazero" - "github.com/tetratelabs/wazero/api" - "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" -) - -var ( - ErrMissingMethod = errors.New("missing method on the wasiLib instance") - ErrAllocMemForParam = errors.New("failed to allocate memory for property") - ErrAllocateOutPtrZeroLen = errors.New("allocate returned 0 for output pointer") - ErrMemoryReadFailed = errors.New("mem.Read(out) failed") - ErrEmptyToken = errors.New("token must not be empty") -) - -// ==================== -// Engine & ApiInstance -// ==================== - -type Engine struct { - r wazero.Runtime - compiledModule wazero.CompiledModule -} - -// NewEngine compiles the WASI module once for the lifetime of the program. -func NewEngine(ctx context.Context, ps io.ReadCloser) (*Engine, error) { - r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig()) - if _, err := wasi_snapshot_preview1.Instantiate(ctx, r); err != nil { - _ = r.Close(ctx) - return nil, fmt.Errorf("instantiate WASI: %w", err) - } - - defer ps.Close() - wasiLib, err := io.ReadAll(ps) - if err != nil { - _ = r.Close(ctx) - return nil, fmt.Errorf("read plugin: %w", err) - } - - cm, err := r.CompileModule(ctx, wasiLib) - if err != nil { - _ = r.Close(ctx) - return nil, fmt.Errorf("compile module: %w", err) - } - - return &Engine{ - r: r, - compiledModule: cm, - }, nil -} - -// Close shuts down the runtime. -func (e *Engine) Close(ctx context.Context) error { - return e.r.Close(ctx) -} - -type ApiInstance struct { - mod api.Module - mem api.Memory - // exported alloc helpers - allocate api.Function - deallocate api.Function - // exported business function - tokenValue api.Function - // scratch output buffers - outPtr uint32 - outCap uint32 - outLenPtr uint32 // 4-byte cell for required length -} - -// NewApiInstance instantiates a fresh module instance. -func (e *Engine) NewApiInstance(ctx context.Context) (*ApiInstance, error) { - mod, err := e.r.InstantiateModule(ctx, e.compiledModule, wazero.NewModuleConfig().WithStartFunctions("_initialize")) - if err != nil { - return nil, fmt.Errorf("instantiate module: %w", err) - } - - inst := &ApiInstance{ - mod: mod, - mem: mod.Memory(), - allocate: mod.ExportedFunction("allocate"), - deallocate: mod.ExportedFunction("deallocate"), - tokenValue: mod.ExportedFunction("strategy_token_value"), - } - - for name, exported := range map[string]api.Function{ - "allocate": inst.allocate, - "deallocate": inst.deallocate, - "strategy_token_value": inst.tokenValue, - } { - if exported == nil { - return nil, fmt.Errorf("%w, method %q not found on exports", ErrMissingMethod, name) - } - } - - return inst, nil -} - -// Close instance (optional). -func (i *ApiInstance) Close(ctx context.Context) { - i.freeScratch(ctx) - _ = i.mod.Close(ctx) -} - -// put allocates module memory and writes bytes into it. -// returns (ptr, size). caller must deallocate(ptr, size). -func (i *ApiInstance) put(ctx context.Context, b []byte) (uint32, uint32, error) { - if len(b) == 0 { - return 0, 0, ErrEmptyToken - } - - res, err := i.allocate.Call(ctx, uint64(len(b))) - if err != nil { - return 0, 0, fmt.Errorf("allocate: %w", err) - } - ptr := uint32(res[0]) - if ptr == 0 { - return 0, 0, fmt.Errorf("allocate returned 0: %w", ErrAllocMemForParam) - } - - if ok := i.mem.Write(ptr, b); !ok { - _, _ = i.deallocate.Call(ctx, uint64(ptr), uint64(len(b))) - return 0, 0, fmt.Errorf("mem.Write failed: %w", ErrAllocMemForParam) - } - - return ptr, uint32(len(b)), nil -} - -// ensureOut ensures the scratch output buffer has at least `need` bytes. -// allocates outLenPtr (4 bytes) once. -func (i *ApiInstance) ensureOut(ctx context.Context, need uint32) error { - // outLenPtr is a 4-byte cell for required length - if i.outLenPtr == 0 { - res, err := i.allocate.Call(ctx, 4) - if err != nil { - return fmt.Errorf("allocate outLenPtr: %w", err) - } - i.outLenPtr = uint32(res[0]) - if i.outLenPtr == 0 { - return ErrAllocateOutPtrZeroLen - } - } - - if need <= i.outCap { - return nil - } - - // grow if needed - free old and alloc new - if i.outPtr != 0 { - _, _ = i.deallocate.Call(ctx, uint64(i.outPtr), uint64(i.outCap)) - i.outPtr, i.outCap = 0, 0 - } - - res, err := i.allocate.Call(ctx, uint64(need)) - if err != nil { - return fmt.Errorf("allocate outPtr: %w", err) - } - i.outPtr, i.outCap = uint32(res[0]), need - if i.outPtr == 0 { - return ErrAllocateOutPtrZeroLen - } - return nil -} - -// freeScratch frees the reusable output buffers (call once per instance). -func (i *ApiInstance) freeScratch(ctx context.Context) { - if i.outPtr != 0 { - _, _ = i.deallocate.Call(ctx, uint64(i.outPtr), uint64(i.outCap)) - i.outPtr, i.outCap = 0, 0 - } - if i.outLenPtr != 0 { - _, _ = i.deallocate.Call(ctx, uint64(i.outLenPtr), 4) - i.outLenPtr = 0 - } -} - -// TokenValue is the nice host-side API: string in, []byte out. -func (i *ApiInstance) TokenValue(ctx context.Context, token *config.ParsedTokenConfig) ([]byte, error) { - if token.StoreToken() == "" { - return nil, ErrEmptyToken - } - tokenBytes, err := token.JSONMessagExchangeBytes() - tokenPtr, tokenLen, err := i.put(ctx, tokenBytes) - if err != nil { - return nil, fmt.Errorf("put input: %w", err) - } - defer i.deallocate.Call(ctx, uint64(tokenPtr), uint64(tokenLen)) - - // start with a smallish buffer; plugin will ask for more if needed - if err := i.ensureOut(ctx, 64); err != nil { - return nil, fmt.Errorf("ensureOut: %w", err) - } - - call := func() (int32, uint32, error) { - res, err := i.tokenValue.Call( - ctx, - uint64(tokenPtr), uint64(tokenLen), // sanitizedToken - uint64(i.outPtr), uint64(i.outCap), // outPtr, outCap - uint64(i.outLenPtr), // outLenPtr - ) - if err != nil { - return 0, 0, fmt.Errorf("call strategy_token_value: %w", err) - } - - lenBytes, ok := i.mem.Read(i.outLenPtr, 4) - if !ok { - return int32(res[0]), 0, ErrMemoryReadFailed - } - - need := binary.LittleEndian.Uint32(lenBytes) - return int32(res[0]), need, nil - } - - rc, need, err := call() - if err != nil { - return nil, err - } - - if rc == plugins.ERR_BUF_TOO_SMALL { - if err := i.ensureOut(ctx, need); err != nil { - return nil, fmt.Errorf("ensureOut resize: %w", err) - } - rc, need, err = call() - if err != nil { - return nil, err - } - } - - if rc != plugins.OK { - switch rc { - case plugins.ERR_INVALID_UTF8: - return nil, errors.New("token value: invalid UTF-8 in input") - case plugins.ERR_EMPTY_INPUT: - return nil, ErrEmptyToken - case plugins.ERR_BUF_TOO_SMALL: - return nil, fmt.Errorf("token value: buffer too small even after resize (need=%d)", need) - default: - return nil, fmt.Errorf("token value: unknown error code %d", rc) - } - } - - out, ok := i.mem.Read(i.outPtr, need) - if !ok { - return nil, ErrMemoryReadFailed - } - - // Detach from wasm memory. - result := make([]byte, need) - copy(result, out) - return result, nil -} diff --git a/internal/plugin/wasip1_test.go b/internal/plugin/wasip1_test.go deleted file mode 100644 index 5442eb1..0000000 --- a/internal/plugin/wasip1_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package plugin_test - -import ( - "context" - "fmt" - "os" - "testing" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/plugin" -) - -func Test_FullFlow(t *testing.T) { - inputReader, err := os.Open("/Users/dusannitschneider/git/dnitsch/configmanager/plugins/awsparams/awsparams.wasm") - if err != nil { - t.Fatal(fmt.Errorf("open plugin.wasm: %w", err)) - } - ctx := context.Background() - - // Load the compiled WASI plugin. - engine, err := plugin.NewEngine(ctx, inputReader) - if err != nil { - t.Fatal(err) - } - defer engine.Close(ctx) - - inst, err := engine.NewApiInstance(ctx) - if err != nil { - t.Fatal(err) - } - defer inst.Close(ctx) - - os.Setenv("AWS_PROFILE", "anabode_terraform_dev") - os.Setenv("AWS_REGION", "eu-west-1") - t1, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) - t1.WithSanitizedToken("/int-test/pocketbase/admin-pwd") - val, err := inst.TokenValue(ctx, t1) - if err != nil { - t.Fatal(err) - } - fmt.Printf("TokenValue(\"foo\") => %q\n", string(val)) - - // Zero-length test (should error) - t2, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) - _, err = inst.TokenValue(ctx, t2) - fmt.Printf("TokenValue(\"\") error: %v\n", err) -} diff --git a/internal/store/azappconf.go b/internal/store/azappconf.go deleted file mode 100644 index c35f538..0000000 --- a/internal/store/azappconf.go +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Azure App Config implementation -**/ -package store - -import ( - "context" - "fmt" - "time" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" -) - -// appConfApi -// uses this package https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig -type appConfApi interface { - GetSetting(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) -} - -type AzAppConf struct { - svc appConfApi - ctx context.Context - config *AzAppConfConfig - token *config.ParsedTokenConfig - strippedToken string - logger log.ILogger -} - -// AzAppConfConfig is the azure conf service specific config -// and it is parsed from the token metadata -type AzAppConfConfig struct { - Label string `json:"label"` - Etag *azcore.ETag `json:"etag"` - AcceptDateTime *time.Time `json:"acceptedDateTime"` -} - -// NewAzAppConf -func NewAzAppConf(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*AzAppConf, error) { - storeConf := &AzAppConfConfig{} - if err := token.ParseMetadata(storeConf); err != nil { - return nil, err - } - - backingStore := &AzAppConf{ - ctx: ctx, - config: storeConf, - token: token, - logger: logger, - } - srvInit := AzServiceFromToken(token.StoreToken(), "https://%s.azconfig.io", 1) - backingStore.strippedToken = srvInit.Token - - cred, err := azidentity.NewDefaultAzureCredential(nil) - if err != nil { - logger.Error("failed to get credentials: %v", err) - return nil, err - } - - c, err := azappconfig.NewClient(srvInit.ServiceUri, cred, nil) - if err != nil { - logger.Error("failed to init the client: %v", err) - return nil, fmt.Errorf("%v\n%w", err, ErrClientInitialization) - } - - backingStore.svc = c - return backingStore, nil - -} - -func (s *AzAppConf) WithSvc(svc appConfApi) { - s.svc = svc -} - -// setTokenVal sets the token -func (implmt *AzAppConf) SetToken(token *config.ParsedTokenConfig) {} - -// tokenVal in AZ App Config -// label can be specified -// From this point then normal rules of configmanager apply, -// including keySeperator and lookup. -func (imp *AzAppConf) Value() (string, error) { - imp.logger.Info("Concrete implementation AzAppConf") - imp.logger.Info("AzAppConf Token: %s", imp.token.String()) - - ctx, cancel := context.WithCancel(imp.ctx) - defer cancel() - opts := &azappconfig.GetSettingOptions{} - - // assign any metadatas from the token - if imp.config.Label != "" { - opts.Label = &imp.config.Label - } - - if imp.config.Etag != nil { - opts.OnlyIfChanged = imp.config.Etag - } - - s, err := imp.svc.GetSetting(ctx, imp.strippedToken, opts) - if err != nil { - imp.logger.Error(implementationNetworkErr, config.AzAppConfigPrefix, err, imp.strippedToken) - return "", fmt.Errorf("token: %s, error: %v. %w", imp.strippedToken, err, ErrRetrieveFailed) - } - if s.Value != nil { - return *s.Value, nil - } - imp.logger.Error("token: %v, %w", imp.token.String(), ErrEmptyResponse) - return "", nil -} diff --git a/internal/store/azappconf_test.go b/internal/store/azappconf_test.go deleted file mode 100644 index 17da526..0000000 --- a/internal/store/azappconf_test.go +++ /dev/null @@ -1,208 +0,0 @@ -package store_test - -import ( - "bytes" - "context" - "errors" - "fmt" - "testing" - - "github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - logger "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" -) - -func azAppConfCommonChecker(t *testing.T, key string, expectedKey string, expectLabel string, opts *azappconfig.GetSettingOptions) { - t.Helper() - if key != expectedKey { - t.Errorf(testutils.TestPhrase, key, expectedKey) - } - - if expectLabel != "" { - if opts == nil { - t.Errorf(testutils.TestPhrase, nil, expectLabel) - } - if *opts.Label != expectLabel { - t.Errorf(testutils.TestPhrase, opts.Label, expectLabel) - } - } -} - -type mockAzAppConfApi func(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) - -func (m mockAzAppConfApi) GetSetting(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) { - return m(ctx, key, options) -} - -func Test_AzAppConf_Success(t *testing.T) { - tsuccessParam := "somecvla" - - logr := logger.New(&bytes.Buffer{}) - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockAzAppConfApi - }{ - "successVal": { - func() *config.ParsedTokenConfig { - // "AZAPPCONF#/test-app-config-instance/table//token/1", - tkn, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-app-config-instance/table//token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - tsuccessParam, - func(t *testing.T) mockAzAppConfApi { - return mockAzAppConfApi(func(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) { - azAppConfCommonChecker(t, key, "table//token/1", "", options) - resp := azappconfig.GetSettingResponse{} - resp.Value = &tsuccessParam - return resp, nil - }) - }, - }, - "successVal with :// token Separator": { - func() *config.ParsedTokenConfig { - // "AZAPPCONF:///test-app-config-instance/conf_key[label=dev]", - tkn, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("://")) - tkn.WithSanitizedToken("/test-app-config-instance/conf_key") - tkn.WithKeyPath("") - tkn.WithMetadata("label=dev") - return tkn - }, - tsuccessParam, - func(t *testing.T) mockAzAppConfApi { - return mockAzAppConfApi(func(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) { - azAppConfCommonChecker(t, key, "conf_key", "dev", options) - resp := azappconfig.GetSettingResponse{} - resp.Value = &tsuccessParam - return resp, nil - }) - }, - }, - "successVal with :// token Separator and etag specified": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-app-config-instance/conf_key") - tkn.WithKeyPath("") - tkn.WithMetadata("label=dev,etag=sometifdsssdsfdi_string01209222") - return tkn - }, - tsuccessParam, - func(t *testing.T) mockAzAppConfApi { - return mockAzAppConfApi(func(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) { - azAppConfCommonChecker(t, key, "conf_key", "dev", options) - if !options.OnlyIfChanged.Equals("sometifdsssdsfdi_string01209222") { - t.Errorf(testutils.TestPhraseWithContext, "Etag not correctly set", options.OnlyIfChanged, "sometifdsssdsfdi_string01209222") - } - resp := azappconfig.GetSettingResponse{} - resp.Value = &tsuccessParam - return resp, nil - }) - }, - }, - "successVal with keyseparator but no val returned": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-app-config-instance/try_to_find") - tkn.WithKeyPath("key_separator.lookup") - tkn.WithMetadata("") - return tkn - }, - "", - func(t *testing.T) mockAzAppConfApi { - return mockAzAppConfApi(func(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) { - azAppConfCommonChecker(t, key, "try_to_find", "", options) - resp := azappconfig.GetSettingResponse{} - resp.Value = nil - return resp, nil - }) - }, - }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - impl, err := store.NewAzAppConf(context.TODO(), tt.token(), logr) - if err != nil { - t.Errorf("failed to init AZAPPCONF") - } - - impl.WithSvc(tt.mockClient(t)) - got, err := impl.Value() - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} - -func Test_AzAppConf_Error(t *testing.T) { - logr := logger.New(&bytes.Buffer{}) - - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect error - mockClient func(t *testing.T) mockAzAppConfApi - }{ - "errored on service method call": { - func() *config.ParsedTokenConfig { - // "AZAPPCONF#/test-app-config-instance/table/token/ok", - tkn, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-app-config-instance/table/token/ok") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - store.ErrRetrieveFailed, - func(t *testing.T) mockAzAppConfApi { - return mockAzAppConfApi(func(ctx context.Context, key string, options *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) { - t.Helper() - resp := azappconfig.GetSettingResponse{} - return resp, fmt.Errorf("network error") - }) - }, - }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - impl, err := store.NewAzAppConf(context.TODO(), tt.token(), logr) - if err != nil { - t.Fatal("failed to init AZAPPCONF") - } - impl.WithSvc(tt.mockClient(t)) - if _, err := impl.Value(); !errors.Is(err, tt.expect) { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - }) - } -} - -func Test_fail_AzAppConf_Client_init(t *testing.T) { - - logr := logger.New(&bytes.Buffer{}) - - // this is basically a wrap around test for the url.Parse method in the stdlib - // as that is what the client uses under the hood - token, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig()) - token.WithSanitizedToken("/%25%65%6e%301-._~/") - } - if !errors.Is(err, store.ErrClientInitialization) { - t.Fatalf(testutils.TestPhraseWithContext, "azappconf client init", err.Error(), store.ErrClientInitialization.Error()) - } -} diff --git a/internal/store/azhelpers.go b/internal/store/azhelpers.go deleted file mode 100644 index 29e66e8..0000000 --- a/internal/store/azhelpers.go +++ /dev/null @@ -1,37 +0,0 @@ -package store - -import ( - "fmt" - "strings" -) - -/* -Generic Azure Service Init Helpers -*/ - -// AzServiceHelper returns a service URI and the stripped token -type AzServiceHelper struct { - ServiceUri string - Token string -} - -// AzServiceFromToken for azure the first part of the token __must__ always be the -// identifier of the service e.g. the account name for tableStore or the Vault name for KVSecret or -// AppConfig instance -// take parameter specifies the number of elements to take from the start only -// -// e.g. a value of 2 for take will take first 2 elements from the slices -// -// For AppConfig or KeyVault we ONLY need the AppConfig instance or KeyVault instance name -func AzServiceFromToken(token string, formatUri string, take int) AzServiceHelper { - // ensure preceding slash is trimmed - stringToken := strings.Split(strings.TrimPrefix(token, "/"), "/") - splitToken := []any{} - // recast []string slice to an []any - for _, st := range stringToken { - splitToken = append(splitToken, st) - } - - uri := fmt.Sprintf(formatUri, splitToken[0:take]...) - return AzServiceHelper{ServiceUri: uri, Token: strings.Join(stringToken[take:], "/")} -} diff --git a/internal/store/azkeyvault.go b/internal/store/azkeyvault.go deleted file mode 100644 index 781b066..0000000 --- a/internal/store/azkeyvault.go +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Azure KeyVault implementation -**/ -package store - -import ( - "context" - - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" -) - -type kvApi interface { - GetSecret(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) -} - -type KvScrtStore struct { - svc kvApi - ctx context.Context - logger log.ILogger - token *config.ParsedTokenConfig - config *AzKvConfig - strippedToken string -} - -// AzKvConfig takes any metadata from the token -// Version is the only -type AzKvConfig struct { - Version string `json:"version"` -} - -// NewKvScrtStore returns a KvScrtStore -// requires `AZURE_SUBSCRIPTION_ID` environment variable to be present to successfully work -func NewKvScrtStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*KvScrtStore, error) { - - storeConf := &AzKvConfig{} - _ = token.ParseMetadata(storeConf) - - backingStore := &KvScrtStore{ - ctx: ctx, - logger: logger, - config: storeConf, - token: token, - } - - srvInit := AzServiceFromToken(token.StoreToken(), "https://%s.vault.azure.net", 1) - backingStore.strippedToken = srvInit.Token - - cred, err := azidentity.NewDefaultAzureCredential(nil) - if err != nil { - logger.Error("failed to get credentials: %v", err) - return nil, err - } - - c, err := azsecrets.NewClient(srvInit.ServiceUri, cred, nil) - if err != nil { - logger.Error("%v\n%w", err, ErrClientInitialization) - return nil, err - } - - backingStore.svc = c - return backingStore, nil - -} - -func (s *KvScrtStore) WithSvc(svc kvApi) { - s.svc = svc -} - -// setToken already happens in AzureKVClient in the constructor -func (implmt *KvScrtStore) SetToken(token *config.ParsedTokenConfig) {} - -func (imp *KvScrtStore) Value() (string, error) { - imp.logger.Info("Concrete implementation AzKeyVault Secret") - imp.logger.Info("AzKeyVault Token: %s", imp.token.String()) - - ctx, cancel := context.WithCancel(imp.ctx) - defer cancel() - - // secretVersion as "" => latest - // imp.config.Version will default `""` if not specified - s, err := imp.svc.GetSecret(ctx, imp.strippedToken, imp.config.Version, nil) - if err != nil { - imp.logger.Error(implementationNetworkErr, imp.token.Prefix(), err, imp.token.String()) - return "", err - } - if s.Value != nil { - return *s.Value, nil - } - imp.logger.Error("value retrieved but empty for token: %v", imp.token) - return "", nil -} diff --git a/internal/store/azkeyvault_test.go b/internal/store/azkeyvault_test.go deleted file mode 100644 index 35b5c7d..0000000 --- a/internal/store/azkeyvault_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package store_test - -import ( - "context" - "fmt" - "io" - "strings" - "testing" - - "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" -) - -func Test_azSplitToken(t *testing.T) { - tests := []struct { - name string - token string - expect store.AzServiceHelper - }{ - { - name: "simple_with_preceding_slash", - token: "/test-vault/somejsontest", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-vault.vault.azure.net", - Token: "somejsontest", - }, - }, - { - name: "missing_initial_slash", - token: "test-vault/somejsontest", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-vault.vault.azure.net", - Token: "somejsontest", - }, - }, - { - name: "missing_initial_slash_multislash_secretname", - token: "test-vault/some/json/test", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-vault.vault.azure.net", - Token: "some/json/test", - }, - }, - { - name: "with_initial_slash_multislash_secretname", - token: "test-vault//some/json/test", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-vault.vault.azure.net", - Token: "/some/json/test", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := store.AzServiceFromToken(tt.token, "https://%s.vault.azure.net", 1) - if got.Token != tt.expect.Token { - t.Errorf(testutils.TestPhrase, tt.expect.Token, got.Token) - } - if got.ServiceUri != tt.expect.ServiceUri { - t.Errorf(testutils.TestPhrase, tt.expect.ServiceUri, got.ServiceUri) - } - }) - } -} - -func azKvCommonGetSecretChecker(t *testing.T, name, version, expectedName string) { - if name == "" { - t.Errorf("expect name to not be nil") - } - if name != expectedName { - t.Errorf(testutils.TestPhrase, name, expectedName) - } - - if strings.Contains(name, "#") { - t.Errorf("incorrectly stripped token separator") - } - - if strings.Contains(name, string(config.AzKeyVaultSecretsPrefix)) { - t.Errorf("incorrectly stripped prefix") - } - - if version != "" { - t.Fatal("expect version to be \"\" an empty string ") - } -} - -type mockAzKvSecretApi func(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) - -func (m mockAzKvSecretApi) GetSecret(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) { - return m(ctx, name, version, options) -} - -func TestAzKeyVault(t *testing.T) { - tsuccessParam := "dssdfdweiuyh" - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockAzKvSecretApi - }{ - "successVal": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-vault//token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - tsuccessParam, func(t *testing.T) mockAzKvSecretApi { - return mockAzKvSecretApi(func(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) { - t.Helper() - azKvCommonGetSecretChecker(t, name, "", "/token/1") - resp := azsecrets.GetSecretResponse{} - resp.Value = &tsuccessParam - return resp, nil - }) - }, - }, - "successVal with version": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-vault//token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("version:123") - return tkn - }, tsuccessParam, func(t *testing.T) mockAzKvSecretApi { - return mockAzKvSecretApi(func(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) { - t.Helper() - azKvCommonGetSecretChecker(t, name, "", "/token/1") - resp := azsecrets.GetSecretResponse{} - resp.Value = &tsuccessParam - return resp, nil - }) - }, - }, - "successVal with keyseparator": { - func() *config.ParsedTokenConfig { - // "AZKVSECRET#/test-vault/token/1|somekey" - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-vault/token/1") - tkn.WithKeyPath("somekey") - tkn.WithMetadata("") - return tkn - }, tsuccessParam, func(t *testing.T) mockAzKvSecretApi { - return mockAzKvSecretApi(func(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) { - t.Helper() - azKvCommonGetSecretChecker(t, name, "", "token/1") - - resp := azsecrets.GetSecretResponse{} - resp.Value = &tsuccessParam - return resp, nil - }) - }, - }, - "errored": { - func() *config.ParsedTokenConfig { - // "AZKVSECRET#/test-vault/token/1|somekey" - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-vault/token/1") - tkn.WithKeyPath("somekey") - tkn.WithMetadata("") - return tkn - }, - "unable to retrieve secret", - func(t *testing.T) mockAzKvSecretApi { - return mockAzKvSecretApi(func(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) { - t.Helper() - azKvCommonGetSecretChecker(t, name, "", "token/1") - - resp := azsecrets.GetSecretResponse{} - return resp, fmt.Errorf("unable to retrieve secret") - }) - }, - }, - "empty": { - func() *config.ParsedTokenConfig { - // "AZKVSECRET#/test-vault/token/1|somekey" - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-vault/token/1") - tkn.WithKeyPath("somekey") - tkn.WithMetadata("") - return tkn - }, "", func(t *testing.T) mockAzKvSecretApi { - return mockAzKvSecretApi(func(ctx context.Context, name string, version string, options *azsecrets.GetSecretOptions) (azsecrets.GetSecretResponse, error) { - t.Helper() - azKvCommonGetSecretChecker(t, name, "", "token/1") - - resp := azsecrets.GetSecretResponse{} - return resp, nil - }) - }, - }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - impl, err := store.NewKvScrtStore(context.TODO(), tt.token(), log.New(io.Discard)) - if err != nil { - t.Errorf("failed to init azkvstore") - } - - impl.WithSvc(tt.mockClient(t)) - got, err := impl.Value() - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} diff --git a/internal/store/aztablestorage.go b/internal/store/aztablestorage.go deleted file mode 100644 index eedef16..0000000 --- a/internal/store/aztablestorage.go +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Azure TableStore implementation -**/ -package store - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/data/aztables" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" -) - -var ErrIncorrectlyStructuredToken = errors.New("incorrectly structured token") - -// tableStoreApi -// uses this package https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/aztables -type tableStoreApi interface { - GetEntity(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) -} - -type AzTableStore struct { - svc tableStoreApi - ctx context.Context - logger log.ILogger - config *AzTableStrgConfig - token *config.ParsedTokenConfig - // token only without table indicators - // key only - strippedToken string -} - -type AzTableStrgConfig struct { - Format string `json:"format"` -} - -// NewAzTableStore -func NewAzTableStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*AzTableStore, error) { - - storeConf := &AzTableStrgConfig{} - _ = token.ParseMetadata(storeConf) - // initialToken := config.ParseMetadata(token, storeConf) - backingStore := &AzTableStore{ - ctx: ctx, - logger: logger, - config: storeConf, - token: token, - } - - srvInit := AzServiceFromToken(token.StoreToken(), "https://%s.table.core.windows.net/%s", 2) - backingStore.strippedToken = srvInit.Token - - cred, err := azidentity.NewDefaultAzureCredential(nil) - if err != nil { - logger.Error("failed to get credentials: %v", err) - return nil, err - } - - c, err := aztables.NewClient(srvInit.ServiceUri, cred, nil) - if err != nil { - logger.Error("failed to init the client: %v", err) - return nil, fmt.Errorf("%v\n%w", err, ErrClientInitialization) - } - - backingStore.svc = c - return backingStore, nil -} - -func (s *AzTableStore) WithSvc(svc tableStoreApi) { - s.svc = svc -} - -// setToken already happens in the constructor -func (implmt *AzTableStore) SetToken(token *config.ParsedTokenConfig) {} - -// tokenVal in AZ table storage if an Entity contains the `value` property -// we attempt to extract it and return. -// -// From this point then normal rules of configmanager apply, -// including keySeperator and lookup. -func (imp *AzTableStore) Value() (string, error) { - imp.logger.Info("AzTableSTore Token: %s", imp.token.String()) - imp.logger.Info("Concrete implementation AzTableSTore") - - ctx, cancel := context.WithCancel(imp.ctx) - defer cancel() - - // split the token for partition and rowKey - pKey, rKey, err := azTableStoreTokenSplitter(imp.strippedToken) - if err != nil { - return "", err - } - - s, err := imp.svc.GetEntity(ctx, pKey, rKey, &aztables.GetEntityOptions{}) - if err != nil { - imp.logger.Error(implementationNetworkErr, config.AzTableStorePrefix, err, imp.strippedToken) - return "", fmt.Errorf(implementationNetworkErr+" %w", config.AzTableStorePrefix, err, imp.token.StoreToken(), ErrRetrieveFailed) - } - if len(s.Value) > 0 { - // check for `value` property in entity - checkVal := make(map[string]interface{}) - _ = json.Unmarshal(s.Value, &checkVal) - if checkVal["value"] != nil { - return fmt.Sprintf("%v", checkVal["value"]), nil - } - return string(s.Value), nil - } - imp.logger.Error("value retrieved but empty for token: %v", imp.token) - return "", nil -} - -func azTableStoreTokenSplitter(token string) (partitionKey, rowKey string, err error) { - splitToken := strings.Split(strings.TrimPrefix(token, "/"), "/") - if len(splitToken) < 2 { - return "", "", fmt.Errorf("token: %s - could not be correctly destructured to pluck the partition and row keys\n%w", token, ErrIncorrectlyStructuredToken) - } - partitionKey = splitToken[0] - rowKey = splitToken[1] - // naked return to save having to define another struct - return -} diff --git a/internal/store/aztablestorage_test.go b/internal/store/aztablestorage_test.go deleted file mode 100644 index 892ee9e..0000000 --- a/internal/store/aztablestorage_test.go +++ /dev/null @@ -1,354 +0,0 @@ -package store_test - -import ( - "context" - "errors" - "fmt" - "io" - "strings" - "testing" - - "github.com/Azure/azure-sdk-for-go/sdk/data/aztables" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" -) - -func azTableStoreCommonChecker(t *testing.T, partitionKey, rowKey, expectedPartitionKey, expectedRowKey string) { - t.Helper() - if partitionKey == "" { - t.Errorf("expect name to not be nil") - } - if partitionKey != expectedPartitionKey { - t.Errorf(testutils.TestPhrase, partitionKey, expectedPartitionKey) - } - - if strings.Contains(partitionKey, string(config.AzTableStorePrefix)) { - t.Errorf("incorrectly stripped prefix") - } - - if rowKey != expectedRowKey { - t.Errorf(testutils.TestPhrase, rowKey, expectedPartitionKey) - } -} - -type mockAzTableStoreApi func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) - -func (m mockAzTableStoreApi) GetEntity(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - return m(ctx, partitionKey, rowKey, options) -} - -func Test_AzTableStore_Success(t *testing.T) { - - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockAzTableStoreApi - }{ - "successVal": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE#/test-account/table//token/1" - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-account/table//token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, "tsuccessParam", func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - azTableStoreCommonChecker(t, partitionKey, rowKey, "token", "1") - resp := aztables.GetEntityResponse{} - resp.Value = []byte("tsuccessParam") - return resp, nil - }) - }, - }, - // "successVal with :// token Separator": {"AZTABLESTORE:///test-account/table//token/1", "tsuccessParam", func(t *testing.T) tableStoreApi { - // return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - // t.Helper() - // azTableStoreCommonChecker(t, partitionKey, rowKey, "token", "1") - // resp := aztables.GetEntityResponse{} - // resp.Value = []byte("tsuccessParam") - // return resp, nil - // }) - // }, config.NewConfig().WithKeySeparator("|").WithTokenSeparator("://"), - // }, - // "successVal with keyseparator but no val returned": {"AZTABLESTORE#/test-account/table/token/1|somekey", "", func(t *testing.T) tableStoreApi { - // return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - // t.Helper() - // azTableStoreCommonChecker(t, partitionKey, rowKey, "token", "1") - - // resp := aztables.GetEntityResponse{} - // resp.Value = nil - // return resp, nil - // }) - // }, - // config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#"), - // }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - impl, err := store.NewAzTableStore(context.TODO(), tt.token(), log.New(io.Discard)) - if err != nil { - t.Errorf("failed to init aztablestore") - } - - impl.WithSvc(tt.mockClient(t)) - - got, err := impl.Value() - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} - -func Test_azstorage_with_value_property(t *testing.T) { - - conf := config.NewConfig().WithKeySeparator("|").WithTokenSeparator("://") - ttests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockAzTableStoreApi - }{ - "return value property with json like object": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE:///test-account/table/partitionkey/rowKey|host", - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *conf) - tkn.WithSanitizedToken("/test-account/table/partitionkey/rowKey") - tkn.WithKeyPath("host") - return tkn - }, - "map[bool:true host:foo port:1234]", - func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - resp := aztables.GetEntityResponse{Value: []byte(`{"value":{"host":"foo","port":1234,"bool":true}}`)} - return resp, nil - }) - }, - }, - "return value property with string only": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE:///test-account/table/partitionkey/rowKey", - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *conf) - tkn.WithSanitizedToken("/test-account/table/partitionkey/rowKey") - // tkn.WithKeyPath("host") - // tkn.WithMetadata("version:123]") - return tkn - }, - "foo.bar.com", - func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - resp := aztables.GetEntityResponse{Value: []byte(`{"value":"foo.bar.com"}`)} - return resp, nil - }) - }, - }, - "return value property with numeric only": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE:///test-account/table/partitionkey/rowKey", - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *conf) - tkn.WithSanitizedToken("/test-account/table/partitionkey/rowKey") - // tkn.WithKeyPath("host") - // tkn.WithMetadata("version:123]") - return tkn - }, - "1234", - func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - resp := aztables.GetEntityResponse{Value: []byte(`{"value":1234}`)} - return resp, nil - }) - }, - }, - "return value property with boolean only": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE:///test-account/table/partitionkey/rowKey", - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *conf) - tkn.WithSanitizedToken("/test-account/table/partitionkey/rowKey") - return tkn - }, - "false", - func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - resp := aztables.GetEntityResponse{Value: []byte(`{"value":false}`)} - return resp, nil - }) - }, - }, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - // token, _ := config.NewToken(tt.token(), *tt.config) - - impl, err := store.NewAzTableStore(context.TODO(), tt.token(), log.New(io.Discard)) - if err != nil { - t.Fatal("failed to init aztablestore") - } - - impl.WithSvc(tt.mockClient(t)) - - got, err := impl.Value() - if err != nil { - t.Fatalf(testutils.TestPhrase, err.Error(), nil) - } - - if got != tt.expect { - t.Errorf(testutils.TestPhraseWithContext, "AZ Table storage with value property inside entity", fmt.Sprintf("%q", got), fmt.Sprintf("%q", tt.expect)) - } - }) - } -} - -func Test_AzTableStore_Error(t *testing.T) { - - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect error - mockClient func(t *testing.T) mockAzTableStoreApi - }{ - "errored on token parsing to partiationKey": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE#/test-vault/token/1|somekey" - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-vault/token/1") - tkn.WithKeyPath("somekey") - tkn.WithMetadata("") - return tkn - }, store.ErrIncorrectlyStructuredToken, func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - resp := aztables.GetEntityResponse{} - return resp, nil - }) - }, - }, - "errored on service method call": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE#/test-account/table/token/ok", - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-account/table/token/ok") - return tkn - }, - store.ErrRetrieveFailed, - func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - resp := aztables.GetEntityResponse{} - return resp, fmt.Errorf("network error") - }) - }, - }, - - "empty": { - func() *config.ParsedTokenConfig { - // "AZTABLESTORE#/test-vault/token/1|somekey", - tkn, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/test-vault/token/1|somekey") - return tkn - }, - store.ErrIncorrectlyStructuredToken, func(t *testing.T) mockAzTableStoreApi { - return mockAzTableStoreApi(func(ctx context.Context, partitionKey string, rowKey string, options *aztables.GetEntityOptions) (aztables.GetEntityResponse, error) { - t.Helper() - resp := aztables.GetEntityResponse{} - return resp, nil - }) - }, - }, - } - - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - impl, err := store.NewAzTableStore(context.TODO(), tt.token(), log.New(io.Discard)) - if err != nil { - t.Fatal("failed to init aztablestore") - } - - impl.WithSvc(tt.mockClient(t)) - if _, err := impl.Value(); !errors.Is(err, tt.expect) { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - }) - } -} - -func Test_fail_AzTable_Client_init(t *testing.T) { - // this is basically a wrap around test for the url.Parse method in the stdlib - // as that is what the client uses under the hood - token, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig()) - // "AZTABLESTORE:///%25%65%6e%301-._~/") - } - if !errors.Is(err, store.ErrClientInitialization) { - t.Fatalf(testutils.TestPhraseWithContext, "aztables client init", err.Error(), store.ErrClientInitialization.Error()) - } -} - -func Test_azSplitTokenTableStore(t *testing.T) { - - tests := []struct { - name string - token string - expect store.AzServiceHelper - }{ - { - name: "simple_with_preceding_slash", - token: "/test-account/tablename/somejsontest", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-account.table.core.windows.net/tablename", - Token: "somejsontest", - }, - }, - { - name: "missing_initial_slash", - token: "test-account/tablename/somejsontest", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-account.table.core.windows.net/tablename", - Token: "somejsontest", - }, - }, - { - name: "missing_initial_slash_multislash_secretname", - token: "test-account/tablename/some/json/test", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-account.table.core.windows.net/tablename", - Token: "some/json/test", - }, - }, - { - name: "with_initial_slash_multislash_secretname", - token: "test-account/tablename//some/json/test", - expect: store.AzServiceHelper{ - ServiceUri: "https://test-account.table.core.windows.net/tablename", - Token: "/some/json/test", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := store.AzServiceFromToken(tt.token, "https://%s.table.core.windows.net/%s", 2) - if got.Token != tt.expect.Token { - t.Errorf(testutils.TestPhrase, tt.expect.Token, got.Token) - } - if got.ServiceUri != tt.expect.ServiceUri { - t.Errorf(testutils.TestPhrase, tt.expect.ServiceUri, got.ServiceUri) - } - }) - } -} diff --git a/internal/store/gcpsecrets.go b/internal/store/gcpsecrets.go deleted file mode 100644 index 07c43e2..0000000 --- a/internal/store/gcpsecrets.go +++ /dev/null @@ -1,91 +0,0 @@ -package store - -import ( - "context" - "fmt" - - gcpsecrets "cloud.google.com/go/secretmanager/apiv1" - gcpsecretspb "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/googleapis/gax-go/v2" -) - -type gcpSecretsApi interface { - AccessSecretVersion(ctx context.Context, req *gcpsecretspb.AccessSecretVersionRequest, opts ...gax.CallOption) (*gcpsecretspb.AccessSecretVersionResponse, error) -} - -type GcpSecrets struct { - svc gcpSecretsApi - logger log.ILogger - ctx context.Context - config *GcpSecretsConfig - close func() error - token *config.ParsedTokenConfig -} - -type GcpSecretsConfig struct { - Version string `json:"version"` -} - -func NewGcpSecrets(ctx context.Context, logger log.ILogger) (*GcpSecrets, error) { - - c, err := gcpsecrets.NewClient(ctx) - if err != nil { - return nil, err - } - return &GcpSecrets{ - svc: c, - logger: logger, - ctx: ctx, - close: c.Close, - }, nil -} - -func (s *GcpSecrets) WithSvc(svc gcpSecretsApi) { - s.svc = svc -} - -func (imp *GcpSecrets) SetToken(token *config.ParsedTokenConfig) { - storeConf := &GcpSecretsConfig{} - _ = token.ParseMetadata(storeConf) - imp.token = token - imp.config = storeConf -} - -func (imp *GcpSecrets) Value() (string, error) { - // Close client currently as new one would be created per iteration - defer func() { - _ = imp.close() - }() - - imp.logger.Info("Concrete implementation GcpSecrets") - imp.logger.Info("GcpSecrets Token: %s", imp.token.String()) - - version := "latest" - if imp.config.Version != "" { - version = imp.config.Version - } - - imp.logger.Info("Getting Secret: %s @version: %s", imp.token, version) - - input := &gcpsecretspb.AccessSecretVersionRequest{ - Name: fmt.Sprintf("%s/versions/%s", imp.token.StoreToken(), version), - } - - ctx, cancel := context.WithCancel(imp.ctx) - defer cancel() - - result, err := imp.svc.AccessSecretVersion(ctx, input) - - if err != nil { - imp.logger.Error(implementationNetworkErr, imp.token.Prefix(), err, imp.token.String()) - return "", err - } - if result.Payload != nil { - return string(result.Payload.Data), nil - } - - imp.logger.Error("value retrieved but empty for token: %v", imp.token) - return "", nil -} diff --git a/internal/store/gcpsecrets_test.go b/internal/store/gcpsecrets_test.go deleted file mode 100644 index 5f859ba..0000000 --- a/internal/store/gcpsecrets_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package store_test - -import ( - "context" - "fmt" - "io" - "os" - "strings" - "testing" - - gcpsecretspb "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" - "github.com/googleapis/gax-go/v2" -) - -type mockGcpSecretsApi func(ctx context.Context, req *gcpsecretspb.AccessSecretVersionRequest, opts ...gax.CallOption) (*gcpsecretspb.AccessSecretVersionResponse, error) - -func (m mockGcpSecretsApi) AccessSecretVersion(ctx context.Context, req *gcpsecretspb.AccessSecretVersionRequest, opts ...gax.CallOption) (*gcpsecretspb.AccessSecretVersionResponse, error) { - return m(ctx, req, opts...) -} - -func (m mockGcpSecretsApi) Close() error { - return nil -} - -var TEST_GCP_CREDS = []byte(`{ - "type": "service_account", - "project_id": "xxxxx", - "private_key_id": "yyyyyyyyyyyy", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDf842hcn5Nvp6e\n7yKARaCVIDfLXpKDhRwUOvHMzJ1ioRgQo/kbv1n4yHGCSUFyY6hKGj0HBjaGj5kE\n79H/6Y3dJNGhnsMnxBhHdo+3FI8QF0CHZh460NMZSAJ41UMQSBGssGVsNfyUzXGH\nLc45sIx/Twx3yr1k2GD3E8FlDcKlZqa3xGHf+aipg2X3NxbYi+Sz7Yed+SOMhNHl\ncX6E/TqG9n1aTyIwjMIHscCYarJqURkJxr24ukDroCeMxAfxYTdMvRU2e8pFEdoY\nrgUC88fYfaVI5txJ6j/ZKauKQX9Pa8tSyXJeGva3JYp4VC7V4IyoVviCUgEGWZDN\n6/i3zoF/AgMBAAECggEAcVBCcVYFIkE48SH+Svjv74SFtpj7eSB4vKO2hPFjEOyB\nyKmu+aMwWvjQtiNqwf46wIPWLR+vpxYxTpYpo1sBNMvUZfp2tEA8KKyMuw3j9ThO\npjO9R/UxWrFcztbZP/u3NbFrH/2Q95mbv9IlbnsuG5xbqqEig0wYg+uzBvaXbig3\n/Jr0vLT2BkRCBKQkYGjVZcHlHVLoF7/J8cghFgkV1PGvknOv6/q7qzn9L4TjQIet\nfhrhN8Z1vgFiSYtpjP6YQEUEPSHmCQeD3WzJcnASPpU2uCUwd/z65ltKPnn+rqMt\n6jt9R1S1Ju2ZSjv+kR5fIXzihdOzncyzDDm33c/QwQKBgQD2QDZuzLjTxnhsfGii\nKJDAts+Jqfs/6SeEJcJKtEngj4m7rgzyEjbKVp8qtRHIzglKRWAe62/qzzy2BkKi\nvAd4+ZzmG2SkgypGsKVfjGXVFixz2gtUdmBOmK/TnYsxNT9yTt+rX9IGqKK60q73\nOWl8VsliLIsfvSH7+bqi7sRcXQKBgQDo0VUebyQHoTAXPdzGy2ysrVPDiHcldH0Y\n/hvhQTZwxYaJr3HpOCGol2Xl6zyawuudEQsoQwJ3Li6yeb0YMGiWX77/t+qX3pSn\nkGuoftGaNDV7sLn9UV2y+InF8EL1CasrhG1k5RIuxyfV0w+QUo+E7LpVR5XkbJqT\n9QNKnDQXiwKBgQDvvEYCCqbp7e/xVhEbxbhfFdro4Cat6tRAz+3egrTlvXhO0jzi\nMp9Kz5f3oP5ma0gaGX5hu75icE1fvKqE+d+ghAqe7w5FJzkyRulJI0tEb2jphN7A\n5NoPypBqyZboWjmhlG4mzouPVf/POCuEnk028truDAWJ6by7Lj3oP+HFNQKBgQCc\n5BQ8QiFBkvnZb7LLtGIzq0n7RockEnAK25LmJRAOxs13E2fsBguIlR3x5qgckqY8\nXjPqmd2bet+1HhyzpEuWqkcIBGRum2wJz2T9UxjklbJE/D8Z2i8OYDZX0SUOA8n5\ntXASwduS8lqB2Y1vcHOO3AhlV6xHFnjEpCPnr4PbKQKBgAhQ9D9MPeuz+5yw3yHg\nkvULZRtud+uuaKrOayprN25RTxr9c0erxqnvM7KHeo6/urOXeEa7x2n21kAT0Nch\nkF2RtWBLZKXGZEVBtw1Fw0UKNh4IDgM26dwlzRfTVHCiw6M6dCiTNk9KkP2vlkim\n3QFDSSUp+eBTXA17WkDAQf7w\n-----END PRIVATE KEY-----\n", - "client_email": "foo@project.iam.gserviceaccount.com", - "client_id": "99999911111111", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/bla" - }`) - -func fixtureInitMockClient(t *testing.T) struct { - name string - close func() error - delete func(name string) error -} { - - cf, err := os.CreateTemp("", "gcp-creds*") - if err != nil { - t.Fatalf(testutils.TestPhraseWithContext, "unable to set up creds file", err.Error(), nil) - } - if _, err := cf.Write(TEST_GCP_CREDS); err != nil { - t.Fatalf(testutils.TestPhraseWithContext, "unable to write mock creds into file", err.Error(), nil) - } - - resp := struct { - name string - close func() error - delete func(name string) error - }{ - name: cf.Name(), - close: cf.Close, - delete: os.Remove, - } - return resp -} - -func gcpSecretsGetChecker(t *testing.T, req *gcpsecretspb.AccessSecretVersionRequest) { - t.Helper() - if req.Name == "" { - t.Fatal("expect name to not be nil") - } - if strings.Contains(req.Name, "#") { - t.Errorf("incorrectly stripped token separator") - } - if strings.Contains(req.Name, string(config.GcpSecretsPrefix)) { - t.Errorf("incorrectly stripped prefix") - } -} - -func Test_GetGcpSecretVarHappy(t *testing.T) { - - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockGcpSecretsApi - }{ - "success": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.GcpSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - "someValue", func(t *testing.T) mockGcpSecretsApi { - return mockGcpSecretsApi(func(ctx context.Context, req *gcpsecretspb.AccessSecretVersionRequest, opts ...gax.CallOption) (*gcpsecretspb.AccessSecretVersionResponse, error) { - gcpSecretsGetChecker(t, req) - return &gcpsecretspb.AccessSecretVersionResponse{ - Payload: &gcpsecretspb.SecretPayload{Data: []byte("someValue")}, - }, nil - }) - }, - }, - "success with version": { - func() *config.ParsedTokenConfig { - // "GCPSECRETS#/token/1[version=123]" - tkn, _ := config.NewToken(config.GcpSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("version=123") - return tkn - }, "someValue", func(t *testing.T) mockGcpSecretsApi { - return mockGcpSecretsApi(func(ctx context.Context, req *gcpsecretspb.AccessSecretVersionRequest, opts ...gax.CallOption) (*gcpsecretspb.AccessSecretVersionResponse, error) { - gcpSecretsGetChecker(t, req) - return &gcpsecretspb.AccessSecretVersionResponse{ - Payload: &gcpsecretspb.SecretPayload{Data: []byte("someValue")}, - }, nil - }) - }, - }, - "error": { - func() *config.ParsedTokenConfig { - // "GCPSECRETS#/token/1" - tkn, _ := config.NewToken(config.GcpSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, "unable to retrieve secret", func(t *testing.T) mockGcpSecretsApi { - return mockGcpSecretsApi(func(ctx context.Context, req *gcpsecretspb.AccessSecretVersionRequest, opts ...gax.CallOption) (*gcpsecretspb.AccessSecretVersionResponse, error) { - gcpSecretsGetChecker(t, req) - return nil, fmt.Errorf("unable to retrieve secret") - }) - }, - }, - "found but empty": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.GcpSecretsPrefix, *config.NewConfig().WithKeySeparator("|").WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - "", - func(t *testing.T) mockGcpSecretsApi { - return mockGcpSecretsApi(func(ctx context.Context, req *gcpsecretspb.AccessSecretVersionRequest, opts ...gax.CallOption) (*gcpsecretspb.AccessSecretVersionResponse, error) { - gcpSecretsGetChecker(t, req) - return &gcpsecretspb.AccessSecretVersionResponse{}, nil - }) - }, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - fixture := fixtureInitMockClient(t) - defer fixture.close() - defer fixture.delete(fixture.name) - - os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", fixture.name) - - impl, err := store.NewGcpSecrets(context.TODO(), log.New(io.Discard)) - - if err != nil { - t.Errorf(testutils.TestPhrase, err.Error(), nil) - } - - impl.WithSvc(tt.mockClient(t)) - - impl.SetToken(tt.token()) - got, err := impl.Value() - - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} diff --git a/internal/store/hashivault.go b/internal/store/hashivault.go deleted file mode 100644 index 558c9b2..0000000 --- a/internal/store/hashivault.go +++ /dev/null @@ -1,172 +0,0 @@ -package store - -import ( - "context" - "encoding/json" - "fmt" - "os" - "strconv" - "strings" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - - vault "github.com/hashicorp/vault/api" - auth "github.com/hashicorp/vault/api/auth/aws" -) - -// HashiVaultHelper provides a broken up string -type HashiVaultHelper struct { - Path string - Token string -} - -type hashiVaultApi interface { - Get(ctx context.Context, secretPath string) (*vault.KVSecret, error) - GetVersion(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) -} - -type VaultStore struct { - svc hashiVaultApi - ctx context.Context - logger log.ILogger - config *VaultConfig - token *config.ParsedTokenConfig - strippedToken string -} - -// VaultConfig holds the parseable metadata struct -type VaultConfig struct { - Version string `json:"version"` - Role string `json:"iam_role"` -} - -func NewVaultStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*VaultStore, error) { - storeConf := &VaultConfig{} - _ = token.ParseMetadata(storeConf) - imp := &VaultStore{ - ctx: ctx, - logger: logger, - config: storeConf, - token: token, - } - - config := vault.DefaultConfig() - vt := SplitHashiVaultToken(token.StoreToken()) - imp.strippedToken = vt.Token - client, err := vault.NewClient(config) - if err != nil { - return nil, fmt.Errorf("%v\n%w", err, ErrClientInitialization) - } - - if strings.HasPrefix(os.Getenv("VAULT_TOKEN"), "aws_iam") { - awsclient, err := newVaultStoreWithAWSAuthIAM(client, storeConf.Role) - if err != nil { - return nil, err - } - client = awsclient - } - imp.svc = client.KVv2(vt.Path) - return imp, nil -} - -func (s *VaultStore) WithSvc(svc hashiVaultApi) { - s.svc = svc -} - -// newVaultStoreWithAWSAuthIAM returns an initialised client with AWSIAMAuth -// EC2 auth type is not supported currently -func newVaultStoreWithAWSAuthIAM(client *vault.Client, role string) (*vault.Client, error) { - if len(role) < 1 { - return nil, fmt.Errorf("role provided is empty, EC2 auth not supported") - } - awsAuth, err := auth.NewAWSAuth( - auth.WithRole(role), - ) - if err != nil { - return nil, fmt.Errorf("unable to initialize AWS auth method: %s. %w", err, ErrClientInitialization) - } - - authInfo, err := client.Auth().Login(context.Background(), awsAuth) - - if err != nil { - return nil, fmt.Errorf("unable to login to AWS auth method: %s. %w", err, ErrClientInitialization) - } - if authInfo == nil { - return nil, fmt.Errorf("no auth info was returned after login") - } - - return client, nil -} - -// setTokenVal -// imp.token is already set in the Vault constructor -// -// This happens inside the New func call -// due to the way the client needs to be -// initialised with a mountpath -// and mountpath is part of the token so it is set then -func (imp *VaultStore) SetToken(token *config.ParsedTokenConfig) {} - -// getTokenValue implements the underlying techonology -// token retrieval and returns a stringified version -// of the secret -func (imp *VaultStore) Value() (string, error) { - imp.logger.Info("%s", "Concrete implementation HashiVault") - imp.logger.Info("Getting Secret: %s", imp.token) - - ctx, cancel := context.WithCancel(imp.ctx) - defer cancel() - - secret, err := imp.getSecret(ctx, imp.strippedToken, imp.config.Version) - if err != nil { - imp.logger.Error(implementationNetworkErr, imp.token.Prefix(), err, imp.token.String()) - return "", err - } - - if secret.Data != nil { - resp, err := marshall(secret.Data) - if err != nil { - imp.logger.Error("marshalling error: %s", err.Error()) - return "", err - } - imp.logger.Debug("marhalled kvv2: %s", resp) - return resp, nil - } - - imp.logger.Error("value retrieved but empty for token: %v", imp.token) - return "", nil -} - -func (imp *VaultStore) getSecret(ctx context.Context, token string, version string) (*vault.KVSecret, error) { - if version != "" { - v, err := strconv.Atoi(version) - if err != nil { - return nil, fmt.Errorf("unable to parse version into an integer: %s", err.Error()) - } - return imp.svc.GetVersion(ctx, token, v) - } - return imp.svc.Get(ctx, token) -} - -func SplitHashiVaultToken(token string) HashiVaultHelper { - vh := HashiVaultHelper{} - // split token to extract the mount path - s := strings.Split(strings.TrimPrefix(token, "/"), "___") - // grab token and trim prefix if slash - vh.Token = strings.TrimPrefix(strings.Join(s[1:], ""), "/") - // assign mount path as extracted from input token - vh.Path = s[0] - return vh -} - -// marshall converts map[string]any into a JSON -// object. Secrets should only be a single level -// deep. -func marshall(secret map[string]any) (string, error) { - b, err := json.Marshal(secret) - if err != nil { - return "", err - } - return string(b), nil -} diff --git a/internal/store/hashivault_test.go b/internal/store/hashivault_test.go deleted file mode 100644 index 8c9aed8..0000000 --- a/internal/store/hashivault_test.go +++ /dev/null @@ -1,624 +0,0 @@ -package store_test - -import ( - "context" - "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "strings" - "testing" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" - vault "github.com/hashicorp/vault/api" -) - -func TestMountPathExtract(t *testing.T) { - ttests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - }{ - "without leading slash": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/demo/configmanager" - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/demo/configmanager") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, "secret"}, - "with leading slash": { - func() *config.ParsedTokenConfig { - // "VAULT:///secret___/demo/configmanager", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("/secret___/demo/configmanager") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, "secret"}, - "with underscore in path name": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("_secret___/demo/configmanager") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, "_secret"}, - "with double underscore in path name": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("__secret___/demo/configmanager") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, "__secret"}, - "with multiple paths in mountpath": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret/bar/path___/demo/configmanager") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, "secret/bar/path"}, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - got := store.SplitHashiVaultToken(tt.token().StoreToken()) - if got.Path != tt.expect { - t.Errorf("got %q, expected %q", got, tt.expect) - } - }) - } -} - -type mockVaultApi struct { - g func(ctx context.Context, secretPath string) (*vault.KVSecret, error) - gv func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) -} - -func (m mockVaultApi) Get(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - return m.g(ctx, secretPath) -} - -func (m mockVaultApi) GetVersion(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { - return m.gv(ctx, secretPath, version) -} - -func TestVaultScenarios(t *testing.T) { - - ttests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockVaultApi - setupEnv func() func() - }{ - "happy return": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/foo") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - `{"foo":"test2130-9sd-0ds"}`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "foo" { - t.Errorf("got %v; want %s", secretPath, `foo`) - } - m := make(map[string]interface{}) - m["foo"] = "test2130-9sd-0ds" - return &vault.KVSecret{Data: m}, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "incorrect json": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/foo", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/foo") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - `json: unsupported type: func() error`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "foo" { - t.Errorf("got %v; want %s", secretPath, `foo`) - } - m := make(map[string]interface{}) - m["error"] = func() error { return fmt.Errorf("ddodod") } - return &vault.KVSecret{Data: m}, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "another return": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret/engine1___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - `{"foo1":"test2130-9sd-0ds","foo2":"dsfsdf3454456"}`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf("got %v; want %s", secretPath, `some/other/foo2`) - } - m := make(map[string]interface{}) - m["foo1"] = "test2130-9sd-0ds" - m["foo2"] = "dsfsdf3454456" - return &vault.KVSecret{Data: m}, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "not found": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/foo", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/foo") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - `secret not found`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "foo" { - t.Errorf("got %v; want %s", secretPath, `foo`) - } - return nil, fmt.Errorf("secret not found") - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "403": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/some/other/foo2", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - `client 403`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf("got %v; want %s", secretPath, `some/other/foo2`) - } - return nil, fmt.Errorf("client 403") - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "found but empty": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/some/other/foo2", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - // config.NewConfig(), - `{}`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf("got %v; want %s", secretPath, `some/other/foo2`) - } - m := make(map[string]interface{}) - return &vault.KVSecret{Data: m}, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "found but nil returned": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - "", - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - return &vault.KVSecret{Data: nil}, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "version provided correctly": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/some/other/foo2[version=1]", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("version=1") - return tkn - }, - `{"foo2":"dsfsdf3454456"}`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.gv = func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - m := make(map[string]interface{}) - m["foo2"] = "dsfsdf3454456" - return &vault.KVSecret{Data: m}, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "version provided but unable to parse": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/some/other/foo2[version=1a]", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("version=1a") - return tkn - }, - "unable to parse version into an integer: strconv.Atoi: parsing \"1a\": invalid syntax", - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.gv = func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - return nil, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "129378y1231283") - return func() { - os.Clearenv() - } - }, - }, - "vault rate limit incorrect": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - `error encountered setting up default configuration: VAULT_RATE_LIMIT was provided but incorrectly formatted -failed to initialize the client`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - return &vault.KVSecret{Data: nil}, nil - } - return mv - }, - func() func() { - os.Setenv("VAULT_TOKEN", "") - os.Setenv("VAULT_RATE_LIMIT", "wrong") - return func() { - os.Clearenv() - } - }, - }, - } - - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - tearDown := tt.setupEnv() - defer tearDown() - - impl, err := store.NewVaultStore(context.TODO(), tt.token(), log.New(io.Discard)) - if err != nil { - if err.Error() != tt.expect { - t.Fatalf("failed to init hashivault, %v", err.Error()) - } - return - } - - impl.WithSvc(tt.mockClient(t)) - got, err := impl.Value() - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} - -func TestAwsIamAuth(t *testing.T) { - ttests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockVaultApi - mockHanlder func(t *testing.T) http.Handler - setupEnv func(addr string) func() - }{ - "aws_iam auth no role specified": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("version=1") - return tkn - }, - "role provided is empty, EC2 auth not supported", - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - return &vault.KVSecret{Data: nil}, nil - } - return mv - }, - func(t *testing.T) http.Handler { - return nil - }, - func(_ string) func() { - os.Setenv("VAULT_TOKEN", "aws_iam") - os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") - os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") - os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") - os.Setenv("AWS_REGION", "eu-west-1") - return func() { - os.Clearenv() - } - }, - }, - "aws_iam auth incorrectly formatted request": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("version=1,iam_role=not_a_role") - return tkn - }, - `unable to login to AWS auth method: unable to log in to auth method: unable to log in with AWS auth: Error making API request. - -URL: PUT %s/v1/auth/aws/login -Code: 400. Raw Message: - -incorrect values supplied. failed to initialize the client`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - return &vault.KVSecret{Data: nil}, nil - } - return mv - }, - func(t *testing.T) http.Handler { - mux := http.NewServeMux() - mux.HandleFunc("/v1/auth/aws/login", func(w http.ResponseWriter, r *http.Request) { - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(400) - w.Write([]byte(`incorrect values supplied`)) - }) - return mux - }, - func(addr string) func() { - os.Setenv("VAULT_TOKEN", "aws_iam") - os.Setenv("VAULT_ADDR", addr) - os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") - os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") - os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") - os.Setenv("AWS_REGION", "eu-west-1") - return func() { - os.Clearenv() - } - }, - }, - "aws_iam auth success": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("iam_role=arn:aws:iam::1111111:role/i-orchestration") - return tkn - }, - // - `{"foo2":"dsfsdf3454456"}`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - m := make(map[string]any) - m["foo2"] = "dsfsdf3454456" - return &vault.KVSecret{Data: m}, nil - } - return mv - }, - func(t *testing.T) http.Handler { - mux := http.NewServeMux() - mux.HandleFunc("/v1/auth/aws/login", func(w http.ResponseWriter, r *http.Request) { - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Write([]byte(`{"auth":{"client_token": "fooresddfasdsasad"}}`)) - }) - return mux - }, - func(addr string) func() { - os.Setenv("VAULT_TOKEN", "aws_iam") - os.Setenv("VAULT_ADDR", addr) - os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") - os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") - os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") - os.Setenv("AWS_REGION", "eu-west-1") - return func() { - os.Clearenv() - } - }, - }, - "aws_iam auth no token returned": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("secret___/some/other/foo2") - tkn.WithKeyPath("") - tkn.WithMetadata("iam_role=arn:aws:iam::1111111:role/i-orchestration") - return tkn - }, - `unable to login to AWS auth method: response did not return ClientToken, client token not set. failed to initialize the client`, - func(t *testing.T) mockVaultApi { - mv := mockVaultApi{} - mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { - t.Helper() - if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) - } - m := make(map[string]interface{}) - m["foo2"] = "dsfsdf3454456" - return &vault.KVSecret{Data: m}, nil - } - return mv - }, - func(t *testing.T) http.Handler { - mux := http.NewServeMux() - mux.HandleFunc("/v1/auth/aws/login", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.Write([]byte(`{"auth":{}}`)) - }) - return mux - }, - func(addr string) func() { - os.Setenv("VAULT_TOKEN", "aws_iam") - os.Setenv("VAULT_ADDR", addr) - os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") - os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") - os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") - os.Setenv("AWS_REGION", "eu-west-1") - return func() { - os.Clearenv() - } - }, - }, - } - - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - // - ts := httptest.NewServer(tt.mockHanlder(t)) - tearDown := tt.setupEnv(ts.URL) - defer tearDown() - impl, err := store.NewVaultStore(context.TODO(), tt.token(), log.New(io.Discard)) - if err != nil { - // WHAT A CRAP way to do this... - if err.Error() != strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0] { - t.Errorf(testutils.TestPhraseWithContext, "aws iam auth", err.Error(), strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0]) - t.Fatalf("failed to init hashivault, %v", err.Error()) - } - return - } - - impl.WithSvc(tt.mockClient(t)) - got, err := impl.Value() - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} diff --git a/internal/store/paramstore_test.go b/internal/store/paramstore_test.go deleted file mode 100644 index 8fc11d4..0000000 --- a/internal/store/paramstore_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package store_test - -import ( - "context" - "fmt" - "io" - "strings" - "testing" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" - "github.com/aws/aws-sdk-go-v2/service/ssm" - "github.com/aws/aws-sdk-go-v2/service/ssm/types" -) - -type mockParamApi func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) - -func (m mockParamApi) GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { - return m(ctx, params, optFns...) -} - -func awsParamtStoreCommonGetChecker(t *testing.T, params *ssm.GetParameterInput) { - if params.Name == nil { - t.Fatal("expect name to not be nil") - } - - if strings.Contains(*params.Name, "#") { - t.Errorf("incorrectly stripped token separator") - } - - if strings.Contains(*params.Name, string(config.ParamStorePrefix)) { - t.Errorf("incorrectly stripped prefix") - } - - if !*params.WithDecryption { - t.Fatal("expect WithDecryption to not be false") - } -} - -func Test_GetParamStore(t *testing.T) { - var ( - tsuccessParam = "someVal" - // tsuccessObj map[string]string = map[string]string{"AWSPARAMSTR#/token/1": "someVal"} - ) - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockParamApi - }{ - "successVal": { - func() *config.ParsedTokenConfig { - // "VAULT://secret___/demo/configmanager" - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - tsuccessParam, func(t *testing.T) mockParamApi { - return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { - t.Helper() - awsParamtStoreCommonGetChecker(t, params) - return &ssm.GetParameterOutput{ - Parameter: &types.Parameter{Value: &tsuccessParam}, - }, nil - }) - }, - }, - "successVal with keyseparator": { - func() *config.ParsedTokenConfig { - // "AWSPARAMSTR#/token/1|somekey", - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("somekey") - tkn.WithMetadata("") - return tkn - }, - tsuccessParam, func(t *testing.T) mockParamApi { - return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { - t.Helper() - awsParamtStoreCommonGetChecker(t, params) - - if strings.Contains(*params.Name, "|somekey") { - t.Errorf("incorrectly stripped key separator") - } - - return &ssm.GetParameterOutput{ - Parameter: &types.Parameter{Value: &tsuccessParam}, - }, nil - }) - }, - }, - "errored": { - func() *config.ParsedTokenConfig { - // "AWSPARAMSTR#/token/1", - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - "unable to retrieve", func(t *testing.T) mockParamApi { - return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { - t.Helper() - awsParamtStoreCommonGetChecker(t, params) - return nil, fmt.Errorf("unable to retrieve") - }) - }, - }, - "nil to empty": { - func() *config.ParsedTokenConfig { - // "AWSPARAMSTR#/token/1", - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - "", func(t *testing.T) mockParamApi { - return mockParamApi(func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) { - t.Helper() - awsParamtStoreCommonGetChecker(t, params) - return &ssm.GetParameterOutput{ - Parameter: &types.Parameter{Value: nil}, - }, nil - }) - }, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - impl, err := store.NewParamStore(context.TODO(), log.New(io.Discard)) - if err != nil { - t.Errorf(testutils.TestPhrase, err.Error(), nil) - } - impl.WithSvc(tt.mockClient(t)) - impl.SetToken(tt.token()) - got, err := impl.Value() - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} diff --git a/internal/store/plugin.go b/internal/store/plugin.go new file mode 100644 index 0000000..ddfd739 --- /dev/null +++ b/internal/store/plugin.go @@ -0,0 +1,64 @@ +package store + +import ( + "context" + "os/exec" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/hashicorp/go-plugin" +) + +// Plugin is responsible for managing plugins within configmanager +// +// It includes the following methods +// - fetch plugins from known sources +// - maintains a list of tokens answerable by a specified pluginEngine +type Plugin struct { + Implementations config.ImplementationPrefix + SourcePath string + Version string + ClientCleanUp func() + tokenStore plugins.TokenStore +} + +// New Plugin gets called once per implementation +func New(ctx context.Context, path string, prefix config.ImplementationPrefix) (*Plugin, error) { + // We're a host. Start by launching the plugin process. + client := plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: plugins.Handshake, + Plugins: plugin.PluginSet{"configmanager_token_store": &plugins.TokenStoreGRPCPlugin{}}, + Cmd: exec.Command(path), + AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, + }) + + // Connect via RPC + rpcClient, err := client.Client() + if err != nil { + client.Kill() + return nil, err + } + + // ensure the loaded plugin can dispense the required prefix implementation + raw, err := rpcClient.Dispense("configmanager_token_store") + if err != nil { + client.Kill() + return nil, err + } + + ts := raw.(plugins.TokenStore) + + p := &Plugin{ + ClientCleanUp: client.Kill, + tokenStore: ts, + } + return p, nil +} + +func (p *Plugin) GetValue(token *config.ParsedTokenConfig) (string, error) { + result, err := p.tokenStore.Value(token.StoreToken(), []byte(token.Metadata())) + if err != nil { + return "", err + } + return result, nil +} diff --git a/internal/store/plugin_test.go b/internal/store/plugin_test.go new file mode 100644 index 0000000..f646841 --- /dev/null +++ b/internal/store/plugin_test.go @@ -0,0 +1,34 @@ +package store_test + +import ( + "context" + "os" + "testing" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/internal/store" +) + +func TestPlugin_GetValue_integration(t *testing.T) { + // as the plugin is technically a subprocess + // setting env vars at this level will affect the loaded plugin + os.Setenv("AWS_REGION", "eu-west-1") + os.Setenv("AWS_PROFILE", "PROFILE_TO_USE") + np, err := store.New(context.TODO(), "../../plugins/awsparamstr/bin/awsparamstr", config.ParamStorePrefix) + if err != nil { + t.Fatal(err) + } + defer np.ClientCleanUp() + token, err := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + if err != nil { + t.Fatal(err) + } + token.WithSanitizedToken("/int-test/pocketbase/admin-pwd") + got, err := np.GetValue(token) + if err != nil { + t.Fatal(err) + } + if got == "" { + t.Error("empty...") + } +} diff --git a/internal/store/secretsmanager.go b/internal/store/secretsmanager.go deleted file mode 100644 index 6744d8a..0000000 --- a/internal/store/secretsmanager.go +++ /dev/null @@ -1,93 +0,0 @@ -package store - -import ( - "context" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/aws/aws-sdk-go-v2/aws" - awsconf "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/secretsmanager" -) - -type secretsMgrApi interface { - GetSecretValue(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) -} - -type SecretsMgr struct { - svc secretsMgrApi - ctx context.Context - logger log.ILogger - config *SecretsMgrConfig - token *config.ParsedTokenConfig -} - -type SecretsMgrConfig struct { - Version string `json:"version"` -} - -func NewSecretsMgr(ctx context.Context, logger log.ILogger) (*SecretsMgr, error) { - cfg, err := awsconf.LoadDefaultConfig(ctx) - if err != nil { - logger.Error("unable to load SDK config, %v\n%w", err, ErrClientInitialization) - return nil, err - } - c := secretsmanager.NewFromConfig(cfg) - - return &SecretsMgr{ - svc: c, - logger: logger, - ctx: ctx, - }, nil - -} - -func (s *SecretsMgr) WithSvc(svc secretsMgrApi) { - s.svc = svc -} - -func (imp *SecretsMgr) SetToken(token *config.ParsedTokenConfig) { - storeConf := &SecretsMgrConfig{} - if err := token.ParseMetadata(storeConf); err != nil { - imp.logger.Error("parse token error %v", err) - } - imp.token = token - imp.config = storeConf -} - -func (imp *SecretsMgr) Value() (string, error) { - imp.logger.Info("Concrete implementation SecretsManager") - imp.logger.Debug("SecretsManager Token: %s", imp.token.String()) - - version := "AWSCURRENT" - if imp.config.Version != "" { - version = imp.config.Version - } - - imp.logger.Info("Getting Secret: %s @version: %s", imp.token, version) - - input := &secretsmanager.GetSecretValueInput{ - SecretId: aws.String(imp.token.StoreToken()), - VersionStage: aws.String(version), - } - - ctx, cancel := context.WithCancel(imp.ctx) - defer cancel() - - result, err := imp.svc.GetSecretValue(ctx, input) - if err != nil { - imp.logger.Error(implementationNetworkErr, imp.token.Prefix(), err, imp.token.String()) - return "", err - } - - if result.SecretString != nil { - return *result.SecretString, nil - } - - if len(result.SecretBinary) > 0 { - return string(result.SecretBinary), nil - } - - imp.logger.Error("value retrieved but empty for token: %v", imp.token) - return "", nil -} diff --git a/internal/store/secretsmanager_test.go b/internal/store/secretsmanager_test.go deleted file mode 100644 index 870bb75..0000000 --- a/internal/store/secretsmanager_test.go +++ /dev/null @@ -1,154 +0,0 @@ -package store_test - -import ( - "context" - "fmt" - "io" - "strings" - "testing" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" - "github.com/aws/aws-sdk-go-v2/service/secretsmanager" -) - -type mockSecretsApi func(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) - -func (m mockSecretsApi) GetSecretValue(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { - return m(ctx, params, optFns...) -} - -func awsSecretsMgrGetChecker(t *testing.T, params *secretsmanager.GetSecretValueInput) { - if params.VersionStage == nil { - t.Fatal("expect name to not be nil") - } - - if strings.Contains(*params.SecretId, "#") { - t.Errorf("incorrectly stripped token separator") - } - - if strings.Contains(*params.SecretId, string(config.SecretMgrPrefix)) { - t.Errorf("incorrectly stripped prefix") - } -} - -func Test_GetSecretMgr(t *testing.T) { - - tsuccessSecret := "dsgkbdsf" - - tests := map[string]struct { - token func() *config.ParsedTokenConfig - expect string - mockClient func(t *testing.T) mockSecretsApi - }{ - "success": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.SecretMgrPrefix, *config.NewConfig()) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, tsuccessSecret, func(t *testing.T) mockSecretsApi { - return mockSecretsApi(func(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { - t.Helper() - awsSecretsMgrGetChecker(t, params) - return &secretsmanager.GetSecretValueOutput{ - SecretString: &tsuccessSecret, - }, nil - }) - }, - }, - "success with version": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.SecretMgrPrefix, *config.NewConfig().WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("version=123") - return tkn - }, - tsuccessSecret, func(t *testing.T) mockSecretsApi { - return mockSecretsApi(func(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { - t.Helper() - awsSecretsMgrGetChecker(t, params) - return &secretsmanager.GetSecretValueOutput{ - SecretString: &tsuccessSecret, - }, nil - }) - }, - }, - "success with binary": { - func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.SecretMgrPrefix, *config.NewConfig().WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - tsuccessSecret, func(t *testing.T) mockSecretsApi { - return mockSecretsApi(func(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { - t.Helper() - awsSecretsMgrGetChecker(t, params) - return &secretsmanager.GetSecretValueOutput{ - SecretBinary: []byte(tsuccessSecret), - }, nil - }) - }, - }, - "errored": { - func() *config.ParsedTokenConfig { - // "AWSSECRETS#/token/1", "|", "#", - tkn, _ := config.NewToken(config.SecretMgrPrefix, *config.NewConfig().WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("") - return tkn - }, - "unable to retrieve secret", func(t *testing.T) mockSecretsApi { - return mockSecretsApi(func(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { - t.Helper() - awsSecretsMgrGetChecker(t, params) - return nil, fmt.Errorf("unable to retrieve secret") - }) - }, - }, - "ok but empty": { - func() *config.ParsedTokenConfig { - // "AWSSECRETS#/token/1", "|", "#", - tkn, _ := config.NewToken(config.SecretMgrPrefix, *config.NewConfig().WithTokenSeparator("#")) - tkn.WithSanitizedToken("/token/1") - tkn.WithKeyPath("") - tkn.WithMetadata("version=123") - return tkn - }, - "", func(t *testing.T) mockSecretsApi { - return mockSecretsApi(func(ctx context.Context, params *secretsmanager.GetSecretValueInput, optFns ...func(*secretsmanager.Options)) (*secretsmanager.GetSecretValueOutput, error) { - t.Helper() - awsSecretsMgrGetChecker(t, params) - return &secretsmanager.GetSecretValueOutput{ - SecretString: nil, - }, nil - }) - }, - }, - } - for name, tt := range tests { - t.Run(name, func(t *testing.T) { - impl, _ := store.NewSecretsMgr(context.TODO(), log.New(io.Discard)) - impl.WithSvc(tt.mockClient(t)) - - impl.SetToken(tt.token()) - got, err := impl.Value() - if err != nil { - if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) - } - return - } - if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) - } - }) - } -} diff --git a/internal/store/store.go b/internal/store/store.go index eceeb45..a4fdbc0 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -22,6 +22,6 @@ var ( type Strategy interface { // Value retrieves the underlying value for the token Value() (s string, e error) - // SetToken + // SetToken SetToken(s *config.ParsedTokenConfig) } diff --git a/internal/strategy/strategy.go b/internal/strategy/strategy.go index ac19a30..7aa8345 100644 --- a/internal/strategy/strategy.go +++ b/internal/strategy/strategy.go @@ -94,27 +94,27 @@ func (tr *TokenResponse) Value() string { func defaultStrategyFuncMap(logger log.ILogger) StrategyFuncMap { return map[config.ImplementationPrefix]StrategyFunc{ - config.AzTableStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - return store.NewAzTableStore(ctx, token, logger) - }, - config.AzAppConfigPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - return store.NewAzAppConf(ctx, token, logger) - }, - config.GcpSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - return store.NewGcpSecrets(ctx, logger) - }, - config.SecretMgrPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - return store.NewSecretsMgr(ctx, logger) - }, - config.ParamStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - return store.NewParamStore(ctx, logger) - }, - config.AzKeyVaultSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - return store.NewKvScrtStore(ctx, token, logger) - }, - config.HashicorpVaultPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - return store.NewVaultStore(ctx, token, logger) - }, + // config.AzTableStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { + // return store.NewAzTableStore(ctx, token, logger) + // }, + // config.AzAppConfigPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { + // return store.NewAzAppConf(ctx, token, logger) + // }, + // config.GcpSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { + // return store.NewGcpSecrets(ctx, logger) + // }, + // config.SecretMgrPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { + // return store.NewSecretsMgr(ctx, logger) + // }, + // config.ParamStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { + // return store.NewParamStore(ctx, logger) + // }, + // config.AzKeyVaultSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { + // return store.NewKvScrtStore(ctx, token, logger) + // }, + // config.HashicorpVaultPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { + // return store.NewVaultStore(ctx, token, logger) + // }, } } diff --git a/internal/strategy/strategy_test.go b/internal/strategy/strategy_test.go index acae5e1..c5fe3f7 100644 --- a/internal/strategy/strategy_test.go +++ b/internal/strategy/strategy_test.go @@ -2,9 +2,7 @@ package strategy_test import ( "context" - "fmt" "io" - "os" "testing" "github.com/DevLabFoundry/configmanager/v3/internal/config" @@ -12,7 +10,6 @@ import ( "github.com/DevLabFoundry/configmanager/v3/internal/store" "github.com/DevLabFoundry/configmanager/v3/internal/strategy" "github.com/DevLabFoundry/configmanager/v3/internal/testutils" - "github.com/go-test/deep" ) type mockGenerate struct { @@ -116,175 +113,175 @@ func Test_CustomStrategyFuncMap_add_own(t *testing.T) { } } -func Test_SelectImpl_With(t *testing.T) { +// func Test_SelectImpl_With(t *testing.T) { - ttests := map[string]struct { - setUpTearDown func() func() - token string - config *config.GenVarsConfig - expect func() store.Strategy - expErr error - impPrefix config.ImplementationPrefix - }{ - "unknown": { - func() func() { - return func() { - } - }, - "foo/bar", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { return nil }, - fmt.Errorf("implementation not found for input string: UNKNOWN#foo/bar"), - config.UnknownPrefix, - }, - "success AZTABLESTORE": { - func() func() { - os.Setenv("AZURE_stuff", "foo") - return func() { - os.Clearenv() - } - }, - "foo/bar1", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { - token, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("#")) - token.WithSanitizedToken("foo/bar1") +// ttests := map[string]struct { +// setUpTearDown func() func() +// token string +// config *config.GenVarsConfig +// expect func() store.Strategy +// expErr error +// impPrefix config.ImplementationPrefix +// }{ +// "unknown": { +// func() func() { +// return func() { +// } +// }, +// "foo/bar", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { return nil }, +// fmt.Errorf("implementation not found for input string: UNKNOWN#foo/bar"), +// config.UnknownPrefix, +// }, +// "success AZTABLESTORE": { +// func() func() { +// os.Setenv("AZURE_stuff", "foo") +// return func() { +// os.Clearenv() +// } +// }, +// "foo/bar1", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { +// token, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("#")) +// token.WithSanitizedToken("foo/bar1") - s, _ := store.NewAzTableStore(context.TODO(), token, log.New(io.Discard)) - return s - }, - nil, - config.AzTableStorePrefix, - }, - "success AWSPARAMSTR": { - func() func() { - os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") - os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") - return func() { - os.Clearenv() - } - }, - "foo/bar1", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { - s, _ := store.NewParamStore(context.TODO(), log.New(io.Discard)) - return s - }, - nil, - config.ParamStorePrefix, - }, - "success AWSSECRETS": { - func() func() { - os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") - os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") - return func() { - os.Clearenv() - } - }, - "foo/bar1", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { - s, _ := store.NewSecretsMgr(context.TODO(), log.New(io.Discard)) - return s - }, - nil, - config.SecretMgrPrefix, - }, - "success AZKVSECRET": { - func() func() { - os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") - os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") - return func() { - os.Clearenv() - } - }, - "foo/bar1", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { - token, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithTokenSeparator("#")) - token.WithSanitizedToken("foo/bar1") - s, _ := store.NewKvScrtStore(context.TODO(), token, log.New(io.Discard)) - return s - }, - nil, - config.AzKeyVaultSecretsPrefix, - }, - "success AZAPPCONF": { - func() func() { - return func() { - os.Clearenv() - } - }, - "foo/bar1", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { - token, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithTokenSeparator("#")) - token.WithSanitizedToken("foo/bar1") - s, _ := store.NewAzAppConf(context.TODO(), token, log.New(io.Discard)) - return s - }, - nil, - config.AzAppConfigPrefix, - }, - "success VAULT": { - func() func() { - os.Setenv("VAULT_", "AAAAAAAAAAAAAAA") - return func() { - os.Clearenv() - } - }, - "foo/bar1", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { - token, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig().WithTokenSeparator("#")) - token.WithSanitizedToken("foo/bar1") - s, _ := store.NewVaultStore(context.TODO(), token, log.New(io.Discard)) - return s - }, - nil, - config.HashicorpVaultPrefix, - }, - "success GCPSECRETS": { - func() func() { - cf, _ := os.CreateTemp(".", "*") - cf.Write(TEST_GCP_CREDS) - os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", cf.Name()) - return func() { - os.Remove(cf.Name()) - os.Clearenv() - } - }, - "foo/bar1", - config.NewConfig().WithTokenSeparator("#"), - func() store.Strategy { - s, _ := store.NewGcpSecrets(context.TODO(), log.New(io.Discard)) - return s - }, - nil, - config.GcpSecretsPrefix, - }, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - tearDown := tt.setUpTearDown() - defer tearDown() - want := tt.expect() - rs := strategy.New(*tt.config, log.New(io.Discard)) - token, _ := config.NewToken(tt.impPrefix, *tt.config) - token.WithSanitizedToken(tt.token) - got, err := rs.GetImplementation(context.TODO(), token) +// s, _ := store.NewAzTableStore(context.TODO(), token, log.New(io.Discard)) +// return s +// }, +// nil, +// config.AzTableStorePrefix, +// }, +// "success AWSPARAMSTR": { +// func() func() { +// os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") +// os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") +// return func() { +// os.Clearenv() +// } +// }, +// "foo/bar1", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { +// s, _ := store.NewParamStore(context.TODO(), log.New(io.Discard)) +// return s +// }, +// nil, +// config.ParamStorePrefix, +// }, +// "success AWSSECRETS": { +// func() func() { +// os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") +// os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") +// return func() { +// os.Clearenv() +// } +// }, +// "foo/bar1", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { +// s, _ := store.NewSecretsMgr(context.TODO(), log.New(io.Discard)) +// return s +// }, +// nil, +// config.SecretMgrPrefix, +// }, +// "success AZKVSECRET": { +// func() func() { +// os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") +// os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") +// return func() { +// os.Clearenv() +// } +// }, +// "foo/bar1", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { +// token, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithTokenSeparator("#")) +// token.WithSanitizedToken("foo/bar1") +// s, _ := store.NewKvScrtStore(context.TODO(), token, log.New(io.Discard)) +// return s +// }, +// nil, +// config.AzKeyVaultSecretsPrefix, +// }, +// "success AZAPPCONF": { +// func() func() { +// return func() { +// os.Clearenv() +// } +// }, +// "foo/bar1", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { +// token, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithTokenSeparator("#")) +// token.WithSanitizedToken("foo/bar1") +// s, _ := store.NewAzAppConf(context.TODO(), token, log.New(io.Discard)) +// return s +// }, +// nil, +// config.AzAppConfigPrefix, +// }, +// "success VAULT": { +// func() func() { +// os.Setenv("VAULT_", "AAAAAAAAAAAAAAA") +// return func() { +// os.Clearenv() +// } +// }, +// "foo/bar1", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { +// token, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig().WithTokenSeparator("#")) +// token.WithSanitizedToken("foo/bar1") +// s, _ := store.NewVaultStore(context.TODO(), token, log.New(io.Discard)) +// return s +// }, +// nil, +// config.HashicorpVaultPrefix, +// }, +// "success GCPSECRETS": { +// func() func() { +// cf, _ := os.CreateTemp(".", "*") +// cf.Write(TEST_GCP_CREDS) +// os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", cf.Name()) +// return func() { +// os.Remove(cf.Name()) +// os.Clearenv() +// } +// }, +// "foo/bar1", +// config.NewConfig().WithTokenSeparator("#"), +// func() store.Strategy { +// s, _ := store.NewGcpSecrets(context.TODO(), log.New(io.Discard)) +// return s +// }, +// nil, +// config.GcpSecretsPrefix, +// }, +// } +// for name, tt := range ttests { +// t.Run(name, func(t *testing.T) { +// tearDown := tt.setUpTearDown() +// defer tearDown() +// want := tt.expect() +// rs := strategy.New(*tt.config, log.New(io.Discard)) +// token, _ := config.NewToken(tt.impPrefix, *tt.config) +// token.WithSanitizedToken(tt.token) +// got, err := rs.GetImplementation(context.TODO(), token) - if err != nil { - if err.Error() != tt.expErr.Error() { - t.Errorf(testutils.TestPhraseWithContext, "uncaught error", err.Error(), tt.expErr.Error()) - } - return - } +// if err != nil { +// if err.Error() != tt.expErr.Error() { +// t.Errorf(testutils.TestPhraseWithContext, "uncaught error", err.Error(), tt.expErr.Error()) +// } +// return +// } - diff := deep.Equal(got, want) - if diff != nil { - t.Errorf(testutils.TestPhraseWithContext, "reflection of initialised implentations", fmt.Sprintf("%q", got), fmt.Sprintf("%q", want)) - } - }) - } -} +// diff := deep.Equal(got, want) +// if diff != nil { +// t.Errorf(testutils.TestPhraseWithContext, "reflection of initialised implentations", fmt.Sprintf("%q", got), fmt.Sprintf("%q", want)) +// } +// }) +// } +// } diff --git a/plugins/awsparams/paramstore.go b/plugins/awsparams/paramstore.go deleted file mode 100644 index 9001e53..0000000 --- a/plugins/awsparams/paramstore.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "context" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/plugins" - "github.com/aws/aws-sdk-go-v2/aws" - awsConf "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/ssm" -) - -type paramStoreApi interface { - GetParameter(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) -} - -type ParamStore struct { - svc paramStoreApi - ctx context.Context - config *ParamStrConfig - token *config.ParsedTokenConfig -} - -type ParamStrConfig struct { - // reserved for potential future use -} - -func NewParamStore(ctx context.Context) (*ParamStore, error) { - cfg, err := awsConf.LoadDefaultConfig(ctx) - if err != nil { - return nil, err - } - c := ssm.NewFromConfig(cfg) - - return &ParamStore{ - svc: c, - ctx: ctx, - }, nil -} - -func (s *ParamStore) WithSvc(svc paramStoreApi) { - s.svc = svc -} - -func (imp *ParamStore) Value(token *plugins.MessagExchange) (string, error) { - // imp.logger.Info("%s", "Concrete implementation ParameterStore") - // imp.logger.Info("ParamStore Token: %s", token.Token) - - input := &ssm.GetParameterInput{ - Name: aws.String(token.Token), - WithDecryption: aws.Bool(true), - } - ctx, cancel := context.WithCancel(imp.ctx) - defer cancel() - - result, err := imp.svc.GetParameter(ctx, input) - if err != nil { - // imp.logger.Error(plugins.ImplementationNetworkErr, config.ParamStorePrefix, err, token) - return "", err - } - - if result.Parameter.Value != nil { - return *result.Parameter.Value, nil - } - // imp.logger.Error("value retrieved but empty for token: %v", imp.token) - return "", nil -} diff --git a/plugins/awsparams/main.go b/plugins/awsparamstr/README.md similarity index 91% rename from plugins/awsparams/main.go rename to plugins/awsparamstr/README.md index 4bd4ec6..cc07402 100644 --- a/plugins/awsparams/main.go +++ b/plugins/awsparamstr/README.md @@ -1,5 +1,13 @@ -package main +# AWS PARAM STORE Plugin +This is the `awsparamstr` implementation plugin built using the gp-plugin architecture from hashicorp... + + +## Alternate architecture + +Explored an alternate architecture using WASIP1 + +```go import ( "context" "encoding/binary" @@ -199,5 +207,10 @@ func StrategyTokenValue(tokenPtr, tokenLen, outPtr, outCap, outLenPtr uint32) in // main is required for wasip1 // scaffolds the `_initialize` method func main() {} +``` + +### Build notes + +build using the `-buildmode=c-shared` which will convert the module to a reactor module -// GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o awsparams.wasm +`GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o awsparams.wasm` diff --git a/internal/store/paramstore.go b/plugins/awsparamstr/impl/paramstore.go similarity index 69% rename from internal/store/paramstore.go rename to plugins/awsparamstr/impl/paramstore.go index aa45ace..6255252 100644 --- a/internal/store/paramstore.go +++ b/plugins/awsparamstr/impl/paramstore.go @@ -1,10 +1,11 @@ -package store +package impl import ( "context" "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/plugins" "github.com/aws/aws-sdk-go-v2/aws" awsConf "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ssm" @@ -17,9 +18,9 @@ type paramStoreApi interface { type ParamStore struct { svc paramStoreApi ctx context.Context - logger log.ILogger config *ParamStrConfig token *config.ParsedTokenConfig + logger log.ILogger } type ParamStrConfig struct { @@ -29,7 +30,6 @@ type ParamStrConfig struct { func NewParamStore(ctx context.Context, logger log.ILogger) (*ParamStore, error) { cfg, err := awsConf.LoadDefaultConfig(ctx) if err != nil { - logger.Error("unable to load SDK config, %v\n%w", err, ErrClientInitialization) return nil, err } c := ssm.NewFromConfig(cfg) @@ -45,19 +45,12 @@ func (s *ParamStore) WithSvc(svc paramStoreApi) { s.svc = svc } -func (imp *ParamStore) SetToken(token *config.ParsedTokenConfig) { - storeConf := &ParamStrConfig{} - _ = token.ParseMetadata(storeConf) - imp.token = token - imp.config = storeConf -} - -func (imp *ParamStore) Value() (string, error) { - imp.logger.Info("%s", "Concrete implementation ParameterStore") - imp.logger.Info("ParamStore Token: %s", imp.token.String()) +func (imp *ParamStore) Value(token string, metadata []byte) (string, error) { + imp.logger.Info("Concrete implementation ParameterStore") + imp.logger.Info("ParamStore Token: %s", token) input := &ssm.GetParameterInput{ - Name: aws.String(imp.token.StoreToken()), + Name: aws.String(token), WithDecryption: aws.Bool(true), } ctx, cancel := context.WithCancel(imp.ctx) @@ -65,7 +58,7 @@ func (imp *ParamStore) Value() (string, error) { result, err := imp.svc.GetParameter(ctx, input) if err != nil { - imp.logger.Error(implementationNetworkErr, config.ParamStorePrefix, err, imp.token.StoreToken()) + imp.logger.Error(plugins.ImplementationNetworkErr, config.ParamStorePrefix, err, token) return "", err } diff --git a/plugins/awsparams/paramstore_test.go b/plugins/awsparamstr/impl/paramstore_test.go similarity index 95% rename from plugins/awsparams/paramstore_test.go rename to plugins/awsparamstr/impl/paramstore_test.go index 84a0d23..cc64dfc 100644 --- a/plugins/awsparams/paramstore_test.go +++ b/plugins/awsparamstr/impl/paramstore_test.go @@ -1,4 +1,4 @@ -package main_test +package impl_test import ( "context" @@ -9,8 +9,8 @@ import ( "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" "github.com/DevLabFoundry/configmanager/v3/internal/testutils" + "github.com/DevLabFoundry/configmanager/v3/plugins/awsparamstr/impl" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/ssm/types" ) @@ -131,13 +131,13 @@ func Test_GetParamStore(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { - impl, err := store.NewParamStore(context.TODO(), log.New(io.Discard)) + impl, err := impl.NewParamStore(context.TODO(), log.New(io.Discard)) if err != nil { t.Errorf(testutils.TestPhrase, err.Error(), nil) } impl.WithSvc(tt.mockClient(t)) - impl.SetToken(tt.token()) - got, err := impl.Value() + + got, err := impl.Value(tt.token().StoreToken(), []byte{}) if err != nil { if err.Error() != tt.expect { t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) diff --git a/plugins/awsparamstr/main.go b/plugins/awsparamstr/main.go new file mode 100644 index 0000000..c4b340f --- /dev/null +++ b/plugins/awsparamstr/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "context" + "os" + + "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager/v3/plugins/awsparamstr/impl" + "github.com/hashicorp/go-plugin" +) + +// Here is a real implementation of KV that writes to a local file with +// the key name and the contents are the value of the key. +type TokenStorePlugin struct{} + +func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { + srv, err := impl.NewParamStore(context.Background(), log.New(os.Stderr)) + if err != nil { + return "", err + } + return srv.Value(key, metadata) +} + +func main() { + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: plugins.Handshake, + Plugins: map[string]plugin.Plugin{ + "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: &TokenStorePlugin{}}, + }, + // A non-nil value here enables gRPC serving for this plugin... + GRPCServer: plugin.DefaultGRPCServer, + }) +} diff --git a/plugins/awssecrets/main.go b/plugins/awssecrets/main.go deleted file mode 100644 index 8a03b12..0000000 --- a/plugins/awssecrets/main.go +++ /dev/null @@ -1,197 +0,0 @@ -package main - -import ( - "encoding/binary" - "strings" - "sync" - "unicode/utf8" - "unsafe" - - "github.com/DevLabFoundry/configmanager/v3/plugins" -) - -// ==================== -// Bump allocator -// ==================== - -const heapSize = 64 * 1024 // 64 KiB arena; tune as needed - -type bumpAllocator struct { - mu sync.Mutex - heap []byte - used uint32 -} - -var alloc = bumpAllocator{ - heap: make([]byte, heapSize), -} - -// round allocation up to 8 bytes for basic alignment. -func roundUp(n uint32) uint32 { - const align = 8 - return (n + align - 1) &^ (align - 1) -} - -//go:wasmexport allocate -func Allocate(size uint32) uint32 { - if size == 0 { - return 0 - } - size = roundUp(size) - - alloc.mu.Lock() - defer alloc.mu.Unlock() - - if alloc.used+size > uint32(len(alloc.heap)) { - // Out of memory in our arena. - return 0 - } - - offset := alloc.used - alloc.used += size - - // Return pointer into linear memory for &heap[offset]. - return uint32(uintptr(unsafe.Pointer(&alloc.heap[offset]))) -} - -//go:wasmexport deallocate -func Deallocate(ptr, size uint32) { - // For a simple bump allocator, deallocate is a no-op. - // Memory is reclaimed when the module instance is destroyed. - _ = ptr - _ = size -} - -type Hdr struct { - Data uintptr - Len int - Cap int -} - -// ==================== -// Helpers -// ==================== - -// bytesFromPtrLen reinterprets a (ptr,len) pair in wasm linear memory -// as a Go []byte without copying. -func bytesFromPtrLen(ptr, length uint32) []byte { - if length == 0 { - return nil - } - - hdr := Hdr{ - Data: uintptr(ptr), - Len: int(length), - Cap: int(length), - } - - return *(*[]byte)(unsafe.Pointer(&hdr)) -} - -// ==================== -// strategy_token_value -// ==================== -// -// ABI: -// -// strategy_token_value( -// inPtr, inLen, outPtr, outCap, outLenPtr uint32, -// ) int32 -// -// Host contract: -// - Input bytes are at (inPtr, inLen) -// - Output buffer is [outPtr : outPtr+outCap) -// - outLenPtr points to 4 bytes where we write the required length -// -// Behaviour: -// - If input length == 0 => ERR_EMPTY_INPUT -// - If invalid UTF-8 => ERR_INVALID_UTF8 -// - Always write required length to *outLenPtr (little-endian) -// - If required > outCap => ERR_BUF_TOO_SMALL -// - Else copy into caller buffer and return OK -// -//go:wasmexport strategy_token_value -func StrategyTokenValue(tokenPtr, tokenLen, metadataStrPtr, metadataStrLen, outPtr, outCap, outLenPtr uint32) int32 { - defer func() { - // Make sure panics don't leak as traps. - if r := recover(); r != nil { - if outLenPtr != 0 { - if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { - binary.LittleEndian.PutUint32(lenCell, 0) - } - } - } - }() - - if tokenLen == 0 { - // Mark required length as 0 and signal error. - if outLenPtr != 0 { - if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { - binary.LittleEndian.PutUint32(lenCell, 0) - } - } - return plugins.ERR_EMPTY_INPUT - } - - tokenBytes := bytesFromPtrLen(tokenPtr, tokenLen) - if !utf8.Valid(tokenBytes) { - if outLenPtr != 0 { - if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { - binary.LittleEndian.PutUint32(lenCell, uint32(len(tokenBytes))) - } - } - return plugins.ERR_INVALID_UTF8 - } - - if metadataStrLen > 0 { - metadataStrBytes := bytesFromPtrLen(metadataStrPtr, metadataStrLen) - if !utf8.Valid(metadataStrBytes) { - if outLenPtr != 0 { - if lenCell := bytesFromPtrLen(metadataStrPtr, 4); len(lenCell) == 4 { - binary.LittleEndian.PutUint32(lenCell, uint32(len(tokenBytes))) - } - } - return plugins.ERR_INVALID_UTF8 - } - } - - // --- Business logic (replace with your real token strategy) --- - - inStr := string(tokenBytes) - outStr := "TOKEN_VALUE:" + strings.ToUpper(inStr) - outBytes := []byte(outStr) - // -------------------------------------------------------------- - - required := uint32(len(outBytes)) - - // Always write required length. - if outLenPtr != 0 { - lenCell := bytesFromPtrLen(outLenPtr, 4) - if len(lenCell) != 4 { - return plugins.ERR_INTERNAL - } - binary.LittleEndian.PutUint32(lenCell, required) - } - - if required > outCap { - return plugins.ERR_BUF_TOO_SMALL - } - - if required == 0 { - return plugins.OK - } - - outSlice := bytesFromPtrLen(outPtr, outCap) - if uint32(len(outSlice)) < required { - return plugins.ERR_INTERNAL - } - - copy(outSlice, outBytes) - return plugins.OK -} - -// main is required for wasip1 -// scaffolds the `_instantiate` method -func main() {} - -// GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o awssecrets.wasm diff --git a/plugins/grpc.go b/plugins/grpc.go index 9d7ffc5..5f9ebf0 100644 --- a/plugins/grpc.go +++ b/plugins/grpc.go @@ -9,7 +9,7 @@ import ( // GRPCClient is an implementation of KV that talks over RPC. type GRPCClient struct{ client proto.TokenStoreClient } -func (m *GRPCClient) Get(key string, metadata []byte) (string, error) { +func (m *GRPCClient) Value(key string, metadata []byte) (string, error) { resp, err := m.client.Value(context.Background(), &proto.TokenValueRequest{ Token: key, Metadata: metadata, @@ -27,9 +27,9 @@ type GRPCServer struct { Impl TokenStore } -func (m *GRPCServer) Get( +func (m *GRPCServer) Value( ctx context.Context, req *proto.TokenValueRequest) (*proto.TokenValueResponse, error) { - v, err := m.Impl.Get(req.) - return &proto.GetResponse{Value: v}, err + v, err := m.Impl.Value(req.Token, req.Metadata) + return &proto.TokenValueResponse{Value: v}, err } diff --git a/plugins/interface.go b/plugins/interface.go index ee33294..5a3cd1b 100644 --- a/plugins/interface.go +++ b/plugins/interface.go @@ -5,34 +5,26 @@ import ( "google.golang.org/grpc" + "github.com/DevLabFoundry/configmanager/v3/plugins/proto" "github.com/hashicorp/go-plugin" - "github.com/hashicorp/go-plugin/examples/grpc/proto" ) // Handshake is a common handshake that is shared by plugin and host. var Handshake = plugin.HandshakeConfig{ // This isn't required when using VersionedPlugins ProtocolVersion: 1, - MagicCookieKey: "BASIC_PLUGIN", + MagicCookieKey: "CONFIGMANAGER_PLUGIN", MagicCookieValue: "hello", } -// PluginMap is the map of plugins we can dispense. +// // PluginMap is the map of plugins we can dispense. var PluginMap = map[string]plugin.Plugin{ - "kv_grpc": &TokenStoreGRPCPlugin{}, - // "kv": &KVPlugin{}, + "configmanager_token_store": &TokenStoreGRPCPlugin{}, } // TokenStore is the interface that we're exposing as a plugin. type TokenStore interface { - Get(token string, metadata []byte) (string, error) -} - -// This is the implementation of plugin.Plugin so we can serve/consume this. -type TokenStorePlugin struct { - // Concrete implementation, written in Go. This is only used for plugins - // that are written in Go. - Impl TokenStore + Value(token string, metadata []byte) (string, error) } // This is the implementation of plugin.GRPCPlugin so we can serve/consume this. @@ -45,10 +37,10 @@ type TokenStoreGRPCPlugin struct { } func (p *TokenStoreGRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { - proto.RegisterKVServer(s, &GRPCServer{Impl: p.Impl}) + proto.RegisterTokenStoreServer(s, &GRPCServer{Impl: p.Impl}) return nil } func (p *TokenStoreGRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { - return &GRPCClient{client: proto(c)}, nil + return &GRPCClient{client: proto.NewTokenStoreClient(c)}, nil } diff --git a/plugins/scaffolding.go b/plugins/scaffolding.go index f4bdce0..f317496 100644 --- a/plugins/scaffolding.go +++ b/plugins/scaffolding.go @@ -22,9 +22,3 @@ var ( ErrEmptyResponse = errors.New("value retrieved but empty for token") ErrServiceCallFailed = errors.New("failed to complete the service call") ) - -type MessagExchange struct { - Token string `json:"token"` - Metadata map[string]any `json:"metadata,omitempty"` - Version string `json:"version"` -} From 6f0a3ba8df9170c594fb03694b2ba8923d53b3ce Mon Sep 17 00:00:00 2001 From: dnitsch Date: Mon, 8 Dec 2025 15:10:51 +0000 Subject: [PATCH 06/15] fix: add init as wip element --- .gitignore | 7 +- .trivyignore.yaml | 7 + Dockerfile | 6 + eirctl.yaml | 54 ++- generator/generator.go | 88 ++-- generator/generatorvars.go | 22 + go.mod | 87 ++-- go.sum | 252 +++-------- internal/store/plugin.go | 12 +- internal/store/plugin_test.go | 14 +- internal/store/store.go | 97 +++- internal/strategy/strategy.go | 250 ++++++----- internal/strategy/strategy_test.go | 200 ++++----- plugins/README.md | 240 ++++++++++ plugins/awsparamstr/README.md | 214 +-------- plugins/awssecrets/.gitkeep | 0 plugins/grpc.go | 3 +- plugins/vault/README.md | 3 + plugins/vault/impl/hashivault.go | 173 +++++++ plugins/vault/impl/hashivault_test.go | 624 ++++++++++++++++++++++++++ plugins/vault/main.go | 34 ++ 21 files changed, 1649 insertions(+), 738 deletions(-) create mode 100644 .trivyignore.yaml create mode 100644 plugins/README.md create mode 100644 plugins/awssecrets/.gitkeep create mode 100644 plugins/vault/README.md create mode 100644 plugins/vault/impl/hashivault.go create mode 100644 plugins/vault/impl/hashivault_test.go create mode 100644 plugins/vault/main.go diff --git a/.gitignore b/.gitignore index 81bfbd1..0c0758a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,13 +7,18 @@ # Go vendor -bin dist .deps/ +# generated +bin +plugins/**/bin + # tests .coverage # local testers and local/ .bin +.configmanager +.trivy/fs-sbom-file.json diff --git a/.trivyignore.yaml b/.trivyignore.yaml new file mode 100644 index 0000000..31c34bb --- /dev/null +++ b/.trivyignore.yaml @@ -0,0 +1,7 @@ +vulnerabilities: + +secrets: + +misconfigurations: + +licenses: diff --git a/Dockerfile b/Dockerfile index 5871513..ba98147 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,4 +17,10 @@ FROM docker.io/alpine:3 COPY --from=builder /app/bin/configmanager /usr/bin/configmanager +RUN chmod +x /usr/bin/configmanager + +RUN adduser -D -s /bin/sh -h /home/configmanager configmanager + +USER configmanager + ENTRYPOINT ["configmanager"] diff --git a/eirctl.yaml b/eirctl.yaml index 7c64937..22db6e4 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -4,7 +4,8 @@ output: prefixed debug: false import: - - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.3/shared/build/go/eirctl.yaml + - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.7/shared/build/go/eirctl.yaml + - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.7/shared/security/eirctl.yaml contexts: bash: @@ -51,6 +52,19 @@ pipelines: - task: proto:generate depends_on: proto:install + build:plugins: + - task: go:build:plugin + name: awsparamstr + env: + PLUGIN: awsparamstr + - task: go:build:plugin + name: vault + env: + PLUGIN: vault + + scan:plugins: + - task: trivy:file:system:sbom + tasks: show:coverage: description: Opens the current coverage viewer for the the configmanager utility. @@ -66,6 +80,44 @@ tasks: open http://localhost:6060/pkg/github.com/DevLabFoundry/configmanager/v2/?m=all godoc -notes "BUG|TODO" -play -http=:6060 + go:build:plugin: + context: go1xalpine + command: + - | + mkdir -p .deps + unset GOTOOLCHAIN + ldflags="-s -w -extldflags -static" + GOPATH=/eirctl/.deps GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} CGO_ENABLED=0 go build -mod=readonly -buildvcs=false -ldflags="$ldflags" \ + -o ./plugins/$PLUGIN/bin/$PLUGIN-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX} ./plugins/$PLUGIN/main.go + echo "---" + echo "Built: $PLUGIN-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX}" + reset_context: true + variations: + - BUILD_GOOS: darwin + BUILD_GOARCH: amd64 + BUILD_SUFFIX: "" + - BUILD_GOOS: darwin + BUILD_GOARCH: arm64 + BUILD_SUFFIX: "" + - BUILD_GOOS: linux + BUILD_GOARCH: amd64 + BUILD_SUFFIX: "" + - BUILD_GOOS: linux + BUILD_GOARCH: arm64 + BUILD_SUFFIX: "" + - BUILD_GOOS: windows + BUILD_GOARCH: amd64 + BUILD_SUFFIX: ".exe" + - BUILD_GOOS: windows + BUILD_GOARCH: arm64 + BUILD_SUFFIX: ".exe" + - BUILD_GOOS: windows + BUILD_GOARCH: "386" + BUILD_SUFFIX: ".exe" + required: + env: + - PLUGIN + go:build:binary: context: go1x description: | diff --git a/generator/generator.go b/generator/generator.go index 8dcef19..ca3243f 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -14,7 +14,7 @@ import ( "github.com/DevLabFoundry/configmanager/v3/internal/lexer" "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/DevLabFoundry/configmanager/v3/internal/parser" - "github.com/DevLabFoundry/configmanager/v3/internal/strategy" + "github.com/DevLabFoundry/configmanager/v3/internal/store" ) // Generator is the main struct holding the @@ -24,10 +24,11 @@ import ( // which wil be passed in a loop into a goroutine to perform the // relevant strategy network calls to the config store implementations type Generator struct { - Logger log.ILogger - strategy strategy.StrategyFuncMap - ctx context.Context - config config.GenVarsConfig + Logger log.ILogger + // strategy strategy.StrategyFuncMap + store *store.Store + ctx context.Context + config config.GenVarsConfig } type Opts func(*Generator) @@ -48,7 +49,7 @@ func new(ctx context.Context, opts ...Opts) *Generator { // return using default config config: *conf, } - g.strategy = nil + // g.strategy = nil // now apply additional opts for _, o := range opts { @@ -58,13 +59,13 @@ func new(ctx context.Context, opts ...Opts) *Generator { return g } -// WithStrategyMap -// -// Adds addtional funcs for storageRetrieval used for testing only -func (c *Generator) WithStrategyMap(sm strategy.StrategyFuncMap) *Generator { - c.strategy = sm - return c -} +// // WithStrategyMap +// // +// // Adds addtional funcs for storageRetrieval used for testing only +// func (c *Generator) WithStrategyMap(sm strategy.StrategyFuncMap) *Generator { +// c.strategy = sm +// return c +// } // WithConfig uses custom config func (c *Generator) WithConfig(cfg *config.GenVarsConfig) *Generator { @@ -97,6 +98,14 @@ func (c *Generator) Generate(tokens []string) (ReplacedToken, error) { return nil, err } + // initialise pugins here based on discovered tokens + // + s, err := store.Init(c.ctx, ntm.TokenSet()) + if err != nil { + return nil, err + } + + c.store = s // pass in default initialised retrieveStrategy // input should be rt, err := c.generate(ntm) @@ -145,14 +154,15 @@ func IsParsed(v any, trm ReplacedToken) bool { // It then denormalizes the NormalizedTokenSafe back to a ReplacedToken map // which stores the values for each token to be returned to the caller func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { - if len(ntm.normalizedTokenMap) < 1 { + if len(ntm.m) < 1 { c.Logger.Debug("no replaceable tokens found in input") return nil, nil } wg := &sync.WaitGroup{} - s := strategy.New(c.config, c.Logger, strategy.WithStrategyFuncMap(c.strategy)) + // initialise the stores here + // s := strategy.New(c.config, c.Logger, strategy.WithStrategyFuncMap(c.strategy)) // safe read of normalized token map // this will ensure that we are minimizing @@ -164,13 +174,20 @@ func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { } token := prsdTkn.parsedTokens[0] wg.Go(func() { - prsdTkn.resp = &strategy.TokenResponse{} - storeStrategy, err := s.GetImplementation(c.ctx, token) + prsdTkn.resp = &TokenResponse{} + prsdTkn.resp.WithKey(token) + storeStrategy, err := c.store.GetImplementation(token.Prefix()) + if err != nil { + prsdTkn.resp.Err = err + return + } + // storeStrategy.GetValue(token) + v, err := storeStrategy.GetValue(token) if err != nil { prsdTkn.resp.Err = err return } - prsdTkn.resp = strategy.ExchangeToken(storeStrategy, token) + prsdTkn.resp.WithValue(v) }) } @@ -200,7 +217,9 @@ func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { // The idea is to minimize the number of networks calls to the underlying `store` Implementations // // The merging is based on the implemenentation and sanitized token being the same, -// if the token contains metadata then it must be +// if the token contains metadata then it must be stored uniquely even if the underlying store is the same. +// This is because a token with metadata must be called uniquely +// as it may contain different versions of the same token - hence the value would be different // // # Merging strategy // @@ -209,7 +228,7 @@ type NormalizedToken struct { // all the tokens that can be used to do a replacement parsedTokens []*config.ParsedTokenConfig // will be assigned post generate - resp *strategy.TokenResponse + resp *TokenResponse // // configToken is the last assigned full config in the loop if multip // configToken *config.ParsedTokenConfig } @@ -222,36 +241,49 @@ func (n *NormalizedToken) WithParsedToken(v *config.ParsedTokenConfig) *Normaliz // NormalizedTokenSafe is the map of lowest common denominators // by token.Keypathless or token.String (full token) if metadata is included type NormalizedTokenSafe struct { - mu *sync.Mutex - normalizedTokenMap map[string]*NormalizedToken + mu *sync.Mutex + m map[string]*NormalizedToken + set map[string]struct{} } func (n NormalizedTokenSafe) GetMap() map[string]*NormalizedToken { n.mu.Lock() defer n.mu.Unlock() - return n.normalizedTokenMap + return n.m +} + +func (n NormalizedTokenSafe) TokenSet() []string { + n.mu.Lock() + defer n.mu.Unlock() + ss := []string{} + for key, _ := range n.set { + ss = append(ss, strings.ToLower(key)) + } + return ss } func (c *Generator) NormalizeRawToken(rtm *RawTokenConfig) NormalizedTokenSafe { - ntm := NormalizedTokenSafe{mu: &sync.Mutex{}, normalizedTokenMap: make(map[string]*NormalizedToken)} + ntm := NormalizedTokenSafe{mu: &sync.Mutex{}, m: make(map[string]*NormalizedToken), set: make(map[string]struct{})} for _, r := range rtm.RawTokenMap() { // if a string contains we need to store it uniquely // future improvements might group all the metadata values together if len(r.Metadata()) > 0 { - if n, found := ntm.normalizedTokenMap[r.String()]; found { + if n, found := ntm.m[r.String()]; found { n.WithParsedToken(r) continue } - ntm.normalizedTokenMap[r.String()] = (&NormalizedToken{}).WithParsedToken(r) + ntm.m[r.String()] = (&NormalizedToken{}).WithParsedToken(r) + ntm.set[string(r.Prefix())] = struct{}{} continue } - if n, found := ntm.normalizedTokenMap[r.Keypathless()]; found { + if n, found := ntm.m[r.Keypathless()]; found { n.WithParsedToken(r) continue } - ntm.normalizedTokenMap[r.Keypathless()] = (&NormalizedToken{}).WithParsedToken(r) + ntm.m[r.Keypathless()] = (&NormalizedToken{}).WithParsedToken(r) + ntm.set[string(r.Prefix())] = struct{}{} continue } return ntm diff --git a/generator/generatorvars.go b/generator/generatorvars.go index d2ac986..0e1bf8d 100644 --- a/generator/generatorvars.go +++ b/generator/generatorvars.go @@ -43,6 +43,28 @@ func (rtm *RawTokenConfig) RawTokenMap() map[string]*config.ParsedTokenConfig { return rtm.tokenMap } +type TokenResponse struct { + val string + key *config.ParsedTokenConfig + Err error +} + +func (tr *TokenResponse) WithKey(key *config.ParsedTokenConfig) { + tr.key = key +} + +func (tr *TokenResponse) WithValue(val string) { + tr.val = val +} + +func (tr *TokenResponse) Key() *config.ParsedTokenConfig { + return tr.key +} + +func (tr *TokenResponse) Value() string { + return tr.val +} + // keySeparatorLookup checks if the key contains // keySeparator character // If it does contain one then it tries to parse diff --git a/go.mod b/go.mod index 4049a5c..2c30ca2 100644 --- a/go.mod +++ b/go.mod @@ -1,70 +1,54 @@ module github.com/DevLabFoundry/configmanager/v3 -go 1.25.4 +go 1.25.5 require ( - cloud.google.com/go/secretmanager v1.11.4 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 - github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0 - github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1 - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 - github.com/aws/aws-sdk-go-v2 v1.40.0 - github.com/aws/aws-sdk-go-v2/config v1.32.0 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1 - github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3 + github.com/aws/aws-sdk-go-v2 v1.40.1 + github.com/aws/aws-sdk-go-v2/config v1.32.3 + github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5 github.com/go-test/deep v1.1.1 - github.com/googleapis/gax-go/v2 v2.12.0 github.com/hashicorp/vault/api v1.22.0 github.com/hashicorp/vault/api/auth/aws v0.11.0 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spyzhov/ajson v0.9.6 + google.golang.org/grpc v1.77.0 + google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/hashicorp/yamux v0.1.2 // indirect - github.com/oklog/run v1.1.0 // indirect - go.opencensus.io v0.24.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/oklog/run v1.2.0 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/stretchr/testify v1.11.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) require ( - cloud.google.com/go/compute/metadata v0.9.0 // indirect - cloud.google.com/go/iam v1.1.5 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/a8m/envsubst v1.4.3 github.com/aws/aws-sdk-go v1.55.8 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.0 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.3 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect - github.com/aws/smithy-go v1.23.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 // indirect + github.com/aws/smithy-go v1.24.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.6.3 + github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.6.2 + github.com/hashicorp/go-plugin v1.7.0 github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect @@ -75,33 +59,16 @@ require ( github.com/hashicorp/hcl v1.0.1-vault-7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/tetratelabs/wazero v1.10.1 - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect - golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.33.0 // indirect - golang.org/x/sync v0.18.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.14.0 // indirect - google.golang.org/api v0.155.0 // indirect - google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect - google.golang.org/grpc v1.61.2 - google.golang.org/protobuf v1.31.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect ) diff --git a/go.sum b/go.sum index 3008268..fa846d7 100644 --- a/go.sum +++ b/go.sum @@ -1,98 +1,52 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= -cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/secretmanager v1.11.4 h1:krnX9qpG2kR2fJ+u+uNyNo+ACVhplIAS4Pu7u+4gd+k= -cloud.google.com/go/secretmanager v1.11.4/go.mod h1:wreJlbS9Zdq21lMzWmJ0XhWW2ZxgPeahsqeV/vZoJ3w= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0 h1:uU4FujKFQAz31AbWOO3INV9qfIanHeIUSsGhRlcJJmg= -github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig v1.2.0/go.mod h1:qr3M3Oy6V98VR0c5tCHKUpaeJTRQh6KYzJewRtFWqfc= -github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1 h1:j0hhYS006eJ54vusoap0f2NVZ1YY3QnaAEnLM68f0SQ= -github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.4.1/go.mod h1:AdtInaXmK8eYmbjezRWgLz+Qs46nc9Up9GWGwteWNfw= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 h1:/g8S6wk65vfC6m3FIxJ+i5QDyN9JWwXI8Hb0Img10hU= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0/go.mod h1:gpl+q95AzZlKVI3xSoseF9QPrypk0hQqBiJYeB/cR/I= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= -github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= -github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY= github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU= github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= -github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc= -github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= -github.com/aws/aws-sdk-go-v2/config v1.32.0 h1:T5WWJYnam9SzBLbsVYDu2HscLDe+GU1AUJtfcDAc/vA= -github.com/aws/aws-sdk-go-v2/config v1.32.0/go.mod h1:pSRm/+D3TxBixGMXlgtX4+MPO9VNtEEtiFmNpxksoxw= -github.com/aws/aws-sdk-go-v2/credentials v1.19.0 h1:7zm+ez+qEqLaNsCSRaistkvJRJv8sByDOVuCnyHbP7M= -github.com/aws/aws-sdk-go-v2/credentials v1.19.0/go.mod h1:pHKPblrT7hqFGkNLxqoS3FlGoPrQg4hMIa+4asZzBfs= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM= +github.com/aws/aws-sdk-go-v2 v1.40.1 h1:difXb4maDZkRH0x//Qkwcfpdg1XQVXEAEs2DdXldFFc= +github.com/aws/aws-sdk-go-v2 v1.40.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/config v1.32.3 h1:cpz7H2uMNTDa0h/5CYL5dLUEzPSLo2g0NkbxTRJtSSU= +github.com/aws/aws-sdk-go-v2/config v1.32.3/go.mod h1:srtPKaJJe3McW6T/+GMBZyIPc+SeqJsNPJsd4mOYZ6s= +github.com/aws/aws-sdk-go-v2/credentials v1.19.3 h1:01Ym72hK43hjwDeJUfi1l2oYLXBAOR8gNSZNmXmvuas= +github.com/aws/aws-sdk-go-v2/credentials v1.19.3/go.mod h1:55nWF/Sr9Zvls0bGnWkRxUdhzKqj9uRNlPvgV1vgxKc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 h1:utxLraaifrSBkeyII9mIbVwXXWrZdlPO7FIKmyLCEcY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15/go.mod h1:hW6zjYUDQwfz3icf4g2O41PHi77u10oAzJ84iSzR/lo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 h1:Y5YXgygXwDI5P4RkteB5yF7v35neH7LfJKBG+hzIons= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15/go.mod h1:K+/1EpG42dFSY7CBj+Fruzm8PsCGWTXJ3jdeJ659oGQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 h1:AvltKnW9ewxX2hFmQS0FyJH93aSvJVUEFvXfU+HWtSE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15/go.mod h1:3I4oCdZdmgrREhU74qS1dK9yZ62yumob+58AbFR4cQA= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1 h1:w6a0H79HrHf3lr+zrw+pSzR5B+caiQFAKiNHlrUcnoc= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.1/go.mod h1:c6Vg0BRiU7v0MVhHupw90RyL120QBwAMLbDCzptGeMk= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 h1:BDgIUYGEo5TkayOWv/oBLPphWwNm/A91AebUjAu5L5g= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.1/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk= -github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3 h1:ofiQvKwka2E3T8FXBsU1iWj7Yvk2wd1p4ZCdS6qGiKQ= -github.com/aws/aws-sdk-go-v2/service/ssm v1.67.3/go.mod h1:+nlWvcgDPQ56mChEBzTC0puAMck+4onOFaHg5cE+Lgg= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 h1:U//SlnkE1wOQiIImxzdY5PXat4Wq+8rlfVEw4Y7J8as= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.4/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8 h1:MvlNs/f+9eM0mOjD9JzBUbf5jghyTk3p+O9yHMXX94Y= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.8/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0i9NT+JnqLTOI5nA= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso= -github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= -github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= -github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= -github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 h1:3/u/4yZOffg5jdNk1sDpOQ4Y+R6Xbh+GzpDrSZjuy3U= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15/go.mod h1:4Zkjq0FKjE78NKjabuM4tRXKFzUJWXgP0ItEZK8l7JU= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 h1:d/6xOGIllc/XW1lzG9a4AUBMmpLA9PXcQnVPTuHHcik= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.3/go.mod h1:fQ7E7Qj9GiW8y0ClD7cUJk3Bz5Iw8wZkWDHsTe8vDKs= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5 h1:YKGgwB1rye0JpV10Bfma3cZdQzX61j2HPWQw+YxWvrQ= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5/go.mod h1:eBDSa0vuYB0lalpNxavIw80Q4Ksy08bhHHbT0aWa4tE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 h1:8sTTiw+9yuNXcfWeqKF2x01GqCF49CpP4Z9nKrrk/ts= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.6/go.mod h1:8WYg+Y40Sn3X2hioaaWAAIngndR8n1XFdRPPX+7QBaM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 h1:E+KqWoVsSrj1tJ6I/fjDIu5xoS2Zacuu1zT+H7KtiIk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11/go.mod h1:qyWHz+4lvkXcr3+PoGlGHEI+3DLLiU6/GdrFfMaAhB0= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 h1:tzMkjh0yTChUqJDgGkcDdxvZDSrJ/WB6R6ymI5ehqJI= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.3/go.mod h1:T270C0R5sZNLbWUe8ueiAF42XSZxxPocTaGSgs5c/60= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -100,43 +54,12 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -147,8 +70,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog= -github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= @@ -173,16 +96,15 @@ github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8 github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= -github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= -github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= 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/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= @@ -190,8 +112,6 @@ 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/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -204,23 +124,22 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= +github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -233,20 +152,11 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8= -github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= @@ -257,89 +167,33 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6 go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 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.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= -google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3 h1:EWIeHfGuUf00zrVZGEgYFxok7plSAXBGcH7NNdMAWvA= -google.golang.org/genproto/googleapis/api v0.0.0-20231211222908-989df2bf70f3/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.61.2 h1:TzJay21lXCf7BiNFKl7mSskt5DlkKAumAYTs52SpJeo= -google.golang.org/grpc v1.61.2/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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= @@ -351,5 +205,3 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/store/plugin.go b/internal/store/plugin.go index ddfd739..ee6fe83 100644 --- a/internal/store/plugin.go +++ b/internal/store/plugin.go @@ -9,11 +9,8 @@ import ( "github.com/hashicorp/go-plugin" ) -// Plugin is responsible for managing plugins within configmanager -// -// It includes the following methods -// - fetch plugins from known sources -// - maintains a list of tokens answerable by a specified pluginEngine +// Plugin is responsible for managing the plugin lifecycle +// within the configmanager flow. Each Implementation will initialise exactly one instance of the plugin type Plugin struct { Implementations config.ImplementationPrefix SourcePath string @@ -22,8 +19,8 @@ type Plugin struct { tokenStore plugins.TokenStore } -// New Plugin gets called once per implementation -func New(ctx context.Context, path string, prefix config.ImplementationPrefix) (*Plugin, error) { +// NewPlugin Plugin gets called once per implementation +func NewPlugin(ctx context.Context, path string) (*Plugin, error) { // We're a host. Start by launching the plugin process. client := plugin.NewClient(&plugin.ClientConfig{ HandshakeConfig: plugins.Handshake, @@ -31,7 +28,6 @@ func New(ctx context.Context, path string, prefix config.ImplementationPrefix) ( Cmd: exec.Command(path), AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, }) - // Connect via RPC rpcClient, err := client.Client() if err != nil { diff --git a/internal/store/plugin_test.go b/internal/store/plugin_test.go index f646841..47a840a 100644 --- a/internal/store/plugin_test.go +++ b/internal/store/plugin_test.go @@ -2,33 +2,41 @@ package store_test import ( "context" + "fmt" "os" + "runtime" "testing" "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/store" ) +// TODO: make the implementation of the plugin system more testable func TestPlugin_GetValue_integration(t *testing.T) { + t.Skip() // as the plugin is technically a subprocess // setting env vars at this level will affect the loaded plugin os.Setenv("AWS_REGION", "eu-west-1") - os.Setenv("AWS_PROFILE", "PROFILE_TO_USE") - np, err := store.New(context.TODO(), "../../plugins/awsparamstr/bin/awsparamstr", config.ParamStorePrefix) + os.Setenv("AWS_PROFILE", "FOO") + tp := fmt.Sprintf("../../.configmanager/plugins/awsparamstr/awsparamstr-%s-%s", runtime.GOOS, runtime.GOARCH) + np, err := store.NewPlugin(context.TODO(), tp) if err != nil { t.Fatal(err) } + defer np.ClientCleanUp() token, err := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) if err != nil { t.Fatal(err) } + token.WithSanitizedToken("/int-test/pocketbase/admin-pwd") got, err := np.GetValue(token) if err != nil { t.Fatal(err) } - if got == "" { + + if len(got) < 1 { t.Error("empty...") } } diff --git a/internal/store/store.go b/internal/store/store.go index a4fdbc0..79ae798 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -1,18 +1,24 @@ package store import ( + "context" "errors" + "fmt" + "os" + "path" + "runtime" + "strings" + "sync" "github.com/DevLabFoundry/configmanager/v3/internal/config" ) -const implementationNetworkErr string = "implementation %s error: %v for token: %s" - var ( ErrRetrieveFailed = errors.New("failed to retrieve config item") ErrClientInitialization = errors.New("failed to initialize the client") ErrEmptyResponse = errors.New("value retrieved but empty for token") ErrServiceCallFailed = errors.New("failed to complete the service call") + ErrPluginNotFound = errors.New("plugin does not exist") ) // Strategy iface that all store implementations @@ -25,3 +31,90 @@ type Strategy interface { // SetToken SetToken(s *config.ParsedTokenConfig) } + +// +// It includes the following methods +// - fetch plugins from known sources +// - maintains a list of tokens answerable by a specified pluginEngine + +type pluginMap struct { + mu *sync.Mutex + // m holds the map of plugins where the key is the lowercased implementation prefix + // e.g. `AWSPARAMSTR://` => `awsparamstr` + m map[string]*Plugin +} + +func (p pluginMap) Add(key string, pl *Plugin) { + p.mu.Lock() + defer p.mu.Unlock() + p.m[key] = pl +} + +const ( + loc string = ".configmanager/plugins" + namePattern string = "%s-%s-%s" +) + +type Store struct { + pluginLocation []string + plugin pluginMap + // PluginCleanUp func() +} + +func Init(ctx context.Context, implt []string) (*Store, error) { + pm := pluginMap{mu: &sync.Mutex{}, m: make(map[string]*Plugin)} + + // l := []string{""} + // + for _, plugin := range implt { + plpath, err := findPlugin(plugin) + if err != nil { + return nil, err + } + p, err := NewPlugin(ctx, plpath) + pm.Add(plugin, p) + } + return &Store{plugin: pm}, nil +} + +func (s *Store) GetImplementation(implemenation config.ImplementationPrefix) (plugin *Plugin, err error) { + var exists bool + if plugin, exists = s.plugin.m[strings.ToLower(string(implemenation))]; exists { + return plugin, nil + } + return nil, ErrPluginNotFound +} + +// PluginCleanUp ensures the plugins are properly shut down +func (s *Store) PluginCleanUp() { + s.plugin.mu.Lock() + defer s.plugin.mu.Unlock() + for _, plugin := range s.plugin.m { + plugin.ClientCleanUp() + } +} + +// findPlugin ensures the path exists and search the following locations +// +// current dir +// home dir +func findPlugin(plugin string) (string, error) { + // fallback locations + // current dir + cwd, err := os.Getwd() + if err != nil { + return "", err + } + hd, err := os.UserHomeDir() + if err != nil { + return "", err + } + for _, p := range []string{cwd, hd} { + ff := path.Join(p, loc, plugin, fmt.Sprintf(namePattern, plugin, runtime.GOOS, runtime.GOARCH)) + if _, err := os.Stat(ff); err == nil { + // break on first non nil error + return ff, nil + } + } + return "", ErrPluginNotFound +} diff --git a/internal/strategy/strategy.go b/internal/strategy/strategy.go index 7aa8345..9548f8b 100644 --- a/internal/strategy/strategy.go +++ b/internal/strategy/strategy.go @@ -1,124 +1,132 @@ // Package strategy is a factory method wrapper around the backing store implementations package strategy -import ( - "context" - "errors" - "fmt" - "sync" - - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" -) - -var ErrTokenInvalid = errors.New("invalid token - cannot get prefix") - -// StrategyFunc -type StrategyFunc func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) - -// StrategyFuncMap -type StrategyFuncMap map[config.ImplementationPrefix]StrategyFunc - -type Strategy struct { - config config.GenVarsConfig - strategyFuncMap strategyFnMap -} - -type Opts func(*Strategy) - -// New -func New(config config.GenVarsConfig, logger log.ILogger, opts ...Opts) *Strategy { - rs := &Strategy{ - config: config, - strategyFuncMap: strategyFnMap{mu: sync.Mutex{}, funcMap: defaultStrategyFuncMap(logger)}, - } - // overwrite or add any options/defaults set above - for _, o := range opts { - o(rs) - } - - return rs -} - -// WithStrategyFuncMap Adds custom implementations for prefix -// -// Mainly used for testing -// NOTE: this may lead to eventual optional configurations by users -func WithStrategyFuncMap(funcMap StrategyFuncMap) Opts { - return func(rs *Strategy) { - rs.strategyFuncMap.mu.Lock() - defer rs.strategyFuncMap.mu.Unlock() - for prefix, implementation := range funcMap { - rs.strategyFuncMap.funcMap[config.ImplementationPrefix(prefix)] = implementation - } - } -} - -// GetImplementation is a factory method returning the concrete implementation for the retrieval of the token value -// i.e. facilitating the exchange of the supplied token for the underlying value -func (rs *Strategy) GetImplementation(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - if token == nil { - return nil, fmt.Errorf("unable to get prefix, %w", ErrTokenInvalid) - } - - if store, found := rs.strategyFuncMap.funcMap[token.Prefix()]; found { - return store(ctx, token) - } - - return nil, fmt.Errorf("implementation not found for input string: %s", token) -} - -func ExchangeToken(s store.Strategy, token *config.ParsedTokenConfig) *TokenResponse { - cr := &TokenResponse{} - cr.Err = nil - cr.key = token - s.SetToken(token) - cr.value, cr.Err = s.Value() - return cr -} - -type TokenResponse struct { - value string - key *config.ParsedTokenConfig - Err error -} - -func (tr *TokenResponse) Key() *config.ParsedTokenConfig { - return tr.key -} - -func (tr *TokenResponse) Value() string { - return tr.value -} - -func defaultStrategyFuncMap(logger log.ILogger) StrategyFuncMap { - return map[config.ImplementationPrefix]StrategyFunc{ - // config.AzTableStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - // return store.NewAzTableStore(ctx, token, logger) - // }, - // config.AzAppConfigPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - // return store.NewAzAppConf(ctx, token, logger) - // }, - // config.GcpSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - // return store.NewGcpSecrets(ctx, logger) - // }, - // config.SecretMgrPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - // return store.NewSecretsMgr(ctx, logger) - // }, - // config.ParamStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - // return store.NewParamStore(ctx, logger) - // }, - // config.AzKeyVaultSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - // return store.NewKvScrtStore(ctx, token, logger) - // }, - // config.HashicorpVaultPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - // return store.NewVaultStore(ctx, token, logger) - // }, - } -} - -type strategyFnMap struct { - mu sync.Mutex - funcMap StrategyFuncMap -} +// import ( +// "context" +// "errors" +// "fmt" +// "sync" + +// "github.com/DevLabFoundry/configmanager/v3/internal/config" +// "github.com/DevLabFoundry/configmanager/v3/internal/log" +// "github.com/DevLabFoundry/configmanager/v3/internal/store" +// ) + +// var ErrTokenInvalid = errors.New("invalid token - cannot get prefix") + +// // StrategyFunc +// type StrategyFunc func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) + +// // StrategyFuncMap +// type StrategyFuncMap map[config.ImplementationPrefix]StrategyFunc + +// type Strategy struct { +// config config.GenVarsConfig +// strategyFuncMap strategyFnMap +// } + +// type Opts func(*Strategy) + +// // New +// func New(config config.GenVarsConfig, logger log.ILogger, opts ...Opts) *Strategy { +// rs := &Strategy{ +// config: config, +// strategyFuncMap: strategyFnMap{mu: sync.Mutex{}, funcMap: defaultStrategyFuncMap(logger)}, +// } +// // overwrite or add any options/defaults set above +// for _, o := range opts { +// o(rs) +// } + +// return rs +// } + +// // WithStrategyFuncMap Adds custom implementations for prefix +// // +// // Mainly used for testing +// // NOTE: this may lead to eventual optional configurations by users +// func WithStrategyFuncMap(funcMap StrategyFuncMap) Opts { +// return func(rs *Strategy) { +// rs.strategyFuncMap.mu.Lock() +// defer rs.strategyFuncMap.mu.Unlock() +// for prefix, implementation := range funcMap { +// rs.strategyFuncMap.funcMap[config.ImplementationPrefix(prefix)] = implementation +// } +// } +// } + +// // GetImplementation is a factory method returning the concrete implementation for the retrieval of the token value +// // i.e. facilitating the exchange of the supplied token for the underlying value +// func (rs *Strategy) GetImplementation(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// if token == nil { +// return nil, fmt.Errorf("unable to get prefix, %w", ErrTokenInvalid) +// } + +// if store, found := rs.strategyFuncMap.funcMap[token.Prefix()]; found { +// return store(ctx, token) +// } + +// return nil, fmt.Errorf("implementation not found for input string: %s", token) +// } + +// func ExchangeToken(s store.Strategy, token *config.ParsedTokenConfig) *TokenResponse { +// cr := &TokenResponse{} +// cr.Err = nil +// cr.key = token +// s.SetToken(token) +// cr.val, cr.Err = s.Value() +// return cr +// } + +// type TokenResponse struct { +// val string +// key *config.ParsedTokenConfig +// Err error +// } + +// func (tr *TokenResponse) WithKey(key *config.ParsedTokenConfig) { +// tr.key = key +// } + +// func (tr *TokenResponse) WithValue(val string) { +// tr.val = val +// } + +// func (tr *TokenResponse) Key() *config.ParsedTokenConfig { +// return tr.key +// } + +// func (tr *TokenResponse) Value() string { +// return tr.val +// } + +// func defaultStrategyFuncMap(logger log.ILogger) StrategyFuncMap { +// return map[config.ImplementationPrefix]StrategyFunc{ +// // config.AzTableStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// // return store.NewAzTableStore(ctx, token, logger) +// // }, +// // config.AzAppConfigPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// // return store.NewAzAppConf(ctx, token, logger) +// // }, +// // config.GcpSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// // return store.NewGcpSecrets(ctx, logger) +// // }, +// // config.SecretMgrPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// // return store.NewSecretsMgr(ctx, logger) +// // }, +// // config.ParamStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// // return store.NewParamStore(ctx, logger) +// // }, +// // config.AzKeyVaultSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// // return store.NewKvScrtStore(ctx, token, logger) +// // }, +// // config.HashicorpVaultPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// // return store.NewVaultStore(ctx, token, logger) +// // }, +// } +// } + +// type strategyFnMap struct { +// mu sync.Mutex +// funcMap StrategyFuncMap +// } diff --git a/internal/strategy/strategy_test.go b/internal/strategy/strategy_test.go index c5fe3f7..b567e52 100644 --- a/internal/strategy/strategy_test.go +++ b/internal/strategy/strategy_test.go @@ -1,117 +1,117 @@ package strategy_test -import ( - "context" - "io" - "testing" +// import ( +// "context" +// "io" +// "testing" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - log "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/strategy" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" -) +// "github.com/DevLabFoundry/configmanager/v3/internal/config" +// log "github.com/DevLabFoundry/configmanager/v3/internal/log" +// "github.com/DevLabFoundry/configmanager/v3/internal/store" +// "github.com/DevLabFoundry/configmanager/v3/internal/strategy" +// "github.com/DevLabFoundry/configmanager/v3/internal/testutils" +// ) -type mockGenerate struct { - inToken, value string - err error -} +// type mockGenerate struct { +// inToken, value string +// err error +// } -func (m mockGenerate) SetToken(s *config.ParsedTokenConfig) { -} +// func (m mockGenerate) SetToken(s *config.ParsedTokenConfig) { +// } -func (m mockGenerate) Value() (s string, e error) { - return m.value, m.err -} +// func (m mockGenerate) Value() (s string, e error) { +// return m.value, m.err +// } -var TEST_GCP_CREDS = []byte(`{ - "type": "service_account", - "project_id": "xxxxx", - "private_key_id": "yyyyyyyyyyyy", - "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDf842hcn5Nvp6e\n7yKARaCVIDfLXpKDhRwUOvHMzJ1ioRgQo/kbv1n4yHGCSUFyY6hKGj0HBjaGj5kE\n79H/6Y3dJNGhnsMnxBhHdo+3FI8QF0CHZh460NMZSAJ41UMQSBGssGVsNfyUzXGH\nLc45sIx/Twx3yr1k2GD3E8FlDcKlZqa3xGHf+aipg2X3NxbYi+Sz7Yed+SOMhNHl\ncX6E/TqG9n1aTyIwjMIHscCYarJqURkJxr24ukDroCeMxAfxYTdMvRU2e8pFEdoY\nrgUC88fYfaVI5txJ6j/ZKauKQX9Pa8tSyXJeGva3JYp4VC7V4IyoVviCUgEGWZDN\n6/i3zoF/AgMBAAECggEAcVBCcVYFIkE48SH+Svjv74SFtpj7eSB4vKO2hPFjEOyB\nyKmu+aMwWvjQtiNqwf46wIPWLR+vpxYxTpYpo1sBNMvUZfp2tEA8KKyMuw3j9ThO\npjO9R/UxWrFcztbZP/u3NbFrH/2Q95mbv9IlbnsuG5xbqqEig0wYg+uzBvaXbig3\n/Jr0vLT2BkRCBKQkYGjVZcHlHVLoF7/J8cghFgkV1PGvknOv6/q7qzn9L4TjQIet\nfhrhN8Z1vgFiSYtpjP6YQEUEPSHmCQeD3WzJcnASPpU2uCUwd/z65ltKPnn+rqMt\n6jt9R1S1Ju2ZSjv+kR5fIXzihdOzncyzDDm33c/QwQKBgQD2QDZuzLjTxnhsfGii\nKJDAts+Jqfs/6SeEJcJKtEngj4m7rgzyEjbKVp8qtRHIzglKRWAe62/qzzy2BkKi\nvAd4+ZzmG2SkgypGsKVfjGXVFixz2gtUdmBOmK/TnYsxNT9yTt+rX9IGqKK60q73\nOWl8VsliLIsfvSH7+bqi7sRcXQKBgQDo0VUebyQHoTAXPdzGy2ysrVPDiHcldH0Y\n/hvhQTZwxYaJr3HpOCGol2Xl6zyawuudEQsoQwJ3Li6yeb0YMGiWX77/t+qX3pSn\nkGuoftGaNDV7sLn9UV2y+InF8EL1CasrhG1k5RIuxyfV0w+QUo+E7LpVR5XkbJqT\n9QNKnDQXiwKBgQDvvEYCCqbp7e/xVhEbxbhfFdro4Cat6tRAz+3egrTlvXhO0jzi\nMp9Kz5f3oP5ma0gaGX5hu75icE1fvKqE+d+ghAqe7w5FJzkyRulJI0tEb2jphN7A\n5NoPypBqyZboWjmhlG4mzouPVf/POCuEnk028truDAWJ6by7Lj3oP+HFNQKBgQCc\n5BQ8QiFBkvnZb7LLtGIzq0n7RockEnAK25LmJRAOxs13E2fsBguIlR3x5qgckqY8\nXjPqmd2bet+1HhyzpEuWqkcIBGRum2wJz2T9UxjklbJE/D8Z2i8OYDZX0SUOA8n5\ntXASwduS8lqB2Y1vcHOO3AhlV6xHFnjEpCPnr4PbKQKBgAhQ9D9MPeuz+5yw3yHg\nkvULZRtud+uuaKrOayprN25RTxr9c0erxqnvM7KHeo6/urOXeEa7x2n21kAT0Nch\nkF2RtWBLZKXGZEVBtw1Fw0UKNh4IDgM26dwlzRfTVHCiw6M6dCiTNk9KkP2vlkim\n3QFDSSUp+eBTXA17WkDAQf7w\n-----END PRIVATE KEY-----\n", - "client_email": "foo@project.iam.gserviceaccount.com", - "client_id": "99999911111111", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/bla" - }`) +// var TEST_GCP_CREDS = []byte(`{ +// "type": "service_account", +// "project_id": "xxxxx", +// "private_key_id": "yyyyyyyyyyyy", +// "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDf842hcn5Nvp6e\n7yKARaCVIDfLXpKDhRwUOvHMzJ1ioRgQo/kbv1n4yHGCSUFyY6hKGj0HBjaGj5kE\n79H/6Y3dJNGhnsMnxBhHdo+3FI8QF0CHZh460NMZSAJ41UMQSBGssGVsNfyUzXGH\nLc45sIx/Twx3yr1k2GD3E8FlDcKlZqa3xGHf+aipg2X3NxbYi+Sz7Yed+SOMhNHl\ncX6E/TqG9n1aTyIwjMIHscCYarJqURkJxr24ukDroCeMxAfxYTdMvRU2e8pFEdoY\nrgUC88fYfaVI5txJ6j/ZKauKQX9Pa8tSyXJeGva3JYp4VC7V4IyoVviCUgEGWZDN\n6/i3zoF/AgMBAAECggEAcVBCcVYFIkE48SH+Svjv74SFtpj7eSB4vKO2hPFjEOyB\nyKmu+aMwWvjQtiNqwf46wIPWLR+vpxYxTpYpo1sBNMvUZfp2tEA8KKyMuw3j9ThO\npjO9R/UxWrFcztbZP/u3NbFrH/2Q95mbv9IlbnsuG5xbqqEig0wYg+uzBvaXbig3\n/Jr0vLT2BkRCBKQkYGjVZcHlHVLoF7/J8cghFgkV1PGvknOv6/q7qzn9L4TjQIet\nfhrhN8Z1vgFiSYtpjP6YQEUEPSHmCQeD3WzJcnASPpU2uCUwd/z65ltKPnn+rqMt\n6jt9R1S1Ju2ZSjv+kR5fIXzihdOzncyzDDm33c/QwQKBgQD2QDZuzLjTxnhsfGii\nKJDAts+Jqfs/6SeEJcJKtEngj4m7rgzyEjbKVp8qtRHIzglKRWAe62/qzzy2BkKi\nvAd4+ZzmG2SkgypGsKVfjGXVFixz2gtUdmBOmK/TnYsxNT9yTt+rX9IGqKK60q73\nOWl8VsliLIsfvSH7+bqi7sRcXQKBgQDo0VUebyQHoTAXPdzGy2ysrVPDiHcldH0Y\n/hvhQTZwxYaJr3HpOCGol2Xl6zyawuudEQsoQwJ3Li6yeb0YMGiWX77/t+qX3pSn\nkGuoftGaNDV7sLn9UV2y+InF8EL1CasrhG1k5RIuxyfV0w+QUo+E7LpVR5XkbJqT\n9QNKnDQXiwKBgQDvvEYCCqbp7e/xVhEbxbhfFdro4Cat6tRAz+3egrTlvXhO0jzi\nMp9Kz5f3oP5ma0gaGX5hu75icE1fvKqE+d+ghAqe7w5FJzkyRulJI0tEb2jphN7A\n5NoPypBqyZboWjmhlG4mzouPVf/POCuEnk028truDAWJ6by7Lj3oP+HFNQKBgQCc\n5BQ8QiFBkvnZb7LLtGIzq0n7RockEnAK25LmJRAOxs13E2fsBguIlR3x5qgckqY8\nXjPqmd2bet+1HhyzpEuWqkcIBGRum2wJz2T9UxjklbJE/D8Z2i8OYDZX0SUOA8n5\ntXASwduS8lqB2Y1vcHOO3AhlV6xHFnjEpCPnr4PbKQKBgAhQ9D9MPeuz+5yw3yHg\nkvULZRtud+uuaKrOayprN25RTxr9c0erxqnvM7KHeo6/urOXeEa7x2n21kAT0Nch\nkF2RtWBLZKXGZEVBtw1Fw0UKNh4IDgM26dwlzRfTVHCiw6M6dCiTNk9KkP2vlkim\n3QFDSSUp+eBTXA17WkDAQf7w\n-----END PRIVATE KEY-----\n", +// "client_email": "foo@project.iam.gserviceaccount.com", +// "client_id": "99999911111111", +// "auth_uri": "https://accounts.google.com/o/oauth2/auth", +// "token_uri": "https://oauth2.googleapis.com/token", +// "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", +// "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/bla" +// }`) -func Test_Strategy_Retrieve_succeeds(t *testing.T) { - ttests := map[string]struct { - impl func(t *testing.T) store.Strategy - config *config.GenVarsConfig - token string - expect string - impPrefix config.ImplementationPrefix - }{ - "with mocked implementation AZTABLESTORAGE": { - func(t *testing.T) store.Strategy { - return &mockGenerate{"mountPath/token", "bar", nil} - }, - config.NewConfig().WithOutputPath("stdout"), - "mountPath/token", - "bar", - config.AzTableStorePrefix, - }, - // "error in retrieval": { - // func(t *testing.T) store.Strategy { - // return &mockGenerate{"SOME://mountPath/token", "bar", fmt.Errorf("unable to perform getTokenValue")} - // }, - // config.NewConfig().WithOutputPath("stdout").WithTokenSeparator("://"), - // []string{"SOME://token"}, - // config.AzAppConfigPrefix, - // "unable to perform getTokenValue", - // }, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - token, _ := config.NewToken(tt.impPrefix, *tt.config) - token.WithSanitizedToken(tt.token) - got := strategy.ExchangeToken(tt.impl(t), token) - if got.Err != nil { - t.Errorf(testutils.TestPhraseWithContext, "Token response errored", got.Err.Error(), tt.expect) - } - if got.Value() != tt.expect { - t.Errorf(testutils.TestPhraseWithContext, "Value not correct", got.Value(), tt.expect) - } - if got.Key().StoreToken() != tt.token { - t.Errorf(testutils.TestPhraseWithContext, "Incorrect Token returned in Key", got.Key().StoreToken(), tt.token) - } - }) - } -} +// func Test_Strategy_Retrieve_succeeds(t *testing.T) { +// ttests := map[string]struct { +// impl func(t *testing.T) store.Strategy +// config *config.GenVarsConfig +// token string +// expect string +// impPrefix config.ImplementationPrefix +// }{ +// "with mocked implementation AZTABLESTORAGE": { +// func(t *testing.T) store.Strategy { +// return &mockGenerate{"mountPath/token", "bar", nil} +// }, +// config.NewConfig().WithOutputPath("stdout"), +// "mountPath/token", +// "bar", +// config.AzTableStorePrefix, +// }, +// // "error in retrieval": { +// // func(t *testing.T) store.Strategy { +// // return &mockGenerate{"SOME://mountPath/token", "bar", fmt.Errorf("unable to perform getTokenValue")} +// // }, +// // config.NewConfig().WithOutputPath("stdout").WithTokenSeparator("://"), +// // []string{"SOME://token"}, +// // config.AzAppConfigPrefix, +// // "unable to perform getTokenValue", +// // }, +// } +// for name, tt := range ttests { +// t.Run(name, func(t *testing.T) { +// token, _ := config.NewToken(tt.impPrefix, *tt.config) +// token.WithSanitizedToken(tt.token) +// got := strategy.ExchangeToken(tt.impl(t), token) +// if got.Err != nil { +// t.Errorf(testutils.TestPhraseWithContext, "Token response errored", got.Err.Error(), tt.expect) +// } +// if got.Value() != tt.expect { +// t.Errorf(testutils.TestPhraseWithContext, "Value not correct", got.Value(), tt.expect) +// } +// if got.Key().StoreToken() != tt.token { +// t.Errorf(testutils.TestPhraseWithContext, "Incorrect Token returned in Key", got.Key().StoreToken(), tt.token) +// } +// }) +// } +// } -func Test_CustomStrategyFuncMap_add_own(t *testing.T) { +// func Test_CustomStrategyFuncMap_add_own(t *testing.T) { - ttests := map[string]struct { - }{ - "default": {}, - } - for name, _ := range ttests { - t.Run(name, func(t *testing.T) { - called := 0 - genVarsConf := config.NewConfig() - token, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig()) - token.WithSanitizedToken("mountPath/token") +// ttests := map[string]struct { +// }{ +// "default": {}, +// } +// for name := range ttests { +// t.Run(name, func(t *testing.T) { +// called := 0 +// genVarsConf := config.NewConfig() +// token, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig()) +// token.WithSanitizedToken("mountPath/token") - var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"AZTABLESTORE://mountPath/token", "bar", nil} - called++ - return m, nil - } +// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"AZTABLESTORE://mountPath/token", "bar", nil} +// called++ +// return m, nil +// } - s := strategy.New(*genVarsConf, log.New(io.Discard), strategy.WithStrategyFuncMap(strategy.StrategyFuncMap{config.AzTableStorePrefix: custFunc})) +// s := strategy.New(*genVarsConf, log.New(io.Discard), strategy.WithStrategyFuncMap(strategy.StrategyFuncMap{config.AzTableStorePrefix: custFunc})) - store, _ := s.GetImplementation(context.TODO(), token) - _ = strategy.ExchangeToken(store, token) +// store, _ := s.GetImplementation(context.TODO(), token) +// _ = strategy.ExchangeToken(store, token) - if called != 1 { - t.Errorf(testutils.TestPhraseWithContext, "custom func not called", called, 1) - } - }) - } -} +// if called != 1 { +// t.Errorf(testutils.TestPhraseWithContext, "custom func not called", called, 1) +// } +// }) +// } +// } // func Test_SelectImpl_With(t *testing.T) { diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 0000000..5ed18b5 --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,240 @@ +# Configmanager Plugin System + +The plugin architecture for configmanager is built using the [go-plugin](https://github.com/hashicorp/go-plugin?tab=readme-ov-file#go-plugin-system-over-rpc) from hashicorp. + + +The existing implementations are converted into plugins using the gRPC model and are built using +gRPC [go-plugin](https://github.com/hashicorp/go-plugin?tab=readme-ov-file#go-plugin-system-over-rpc) and generated/updated with the [buf cli](https://buf.build/docs/cli/). + + +## Plugin Architecture + + +```mermaid +``` + +The plugins will need to be downloaded into any one of these locations on disk, they will be checked in this order + +- currentDirectory (directory from which the configmanager executable is run) +- users home directory + +The plugin is expected to be found under this path in the above locations +> `.configmanager/plugins/$PLUGIN_PREFIX_LOWERCASE/$PLUGIN_PREFIX_LOWERCASE-$GOOS-$GOARCH` + +e.g. in case of the AWS Parameter Store plugin `.configmanager/plugins/awsparamstr/awsparamstr-linux-amd64` + + + +## Alternate architecture explored + +As part of the decision on which pluging architecture to use we also explored an alternate architecture using WASIP1. + + + +```go +import ( + "context" + "encoding/binary" + "encoding/json" + "sync" + "unicode/utf8" + "unsafe" + + "github.com/DevLabFoundry/configmanager/v3/plugins" +) + +// ==================== +// Bump allocator +// ==================== + +const heapSize = 64 * 1024 // 64 KiB arena; tune as needed + +type bumpAllocator struct { + mu sync.Mutex + heap []byte + used uint32 +} + +var alloc = bumpAllocator{ + heap: make([]byte, heapSize), +} + +// round allocation up to 8 bytes for basic alignment. +func roundUp(n uint32) uint32 { + const align = 8 + return (n + align - 1) &^ (align - 1) +} + +//go:wasmexport allocate +func Allocate(size uint32) uint32 { + if size == 0 { + return 0 + } + size = roundUp(size) + + alloc.mu.Lock() + defer alloc.mu.Unlock() + + if alloc.used+size > uint32(len(alloc.heap)) { + // Out of memory in our arena. + return 0 + } + + offset := alloc.used + alloc.used += size + + // Return pointer into linear memory for &heap[offset]. + return uint32(uintptr(unsafe.Pointer(&alloc.heap[offset]))) +} + +//go:wasmexport deallocate +func Deallocate(ptr, size uint32) { + // For a simple bump allocator, deallocate is a no-op. + // Memory is reclaimed when the module instance is destroyed. + _ = ptr + _ = size +} + +type Hdr struct { + Data uintptr + Len int + Cap int +} + +// ==================== +// Helpers +// ==================== + +// bytesFromPtrLen reinterprets a (ptr,len) pair in wasm linear memory +// as a Go []byte without copying. +func bytesFromPtrLen(ptr, length uint32) []byte { + if length == 0 { + return nil + } + + hdr := Hdr{ + Data: uintptr(ptr), + Len: int(length), + Cap: int(length), + } + + return *(*[]byte)(unsafe.Pointer(&hdr)) +} + +// ==================== +// strategy_token_value +// ==================== +// +// ABI: +// +// strategy_token_value( +// inPtr, inLen, outPtr, outCap, outLenPtr uint32, +// ) int32 +// +// Host contract: +// - Input bytes are at (inPtr, inLen) +// - Output buffer is [outPtr : outPtr+outCap) +// - outLenPtr points to 4 bytes where we write the required length +// +// Behaviour: +// - If input length == 0 => ERR_EMPTY_INPUT +// - If invalid UTF-8 => ERR_INVALID_UTF8 +// - Always write required length to *outLenPtr (little-endian) +// - If required > outCap => ERR_BUF_TOO_SMALL +// - Else copy into caller buffer and return OK +// +//go:wasmexport strategy_token_value +func StrategyTokenValue(tokenPtr, tokenLen, outPtr, outCap, outLenPtr uint32) int32 { + defer func() { + // Make sure panics don't leak as traps. + if r := recover(); r != nil { + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, 0) + } + } + } + }() + + if tokenLen == 0 { + // Mark required length as 0 and signal error. + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, 0) + } + } + return plugins.ERR_EMPTY_INPUT + } + + tokenBytes := bytesFromPtrLen(tokenPtr, tokenLen) + if !utf8.Valid(tokenBytes) { + if outLenPtr != 0 { + if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { + binary.LittleEndian.PutUint32(lenCell, uint32(len(tokenBytes))) + } + } + return plugins.ERR_INVALID_UTF8 + } + + // --- Business logic (replace with your real token strategy) --- + // unmarshal string into an object + token := &plugins.MessagExchange{} + if err := json.Unmarshal(tokenBytes, token); err != nil { + return plugins.ERR_FAILED_UNMARSHAL_MESSAGE + } + + // logger := log.New(os.Stdout) + // logger.SetLevel(log.DebugLvl) + + store, err := NewParamStore(context.Background()) + if err != nil { + return plugins.ERR_INIT_STORE + } + + outStr, err := store.Value(token) + if err != nil { + return plugins.ERR_FAILED_VALUE_RETRIEVAL + } + + outBytes := []byte(outStr) + // -------------------------------------------------------------- + // BEGIN RETURN Allocation + // -------------------------------------------------------------- + required := uint32(len(outBytes)) + + // Always write required length. + if outLenPtr != 0 { + lenCell := bytesFromPtrLen(outLenPtr, 4) + if len(lenCell) != 4 { + return plugins.ERR_INTERNAL + } + binary.LittleEndian.PutUint32(lenCell, required) + } + + if required > outCap { + return plugins.ERR_BUF_TOO_SMALL + } + + if required == 0 { + return plugins.OK + } + + outSlice := bytesFromPtrLen(outPtr, outCap) + if uint32(len(outSlice)) < required { + return plugins.ERR_INTERNAL + } + + copy(outSlice, outBytes) + return plugins.OK +} + +// main is required for wasip1 +// scaffolds the `_initialize` method +func main() {} +``` + +### Build notes + +build using the `-buildmode=c-shared` which will convert the module to a reactor module + +`GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o awsparams.wasm` diff --git a/plugins/awsparamstr/README.md b/plugins/awsparamstr/README.md index cc07402..8f4fda3 100644 --- a/plugins/awsparamstr/README.md +++ b/plugins/awsparamstr/README.md @@ -1,216 +1,4 @@ # AWS PARAM STORE Plugin -This is the `awsparamstr` implementation plugin built using the gp-plugin architecture from hashicorp... +This is the `awsparamstr` implementation plugin built using the go-plugin architecture from hashicorp... - -## Alternate architecture - -Explored an alternate architecture using WASIP1 - -```go -import ( - "context" - "encoding/binary" - "encoding/json" - "sync" - "unicode/utf8" - "unsafe" - - "github.com/DevLabFoundry/configmanager/v3/plugins" -) - -// ==================== -// Bump allocator -// ==================== - -const heapSize = 64 * 1024 // 64 KiB arena; tune as needed - -type bumpAllocator struct { - mu sync.Mutex - heap []byte - used uint32 -} - -var alloc = bumpAllocator{ - heap: make([]byte, heapSize), -} - -// round allocation up to 8 bytes for basic alignment. -func roundUp(n uint32) uint32 { - const align = 8 - return (n + align - 1) &^ (align - 1) -} - -//go:wasmexport allocate -func Allocate(size uint32) uint32 { - if size == 0 { - return 0 - } - size = roundUp(size) - - alloc.mu.Lock() - defer alloc.mu.Unlock() - - if alloc.used+size > uint32(len(alloc.heap)) { - // Out of memory in our arena. - return 0 - } - - offset := alloc.used - alloc.used += size - - // Return pointer into linear memory for &heap[offset]. - return uint32(uintptr(unsafe.Pointer(&alloc.heap[offset]))) -} - -//go:wasmexport deallocate -func Deallocate(ptr, size uint32) { - // For a simple bump allocator, deallocate is a no-op. - // Memory is reclaimed when the module instance is destroyed. - _ = ptr - _ = size -} - -type Hdr struct { - Data uintptr - Len int - Cap int -} - -// ==================== -// Helpers -// ==================== - -// bytesFromPtrLen reinterprets a (ptr,len) pair in wasm linear memory -// as a Go []byte without copying. -func bytesFromPtrLen(ptr, length uint32) []byte { - if length == 0 { - return nil - } - - hdr := Hdr{ - Data: uintptr(ptr), - Len: int(length), - Cap: int(length), - } - - return *(*[]byte)(unsafe.Pointer(&hdr)) -} - -// ==================== -// strategy_token_value -// ==================== -// -// ABI: -// -// strategy_token_value( -// inPtr, inLen, outPtr, outCap, outLenPtr uint32, -// ) int32 -// -// Host contract: -// - Input bytes are at (inPtr, inLen) -// - Output buffer is [outPtr : outPtr+outCap) -// - outLenPtr points to 4 bytes where we write the required length -// -// Behaviour: -// - If input length == 0 => ERR_EMPTY_INPUT -// - If invalid UTF-8 => ERR_INVALID_UTF8 -// - Always write required length to *outLenPtr (little-endian) -// - If required > outCap => ERR_BUF_TOO_SMALL -// - Else copy into caller buffer and return OK -// -//go:wasmexport strategy_token_value -func StrategyTokenValue(tokenPtr, tokenLen, outPtr, outCap, outLenPtr uint32) int32 { - defer func() { - // Make sure panics don't leak as traps. - if r := recover(); r != nil { - if outLenPtr != 0 { - if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { - binary.LittleEndian.PutUint32(lenCell, 0) - } - } - } - }() - - if tokenLen == 0 { - // Mark required length as 0 and signal error. - if outLenPtr != 0 { - if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { - binary.LittleEndian.PutUint32(lenCell, 0) - } - } - return plugins.ERR_EMPTY_INPUT - } - - tokenBytes := bytesFromPtrLen(tokenPtr, tokenLen) - if !utf8.Valid(tokenBytes) { - if outLenPtr != 0 { - if lenCell := bytesFromPtrLen(outLenPtr, 4); len(lenCell) == 4 { - binary.LittleEndian.PutUint32(lenCell, uint32(len(tokenBytes))) - } - } - return plugins.ERR_INVALID_UTF8 - } - - // --- Business logic (replace with your real token strategy) --- - // unmarshal string into an object - token := &plugins.MessagExchange{} - if err := json.Unmarshal(tokenBytes, token); err != nil { - return plugins.ERR_FAILED_UNMARSHAL_MESSAGE - } - - // logger := log.New(os.Stdout) - // logger.SetLevel(log.DebugLvl) - - store, err := NewParamStore(context.Background()) - if err != nil { - return plugins.ERR_INIT_STORE - } - - outStr, err := store.Value(token) - if err != nil { - return plugins.ERR_FAILED_VALUE_RETRIEVAL - } - - outBytes := []byte(outStr) - // -------------------------------------------------------------- - // BEGIN RETURN Allocation - // -------------------------------------------------------------- - required := uint32(len(outBytes)) - - // Always write required length. - if outLenPtr != 0 { - lenCell := bytesFromPtrLen(outLenPtr, 4) - if len(lenCell) != 4 { - return plugins.ERR_INTERNAL - } - binary.LittleEndian.PutUint32(lenCell, required) - } - - if required > outCap { - return plugins.ERR_BUF_TOO_SMALL - } - - if required == 0 { - return plugins.OK - } - - outSlice := bytesFromPtrLen(outPtr, outCap) - if uint32(len(outSlice)) < required { - return plugins.ERR_INTERNAL - } - - copy(outSlice, outBytes) - return plugins.OK -} - -// main is required for wasip1 -// scaffolds the `_initialize` method -func main() {} -``` - -### Build notes - -build using the `-buildmode=c-shared` which will convert the module to a reactor module - -`GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o awsparams.wasm` diff --git a/plugins/awssecrets/.gitkeep b/plugins/awssecrets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugins/grpc.go b/plugins/grpc.go index 5f9ebf0..c59d35f 100644 --- a/plugins/grpc.go +++ b/plugins/grpc.go @@ -6,7 +6,8 @@ import ( "github.com/DevLabFoundry/configmanager/v3/plugins/proto" ) -// GRPCClient is an implementation of KV that talks over RPC. +// GRPCClient is the host process talking to the plugins +// i.e. the GRPCServer implementation of the TokenStore type GRPCClient struct{ client proto.TokenStoreClient } func (m *GRPCClient) Value(key string, metadata []byte) (string, error) { diff --git a/plugins/vault/README.md b/plugins/vault/README.md new file mode 100644 index 0000000..27b590e --- /dev/null +++ b/plugins/vault/README.md @@ -0,0 +1,3 @@ +# Hashicorp Vault + + diff --git a/plugins/vault/impl/hashivault.go b/plugins/vault/impl/hashivault.go new file mode 100644 index 0000000..75ba351 --- /dev/null +++ b/plugins/vault/impl/hashivault.go @@ -0,0 +1,173 @@ +package impl + +import ( + "context" + "encoding/json" + "fmt" + "os" + "strconv" + "strings" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/plugins" + + vault "github.com/hashicorp/vault/api" + auth "github.com/hashicorp/vault/api/auth/aws" +) + +// HashiVaultHelper provides a broken up string +type HashiVaultHelper struct { + Path string + Token string +} + +type hashiVaultApi interface { + Get(ctx context.Context, secretPath string) (*vault.KVSecret, error) + GetVersion(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) +} + +type VaultStore struct { + svc hashiVaultApi + ctx context.Context + logger log.ILogger + config *VaultConfig + token *config.ParsedTokenConfig + strippedToken string +} + +// VaultConfig holds the parseable metadata struct +type VaultConfig struct { + Version string `json:"version"` + Role string `json:"iam_role"` +} + +func NewVaultStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*VaultStore, error) { + storeConf := &VaultConfig{} + _ = token.ParseMetadata(storeConf) + imp := &VaultStore{ + ctx: ctx, + logger: logger, + config: storeConf, + token: token, + } + + config := vault.DefaultConfig() + vt := SplitHashiVaultToken(token.StoreToken()) + imp.strippedToken = vt.Token + client, err := vault.NewClient(config) + if err != nil { + return nil, fmt.Errorf("%v\n%w", err, plugins.ErrClientInitialization) + } + + if strings.HasPrefix(os.Getenv("VAULT_TOKEN"), "aws_iam") { + awsclient, err := newVaultStoreWithAWSAuthIAM(client, storeConf.Role) + if err != nil { + return nil, err + } + client = awsclient + } + imp.svc = client.KVv2(vt.Path) + return imp, nil +} + +func (s *VaultStore) WithSvc(svc hashiVaultApi) { + s.svc = svc +} + +// newVaultStoreWithAWSAuthIAM returns an initialised client with AWSIAMAuth +// EC2 auth type is not supported currently +func newVaultStoreWithAWSAuthIAM(client *vault.Client, role string) (*vault.Client, error) { + if len(role) < 1 { + return nil, fmt.Errorf("role provided is empty, EC2 auth not supported") + } + awsAuth, err := auth.NewAWSAuth( + auth.WithRole(role), + ) + if err != nil { + return nil, fmt.Errorf("unable to initialize AWS auth method: %s. %w", err, plugins.ErrClientInitialization) + } + + authInfo, err := client.Auth().Login(context.Background(), awsAuth) + + if err != nil { + return nil, fmt.Errorf("unable to login to AWS auth method: %s. %w", err, plugins.ErrClientInitialization) + } + if authInfo == nil { + return nil, fmt.Errorf("no auth info was returned after login") + } + + return client, nil +} + +// setTokenVal +// imp.token is already set in the Vault constructor +// +// This happens inside the New func call +// due to the way the client needs to be +// initialised with a mountpath +// and mountpath is part of the token so it is set then +func (imp *VaultStore) SetToken(token *config.ParsedTokenConfig) {} + +// getTokenValue implements the underlying techonology +// token retrieval and returns a stringified version +// of the secret +func (imp *VaultStore) Value() (string, error) { + imp.logger.Info("%s", "Concrete implementation HashiVault") + imp.logger.Info("Getting Secret: %s", imp.token) + + ctx, cancel := context.WithCancel(imp.ctx) + defer cancel() + + secret, err := imp.getSecret(ctx, imp.strippedToken, imp.config.Version) + if err != nil { + imp.logger.Error(plugins.ImplementationNetworkErr, imp.token.Prefix(), err, imp.token.String()) + return "", err + } + + if secret.Data != nil { + resp, err := marshall(secret.Data) + if err != nil { + imp.logger.Error("marshalling error: %s", err.Error()) + return "", err + } + imp.logger.Debug("marhalled kvv2: %s", resp) + return resp, nil + } + + imp.logger.Error("value retrieved but empty for token: %v", imp.token) + return "", nil +} + +func (imp *VaultStore) getSecret(ctx context.Context, token string, version string) (*vault.KVSecret, error) { + if version != "" { + v, err := strconv.Atoi(version) + if err != nil { + return nil, fmt.Errorf("unable to parse version into an integer: %s", err.Error()) + } + return imp.svc.GetVersion(ctx, token, v) + } + return imp.svc.Get(ctx, token) +} + +func SplitHashiVaultToken(token string) HashiVaultHelper { + vh := HashiVaultHelper{} + // split token to extract the mount path + s := strings.Split(strings.TrimPrefix(token, "/"), "___") + // grab token and trim prefix if slash + vh.Token = strings.TrimPrefix(strings.Join(s[1:], ""), "/") + // assign mount path as extracted from input token + vh.Path = s[0] + return vh +} + +// marshall converts map[string]any into a JSON +// object. Secrets should only be a single level +// deep. +func marshall(secret map[string]any) (string, error) { + b, err := json.Marshal(secret) + if err != nil { + return "", err + } + return string(b), nil +} diff --git a/plugins/vault/impl/hashivault_test.go b/plugins/vault/impl/hashivault_test.go new file mode 100644 index 0000000..8e6f94a --- /dev/null +++ b/plugins/vault/impl/hashivault_test.go @@ -0,0 +1,624 @@ +package impl_test + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/internal/testutils" + "github.com/DevLabFoundry/configmanager/v3/plugins/vault/impl" + vault "github.com/hashicorp/vault/api" +) + +func TestMountPathExtract(t *testing.T) { + ttests := map[string]struct { + token func() *config.ParsedTokenConfig + expect string + }{ + "without leading slash": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/demo/configmanager" + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/demo/configmanager") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, "secret"}, + "with leading slash": { + func() *config.ParsedTokenConfig { + // "VAULT:///secret___/demo/configmanager", + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("/secret___/demo/configmanager") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, "secret"}, + "with underscore in path name": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("_secret___/demo/configmanager") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, "_secret"}, + "with double underscore in path name": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("__secret___/demo/configmanager") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, "__secret"}, + "with multiple paths in mountpath": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret/bar/path___/demo/configmanager") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, "secret/bar/path"}, + } + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + got := impl.SplitHashiVaultToken(tt.token().StoreToken()) + if got.Path != tt.expect { + t.Errorf("got %q, expected %q", got, tt.expect) + } + }) + } +} + +type mockVaultApi struct { + g func(ctx context.Context, secretPath string) (*vault.KVSecret, error) + gv func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) +} + +func (m mockVaultApi) Get(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + return m.g(ctx, secretPath) +} + +func (m mockVaultApi) GetVersion(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { + return m.gv(ctx, secretPath, version) +} + +func TestVaultScenarios(t *testing.T) { + + ttests := map[string]struct { + token func() *config.ParsedTokenConfig + expect string + mockClient func(t *testing.T) mockVaultApi + setupEnv func() func() + }{ + "happy return": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/foo") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + `{"foo":"test2130-9sd-0ds"}`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "foo" { + t.Errorf("got %v; want %s", secretPath, `foo`) + } + m := make(map[string]interface{}) + m["foo"] = "test2130-9sd-0ds" + return &vault.KVSecret{Data: m}, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "incorrect json": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/foo", + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/foo") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + `json: unsupported type: func() error`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "foo" { + t.Errorf("got %v; want %s", secretPath, `foo`) + } + m := make(map[string]interface{}) + m["error"] = func() error { return fmt.Errorf("ddodod") } + return &vault.KVSecret{Data: m}, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "another return": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret/engine1___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + `{"foo1":"test2130-9sd-0ds","foo2":"dsfsdf3454456"}`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf("got %v; want %s", secretPath, `some/other/foo2`) + } + m := make(map[string]interface{}) + m["foo1"] = "test2130-9sd-0ds" + m["foo2"] = "dsfsdf3454456" + return &vault.KVSecret{Data: m}, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "not found": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/foo", + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/foo") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + `secret not found`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "foo" { + t.Errorf("got %v; want %s", secretPath, `foo`) + } + return nil, fmt.Errorf("secret not found") + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "403": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/some/other/foo2", + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + `client 403`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf("got %v; want %s", secretPath, `some/other/foo2`) + } + return nil, fmt.Errorf("client 403") + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "found but empty": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/some/other/foo2", + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + // config.NewConfig(), + `{}`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf("got %v; want %s", secretPath, `some/other/foo2`) + } + m := make(map[string]interface{}) + return &vault.KVSecret{Data: m}, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "found but nil returned": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + "", + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + return &vault.KVSecret{Data: nil}, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "version provided correctly": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/some/other/foo2[version=1]", + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("version=1") + return tkn + }, + `{"foo2":"dsfsdf3454456"}`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.gv = func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + m := make(map[string]interface{}) + m["foo2"] = "dsfsdf3454456" + return &vault.KVSecret{Data: m}, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "version provided but unable to parse": { + func() *config.ParsedTokenConfig { + // "VAULT://secret___/some/other/foo2[version=1a]", + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("version=1a") + return tkn + }, + "unable to parse version into an integer: strconv.Atoi: parsing \"1a\": invalid syntax", + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.gv = func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + return nil, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "129378y1231283") + return func() { + os.Clearenv() + } + }, + }, + "vault rate limit incorrect": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("") + return tkn + }, + `error encountered setting up default configuration: VAULT_RATE_LIMIT was provided but incorrectly formatted +failed to initialize the client`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + return &vault.KVSecret{Data: nil}, nil + } + return mv + }, + func() func() { + os.Setenv("VAULT_TOKEN", "") + os.Setenv("VAULT_RATE_LIMIT", "wrong") + return func() { + os.Clearenv() + } + }, + }, + } + + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + tearDown := tt.setupEnv() + defer tearDown() + + i, err := impl.NewVaultStore(context.TODO(), tt.token(), log.New(io.Discard)) + if err != nil { + if err.Error() != tt.expect { + t.Fatalf("failed to init hashivault, %v", err.Error()) + } + return + } + + i.WithSvc(tt.mockClient(t)) + got, err := i.Value() + if err != nil { + if err.Error() != tt.expect { + t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) + } + return + } + if got != tt.expect { + t.Errorf(testutils.TestPhrase, got, tt.expect) + } + }) + } +} + +func TestAwsIamAuth(t *testing.T) { + ttests := map[string]struct { + token func() *config.ParsedTokenConfig + expect string + mockClient func(t *testing.T) mockVaultApi + mockHanlder func(t *testing.T) http.Handler + setupEnv func(addr string) func() + }{ + "aws_iam auth no role specified": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("version=1") + return tkn + }, + "role provided is empty, EC2 auth not supported", + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + return &vault.KVSecret{Data: nil}, nil + } + return mv + }, + func(t *testing.T) http.Handler { + return nil + }, + func(_ string) func() { + os.Setenv("VAULT_TOKEN", "aws_iam") + os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") + os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") + os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") + os.Setenv("AWS_REGION", "eu-west-1") + return func() { + os.Clearenv() + } + }, + }, + "aws_iam auth incorrectly formatted request": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("version=1,iam_role=not_a_role") + return tkn + }, + `unable to login to AWS auth method: unable to log in to auth method: unable to log in with AWS auth: Error making API request. + +URL: PUT %s/v1/auth/aws/login +Code: 400. Raw Message: + +incorrect values supplied. failed to initialize the client`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + return &vault.KVSecret{Data: nil}, nil + } + return mv + }, + func(t *testing.T) http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/v1/auth/aws/login", func(w http.ResponseWriter, r *http.Request) { + + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(400) + w.Write([]byte(`incorrect values supplied`)) + }) + return mux + }, + func(addr string) func() { + os.Setenv("VAULT_TOKEN", "aws_iam") + os.Setenv("VAULT_ADDR", addr) + os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") + os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") + os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") + os.Setenv("AWS_REGION", "eu-west-1") + return func() { + os.Clearenv() + } + }, + }, + "aws_iam auth success": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("iam_role=arn:aws:iam::1111111:role/i-orchestration") + return tkn + }, + // + `{"foo2":"dsfsdf3454456"}`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + m := make(map[string]any) + m["foo2"] = "dsfsdf3454456" + return &vault.KVSecret{Data: m}, nil + } + return mv + }, + func(t *testing.T) http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/v1/auth/aws/login", func(w http.ResponseWriter, r *http.Request) { + + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Write([]byte(`{"auth":{"client_token": "fooresddfasdsasad"}}`)) + }) + return mux + }, + func(addr string) func() { + os.Setenv("VAULT_TOKEN", "aws_iam") + os.Setenv("VAULT_ADDR", addr) + os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") + os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") + os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") + os.Setenv("AWS_REGION", "eu-west-1") + return func() { + os.Clearenv() + } + }, + }, + "aws_iam auth no token returned": { + func() *config.ParsedTokenConfig { + tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn.WithSanitizedToken("secret___/some/other/foo2") + tkn.WithKeyPath("") + tkn.WithMetadata("iam_role=arn:aws:iam::1111111:role/i-orchestration") + return tkn + }, + `unable to login to AWS auth method: response did not return ClientToken, client token not set. failed to initialize the client`, + func(t *testing.T) mockVaultApi { + mv := mockVaultApi{} + mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { + t.Helper() + if secretPath != "some/other/foo2" { + t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + } + m := make(map[string]interface{}) + m["foo2"] = "dsfsdf3454456" + return &vault.KVSecret{Data: m}, nil + } + return mv + }, + func(t *testing.T) http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/v1/auth/aws/login", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Write([]byte(`{"auth":{}}`)) + }) + return mux + }, + func(addr string) func() { + os.Setenv("VAULT_TOKEN", "aws_iam") + os.Setenv("VAULT_ADDR", addr) + os.Setenv("AWS_ACCESS_KEY_ID", "1280qwed9u9nsc9fdsbv9gsfrd") + os.Setenv("AWS_SECRET_ACCESS_KEY", "SED)SDVfdv0jfds08sdfgu09sd943tj4fELH/") + os.Setenv("AWS_SESSION_TOKEN", "IQoJb3JpZ2luX2VjELH//////////wEaCWV1LXdlc3QtMiJIMEYCIQDPU6UGJ0...df.fdgdfg.dfg.gdf.dgf") + os.Setenv("AWS_REGION", "eu-west-1") + return func() { + os.Clearenv() + } + }, + }, + } + + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + // + ts := httptest.NewServer(tt.mockHanlder(t)) + tearDown := tt.setupEnv(ts.URL) + defer tearDown() + i, err := impl.NewVaultStore(context.TODO(), tt.token(), log.New(io.Discard)) + if err != nil { + // WHAT A CRAP way to do this... + if err.Error() != strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0] { + t.Errorf(testutils.TestPhraseWithContext, "aws iam auth", err.Error(), strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0]) + t.Fatalf("failed to init hashivault, %v", err.Error()) + } + return + } + + i.WithSvc(tt.mockClient(t)) + got, err := i.Value() + if err != nil { + if err.Error() != tt.expect { + t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) + } + return + } + if got != tt.expect { + t.Errorf(testutils.TestPhrase, got, tt.expect) + } + }) + } +} diff --git a/plugins/vault/main.go b/plugins/vault/main.go new file mode 100644 index 0000000..c4b340f --- /dev/null +++ b/plugins/vault/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "context" + "os" + + "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager/v3/plugins/awsparamstr/impl" + "github.com/hashicorp/go-plugin" +) + +// Here is a real implementation of KV that writes to a local file with +// the key name and the contents are the value of the key. +type TokenStorePlugin struct{} + +func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { + srv, err := impl.NewParamStore(context.Background(), log.New(os.Stderr)) + if err != nil { + return "", err + } + return srv.Value(key, metadata) +} + +func main() { + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: plugins.Handshake, + Plugins: map[string]plugin.Plugin{ + "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: &TokenStorePlugin{}}, + }, + // A non-nil value here enables gRPC serving for this plugin... + GRPCServer: plugin.DefaultGRPCServer, + }) +} From bef899b45560727c49186163de061ab7586702ac Mon Sep 17 00:00:00 2001 From: dnitsch Date: Thu, 19 Mar 2026 20:45:03 +0000 Subject: [PATCH 07/15] fix: rejig packages --- cmd/configmanager/configmanager.go | 2 +- {internal/config => config}/config.go | 12 +- {internal/config => config}/config_test.go | 34 +- configmanager.go | 2 +- configmanager_test.go | 2 +- generator/generator.go | 35 +- generator/generator_test.go | 435 ++++++++++---------- generator/generatorvars.go | 2 +- go.mod | 48 +-- go.sum | 49 +++ internal/cmdutils/cmdutils.go | 2 +- internal/cmdutils/cmdutils_test.go | 2 +- internal/cmdutils/postprocessor.go | 2 +- internal/cmdutils/postprocessor_test.go | 2 +- internal/lexer/lexer.go | 75 ++-- internal/lexer/lexer_test.go | 111 ++--- internal/parser/parser.go | 60 +-- internal/parser/parser_test.go | 2 +- internal/store/plugin.go | 2 +- internal/store/plugin_test.go | 14 +- internal/store/store.go | 44 +- internal/{config => token}/token.go | 6 +- internal/token/token_test.go | 25 ++ plugins/awsparamstr/go.mod | 46 +++ plugins/awsparamstr/go.sum | 109 +++++ plugins/awsparamstr/impl/paramstore.go | 10 +- plugins/awsparamstr/impl/paramstore_test.go | 29 +- plugins/awsparamstr/main.go | 21 +- plugins/empty/main.go | 35 ++ plugins/vault/impl/hashivault.go | 8 +- plugins/vault/impl/hashivault_test.go | 47 ++- plugins/vault/main.go | 18 +- 32 files changed, 767 insertions(+), 524 deletions(-) rename {internal/config => config}/config.go (92%) rename {internal/config => config}/config_test.go (81%) rename internal/{config => token}/token.go (95%) create mode 100644 internal/token/token_test.go create mode 100644 plugins/awsparamstr/go.mod create mode 100644 plugins/awsparamstr/go.sum create mode 100644 plugins/empty/main.go diff --git a/cmd/configmanager/configmanager.go b/cmd/configmanager/configmanager.go index 383d229..557ea84 100644 --- a/cmd/configmanager/configmanager.go +++ b/cmd/configmanager/configmanager.go @@ -6,9 +6,9 @@ import ( "io" "github.com/DevLabFoundry/configmanager/v3" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" "github.com/DevLabFoundry/configmanager/v3/internal/cmdutils" - "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/spf13/cobra" ) diff --git a/internal/config/config.go b/config/config.go similarity index 92% rename from internal/config/config.go rename to config/config.go index f7b3713..ac8f9b7 100644 --- a/internal/config/config.go +++ b/config/config.go @@ -41,14 +41,6 @@ const ( ) var ( - // default varPrefix used by the replacer function - // any token must beging with one of these else - // it will be skipped as not a replaceable token - VarPrefix = map[ImplementationPrefix]bool{ - SecretMgrPrefix: true, ParamStorePrefix: true, AzKeyVaultSecretsPrefix: true, - GcpSecretsPrefix: true, HashicorpVaultPrefix: true, AzTableStorePrefix: true, - AzAppConfigPrefix: true, UnknownPrefix: true, - } ErrConfigValidation = errors.New("config validation failed") ) @@ -144,8 +136,8 @@ type ParsedTokenConfig struct { sanitizedToken string } -// NewToken initialises a *ParsedTokenConfig -func NewToken(prefix ImplementationPrefix, config GenVarsConfig) (*ParsedTokenConfig, error) { +// NewParsedToken initialises a *ParsedTokenConfig +func NewParsedToken(prefix ImplementationPrefix, config GenVarsConfig) (*ParsedTokenConfig, error) { tokenConf := &ParsedTokenConfig{} if err := config.Validate(); err != nil { return nil, err diff --git a/internal/config/config_test.go b/config/config_test.go similarity index 81% rename from internal/config/config_test.go rename to config/config_test.go index 8fc33f0..c6cedaa 100644 --- a/internal/config/config_test.go +++ b/config/config_test.go @@ -3,7 +3,7 @@ package config_test import ( "testing" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/internal/testutils" ) @@ -36,7 +36,7 @@ func Test_MarshalMetadata_with_label_struct_succeeds(t *testing.T) { }{ "when provider expects label on token and label exists": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) + tkn, _ := config.NewParsedToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) tkn.WithKeyPath("d88") tkn.WithMetadata("label=dev") tkn.WithSanitizedToken("basjh/dskjuds/123") @@ -47,7 +47,7 @@ func Test_MarshalMetadata_with_label_struct_succeeds(t *testing.T) { }, "when provider expects label on token and label does not exist": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) + tkn, _ := config.NewParsedToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) tkn.WithKeyPath("d88") tkn.WithMetadata("someother=dev") tkn.WithSanitizedToken("basjh/dskjuds/123") @@ -58,7 +58,7 @@ func Test_MarshalMetadata_with_label_struct_succeeds(t *testing.T) { }, "no metadata found": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) + tkn, _ := config.NewParsedToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) tkn.WithKeyPath("d88") tkn.WithSanitizedToken("basjh/dskjuds/123") return tkn @@ -68,7 +68,7 @@ func Test_MarshalMetadata_with_label_struct_succeeds(t *testing.T) { }, "no metadata found incorrect marker placement": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) + tkn, _ := config.NewParsedToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) tkn.WithKeyPath("d88]asdas=bar[") tkn.WithSanitizedToken("basjh/dskjuds/123") return tkn @@ -78,7 +78,7 @@ func Test_MarshalMetadata_with_label_struct_succeeds(t *testing.T) { }, "no metadata found incorrect marker placement and no key separator": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) + tkn, _ := config.NewParsedToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) tkn.WithSanitizedToken("basjh/dskjuds/123]asdas=bar[") return tkn }, @@ -87,7 +87,7 @@ func Test_MarshalMetadata_with_label_struct_succeeds(t *testing.T) { }, "no start found incorrect marker placement and no key separator": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) + tkn, _ := config.NewParsedToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("://")) tkn.WithKeyPath("d88") tkn.WithMetadata("someother=dev") tkn.WithSanitizedToken("basjh/dskjuds/123]asdas=bar]") @@ -139,7 +139,7 @@ func Test_TokenParser_config(t *testing.T) { for name, tt := range ttests { t.Run(name, func(t *testing.T) { conf := &mockConfAwsSecrMgr{} - got, _ := config.NewToken(tt.expPrefix, *config.NewConfig()) + got, _ := config.NewParsedToken(tt.expPrefix, *config.NewConfig()) got.WithSanitizedToken(tt.rawToken) got.WithKeyPath(tt.keyPath) got.WithMetadata(tt.metadataStr) @@ -164,21 +164,3 @@ func Test_TokenParser_config(t *testing.T) { }) } } - -func TestLookupIdent(t *testing.T) { - ttests := map[string]struct { - char string - expect config.TokenType - }{ - "new line": {"\n", config.NEW_LINE}, - "dash": {"-", config.TEXT}, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - got := config.LookupIdent(tt.char) - if got != tt.expect { - t.Errorf("got %v wanted %v", got, tt.expect) - } - }) - } -} diff --git a/configmanager.go b/configmanager.go index 4da0d1b..ff6c3ea 100644 --- a/configmanager.go +++ b/configmanager.go @@ -9,8 +9,8 @@ import ( "slices" "strings" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" - "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/a8m/envsubst" ) diff --git a/configmanager_test.go b/configmanager_test.go index 1321232..1179d61 100644 --- a/configmanager_test.go +++ b/configmanager_test.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/DevLabFoundry/configmanager/v3" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" - "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/testutils" "github.com/go-test/deep" "gopkg.in/yaml.v3" diff --git a/generator/generator.go b/generator/generator.go index ca3243f..0d4010f 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -10,7 +10,7 @@ import ( "strings" "sync" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/internal/lexer" "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/DevLabFoundry/configmanager/v3/internal/parser" @@ -47,9 +47,9 @@ func new(ctx context.Context, opts ...Opts) *Generator { Logger: log.New(io.Discard), ctx: ctx, // return using default config + store: store.New(ctx), config: *conf, } - // g.strategy = nil // now apply additional opts for _, o := range opts { @@ -59,13 +59,13 @@ func new(ctx context.Context, opts ...Opts) *Generator { return g } -// // WithStrategyMap -// // -// // Adds addtional funcs for storageRetrieval used for testing only -// func (c *Generator) WithStrategyMap(sm strategy.StrategyFuncMap) *Generator { -// c.strategy = sm -// return c -// } +// WithStrategyMap +// +// Adds addtional funcs for storageRetrieval used for testing only +func (c *Generator) WithStores(sm *store.Store) *Generator { + c.store = sm + return c +} // WithConfig uses custom config func (c *Generator) WithConfig(cfg *config.GenVarsConfig) *Generator { @@ -76,11 +76,11 @@ func (c *Generator) WithConfig(cfg *config.GenVarsConfig) *Generator { return c } -// WithContext uses caller passed context -func (c *Generator) WithContext(ctx context.Context) *Generator { - c.ctx = ctx - return c -} +// // WithContext uses caller passed context +// func (c *Generator) WithContext(ctx context.Context) *Generator { +// c.ctx = ctx +// return c +// } // Config gets Config on the GenVars func (c *Generator) Config() *config.GenVarsConfig { @@ -100,12 +100,11 @@ func (c *Generator) Generate(tokens []string) (ReplacedToken, error) { // initialise pugins here based on discovered tokens // - s, err := store.Init(c.ctx, ntm.TokenSet()) - if err != nil { + // this can only be done once the tokens are known + if err := c.store.Init(c.ctx, ntm.TokenSet()); err != nil { return nil, err } - c.store = s // pass in default initialised retrieveStrategy // input should be rt, err := c.generate(ntm) @@ -256,7 +255,7 @@ func (n NormalizedTokenSafe) TokenSet() []string { n.mu.Lock() defer n.mu.Unlock() ss := []string{} - for key, _ := range n.set { + for key := range n.set { ss = append(ss, strings.ToLower(key)) } return ss diff --git a/generator/generator_test.go b/generator/generator_test.go index d391d86..6e5418b 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -1,18 +1,11 @@ package generator_test import ( - "bytes" "context" - "fmt" - "slices" "testing" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/store" - "github.com/DevLabFoundry/configmanager/v3/internal/strategy" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" ) type mockGenerate struct { @@ -26,187 +19,187 @@ func (m *mockGenerate) Value() (s string, e error) { return m.value, m.err } -func TestGenerate(t *testing.T) { +// func TestGenerate(t *testing.T) { - t.Run("succeeds with funcMap", func(t *testing.T) { - var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"AWSPARAMSTR://mountPath/token", "bar", nil} - return m, nil - } +// t.Run("succeeds with funcMap", func(t *testing.T) { +// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"AWSPARAMSTR://mountPath/token", "bar", nil} +// return m, nil +// } - g := generator.New(context.TODO(), func(gv *generator.Generator) { - gv.Logger = log.New(&bytes.Buffer{}) - }) - g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) - got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) +// g := generator.New(context.TODO(), func(gv *generator.Generator) { +// gv.Logger = log.New(&bytes.Buffer{}) +// }) +// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) +// got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) - if err != nil { - t.Fatal("errored on generate") - } - if len(got) != 1 { - t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 1) - } - }) +// if err != nil { +// t.Fatal("errored on generate") +// } +// if len(got) != 1 { +// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 1) +// } +// }) - t.Run("errors in retrieval and logs it out", func(t *testing.T) { - var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"AWSPARAMSTR://mountPath/token", "bar", fmt.Errorf("failed to get value")} - return m, nil - } +// t.Run("errors in retrieval and logs it out", func(t *testing.T) { +// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"AWSPARAMSTR://mountPath/token", "bar", fmt.Errorf("failed to get value")} +// return m, nil +// } - g := generator.New(context.TODO()) - g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) - got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) +// g := generator.New(context.TODO()) +// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) +// got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) - if err != nil { - t.Fatal("errored on generate") - } - if len(got) != 0 { - t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) - } - }) +// if err != nil { +// t.Fatal("errored on generate") +// } +// if len(got) != 0 { +// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) +// } +// }) - t.Run("retrieves values correctly from a keylookup inside", func(t *testing.T) { - var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"token-unused", `{"foo":"bar","key1":{"key2":"val"}}`, nil} - return m, nil - } +// t.Run("retrieves values correctly from a keylookup inside", func(t *testing.T) { +// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"token-unused", `{"foo":"bar","key1":{"key2":"val"}}`, nil} +// return m, nil +// } - g := generator.New(context.TODO()) - g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) - got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token|key1.key2"}) +// g := generator.New(context.TODO()) +// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc, store: nil}) +// got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token|key1.key2"}) - if err != nil { - t.Fatal("errored on generate") - } - if len(got) != 1 { - t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) - } - if got["AWSPARAMSTR://mountPath/token|key1.key2"] != "val" { - t.Errorf(testutils.TestPhraseWithContext, "incorrect value returned in parsedMap", got["AWSPARAMSTR://mountPath/token|key1.key2"], "val") - } - }) -} +// if err != nil { +// t.Fatal("errored on generate") +// } +// if len(got) != 1 { +// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) +// } +// if got["AWSPARAMSTR://mountPath/token|key1.key2"] != "val" { +// t.Errorf(testutils.TestPhraseWithContext, "incorrect value returned in parsedMap", got["AWSPARAMSTR://mountPath/token|key1.key2"], "val") +// } +// }) +// } -func TestGenerate_withKeys_lookup(t *testing.T) { - ttests := map[string]struct { - custFunc strategy.StrategyFunc - token string - expectVal string - }{ - "retrieves string value correctly from a keylookup inside": { - custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":"val"}}`, nil} - return m, nil - }, - token: "AWSPARAMSTR://mountPath/token|key1.key2", - expectVal: "val", - }, - "retrieves number value correctly from a keylookup inside": { - custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":123}}`, nil} - return m, nil - }, - token: "AWSPARAMSTR://mountPath/token|key1.key2", - expectVal: "123", - }, - "retrieves nothing as keylookup is incorrect": { - custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":123}}`, nil} - return m, nil - }, - token: "AWSPARAMSTR://mountPath/token|noprop", - expectVal: "", - }, - "retrieves value as is due to incorrectly stored json in backing store": { - custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"token", `foo":"bar","key1":{"key2":123}}`, nil} - return m, nil - }, - token: "AWSPARAMSTR://mountPath/token|noprop", - expectVal: `foo":"bar","key1":{"key2":123}}`, - }, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - g := generator.New(context.TODO()) - g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: tt.custFunc}) - got, err := g.Generate([]string{tt.token}) +// func TestGenerate_withKeys_lookup(t *testing.T) { +// ttests := map[string]struct { +// custFunc strategy.StrategyFunc +// token string +// expectVal string +// }{ +// "retrieves string value correctly from a keylookup inside": { +// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":"val"}}`, nil} +// return m, nil +// }, +// token: "AWSPARAMSTR://mountPath/token|key1.key2", +// expectVal: "val", +// }, +// "retrieves number value correctly from a keylookup inside": { +// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":123}}`, nil} +// return m, nil +// }, +// token: "AWSPARAMSTR://mountPath/token|key1.key2", +// expectVal: "123", +// }, +// "retrieves nothing as keylookup is incorrect": { +// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":123}}`, nil} +// return m, nil +// }, +// token: "AWSPARAMSTR://mountPath/token|noprop", +// expectVal: "", +// }, +// "retrieves value as is due to incorrectly stored json in backing store": { +// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"token", `foo":"bar","key1":{"key2":123}}`, nil} +// return m, nil +// }, +// token: "AWSPARAMSTR://mountPath/token|noprop", +// expectVal: `foo":"bar","key1":{"key2":123}}`, +// }, +// } +// for name, tt := range ttests { +// t.Run(name, func(t *testing.T) { +// g := generator.New(context.TODO()) +// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: tt.custFunc}) +// got, err := g.Generate([]string{tt.token}) - if err != nil { - t.Fatal("errored on generate") - } - if len(got) != 1 { - t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) - } - if got[tt.token] != tt.expectVal { - t.Errorf(testutils.TestPhraseWithContext, "incorrect value returned in parsedMap", got[tt.token], tt.expectVal) - } - }) - } -} +// if err != nil { +// t.Fatal("errored on generate") +// } +// if len(got) != 1 { +// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) +// } +// if got[tt.token] != tt.expectVal { +// t.Errorf(testutils.TestPhraseWithContext, "incorrect value returned in parsedMap", got[tt.token], tt.expectVal) +// } +// }) +// } +// } -func Test_IsParsed(t *testing.T) { - ttests := map[string]struct { - val any - isParsed bool - }{ - "not parseable": { - `notparseable`, false, - }, - "one level parseable": { - `{"parseable":"foo"}`, true, - }, - "incorrect JSON": { - `parseable":"foo"}`, false, - }, - } - for name, tt := range ttests { - t.Run(name, func(t *testing.T) { - typ := generator.ReplacedToken{} - got := generator.IsParsed(tt.val, typ) - if got != tt.isParsed { - t.Errorf(testutils.TestPhraseWithContext, "unexpected IsParsed", got, tt.isParsed) - } - }) - } -} +// func Test_IsParsed(t *testing.T) { +// ttests := map[string]struct { +// val any +// isParsed bool +// }{ +// "not parseable": { +// `notparseable`, false, +// }, +// "one level parseable": { +// `{"parseable":"foo"}`, true, +// }, +// "incorrect JSON": { +// `parseable":"foo"}`, false, +// }, +// } +// for name, tt := range ttests { +// t.Run(name, func(t *testing.T) { +// typ := generator.ReplacedToken{} +// got := generator.IsParsed(tt.val, typ) +// if got != tt.isParsed { +// t.Errorf(testutils.TestPhraseWithContext, "unexpected IsParsed", got, tt.isParsed) +// } +// }) +// } +// } -func TestGenVars_NormalizeRawToken(t *testing.T) { +// func TestGenVars_NormalizeRawToken(t *testing.T) { - t.Run("multiple tokens", func(t *testing.T) { - g := generator.New(context.TODO()) +// t.Run("multiple tokens", func(t *testing.T) { +// g := generator.New(context.TODO()) - input := `GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj - GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|a - GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|b - GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|c - AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj - AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123] - AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key1 - AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key2 - AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj - VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj` - want := []string{"GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", - "AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", - "AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123]", - "AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", - "AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", - "VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj"} - got, err := g.DiscoverTokens(input) - if err != nil { - t.Fatal(err) - } - if len(got.GetMap()) != len(want) { - t.Errorf("got %v wanted %d", len(got.GetMap()), len(want)) - } - for key := range got.GetMap() { - if !slices.Contains(want, key) { - t.Errorf("got %s, wanted to be included in %v", key, want) - } - } - }) -} +// input := `GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj +// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|a +// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|b +// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|c +// AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj +// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123] +// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key1 +// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key2 +// AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj +// VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj` +// want := []string{"GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", +// "AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", +// "AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123]", +// "AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", +// "AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", +// "VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj"} +// got, err := g.DiscoverTokens(input) +// if err != nil { +// t.Fatal(err) +// } +// if len(got.GetMap()) != len(want) { +// t.Errorf("got %v wanted %d", len(got.GetMap()), len(want)) +// } +// for key := range got.GetMap() { +// if !slices.Contains(want, key) { +// t.Errorf("got %s, wanted to be included in %v", key, want) +// } +// } +// }) +// } func Test_ConfigManager_DiscoverTokens(t *testing.T) { ttests := map[string]struct { @@ -297,7 +290,7 @@ func Test_ConfigManager_DiscoverTokens(t *testing.T) { } for name, tt := range ttests { t.Run(name, func(t *testing.T) { - config.VarPrefix = map[config.ImplementationPrefix]bool{"AWSPARAMSTR": true} + // config.VarPrefix = map[config.ImplementationPrefix]bool{"AWSPARAMSTR": true} g := generator.New(context.TODO()) g.Config().WithTokenSeparator(tt.separator) gdt, err := g.DiscoverTokens(tt.input) @@ -318,53 +311,53 @@ func Test_ConfigManager_DiscoverTokens(t *testing.T) { } } -func Test_Generate_EnsureRaceFree(t *testing.T) { - g := generator.New(context.TODO()) +// func Test_Generate_EnsureRaceFree(t *testing.T) { +// g := generator.New(context.TODO()) - input := ` -fg -dfg gdfgfdGCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj -GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|a -GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|b -GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|c -ddsffds AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj - 'AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123]' - AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key1 - AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key2 - AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj gdf gdfgdf - dfg gdf gdf gdf - fdg dgf dgf - VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj . dfg dfgdf dfg fddf` +// input := ` +// fg +// dfg gdfgfdGCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj +// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|a +// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|b +// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|c +// ddsffds AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj +// 'AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123]' +// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key1 +// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key2 +// AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj gdf gdfgdf +// dfg gdf gdf gdf +// fdg dgf dgf +// VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj . dfg dfgdf dfg fddf` - g.WithStrategyMap(strategy.StrategyFuncMap{ - config.GcpSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"a":"bar","b":{"key2":"val"},"c":123}`, nil} - return m, nil - }, - config.ParamStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"a":"bar","b":{"key2":"val"},"c":123}`, nil} - return m, nil - }, - config.SecretMgrPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"key1":"bar","key2":"val","c":123}`, nil} - return m, nil - }, - config.AzKeyVaultSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"key1":"bar","key2":"val","c":123}`, nil} - return m, nil - }, - config.HashicorpVaultPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { - m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"key1":"bar","key2":"val","c":123}`, nil} - return m, nil - }, - }) +// g.WithStrategyMap(strategy.StrategyFuncMap{ +// config.GcpSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"a":"bar","b":{"key2":"val"},"c":123}`, nil} +// return m, nil +// }, +// config.ParamStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"a":"bar","b":{"key2":"val"},"c":123}`, nil} +// return m, nil +// }, +// config.SecretMgrPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"key1":"bar","key2":"val","c":123}`, nil} +// return m, nil +// }, +// config.AzKeyVaultSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"key1":"bar","key2":"val","c":123}`, nil} +// return m, nil +// }, +// config.HashicorpVaultPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { +// m := &mockGenerate{"/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", `{"key1":"bar","key2":"val","c":123}`, nil} +// return m, nil +// }, +// }) - got, err := g.Generate([]string{input}) - if err != nil { - t.Fatal(err) - } - if len(got) != 10 { - t.Errorf("got %v wanted %d", len(got), 10) - } +// got, err := g.Generate([]string{input}) +// if err != nil { +// t.Fatal(err) +// } +// if len(got) != 10 { +// t.Errorf("got %v wanted %d", len(got), 10) +// } -} +// } diff --git a/generator/generatorvars.go b/generator/generatorvars.go index 0e1bf8d..6591d65 100644 --- a/generator/generatorvars.go +++ b/generator/generatorvars.go @@ -5,7 +5,7 @@ import ( "strconv" "sync" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/spyzhov/ajson" ) diff --git a/go.mod b/go.mod index 2c30ca2..9555ab0 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,25 @@ module github.com/DevLabFoundry/configmanager/v3 -go 1.25.5 +go 1.26 + +toolchain go1.26.1 require ( - github.com/aws/aws-sdk-go-v2 v1.40.1 - github.com/aws/aws-sdk-go-v2/config v1.32.3 - github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5 + github.com/aws/aws-sdk-go-v2 v1.41.4 + github.com/aws/aws-sdk-go-v2/config v1.32.12 + github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 github.com/go-test/deep v1.1.1 github.com/hashicorp/vault/api v1.22.0 github.com/hashicorp/vault/api/auth/aws v0.11.0 github.com/spf13/cobra v1.10.2 github.com/spyzhov/ajson v0.9.6 - google.golang.org/grpc v1.77.0 - google.golang.org/protobuf v1.36.10 + google.golang.org/grpc v1.79.3 + google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -30,17 +32,17 @@ require ( require ( github.com/a8m/envsubst v1.4.3 github.com/aws/aws-sdk-go v1.55.8 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.3 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 // indirect - github.com/aws/smithy-go v1.24.0 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/aws/smithy-go v1.24.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect @@ -66,9 +68,9 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.10 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/time v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 // indirect ) diff --git a/go.sum b/go.sum index fa846d7..53815db 100644 --- a/go.sum +++ b/go.sum @@ -5,34 +5,64 @@ github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= github.com/aws/aws-sdk-go-v2 v1.40.1 h1:difXb4maDZkRH0x//Qkwcfpdg1XQVXEAEs2DdXldFFc= github.com/aws/aws-sdk-go-v2 v1.40.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/aws-sdk-go-v2/config v1.32.3 h1:cpz7H2uMNTDa0h/5CYL5dLUEzPSLo2g0NkbxTRJtSSU= github.com/aws/aws-sdk-go-v2/config v1.32.3/go.mod h1:srtPKaJJe3McW6T/+GMBZyIPc+SeqJsNPJsd4mOYZ6s= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.3 h1:01Ym72hK43hjwDeJUfi1l2oYLXBAOR8gNSZNmXmvuas= github.com/aws/aws-sdk-go-v2/credentials v1.19.3/go.mod h1:55nWF/Sr9Zvls0bGnWkRxUdhzKqj9uRNlPvgV1vgxKc= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 h1:utxLraaifrSBkeyII9mIbVwXXWrZdlPO7FIKmyLCEcY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15/go.mod h1:hW6zjYUDQwfz3icf4g2O41PHi77u10oAzJ84iSzR/lo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 h1:Y5YXgygXwDI5P4RkteB5yF7v35neH7LfJKBG+hzIons= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15/go.mod h1:K+/1EpG42dFSY7CBj+Fruzm8PsCGWTXJ3jdeJ659oGQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 h1:AvltKnW9ewxX2hFmQS0FyJH93aSvJVUEFvXfU+HWtSE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15/go.mod h1:3I4oCdZdmgrREhU74qS1dK9yZ62yumob+58AbFR4cQA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 h1:3/u/4yZOffg5jdNk1sDpOQ4Y+R6Xbh+GzpDrSZjuy3U= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15/go.mod h1:4Zkjq0FKjE78NKjabuM4tRXKFzUJWXgP0ItEZK8l7JU= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 h1:d/6xOGIllc/XW1lzG9a4AUBMmpLA9PXcQnVPTuHHcik= github.com/aws/aws-sdk-go-v2/service/signin v1.0.3/go.mod h1:fQ7E7Qj9GiW8y0ClD7cUJk3Bz5Iw8wZkWDHsTe8vDKs= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5 h1:YKGgwB1rye0JpV10Bfma3cZdQzX61j2HPWQw+YxWvrQ= github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5/go.mod h1:eBDSa0vuYB0lalpNxavIw80Q4Ksy08bhHHbT0aWa4tE= +github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 h1:bBoWhx8lsFLTXintRX64ZBXcmFZbGqUmaPUrjXECqIc= +github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3/go.mod h1:rcRkKbUJ2437WuXdq9fbj+MjTudYWzY9Ct8kiBbN8a8= github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 h1:8sTTiw+9yuNXcfWeqKF2x01GqCF49CpP4Z9nKrrk/ts= github.com/aws/aws-sdk-go-v2/service/sso v1.30.6/go.mod h1:8WYg+Y40Sn3X2hioaaWAAIngndR8n1XFdRPPX+7QBaM= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 h1:E+KqWoVsSrj1tJ6I/fjDIu5xoS2Zacuu1zT+H7KtiIk= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11/go.mod h1:qyWHz+4lvkXcr3+PoGlGHEI+3DLLiU6/GdrFfMaAhB0= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 h1:tzMkjh0yTChUqJDgGkcDdxvZDSrJ/WB6R6ymI5ehqJI= github.com/aws/aws-sdk-go-v2/service/sts v1.41.3/go.mod h1:T270C0R5sZNLbWUe8ueiAF42XSZxxPocTaGSgs5c/60= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -159,19 +189,26 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -181,19 +218,31 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 h1:sRy++txmErSjyVWlIgQB5nB+U75+Di+AH7eEZ002B/s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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= diff --git a/internal/cmdutils/cmdutils.go b/internal/cmdutils/cmdutils.go index b86b292..360b7d3 100644 --- a/internal/cmdutils/cmdutils.go +++ b/internal/cmdutils/cmdutils.go @@ -11,8 +11,8 @@ import ( "os" "strings" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" - "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/spf13/cobra" ) diff --git a/internal/cmdutils/cmdutils_test.go b/internal/cmdutils/cmdutils_test.go index 7ec1daf..f408f12 100644 --- a/internal/cmdutils/cmdutils_test.go +++ b/internal/cmdutils/cmdutils_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" "github.com/DevLabFoundry/configmanager/v3/internal/cmdutils" - "github.com/DevLabFoundry/configmanager/v3/internal/config" log "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/DevLabFoundry/configmanager/v3/internal/testutils" "github.com/spf13/cobra" diff --git a/internal/cmdutils/postprocessor.go b/internal/cmdutils/postprocessor.go index 3b4a33b..c166085 100644 --- a/internal/cmdutils/postprocessor.go +++ b/internal/cmdutils/postprocessor.go @@ -5,8 +5,8 @@ import ( "io" "strings" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" - "github.com/DevLabFoundry/configmanager/v3/internal/config" ) // PostProcessor diff --git a/internal/cmdutils/postprocessor_test.go b/internal/cmdutils/postprocessor_test.go index 5c18e23..2668748 100644 --- a/internal/cmdutils/postprocessor_test.go +++ b/internal/cmdutils/postprocessor_test.go @@ -5,9 +5,9 @@ import ( "strings" "testing" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" "github.com/DevLabFoundry/configmanager/v3/internal/cmdutils" - "github.com/DevLabFoundry/configmanager/v3/internal/config" "github.com/DevLabFoundry/configmanager/v3/internal/testutils" ) diff --git a/internal/lexer/lexer.go b/internal/lexer/lexer.go index a8f5d5a..ff352f3 100644 --- a/internal/lexer/lexer.go +++ b/internal/lexer/lexer.go @@ -4,7 +4,8 @@ package lexer import ( - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" + "github.com/DevLabFoundry/configmanager/v3/internal/token" ) // nonText characters captures all character sets that are _not_ assignable to TEXT @@ -59,93 +60,93 @@ func New(source Source, config config.GenVarsConfig) *Lexer { } // NextToken advances through the source returning a found token -func (l *Lexer) NextToken() config.Token { - var tok config.Token +func (l *Lexer) NextToken() token.Token { + var tok token.Token switch l.ch { // identify the dynamically selected key separator case l.keySeparator: - tok = config.Token{Type: config.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR, Literal: string(l.ch)} + tok = token.Token{Type: token.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR, Literal: string(l.ch)} // Specific cases for BEGIN_CONFIGMANAGER_TOKEN possibilities case 'A': if l.peekChar() == 'W' { // AWS store types l.readChar() if found, literal, imp := l.peekIsBeginOfToken([]config.ImplementationPrefix{config.SecretMgrPrefix, config.ParamStorePrefix}, "AW"); found { - tok = config.Token{Type: config.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} + tok = token.Token{Type: token.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} } else { // it is not a marker AW as text - tok = config.Token{Type: config.TEXT, Literal: "AW"} + tok = token.Token{Type: token.TEXT, Literal: "AW"} } } else if l.peekChar() == 'Z' { // Azure Store Types l.readChar() if found, literal, imp := l.peekIsBeginOfToken([]config.ImplementationPrefix{config.AzKeyVaultSecretsPrefix, config.AzTableStorePrefix, config.AzAppConfigPrefix}, "AZ"); found { - tok = config.Token{Type: config.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} + tok = token.Token{Type: token.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} } else { // it is not a marker AZ as text - tok = config.Token{Type: config.TEXT, Literal: "AZ"} + tok = token.Token{Type: token.TEXT, Literal: "AZ"} } } else { - tok = config.Token{Type: config.TEXT, Literal: "A"} + tok = token.Token{Type: token.TEXT, Literal: "A"} } case 'G': // GCP TOKENS if l.peekChar() == 'C' { l.readChar() if found, literal, imp := l.peekIsBeginOfToken([]config.ImplementationPrefix{config.GcpSecretsPrefix}, "GC"); found { - tok = config.Token{Type: config.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} + tok = token.Token{Type: token.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} } else { // it is not a marker - GC literal as text - tok = config.Token{Type: config.TEXT, Literal: "GC"} + tok = token.Token{Type: token.TEXT, Literal: "GC"} } } else { - tok = config.Token{Type: config.TEXT, Literal: "G"} + tok = token.Token{Type: token.TEXT, Literal: "G"} } case 'V': // HASHI VAULT Tokens if l.peekChar() == 'A' { l.readChar() if found, literal, imp := l.peekIsBeginOfToken([]config.ImplementationPrefix{config.HashicorpVaultPrefix}, "VA"); found { - tok = config.Token{Type: config.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} + tok = token.Token{Type: token.BEGIN_CONFIGMANAGER_TOKEN, Literal: literal, ImpPrefix: imp} } else { // it is not a marker VA as text - tok = config.Token{Type: config.TEXT, Literal: "VA"} + tok = token.Token{Type: token.TEXT, Literal: "VA"} } } else { - tok = config.Token{Type: config.TEXT, Literal: "V"} + tok = token.Token{Type: token.TEXT, Literal: "V"} } case '=': - tok = config.Token{Type: config.EQUALS, Literal: "="} + tok = token.Token{Type: token.EQUALS, Literal: "="} case '.': - tok = config.Token{Type: config.DOT, Literal: "."} + tok = token.Token{Type: token.DOT, Literal: "."} case ',': - tok = config.Token{Type: config.COMMA, Literal: ","} + tok = token.Token{Type: token.COMMA, Literal: ","} case '/': if l.peekChar() == '?' { l.readChar() - tok = config.Token{Type: config.SLASH_QUESTION_MARK, Literal: "/?"} + tok = token.Token{Type: token.SLASH_QUESTION_MARK, Literal: "/?"} } else { - tok = config.Token{Type: config.FORWARD_SLASH, Literal: "/"} + tok = token.Token{Type: token.FORWARD_SLASH, Literal: "/"} } case '\\': - tok = config.Token{Type: config.BACK_SLASH, Literal: "\\"} + tok = token.Token{Type: token.BACK_SLASH, Literal: "\\"} case '?': - tok = config.Token{Type: config.QUESTION_MARK, Literal: "?"} + tok = token.Token{Type: token.QUESTION_MARK, Literal: "?"} case ']': - tok = config.Token{Type: config.END_META_CONFIGMANAGER_TOKEN, Literal: "]"} + tok = token.Token{Type: token.END_META_CONFIGMANAGER_TOKEN, Literal: "]"} case '[': - tok = config.Token{Type: config.BEGIN_META_CONFIGMANAGER_TOKEN, Literal: "["} + tok = token.Token{Type: token.BEGIN_META_CONFIGMANAGER_TOKEN, Literal: "["} case '|': - tok = config.Token{Type: config.PIPE, Literal: "|"} + tok = token.Token{Type: token.PIPE, Literal: "|"} case '@': - tok = config.Token{Type: config.AT_SIGN, Literal: "@"} + tok = token.Token{Type: token.AT_SIGN, Literal: "@"} case ':': - tok = config.Token{Type: config.COLON, Literal: ":"} + tok = token.Token{Type: token.COLON, Literal: ":"} case '"': - tok = config.Token{Type: config.DOUBLE_QUOTE, Literal: "\""} + tok = token.Token{Type: token.DOUBLE_QUOTE, Literal: "\""} case '\'': - tok = config.Token{Type: config.SINGLE_QUOTE, Literal: "'"} + tok = token.Token{Type: token.SINGLE_QUOTE, Literal: "'"} case '\n': l.line = l.line + 1 l.column = 0 // reset column count @@ -155,19 +156,19 @@ func (l *Lexer) NextToken() config.Token { tok = l.setTextSeparatorToken() case 0: tok.Literal = "" - tok.Type = config.EOF + tok.Type = token.EOF default: if isText(l.ch) { tok.Literal = l.readText() - tok.Type = config.TEXT + tok.Type = token.TEXT return tok } - tok = newToken(config.ILLEGAL, l.ch) + tok = newToken(token.ILLEGAL, l.ch) } // add general properties to each token tok.Line = l.line tok.Column = l.column - tok.Source = config.Source{Path: l.source.FullPath, File: l.source.FileName} + tok.Source = token.Source{Path: l.source.FullPath, File: l.source.FileName} l.readChar() return tok } @@ -201,8 +202,8 @@ func (l *Lexer) readText() string { return l.source.Input[position:l.position] } -func (l *Lexer) setTextSeparatorToken() config.Token { - tok := newToken(config.LookupIdent(string(l.ch)), l.ch) +func (l *Lexer) setTextSeparatorToken() token.Token { + tok := newToken(token.LookupIdent(string(l.ch)), l.ch) return tok } @@ -236,6 +237,6 @@ func isText(ch byte) bool { return !nonText[string(ch)] } -func newToken(tokenType config.TokenType, ch byte) config.Token { - return config.Token{Type: tokenType, Literal: string(ch)} +func newToken(tokenType token.TokenType, ch byte) token.Token { + return token.Token{Type: tokenType, Literal: string(ch)} } diff --git a/internal/lexer/lexer_test.go b/internal/lexer/lexer_test.go index 97f6ba0..ec90e35 100644 --- a/internal/lexer/lexer_test.go +++ b/internal/lexer/lexer_test.go @@ -3,8 +3,9 @@ package lexer_test import ( "testing" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/internal/lexer" + "github.com/DevLabFoundry/configmanager/v3/internal/token" ) func Test_Lexer_NextToken(t *testing.T) { @@ -13,60 +14,60 @@ foo=AWSPARAMSTR:///path|keyAWSSECRETS:///foo META_INCLUDED=VAULT://baz/bar/123|key1.prop2[role=arn:aws:iam::1111111:role,version=1082313] ` ttests := []struct { - expectedType config.TokenType + expectedType token.TokenType expectedLiteral string }{ - {config.TEXT, "foo"}, - {config.SPACE, " "}, - {config.TEXT, "stuyfsdfsf"}, - {config.NEW_LINE, "\n"}, - {config.TEXT, "foo"}, - {config.EQUALS, "="}, - {config.BEGIN_CONFIGMANAGER_TOKEN, "AWSPARAMSTR://"}, - {config.FORWARD_SLASH, "/"}, - {config.TEXT, "path"}, - {config.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR, "|"}, - {config.TEXT, "key"}, - {config.BEGIN_CONFIGMANAGER_TOKEN, "AWSSECRETS://"}, - {config.FORWARD_SLASH, "/"}, - {config.TEXT, "foo"}, - {config.NEW_LINE, "\n"}, - {config.TEXT, "MET"}, - {config.TEXT, "A"}, - {config.TEXT, "_INCLUDED"}, - // {config.TEXT, "U"}, - // {config.TEXT, "DED"}, - {config.EQUALS, "="}, - {config.BEGIN_CONFIGMANAGER_TOKEN, "VAULT://"}, - {config.TEXT, "baz"}, - {config.FORWARD_SLASH, "/"}, - {config.TEXT, "bar"}, - {config.FORWARD_SLASH, "/"}, - {config.TEXT, "123"}, - {config.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR, "|"}, - {config.TEXT, "key1"}, - {config.DOT, "."}, - {config.TEXT, "prop2"}, - {config.BEGIN_META_CONFIGMANAGER_TOKEN, "["}, - {config.TEXT, "role"}, - {config.EQUALS, "="}, - {config.TEXT, "arn"}, - {config.COLON, ":"}, - {config.TEXT, "aws"}, - {config.COLON, ":"}, - {config.TEXT, "iam"}, - {config.COLON, ":"}, - {config.COLON, ":"}, - {config.TEXT, "1111111"}, - {config.COLON, ":"}, - {config.TEXT, "role"}, - {config.COMMA, ","}, - {config.TEXT, "version"}, - {config.EQUALS, "="}, - {config.TEXT, "1082313"}, - {config.END_META_CONFIGMANAGER_TOKEN, "]"}, - {config.NEW_LINE, "\n"}, - {config.EOF, ""}, + {token.TEXT, "foo"}, + {token.SPACE, " "}, + {token.TEXT, "stuyfsdfsf"}, + {token.NEW_LINE, "\n"}, + {token.TEXT, "foo"}, + {token.EQUALS, "="}, + {token.BEGIN_CONFIGMANAGER_TOKEN, "AWSPARAMSTR://"}, + {token.FORWARD_SLASH, "/"}, + {token.TEXT, "path"}, + {token.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR, "|"}, + {token.TEXT, "key"}, + {token.BEGIN_CONFIGMANAGER_TOKEN, "AWSSECRETS://"}, + {token.FORWARD_SLASH, "/"}, + {token.TEXT, "foo"}, + {token.NEW_LINE, "\n"}, + {token.TEXT, "MET"}, + {token.TEXT, "A"}, + {token.TEXT, "_INCLUDED"}, + // {token.TEXT, "U"}, + // {token.TEXT, "DED"}, + {token.EQUALS, "="}, + {token.BEGIN_CONFIGMANAGER_TOKEN, "VAULT://"}, + {token.TEXT, "baz"}, + {token.FORWARD_SLASH, "/"}, + {token.TEXT, "bar"}, + {token.FORWARD_SLASH, "/"}, + {token.TEXT, "123"}, + {token.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR, "|"}, + {token.TEXT, "key1"}, + {token.DOT, "."}, + {token.TEXT, "prop2"}, + {token.BEGIN_META_CONFIGMANAGER_TOKEN, "["}, + {token.TEXT, "role"}, + {token.EQUALS, "="}, + {token.TEXT, "arn"}, + {token.COLON, ":"}, + {token.TEXT, "aws"}, + {token.COLON, ":"}, + {token.TEXT, "iam"}, + {token.COLON, ":"}, + {token.COLON, ":"}, + {token.TEXT, "1111111"}, + {token.COLON, ":"}, + {token.TEXT, "role"}, + {token.COMMA, ","}, + {token.TEXT, "version"}, + {token.EQUALS, "="}, + {token.TEXT, "1082313"}, + {token.END_META_CONFIGMANAGER_TOKEN, "]"}, + {token.NEW_LINE, "\n"}, + {token.EOF, ""}, } l := lexer.New(lexer.Source{Input: input, FullPath: "/foo/bar", FileName: "bar"}, *config.NewConfig()) @@ -83,7 +84,7 @@ META_INCLUDED=VAULT://baz/bar/123|key1.prop2[role=arn:aws:iam::1111111:role,vers t.Fatalf("tests[%d] - literal wrong. got=%q, expected=%q", i, tok.Literal, tt.expectedLiteral) } - if tok.Type == config.BEGIN_CONFIGMANAGER_TOKEN { + if tok.Type == token.BEGIN_CONFIGMANAGER_TOKEN { } } @@ -93,7 +94,7 @@ func Test_empty_file(t *testing.T) { input := `` l := lexer.New(lexer.Source{Input: input, FullPath: "/foo/bar", FileName: "bar"}, *config.NewConfig()) tok := l.NextToken() - if tok.Type != config.EOF { + if tok.Type != token.EOF { t.Fatal("expected EOF") } } diff --git a/internal/parser/parser.go b/internal/parser/parser.go index b785dc2..51ae8e4 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -3,11 +3,13 @@ package parser import ( "errors" "fmt" + "os" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/internal/lexer" "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/internal/token" ) func wrapErr(incompleteToken *config.ParsedTokenConfig, sanitized string, line, position int, etyp error) error { @@ -20,17 +22,17 @@ var ( ) type ConfigManagerTokenBlock struct { - BeginToken config.Token + BeginToken token.Token ParsedToken config.ParsedTokenConfig - EndToken config.Token + EndToken token.Token } type Parser struct { l *lexer.Lexer errors []error log log.ILogger - currentToken config.Token - peekToken config.Token + currentToken token.Token + peekToken token.Token config *config.GenVarsConfig environ []string } @@ -68,10 +70,10 @@ func (p *Parser) WithLogger(logger log.ILogger) *Parser { func (p *Parser) Parse() ([]ConfigManagerTokenBlock, []error) { stmts := []ConfigManagerTokenBlock{} - for !p.currentTokenIs(config.EOF) { - if p.currentTokenIs(config.BEGIN_CONFIGMANAGER_TOKEN) { + for !p.currentTokenIs(token.EOF) { + if p.currentTokenIs(token.BEGIN_CONFIGMANAGER_TOKEN) { // continues to read the tokens until it hits an end token or errors - configManagerToken, err := config.NewToken(p.currentToken.ImpPrefix, *p.config) + configManagerToken, err := config.NewParsedToken(p.currentToken.ImpPrefix, *p.config) if err != nil { return nil, []error{err} } @@ -90,21 +92,21 @@ func (p *Parser) nextToken() { p.peekToken = p.l.NextToken() } -func (p *Parser) currentTokenIs(t config.TokenType) bool { +func (p *Parser) currentTokenIs(t token.TokenType) bool { return p.currentToken.Type == t } -func (p *Parser) peekTokenIs(t config.TokenType) bool { +func (p *Parser) peekTokenIs(t token.TokenType) bool { return p.peekToken.Type == t } func (p *Parser) peekTokenIsEnd() bool { - endTokens := map[config.TokenType]bool{ - config.AT_SIGN: true, config.QUESTION_MARK: true, config.COLON: true, - config.SLASH_QUESTION_MARK: true, config.EOF: true, + endTokens := map[token.TokenType]bool{ + token.AT_SIGN: true, token.QUESTION_MARK: true, token.COLON: true, + token.SLASH_QUESTION_MARK: true, token.EOF: true, // traditional ends of tokens - config.DOUBLE_QUOTE: true, config.SINGLE_QUOTE: true, config.SPACE: true, - config.NEW_LINE: true, + token.DOUBLE_QUOTE: true, token.SINGLE_QUOTE: true, token.SPACE: true, + token.NEW_LINE: true, } return endTokens[p.peekToken.Type] } @@ -121,11 +123,11 @@ func (p *Parser) buildConfigManagerTokenFromBlocks(configManagerToken *config.Pa sanitizedToken := "" // stop on end of file - for !p.peekTokenIs(config.EOF) { + for !p.peekTokenIs(token.EOF) { // // This is the target state when there is an optional token wrapping // // e.g. `{{ IMP://path }}` // // currently this is untestable - // if p.peekTokenIs(config.END_CONFIGMANAGER_TOKEN) { + // if p.peekTokenIs(token.END_CONFIGMANAGER_TOKEN) { // notFoundEnd = false // fullToken += p.curToken.Literal // sanitizedToken += p.curToken.Literal @@ -135,7 +137,7 @@ func (p *Parser) buildConfigManagerTokenFromBlocks(configManagerToken *config.Pa // when next token is another token // i.e. the tokens are adjacent - if p.peekTokenIs(config.BEGIN_CONFIGMANAGER_TOKEN) { + if p.peekTokenIs(token.BEGIN_CONFIGMANAGER_TOKEN) { sanitizedToken += p.currentToken.Literal stmt.EndToken = p.currentToken break @@ -155,7 +157,7 @@ func (p *Parser) buildConfigManagerTokenFromBlocks(configManagerToken *config.Pa // check key separator this marks the end of a normal token path // // keyLookup and Metadata are optional - is always specified in that order - if p.currentTokenIs(config.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR) { + if p.currentTokenIs(token.CONFIGMANAGER_TOKEN_KEY_PATH_SEPARATOR) { if err := p.buildKeyPathSeparator(configManagerToken); err != nil { p.errors = append(p.errors, wrapErr(configManagerToken, sanitizedToken, currentToken.Line, currentToken.Column, err)) return nil @@ -166,7 +168,7 @@ func (p *Parser) buildConfigManagerTokenFromBlocks(configManagerToken *config.Pa // optionally at the end of the path without key separator // check metadata there can be a metadata bracket `[key=val,k1=v2]` - if p.currentTokenIs(config.BEGIN_META_CONFIGMANAGER_TOKEN) { + if p.currentTokenIs(token.BEGIN_META_CONFIGMANAGER_TOKEN) { if err := p.buildMetadata(configManagerToken); err != nil { p.errors = append(p.errors, wrapErr(configManagerToken, sanitizedToken, currentToken.Line, currentToken.Column, err)) return nil @@ -180,7 +182,7 @@ func (p *Parser) buildConfigManagerTokenFromBlocks(configManagerToken *config.Pa // we want set the current token // else it would be lost once the parser is advanced below p.nextToken() - if p.peekTokenIs(config.EOF) { + if p.peekTokenIs(token.EOF) { sanitizedToken += p.currentToken.Literal stmt.EndToken = p.currentToken break @@ -198,14 +200,14 @@ func (p *Parser) buildKeyPathSeparator(configManagerToken *config.ParsedTokenCon // advance to next token i.e. post the path separator p.nextToken() keyPath := "" - if p.peekTokenIs(config.EOF) { + if p.peekTokenIs(token.EOF) { // if the next token EOF we set the path as current token and exit // otherwise we would never hit the below loop configManagerToken.WithKeyPath(p.currentToken.Literal) return nil } - for !p.peekTokenIs(config.EOF) { - if p.peekTokenIs(config.BEGIN_META_CONFIGMANAGER_TOKEN) { + for !p.peekTokenIs(token.EOF) { + if p.peekTokenIs(token.BEGIN_META_CONFIGMANAGER_TOKEN) { // add current token to the keysPath and move onto the metadata keyPath += p.currentToken.Literal p.nextToken() @@ -215,13 +217,13 @@ func (p *Parser) buildKeyPathSeparator(configManagerToken *config.ParsedTokenCon break } // touching another token or end of token - if p.peekTokenIs(config.BEGIN_CONFIGMANAGER_TOKEN) || p.peekTokenIsEnd() { + if p.peekTokenIs(token.BEGIN_CONFIGMANAGER_TOKEN) || p.peekTokenIsEnd() { keyPath += p.currentToken.Literal break } keyPath += p.currentToken.Literal p.nextToken() - if p.peekTokenIs(config.EOF) { + if p.peekTokenIs(token.EOF) { // check if the next token is EOF once advanced // if it is we want to consume current token else it will be skipped keyPath += p.currentToken.Literal @@ -238,16 +240,16 @@ var ErrMetadataEmpty = errors.New("emtpy metadata") func (p *Parser) buildMetadata(configManagerToken *config.ParsedTokenConfig) error { metadata := "" found := false - if p.peekTokenIs(config.END_META_CONFIGMANAGER_TOKEN) { + if p.peekTokenIs(token.END_META_CONFIGMANAGER_TOKEN) { return fmt.Errorf("%w, metadata brackets must include at least one set of key=value pairs", ErrMetadataEmpty) } p.nextToken() - for !p.peekTokenIs(config.EOF) { + for !p.peekTokenIs(token.EOF) { if p.peekTokenIsEnd() { // next token is an end of token but no closing `]` found return fmt.Errorf("%w, metadata (%s) string has no closing", ErrNoEndTagFound, metadata) } - if p.peekTokenIs(config.END_META_CONFIGMANAGER_TOKEN) { + if p.peekTokenIs(token.END_META_CONFIGMANAGER_TOKEN) { metadata += p.currentToken.Literal found = true p.nextToken() diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go index 9b93cf5..7fe25e7 100644 --- a/internal/parser/parser_test.go +++ b/internal/parser/parser_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/internal/lexer" "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/DevLabFoundry/configmanager/v3/internal/parser" diff --git a/internal/store/plugin.go b/internal/store/plugin.go index ee6fe83..eeb0994 100644 --- a/internal/store/plugin.go +++ b/internal/store/plugin.go @@ -4,7 +4,7 @@ import ( "context" "os/exec" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/plugins" "github.com/hashicorp/go-plugin" ) diff --git a/internal/store/plugin_test.go b/internal/store/plugin_test.go index 47a840a..0185b5e 100644 --- a/internal/store/plugin_test.go +++ b/internal/store/plugin_test.go @@ -7,25 +7,26 @@ import ( "runtime" "testing" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/internal/store" ) // TODO: make the implementation of the plugin system more testable func TestPlugin_GetValue_integration(t *testing.T) { - t.Skip() // as the plugin is technically a subprocess // setting env vars at this level will affect the loaded plugin os.Setenv("AWS_REGION", "eu-west-1") os.Setenv("AWS_PROFILE", "FOO") - tp := fmt.Sprintf("../../.configmanager/plugins/awsparamstr/awsparamstr-%s-%s", runtime.GOOS, runtime.GOARCH) + defer os.Unsetenv("AWS_PROFILE") + defer os.Unsetenv("AWS_REGION") + tp := fmt.Sprintf("../../.configmanager/plugins/empty/empty-%s-%s", runtime.GOOS, runtime.GOARCH) np, err := store.NewPlugin(context.TODO(), tp) if err != nil { t.Fatal(err) } defer np.ClientCleanUp() - token, err := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + token, err := config.NewParsedToken(config.ParamStorePrefix, *config.NewConfig()) if err != nil { t.Fatal(err) } @@ -37,6 +38,9 @@ func TestPlugin_GetValue_integration(t *testing.T) { } if len(got) < 1 { - t.Error("empty...") + t.Fatal("empty...") + } + if got != "/int-test/pocketbase/admin-pwd->" { + t.Errorf("") } } diff --git a/internal/store/store.go b/internal/store/store.go index 79ae798..798c6f7 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -10,7 +10,7 @@ import ( "strings" "sync" - "github.com/DevLabFoundry/configmanager/v3/internal/config" + "github.com/DevLabFoundry/configmanager/v3/config" ) var ( @@ -21,22 +21,20 @@ var ( ErrPluginNotFound = errors.New("plugin does not exist") ) -// Strategy iface that all store implementations -// must conform to, in order to be be used by the retrieval implementation -// -// Defined on the package for easier re-use across the program -type Strategy interface { - // Value retrieves the underlying value for the token - Value() (s string, e error) - // SetToken - SetToken(s *config.ParsedTokenConfig) -} +// // Strategy iface that all store implementations +// // must conform to, in order to be be used by the retrieval implementation +// // +// // Defined on the package for easier re-use across the program +// type Strategy interface { +// // Value retrieves the underlying value for the token +// Value() (s string, e error) +// // SetToken +// SetToken(s *config.ParsedTokenConfig) +// } -// // It includes the following methods // - fetch plugins from known sources // - maintains a list of tokens answerable by a specified pluginEngine - type pluginMap struct { mu *sync.Mutex // m holds the map of plugins where the key is the lowercased implementation prefix @@ -56,25 +54,27 @@ const ( ) type Store struct { - pluginLocation []string - plugin pluginMap - // PluginCleanUp func() + plugin pluginMap } -func Init(ctx context.Context, implt []string) (*Store, error) { +func New(ctx context.Context) *Store { pm := pluginMap{mu: &sync.Mutex{}, m: make(map[string]*Plugin)} + s := &Store{plugin: pm} + return s +} + +// Init ensures all the discovered tokens have their implementations initialised +func (s *Store) Init(ctx context.Context, implt []string) error { - // l := []string{""} - // for _, plugin := range implt { plpath, err := findPlugin(plugin) if err != nil { - return nil, err + return err } p, err := NewPlugin(ctx, plpath) - pm.Add(plugin, p) + s.plugin.Add(plugin, p) } - return &Store{plugin: pm}, nil + return nil } func (s *Store) GetImplementation(implemenation config.ImplementationPrefix) (plugin *Plugin, err error) { diff --git a/internal/config/token.go b/internal/token/token.go similarity index 95% rename from internal/config/token.go rename to internal/token/token.go index 00b13a6..c03de69 100644 --- a/internal/config/token.go +++ b/internal/token/token.go @@ -1,4 +1,6 @@ -package config +package token + +import "github.com/DevLabFoundry/configmanager/v3/config" // TokenType is the lexer parsed TokenType type TokenType string @@ -58,7 +60,7 @@ type Source struct { type Token struct { Type TokenType Literal string - ImpPrefix ImplementationPrefix + ImpPrefix config.ImplementationPrefix Line int Column int Source Source diff --git a/internal/token/token_test.go b/internal/token/token_test.go new file mode 100644 index 0000000..e25146f --- /dev/null +++ b/internal/token/token_test.go @@ -0,0 +1,25 @@ +package token_test + +import ( + "testing" + + "github.com/DevLabFoundry/configmanager/v3/internal/token" +) + +func TestLookupIdent(t *testing.T) { + ttests := map[string]struct { + char string + expect token.TokenType + }{ + "new line": {"\n", token.NEW_LINE}, + "dash": {"-", token.TEXT}, + } + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + got := token.LookupIdent(tt.char) + if got != tt.expect { + t.Errorf("got %v wanted %v", got, tt.expect) + } + }) + } +} diff --git a/plugins/awsparamstr/go.mod b/plugins/awsparamstr/go.mod new file mode 100644 index 0000000..7289550 --- /dev/null +++ b/plugins/awsparamstr/go.mod @@ -0,0 +1,46 @@ +module github.com/DevLabFoundry/configmanager/plugins/awsparamstr + +go 1.26 + +toolchain go1.26.1 + +require ( + github.com/DevLabFoundry/configmanager/v3 v3.0.0 + github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 + github.com/hashicorp/go-hclog v1.6.3 +) + +require ( + github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/oklog/run v1.2.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) + +require ( + github.com/aws/aws-sdk-go-v2 v1.41.4 + github.com/aws/aws-sdk-go-v2/config v1.32.12 + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect + github.com/aws/smithy-go v1.24.2 // indirect + github.com/hashicorp/go-plugin v1.7.0 +) + +replace github.com/DevLabFoundry/configmanager/v3 v3.0.0 => ../../../configmanager diff --git a/plugins/awsparamstr/go.sum b/plugins/awsparamstr/go.sum new file mode 100644 index 0000000..20a3784 --- /dev/null +++ b/plugins/awsparamstr/go.sum @@ -0,0 +1,109 @@ +github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= +github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= +github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= +github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 h1:bBoWhx8lsFLTXintRX64ZBXcmFZbGqUmaPUrjXECqIc= +github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3/go.mod h1:rcRkKbUJ2437WuXdq9fbj+MjTudYWzY9Ct8kiBbN8a8= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= +github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 h1:sRy++txmErSjyVWlIgQB5nB+U75+Di+AH7eEZ002B/s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/awsparamstr/impl/paramstore.go b/plugins/awsparamstr/impl/paramstore.go index 6255252..23f98a0 100644 --- a/plugins/awsparamstr/impl/paramstore.go +++ b/plugins/awsparamstr/impl/paramstore.go @@ -3,12 +3,12 @@ package impl import ( "context" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/plugins" "github.com/aws/aws-sdk-go-v2/aws" awsConf "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/hashicorp/go-hclog" ) type paramStoreApi interface { @@ -20,14 +20,14 @@ type ParamStore struct { ctx context.Context config *ParamStrConfig token *config.ParsedTokenConfig - logger log.ILogger + logger hclog.Logger } type ParamStrConfig struct { // reserved for potential future use } -func NewParamStore(ctx context.Context, logger log.ILogger) (*ParamStore, error) { +func NewParamStore(ctx context.Context, logger hclog.Logger) (*ParamStore, error) { cfg, err := awsConf.LoadDefaultConfig(ctx) if err != nil { return nil, err @@ -58,7 +58,7 @@ func (imp *ParamStore) Value(token string, metadata []byte) (string, error) { result, err := imp.svc.GetParameter(ctx, input) if err != nil { - imp.logger.Error(plugins.ImplementationNetworkErr, config.ParamStorePrefix, err, token) + imp.logger.Error(plugins.ImplementationNetworkErr, "config.ParamStorePrefix", err, token) return "", err } diff --git a/plugins/awsparamstr/impl/paramstore_test.go b/plugins/awsparamstr/impl/paramstore_test.go index cc64dfc..948fd52 100644 --- a/plugins/awsparamstr/impl/paramstore_test.go +++ b/plugins/awsparamstr/impl/paramstore_test.go @@ -3,16 +3,19 @@ package impl_test import ( "context" "fmt" - "io" "strings" "testing" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" - "github.com/DevLabFoundry/configmanager/v3/plugins/awsparamstr/impl" + "github.com/DevLabFoundry/configmanager/plugins/awsparamstr/impl" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/ssm/types" + "github.com/hashicorp/go-hclog" +) + +const ( + TestPhrase string = "got: %v want: %v\n" + TestPhraseWithContext string = "%s\n got: %v\n\n want: %v\n" ) type mockParamApi func(ctx context.Context, params *ssm.GetParameterInput, optFns ...func(*ssm.Options)) (*ssm.GetParameterOutput, error) @@ -52,7 +55,7 @@ func Test_GetParamStore(t *testing.T) { "successVal": { func() *config.ParsedTokenConfig { // "VAULT://secret___/demo/configmanager" - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.ParamStorePrefix, *config.NewConfig()) tkn.WithSanitizedToken("/token/1") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -71,7 +74,7 @@ func Test_GetParamStore(t *testing.T) { "successVal with keyseparator": { func() *config.ParsedTokenConfig { // "AWSPARAMSTR#/token/1|somekey", - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.ParamStorePrefix, *config.NewConfig()) tkn.WithSanitizedToken("/token/1") tkn.WithKeyPath("somekey") tkn.WithMetadata("") @@ -95,7 +98,7 @@ func Test_GetParamStore(t *testing.T) { "errored": { func() *config.ParsedTokenConfig { // "AWSPARAMSTR#/token/1", - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.ParamStorePrefix, *config.NewConfig()) tkn.WithSanitizedToken("/token/1") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -112,7 +115,7 @@ func Test_GetParamStore(t *testing.T) { "nil to empty": { func() *config.ParsedTokenConfig { // "AWSPARAMSTR#/token/1", - tkn, _ := config.NewToken(config.ParamStorePrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.ParamStorePrefix, *config.NewConfig()) tkn.WithSanitizedToken("/token/1") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -131,21 +134,21 @@ func Test_GetParamStore(t *testing.T) { } for name, tt := range tests { t.Run(name, func(t *testing.T) { - impl, err := impl.NewParamStore(context.TODO(), log.New(io.Discard)) + impl, err := impl.NewParamStore(context.TODO(), hclog.NewNullLogger()) if err != nil { - t.Errorf(testutils.TestPhrase, err.Error(), nil) + t.Errorf(TestPhrase, err.Error(), nil) } impl.WithSvc(tt.mockClient(t)) got, err := impl.Value(tt.token().StoreToken(), []byte{}) if err != nil { if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) + t.Errorf(TestPhrase, err.Error(), tt.expect) } return } if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) + t.Errorf(TestPhrase, got, tt.expect) } }) } diff --git a/plugins/awsparamstr/main.go b/plugins/awsparamstr/main.go index c4b340f..e9db859 100644 --- a/plugins/awsparamstr/main.go +++ b/plugins/awsparamstr/main.go @@ -2,20 +2,19 @@ package main import ( "context" - "os" - "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/plugins/awsparamstr/impl" "github.com/DevLabFoundry/configmanager/v3/plugins" - "github.com/DevLabFoundry/configmanager/v3/plugins/awsparamstr/impl" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" ) -// Here is a real implementation of KV that writes to a local file with -// the key name and the contents are the value of the key. -type TokenStorePlugin struct{} +type TokenStorePlugin struct { + log hclog.Logger +} func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { - srv, err := impl.NewParamStore(context.Background(), log.New(os.Stderr)) + srv, err := impl.NewParamStore(context.Background(), ts.log) if err != nil { return "", err } @@ -23,10 +22,16 @@ func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { } func main() { + log := hclog.New(hclog.DefaultOptions) + log.SetLevel(hclog.LevelFromString("error")) + + // if os.Getenv("CONFIGMANAGER_LOG") + ts := TokenStorePlugin{log: log} plugin.Serve(&plugin.ServeConfig{ + // Logger: , HandshakeConfig: plugins.Handshake, Plugins: map[string]plugin.Plugin{ - "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: &TokenStorePlugin{}}, + "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: ts}, }, // A non-nil value here enables gRPC serving for this plugin... GRPCServer: plugin.DefaultGRPCServer, diff --git a/plugins/empty/main.go b/plugins/empty/main.go new file mode 100644 index 0000000..db5a189 --- /dev/null +++ b/plugins/empty/main.go @@ -0,0 +1,35 @@ +// Package main of empty implementation is used for "unit" testing +// +// The TokenStore Value implementation returns the key and metadata passed +// in the case of key being `err` a simulated error is returned +package main + +import ( + "errors" + "fmt" + + "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/hashicorp/go-plugin" +) + +// TokenStorePlugin here is a sample plugin we can use in tests +// It handles some basic error scenarios +type TokenStorePlugin struct{} + +func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { + if key == "err" { + return "", errors.New("token store implementation simulated error") + } + return fmt.Sprintf("%s->%s", key, metadata), nil +} + +func main() { + plugin.Serve(&plugin.ServeConfig{ + HandshakeConfig: plugins.Handshake, + Plugins: map[string]plugin.Plugin{ + "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: &TokenStorePlugin{}}, + }, + // A non-nil value here enables gRPC serving for this plugin... + GRPCServer: plugin.DefaultGRPCServer, + }) +} diff --git a/plugins/vault/impl/hashivault.go b/plugins/vault/impl/hashivault.go index 75ba351..ab0061d 100644 --- a/plugins/vault/impl/hashivault.go +++ b/plugins/vault/impl/hashivault.go @@ -8,10 +8,10 @@ import ( "strconv" "strings" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/hashicorp/go-hclog" vault "github.com/hashicorp/vault/api" auth "github.com/hashicorp/vault/api/auth/aws" ) @@ -30,7 +30,7 @@ type hashiVaultApi interface { type VaultStore struct { svc hashiVaultApi ctx context.Context - logger log.ILogger + logger hclog.Logger config *VaultConfig token *config.ParsedTokenConfig strippedToken string @@ -42,7 +42,7 @@ type VaultConfig struct { Role string `json:"iam_role"` } -func NewVaultStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*VaultStore, error) { +func NewVaultStore(ctx context.Context, token *config.ParsedTokenConfig, logger hclog.Logger) (*VaultStore, error) { storeConf := &VaultConfig{} _ = token.ParseMetadata(storeConf) imp := &VaultStore{ diff --git a/plugins/vault/impl/hashivault_test.go b/plugins/vault/impl/hashivault_test.go index 8e6f94a..7911bc9 100644 --- a/plugins/vault/impl/hashivault_test.go +++ b/plugins/vault/impl/hashivault_test.go @@ -3,17 +3,16 @@ package impl_test import ( "context" "fmt" - "io" "net/http" "net/http/httptest" "os" "strings" "testing" - "github.com/DevLabFoundry/configmanager/v3/internal/config" - "github.com/DevLabFoundry/configmanager/v3/internal/log" + "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/internal/testutils" "github.com/DevLabFoundry/configmanager/v3/plugins/vault/impl" + "github.com/hashicorp/go-hclog" vault "github.com/hashicorp/vault/api" ) @@ -25,7 +24,7 @@ func TestMountPathExtract(t *testing.T) { "without leading slash": { func() *config.ParsedTokenConfig { // "VAULT://secret___/demo/configmanager" - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/demo/configmanager") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -34,7 +33,7 @@ func TestMountPathExtract(t *testing.T) { "with leading slash": { func() *config.ParsedTokenConfig { // "VAULT:///secret___/demo/configmanager", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("/secret___/demo/configmanager") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -42,7 +41,7 @@ func TestMountPathExtract(t *testing.T) { }, "secret"}, "with underscore in path name": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("_secret___/demo/configmanager") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -50,7 +49,7 @@ func TestMountPathExtract(t *testing.T) { }, "_secret"}, "with double underscore in path name": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("__secret___/demo/configmanager") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -58,7 +57,7 @@ func TestMountPathExtract(t *testing.T) { }, "__secret"}, "with multiple paths in mountpath": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret/bar/path___/demo/configmanager") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -98,7 +97,7 @@ func TestVaultScenarios(t *testing.T) { }{ "happy return": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/foo") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -128,7 +127,7 @@ func TestVaultScenarios(t *testing.T) { "incorrect json": { func() *config.ParsedTokenConfig { // "VAULT://secret___/foo", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/foo") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -157,7 +156,7 @@ func TestVaultScenarios(t *testing.T) { }, "another return": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret/engine1___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -188,7 +187,7 @@ func TestVaultScenarios(t *testing.T) { "not found": { func() *config.ParsedTokenConfig { // "VAULT://secret___/foo", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/foo") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -216,7 +215,7 @@ func TestVaultScenarios(t *testing.T) { "403": { func() *config.ParsedTokenConfig { // "VAULT://secret___/some/other/foo2", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -244,7 +243,7 @@ func TestVaultScenarios(t *testing.T) { "found but empty": { func() *config.ParsedTokenConfig { // "VAULT://secret___/some/other/foo2", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -273,7 +272,7 @@ func TestVaultScenarios(t *testing.T) { }, "found but nil returned": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -301,7 +300,7 @@ func TestVaultScenarios(t *testing.T) { "version provided correctly": { func() *config.ParsedTokenConfig { // "VAULT://secret___/some/other/foo2[version=1]", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("version=1") @@ -331,7 +330,7 @@ func TestVaultScenarios(t *testing.T) { "version provided but unable to parse": { func() *config.ParsedTokenConfig { // "VAULT://secret___/some/other/foo2[version=1a]", - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("version=1a") @@ -358,7 +357,7 @@ func TestVaultScenarios(t *testing.T) { }, "vault rate limit incorrect": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("") @@ -392,7 +391,7 @@ failed to initialize the client`, tearDown := tt.setupEnv() defer tearDown() - i, err := impl.NewVaultStore(context.TODO(), tt.token(), log.New(io.Discard)) + i, err := impl.NewVaultStore(context.TODO(), tt.token(), hclog.NewNullLogger()) if err != nil { if err.Error() != tt.expect { t.Fatalf("failed to init hashivault, %v", err.Error()) @@ -425,7 +424,7 @@ func TestAwsIamAuth(t *testing.T) { }{ "aws_iam auth no role specified": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("version=1") @@ -459,7 +458,7 @@ func TestAwsIamAuth(t *testing.T) { }, "aws_iam auth incorrectly formatted request": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("version=1,iam_role=not_a_role") @@ -506,7 +505,7 @@ incorrect values supplied. failed to initialize the client`, }, "aws_iam auth success": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("iam_role=arn:aws:iam::1111111:role/i-orchestration") @@ -550,7 +549,7 @@ incorrect values supplied. failed to initialize the client`, }, "aws_iam auth no token returned": { func() *config.ParsedTokenConfig { - tkn, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig()) + tkn, _ := config.NewParsedToken(config.HashicorpVaultPrefix, *config.NewConfig()) tkn.WithSanitizedToken("secret___/some/other/foo2") tkn.WithKeyPath("") tkn.WithMetadata("iam_role=arn:aws:iam::1111111:role/i-orchestration") @@ -598,7 +597,7 @@ incorrect values supplied. failed to initialize the client`, ts := httptest.NewServer(tt.mockHanlder(t)) tearDown := tt.setupEnv(ts.URL) defer tearDown() - i, err := impl.NewVaultStore(context.TODO(), tt.token(), log.New(io.Discard)) + i, err := impl.NewVaultStore(context.TODO(), tt.token(), hclog.NewNullLogger()) if err != nil { // WHAT A CRAP way to do this... if err.Error() != strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0] { diff --git a/plugins/vault/main.go b/plugins/vault/main.go index c4b340f..b2e3e8c 100644 --- a/plugins/vault/main.go +++ b/plugins/vault/main.go @@ -1,25 +1,19 @@ package main import ( - "context" - "os" - - "github.com/DevLabFoundry/configmanager/v3/internal/log" "github.com/DevLabFoundry/configmanager/v3/plugins" - "github.com/DevLabFoundry/configmanager/v3/plugins/awsparamstr/impl" "github.com/hashicorp/go-plugin" ) -// Here is a real implementation of KV that writes to a local file with -// the key name and the contents are the value of the key. type TokenStorePlugin struct{} func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { - srv, err := impl.NewParamStore(context.Background(), log.New(os.Stderr)) - if err != nil { - return "", err - } - return srv.Value(key, metadata) + // srv, err := impl.NewParamStore(context.Background(), log.New(os.Stderr)) + // if err != nil { + // return "", err + // } + // return srv.Value(key, metadata) + return "", nil } func main() { From b6417105ca65d553dd921060204a766f3a26f725 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Thu, 19 Mar 2026 21:45:21 +0000 Subject: [PATCH 08/15] fix: rejig layout of sub modules --- eirctl.yaml | 5 +- go.mod | 37 ---- go.sum | 161 +----------------- {plugins => tokenstore}/README.md | 0 {plugins => tokenstore}/grpc.go | 8 +- {plugins => tokenstore}/interface.go | 12 +- .../proto/token_store.pb.go | 0 .../proto/token_store.proto | 0 .../proto/token_store_grpc.pb.go | 0 .../provider}/awsparamstr/README.md | 0 .../provider}/awsparamstr/go.mod | 4 +- .../provider}/awsparamstr/go.sum | 0 .../provider}/awsparamstr/impl/paramstore.go | 4 +- .../awsparamstr/impl/paramstore_test.go | 2 +- .../provider}/awsparamstr/main.go | 8 +- .../provider}/awssecrets/.gitkeep | 0 tokenstore/provider/empty/go.mod | 28 +++ tokenstore/provider/empty/go.sum | 79 +++++++++ .../provider}/empty/main.go | 6 +- .../provider}/vault/README.md | 1 - tokenstore/provider/vault/go.mod | 53 ++++++ tokenstore/provider/vault/go.sum | 155 +++++++++++++++++ .../provider}/vault/impl/hashivault.go | 10 +- .../provider}/vault/impl/hashivault_test.go | 34 ++-- .../provider}/vault/main.go | 6 +- {plugins => tokenstore}/scaffolding.go | 2 +- 26 files changed, 374 insertions(+), 241 deletions(-) rename {plugins => tokenstore}/README.md (100%) rename {plugins => tokenstore}/grpc.go (77%) rename {plugins => tokenstore}/interface.go (72%) rename {plugins => tokenstore}/proto/token_store.pb.go (100%) rename {plugins => tokenstore}/proto/token_store.proto (100%) rename {plugins => tokenstore}/proto/token_store_grpc.pb.go (100%) rename {plugins => tokenstore/provider}/awsparamstr/README.md (100%) rename {plugins => tokenstore/provider}/awsparamstr/go.mod (95%) rename {plugins => tokenstore/provider}/awsparamstr/go.sum (100%) rename {plugins => tokenstore/provider}/awsparamstr/impl/paramstore.go (91%) rename {plugins => tokenstore/provider}/awsparamstr/impl/paramstore_test.go (98%) rename {plugins => tokenstore/provider}/awsparamstr/main.go (76%) rename {plugins => tokenstore/provider}/awssecrets/.gitkeep (100%) create mode 100644 tokenstore/provider/empty/go.mod create mode 100644 tokenstore/provider/empty/go.sum rename {plugins => tokenstore/provider}/empty/main.go (82%) rename {plugins => tokenstore/provider}/vault/README.md (95%) create mode 100644 tokenstore/provider/vault/go.mod create mode 100644 tokenstore/provider/vault/go.sum rename {plugins => tokenstore/provider}/vault/impl/hashivault.go (93%) rename {plugins => tokenstore/provider}/vault/impl/hashivault_test.go (94%) rename {plugins => tokenstore/provider}/vault/main.go (75%) rename {plugins => tokenstore}/scaffolding.go (97%) diff --git a/eirctl.yaml b/eirctl.yaml index 22db6e4..8577638 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -87,8 +87,9 @@ tasks: mkdir -p .deps unset GOTOOLCHAIN ldflags="-s -w -extldflags -static" - GOPATH=/eirctl/.deps GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} CGO_ENABLED=0 go build -mod=readonly -buildvcs=false -ldflags="$ldflags" \ - -o ./plugins/$PLUGIN/bin/$PLUGIN-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX} ./plugins/$PLUGIN/main.go + export GOPATH=/eirctl/.deps GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} CGO_ENABLED=0 + go build -C ./plugins/$PLUGIN -mod=readonly -buildvcs=false -ldflags="$ldflags" \ + -o bin/$PLUGIN-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX} main.go echo "---" echo "Built: $PLUGIN-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX}" reset_context: true diff --git a/go.mod b/go.mod index 9555ab0..dff52b3 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,7 @@ go 1.26 toolchain go1.26.1 require ( - github.com/aws/aws-sdk-go-v2 v1.41.4 - github.com/aws/aws-sdk-go-v2/config v1.32.12 - github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 github.com/go-test/deep v1.1.1 - github.com/hashicorp/vault/api v1.22.0 - github.com/hashicorp/vault/api/auth/aws v0.11.0 github.com/spf13/cobra v1.10.2 github.com/spyzhov/ajson v0.9.6 google.golang.org/grpc v1.79.3 @@ -19,7 +14,6 @@ require ( ) require ( - github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -31,46 +25,15 @@ require ( require ( github.com/a8m/envsubst v1.4.3 - github.com/aws/aws-sdk-go v1.55.8 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect - github.com/aws/smithy-go v1.24.2 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.7.0 - github.com/hashicorp/go-retryablehttp v0.7.8 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect - github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect - github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect - github.com/hashicorp/go-sockaddr v1.0.7 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/hcl v1.0.1-vault-7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/ryanuber/go-glob v1.0.0 // indirect github.com/spf13/pflag v1.0.10 // indirect golang.org/x/net v0.52.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 // indirect ) diff --git a/go.sum b/go.sum index 53815db..eeb042f 100644 --- a/go.sum +++ b/go.sum @@ -1,72 +1,9 @@ github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY= github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU= -github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= -github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= -github.com/aws/aws-sdk-go-v2 v1.40.1 h1:difXb4maDZkRH0x//Qkwcfpdg1XQVXEAEs2DdXldFFc= -github.com/aws/aws-sdk-go-v2 v1.40.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= -github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= -github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= -github.com/aws/aws-sdk-go-v2/config v1.32.3 h1:cpz7H2uMNTDa0h/5CYL5dLUEzPSLo2g0NkbxTRJtSSU= -github.com/aws/aws-sdk-go-v2/config v1.32.3/go.mod h1:srtPKaJJe3McW6T/+GMBZyIPc+SeqJsNPJsd4mOYZ6s= -github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= -github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= -github.com/aws/aws-sdk-go-v2/credentials v1.19.3 h1:01Ym72hK43hjwDeJUfi1l2oYLXBAOR8gNSZNmXmvuas= -github.com/aws/aws-sdk-go-v2/credentials v1.19.3/go.mod h1:55nWF/Sr9Zvls0bGnWkRxUdhzKqj9uRNlPvgV1vgxKc= -github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 h1:utxLraaifrSBkeyII9mIbVwXXWrZdlPO7FIKmyLCEcY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15/go.mod h1:hW6zjYUDQwfz3icf4g2O41PHi77u10oAzJ84iSzR/lo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 h1:Y5YXgygXwDI5P4RkteB5yF7v35neH7LfJKBG+hzIons= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15/go.mod h1:K+/1EpG42dFSY7CBj+Fruzm8PsCGWTXJ3jdeJ659oGQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 h1:AvltKnW9ewxX2hFmQS0FyJH93aSvJVUEFvXfU+HWtSE= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15/go.mod h1:3I4oCdZdmgrREhU74qS1dK9yZ62yumob+58AbFR4cQA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 h1:3/u/4yZOffg5jdNk1sDpOQ4Y+R6Xbh+GzpDrSZjuy3U= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15/go.mod h1:4Zkjq0FKjE78NKjabuM4tRXKFzUJWXgP0ItEZK8l7JU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 h1:d/6xOGIllc/XW1lzG9a4AUBMmpLA9PXcQnVPTuHHcik= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.3/go.mod h1:fQ7E7Qj9GiW8y0ClD7cUJk3Bz5Iw8wZkWDHsTe8vDKs= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= -github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5 h1:YKGgwB1rye0JpV10Bfma3cZdQzX61j2HPWQw+YxWvrQ= -github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5/go.mod h1:eBDSa0vuYB0lalpNxavIw80Q4Ksy08bhHHbT0aWa4tE= -github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3 h1:bBoWhx8lsFLTXintRX64ZBXcmFZbGqUmaPUrjXECqIc= -github.com/aws/aws-sdk-go-v2/service/ssm v1.68.3/go.mod h1:rcRkKbUJ2437WuXdq9fbj+MjTudYWzY9Ct8kiBbN8a8= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 h1:8sTTiw+9yuNXcfWeqKF2x01GqCF49CpP4Z9nKrrk/ts= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.6/go.mod h1:8WYg+Y40Sn3X2hioaaWAAIngndR8n1XFdRPPX+7QBaM= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 h1:E+KqWoVsSrj1tJ6I/fjDIu5xoS2Zacuu1zT+H7KtiIk= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11/go.mod h1:qyWHz+4lvkXcr3+PoGlGHEI+3DLLiU6/GdrFfMaAhB0= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 h1:tzMkjh0yTChUqJDgGkcDdxvZDSrJ/WB6R6ymI5ehqJI= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.3/go.mod h1:T270C0R5sZNLbWUe8ueiAF42XSZxxPocTaGSgs5c/60= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= -github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= -github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= -github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= -github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 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= @@ -75,13 +12,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -90,52 +24,17 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= -github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= -github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 h1:I8bynUKMh9I7JdwtW9voJ0xmHvBpxQtLjrMFDYmhOxY= -github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0/go.mod h1:oKHSQs4ivIfZ3fbXGQOop1XuDfdSb8RIsWTGaAanSfg= -github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= -github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= -github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= -github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= -github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0= -github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= -github.com/hashicorp/vault/api/auth/aws v0.11.0 h1:lWdUxrzvPotg6idNr62al4w97BgI9xTDdzMCTViNH2s= -github.com/hashicorp/vault/api/auth/aws v0.11.0/go.mod h1:PWqdH/xqaudapmnnGP9ip2xbxT/kRW2qEgpqiQff6Gc= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -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/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= @@ -150,24 +49,15 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -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/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -176,81 +66,44 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/spyzhov/ajson v0.9.6 h1:iJRDaLa+GjhCDAt1yFtU/LKMtLtsNVKkxqlpvrHHlpQ= github.com/spyzhov/ajson v0.9.6/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -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/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 h1:sRy++txmErSjyVWlIgQB5nB+U75+Di+AH7eEZ002B/s= google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/README.md b/tokenstore/README.md similarity index 100% rename from plugins/README.md rename to tokenstore/README.md diff --git a/plugins/grpc.go b/tokenstore/grpc.go similarity index 77% rename from plugins/grpc.go rename to tokenstore/grpc.go index c59d35f..ed618ca 100644 --- a/plugins/grpc.go +++ b/tokenstore/grpc.go @@ -1,9 +1,9 @@ -package plugins +package tokenstore import ( "context" - "github.com/DevLabFoundry/configmanager/v3/plugins/proto" + "github.com/DevLabFoundry/configmanager/v3/tokenstore/proto" ) // GRPCClient is the host process talking to the plugins @@ -28,9 +28,7 @@ type GRPCServer struct { Impl TokenStore } -func (m *GRPCServer) Value( - ctx context.Context, - req *proto.TokenValueRequest) (*proto.TokenValueResponse, error) { +func (m *GRPCServer) Value(ctx context.Context, req *proto.TokenValueRequest) (*proto.TokenValueResponse, error) { v, err := m.Impl.Value(req.Token, req.Metadata) return &proto.TokenValueResponse{Value: v}, err } diff --git a/plugins/interface.go b/tokenstore/interface.go similarity index 72% rename from plugins/interface.go rename to tokenstore/interface.go index 5a3cd1b..faaa4c5 100644 --- a/plugins/interface.go +++ b/tokenstore/interface.go @@ -1,11 +1,11 @@ -package plugins +package tokenstore import ( "context" "google.golang.org/grpc" - "github.com/DevLabFoundry/configmanager/v3/plugins/proto" + "github.com/DevLabFoundry/configmanager/v3/tokenstore/proto" "github.com/hashicorp/go-plugin" ) @@ -19,7 +19,7 @@ var Handshake = plugin.HandshakeConfig{ // // PluginMap is the map of plugins we can dispense. var PluginMap = map[string]plugin.Plugin{ - "configmanager_token_store": &TokenStoreGRPCPlugin{}, + "configmanager_token_store": &GRPCPlugin{}, } // TokenStore is the interface that we're exposing as a plugin. @@ -28,7 +28,7 @@ type TokenStore interface { } // This is the implementation of plugin.GRPCPlugin so we can serve/consume this. -type TokenStoreGRPCPlugin struct { +type GRPCPlugin struct { // GRPCPlugin must still implement the Plugin interface plugin.Plugin // Concrete implementation, written in Go. This is only used for plugins @@ -36,11 +36,11 @@ type TokenStoreGRPCPlugin struct { Impl TokenStore } -func (p *TokenStoreGRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { +func (p *GRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { proto.RegisterTokenStoreServer(s, &GRPCServer{Impl: p.Impl}) return nil } -func (p *TokenStoreGRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { +func (p *GRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { return &GRPCClient{client: proto.NewTokenStoreClient(c)}, nil } diff --git a/plugins/proto/token_store.pb.go b/tokenstore/proto/token_store.pb.go similarity index 100% rename from plugins/proto/token_store.pb.go rename to tokenstore/proto/token_store.pb.go diff --git a/plugins/proto/token_store.proto b/tokenstore/proto/token_store.proto similarity index 100% rename from plugins/proto/token_store.proto rename to tokenstore/proto/token_store.proto diff --git a/plugins/proto/token_store_grpc.pb.go b/tokenstore/proto/token_store_grpc.pb.go similarity index 100% rename from plugins/proto/token_store_grpc.pb.go rename to tokenstore/proto/token_store_grpc.pb.go diff --git a/plugins/awsparamstr/README.md b/tokenstore/provider/awsparamstr/README.md similarity index 100% rename from plugins/awsparamstr/README.md rename to tokenstore/provider/awsparamstr/README.md diff --git a/plugins/awsparamstr/go.mod b/tokenstore/provider/awsparamstr/go.mod similarity index 95% rename from plugins/awsparamstr/go.mod rename to tokenstore/provider/awsparamstr/go.mod index 7289550..c3d1a37 100644 --- a/plugins/awsparamstr/go.mod +++ b/tokenstore/provider/awsparamstr/go.mod @@ -1,4 +1,4 @@ -module github.com/DevLabFoundry/configmanager/plugins/awsparamstr +module github.com/DevLabFoundry/configmanager-plugin/awsparamstr go 1.26 @@ -43,4 +43,4 @@ require ( github.com/hashicorp/go-plugin v1.7.0 ) -replace github.com/DevLabFoundry/configmanager/v3 v3.0.0 => ../../../configmanager +replace github.com/DevLabFoundry/configmanager/v3 v3.0.0 => ../../../ diff --git a/plugins/awsparamstr/go.sum b/tokenstore/provider/awsparamstr/go.sum similarity index 100% rename from plugins/awsparamstr/go.sum rename to tokenstore/provider/awsparamstr/go.sum diff --git a/plugins/awsparamstr/impl/paramstore.go b/tokenstore/provider/awsparamstr/impl/paramstore.go similarity index 91% rename from plugins/awsparamstr/impl/paramstore.go rename to tokenstore/provider/awsparamstr/impl/paramstore.go index 23f98a0..58ffbb5 100644 --- a/plugins/awsparamstr/impl/paramstore.go +++ b/tokenstore/provider/awsparamstr/impl/paramstore.go @@ -4,7 +4,7 @@ import ( "context" "github.com/DevLabFoundry/configmanager/v3/config" - "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager/v3/tokenstore" "github.com/aws/aws-sdk-go-v2/aws" awsConf "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/ssm" @@ -58,7 +58,7 @@ func (imp *ParamStore) Value(token string, metadata []byte) (string, error) { result, err := imp.svc.GetParameter(ctx, input) if err != nil { - imp.logger.Error(plugins.ImplementationNetworkErr, "config.ParamStorePrefix", err, token) + imp.logger.Error(tokenstore.ImplementationNetworkErr, "config.ParamStorePrefix", err, token) return "", err } diff --git a/plugins/awsparamstr/impl/paramstore_test.go b/tokenstore/provider/awsparamstr/impl/paramstore_test.go similarity index 98% rename from plugins/awsparamstr/impl/paramstore_test.go rename to tokenstore/provider/awsparamstr/impl/paramstore_test.go index 948fd52..0979e6a 100644 --- a/plugins/awsparamstr/impl/paramstore_test.go +++ b/tokenstore/provider/awsparamstr/impl/paramstore_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/DevLabFoundry/configmanager/plugins/awsparamstr/impl" + "github.com/DevLabFoundry/configmanager-plugin/awsparamstr/impl" "github.com/DevLabFoundry/configmanager/v3/config" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/ssm/types" diff --git a/plugins/awsparamstr/main.go b/tokenstore/provider/awsparamstr/main.go similarity index 76% rename from plugins/awsparamstr/main.go rename to tokenstore/provider/awsparamstr/main.go index e9db859..a01ecda 100644 --- a/plugins/awsparamstr/main.go +++ b/tokenstore/provider/awsparamstr/main.go @@ -3,8 +3,8 @@ package main import ( "context" - "github.com/DevLabFoundry/configmanager/plugins/awsparamstr/impl" - "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager-plugin/awsparamstr/impl" + "github.com/DevLabFoundry/configmanager/v3/tokenstore" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" ) @@ -29,9 +29,9 @@ func main() { ts := TokenStorePlugin{log: log} plugin.Serve(&plugin.ServeConfig{ // Logger: , - HandshakeConfig: plugins.Handshake, + HandshakeConfig: tokenstore.Handshake, Plugins: map[string]plugin.Plugin{ - "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: ts}, + "configmanager_token_store": &tokenstore.GRPCPlugin{Impl: ts}, }, // A non-nil value here enables gRPC serving for this plugin... GRPCServer: plugin.DefaultGRPCServer, diff --git a/plugins/awssecrets/.gitkeep b/tokenstore/provider/awssecrets/.gitkeep similarity index 100% rename from plugins/awssecrets/.gitkeep rename to tokenstore/provider/awssecrets/.gitkeep diff --git a/tokenstore/provider/empty/go.mod b/tokenstore/provider/empty/go.mod new file mode 100644 index 0000000..9fe9cf3 --- /dev/null +++ b/tokenstore/provider/empty/go.mod @@ -0,0 +1,28 @@ +module github.com/DevLabFoundry/configmanager-plugin/empty + +go 1.26 + +toolchain go1.26.1 + +require ( + github.com/DevLabFoundry/configmanager/v3 v3.0.0 + github.com/hashicorp/go-plugin v1.7.0 +) + +require ( + github.com/fatih/color v1.18.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/oklog/run v1.2.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) + +replace github.com/DevLabFoundry/configmanager/v3 v3.0.0 => ../../../ diff --git a/tokenstore/provider/empty/go.sum b/tokenstore/provider/empty/go.sum new file mode 100644 index 0000000..3241034 --- /dev/null +++ b/tokenstore/provider/empty/go.sum @@ -0,0 +1,79 @@ +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= +github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 h1:sRy++txmErSjyVWlIgQB5nB+U75+Di+AH7eEZ002B/s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/empty/main.go b/tokenstore/provider/empty/main.go similarity index 82% rename from plugins/empty/main.go rename to tokenstore/provider/empty/main.go index db5a189..9b723a4 100644 --- a/plugins/empty/main.go +++ b/tokenstore/provider/empty/main.go @@ -8,7 +8,7 @@ import ( "errors" "fmt" - "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager/v3/tokenstore" "github.com/hashicorp/go-plugin" ) @@ -25,9 +25,9 @@ func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { func main() { plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: plugins.Handshake, + HandshakeConfig: tokenstore.Handshake, Plugins: map[string]plugin.Plugin{ - "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: &TokenStorePlugin{}}, + "configmanager_token_store": &tokenstore.GRPCPlugin{Impl: &TokenStorePlugin{}}, }, // A non-nil value here enables gRPC serving for this plugin... GRPCServer: plugin.DefaultGRPCServer, diff --git a/plugins/vault/README.md b/tokenstore/provider/vault/README.md similarity index 95% rename from plugins/vault/README.md rename to tokenstore/provider/vault/README.md index 27b590e..89622a7 100644 --- a/plugins/vault/README.md +++ b/tokenstore/provider/vault/README.md @@ -1,3 +1,2 @@ # Hashicorp Vault - diff --git a/tokenstore/provider/vault/go.mod b/tokenstore/provider/vault/go.mod new file mode 100644 index 0000000..6fbe8e4 --- /dev/null +++ b/tokenstore/provider/vault/go.mod @@ -0,0 +1,53 @@ +module github.com/DevLabFoundry/configmanager-plugin/vault + +go 1.26 + +toolchain go1.26.1 + +require ( + github.com/DevLabFoundry/configmanager/v3 v3.0.0 + github.com/hashicorp/go-hclog v1.6.3 + github.com/hashicorp/go-plugin v1.7.0 +) + +require ( + github.com/aws/aws-sdk-go v1.55.8 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.8 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect + github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.7 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect + github.com/hashicorp/hcl v1.0.1-vault-7 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + golang.org/x/time v0.15.0 // indirect +) + +require ( + github.com/fatih/color v1.18.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/hashicorp/vault/api v1.22.0 + github.com/hashicorp/vault/api/auth/aws v0.11.0 + github.com/hashicorp/yamux v0.1.2 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/oklog/run v1.2.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 // indirect + google.golang.org/grpc v1.79.3 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) + +replace github.com/DevLabFoundry/configmanager/v3 v3.0.0 => ../../../ diff --git a/tokenstore/provider/vault/go.sum b/tokenstore/provider/vault/go.sum new file mode 100644 index 0000000..fc08a5f --- /dev/null +++ b/tokenstore/provider/vault/go.sum @@ -0,0 +1,155 @@ +github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= +github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= +github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= +github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-plugin v1.7.0 h1:YghfQH/0QmPNc/AZMTFE3ac8fipZyZECHdDPshfk+mA= +github.com/hashicorp/go-plugin v1.7.0/go.mod h1:BExt6KEaIYx804z8k4gRzRLEvxKVb+kn0NMcihqOqb8= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 h1:I8bynUKMh9I7JdwtW9voJ0xmHvBpxQtLjrMFDYmhOxY= +github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0/go.mod h1:oKHSQs4ivIfZ3fbXGQOop1XuDfdSb8RIsWTGaAanSfg= +github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= +github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= +github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= +github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= +github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0= +github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= +github.com/hashicorp/vault/api/auth/aws v0.11.0 h1:lWdUxrzvPotg6idNr62al4w97BgI9xTDdzMCTViNH2s= +github.com/hashicorp/vault/api/auth/aws v0.11.0/go.mod h1:PWqdH/xqaudapmnnGP9ip2xbxT/kRW2qEgpqiQff6Gc= +github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= +github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= +github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= +github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +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/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= +github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44 h1:sRy++txmErSjyVWlIgQB5nB+U75+Di+AH7eEZ002B/s= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260319171110-e3a33c96fb44/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/vault/impl/hashivault.go b/tokenstore/provider/vault/impl/hashivault.go similarity index 93% rename from plugins/vault/impl/hashivault.go rename to tokenstore/provider/vault/impl/hashivault.go index ab0061d..dd4ef31 100644 --- a/plugins/vault/impl/hashivault.go +++ b/tokenstore/provider/vault/impl/hashivault.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/DevLabFoundry/configmanager/v3/config" - "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager/v3/tokenstore" "github.com/hashicorp/go-hclog" vault "github.com/hashicorp/vault/api" @@ -57,7 +57,7 @@ func NewVaultStore(ctx context.Context, token *config.ParsedTokenConfig, logger imp.strippedToken = vt.Token client, err := vault.NewClient(config) if err != nil { - return nil, fmt.Errorf("%v\n%w", err, plugins.ErrClientInitialization) + return nil, fmt.Errorf("%v\n%w", err, tokenstore.ErrClientInitialization) } if strings.HasPrefix(os.Getenv("VAULT_TOKEN"), "aws_iam") { @@ -85,13 +85,13 @@ func newVaultStoreWithAWSAuthIAM(client *vault.Client, role string) (*vault.Clie auth.WithRole(role), ) if err != nil { - return nil, fmt.Errorf("unable to initialize AWS auth method: %s. %w", err, plugins.ErrClientInitialization) + return nil, fmt.Errorf("unable to initialize AWS auth method: %s. %w", err, tokenstore.ErrClientInitialization) } authInfo, err := client.Auth().Login(context.Background(), awsAuth) if err != nil { - return nil, fmt.Errorf("unable to login to AWS auth method: %s. %w", err, plugins.ErrClientInitialization) + return nil, fmt.Errorf("unable to login to AWS auth method: %s. %w", err, tokenstore.ErrClientInitialization) } if authInfo == nil { return nil, fmt.Errorf("no auth info was returned after login") @@ -121,7 +121,7 @@ func (imp *VaultStore) Value() (string, error) { secret, err := imp.getSecret(ctx, imp.strippedToken, imp.config.Version) if err != nil { - imp.logger.Error(plugins.ImplementationNetworkErr, imp.token.Prefix(), err, imp.token.String()) + imp.logger.Error(tokenstore.ImplementationNetworkErr, imp.token.Prefix(), err, imp.token.String()) return "", err } diff --git a/plugins/vault/impl/hashivault_test.go b/tokenstore/provider/vault/impl/hashivault_test.go similarity index 94% rename from plugins/vault/impl/hashivault_test.go rename to tokenstore/provider/vault/impl/hashivault_test.go index 7911bc9..f4c437b 100644 --- a/plugins/vault/impl/hashivault_test.go +++ b/tokenstore/provider/vault/impl/hashivault_test.go @@ -9,13 +9,17 @@ import ( "strings" "testing" + "github.com/DevLabFoundry/configmanager-plugin/vault/impl" "github.com/DevLabFoundry/configmanager/v3/config" - "github.com/DevLabFoundry/configmanager/v3/internal/testutils" - "github.com/DevLabFoundry/configmanager/v3/plugins/vault/impl" "github.com/hashicorp/go-hclog" vault "github.com/hashicorp/vault/api" ) +const ( + TestPhrase string = "got: %v want: %v\n" + TestPhraseWithContext string = "%s\n got: %v\n\n want: %v\n" +) + func TestMountPathExtract(t *testing.T) { ttests := map[string]struct { token func() *config.ParsedTokenConfig @@ -284,7 +288,7 @@ func TestVaultScenarios(t *testing.T) { mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } return &vault.KVSecret{Data: nil}, nil } @@ -312,7 +316,7 @@ func TestVaultScenarios(t *testing.T) { mv.gv = func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } m := make(map[string]interface{}) m["foo2"] = "dsfsdf3454456" @@ -342,7 +346,7 @@ func TestVaultScenarios(t *testing.T) { mv.gv = func(ctx context.Context, secretPath string, version int) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } return nil, nil } @@ -370,7 +374,7 @@ failed to initialize the client`, mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } return &vault.KVSecret{Data: nil}, nil } @@ -403,12 +407,12 @@ failed to initialize the client`, got, err := i.Value() if err != nil { if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) + t.Errorf(TestPhrase, err.Error(), tt.expect) } return } if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) + t.Errorf(TestPhrase, got, tt.expect) } }) } @@ -436,7 +440,7 @@ func TestAwsIamAuth(t *testing.T) { mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } return &vault.KVSecret{Data: nil}, nil } @@ -475,7 +479,7 @@ incorrect values supplied. failed to initialize the client`, mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } return &vault.KVSecret{Data: nil}, nil } @@ -518,7 +522,7 @@ incorrect values supplied. failed to initialize the client`, mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } m := make(map[string]any) m["foo2"] = "dsfsdf3454456" @@ -561,7 +565,7 @@ incorrect values supplied. failed to initialize the client`, mv.g = func(ctx context.Context, secretPath string) (*vault.KVSecret, error) { t.Helper() if secretPath != "some/other/foo2" { - t.Errorf(testutils.TestPhrase, secretPath, `some/other/foo2`) + t.Errorf(TestPhrase, secretPath, `some/other/foo2`) } m := make(map[string]interface{}) m["foo2"] = "dsfsdf3454456" @@ -601,7 +605,7 @@ incorrect values supplied. failed to initialize the client`, if err != nil { // WHAT A CRAP way to do this... if err.Error() != strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0] { - t.Errorf(testutils.TestPhraseWithContext, "aws iam auth", err.Error(), strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0]) + t.Errorf(TestPhraseWithContext, "aws iam auth", err.Error(), strings.Split(fmt.Sprintf(tt.expect, ts.URL), `%!`)[0]) t.Fatalf("failed to init hashivault, %v", err.Error()) } return @@ -611,12 +615,12 @@ incorrect values supplied. failed to initialize the client`, got, err := i.Value() if err != nil { if err.Error() != tt.expect { - t.Errorf(testutils.TestPhrase, err.Error(), tt.expect) + t.Errorf(TestPhrase, err.Error(), tt.expect) } return } if got != tt.expect { - t.Errorf(testutils.TestPhrase, got, tt.expect) + t.Errorf(TestPhrase, got, tt.expect) } }) } diff --git a/plugins/vault/main.go b/tokenstore/provider/vault/main.go similarity index 75% rename from plugins/vault/main.go rename to tokenstore/provider/vault/main.go index b2e3e8c..cdf20e4 100644 --- a/plugins/vault/main.go +++ b/tokenstore/provider/vault/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager/v3/tokenstore" "github.com/hashicorp/go-plugin" ) @@ -18,9 +18,9 @@ func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { func main() { plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: plugins.Handshake, + HandshakeConfig: tokenstore.Handshake, Plugins: map[string]plugin.Plugin{ - "configmanager_token_store": &plugins.TokenStoreGRPCPlugin{Impl: &TokenStorePlugin{}}, + "configmanager_token_store": &tokenstore.GRPCPlugin{Impl: &TokenStorePlugin{}}, }, // A non-nil value here enables gRPC serving for this plugin... GRPCServer: plugin.DefaultGRPCServer, diff --git a/plugins/scaffolding.go b/tokenstore/scaffolding.go similarity index 97% rename from plugins/scaffolding.go rename to tokenstore/scaffolding.go index f317496..72ee236 100644 --- a/plugins/scaffolding.go +++ b/tokenstore/scaffolding.go @@ -1,4 +1,4 @@ -package plugins +package tokenstore import "errors" From 746b1a500ea00e29999f360b9a7dd3ca517a3945 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Thu, 19 Mar 2026 22:19:36 +0000 Subject: [PATCH 09/15] fix: minor clean up --- .github/workflows/build.yml | 4 +- .github/workflows/release.yml | 8 +-- buf.gen.yaml | 6 +- buf.yaml | 2 +- eirctl.yaml | 8 ++- internal/parser/parser_test.go | 110 +++++++++++++++-------------- internal/store/plugin.go | 10 +-- tokenstore/proto/token_store.pb.go | 2 +- tokenstore/provider/empty/main.go | 2 + 9 files changed, 78 insertions(+), 74 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0aae4f..95e6f70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,8 @@ name: CI on: - push: - branches: [master, main] pull_request: - branches: [master, main] + branches: [master, main, v3] permissions: contents: write diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 405c112..bfb8538 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,12 +2,13 @@ name: release on: workflow_run: - workflows: ['CI'] + workflows: ["CI"] types: - completed branches: - master - main + - v3 permissions: contents: write @@ -34,7 +35,7 @@ jobs: - name: Install GitVersion uses: gittools/actions/gitversion/setup@v4.1.0 with: - versionSpec: '6.x' + versionSpec: "6.x" - name: Set SemVer Version uses: gittools/actions/gitversion/execute@v4.1.0 id: gitversion @@ -52,9 +53,6 @@ jobs: - name: Install Eirctl uses: ensono/actions/eirctl-setup@v0.3.1 - with: - version: 0.7.6 - isPrerelease: false - name: build binary run: | diff --git a/buf.gen.yaml b/buf.gen.yaml index eb64d58..7aea791 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -3,13 +3,13 @@ version: v2 clean: false plugins: - remote: buf.build/protocolbuffers/go - out: plugins/proto + out: tokenstore/proto opt: - paths=source_relative - remote: buf.build/grpc/go:v1.3.0 - out: plugins/proto + out: tokenstore/proto opt: - paths=source_relative - require_unimplemented_servers=false inputs: - - directory: plugins/proto + - directory: tokenstore/proto diff --git a/buf.yaml b/buf.yaml index d055ccd..fa50bb2 100644 --- a/buf.yaml +++ b/buf.yaml @@ -1,6 +1,6 @@ version: v2 modules: - - path: ./plugins/proto + - path: ./tokenstore/proto lint: use: - STANDARD diff --git a/eirctl.yaml b/eirctl.yaml index 8577638..cc4b208 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -61,6 +61,10 @@ pipelines: name: vault env: PLUGIN: vault + - task: go:build:plugin + name: empty + env: + PLUGIN: empty scan:plugins: - task: trivy:file:system:sbom @@ -88,7 +92,7 @@ tasks: unset GOTOOLCHAIN ldflags="-s -w -extldflags -static" export GOPATH=/eirctl/.deps GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} CGO_ENABLED=0 - go build -C ./plugins/$PLUGIN -mod=readonly -buildvcs=false -ldflags="$ldflags" \ + go build -C ./tokenstore/provider/$PLUGIN -mod=readonly -buildvcs=false -ldflags="$ldflags" \ -o bin/$PLUGIN-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX} main.go echo "---" echo "Built: $PLUGIN-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX}" @@ -166,7 +170,7 @@ tasks: context: bash command: - | - sed -i 's|github.com/DevLabFoundry/configmanager/v2/||g' .coverage/out + sed -i 's|github.com/DevLabFoundry/configmanager/v3/||g' .coverage/out echo "Coverage file first 20 lines after conversion:" head -20 .coverage/out echo "Coverage file line count:" diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go index 7fe25e7..b22b026 100644 --- a/internal/parser/parser_test.go +++ b/internal/parser/parser_test.go @@ -90,14 +90,14 @@ func Test_ParserBlocks(t *testing.T) { } if len(parsed) != len(tt.expected) { - t.Fatalf("parsed statements count does not match\ngot=%d want=%d\nparsed %q", + t.Fatalf("parsed statements count does not match\ngot=%d want=%d\nparsed %v", len(parsed), len(tt.expected), parsed) } for idx, stmt := range parsed { - if !testHelperGenDocBlock(t, stmt, config.ImplementationPrefix(tt.expected[idx][0]), tt.expected[idx][1], tt.expected[idx][2]) { + if !testHelperParsedBlock(t, stmt, config.ImplementationPrefix(tt.expected[idx][0]), tt.expected[idx][1], tt.expected[idx][2]) { return } } @@ -229,60 +229,62 @@ func Test_Parse_should_pass_with_metadata_end_tag(t *testing.T) { // } // } -// func Test_Parse_Path_Keys_WithParsedMetadat(t *testing.T) { - -// ttests := map[string]struct { -// input string -// typ *store.SecretsMgrConfig -// wantSanitizedPath string -// wantKeyPath string -// }{ -// "without keysPath": { -// `AWSSECRETS:///foo[version=1.2.3]`, -// &store.SecretsMgrConfig{}, -// "/foo", "", -// }, -// "with keysPath": { -// `AWSSECRETS:///foo|path.one[version=1.2.3]`, -// &store.SecretsMgrConfig{}, -// "/foo", "path.one", -// }, -// "nestled in text": { -// `someQ=AWSPARAMSTR:///path/queryparam|p1[version=1.2.3]&anotherQ`, -// &store.SecretsMgrConfig{}, -// "/path/queryparam", "p1", -// }, -// } -// for name, tt := range ttests { -// t.Run(name, func(t *testing.T) { -// lexerSource.Input = tt.input -// cfg := config.NewConfig() -// l := lexer.New(lexerSource, *cfg) -// p := parser.New(l, cfg).WithLogger(log.New(os.Stderr)) -// parsed, errs := p.Parse() -// if len(errs) > 0 { -// t.Fatalf("%v", errs) -// } +func Test_Parse_Path_Keys_WithParsedMetadat(t *testing.T) { + type version struct { + Version string + } + ttests := map[string]struct { + input string + typ *version + wantSanitizedPath string + wantKeyPath string + }{ + "without keysPath": { + `AWSSECRETS:///foo[version=1.2.3]`, + &version{}, + "/foo", "", + }, + "with keysPath": { + `AWSSECRETS:///foo|path.one[version=1.2.3]`, + &version{}, + "/foo", "path.one", + }, + "nestled in text": { + `someQ=AWSPARAMSTR:///path/queryparam|p1[version=1.2.3]&anotherQ`, + &version{}, + "/path/queryparam", "p1", + }, + } + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + lexerSource.Input = tt.input + cfg := config.NewConfig() + l := lexer.New(lexerSource, *cfg) + p := parser.New(l, cfg).WithLogger(log.New(os.Stderr)) + parsed, errs := p.Parse() + if len(errs) > 0 { + t.Fatalf("%v", errs) + } -// for _, p := range parsed { -// if p.ParsedToken.StoreToken() != tt.wantSanitizedPath { -// t.Errorf("got %s want %s", p.ParsedToken.StoreToken(), tt.wantSanitizedPath) -// } -// if p.ParsedToken.LookupKeys() != tt.wantKeyPath { -// t.Errorf("got %s want %s", p.ParsedToken.LookupKeys(), tt.wantKeyPath) -// } -// if err := p.ParsedToken.ParseMetadata(tt.typ); err != nil { -// t.Fatal(err) -// } -// if tt.typ.Version != "1.2.3" { -// t.Errorf("got %v wanted 1.2.3", tt.typ.Version) -// } -// } -// }) -// } -// } + for _, p := range parsed { + if p.ParsedToken.StoreToken() != tt.wantSanitizedPath { + t.Errorf("got %s want %s", p.ParsedToken.StoreToken(), tt.wantSanitizedPath) + } + if p.ParsedToken.LookupKeys() != tt.wantKeyPath { + t.Errorf("got %s want %s", p.ParsedToken.LookupKeys(), tt.wantKeyPath) + } + if err := p.ParsedToken.ParseMetadata(tt.typ); err != nil { + t.Fatal(err) + } + if tt.typ.Version != "1.2.3" { + t.Errorf("got %v wanted 1.2.3", tt.typ.Version) + } + } + }) + } +} -func testHelperGenDocBlock(t *testing.T, stmtBlock parser.ConfigManagerTokenBlock, tokenType config.ImplementationPrefix, tokenValue, keysLookupPath string) bool { +func testHelperParsedBlock(t *testing.T, stmtBlock parser.ConfigManagerTokenBlock, tokenType config.ImplementationPrefix, tokenValue, keysLookupPath string) bool { t.Helper() if stmtBlock.ParsedToken.Prefix() != tokenType { t.Errorf("got=%q, wanted stmtBlock.ImpPrefix = '%v'.", stmtBlock.ParsedToken.Prefix(), tokenType) diff --git a/internal/store/plugin.go b/internal/store/plugin.go index eeb0994..912b40b 100644 --- a/internal/store/plugin.go +++ b/internal/store/plugin.go @@ -5,7 +5,7 @@ import ( "os/exec" "github.com/DevLabFoundry/configmanager/v3/config" - "github.com/DevLabFoundry/configmanager/v3/plugins" + "github.com/DevLabFoundry/configmanager/v3/tokenstore" "github.com/hashicorp/go-plugin" ) @@ -16,15 +16,15 @@ type Plugin struct { SourcePath string Version string ClientCleanUp func() - tokenStore plugins.TokenStore + tokenStore tokenstore.TokenStore } // NewPlugin Plugin gets called once per implementation func NewPlugin(ctx context.Context, path string) (*Plugin, error) { // We're a host. Start by launching the plugin process. client := plugin.NewClient(&plugin.ClientConfig{ - HandshakeConfig: plugins.Handshake, - Plugins: plugin.PluginSet{"configmanager_token_store": &plugins.TokenStoreGRPCPlugin{}}, + HandshakeConfig: tokenstore.Handshake, + Plugins: plugin.PluginSet{"configmanager_token_store": &tokenstore.GRPCPlugin{}}, Cmd: exec.Command(path), AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, }) @@ -42,7 +42,7 @@ func NewPlugin(ctx context.Context, path string) (*Plugin, error) { return nil, err } - ts := raw.(plugins.TokenStore) + ts := raw.(tokenstore.TokenStore) p := &Plugin{ ClientCleanUp: client.Kill, diff --git a/tokenstore/proto/token_store.pb.go b/tokenstore/proto/token_store.pb.go index aa4eff0..db5e957 100644 --- a/tokenstore/proto/token_store.pb.go +++ b/tokenstore/proto/token_store.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.10 +// protoc-gen-go v1.36.11 // protoc (unknown) // source: token_store.proto diff --git a/tokenstore/provider/empty/main.go b/tokenstore/provider/empty/main.go index 9b723a4..31abf0d 100644 --- a/tokenstore/provider/empty/main.go +++ b/tokenstore/provider/empty/main.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/DevLabFoundry/configmanager/v3/tokenstore" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" ) @@ -25,6 +26,7 @@ func (ts TokenStorePlugin) Value(key string, metadata []byte) (string, error) { func main() { plugin.Serve(&plugin.ServeConfig{ + Logger: hclog.NewNullLogger(), HandshakeConfig: tokenstore.Handshake, Plugins: map[string]plugin.Plugin{ "configmanager_token_store": &tokenstore.GRPCPlugin{Impl: &TokenStorePlugin{}}, From 5d65761b054564cf3ca013384725ef36c77991b5 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Thu, 2 Apr 2026 15:19:01 +0100 Subject: [PATCH 10/15] fix: interim --- eirctl.yaml | 2 +- generator/generator.go | 2 +- internal/store/store.go | 19 ++++++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/eirctl.yaml b/eirctl.yaml index cc4b208..cdc62f6 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -19,7 +19,7 @@ contexts: go1xalpine: container: - name: mirror.gcr.io/golang:1.25-alpine + name: mirror.gcr.io/golang:1.26-alpine envfile: exclude: - GO diff --git a/generator/generator.go b/generator/generator.go index 0d4010f..73e355a 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -59,7 +59,7 @@ func new(ctx context.Context, opts ...Opts) *Generator { return g } -// WithStrategyMap +// WithStores assigns additional stores to the strategy // // Adds addtional funcs for storageRetrieval used for testing only func (c *Generator) WithStores(sm *store.Store) *Generator { diff --git a/internal/store/store.go b/internal/store/store.go index 798c6f7..5ea4af0 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -53,13 +53,22 @@ const ( namePattern string = "%s-%s-%s" ) +type osOps struct { + UserHomeDir func() (string, error) + Getwd func() (dir string, err error) +} + type Store struct { plugin pluginMap + osOps osOps } func New(ctx context.Context) *Store { pm := pluginMap{mu: &sync.Mutex{}, m: make(map[string]*Plugin)} - s := &Store{plugin: pm} + s := &Store{ + plugin: pm, + osOps: osOps{UserHomeDir: os.UserHomeDir, Getwd: os.Getwd}, + } return s } @@ -67,11 +76,15 @@ func New(ctx context.Context) *Store { func (s *Store) Init(ctx context.Context, implt []string) error { for _, plugin := range implt { - plpath, err := findPlugin(plugin) + plpath, err := s.findPlugin(plugin) if err != nil { return err } p, err := NewPlugin(ctx, plpath) + if err != nil { + // wrap in init error + return err + } s.plugin.Add(plugin, p) } return nil @@ -98,7 +111,7 @@ func (s *Store) PluginCleanUp() { // // current dir // home dir -func findPlugin(plugin string) (string, error) { +func (s *Store) findPlugin(plugin string) (string, error) { // fallback locations // current dir cwd, err := os.Getwd() From a3f89bd3cc10dff334ce0a1163fe670214ec1133 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Fri, 3 Apr 2026 11:25:40 +0100 Subject: [PATCH 11/15] fix: add tests after re-work --- config/config.go | 16 +- eirctl.yaml | 18 +- generator/generator.go | 39 ++-- generator/generator_test.go | 381 ++++++++++++++++++---------------- internal/store/plugin.go | 4 + internal/store/plugin_test.go | 14 +- internal/store/store.go | 10 +- internal/store/store_test.go | 2 + 8 files changed, 260 insertions(+), 224 deletions(-) diff --git a/config/config.go b/config/config.go index ac8f9b7..ed57d80 100644 --- a/config/config.go +++ b/config/config.go @@ -50,6 +50,7 @@ type GenVarsConfig struct { tokenSeparator string keySeparator string enableEnvSubst bool + enableLaxMode bool // parseAdditionalVars func(token string) TokenConfigVars } @@ -83,12 +84,18 @@ func (c *GenVarsConfig) WithKeySeparator(keySeparator string) *GenVarsConfig { return c } -// WithKeySeparator adds a custom key separotor +// WithEnvSubst adds env subst flag func (c *GenVarsConfig) WithEnvSubst(enabled bool) *GenVarsConfig { c.enableEnvSubst = enabled return c } +// WithLaxMode adds lax mode enabled flag +func (c *GenVarsConfig) WithLaxMode(enabled bool) *GenVarsConfig { + c.enableLaxMode = enabled + return c +} + // OutputPath returns the outpath set in the config func (c *GenVarsConfig) OutputPath() string { return c.outpath @@ -109,6 +116,13 @@ func (c *GenVarsConfig) EnvSubstEnabled() bool { return c.enableEnvSubst } +// LaxModeEnabled returns whether or not lax mode is enabled +// +// It is disabled by default which will break the existing v2 behaviour +func (c *GenVarsConfig) LaxModeEnabled() bool { + return c.enableLaxMode +} + // Config returns the derefed value func (c *GenVarsConfig) Config() GenVarsConfig { cc := *c diff --git a/eirctl.yaml b/eirctl.yaml index cdc62f6..a8bd4d9 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -4,8 +4,8 @@ output: prefixed debug: false import: - - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.7/shared/build/go/eirctl.yaml - - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.7/shared/security/eirctl.yaml + - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.17/shared/build/go/eirctl.yaml + - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.17/shared/security/eirctl.yaml contexts: bash: @@ -28,7 +28,13 @@ contexts: pipelines: unit:test: + - task: go:build:plugin + condition: if [ -z "$(ls -A tokenstore/provider/empty/bin/empty-* 2>/dev/null)" ]; then exit 0; else exit 1; fi; + name: empty + env: + PLUGIN: empty - pipeline: test:unit + depends_on: empty env: ROOT_PKG_NAME: github.com/DevLabFoundry @@ -37,9 +43,9 @@ pipelines: - task: sonar:coverage:prep depends_on: unit:test - show_coverage: + code:coverage: - pipeline: unit:test - - task: show:coverage + - task: show_coverage depends_on: unit:test build:bin: @@ -70,10 +76,6 @@ pipelines: - task: trivy:file:system:sbom tasks: - show:coverage: - description: Opens the current coverage viewer for the the configmanager utility. - command: go tool cover -html=.coverage/out - show_docs: description: | Opens a webview with godoc running diff --git a/generator/generator.go b/generator/generator.go index 73e355a..f489682 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -17,6 +17,14 @@ import ( "github.com/DevLabFoundry/configmanager/v3/internal/store" ) +var ErrTokenNotFound = errors.New("token not found") +var ErrProvidersNotFound = errors.New("providers not initialised") + +type storeIface interface { + GetValue(implemenation *config.ParsedTokenConfig) (string, error) + Init(ctx context.Context, implt []string) error +} + // Generator is the main struct holding the // strategy patterns iface // any initialised config if overridded with withers @@ -26,7 +34,7 @@ import ( type Generator struct { Logger log.ILogger // strategy strategy.StrategyFuncMap - store *store.Store + store storeIface ctx context.Context config config.GenVarsConfig } @@ -62,7 +70,7 @@ func new(ctx context.Context, opts ...Opts) *Generator { // WithStores assigns additional stores to the strategy // // Adds addtional funcs for storageRetrieval used for testing only -func (c *Generator) WithStores(sm *store.Store) *Generator { +func (c *Generator) WithStores(sm storeIface) *Generator { c.store = sm return c } @@ -102,7 +110,7 @@ func (c *Generator) Generate(tokens []string) (ReplacedToken, error) { // // this can only be done once the tokens are known if err := c.store.Init(c.ctx, ntm.TokenSet()); err != nil { - return nil, err + return nil, fmt.Errorf("%w, %v", ErrProvidersNotFound, err) } // pass in default initialised retrieveStrategy @@ -111,6 +119,7 @@ func (c *Generator) Generate(tokens []string) (ReplacedToken, error) { if err != nil { return nil, err } + return rt, nil } @@ -143,8 +152,7 @@ func (c *Generator) DiscoverTokens(text string) (NormalizedTokenSafe, error) { // and any characters func IsParsed(v any, trm ReplacedToken) bool { str := fmt.Sprint(v) - err := json.Unmarshal([]byte(str), &trm) - return err == nil + return json.Unmarshal([]byte(str), &trm) == nil } // generate initiates waitGroup to handle 1 or more normalized network calls concurrently to the underlying stores @@ -160,9 +168,6 @@ func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { wg := &sync.WaitGroup{} - // initialise the stores here - // s := strategy.New(c.config, c.Logger, strategy.WithStrategyFuncMap(c.strategy)) - // safe read of normalized token map // this will ensure that we are minimizing // the number of network calls to each underlying store @@ -175,18 +180,12 @@ func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { wg.Go(func() { prsdTkn.resp = &TokenResponse{} prsdTkn.resp.WithKey(token) - storeStrategy, err := c.store.GetImplementation(token.Prefix()) + val, err := c.store.GetValue(token) if err != nil { prsdTkn.resp.Err = err return } - // storeStrategy.GetValue(token) - v, err := storeStrategy.GetValue(token) - if err != nil { - prsdTkn.resp.Err = err - return - } - prsdTkn.resp.WithValue(v) + prsdTkn.resp.WithValue(val) }) } @@ -195,6 +194,7 @@ func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { // now we fan out the normalized value to ReplacedToken map // this will ensure all found tokens will have a value assigned to them replacedToken := make(ReplacedToken) + notfound := []string{} for _, r := range ntm.GetMap() { if r == nil { // defensive as this shouldn't happen @@ -202,12 +202,19 @@ func (c *Generator) generate(ntm NormalizedTokenSafe) (ReplacedToken, error) { } if r.resp.Err != nil { c.Logger.Debug("cr.err %v, for token: %s", r.resp.Err, r.resp.Key().String()) + if !c.config.LaxModeEnabled() { + // we want to collect all the errors + notfound = append(notfound, fmt.Sprintf("token: %s\n", r.resp.Key().String())) + } continue } for _, originalToken := range r.parsedTokens { replacedToken[originalToken.String()] = keySeparatorLookup(originalToken, r.resp.Value()) } } + if len(notfound) > 0 { + return replacedToken, fmt.Errorf("%w\n%v", ErrTokenNotFound, notfound) + } return replacedToken, nil } diff --git a/generator/generator_test.go b/generator/generator_test.go index 6e5418b..86aa3ea 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -2,204 +2,217 @@ package generator_test import ( "context" + "errors" + "fmt" + "slices" "testing" "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/generator" + "github.com/DevLabFoundry/configmanager/v3/internal/testutils" ) -type mockGenerate struct { - inToken, value string - err error +type mockStore struct { + getVal func(implemenation *config.ParsedTokenConfig) (string, error) + init func(ctx context.Context, implt []string) error } -func (m *mockGenerate) SetToken(s *config.ParsedTokenConfig) { +func (m mockStore) GetValue(implemenation *config.ParsedTokenConfig) (string, error) { + return m.getVal(implemenation) } -func (m *mockGenerate) Value() (s string, e error) { - return m.value, m.err + +func (m mockStore) Init(ctx context.Context, implt []string) error { + if m.init != nil { + return m.init(ctx, implt) + } + return nil } -// func TestGenerate(t *testing.T) { +func Test_Generate(t *testing.T) { -// t.Run("succeeds with funcMap", func(t *testing.T) { -// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"AWSPARAMSTR://mountPath/token", "bar", nil} -// return m, nil -// } - -// g := generator.New(context.TODO(), func(gv *generator.Generator) { -// gv.Logger = log.New(&bytes.Buffer{}) -// }) -// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) -// got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) - -// if err != nil { -// t.Fatal("errored on generate") -// } -// if len(got) != 1 { -// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 1) -// } -// }) + t.Run("succeeds", func(t *testing.T) { + m := &mockStore{} + m.getVal = func(implemenation *config.ParsedTokenConfig) (string, error) { + return `{"foo":"bar","key1":{"key2":"val"}}`, nil + } + g := generator.New(context.TODO()) + g.WithStores(m) -// t.Run("errors in retrieval and logs it out", func(t *testing.T) { -// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"AWSPARAMSTR://mountPath/token", "bar", fmt.Errorf("failed to get value")} -// return m, nil -// } - -// g := generator.New(context.TODO()) -// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc}) -// got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) - -// if err != nil { -// t.Fatal("errored on generate") -// } -// if len(got) != 0 { -// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) -// } -// }) + got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) -// t.Run("retrieves values correctly from a keylookup inside", func(t *testing.T) { -// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"token-unused", `{"foo":"bar","key1":{"key2":"val"}}`, nil} -// return m, nil -// } - -// g := generator.New(context.TODO()) -// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: custFunc, store: nil}) -// got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token|key1.key2"}) - -// if err != nil { -// t.Fatal("errored on generate") -// } -// if len(got) != 1 { -// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) -// } -// if got["AWSPARAMSTR://mountPath/token|key1.key2"] != "val" { -// t.Errorf(testutils.TestPhraseWithContext, "incorrect value returned in parsedMap", got["AWSPARAMSTR://mountPath/token|key1.key2"], "val") -// } -// }) -// } + if err != nil { + t.Fatal("errored on generate") + } + if len(got) != 1 { + t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 1) + } + }) -// func TestGenerate_withKeys_lookup(t *testing.T) { -// ttests := map[string]struct { -// custFunc strategy.StrategyFunc -// token string -// expectVal string -// }{ -// "retrieves string value correctly from a keylookup inside": { -// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":"val"}}`, nil} -// return m, nil -// }, -// token: "AWSPARAMSTR://mountPath/token|key1.key2", -// expectVal: "val", -// }, -// "retrieves number value correctly from a keylookup inside": { -// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":123}}`, nil} -// return m, nil -// }, -// token: "AWSPARAMSTR://mountPath/token|key1.key2", -// expectVal: "123", -// }, -// "retrieves nothing as keylookup is incorrect": { -// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"token", `{"foo":"bar","key1":{"key2":123}}`, nil} -// return m, nil -// }, -// token: "AWSPARAMSTR://mountPath/token|noprop", -// expectVal: "", -// }, -// "retrieves value as is due to incorrectly stored json in backing store": { -// custFunc: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"token", `foo":"bar","key1":{"key2":123}}`, nil} -// return m, nil -// }, -// token: "AWSPARAMSTR://mountPath/token|noprop", -// expectVal: `foo":"bar","key1":{"key2":123}}`, -// }, -// } -// for name, tt := range ttests { -// t.Run(name, func(t *testing.T) { -// g := generator.New(context.TODO()) -// g.WithStrategyMap(strategy.StrategyFuncMap{config.ParamStorePrefix: tt.custFunc}) -// got, err := g.Generate([]string{tt.token}) - -// if err != nil { -// t.Fatal("errored on generate") -// } -// if len(got) != 1 { -// t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) -// } -// if got[tt.token] != tt.expectVal { -// t.Errorf(testutils.TestPhraseWithContext, "incorrect value returned in parsedMap", got[tt.token], tt.expectVal) -// } -// }) -// } -// } + t.Run("fails to init providers", func(t *testing.T) { + m := &mockStore{} + m.init = func(ctx context.Context, implt []string) error { + return fmt.Errorf("failed to find providers") + } -// func Test_IsParsed(t *testing.T) { -// ttests := map[string]struct { -// val any -// isParsed bool -// }{ -// "not parseable": { -// `notparseable`, false, -// }, -// "one level parseable": { -// `{"parseable":"foo"}`, true, -// }, -// "incorrect JSON": { -// `parseable":"foo"}`, false, -// }, -// } -// for name, tt := range ttests { -// t.Run(name, func(t *testing.T) { -// typ := generator.ReplacedToken{} -// got := generator.IsParsed(tt.val, typ) -// if got != tt.isParsed { -// t.Errorf(testutils.TestPhraseWithContext, "unexpected IsParsed", got, tt.isParsed) -// } -// }) -// } -// } + g := generator.New(context.TODO()) + g.WithStores(m) -// func TestGenVars_NormalizeRawToken(t *testing.T) { + _, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) -// t.Run("multiple tokens", func(t *testing.T) { -// g := generator.New(context.TODO()) + if err == nil { + t.Fatal("got nil, wanted err") + } + if !errors.Is(err, generator.ErrProvidersNotFound) { + t.Errorf("got %v, wanted %v", err, generator.ErrProvidersNotFound) + } + }) + t.Run("ignores no tokens", func(t *testing.T) { + m := &mockStore{} + g := generator.New(context.TODO()) + g.WithStores(m) -// input := `GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj -// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|a -// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|b -// GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|c -// AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj -// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123] -// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key1 -// AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj|key2 -// AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj -// VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj` -// want := []string{"GCPSECRETS:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", -// "AWSPARAMSTR:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", -// "AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj[version=123]", -// "AWSSECRETS://bar/djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", -// "AZKVSECRET:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj", -// "VAULT:///djsfsdkjvfjkhfdvibdfinjdsfnjvdsflj"} -// got, err := g.DiscoverTokens(input) -// if err != nil { -// t.Fatal(err) -// } -// if len(got.GetMap()) != len(want) { -// t.Errorf("got %v wanted %d", len(got.GetMap()), len(want)) -// } -// for key := range got.GetMap() { -// if !slices.Contains(want, key) { -// t.Errorf("got %s, wanted to be included in %v", key, want) -// } -// } -// }) -// } + _, err := g.Generate([]string{}) + + if err != nil { + t.Fatal("got nil, wanted err") + } + }) + t.Run("lax mode enabled - maintains v2 behaviour no rerrors in retrieval", func(t *testing.T) { + + m := &mockStore{} + m.getVal = func(implemenation *config.ParsedTokenConfig) (string, error) { + return ``, fmt.Errorf("failed to get value") + } + + g := generator.New(context.TODO()) + g.Config().WithLaxMode(true) + g.WithStores(m) + + got, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) + + if err != nil { + t.Fatal("errored on generate") + } + if len(got) != 0 { + t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) + } + }) + t.Run("errors in retrieval and logs it out", func(t *testing.T) { + m := &mockStore{} + m.getVal = func(implemenation *config.ParsedTokenConfig) (string, error) { + return ``, fmt.Errorf("failed to get value") + } + g := generator.New(context.TODO()) + g.WithStores(m) + + _, err := g.Generate([]string{"AWSPARAMSTR://mountPath/token"}) + + if err == nil { + t.Fatal("got nil, wanted err") + } + if !errors.Is(err, generator.ErrTokenNotFound) { + t.Errorf("got %v, wanted %v", err, generator.ErrTokenNotFound) + } + }) +} + +func TestGenerate_withKeys_lookup(t *testing.T) { + + ttests := map[string]struct { + store func(t *testing.T) *mockStore + token string + expectVal string + }{ + "retrieves string value correctly from a keylookup inside": { + store: func(t *testing.T) *mockStore { + m := &mockStore{} + m.getVal = func(implemenation *config.ParsedTokenConfig) (string, error) { + return `{"foo":"bar","key1":{"key2":"val"}}`, nil + } + return m + }, + token: "AWSPARAMSTR://mountPath/token|key1.key2", + expectVal: "val", + }, + "retrieves number value correctly from a keylookup inside": { + + store: func(t *testing.T) *mockStore { + m := &mockStore{} + m.getVal = func(implemenation *config.ParsedTokenConfig) (string, error) { + return `{"foo":"bar","key1":{"key2":123}}`, nil + } + return m + }, + token: "AWSPARAMSTR://mountPath/token|key1.key2", + expectVal: "123", + }, + "retrieves nothing as keylookup is incorrect": { + store: func(t *testing.T) *mockStore { + m := &mockStore{} + m.getVal = func(implemenation *config.ParsedTokenConfig) (string, error) { + return `{"foo":"bar","key1":{"key2":123}}`, nil + } + return m + }, + token: "AWSPARAMSTR://mountPath/token|noprop", + expectVal: "", + }, + "retrieves value as is due to incorrectly stored json in backing store": { + store: func(t *testing.T) *mockStore { + m := &mockStore{} + m.getVal = func(implemenation *config.ParsedTokenConfig) (string, error) { + return `foo":"bar","key1":{"key2":123}}`, nil + } + return m + }, + token: "AWSPARAMSTR://mountPath/token|noprop", + expectVal: `foo":"bar","key1":{"key2":123}}`, + }, + } + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + g := generator.New(context.TODO()) + g.WithStores(tt.store(t)) + got, err := g.Generate([]string{tt.token}) + + if err != nil { + t.Fatal("errored on generate") + } + if len(got) != 1 { + t.Errorf(testutils.TestPhraseWithContext, "incorect number in a map", len(got), 0) + } + if got[tt.token] != tt.expectVal { + t.Errorf(testutils.TestPhraseWithContext, "incorrect value returned in parsedMap", got[tt.token], tt.expectVal) + } + }) + } +} + +func Test_IsParsed(t *testing.T) { + ttests := map[string]struct { + val any + isParsed bool + }{ + "not parseable": { + `notparseable`, false, + }, + "one level parseable": { + `{"parseable":"foo"}`, true, + }, + "incorrect JSON": { + `parseable":"foo"}`, false, + }, + } + for name, tt := range ttests { + t.Run(name, func(t *testing.T) { + typ := generator.ReplacedToken{} + got := generator.IsParsed(tt.val, typ) + if got != tt.isParsed { + t.Errorf(testutils.TestPhraseWithContext, "unexpected IsParsed", got, tt.isParsed) + } + }) + } +} func Test_ConfigManager_DiscoverTokens(t *testing.T) { ttests := map[string]struct { @@ -302,11 +315,11 @@ func Test_ConfigManager_DiscoverTokens(t *testing.T) { if len(got) != len(tt.expect) { t.Errorf("wrong nmber of tokens resolved\ngot (%d) want (%d)", len(got), len(tt.expect)) } - // for _, v := range got { - // if !slices.Contains(tt.expect, v.String()) { - // t.Errorf("got (%s) not found in expected slice (%v)", v, tt.expect) - // } - // } + for key := range got { + if !slices.Contains(tt.expect, key) { + t.Errorf("got (%s) not found in expected slice (%v)", key, tt.expect) + } + } }) } } diff --git a/internal/store/plugin.go b/internal/store/plugin.go index 912b40b..2d579ae 100644 --- a/internal/store/plugin.go +++ b/internal/store/plugin.go @@ -51,6 +51,10 @@ func NewPlugin(ctx context.Context, path string) (*Plugin, error) { return p, nil } +func (p *Plugin) WithTokenStore(ts tokenstore.TokenStore) { + p.tokenStore = ts +} + func (p *Plugin) GetValue(token *config.ParsedTokenConfig) (string, error) { result, err := p.tokenStore.Value(token.StoreToken(), []byte(token.Metadata())) if err != nil { diff --git a/internal/store/plugin_test.go b/internal/store/plugin_test.go index 0185b5e..56cb55b 100644 --- a/internal/store/plugin_test.go +++ b/internal/store/plugin_test.go @@ -3,7 +3,6 @@ package store_test import ( "context" "fmt" - "os" "runtime" "testing" @@ -11,15 +10,10 @@ import ( "github.com/DevLabFoundry/configmanager/v3/internal/store" ) -// TODO: make the implementation of the plugin system more testable -func TestPlugin_GetValue_integration(t *testing.T) { - // as the plugin is technically a subprocess - // setting env vars at this level will affect the loaded plugin - os.Setenv("AWS_REGION", "eu-west-1") - os.Setenv("AWS_PROFILE", "FOO") - defer os.Unsetenv("AWS_PROFILE") - defer os.Unsetenv("AWS_REGION") - tp := fmt.Sprintf("../../.configmanager/plugins/empty/empty-%s-%s", runtime.GOOS, runtime.GOARCH) +// Note: this step depends on pre-built empty tester plugin provider +// Running the +func Test_Plugin_GetValue(t *testing.T) { + tp := fmt.Sprintf("../../tokenstore/provider/empty/bin/empty-%s-%s", runtime.GOOS, runtime.GOARCH) np, err := store.NewPlugin(context.TODO(), tp) if err != nil { t.Fatal(err) diff --git a/internal/store/store.go b/internal/store/store.go index 5ea4af0..e8fff35 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -90,12 +90,12 @@ func (s *Store) Init(ctx context.Context, implt []string) error { return nil } -func (s *Store) GetImplementation(implemenation config.ImplementationPrefix) (plugin *Plugin, err error) { - var exists bool - if plugin, exists = s.plugin.m[strings.ToLower(string(implemenation))]; exists { - return plugin, nil +func (s *Store) GetValue(implemenation *config.ParsedTokenConfig) (string, error) { + plugin, exists := s.plugin.m[strings.ToLower(string(implemenation.Prefix()))] + if !exists { + return "", ErrPluginNotFound } - return nil, ErrPluginNotFound + return plugin.GetValue(implemenation) } // PluginCleanUp ensures the plugins are properly shut down diff --git a/internal/store/store_test.go b/internal/store/store_test.go index bb1c5b3..2394565 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -1 +1,3 @@ package store_test + +// From ccf0c38823b0a250b5c26fe2d643c40453afcf9e Mon Sep 17 00:00:00 2001 From: dnitsch Date: Sat, 4 Apr 2026 08:46:51 +0100 Subject: [PATCH 12/15] fix: add more tests to store package --- config/config.go | 4 +++ eirctl.yaml | 11 +++++- internal/store/plugin.go | 8 ++++- internal/store/plugin_test.go | 40 --------------------- internal/store/store.go | 20 ++++------- internal/store/store_test.go | 68 ++++++++++++++++++++++++++++++++++- 6 files changed, 95 insertions(+), 56 deletions(-) delete mode 100644 internal/store/plugin_test.go diff --git a/config/config.go b/config/config.go index ed57d80..aeec5ff 100644 --- a/config/config.go +++ b/config/config.go @@ -9,6 +9,10 @@ import ( const ( SELF_NAME = "configmanager" + // CONFIGMANAGER_DIR is used for any operations that require a lookup of dependencies/providers + // + // If it is empty or unset the default locations for these is `$PWD/.configmanager` and then `~/.configmanager` + CONFIGMANAGER_DIR string = "CONFIGMANAGER_DIR" ) const ( diff --git a/eirctl.yaml b/eirctl.yaml index a8bd4d9..d4e5204 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -33,8 +33,11 @@ pipelines: name: empty env: PLUGIN: empty - - pipeline: test:unit + - task: move:empty:provider + condition: if [ -z "$(ls -A tokenstore/provider/empty/.configmanager/plugins/empty/* 2>/dev/null)" ]; then exit 0; else exit 1; fi; depends_on: empty + - pipeline: test:unit + depends_on: move:empty:provider env: ROOT_PKG_NAME: github.com/DevLabFoundry @@ -178,6 +181,12 @@ tasks: echo "Coverage file line count:" wc -l .coverage/out + move:empty:provider: + command: + - | + mkdir -p ./tokenstore/provider/empty/.configmanager/plugins/empty + cp -r ./tokenstore/provider/empty/bin/* ./tokenstore/provider/empty/.configmanager/plugins/empty/ + tag: description: | Usage `eirctl tag GIT_TAG=2111dsfsdfa REVISION=as2342432` diff --git a/internal/store/plugin.go b/internal/store/plugin.go index 2d579ae..2c87cc0 100644 --- a/internal/store/plugin.go +++ b/internal/store/plugin.go @@ -2,13 +2,18 @@ package store import ( "context" + "errors" + "fmt" "os/exec" "github.com/DevLabFoundry/configmanager/v3/config" "github.com/DevLabFoundry/configmanager/v3/tokenstore" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-plugin" ) +var ErrTokenRetrieval = errors.New("failed to exchange token for value") + // Plugin is responsible for managing the plugin lifecycle // within the configmanager flow. Each Implementation will initialise exactly one instance of the plugin type Plugin struct { @@ -27,6 +32,7 @@ func NewPlugin(ctx context.Context, path string) (*Plugin, error) { Plugins: plugin.PluginSet{"configmanager_token_store": &tokenstore.GRPCPlugin{}}, Cmd: exec.Command(path), AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, + Logger: hclog.NewNullLogger(), }) // Connect via RPC rpcClient, err := client.Client() @@ -58,7 +64,7 @@ func (p *Plugin) WithTokenStore(ts tokenstore.TokenStore) { func (p *Plugin) GetValue(token *config.ParsedTokenConfig) (string, error) { result, err := p.tokenStore.Value(token.StoreToken(), []byte(token.Metadata())) if err != nil { - return "", err + return "", fmt.Errorf("%w - (%s), %v", ErrRetrieveFailed, token.String(), err) } return result, nil } diff --git a/internal/store/plugin_test.go b/internal/store/plugin_test.go deleted file mode 100644 index 56cb55b..0000000 --- a/internal/store/plugin_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package store_test - -import ( - "context" - "fmt" - "runtime" - "testing" - - "github.com/DevLabFoundry/configmanager/v3/config" - "github.com/DevLabFoundry/configmanager/v3/internal/store" -) - -// Note: this step depends on pre-built empty tester plugin provider -// Running the -func Test_Plugin_GetValue(t *testing.T) { - tp := fmt.Sprintf("../../tokenstore/provider/empty/bin/empty-%s-%s", runtime.GOOS, runtime.GOARCH) - np, err := store.NewPlugin(context.TODO(), tp) - if err != nil { - t.Fatal(err) - } - - defer np.ClientCleanUp() - token, err := config.NewParsedToken(config.ParamStorePrefix, *config.NewConfig()) - if err != nil { - t.Fatal(err) - } - - token.WithSanitizedToken("/int-test/pocketbase/admin-pwd") - got, err := np.GetValue(token) - if err != nil { - t.Fatal(err) - } - - if len(got) < 1 { - t.Fatal("empty...") - } - if got != "/int-test/pocketbase/admin-pwd->" { - t.Errorf("") - } -} diff --git a/internal/store/store.go b/internal/store/store.go index e8fff35..c35e71d 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -21,17 +21,6 @@ var ( ErrPluginNotFound = errors.New("plugin does not exist") ) -// // Strategy iface that all store implementations -// // must conform to, in order to be be used by the retrieval implementation -// // -// // Defined on the package for easier re-use across the program -// type Strategy interface { -// // Value retrieves the underlying value for the token -// Value() (s string, e error) -// // SetToken -// SetToken(s *config.ParsedTokenConfig) -// } - // It includes the following methods // - fetch plugins from known sources // - maintains a list of tokens answerable by a specified pluginEngine @@ -122,12 +111,17 @@ func (s *Store) findPlugin(plugin string) (string, error) { if err != nil { return "", err } - for _, p := range []string{cwd, hd} { + + fallbackPath := []string{cwd, hd} + if val, exists := os.LookupEnv(config.CONFIGMANAGER_DIR); exists { + fallbackPath = append([]string{val}, fallbackPath...) + } + for _, p := range []string{os.Getenv(config.CONFIGMANAGER_DIR), cwd, hd} { ff := path.Join(p, loc, plugin, fmt.Sprintf(namePattern, plugin, runtime.GOOS, runtime.GOARCH)) if _, err := os.Stat(ff); err == nil { // break on first non nil error return ff, nil } } - return "", ErrPluginNotFound + return "", fmt.Errorf("configmanger provider: ( %s ) %w", plugin, ErrPluginNotFound) } diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 2394565..c21fcff 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -1,3 +1,69 @@ package store_test -// +import ( + "context" + "errors" + "os" + "testing" + + "github.com/DevLabFoundry/configmanager/v3/config" + "github.com/DevLabFoundry/configmanager/v3/internal/store" +) + +// These tests are more of an integration test as they rely on +func Test_Store(t *testing.T) { + + // Setup test store + os.Setenv(config.CONFIGMANAGER_DIR, "../../tokenstore/provider/empty") + + defer os.Unsetenv(config.CONFIGMANAGER_DIR) + + s := store.New(context.TODO()) + + if err := s.Init(context.TODO(), []string{"empty"}); err != nil { + t.Fatal(err) + } + token, err := config.NewParsedToken("empty", *config.NewConfig()) + if err != nil { + t.Fatal(err) + } + + t.Run("success no metadata", func(t *testing.T) { + token.WithSanitizedToken("/my/token") + got, err := s.GetValue(token) + assertStoreResp(t, got, "/my/token->", err, nil) + }) + + t.Run("succeds with metadata", func(t *testing.T) { + token.WithSanitizedToken("/my/token") + token.WithMetadata(`[version=123]`) + got, err := s.GetValue(token) + assertStoreResp(t, got, "/my/token->[version=123]", err, nil) + + }) + + t.Run("errors on retrieve", func(t *testing.T) { + token.WithSanitizedToken("err") + got, err := s.GetValue(token) + assertStoreResp(t, got, "", err, store.ErrRetrieveFailed) + }) +} + +func assertStoreResp(t *testing.T, got, want string, err, wantErr error) { + t.Helper() + + if err != nil && wantErr == nil { + t.Fatal(err) + } + + if wantErr != nil { + if !errors.Is(err, wantErr) { + t.Errorf("errors don't match, got %v, wanted %v", err, wantErr) + } + } + + if got != want { + t.Errorf("got %s, wanted %s", got, want) + } + +} From a6e6706fa86c50c582aa086d146e04aec2424403 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Sat, 4 Apr 2026 12:28:55 +0100 Subject: [PATCH 13/15] fix: linter errors --- internal/store/store.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/store/store.go b/internal/store/store.go index c35e71d..e834880 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -116,12 +116,12 @@ func (s *Store) findPlugin(plugin string) (string, error) { if val, exists := os.LookupEnv(config.CONFIGMANAGER_DIR); exists { fallbackPath = append([]string{val}, fallbackPath...) } - for _, p := range []string{os.Getenv(config.CONFIGMANAGER_DIR), cwd, hd} { + for _, p := range fallbackPath { ff := path.Join(p, loc, plugin, fmt.Sprintf(namePattern, plugin, runtime.GOOS, runtime.GOARCH)) if _, err := os.Stat(ff); err == nil { // break on first non nil error return ff, nil } } - return "", fmt.Errorf("configmanger provider: ( %s ) %w", plugin, ErrPluginNotFound) + return "", fmt.Errorf("configmanager provider: ( %s ) %w", plugin, ErrPluginNotFound) } From 9d7cc13c4041abf02033d898afe9307098b1e9dc Mon Sep 17 00:00:00 2001 From: dnitsch Date: Sat, 4 Apr 2026 12:41:39 +0100 Subject: [PATCH 14/15] fix: sonar and remove unused --- .github/workflows/build.yml | 3 - internal/strategy/strategy.go | 132 ------------- internal/strategy/strategy_test.go | 287 ----------------------------- sonar-project.properties | 5 +- 4 files changed, 2 insertions(+), 425 deletions(-) delete mode 100644 internal/strategy/strategy.go delete mode 100644 internal/strategy/strategy_test.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 95e6f70..03409b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,9 +56,6 @@ jobs: - name: Install Eirctl uses: ensono/actions/eirctl-setup@v0.3.1 - with: - version: 0.9.3 - isPrerelease: false - name: Run Lint run: | diff --git a/internal/strategy/strategy.go b/internal/strategy/strategy.go deleted file mode 100644 index 9548f8b..0000000 --- a/internal/strategy/strategy.go +++ /dev/null @@ -1,132 +0,0 @@ -// Package strategy is a factory method wrapper around the backing store implementations -package strategy - -// import ( -// "context" -// "errors" -// "fmt" -// "sync" - -// "github.com/DevLabFoundry/configmanager/v3/internal/config" -// "github.com/DevLabFoundry/configmanager/v3/internal/log" -// "github.com/DevLabFoundry/configmanager/v3/internal/store" -// ) - -// var ErrTokenInvalid = errors.New("invalid token - cannot get prefix") - -// // StrategyFunc -// type StrategyFunc func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) - -// // StrategyFuncMap -// type StrategyFuncMap map[config.ImplementationPrefix]StrategyFunc - -// type Strategy struct { -// config config.GenVarsConfig -// strategyFuncMap strategyFnMap -// } - -// type Opts func(*Strategy) - -// // New -// func New(config config.GenVarsConfig, logger log.ILogger, opts ...Opts) *Strategy { -// rs := &Strategy{ -// config: config, -// strategyFuncMap: strategyFnMap{mu: sync.Mutex{}, funcMap: defaultStrategyFuncMap(logger)}, -// } -// // overwrite or add any options/defaults set above -// for _, o := range opts { -// o(rs) -// } - -// return rs -// } - -// // WithStrategyFuncMap Adds custom implementations for prefix -// // -// // Mainly used for testing -// // NOTE: this may lead to eventual optional configurations by users -// func WithStrategyFuncMap(funcMap StrategyFuncMap) Opts { -// return func(rs *Strategy) { -// rs.strategyFuncMap.mu.Lock() -// defer rs.strategyFuncMap.mu.Unlock() -// for prefix, implementation := range funcMap { -// rs.strategyFuncMap.funcMap[config.ImplementationPrefix(prefix)] = implementation -// } -// } -// } - -// // GetImplementation is a factory method returning the concrete implementation for the retrieval of the token value -// // i.e. facilitating the exchange of the supplied token for the underlying value -// func (rs *Strategy) GetImplementation(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// if token == nil { -// return nil, fmt.Errorf("unable to get prefix, %w", ErrTokenInvalid) -// } - -// if store, found := rs.strategyFuncMap.funcMap[token.Prefix()]; found { -// return store(ctx, token) -// } - -// return nil, fmt.Errorf("implementation not found for input string: %s", token) -// } - -// func ExchangeToken(s store.Strategy, token *config.ParsedTokenConfig) *TokenResponse { -// cr := &TokenResponse{} -// cr.Err = nil -// cr.key = token -// s.SetToken(token) -// cr.val, cr.Err = s.Value() -// return cr -// } - -// type TokenResponse struct { -// val string -// key *config.ParsedTokenConfig -// Err error -// } - -// func (tr *TokenResponse) WithKey(key *config.ParsedTokenConfig) { -// tr.key = key -// } - -// func (tr *TokenResponse) WithValue(val string) { -// tr.val = val -// } - -// func (tr *TokenResponse) Key() *config.ParsedTokenConfig { -// return tr.key -// } - -// func (tr *TokenResponse) Value() string { -// return tr.val -// } - -// func defaultStrategyFuncMap(logger log.ILogger) StrategyFuncMap { -// return map[config.ImplementationPrefix]StrategyFunc{ -// // config.AzTableStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// // return store.NewAzTableStore(ctx, token, logger) -// // }, -// // config.AzAppConfigPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// // return store.NewAzAppConf(ctx, token, logger) -// // }, -// // config.GcpSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// // return store.NewGcpSecrets(ctx, logger) -// // }, -// // config.SecretMgrPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// // return store.NewSecretsMgr(ctx, logger) -// // }, -// // config.ParamStorePrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// // return store.NewParamStore(ctx, logger) -// // }, -// // config.AzKeyVaultSecretsPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// // return store.NewKvScrtStore(ctx, token, logger) -// // }, -// // config.HashicorpVaultPrefix: func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// // return store.NewVaultStore(ctx, token, logger) -// // }, -// } -// } - -// type strategyFnMap struct { -// mu sync.Mutex -// funcMap StrategyFuncMap -// } diff --git a/internal/strategy/strategy_test.go b/internal/strategy/strategy_test.go deleted file mode 100644 index b567e52..0000000 --- a/internal/strategy/strategy_test.go +++ /dev/null @@ -1,287 +0,0 @@ -package strategy_test - -// import ( -// "context" -// "io" -// "testing" - -// "github.com/DevLabFoundry/configmanager/v3/internal/config" -// log "github.com/DevLabFoundry/configmanager/v3/internal/log" -// "github.com/DevLabFoundry/configmanager/v3/internal/store" -// "github.com/DevLabFoundry/configmanager/v3/internal/strategy" -// "github.com/DevLabFoundry/configmanager/v3/internal/testutils" -// ) - -// type mockGenerate struct { -// inToken, value string -// err error -// } - -// func (m mockGenerate) SetToken(s *config.ParsedTokenConfig) { -// } - -// func (m mockGenerate) Value() (s string, e error) { -// return m.value, m.err -// } - -// var TEST_GCP_CREDS = []byte(`{ -// "type": "service_account", -// "project_id": "xxxxx", -// "private_key_id": "yyyyyyyyyyyy", -// "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDf842hcn5Nvp6e\n7yKARaCVIDfLXpKDhRwUOvHMzJ1ioRgQo/kbv1n4yHGCSUFyY6hKGj0HBjaGj5kE\n79H/6Y3dJNGhnsMnxBhHdo+3FI8QF0CHZh460NMZSAJ41UMQSBGssGVsNfyUzXGH\nLc45sIx/Twx3yr1k2GD3E8FlDcKlZqa3xGHf+aipg2X3NxbYi+Sz7Yed+SOMhNHl\ncX6E/TqG9n1aTyIwjMIHscCYarJqURkJxr24ukDroCeMxAfxYTdMvRU2e8pFEdoY\nrgUC88fYfaVI5txJ6j/ZKauKQX9Pa8tSyXJeGva3JYp4VC7V4IyoVviCUgEGWZDN\n6/i3zoF/AgMBAAECggEAcVBCcVYFIkE48SH+Svjv74SFtpj7eSB4vKO2hPFjEOyB\nyKmu+aMwWvjQtiNqwf46wIPWLR+vpxYxTpYpo1sBNMvUZfp2tEA8KKyMuw3j9ThO\npjO9R/UxWrFcztbZP/u3NbFrH/2Q95mbv9IlbnsuG5xbqqEig0wYg+uzBvaXbig3\n/Jr0vLT2BkRCBKQkYGjVZcHlHVLoF7/J8cghFgkV1PGvknOv6/q7qzn9L4TjQIet\nfhrhN8Z1vgFiSYtpjP6YQEUEPSHmCQeD3WzJcnASPpU2uCUwd/z65ltKPnn+rqMt\n6jt9R1S1Ju2ZSjv+kR5fIXzihdOzncyzDDm33c/QwQKBgQD2QDZuzLjTxnhsfGii\nKJDAts+Jqfs/6SeEJcJKtEngj4m7rgzyEjbKVp8qtRHIzglKRWAe62/qzzy2BkKi\nvAd4+ZzmG2SkgypGsKVfjGXVFixz2gtUdmBOmK/TnYsxNT9yTt+rX9IGqKK60q73\nOWl8VsliLIsfvSH7+bqi7sRcXQKBgQDo0VUebyQHoTAXPdzGy2ysrVPDiHcldH0Y\n/hvhQTZwxYaJr3HpOCGol2Xl6zyawuudEQsoQwJ3Li6yeb0YMGiWX77/t+qX3pSn\nkGuoftGaNDV7sLn9UV2y+InF8EL1CasrhG1k5RIuxyfV0w+QUo+E7LpVR5XkbJqT\n9QNKnDQXiwKBgQDvvEYCCqbp7e/xVhEbxbhfFdro4Cat6tRAz+3egrTlvXhO0jzi\nMp9Kz5f3oP5ma0gaGX5hu75icE1fvKqE+d+ghAqe7w5FJzkyRulJI0tEb2jphN7A\n5NoPypBqyZboWjmhlG4mzouPVf/POCuEnk028truDAWJ6by7Lj3oP+HFNQKBgQCc\n5BQ8QiFBkvnZb7LLtGIzq0n7RockEnAK25LmJRAOxs13E2fsBguIlR3x5qgckqY8\nXjPqmd2bet+1HhyzpEuWqkcIBGRum2wJz2T9UxjklbJE/D8Z2i8OYDZX0SUOA8n5\ntXASwduS8lqB2Y1vcHOO3AhlV6xHFnjEpCPnr4PbKQKBgAhQ9D9MPeuz+5yw3yHg\nkvULZRtud+uuaKrOayprN25RTxr9c0erxqnvM7KHeo6/urOXeEa7x2n21kAT0Nch\nkF2RtWBLZKXGZEVBtw1Fw0UKNh4IDgM26dwlzRfTVHCiw6M6dCiTNk9KkP2vlkim\n3QFDSSUp+eBTXA17WkDAQf7w\n-----END PRIVATE KEY-----\n", -// "client_email": "foo@project.iam.gserviceaccount.com", -// "client_id": "99999911111111", -// "auth_uri": "https://accounts.google.com/o/oauth2/auth", -// "token_uri": "https://oauth2.googleapis.com/token", -// "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", -// "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/bla" -// }`) - -// func Test_Strategy_Retrieve_succeeds(t *testing.T) { -// ttests := map[string]struct { -// impl func(t *testing.T) store.Strategy -// config *config.GenVarsConfig -// token string -// expect string -// impPrefix config.ImplementationPrefix -// }{ -// "with mocked implementation AZTABLESTORAGE": { -// func(t *testing.T) store.Strategy { -// return &mockGenerate{"mountPath/token", "bar", nil} -// }, -// config.NewConfig().WithOutputPath("stdout"), -// "mountPath/token", -// "bar", -// config.AzTableStorePrefix, -// }, -// // "error in retrieval": { -// // func(t *testing.T) store.Strategy { -// // return &mockGenerate{"SOME://mountPath/token", "bar", fmt.Errorf("unable to perform getTokenValue")} -// // }, -// // config.NewConfig().WithOutputPath("stdout").WithTokenSeparator("://"), -// // []string{"SOME://token"}, -// // config.AzAppConfigPrefix, -// // "unable to perform getTokenValue", -// // }, -// } -// for name, tt := range ttests { -// t.Run(name, func(t *testing.T) { -// token, _ := config.NewToken(tt.impPrefix, *tt.config) -// token.WithSanitizedToken(tt.token) -// got := strategy.ExchangeToken(tt.impl(t), token) -// if got.Err != nil { -// t.Errorf(testutils.TestPhraseWithContext, "Token response errored", got.Err.Error(), tt.expect) -// } -// if got.Value() != tt.expect { -// t.Errorf(testutils.TestPhraseWithContext, "Value not correct", got.Value(), tt.expect) -// } -// if got.Key().StoreToken() != tt.token { -// t.Errorf(testutils.TestPhraseWithContext, "Incorrect Token returned in Key", got.Key().StoreToken(), tt.token) -// } -// }) -// } -// } - -// func Test_CustomStrategyFuncMap_add_own(t *testing.T) { - -// ttests := map[string]struct { -// }{ -// "default": {}, -// } -// for name := range ttests { -// t.Run(name, func(t *testing.T) { -// called := 0 -// genVarsConf := config.NewConfig() -// token, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig()) -// token.WithSanitizedToken("mountPath/token") - -// var custFunc = func(ctx context.Context, token *config.ParsedTokenConfig) (store.Strategy, error) { -// m := &mockGenerate{"AZTABLESTORE://mountPath/token", "bar", nil} -// called++ -// return m, nil -// } - -// s := strategy.New(*genVarsConf, log.New(io.Discard), strategy.WithStrategyFuncMap(strategy.StrategyFuncMap{config.AzTableStorePrefix: custFunc})) - -// store, _ := s.GetImplementation(context.TODO(), token) -// _ = strategy.ExchangeToken(store, token) - -// if called != 1 { -// t.Errorf(testutils.TestPhraseWithContext, "custom func not called", called, 1) -// } -// }) -// } -// } - -// func Test_SelectImpl_With(t *testing.T) { - -// ttests := map[string]struct { -// setUpTearDown func() func() -// token string -// config *config.GenVarsConfig -// expect func() store.Strategy -// expErr error -// impPrefix config.ImplementationPrefix -// }{ -// "unknown": { -// func() func() { -// return func() { -// } -// }, -// "foo/bar", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { return nil }, -// fmt.Errorf("implementation not found for input string: UNKNOWN#foo/bar"), -// config.UnknownPrefix, -// }, -// "success AZTABLESTORE": { -// func() func() { -// os.Setenv("AZURE_stuff", "foo") -// return func() { -// os.Clearenv() -// } -// }, -// "foo/bar1", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { -// token, _ := config.NewToken(config.AzTableStorePrefix, *config.NewConfig().WithTokenSeparator("#")) -// token.WithSanitizedToken("foo/bar1") - -// s, _ := store.NewAzTableStore(context.TODO(), token, log.New(io.Discard)) -// return s -// }, -// nil, -// config.AzTableStorePrefix, -// }, -// "success AWSPARAMSTR": { -// func() func() { -// os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") -// os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") -// return func() { -// os.Clearenv() -// } -// }, -// "foo/bar1", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { -// s, _ := store.NewParamStore(context.TODO(), log.New(io.Discard)) -// return s -// }, -// nil, -// config.ParamStorePrefix, -// }, -// "success AWSSECRETS": { -// func() func() { -// os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") -// os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") -// return func() { -// os.Clearenv() -// } -// }, -// "foo/bar1", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { -// s, _ := store.NewSecretsMgr(context.TODO(), log.New(io.Discard)) -// return s -// }, -// nil, -// config.SecretMgrPrefix, -// }, -// "success AZKVSECRET": { -// func() func() { -// os.Setenv("AWS_ACCESS_KEY", "AAAAAAAAAAAAAAA") -// os.Setenv("AWS_SECRET_ACCESS_KEY", "00000000000000000000111111111") -// return func() { -// os.Clearenv() -// } -// }, -// "foo/bar1", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { -// token, _ := config.NewToken(config.AzKeyVaultSecretsPrefix, *config.NewConfig().WithTokenSeparator("#")) -// token.WithSanitizedToken("foo/bar1") -// s, _ := store.NewKvScrtStore(context.TODO(), token, log.New(io.Discard)) -// return s -// }, -// nil, -// config.AzKeyVaultSecretsPrefix, -// }, -// "success AZAPPCONF": { -// func() func() { -// return func() { -// os.Clearenv() -// } -// }, -// "foo/bar1", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { -// token, _ := config.NewToken(config.AzAppConfigPrefix, *config.NewConfig().WithTokenSeparator("#")) -// token.WithSanitizedToken("foo/bar1") -// s, _ := store.NewAzAppConf(context.TODO(), token, log.New(io.Discard)) -// return s -// }, -// nil, -// config.AzAppConfigPrefix, -// }, -// "success VAULT": { -// func() func() { -// os.Setenv("VAULT_", "AAAAAAAAAAAAAAA") -// return func() { -// os.Clearenv() -// } -// }, -// "foo/bar1", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { -// token, _ := config.NewToken(config.HashicorpVaultPrefix, *config.NewConfig().WithTokenSeparator("#")) -// token.WithSanitizedToken("foo/bar1") -// s, _ := store.NewVaultStore(context.TODO(), token, log.New(io.Discard)) -// return s -// }, -// nil, -// config.HashicorpVaultPrefix, -// }, -// "success GCPSECRETS": { -// func() func() { -// cf, _ := os.CreateTemp(".", "*") -// cf.Write(TEST_GCP_CREDS) -// os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", cf.Name()) -// return func() { -// os.Remove(cf.Name()) -// os.Clearenv() -// } -// }, -// "foo/bar1", -// config.NewConfig().WithTokenSeparator("#"), -// func() store.Strategy { -// s, _ := store.NewGcpSecrets(context.TODO(), log.New(io.Discard)) -// return s -// }, -// nil, -// config.GcpSecretsPrefix, -// }, -// } -// for name, tt := range ttests { -// t.Run(name, func(t *testing.T) { -// tearDown := tt.setUpTearDown() -// defer tearDown() -// want := tt.expect() -// rs := strategy.New(*tt.config, log.New(io.Discard)) -// token, _ := config.NewToken(tt.impPrefix, *tt.config) -// token.WithSanitizedToken(tt.token) -// got, err := rs.GetImplementation(context.TODO(), token) - -// if err != nil { -// if err.Error() != tt.expErr.Error() { -// t.Errorf(testutils.TestPhraseWithContext, "uncaught error", err.Error(), tt.expErr.Error()) -// } -// return -// } - -// diff := deep.Equal(got, want) -// if diff != nil { -// t.Errorf(testutils.TestPhraseWithContext, "reflection of initialised implentations", fmt.Sprintf("%q", got), fmt.Sprintf("%q", want)) -// } -// }) -// } -// } diff --git a/sonar-project.properties b/sonar-project.properties index b070476..c823ad6 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,9 +1,8 @@ -sonar.projectKey=dnitsch_configmanager -sonar.organization=dnitsch +sonar.projectKey=DevLabFoundry_configmanager +sonar.organization=devlabfoundry # This is the name and version displayed in the SonarCloud UI. sonar.projectName=configmanager -# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=. sonar.exclusions=**/*_test.go,**/*_generated*.go,**/*_generated/**,**/vendor/**,**/examples/** sonar.inclusions=**/*.go From dcdc163c75b1a39978fe92b483de4fc596409ea4 Mon Sep 17 00:00:00 2001 From: dnitsch Date: Sat, 4 Apr 2026 15:33:10 +0100 Subject: [PATCH 15/15] fix: update set up tasks --- .github/workflows/build.yml | 3 +++ .github/workflows/release.yml | 3 +++ .github/workflows/release_container.yml | 17 ++++++++++------- sonar-project.properties | 3 ++- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03409b1..47c3611 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,9 @@ jobs: - name: Set SemVer Version uses: gittools/actions/gitversion/execute@v4.1.0 id: gitversion + with: + overrideConfig: | + next-version=3.0.0 - name: echo VERSIONS run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bfb8538..89db3d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,6 +39,9 @@ jobs: - name: Set SemVer Version uses: gittools/actions/gitversion/execute@v4.1.0 id: gitversion + with: + overrideConfig: | + next-version=3.0.0 release: name: Release diff --git a/.github/workflows/release_container.yml b/.github/workflows/release_container.yml index 3ac8556..2d1e3d5 100644 --- a/.github/workflows/release_container.yml +++ b/.github/workflows/release_container.yml @@ -2,15 +2,15 @@ name: Publish Container on: workflow_run: - workflows: ['CI'] + workflows: ["CI"] types: - completed - branches: + branches: - main permissions: contents: write - packages: write + packages: write jobs: set-version-tag: @@ -25,12 +25,15 @@ jobs: with: fetch-depth: 0 - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v3.0 + uses: gittools/actions/gitversion/setup@v4.1.0 with: - versionSpec: '5.x' + versionSpec: "6.x" - name: Set SemVer Version - uses: gittools/actions/gitversion/execute@v3.0 + uses: gittools/actions/gitversion/execute@v4.1.0 id: gitversion + with: + overrideConfig: | + next-version=3.0.0 build-and-push: runs-on: ubuntu-latest @@ -63,4 +66,4 @@ jobs: build-args: Version=${{ needs.set-version-tag.outputs.semVer }},Revision=${{ github.sha }} tags: | ghcr.io/ensono/eirctl:${{ needs.set-version-tag.outputs.semVer }} - platforms: linux/amd64,linux/arm64 # adjust as needed + platforms: linux/amd64,linux/arm64 # adjust as needed diff --git a/sonar-project.properties b/sonar-project.properties index c823ad6..818232b 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -12,7 +12,8 @@ sonar.test.inclusions=**/*_test.go sonar.test.exclusions=**/*_generated*.go,**/*_generated/**,**/vendor/** sonar.sourceEncoding=UTF-8 -sonar.qualitygate.wait=true +# until we start PR'ing into main - this needs to stay as false +sonar.qualitygate.wait=false # go sonar.go.coverage.reportPaths=.coverage/out