get.porter.sh/porter@v1.3.0/pkg/porter/internal_plugins.go (about)

     1  package porter
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  
    10  	"get.porter.sh/porter/pkg/config"
    11  	"get.porter.sh/porter/pkg/plugins"
    12  	"get.porter.sh/porter/pkg/portercontext"
    13  	secretsplugins "get.porter.sh/porter/pkg/secrets/plugins"
    14  	"get.porter.sh/porter/pkg/secrets/plugins/filesystem"
    15  	"get.porter.sh/porter/pkg/secrets/plugins/host"
    16  	signingplugins "get.porter.sh/porter/pkg/signing/plugins"
    17  	"get.porter.sh/porter/pkg/signing/plugins/cosign"
    18  	"get.porter.sh/porter/pkg/signing/plugins/notation"
    19  	storageplugins "get.porter.sh/porter/pkg/storage/plugins"
    20  	"get.porter.sh/porter/pkg/storage/plugins/mongodb"
    21  	"get.porter.sh/porter/pkg/storage/plugins/mongodb_docker"
    22  	"get.porter.sh/porter/pkg/tracing"
    23  	"github.com/hashicorp/go-plugin"
    24  )
    25  
    26  type RunInternalPluginOpts struct {
    27  	Key string
    28  }
    29  
    30  func (o *RunInternalPluginOpts) ApplyArgs(args []string) error {
    31  	if len(args) == 0 {
    32  		return errors.New("The positional argument KEY was not specified")
    33  	}
    34  	if len(args) > 1 {
    35  		return errors.New("Multiple positional arguments were specified but only one KEY is expected")
    36  	}
    37  
    38  	o.Key = args[0]
    39  	return nil
    40  }
    41  
    42  func (o *RunInternalPluginOpts) Validate() error {
    43  	if o.Key == "" {
    44  		return fmt.Errorf("no plugin key was specified")
    45  	}
    46  
    47  	if _, ok := internalPlugins[o.Key]; !ok {
    48  		return fmt.Errorf("invalid plugin key specified: %s", o.Key)
    49  	}
    50  
    51  	return nil
    52  }
    53  
    54  func (o *RunInternalPluginOpts) parsePluginConfig(c *portercontext.Context) (interface{}, error) {
    55  	pluginCfg := map[string]interface{}{}
    56  	if err := json.NewDecoder(c.In).Decode(&pluginCfg); err != nil {
    57  		if err == io.EOF {
    58  			// No plugin config was specified
    59  			return pluginCfg, nil
    60  		}
    61  		return nil, fmt.Errorf("error parsing plugin configuration from stdin as json: %w", err)
    62  	}
    63  	return pluginCfg, nil
    64  }
    65  
    66  func (p *Porter) RunInternalPlugins(ctx context.Context, opts RunInternalPluginOpts) (err error) {
    67  	err = opts.Validate()
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	// Read the plugin configuration from the porter config file from STDIN
    73  	pluginCfg, err := opts.parsePluginConfig(p.Context)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	// Create an instance of the plugin
    79  	selectedPlugin := internalPlugins[opts.Key]
    80  	impl, err := selectedPlugin.Create(p.Config, pluginCfg)
    81  	if err != nil {
    82  		return fmt.Errorf("could not create an instance of the requested internal plugin %s: %w", opts.Key, err)
    83  	}
    84  
    85  	defer func() {
    86  		if panicErr := recover(); err != nil {
    87  			err = fmt.Errorf("%v", panicErr)
    88  		}
    89  
    90  		if closer, ok := impl.(closablePlugin); ok {
    91  			if err = closer.Close(ctx); err != nil {
    92  				log := tracing.LoggerFromContext(ctx)
    93  				_ = log.Error(fmt.Errorf("error stopping the %s plugin: %w", opts.Key, err))
    94  			}
    95  		}
    96  	}()
    97  
    98  	plugins.Serve(p.Context, selectedPlugin.Interface, impl, selectedPlugin.ProtocolVersion)
    99  	return err // Return the error that may have been set during recover above
   100  }
   101  
   102  // A list of available plugins that we can serve directly from the porter binary
   103  var internalPlugins map[string]InternalPlugin = getInternalPlugins()
   104  
   105  // InternalPlugin represents the information needed to run one of the plugins
   106  // defined in porter's repository.
   107  type InternalPlugin struct {
   108  	Interface       string
   109  	ProtocolVersion int
   110  	Create          func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error)
   111  }
   112  
   113  // A long running plugin needs to setup a connection or other resources first,
   114  // and will hold those resources until porter is done with the plugin.
   115  type closablePlugin interface {
   116  	Close(ctx context.Context) error
   117  }
   118  
   119  func getInternalPlugins() map[string]InternalPlugin {
   120  	return map[string]InternalPlugin{
   121  		host.PluginKey: {
   122  			Interface:       secretsplugins.PluginInterface,
   123  			ProtocolVersion: secretsplugins.PluginProtocolVersion,
   124  			Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) {
   125  				return host.NewPlugin(c.Context), nil
   126  			},
   127  		},
   128  		filesystem.PluginKey: {
   129  			Interface:       secretsplugins.PluginInterface,
   130  			ProtocolVersion: secretsplugins.PluginProtocolVersion,
   131  			Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) {
   132  				return filesystem.NewPlugin(c, pluginCfg), nil
   133  			},
   134  		},
   135  		mongodb.PluginKey: {
   136  			Interface:       storageplugins.PluginInterface,
   137  			ProtocolVersion: storageplugins.PluginProtocolVersion,
   138  			Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) {
   139  				return mongodb.NewPlugin(c.Context, pluginCfg)
   140  			},
   141  		},
   142  		mongodb_docker.PluginKey: {
   143  			Interface:       storageplugins.PluginInterface,
   144  			ProtocolVersion: storageplugins.PluginProtocolVersion,
   145  			Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) {
   146  				return mongodb_docker.NewPlugin(c.Context, pluginCfg)
   147  			},
   148  		},
   149  		notation.PluginKey: {
   150  			Interface:       signingplugins.PluginInterface,
   151  			ProtocolVersion: signingplugins.PluginProtocolVersion,
   152  			Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) {
   153  				return notation.NewPlugin(c.Context, pluginCfg)
   154  			},
   155  		},
   156  		cosign.PluginKey: {
   157  			Interface:       signingplugins.PluginInterface,
   158  			ProtocolVersion: signingplugins.PluginProtocolVersion,
   159  			Create: func(c *config.Config, pluginCfg interface{}) (plugin.Plugin, error) {
   160  				return cosign.NewPlugin(c.Context, pluginCfg)
   161  			},
   162  		},
   163  	}
   164  }