get.porter.sh/porter@v1.3.0/pkg/storage/migrations/legacy_plugin_adapter.go (about) 1 package migrations 2 3 import ( 4 "context" 5 "fmt" 6 "path/filepath" 7 8 "get.porter.sh/porter/pkg/config" 9 "get.porter.sh/porter/pkg/plugins/pluggable" 10 "get.porter.sh/porter/pkg/storage/migrations/crudstore" 11 "get.porter.sh/porter/pkg/storage/plugins" 12 "get.porter.sh/porter/pkg/tracing" 13 "go.opentelemetry.io/otel/attribute" 14 ) 15 16 // LegacyPluginAdapter can connect to a legacy Porter v0.38 storage plugin to 17 // retrieve data to migrate to the current version of Porter. 18 type LegacyPluginAdapter struct { 19 config *config.Config 20 21 // The name of the source storage account from which we will read old Porter data. 22 storageName string 23 24 // The path to an older PORTER_HOME directory containing the previous version 25 // of Porter and compatible plugins. 26 oldPorterHome string 27 28 // The legacy porter v0.38 storage interface that we will use with the old plugins 29 // to retrieve data. 30 store crudstore.Store 31 32 // Connection to the legacy plugin 33 pluginConn *pluggable.PluginConnection 34 } 35 36 func NewLegacyPluginAdapter(c *config.Config, oldPorterHome string, storageName string) *LegacyPluginAdapter { 37 return &LegacyPluginAdapter{ 38 config: c, 39 oldPorterHome: oldPorterHome, 40 storageName: storageName, 41 } 42 } 43 44 // Connect loads the legacy plugin specified by the source storage account. 45 func (m *LegacyPluginAdapter) Connect(ctx context.Context) error { 46 ctx, log := tracing.StartSpan(ctx, 47 attribute.String("storage-name", m.storageName)) 48 defer log.EndSpan() 49 50 // Create a config file that uses the old PORTER_HOME 51 oldConfig := config.New() 52 oldConfig.SetHomeDir(m.oldPorterHome) 53 oldConfig.SetPorterPath(filepath.Join(m.oldPorterHome, "porter")) 54 _, err := oldConfig.Load(ctx, func(ctx context.Context, secretKey string) (string, error) { 55 return "", nil 56 }) 57 if err != nil { 58 return err 59 } 60 oldConfig.Setenv(config.EnvHOME, m.oldPorterHome) 61 62 // Start the plugin 63 l := pluggable.NewPluginLoader(oldConfig) 64 conn, err := l.Load(ctx, m.makePluginConfig()) 65 if err != nil { 66 return log.Error(fmt.Errorf("could not load legacy storage plugin: %w", err)) 67 } 68 m.pluginConn = conn 69 70 // Ensure we close the plugin connection if anything fails 71 // Afterwards its on the caller to close it 72 connected := false 73 defer func() { 74 if !connected { 75 conn.Close(ctx) 76 } 77 }() 78 79 // Cast the plugin connection to a subset of the old protocol from v0.38 that can only read data 80 store, ok := conn.GetClient().(crudstore.Store) 81 if !ok { 82 return log.Error(fmt.Errorf("the interface exposed by the %s plugin was not crudstore.Store", conn)) 83 } 84 85 m.store = store 86 connected = true 87 return nil 88 } 89 90 // makePluginConfig creates a plugin configuration compatible with the legacy plugins 91 // from porter v0.38 92 func (m *LegacyPluginAdapter) makePluginConfig() pluggable.PluginTypeConfig { 93 return pluggable.PluginTypeConfig{ 94 Interface: plugins.PluginInterface, 95 Plugin: &crudstore.Plugin{}, 96 GetDefaultPluggable: func(c *config.Config) string { 97 // Load the config for the specific storage account named as the source for the migration 98 return m.storageName 99 }, 100 GetPluggable: func(c *config.Config, name string) (pluggable.Entry, error) { 101 return c.GetStorage(name) 102 }, 103 GetDefaultPlugin: func(c *config.Config) string { 104 // filesystem is the default storage plugin for v0.38 105 return "filesystem" 106 }, 107 ProtocolVersion: 1, // protocol version used by porter v0.38 108 } 109 } 110 111 func (m *LegacyPluginAdapter) Close() error { 112 if m.pluginConn != nil { 113 m.pluginConn.Close(context.Background()) 114 m.pluginConn = nil 115 } 116 return nil 117 } 118 119 func (m *LegacyPluginAdapter) List(ctx context.Context, itemType string, group string) ([]string, error) { 120 if err := m.Connect(ctx); err != nil { 121 return nil, err 122 } 123 124 return m.store.List(itemType, group) 125 } 126 127 func (m *LegacyPluginAdapter) Read(ctx context.Context, itemType string, name string) ([]byte, error) { 128 if err := m.Connect(ctx); err != nil { 129 return nil, err 130 } 131 132 return m.store.Read(itemType, name) 133 }