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 }