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  }