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

     1  package porter
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  
     9  	"get.porter.sh/porter/pkg/encoding"
    10  	"get.porter.sh/porter/pkg/pkgmgmt"
    11  	"get.porter.sh/porter/pkg/plugins"
    12  	"get.porter.sh/porter/pkg/printer"
    13  	"get.porter.sh/porter/pkg/tracing"
    14  	"github.com/olekukonko/tablewriter"
    15  	"go.opentelemetry.io/otel/attribute"
    16  	"go.uber.org/zap/zapcore"
    17  )
    18  
    19  // PrintPluginsOptions represent options for the PrintPlugins function
    20  type PrintPluginsOptions struct {
    21  	printer.PrintOptions
    22  }
    23  
    24  // ShowPluginOptions represent options for showing a particular plugin.
    25  type ShowPluginOptions struct {
    26  	printer.PrintOptions
    27  	Name string
    28  }
    29  
    30  func (o *ShowPluginOptions) Validate(args []string) error {
    31  	err := o.validateName(args)
    32  	if err != nil {
    33  		return err
    34  	}
    35  
    36  	return o.ParseFormat()
    37  }
    38  
    39  // validateName grabs the name from the first positional argument.
    40  func (o *ShowPluginOptions) validateName(args []string) error {
    41  	switch len(args) {
    42  	case 0:
    43  		return fmt.Errorf("no name was specified")
    44  	case 1:
    45  		o.Name = strings.ToLower(args[0])
    46  		return nil
    47  	default:
    48  		return fmt.Errorf("only one positional argument may be specified, the name, but multiple were received: %s", args)
    49  
    50  	}
    51  }
    52  
    53  func (p *Porter) PrintPlugins(ctx context.Context, opts PrintPluginsOptions) error {
    54  	installedPlugins, err := p.ListPlugins(ctx)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	switch opts.Format {
    60  	case printer.FormatPlaintext:
    61  		printRow :=
    62  			func(v interface{}) []string {
    63  				m, ok := v.(plugins.Metadata)
    64  				if !ok {
    65  					return nil
    66  				}
    67  				return []string{m.Name, m.VersionInfo.Version, m.VersionInfo.Author}
    68  			}
    69  		return printer.PrintTable(p.Out, installedPlugins, printRow, "Name", "Version", "Author")
    70  	case printer.FormatJson:
    71  		return printer.PrintJson(p.Out, installedPlugins)
    72  	case printer.FormatYaml:
    73  		return printer.PrintYaml(p.Out, installedPlugins)
    74  	default:
    75  		return fmt.Errorf("invalid format: %s", opts.Format)
    76  	}
    77  }
    78  
    79  func (p *Porter) ListPlugins(ctx context.Context) ([]plugins.Metadata, error) {
    80  	// List out what is installed on the file system
    81  	names, err := p.Plugins.List()
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	// Query each plugin and fill out their metadata, handle the
    87  	// cast from the PackageMetadata interface to the concrete type
    88  	installedPlugins := make([]plugins.Metadata, len(names))
    89  	for i, name := range names {
    90  		plugin, err := p.Plugins.GetMetadata(ctx, name)
    91  		if err != nil {
    92  			fmt.Fprintf(os.Stderr, "could not get version from plugin %s: %s\n ", name, err.Error())
    93  			continue
    94  		}
    95  
    96  		meta, _ := plugin.(*plugins.Metadata)
    97  		installedPlugins[i] = *meta
    98  	}
    99  
   100  	return installedPlugins, nil
   101  }
   102  
   103  func (p *Porter) ShowPlugin(ctx context.Context, opts ShowPluginOptions) error {
   104  	plugin, err := p.GetPlugin(ctx, opts.Name)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	switch opts.Format {
   110  	case printer.FormatPlaintext:
   111  		// Build and configure our tablewriter
   112  		// TODO: make this a function and reuse it in printer/table.go
   113  		table := tablewriter.NewWriter(p.Out)
   114  		table.SetCenterSeparator("")
   115  		table.SetColumnSeparator("")
   116  		table.SetAlignment(tablewriter.ALIGN_LEFT)
   117  		table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
   118  		table.SetBorders(tablewriter.Border{Left: false, Right: false, Bottom: false, Top: true})
   119  		table.SetAutoFormatHeaders(false)
   120  
   121  		// First, print the plugin metadata
   122  		fmt.Fprintf(p.Out, "Name: %s\n", plugin.Name)
   123  		fmt.Fprintf(p.Out, "Version: %s\n", plugin.Version)
   124  		fmt.Fprintf(p.Out, "Commit: %s\n", plugin.Commit)
   125  		fmt.Fprintf(p.Out, "Author: %s\n\n", plugin.Author)
   126  
   127  		table.SetHeader([]string{"Type", "Implementation"})
   128  		for _, row := range plugin.Implementations {
   129  			table.Append([]string{row.Type, row.Name})
   130  		}
   131  		table.Render()
   132  		return nil
   133  
   134  	case printer.FormatJson:
   135  		return printer.PrintJson(p.Out, plugin)
   136  	case printer.FormatYaml:
   137  		return printer.PrintYaml(p.Out, plugin)
   138  	default:
   139  		return fmt.Errorf("invalid format: %s", opts.Format)
   140  	}
   141  }
   142  
   143  func (p *Porter) GetPlugin(ctx context.Context, name string) (*plugins.Metadata, error) {
   144  	meta, err := p.Plugins.GetMetadata(ctx, name)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	plugin, ok := meta.(*plugins.Metadata)
   150  	if !ok {
   151  		return nil, fmt.Errorf("could not cast plugin %s to plugins.Metadata", name)
   152  	}
   153  
   154  	return plugin, nil
   155  }
   156  
   157  func (p *Porter) InstallPlugin(ctx context.Context, opts plugins.InstallOptions) error {
   158  	ctx, log := tracing.StartSpan(ctx)
   159  	defer log.EndSpan()
   160  
   161  	installOpts, err := p.getPluginInstallOptions(ctx, opts)
   162  	if err != nil {
   163  		return err
   164  	}
   165  	for _, opt := range installOpts {
   166  		err := p.Plugins.Install(ctx, opt)
   167  		if err != nil {
   168  			return err
   169  		}
   170  
   171  		plugin, err := p.Plugins.GetMetadata(ctx, opt.Name)
   172  		if err != nil {
   173  			return fmt.Errorf("failed to get plugin metadata: %w", err)
   174  		}
   175  
   176  		v := plugin.GetVersionInfo()
   177  		fmt.Fprintf(p.Out, "installed %s plugin %s (%s)\n", opt.Name, v.Version, v.Commit)
   178  	}
   179  
   180  	return nil
   181  }
   182  
   183  func (p *Porter) UninstallPlugin(ctx context.Context, opts pkgmgmt.UninstallOptions) error {
   184  	err := p.Plugins.Uninstall(ctx, opts)
   185  	if err != nil {
   186  		return err
   187  	}
   188  
   189  	fmt.Fprintf(p.Out, "Uninstalled %s plugin", opts.Name)
   190  
   191  	return nil
   192  }
   193  
   194  func (p *Porter) getPluginInstallOptions(ctx context.Context, opts plugins.InstallOptions) ([]pkgmgmt.InstallOptions, error) {
   195  	_, log := tracing.StartSpan(ctx)
   196  	defer log.EndSpan()
   197  
   198  	var installConfigs []pkgmgmt.InstallOptions
   199  	if opts.File != "" {
   200  		var data plugins.InstallPluginsSpec
   201  		if log.ShouldLog(zapcore.DebugLevel) {
   202  			// ignoring any error here, printing debug info isn't critical
   203  			contents, _ := p.FileSystem.ReadFile(opts.File)
   204  			log.Debug("read input file", attribute.String("contents", string(contents)))
   205  		}
   206  
   207  		if err := encoding.UnmarshalFile(p.FileSystem, opts.File, &data); err != nil {
   208  			return nil, fmt.Errorf("unable to parse %s as an installation document: %w", opts.File, err)
   209  		}
   210  
   211  		if err := data.Validate(); err != nil {
   212  			return nil, err
   213  		}
   214  
   215  		sortedCfgs := plugins.NewInstallPluginConfigs(data.Plugins)
   216  
   217  		for _, config := range sortedCfgs.Values() {
   218  			// if user specified a feed url or mirror using the flags, it will become
   219  			// the default value and apply to empty values parsed from the provided file
   220  			if config.FeedURL == "" {
   221  				config.FeedURL = opts.FeedURL
   222  			}
   223  			if config.Mirror == "" {
   224  				config.Mirror = opts.Mirror
   225  			}
   226  
   227  			if err := config.Validate([]string{config.Name}); err != nil {
   228  				return nil, err
   229  			}
   230  			installConfigs = append(installConfigs, config)
   231  
   232  		}
   233  
   234  		return installConfigs, nil
   235  	}
   236  
   237  	installConfigs = append(installConfigs, opts.InstallOptions)
   238  	return installConfigs, nil
   239  }