get.porter.sh/porter@v1.3.0/pkg/secrets/pluginstore/store.go (about) 1 package pluginstore 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "get.porter.sh/porter/pkg/config" 9 "get.porter.sh/porter/pkg/plugins/pluggable" 10 "get.porter.sh/porter/pkg/secrets/plugins" 11 "get.porter.sh/porter/pkg/tracing" 12 "go.opentelemetry.io/otel/attribute" 13 ) 14 15 var _ plugins.SecretsProtocol = &Store{} 16 17 // Store is a plugin-backed source of secrets. It resolves the appropriate 18 // plugin based on Porter's config and implements the plugins.SecretsProtocol interface 19 // using the backing plugin. 20 // 21 // Connects just-in-time, but you must call Close to release resources. 22 type Store struct { 23 *config.Config 24 plugin plugins.SecretsProtocol 25 conn *pluggable.PluginConnection 26 } 27 28 func NewStore(c *config.Config) *Store { 29 return &Store{ 30 Config: c, 31 } 32 } 33 34 // NewSecretsPluginConfig for secret sources. 35 func NewSecretsPluginConfig() pluggable.PluginTypeConfig { 36 return pluggable.PluginTypeConfig{ 37 Interface: plugins.PluginInterface, 38 Plugin: &Plugin{}, 39 GetDefaultPluggable: func(c *config.Config) string { 40 return c.Data.DefaultSecrets 41 }, 42 GetPluggable: func(c *config.Config, name string) (pluggable.Entry, error) { 43 return c.GetSecretsPlugin(name) 44 }, 45 GetDefaultPlugin: func(c *config.Config) string { 46 return c.Data.DefaultSecretsPlugin 47 }, 48 ProtocolVersion: plugins.PluginProtocolVersion, 49 } 50 } 51 52 func (s *Store) Resolve(ctx context.Context, keyName string, keyValue string) (string, error) { 53 ctx, span := tracing.StartSpan(ctx, 54 attribute.String("keyName", keyName), 55 attribute.String("keyValue", keyValue)) 56 defer span.EndSpan() 57 58 if err := s.Connect(ctx); err != nil { 59 return "", err 60 } 61 62 value, err := s.plugin.Resolve(ctx, keyName, keyValue) 63 if err != nil { 64 return "", span.Error(err) 65 } 66 return value, nil 67 } 68 69 func (s *Store) Create(ctx context.Context, keyName string, keyValue string, value string) error { 70 ctx, span := tracing.StartSpan(ctx, 71 attribute.String("keyName", keyName), 72 attribute.String("keyValue", keyValue)) 73 defer span.EndSpan() 74 75 if err := s.Connect(ctx); err != nil { 76 return err 77 } 78 79 err := s.plugin.Create(ctx, keyName, keyValue, value) 80 if errors.Is(err, plugins.ErrNotImplemented) { 81 return span.Error(fmt.Errorf(`the current secrets plugin does not support persisting secrets. You need to edit your porter configuration file and configure a different secrets plugin. See https://porter.sh/end-users/configuration/#change-the-default-secrets-plugin for details: %w`, err)) 82 } 83 84 return span.Error(err) 85 } 86 87 // Connect initializes the plugin for use. 88 // The plugin itself is responsible for ensuring it was called. 89 // Close is called automatically when the plugin is used by Porter. 90 func (s *Store) Connect(ctx context.Context) error { 91 if s.plugin != nil { 92 return nil 93 } 94 95 ctx, span := tracing.StartSpan(ctx) 96 defer span.EndSpan() 97 98 pluginType := NewSecretsPluginConfig() 99 100 l := pluggable.NewPluginLoader(s.Config) 101 conn, err := l.Load(ctx, pluginType) 102 if err != nil { 103 return span.Error(err) 104 } 105 s.conn = conn 106 107 store, ok := conn.GetClient().(plugins.SecretsProtocol) 108 if !ok { 109 conn.Close(ctx) 110 return span.Error(fmt.Errorf("the interface (%T) exposed by the %s plugin was not plugins.SecretsProtocol", conn.GetClient(), conn)) 111 } 112 s.plugin = store 113 114 return nil 115 } 116 117 func (s *Store) Close() error { 118 if s.conn != nil { 119 s.conn.Close(context.Background()) 120 s.conn = nil 121 } 122 return nil 123 }