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

     1  package porter
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"get.porter.sh/porter/pkg/cnab"
     9  	"get.porter.sh/porter/pkg/storage"
    10  	"get.porter.sh/porter/pkg/tracing"
    11  )
    12  
    13  var _ BundleAction = NewInstallOptions()
    14  
    15  // InstallOptions that may be specified when installing a bundle.
    16  // Porter handles defaulting any missing values.
    17  type InstallOptions struct {
    18  	*BundleExecutionOptions
    19  
    20  	// Labels to apply to the installation.
    21  	Labels []string
    22  }
    23  
    24  func (o InstallOptions) Validate(ctx context.Context, args []string, p *Porter) error {
    25  	err := o.BundleExecutionOptions.Validate(ctx, args, p)
    26  	if err != nil {
    27  		return err
    28  	}
    29  
    30  	// Install requires special logic because the bundle must always be specified, including a name isn't enough.
    31  	// So we have a slight repeat of the logic performed in by the generic bundle action args
    32  	if o.File == "" && o.CNABFile == "" && o.Reference == "" {
    33  		return errors.New("No bundle specified. Either --reference, --file or --cnab-file must be specified or the current directory must contain a porter.yaml file.")
    34  	}
    35  
    36  	return nil
    37  }
    38  
    39  func (o InstallOptions) ParseLabels() map[string]string {
    40  	return parseLabels(o.Labels)
    41  }
    42  
    43  func (o InstallOptions) GetAction() string {
    44  	return cnab.ActionInstall
    45  }
    46  
    47  func (o InstallOptions) GetActionVerb() string {
    48  	return "installing"
    49  }
    50  
    51  func NewInstallOptions() InstallOptions {
    52  	return InstallOptions{
    53  		BundleExecutionOptions: NewBundleExecutionOptions(),
    54  	}
    55  }
    56  
    57  // InstallBundle accepts a set of pre-validated InstallOptions and uses
    58  // them to install a bundle.
    59  func (p *Porter) InstallBundle(ctx context.Context, opts InstallOptions) error {
    60  	ctx, log := tracing.StartSpan(ctx)
    61  	defer log.EndSpan()
    62  
    63  	i, err := p.Installations.GetInstallation(ctx, opts.Namespace, opts.Name)
    64  	if err == nil {
    65  		// Validate that we are not overwriting an existing installation
    66  		if i.IsInstalled() && !opts.Force {
    67  			err = errors.New("The installation has already been successfully installed and as a protection against accidentally overwriting existing installations, porter install cannot be repeated. Verify the installation name and namespace, and if correct, use porter upgrade. You can skip this check by using the --force flag.")
    68  			return log.Error(err)
    69  		}
    70  	} else if errors.Is(err, storage.ErrNotFound{}) {
    71  		// Create the installation record
    72  		i = storage.NewInstallation(opts.Namespace, opts.Name)
    73  	} else {
    74  		err = fmt.Errorf("could not retrieve the installation record: %w", err)
    75  		return log.Error(err)
    76  	}
    77  
    78  	// Apply labels that were specified as flags to the installation record
    79  	i.Labels = opts.ParseLabels()
    80  
    81  	err = p.applyActionOptionsToInstallation(ctx, opts, &i)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	err = p.Installations.UpsertInstallation(ctx, i)
    87  	if err != nil {
    88  		return fmt.Errorf("error saving installation record: %w", err)
    89  	}
    90  
    91  	if opts.VerifyBundleBeforeExecution {
    92  		ref, ok, err := i.Bundle.GetBundleReference()
    93  		if err != nil {
    94  			return err
    95  		}
    96  		log.Debugf("verifying bundle signature for %s", ref.String())
    97  		if !ok {
    98  			return log.Errorf("unable to get reference for bundle %s: %w", ref.String(), err)
    99  		}
   100  		err = p.Signer.Verify(ctx, ref.String())
   101  		if err != nil {
   102  			return log.Errorf("unable to verify signature: %w", err)
   103  		}
   104  		log.Debugf("bundle signature verified for %s", ref.String())
   105  
   106  		bun, err := opts.GetOptions().GetBundleReference(ctx, p)
   107  		if err != nil {
   108  			return log.Errorf("unable to get bundle reference")
   109  		}
   110  
   111  		invocationImage := bun.Definition.InvocationImages[0].Image
   112  		if relocInvImage, ok := bun.RelocationMap[invocationImage]; ok {
   113  			invocationImage = relocInvImage
   114  		}
   115  		log.Debugf("verifying bundle image signature for %s", invocationImage)
   116  		err = p.Signer.Verify(ctx, invocationImage)
   117  		if err != nil {
   118  			return log.Errorf("unable to verify signature: %w", err)
   119  		}
   120  		log.Debugf("bundle image signature verified for %s", invocationImage)
   121  	}
   122  
   123  	// Run install using the updated installation record
   124  	return p.ExecuteAction(ctx, i, opts)
   125  }
   126  
   127  func (p *Porter) sanitizeInstallation(ctx context.Context, inst *storage.Installation, bun cnab.ExtendedBundle) error {
   128  	strategies, err := p.Sanitizer.CleanParameters(ctx, inst.Parameters.Parameters, bun, inst.ID)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	inst.Parameters = inst.NewInternalParameterSet(strategies...)
   134  	return nil
   135  }