Repository: getporter/porter
Pull request #1747 (2021-08-30 15:01:35)

Remove debug print statement
This was a debug print statement that I meant to just add while fixing something locally. It shouldn't be printed every time you run a bundle!



Patch:
diff --git a/cmd/porter/bundle.go b/cmd/porter/bundle.go
index bc46cbfd..331783da 100644
--- a/cmd/porter/bundle.go
+++ b/cmd/porter/bundle.go
@@ -182,7 +182,7 @@ The first argument is the installation name to upgrade. This defaults to the nam
 
 Porter uses the Docker driver as the default runtime for executing a bundle's invocation image, but an alternate driver may be supplied via '--driver/-d'.
 For example, the 'debug' driver may be specified, which simply logs the info given to it and then exits.`,
-		Example: `  porter bundle upgrade --version 0.2.0
+		Example: `  porter bundle upgrade
   porter bundle upgrade --reference getporter/kubernetes:v0.1.0
   porter bundle upgrade --reference localhost:5000/getporter/kubernetes:v0.1.0 --insecure-registry --force
   porter bundle upgrade MyAppInDev --file myapp/bundle.json
@@ -215,8 +215,6 @@ For example, the 'debug' driver may be specified, which simply logs the info giv
 		"Specify a driver to use. Allowed values: docker, debug")
 	f.StringVarP(&opts.Namespace, "namespace", "n", "",
 		"Namespace of the specified installation. Defaults to the global namespace.")
-	f.StringVar(&opts.Version, "version", "",
-		"Version to which the installation should be upgraded. This represents the version of the bundle, which assumes the convention of setting the bundle tag to its version.")
 	addBundlePullFlags(f, &opts.BundlePullOptions)
 
 	return cmd
diff --git a/cmd/porter/credentials.go b/cmd/porter/credentials.go
index 10439442..8e25ec28 100644
--- a/cmd/porter/credentials.go
+++ b/cmd/porter/credentials.go
@@ -106,7 +106,7 @@ will then provide it to the bundle in the correct location. `,
   porter credential generate kubecred --cnab-file myapp/bundle.json
 `,
 		PreRunE: func(cmd *cobra.Command, args []string) error {
-			return opts.Validate(args, p)
+			return opts.Validate(args, p.Context)
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return p.GenerateCredentials(opts)
diff --git a/cmd/porter/parameters.go b/cmd/porter/parameters.go
index 232c037a..f99d9488 100644
--- a/cmd/porter/parameters.go
+++ b/cmd/porter/parameters.go
@@ -106,7 +106,7 @@ will then provide it to the bundle in the correct location. `,
   porter parameter generate myparamset --cnab-file myapp/bundle.json
 `,
 		PreRunE: func(cmd *cobra.Command, args []string) error {
-			return opts.Validate(args, p)
+			return opts.Validate(args, p.Context)
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			return p.GenerateParameters(opts)
diff --git a/docs/content/cli/bundles_upgrade.md b/docs/content/cli/bundles_upgrade.md
index 88805350..b43c9e9c 100644
--- a/docs/content/cli/bundles_upgrade.md
+++ b/docs/content/cli/bundles_upgrade.md
@@ -23,7 +23,7 @@ porter bundles upgrade [INSTALLATION] [flags]
 ### Examples
 
 ```
-  porter bundle upgrade --version 0.2.0
+  porter bundle upgrade
   porter bundle upgrade --reference getporter/kubernetes:v0.1.0
   porter bundle upgrade --reference localhost:5000/getporter/kubernetes:v0.1.0 --insecure-registry --force
   porter bundle upgrade MyAppInDev --file myapp/bundle.json
@@ -48,7 +48,6 @@ porter bundles upgrade [INSTALLATION] [flags]
       --param strings              Define an individual parameter in the form NAME=VALUE. Overrides parameters otherwise set via --parameter-set. May be specified multiple times.
   -p, --parameter-set strings      Name of a parameter set file for the bundle. May be either a named set of parameters or a filepath, and specified multiple times.
   -r, --reference string           Use a bundle in an OCI registry specified by the given reference.
-      --version string             Version to which the installation should be upgraded. This represents the version of the bundle, which assumes the convention of setting the bundle tag to its version.
 ```
 
 ### Options inherited from parent commands
diff --git a/docs/content/cli/upgrade.md b/docs/content/cli/upgrade.md
index 10dc39be..dc2a7105 100644
--- a/docs/content/cli/upgrade.md
+++ b/docs/content/cli/upgrade.md
@@ -23,7 +23,7 @@ porter upgrade [INSTALLATION] [flags]
 ### Examples
 
 ```
-  porter upgrade --version 0.2.0
+  porter upgrade
   porter upgrade --reference getporter/kubernetes:v0.1.0
   porter upgrade --reference localhost:5000/getporter/kubernetes:v0.1.0 --insecure-registry --force
   porter upgrade MyAppInDev --file myapp/bundle.json
@@ -48,7 +48,6 @@ porter upgrade [INSTALLATION] [flags]
       --param strings              Define an individual parameter in the form NAME=VALUE. Overrides parameters otherwise set via --parameter-set. May be specified multiple times.
   -p, --parameter-set strings      Name of a parameter set file for the bundle. May be either a named set of parameters or a filepath, and specified multiple times.
   -r, --reference string           Use a bundle in an OCI registry specified by the given reference.
-      --version string             Version to which the installation should be upgraded. This represents the version of the bundle, which assumes the convention of setting the bundle tag to its version.
 ```
 
 ### Options inherited from parent commands
diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go
index e0d0378b..249bdc15 100644
--- a/pkg/cache/cache.go
+++ b/pkg/cache/cache.go
@@ -9,9 +9,7 @@ import (
 	"get.porter.sh/porter/pkg/cnab"
 	configadapter "get.porter.sh/porter/pkg/cnab/config-adapter"
 	"get.porter.sh/porter/pkg/config"
-	"get.porter.sh/porter/pkg/encoding"
 	"get.porter.sh/porter/pkg/manifest"
-	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 )
 
@@ -95,11 +93,6 @@ func (c *Cache) StoreBundle(bundleRef cnab.BundleReference) (CachedBundle, error
 		return CachedBundle{}, errors.Wrapf(err, "error writing to cnab/bundle.json for %s", cb.Reference)
 	}
 
-	err = c.cacheMetadata(&cb)
-	if err != nil {
-		return CachedBundle{}, err
-	}
-
 	err = c.cacheManifest(&cb)
 	if err != nil {
 		return CachedBundle{}, err
@@ -129,22 +122,6 @@ func (c *Cache) StoreBundle(bundleRef cnab.BundleReference) (CachedBundle, error
 	return cb, nil
 }
 
-// cacheMetadata stores additional metadata about the bundle.
-func (c *Cache) cacheMetadata(cb *CachedBundle) error {
-	meta := Metadata{
-		Reference: cb.Reference,
-		Digest:    cb.Digest,
-	}
-	path := cb.BuildMetadataPath()
-	return encoding.MarshalFile(c.FileSystem, path, meta)
-}
-
-// Metadata associated with a cached bundle.
-type Metadata struct {
-	Reference cnab.OCIReference `json:"reference"`
-	Digest    digest.Digest     `json:"digest"`
-}
-
 // cacheManifest extracts the porter.yaml from the bundle, if present and caches it
 // in the same cache directory as the rest of the bundle.
 func (c *Cache) cacheManifest(cb *CachedBundle) error {
diff --git a/pkg/cache/cached_bundle.go b/pkg/cache/cached_bundle.go
index ef8673fe..f0ae8409 100644
--- a/pkg/cache/cached_bundle.go
+++ b/pkg/cache/cached_bundle.go
@@ -9,7 +9,6 @@ import (
 	"get.porter.sh/porter/pkg/cnab"
 	"get.porter.sh/porter/pkg/config"
 	"get.porter.sh/porter/pkg/context"
-	"get.porter.sh/porter/pkg/encoding"
 	"get.porter.sh/porter/pkg/manifest"
 	"github.com/cnabio/cnab-to-oci/relocation"
 	"github.com/pkg/errors"
@@ -67,39 +66,18 @@ func (cb *CachedBundle) BuildManifestPath() string {
 	return filepath.Join(cb.cacheDir, config.Name)
 }
 
-// BuildMetadataPath generates the location of the cache metadata.
-func (cb *CachedBundle) BuildMetadataPath() string {
-	return filepath.Join(cb.cacheDir, "metadata.json")
-}
-
 // Load starts from the bundle tag, and hydrates the cached bundle from the cache.
 func (cb *CachedBundle) Load(cxt *context.Context) (bool, error) {
 	// Check that the bundle exists
 	cb.BundlePath = cb.BuildBundlePath()
-	bundleExists, err := cxt.FileSystem.Exists(cb.BuildMetadataPath())
+	bundleExists, err := cxt.FileSystem.Exists(cb.BundlePath)
 	if err != nil {
-		return false, errors.Wrapf(err, "unable to read bundle %s at %s", cb.Reference, cb.BuildMetadataPath())
+		return false, errors.Wrapf(err, "unable to read bundle %s at %s", cb.Reference, cb.BundlePath)
 	}
 	if !bundleExists {
 		return false, nil
 	}
 
-	metaPath := cb.BuildMetadataPath()
-	metaExists, err := cxt.FileSystem.Exists(metaPath)
-	if err != nil {
-		return false, errors.Wrapf(err, "unable to access bundle metadata %s at %s", cb.Reference, metaPath)
-	}
-	if !metaExists {
-		// consider this a miss, recache with the metadata
-		return false, nil
-	}
-	var meta Metadata
-	err = encoding.UnmarshalFile(cxt.FileSystem, metaPath, &meta)
-	if err != nil {
-		return false, errors.Wrapf(err, "unable to parse cached bundle metadata %s at %s", cb.Reference, metaPath)
-	}
-	cb.Digest = meta.Digest
-
 	// Check for the optional relocation mapping next to it
 	reloPath := cb.BuildRelocationFilePath()
 	reloExists, err := cxt.FileSystem.Exists(reloPath)
diff --git a/pkg/claims/installation.go b/pkg/claims/installation.go
index 8f8afb5d..55b5b34b 100644
--- a/pkg/claims/installation.go
+++ b/pkg/claims/installation.go
@@ -8,7 +8,6 @@ import (
 	"get.porter.sh/porter/pkg/storage"
 	"github.com/Masterminds/semver/v3"
 	"github.com/cnabio/cnab-go/schema"
-	"github.com/opencontainers/go-digest"
 	"github.com/pkg/errors"
 )
 
@@ -45,9 +44,6 @@ type Installation struct {
 	// BundleDigest is the current digest of the bundle.
 	BundleDigest string `json:"bundleDigest,omitempty" yaml:"bundleDigest,omitempty" toml:"bundleDigest,omitempty"`
 
-	// BundleTag is the OCI tag of the current bundle definition.
-	BundleTag string `json:"bundleTag,omitempty" yaml:"bundleTag,omitempty" toml:"bundleTag,omitempty"`
-
 	// Custom extension data applicable to a given runtime.
 	// TODO(carolynvs): remove and populate in ToCNAB when we firm up the spec
 	Custom interface{} `json:"custom,omitempty" yaml:"custom,omitempty" toml:"custom,omitempty"`
@@ -57,71 +53,16 @@ type Installation struct {
 
 	// Parameters specified by the user through overrides or parameter sets.
 	// Does not include defaults, or values resolved from parameter sources.
-	Parameters map[string]interface{} `json:"parameters,omitempty" yaml:"parameters,omitempty" toml:"parameters,omitempty"`
-
-	// CredentialSets that should be included when the bundle is reconciled.
-	CredentialSets []string `json:"credentialSets,omitempty" yaml:"credentialSets,omitempty" toml:"credentialSets,omitempty"`
-
-	// ParameterSets that should be included when the bundle is reconciled.
-	ParameterSets []string `json:"parameterSets,omitempty" yaml:"parameterSets,omitempty" toml:"parameterSets,omitempty"`
+	Parameters map[string]interface{} `json:"parameters" yaml:"parameters" toml:"parameters"`
 
 	// Status of the installation.
-	Status InstallationStatus `json:"status,omitempty" yaml:"status,omitempty" toml:"status,omitempty"`
+	Status InstallationStatus `json:"status" yaml:"status" toml:"status"`
 }
 
 func (i Installation) String() string {
 	return fmt.Sprintf("%s/%s", i.Namespace, i.Name)
 }
 
-func (i Installation) GetBundleReference() (cnab.OCIReference, bool, error) {
-	if i.BundleRepository == "" {
-		return cnab.OCIReference{}, false, nil
-	}
-
-	ref, err := cnab.ParseOCIReference(i.BundleRepository)
-	if err != nil {
-		return cnab.OCIReference{}, false, errors.Wrapf(err, "invalid BundleRepository %s", i.BundleRepository)
-	}
-
-	if i.BundleDigest != "" {
-		d, err := digest.Parse(i.BundleDigest)
-		if err != nil {
-			return cnab.OCIReference{}, false, errors.Wrapf(err, "invalid BundleDigest %s", i.BundleDigest)
-		}
-
-		ref, err = ref.WithDigest(d)
-		if err != nil {
-			return cnab.OCIReference{}, false, errors.Wrapf(err, "error joining the BundleRepository %s and BundleDigest %s", i.BundleRepository, i.BundleDigest)
-		}
-		return ref, true, nil
-	}
-
-	if i.BundleVersion != "" {
-		v, err := semver.NewVersion(i.BundleVersion)
-		if err != nil {
-			return cnab.OCIReference{}, false, errors.New("invalid BundleVersion")
-		}
-
-		// The bundle version feature can only be used with standard naming conventions
-		// everyone else can use the tag field if they do weird things
-		ref, err = ref.WithTag("v" + v.String())
-		if err != nil {
-			return cnab.OCIReference{}, false, errors.Wrapf(err, "error joining the BundleRepository %s and BundleVersion %s", i.BundleRepository, i.BundleVersion)
-		}
-		return ref, true, nil
-	}
-
-	if i.BundleTag != "" {
-		ref, err = ref.WithTag(i.BundleTag)
-		if err != nil {
-			return cnab.OCIReference{}, false, errors.Wrapf(err, "error joining the BundleRepository %s and BundleTag %s", i.BundleRepository, i.BundleTag)
-		}
-		return ref, true, nil
-	}
-
-	return cnab.OCIReference{}, false, errors.New("Invalid installation, either BundleDigest, BundleVersion or BundleTag must be specified")
-}
-
 func (i Installation) DefaultDocumentFilter() interface{} {
 	return map[string]interface{}{"namespace": i.Namespace, "name": i.Name}
 }
@@ -166,9 +107,6 @@ func (i Installation) NewRun(action string) Run {
 func (i *Installation) ApplyResult(run Run, result Result) {
 	// Update the installation with the last modifying action
 	if action, err := run.Bundle.GetAction(run.Action); err == nil && action.Modifies {
-		i.Status.BundleReference = run.BundleReference
-		i.Status.BundleVersion = run.Bundle.Version
-		i.Status.BundleDigest = run.BundleDigest
 		i.Status.RunID = run.ID
 		i.Status.Action = run.Action
 		i.Status.ResultID = result.ID
@@ -187,10 +125,7 @@ func (i *Installation) Apply(input Installation) {
 	i.BundleRepository = input.BundleRepository
 	i.BundleDigest = input.BundleDigest
 	i.BundleVersion = input.BundleVersion
-	i.BundleTag = input.BundleTag
 	i.Parameters = input.Parameters
-	i.CredentialSets = input.CredentialSets
-	i.ParameterSets = input.ParameterSets
 	i.Labels = input.Labels
 }
 
@@ -203,21 +138,26 @@ func (i *Installation) Validate() error {
 	// We can change these to better checks if we consolidate our logic around the various ways we let you
 	// install from a bundle definition https://github.com/getporter/porter/issues/1024#issuecomment-899828081
 	// Until then, these are pretty weak checks
-	_, _, err := i.GetBundleReference()
-	return errors.Wrapf(err, "could not determine the fully-qualified bundle reference")
-}
 
-// TrackBundle updates the bundle that the installation is tracking.
-func (i *Installation) TrackBundle(ref cnab.OCIReference) {
-	// Determine if the bundle is managed by version, digest or tag
-	i.BundleRepository = ref.Repository()
-	if ref.HasVersion() {
-		i.BundleVersion = ref.Version()
-	} else if ref.HasDigest() {
-		i.BundleDigest = ref.Digest().String()
-	} else {
-		i.BundleTag = ref.Tag()
+	if i.BundleRepository != "" {
+		ref, err := cnab.ParseOCIReference(i.BundleRepository)
+		if err != nil {
+			return errors.Wrapf(err, "invalid bundleRepository %s", i.BundleRepository)
+		}
+		i.BundleRepository = ref.Repository()
+	}
+
+	if i.BundleVersion != "" {
+		if _, err := semver.NewVersion(i.BundleVersion); err != nil {
+			return errors.Wrapf(err, "Invalid bundleVersion. Must be a valid v2 semver value.")
+		}
+	}
+
+	if i.BundleRepository != "" && i.BundleDigest == "" && i.BundleVersion == "" {
+		return errors.Errorf("either the bundleVersion or the bundleDigest must be provided.")
 	}
+
+	return nil
 }
 
 // InstallationStatus's purpose is to assist with making porter list be able to display everything
@@ -239,13 +179,4 @@ type InstallationStatus struct {
 	// Once that state is reached, Porter should not allow it to be reinstalled as a protection from installations
 	// being overwritten.
 	InstallationCompleted bool `json:"installationCompleted" yaml:"installationCompleted" toml:"installationCompleted"`
-
-	// BundleReference of the bundle that last altered the installation state.
-	BundleReference string `json:"bundleReference" yaml:"bundleReference" toml:"bundleReference"`
-
-	// BundleVersion is the version of the bundle that last altered the installation state.
-	BundleVersion string `json:"bundleVersion" yaml:"bundleVersion" toml:"bundleVersion"`
-
-	// BundleDigest is the digest of the bundle that last altered the installation state.
-	BundleDigest string `json:"bundleDigest" yaml:"bundleDigest" toml:"bundleDigest"`
 }
diff --git a/pkg/cnab/parameter_sources.go b/pkg/cnab/parameter_sources.go
index 2a1b8549..21834ac3 100644
--- a/pkg/cnab/parameter_sources.go
+++ b/pkg/cnab/parameter_sources.go
@@ -2,7 +2,6 @@ package cnab
 
 import (
 	"encoding/json"
-	"fmt"
 
 	"github.com/pkg/errors"
 )
@@ -209,7 +208,6 @@ func (b ExtendedBundle) SupportsParameterSources() bool {
 // GetParameterSources checks if the parameter sources extension is present and returns its
 // extension configuration.
 func (e ProcessedExtensions) GetParameterSources() (ParameterSources, bool, error) {
-	fmt.Printf("Processed bundle extensions:\n%#v\n", e)
 	rawExt, required := e[ParameterSourcesExtensionKey]
 
 	ext, ok := rawExt.(ParameterSources)
diff --git a/pkg/cnab/provider/action.go b/pkg/cnab/provider/action.go
index c5575b8d..f37dff18 100644
--- a/pkg/cnab/provider/action.go
+++ b/pkg/cnab/provider/action.go
@@ -127,7 +127,9 @@ func (r *Runtime) Execute(args ActionArguments) error {
 	// Create a record for the run we are about to execute
 	var currentRun = args.Installation.NewRun(args.Action)
 	currentRun.Bundle = b.Bundle
-	currentRun.BundleReference = args.BundleReference.Reference.String()
+	if args.BundleReference.Reference != (cnab.OCIReference{}) {
+		currentRun.BundleReference = args.BundleReference.Reference.String()
+	}
 	currentRun.BundleDigest = args.BundleReference.Digest.String()
 	currentRun.Parameters = params
 
diff --git a/pkg/encoding/encoding.go b/pkg/encoding/encoding.go
index bc0c35f3..2d00e20f 100644
--- a/pkg/encoding/encoding.go
+++ b/pkg/encoding/encoding.go
@@ -18,17 +18,6 @@ const (
 	Toml = "toml"
 )
 
-// MarshalFile encodes the specified struct to a file.
-// Supported file extensions are: yaml, yml, json, and toml.
-func MarshalFile(fs aferox.Aferox, path string, in interface{}) error {
-	format := strings.TrimPrefix(filepath.Ext(path), ".")
-	data, err := Marshal(format, in)
-	if err != nil {
-		return err
-	}
-	return fs.WriteFile(path, data, 0700)
-}
-
 // MarshalYaml converts the input to yaml.
 func MarshalYaml(in interface{}) ([]byte, error) {
 	return Marshal(Yaml, in)
diff --git a/pkg/porter/action.go b/pkg/porter/action.go
index 0b0b33e2..a26c0312 100644
--- a/pkg/porter/action.go
+++ b/pkg/porter/action.go
@@ -11,8 +11,11 @@ import (
 func (p *Porter) ExecuteAction(installation claims.Installation, action BundleAction) error {
 	actionOpts := action.GetOptions()
 
+	// TODO(carolynvs): this is being called twice
+	_, err := p.resolveBundleReference(actionOpts)
+
 	deperator := newDependencyExecutioner(p, action.GetAction())
-	err := deperator.Prepare(action)
+	err = deperator.Prepare(action)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/porter/credentials.go b/pkg/porter/credentials.go
index f3a697ba..aec233cc 100644
--- a/pkg/porter/credentials.go
+++ b/pkg/porter/credentials.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"time"
 
+	"get.porter.sh/porter/pkg/context"
 	"get.porter.sh/porter/pkg/credentials"
 	"get.porter.sh/porter/pkg/editor"
 	"get.porter.sh/porter/pkg/encoding"
@@ -80,18 +81,18 @@ func (o CredentialOptions) ParseLabels() map[string]string {
 // Validate prepares for an action and validates the options.
 // For example, relative paths are converted to full paths and then checked that
 // they exist and are accessible.
-func (o *CredentialOptions) Validate(args []string, p *Porter) error {
-	err := o.validateCredName(args)
+func (g *CredentialOptions) Validate(args []string, cxt *context.Context) error {
+	err := g.validateCredName(args)
 	if err != nil {
 		return err
 	}
 
-	return o.BundleActionOptions.Validate(args, p)
+	return g.bundleFileOptions.Validate(cxt)
 }
 
-func (o *CredentialOptions) validateCredName(args []string) error {
+func (g *CredentialOptions) validateCredName(args []string) error {
 	if len(args) == 1 {
-		o.Name = args[0]
+		g.Name = args[0]
 	} else if len(args) > 1 {
 		return errors.Errorf("only one positional argument may be specified, the credential name, but multiple were received: %s", args)
 	}
diff --git a/pkg/porter/install.go b/pkg/porter/install.go
index a6b8e91b..ce3b0a2c 100644
--- a/pkg/porter/install.go
+++ b/pkg/porter/install.go
@@ -52,11 +52,8 @@ func NewInstallOptions() InstallOptions {
 // InstallBundle accepts a set of pre-validated InstallOptions and uses
 // them to install a bundle.
 func (p *Porter) InstallBundle(opts InstallOptions) error {
-	// Figure out which bundle/installation we are working with
-	bundleRef, err := p.resolveBundleReference(opts.BundleActionOptions)
-	if err != nil {
-		return err
-	}
+	// TODO(carolynvs): this is being called twice
+	_, err := p.resolveBundleReference(opts.BundleActionOptions)
 
 	i, err := p.Claims.GetInstallation(opts.Namespace, opts.Name)
 	if err == nil {
@@ -71,59 +68,11 @@ func (p *Porter) InstallBundle(opts InstallOptions) error {
 		return errors.Wrapf(err, "could not retrieve the installation record")
 	}
 
-	err = p.applyActionOptionsToInstallation(i, opts.BundleActionOptions)
-	if err != nil {
-		return err
-	}
-	i.TrackBundle(bundleRef.Reference)
 	i.Labels = opts.ParseLabels()
 	err = p.Claims.UpsertInstallation(i)
 	if err != nil {
 		return errors.Wrap(err, "error saving installation record")
 	}
 
-	// Run install using the updated installation record
 	return p.ExecuteAction(i, opts)
 }
-
-// Remember the parameters and credentials used with the bundle last.
-// Appends any newly specified parameters, parameter/credential sets to the installation record.
-// Users are expected to edit the installation record if they don't want that behavior.
-func (p *Porter) applyActionOptionsToInstallation(i claims.Installation, opts *BundleActionOptions) error {
-	// Record the parameters specified by the user, with flags taking precedence over parameter set values
-	err := opts.LoadParameters(p)
-	if err != nil {
-		return err
-	}
-	if i.Parameters == nil {
-		i.Parameters = make(map[string]interface{}, len(opts.combinedParameters))
-	}
-	for k, v := range opts.combinedParameters {
-		i.Parameters[k] = v
-	}
-	// Record the names of the parameter sets used
-	for _, ps := range opts.ParameterSets {
-		if isParameterSetFile, _ := p.FileSystem.Exists(ps); isParameterSetFile {
-			continue
-		}
-		for _, existing := range i.ParameterSets {
-			if existing == ps {
-				continue
-			}
-		}
-		i.ParameterSets = append(i.ParameterSets, ps)
-	}
-	// Record the names of the credential sets used
-	for _, cs := range opts.CredentialIdentifiers {
-		if isCredentialSetFile, _ := p.FileSystem.Exists(cs); isCredentialSetFile {
-			continue
-		}
-		for _, existing := range i.ParameterSets {
-			if existing == cs {
-				continue
-			}
-		}
-		i.CredentialSets = append(i.CredentialSets, cs)
-	}
-	return nil
-}
diff --git a/pkg/porter/invoke.go b/pkg/porter/invoke.go
index f73214a3..83c3786f 100644
--- a/pkg/porter/invoke.go
+++ b/pkg/porter/invoke.go
@@ -41,20 +41,18 @@ func (o InvokeOptions) Validate(args []string, p *Porter) error {
 // InvokeBundle accepts a set of pre-validated InvokeOptions and uses
 // them to upgrade a bundle.
 func (p *Porter) InvokeBundle(opts InvokeOptions) error {
-	// Figure out which bundle/installation we are working with
 	bundleRef, err := p.resolveBundleReference(opts.BundleActionOptions)
 	if err != nil {
 		return err
 	}
 
+	action, err := bundleRef.Definition.GetAction(opts.Action)
+	if err != nil {
+		return fmt.Errorf("invalid --action %s", opts.Action)
+	}
+
 	installation, err := p.Claims.GetInstallation(opts.Namespace, opts.Name)
 	if errors.Is(err, storage.ErrNotFound{}) {
-		action, actionErr := bundleRef.Definition.GetAction(opts.Action)
-		if actionErr != nil {
-			return fmt.Errorf("invalid --action %s", opts.Action)
-		}
-
-		// Only allow actions on a non-existing installation when it won't change anything
 		if action.Modifies || !action.Stateless {
 			return errors.Wrapf(err, "could not find installation %s/%s", opts.Namespace, opts.Name)
 		}
diff --git a/pkg/porter/lifecycle.go b/pkg/porter/lifecycle.go
index 5866ac22..85456925 100644
--- a/pkg/porter/lifecycle.go
+++ b/pkg/porter/lifecycle.go
@@ -28,8 +28,6 @@ type BundleActionOptions struct {
 	sharedOptions
 	BundlePullOptions
 	AllowAccessToDockerHost bool
-
-	bundleRef *cnab.BundleReference
 }
 
 func (o *BundleActionOptions) Validate(args []string, porter *Porter) error {
@@ -63,30 +61,18 @@ func (o *BundleActionOptions) GetOptions() *BundleActionOptions {
 }
 
 func (p *Porter) resolveBundleReference(opts *BundleActionOptions) (cnab.BundleReference, error) {
-	// Some actions need to resolve this early
-	if opts.bundleRef != nil {
-		return *opts.bundleRef, nil
-	}
-
 	var bundleRef cnab.BundleReference
 
-	useReference := func(ref cnab.OCIReference) error {
-		pullOpts := *opts // make a copy just to do the pull
-		pullOpts.Reference = ref.String()
-		cachedBundle, err := p.prepullBundleByReference(&pullOpts)
+	// load the referenced bundle
+	if opts.Reference != "" {
+		cachedBundle, err := p.prepullBundleByReference(opts)
 		if err != nil {
-			return errors.Wrapf(err, "unable to pull bundle %s", opts.Reference)
+			return cnab.BundleReference{}, errors.Wrapf(err, "unable to pull bundle %s", opts.Reference)
 		}
 
 		bundleRef = cachedBundle.BundleReference
-		return nil
-	}
-
-	// load the referenced bundle
-	if opts.Reference != "" {
-		if err := useReference(opts.GetReference()); err != nil {
-			return cnab.BundleReference{}, err
-		}
+		opts.File = cachedBundle.ManifestPath
+		opts.CNABFile = cachedBundle.BundlePath
 	} else if opts.File != "" { // load the local bundle source
 		localBundle, err := p.ensureLocalBundleIsUpToDate(opts.bundleFileOptions)
 		if err != nil {
@@ -100,33 +86,19 @@ func (p *Porter) resolveBundleReference(opts *BundleActionOptions) (cnab.BundleR
 		}
 		bundleRef = cnab.BundleReference{Definition: bun}
 	} else if opts.Name != "" { // Return the bundle associated with the installation
-		i, err := p.Claims.GetInstallation(opts.Namespace, opts.Name)
+		lastRun, err := p.Claims.GetLastRun(opts.Namespace, opts.Name)
 		if err != nil {
-			return cnab.BundleReference{}, errors.Wrapf(err, "installation %s/%s not found", opts.Namespace, opts.Name)
+			return cnab.BundleReference{}, errors.Wrap(err, "could not load the bundle definition from the installation's last run")
 		}
-		if i.Status.BundleReference != "" {
-			ref, err := cnab.ParseOCIReference(i.Status.BundleReference)
-			if err != nil {
-				return cnab.BundleReference{}, errors.Wrapf(err, "installation.Status.BundleReference is invalid")
-			}
-			if err := useReference(ref); err != nil {
-				return cnab.BundleReference{}, err
-			}
-		} else { // The bundle was installed from source
-			lastRun, err := p.Claims.GetLastRun(opts.Namespace, opts.Name)
-			if err != nil {
-				return cnab.BundleReference{}, errors.Wrap(err, "could not load the bundle definition from the installation's last run")
-			}
 
-			bundleRef = cnab.BundleReference{
-				Definition: cnab.ExtendedBundle{lastRun.Bundle},
-				Digest:     digest.Digest(lastRun.BundleDigest)}
+		bundleRef = cnab.BundleReference{
+			Definition: cnab.ExtendedBundle{lastRun.Bundle},
+			Digest:     digest.Digest(lastRun.BundleDigest)}
 
-			if lastRun.BundleReference != "" {
-				bundleRef.Reference, err = cnab.ParseOCIReference(lastRun.BundleReference)
-				if err != nil {
-					return cnab.BundleReference{}, errors.Wrapf(err, "invalid bundle reference, %s, found on the last bundle run record %s", lastRun.BundleReference, lastRun.ID)
-				}
+		if lastRun.BundleReference != "" {
+			bundleRef.Reference, err = cnab.ParseOCIReference(lastRun.BundleReference)
+			if err != nil {
+				return cnab.BundleReference{}, errors.Wrapf(err, "invalid bundle reference, %s, found on the last bundle run record %s", lastRun.BundleReference, lastRun.ID)
 			}
 		}
 	} else { // Nothing was referenced
@@ -137,7 +109,6 @@ func (p *Porter) resolveBundleReference(opts *BundleActionOptions) (cnab.BundleR
 		opts.Name = bundleRef.Definition.Name
 	}
 
-	opts.bundleRef = &bundleRef
 	return bundleRef, nil
 }
 
@@ -192,6 +163,7 @@ func (p *Porter) prepullBundleByReference(opts *BundleActionOptions) (cache.Cach
 		return cache.CachedBundle{}, errors.Wrapf(err, "unable to pull bundle %s", opts.Reference)
 	}
 
+	opts.CNABFile = cachedBundle.BundlePath
 	opts.RelocationMapping = cachedBundle.RelocationFilePath
 
 	if opts.Name == "" {
diff --git a/pkg/porter/list.go b/pkg/porter/list.go
index 87a9772e..3d53ab85 100644
--- a/pkg/porter/list.go
+++ b/pkg/porter/list.go
@@ -58,41 +58,31 @@ func parseLabels(raw []string) map[string]string {
 // DisplayInstallation holds a subset of pertinent values to be listed from installation data
 // originating from its claims, results and outputs records
 type DisplayInstallation struct {
-	Name              string
-	Namespace         string
-	Created           time.Time
-	Modified          time.Time
-	TrackedRepository string
-	TrackedVersion    string
-	TrackedDigest     string
-	StatusLastAction  string
-	StatusText        string
-	StatusReference   string
-	StatusVersion     string
-	StatusDigest      string
-
-	Parameters     DisplayValues
-	Labels         []string
-	ParameterSets  []string
-	CredentialSets []string
+	Name      string
+	Namespace string
+	Created   time.Time
+	Modified  time.Time
+	Bundle    string
+	Version   string
+	Digest    string
+	Action    string
+	Status    string
+
+	Parameters DisplayValues
+	Labels     []string
 }
 
 func NewDisplayInstallation(installation claims.Installation, run *claims.Run) DisplayInstallation {
 	di := DisplayInstallation{
-		Name:              installation.Name,
-		Namespace:         installation.Namespace,
-		TrackedRepository: installation.BundleRepository,
-		TrackedVersion:    installation.BundleVersion,
-		TrackedDigest:     installation.BundleDigest,
-		ParameterSets:     installation.ParameterSets,
-		CredentialSets:    installation.CredentialSets,
-		Created:           installation.Created,
-		Modified:          installation.Modified,
-		StatusReference:   installation.Status.BundleReference,
-		StatusVersion:     installation.Status.BundleVersion,
-		StatusDigest:      installation.Status.BundleDigest,
-		StatusLastAction:  installation.Status.Action,
-		StatusText:        installation.Status.ResultStatus,
+		Name:      installation.Name,
+		Namespace: installation.Namespace,
+		Bundle:    installation.BundleRepository,
+		Version:   installation.BundleVersion,
+		Digest:    installation.BundleDigest,
+		Created:   installation.Created,
+		Modified:  installation.Modified,
+		Action:    installation.Status.Action,
+		Status:    installation.Status.ResultStatus,
 	}
 
 	labels := make([]string, 0, len(installation.Labels))
@@ -187,7 +177,7 @@ func (p *Porter) PrintInstallations(opts ListOptions) error {
 				if !ok {
 					return nil
 				}
-				return []interface{}{cl.Namespace, cl.Name, tp.Format(cl.Created), tp.Format(cl.Modified), cl.StatusLastAction, cl.StatusText}
+				return []interface{}{cl.Namespace, cl.Name, tp.Format(cl.Created), tp.Format(cl.Modified), cl.Action, cl.Status}
 			}
 		return printer.PrintTable(p.Out, displayInstallations, row,
 			"NAMESPACE", "NAME", "CREATED", "MODIFIED", "LAST ACTION", "LAST STATUS")
diff --git a/pkg/porter/parameters.go b/pkg/porter/parameters.go
index 83ffa03c..e15bd178 100644
--- a/pkg/porter/parameters.go
+++ b/pkg/porter/parameters.go
@@ -7,6 +7,7 @@ import (
 	"time"
 
 	"get.porter.sh/porter/pkg/cnab"
+	"get.porter.sh/porter/pkg/context"
 	"get.porter.sh/porter/pkg/editor"
 	"get.porter.sh/porter/pkg/encoding"
 	"get.porter.sh/porter/pkg/generator"
@@ -87,18 +88,18 @@ func (o ParameterOptions) ParseLabels() map[string]string {
 // Validate prepares for an action and validates the options.
 // For example, relative paths are converted to full paths and then checked that
 // they exist and are accessible.
-func (o *ParameterOptions) Validate(args []string, p *Porter) error {
-	err := o.validateParamName(args)
+func (g *ParameterOptions) Validate(args []string, cxt *context.Context) error {
+	err := g.validateParamName(args)
 	if err != nil {
 		return err
 	}
 
-	return o.BundleActionOptions.Validate(args, p)
+	return g.bundleFileOptions.Validate(cxt)
 }
 
-func (o *ParameterOptions) validateParamName(args []string) error {
+func (g *ParameterOptions) validateParamName(args []string) error {
 	if len(args) == 1 {
-		o.Name = args[0]
+		g.Name = args[0]
 	} else if len(args) > 1 {
 		return errors.Errorf("only one positional argument may be specified, the parameter set name, but multiple were received: %s", args)
 	}
diff --git a/pkg/porter/publish.go b/pkg/porter/publish.go
index e5941489..d5ea942d 100644
--- a/pkg/porter/publish.go
+++ b/pkg/porter/publish.go
@@ -248,7 +248,7 @@ func (p *Porter) publishFromArchive(opts PublishOptions) error {
 	}
 
 	bundleRef := cnab.BundleReference{
-		Reference:  opts.GetReference(),
+		Reference:  opts.ref,
 		Definition: bun,
 	}
 	bundleRef, err = p.Registry.PushBundle(bundleRef, opts.InsecureRegistry)
diff --git a/pkg/porter/pull.go b/pkg/porter/pull.go
index a93bbfd2..0f9a7478 100644
--- a/pkg/porter/pull.go
+++ b/pkg/porter/pull.go
@@ -8,7 +8,7 @@ import (
 
 type BundlePullOptions struct {
 	Reference        string
-	_ref             *cnab.OCIReference
+	ref              cnab.OCIReference
 	InsecureRegistry bool
 	Force            bool
 }
@@ -17,23 +17,12 @@ func (b *BundlePullOptions) Validate() error {
 	return b.validateReference()
 }
 
-func (b *BundlePullOptions) GetReference() cnab.OCIReference {
-	if b._ref == nil {
-		ref, err := cnab.ParseOCIReference(b.Reference)
-		if err != nil {
-			panic(err)
-		}
-		b._ref = &ref
-	}
-	return *b._ref
-}
-
 func (b *BundlePullOptions) validateReference() error {
-	ref, err := cnab.ParseOCIReference(b.Reference)
+	var err error
+	b.ref, err = cnab.ParseOCIReference(b.Reference)
 	if err != nil {
 		return errors.Wrap(err, "invalid value for --reference, specified value should be of the form REGISTRY/bundle:tag")
 	}
-	b._ref = &ref
 	return nil
 }
 
diff --git a/pkg/porter/resolver.go b/pkg/porter/resolver.go
index afbc4c80..388be653 100644
--- a/pkg/porter/resolver.go
+++ b/pkg/porter/resolver.go
@@ -15,7 +15,7 @@ type BundleResolver struct {
 // Returns the location of the bundle or an error
 func (r *BundleResolver) Resolve(opts BundlePullOptions) (cache.CachedBundle, error) {
 	if !opts.Force {
-		cachedBundle, ok, err := r.Cache.FindBundle(opts.GetReference())
+		cachedBundle, ok, err := r.Cache.FindBundle(opts.ref)
 		if err != nil {
 			return cache.CachedBundle{}, errors.Wrapf(err, "unable to load bundle %s from cache", opts.Reference)
 		}
@@ -25,7 +25,7 @@ func (r *BundleResolver) Resolve(opts BundlePullOptions) (cache.CachedBundle, er
 		}
 	}
 
-	bundleRef, err := r.Registry.PullBundle(opts.GetReference(), opts.InsecureRegistry)
+	bundleRef, err := r.Registry.PullBundle(opts.ref, opts.InsecureRegistry)
 	if err != nil {
 		return cache.CachedBundle{}, err
 	}
diff --git a/pkg/porter/show.go b/pkg/porter/show.go
index 3a9fa510..de2b430f 100644
--- a/pkg/porter/show.go
+++ b/pkg/porter/show.go
@@ -87,18 +87,11 @@ func (p *Porter) ShowInstallation(opts ShowOptions) error {
 		fmt.Fprintf(p.Out, "Namespace: %s\n", displayInstallation.Namespace)
 		fmt.Fprintf(p.Out, "Created: %s\n", tp.Format(displayInstallation.Created))
 		fmt.Fprintf(p.Out, "Modified: %s\n", tp.Format(displayInstallation.Modified))
-
-		if displayInstallation.TrackedRepository != "" {
-			fmt.Fprintln(p.Out)
-			fmt.Fprintln(p.Out, "Bundle:")
-			fmt.Fprintf(p.Out, "  Repository: %s\n", displayInstallation.TrackedRepository)
-			if displayInstallation.TrackedVersion != "" {
-				fmt.Fprintf(p.Out, "  Version: %s\n", displayInstallation.TrackedVersion)
-			}
-			if displayInstallation.TrackedDigest != "" {
-				fmt.Fprintf(p.Out, "  Digest: %s\n", displayInstallation.TrackedDigest)
-			}
+		if displayInstallation.Bundle != "" {
+			fmt.Fprintf(p.Out, "Bundle: %s\n", displayInstallation.Bundle)
 		}
+		fmt.Fprintf(p.Out, "Version: %s\n", displayInstallation.Version)
+		fmt.Fprintf(p.Out, "Digest: %s\n", displayInstallation.Digest)
 
 		// Print labels, if any
 		if len(displayInstallation.Labels) > 0 {
@@ -121,33 +114,12 @@ func (p *Porter) ShowInstallation(opts ShowOptions) error {
 			}
 		}
 
-		// Print parameter sets, if any
-		if len(displayInstallation.ParameterSets) > 0 {
-			fmt.Fprintln(p.Out)
-			fmt.Fprintln(p.Out, "Parameter Sets:")
-			for _, ps := range displayInstallation.ParameterSets {
-				fmt.Fprintf(p.Out, "  - %s\n", ps)
-			}
-		}
-
-		// Print credential sets, if any
-		if len(displayInstallation.CredentialSets) > 0 {
-			fmt.Fprintln(p.Out)
-			fmt.Fprintln(p.Out, "Credential Sets:")
-			for _, cs := range displayInstallation.CredentialSets {
-				fmt.Fprintf(p.Out, "  - %s\n", cs)
-			}
-		}
-
 		// Print the status (it may not be present if it's newly created using apply)
 		if installation.Status != (claims.InstallationStatus{}) {
 			fmt.Fprintln(p.Out)
 			fmt.Fprintln(p.Out, "Status:")
-			fmt.Fprintf(p.Out, "  Reference: %s\n", displayInstallation.StatusReference)
-			fmt.Fprintf(p.Out, "  Version: %s\n", displayInstallation.StatusVersion)
-			fmt.Fprintf(p.Out, "  Last Action: %s\n", displayInstallation.StatusLastAction)
-			fmt.Fprintf(p.Out, "  Status: %s\n", displayInstallation.StatusText)
-			fmt.Fprintf(p.Out, "  Digest: %s\n", displayInstallation.StatusDigest)
+			fmt.Fprintf(p.Out, "  Last Action: %s\n", displayInstallation.Action)
+			fmt.Fprintf(p.Out, "  Status: %s\n", displayInstallation.Status)
 		}
 
 		return nil
diff --git a/pkg/porter/uninstall.go b/pkg/porter/uninstall.go
index 5d172e2a..22be775f 100644
--- a/pkg/porter/uninstall.go
+++ b/pkg/porter/uninstall.go
@@ -67,7 +67,6 @@ func (opts *UninstallDeleteOptions) handleUninstallErrs(out io.Writer, err error
 // UninstallBundle accepts a set of pre-validated UninstallOptions and uses
 // them to uninstall a bundle.
 func (p *Porter) UninstallBundle(opts UninstallOptions) error {
-	// Figure out which bundle/installation we are working with
 	_, err := p.resolveBundleReference(opts.BundleActionOptions)
 	if err != nil {
 		return err
diff --git a/pkg/porter/upgrade.go b/pkg/porter/upgrade.go
index b97431b8..2034e9d5 100644
--- a/pkg/porter/upgrade.go
+++ b/pkg/porter/upgrade.go
@@ -1,10 +1,7 @@
 package porter
 
 import (
-	"time"
-
 	"get.porter.sh/porter/pkg/cnab"
-	"github.com/Masterminds/semver/v3"
 	"github.com/pkg/errors"
 )
 
@@ -14,29 +11,10 @@ var _ BundleAction = NewUpgradeOptions()
 // Porter handles defaulting any missing values.
 type UpgradeOptions struct {
 	*BundleActionOptions
-
-	// Version of the bundle to upgrade to
-	Version string
 }
 
 func NewUpgradeOptions() UpgradeOptions {
-	return UpgradeOptions{BundleActionOptions: &BundleActionOptions{}}
-}
-
-func (o UpgradeOptions) Validate(args []string, p *Porter) error {
-	if o.Version != "" && o.Reference != "" {
-		return errors.New("either --version or --reference may be set, but not both")
-	}
-
-	if o.Version != "" {
-		v, err := semver.NewVersion(o.Version)
-		if err != nil {
-			return errors.New("invalid bundle version --version. Must be a semantic version, for example 1.2.3")
-		}
-		o.Version = v.String()
-	}
-
-	return o.BundleActionOptions.Validate(args, p)
+	return UpgradeOptions{&BundleActionOptions{}}
 }
 
 func (o UpgradeOptions) GetAction() string {
@@ -50,43 +28,9 @@ func (o UpgradeOptions) GetActionVerb() string {
 // UpgradeBundle accepts a set of pre-validated UpgradeOptions and uses
 // them to upgrade a bundle.
 func (p *Porter) UpgradeBundle(opts UpgradeOptions) error {
-	// Figure out which bundle/installation we are working with
-	_, err := p.resolveBundleReference(opts.BundleActionOptions)
-	if err != nil {
-		return err
-	}
-
-	// Sync any changes specified by the user to the installation before running upgrade
-	i, err := p.Claims.GetInstallation(opts.Namespace, opts.Name)
+	installation, err := p.Claims.GetInstallation(opts.Namespace, opts.Name)
 	if err != nil {
 		return errors.Wrapf(err, "could not find installation %s/%s", opts.Namespace, opts.Name)
 	}
-
-	if opts.Reference != "" {
-		i.TrackBundle(opts.GetReference())
-	} else if opts.Version != "" {
-		i.BundleVersion = opts.Version
-		i.BundleDigest = ""
-		i.BundleTag = ""
-	}
-
-	err = p.applyActionOptionsToInstallation(i, opts.BundleActionOptions)
-	i.Modified = time.Now()
-	err = i.Validate()
-	if err != nil {
-		return err
-	}
-	err = p.Claims.UpdateInstallation(i)
-	if err != nil {
-		return err
-	}
-
-	// Re-resolve the bundle after we have figured out the version we are upgrading to
-	opts.bundleRef = nil
-	_, err = p.resolveBundleReference(opts.BundleActionOptions)
-	if err != nil {
-		return err
-	}
-
-	return p.ExecuteAction(i, opts)
+	return p.ExecuteAction(installation, opts)
 }


Test patch:
diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go
index 31ac57b8..b9856a55 100644
--- a/pkg/cache/cache_test.go
+++ b/pkg/cache/cache_test.go
@@ -8,10 +8,8 @@ import (
 
 	"get.porter.sh/porter/pkg/cnab"
 	"get.porter.sh/porter/pkg/config"
-	"get.porter.sh/porter/pkg/encoding"
 	"github.com/cnabio/cnab-go/bundle"
 	"github.com/cnabio/cnab-to-oci/relocation"
-	"github.com/opencontainers/go-digest"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -69,8 +67,8 @@ func TestFindBundleBundleCached(t *testing.T) {
 	c := New(cfg.Config)
 
 	cb, ok, err := c.FindBundle(kahn1dot01)
-	require.NoError(t, err, "the cache dir should exist, no error should have happened")
-	require.True(t, ok, "the bundle should exist")
+	assert.NoError(t, err, "the cache dir should exist, no error should have happened")
+	assert.True(t, ok, "the bundle should exist")
 	assert.Equal(t, expectedCacheFile, cb.BundlePath)
 }
 
@@ -80,7 +78,7 @@ func TestFindBundleBundleNotCached(t *testing.T) {
 	cfg := config.NewTestConfig(t)
 	c := New(cfg.Config)
 	cb, ok, err := c.FindBundle(kahnlatest)
-	require.NoError(t, err, "the cache dir should exist, no error should have happened")
+	assert.NoError(t, err, "the cache dir should exist, no error should have happened")
 	assert.False(t, ok, "the bundle should not exist")
 	assert.Empty(t, cb.BundlePath, "should not have a path")
 }
@@ -121,13 +119,7 @@ func TestCacheWriteCacheDirExists(t *testing.T) {
 
 	c := New(cfg.Config)
 	var reloMap relocation.ImageRelocationMap
-	bundleRef := cnab.BundleReference{
-		Reference:     kahn1dot01,
-		Definition:    bun,
-		RelocationMap: reloMap,
-		Digest:        digest.Digest("sha256:2249472f86d0cea9ac8809331931e9100e1d0464afff3d2869bbb8dedfe2d396"),
-	}
-	cb, err := c.StoreBundle(bundleRef)
+	cb, err := c.StoreBundle(cnab.BundleReference{Reference: kahn1dot01, Definition: bun, RelocationMap: reloMap})
 
 	expectedCacheDirectory := filepath.Join(cacheDir, kahn1dot0Hash)
 	expectedCacheCNABDirectory := filepath.Join(expectedCacheDirectory, "cnab")
@@ -135,11 +127,6 @@ func TestCacheWriteCacheDirExists(t *testing.T) {
 
 	assert.Equal(t, expectedCacheFile, cb.BundlePath)
 	assert.NoError(t, err, "storing bundle should have succeeded")
-
-	var meta Metadata
-	expectedMetaFile := filepath.Join(expectedCacheDirectory, "metadata.json")
-	require.NoError(t, encoding.UnmarshalFile(cfg.FileSystem, expectedMetaFile, &meta))
-	assert.Equal(t, Metadata{Reference: bundleRef.Reference, Digest: bundleRef.Digest}, meta, "incorrect metadata.json persisted")
 }
 
 func TestStoreRelocationMapping(t *testing.T) {
diff --git a/pkg/cache/testdata/887e7e65e39277f8744bd00278760b06/metadata.json b/pkg/cache/testdata/887e7e65e39277f8744bd00278760b06/metadata.json
deleted file mode 100644
index c044d46d..00000000
--- a/pkg/cache/testdata/887e7e65e39277f8744bd00278760b06/metadata.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "reference": "deislabs/kubekahn:1.0",
-  "digest": "sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9"
-}
\ No newline at end of file
diff --git a/pkg/claims/installation_test.go b/pkg/claims/installation_test.go
index fb0ff06d..baae298e 100644
--- a/pkg/claims/installation_test.go
+++ b/pkg/claims/installation_test.go
@@ -4,7 +4,6 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
 )
 
 func TestInstallation_String(t *testing.T) {
@@ -14,44 +13,3 @@ func TestInstallation_String(t *testing.T) {
 	i.Namespace = "dev"
 	assert.Equal(t, "dev/mybun", i.String())
 }
-
-func TestInstallation_GetBundleReference(t *testing.T) {
-	testcases := []struct {
-		name    string
-		repo    string
-		digest  string
-		version string
-		tag string
-		wantRef string
-		wantErr string
-	}{
-		{name: "repo missing", wantRef: ""},
-		{name: "incomplete reference", repo: "getporter/porter-hello", wantErr: "Invalid installation"},
-		{name: "version specified", repo: "getporter/porter-hello", version: "v0.1.1", wantRef: "getporter/porter-hello:v0.1.1"},
-		{name: "digest specified", repo: "getporter/porter-hello", digest: "sha256:a881bbc015bade9f11d95a4244888d8e7fa8800f843b43c74cc07c7b7276b062", wantRef: "getporter/porter-hello@sha256:a881bbc015bade9f11d95a4244888d8e7fa8800f843b43c74cc07c7b7276b062"},
-		{name: "tag specified", repo: "getporter/porter-hello", tag: "latest", wantRef: "getporter/porter-hello:latest"},
-	}
-
-	for _, tc := range testcases {
-		t.Run(tc.name, func(t *testing.T) {
-			i := Installation{
-				BundleRepository: tc.repo,
-				BundleDigest:     tc.digest,
-				BundleVersion:    tc.version,
-				BundleTag: tc.tag,
-			}
-
-			ref, ok, err := i.GetBundleReference()
-			if tc.wantErr != "" {
-				require.Error(t, err)
-				assert.Contains(t, err.Error(), tc.wantErr)
-			} else if tc.wantRef != "" {
-				require.NoError(t, err)
-				assert.Equal(t, tc.wantRef, ref.String())
-			} else {
-				require.NoError(t, err)
-				require.False(t, ok)
-			}
-		})
-	}
-}
diff --git a/pkg/porter/credentials_test.go b/pkg/porter/credentials_test.go
index 8ea5e389..cf991c14 100644
--- a/pkg/porter/credentials_test.go
+++ b/pkg/porter/credentials_test.go
@@ -25,7 +25,7 @@ func TestGenerateNoName(t *testing.T) {
 		Silent: true,
 	}
 	opts.CNABFile = "/bundle.json"
-	err := opts.Validate(nil, p.Porter)
+	err := opts.Validate(nil, p.Context)
 	require.NoError(t, err, "Validate failed")
 
 	err = p.GenerateCredentials(opts)
@@ -51,7 +51,7 @@ func TestGenerateNameProvided(t *testing.T) {
 	opts.Name = "kool-kred"
 	opts.Labels = []string{"env=dev"}
 	opts.CNABFile = "/bundle.json"
-	err := opts.Validate(nil, p.Porter)
+	err := opts.Validate(nil, p.Context)
 	require.NoError(t, err, "Validate failed")
 
 	err = p.GenerateCredentials(opts)
@@ -72,7 +72,7 @@ func TestGenerateBadNameProvided(t *testing.T) {
 	}
 	opts.Name = "this.isabadname"
 	opts.CNABFile = "/bundle.json"
-	err := opts.Validate(nil, p.Porter)
+	err := opts.Validate(nil, p.Context)
 	require.NoError(t, err, "Validate failed")
 
 	err = p.GenerateCredentials(opts)
diff --git a/pkg/porter/install_test.go b/pkg/porter/install_test.go
index cd4f9bfd..98d41d38 100644
--- a/pkg/porter/install_test.go
+++ b/pkg/porter/install_test.go
@@ -4,6 +4,8 @@ import (
 	"testing"
 
 	"get.porter.sh/porter/pkg/context"
+	"get.porter.sh/porter/pkg/credentials"
+	"get.porter.sh/porter/pkg/secrets"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -68,3 +70,34 @@ func TestInstallOptions_validateDriver(t *testing.T) {
 		})
 	}
 }
+
+func TestPorter_InstallBundle_WithDepsFromTag(t *testing.T) {
+	p := NewTestPorter(t)
+	defer p.Teardown()
+
+	cacheDir, _ := p.Cache.GetCacheDir()
+	p.TestConfig.TestContext.AddTestDirectory("testdata/cache", cacheDir)
+
+	// Make some fake credentials to give to the install operation, they won't be used because it's a dummy driver
+	cs := credentials.NewCredentialSet("", "wordpress", secrets.Strategy{
+		Name: "kubeconfig",
+		Source: secrets.Source{
+			Key:   secrets.SourceSecret,
+			Value: "kubeconfig",
+		},
+	})
+	p.TestCredentials.TestSecrets.AddSecret("kubeconfig", "abc123")
+	err := p.Credentials.InsertCredentialSet(cs)
+	require.NoError(t, err, "Credentials.Save failed")
+
+	opts := NewInstallOptions()
+	opts.Driver = DebugDriver
+	opts.Reference = "localhost:5000/wordpress:v0.1.3"
+	opts.CredentialIdentifiers = []string{"wordpress"}
+	opts.Params = []string{"wordpress-password=mypassword"}
+	err = opts.Validate(nil, p.Porter)
+	require.NoError(t, err, "Validate install options failed")
+
+	err = p.InstallBundle(opts)
+	require.NoError(t, err, "InstallBundle failed")
+}
diff --git a/pkg/porter/lifecycle_integration_test.go b/pkg/porter/lifecycle_integration_test.go
index a34677d9..50b4b2d8 100644
--- a/pkg/porter/lifecycle_integration_test.go
+++ b/pkg/porter/lifecycle_integration_test.go
@@ -13,6 +13,37 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
+func TestInstallFromTag_ManageFromClaim(t *testing.T) {
+	p := NewTestPorter(t)
+	defer p.Teardown()
+
+	cacheDir, _ := p.Cache.GetCacheDir()
+	p.TestConfig.TestContext.AddTestDirectory("testdata/cache", cacheDir)
+
+	installOpts := NewInstallOptions()
+	installOpts.Name = "hello"
+	installOpts.Reference = "getporter/porter-hello:v0.1.1"
+	err := installOpts.Validate(nil, p.Porter)
+	require.NoError(t, err, "InstallOptions.Validate failed")
+
+	err = p.InstallBundle(installOpts)
+	require.NoError(t, err, "InstallBundle failed")
+
+	upgradeOpts := NewUpgradeOptions()
+	upgradeOpts.Name = installOpts.Name
+	err = upgradeOpts.Validate(nil, p.Porter)
+
+	err = p.UpgradeBundle(upgradeOpts)
+	require.NoError(t, err, "UpgradeBundle failed")
+
+	uninstallOpts := NewUninstallOptions()
+	uninstallOpts.Name = installOpts.Name
+	err = uninstallOpts.Validate(nil, p.Porter)
+
+	err = p.UninstallBundle(uninstallOpts)
+	require.NoError(t, err, "UninstallBundle failed")
+}
+
 func TestResolveBundleReference(t *testing.T) {
 	t.Parallel()
 	t.Run("current bundle source", func(t *testing.T) {
diff --git a/pkg/porter/lifecycle_test.go b/pkg/porter/lifecycle_test.go
index ba8aff5b..7684be88 100644
--- a/pkg/porter/lifecycle_test.go
+++ b/pkg/porter/lifecycle_test.go
@@ -1,6 +1,9 @@
 package porter
 
 import (
+	"errors"
+	"path"
+	"path/filepath"
 	"testing"
 
 	"get.porter.sh/porter/pkg/claims"
@@ -18,6 +21,75 @@ var (
 	kahnlatest     = cnab.MustParseOCIReference("deislabs/kubekahn:latest")
 )
 
+func TestBundlePullUpdateOpts_bundleCached(t *testing.T) {
+	p := NewTestPorter(t)
+	defer p.Teardown()
+
+	home, err := p.TestConfig.GetHomeDir()
+	t.Logf("home dir is: %s", home)
+	cacheDir, err := p.Cache.GetCacheDir()
+	require.NoError(t, err, "should have had a porter cache dir")
+	t.Logf("cache dir is: %s", cacheDir)
+	p.TestConfig.TestContext.AddTestDirectory("testdata/cache", cacheDir)
+	fullPath := filepath.Join(cacheDir, path.Join(kahn1dot0Hash, "/cnab/bundle.json"))
+	fileExists, err := p.FileSystem.Exists(fullPath)
+	require.True(t, fileExists, "this test requires that the file exist")
+	_, ok, err := p.Cache.FindBundle(kahn1dot01)
+	assert.True(t, ok, "should have found the bundle...")
+	b := &BundleActionOptions{
+		BundlePullOptions: BundlePullOptions{
+			Reference: "deislabs/kubekahn:1.0",
+		},
+	}
+	err = b.Validate(nil, p.Porter)
+	require.NoError(t, err)
+	_, err = p.prepullBundleByReference(b)
+	assert.NoError(t, err, "pulling bundle should not have resulted in an error")
+	assert.Equal(t, "mysql", b.Name, "name should have matched testdata bundle")
+	assert.Equal(t, fullPath, b.CNABFile, "the prepare method should have set the file to the fullpath")
+}
+
+func TestBundlePullUpdateOpts_pullError(t *testing.T) {
+	p := NewTestPorter(t)
+	defer p.Teardown()
+
+	p.TestRegistry.MockPullBundle = func(ref cnab.OCIReference, insecureRegistry bool) (bundleReference cnab.BundleReference, err error) {
+		return cnab.BundleReference{}, errors.New("unable to pull bundle deislabs/kubekahn:latest")
+	}
+
+	b := &BundleActionOptions{
+		BundlePullOptions: BundlePullOptions{
+			Reference: "deislabs/kubekahn:latest",
+		},
+	}
+	err := b.Validate(nil, p.Porter)
+	require.NoError(t, err)
+	_, err = p.prepullBundleByReference(b)
+	assert.Error(t, err, "pulling bundle should have resulted in an error")
+	assert.Contains(t, err.Error(), "unable to pull bundle deislabs/kubekahn:latest")
+
+}
+
+func TestBundlePullUpdateOpts_cacheLies(t *testing.T) {
+	p := NewTestPorter(t)
+	defer p.Teardown()
+
+	// mess up the cache
+	p.FileSystem.WriteFile(path.Join("/root/.porter/cache", kahn1dot0Hash, "/cnab/bundle.json"), []byte(""), 0644)
+
+	b := &BundleActionOptions{
+		BundlePullOptions: BundlePullOptions{
+			Reference: "deislabs/kubekahn:1.0",
+		},
+	}
+	err := b.Validate(nil, p.Porter)
+	require.NoError(t, err)
+
+	_, err = p.prepullBundleByReference(b)
+	assert.Error(t, err, "pulling bundle should have resulted in an error")
+	assert.Contains(t, err.Error(), "unable to parse cached bundle file")
+}
+
 func TestInstallFromTagIgnoresCurrentBundle(t *testing.T) {
 	p := NewTestPorter(t)
 	defer p.Teardown()
diff --git a/pkg/porter/list_test.go b/pkg/porter/list_test.go
index 39e7af15..1129164e 100644
--- a/pkg/porter/list_test.go
+++ b/pkg/porter/list_test.go
@@ -27,8 +27,8 @@ func TestNewDisplayInstallation(t *testing.T) {
 		require.Equal(t, di.Name, i.Name, "invalid installation name")
 		require.Equal(t, di.Created, i.Created, "invalid created time")
 		require.Equal(t, di.Modified, i.Modified, "invalid modified time")
-		require.Equal(t, cnab.ActionUpgrade, di.StatusLastAction, "invalid last action")
-		require.Equal(t, cnab.StatusRunning, di.StatusText, "invalid last status")
+		require.Equal(t, cnab.ActionUpgrade, di.Action, "invalid last action")
+		require.Equal(t, cnab.StatusRunning, di.Status, "invalid last status")
 	})
 
 	t.Run("installation has not been installed", func(t *testing.T) {
@@ -45,8 +45,8 @@ func TestNewDisplayInstallation(t *testing.T) {
 		require.Equal(t, di.Name, i.Name, "invalid installation name")
 		require.Equal(t, i.Created, di.Created, "invalid created time")
 		require.Equal(t, i.Modified, di.Modified, "invalid modified time")
-		require.Empty(t, di.StatusLastAction, "invalid last action")
-		require.Empty(t, di.StatusText, "invalid last status")
+		require.Empty(t, di.Action, "invalid last action")
+		require.Empty(t, di.Status, "invalid last status")
 	})
 }
 
diff --git a/pkg/porter/parameters_test.go b/pkg/porter/parameters_test.go
index 9af3adf4..43904d7d 100644
--- a/pkg/porter/parameters_test.go
+++ b/pkg/porter/parameters_test.go
@@ -36,7 +36,7 @@ func TestGenerateParameterSet(t *testing.T) {
 	opts.Name = "kool-params"
 	opts.Labels = []string{"env=dev"}
 	opts.CNABFile = "/bundle.json"
-	err := opts.Validate(nil, p.Porter)
+	err := opts.Validate(nil, p.Context)
 	require.NoError(t, err, "Validate failed")
 
 	err = p.GenerateParameters(opts)
diff --git a/pkg/porter/pull_test.go b/pkg/porter/pull_test.go
index 7e79d091..81207132 100644
--- a/pkg/porter/pull_test.go
+++ b/pkg/porter/pull_test.go
@@ -14,7 +14,7 @@ func TestBundlePullOptions_validtag(t *testing.T) {
 
 	err := opts.Validate()
 	require.NoError(t, err, "valid tag should not produce an error")
-	assert.Equal(t, opts.Reference, opts.GetReference().String())
+	assert.Equal(t, opts.Reference, opts.ref.String())
 }
 
 func TestBundlePullOptions_invalidtag(t *testing.T) {
diff --git a/pkg/porter/resolver_test.go b/pkg/porter/resolver_test.go
index 3154e56c..a09fa879 100644
--- a/pkg/porter/resolver_test.go
+++ b/pkg/porter/resolver_test.go
@@ -55,16 +55,16 @@ func TestBundleResolver_Resolve_CacheHit(t *testing.T) {
 	cacheSearched := false
 	testCache.FindBundleMock = func(ref cnab.OCIReference) (cache.CachedBundle, bool, error) {
 		cacheSearched = true
-		return cache.CachedBundle{BundleReference: cnab.BundleReference{Reference: ref}}, true, nil
+		return cache.CachedBundle{}, true, nil
 	}
 
 	pulled := false
 	testReg.MockPullBundle = func(ref cnab.OCIReference, insecureRegistry bool) (cnab.BundleReference, error) {
 		pulled = true
-		return cnab.BundleReference{Reference: ref}, nil
+		return cnab.BundleReference{}, nil
 	}
 
-	opts := BundlePullOptions{Reference: "getporter/porter-hello:v0.1.1"}
+	opts := BundlePullOptions{}
 	resolver.Resolve(opts)
 
 	assert.True(t, cacheSearched, "The cache should be searched when force is not specified")
@@ -83,7 +83,7 @@ func TestBundleResolver_Resolve_CacheMiss(t *testing.T) {
 	cacheSearched := false
 	testCache.FindBundleMock = func(ref cnab.OCIReference) (cache.CachedBundle, bool, error) {
 		cacheSearched = true
-		return cache.CachedBundle{}, false, nil
+		return cache.CachedBundle{BundleReference: cnab.BundleReference{Reference: ref}}, false, nil
 	}
 
 	pulled := false
@@ -92,7 +92,7 @@ func TestBundleResolver_Resolve_CacheMiss(t *testing.T) {
 		return cnab.BundleReference{Reference: ref}, nil
 	}
 
-	opts := BundlePullOptions{Reference: "getporter/porter-hello:v0.1.1"}
+	opts := BundlePullOptions{}
 	resolver.Resolve(opts)
 
 	assert.True(t, cacheSearched, "The cache should be searched when force is not specified")
diff --git a/pkg/porter/show_test.go b/pkg/porter/show_test.go
index 2fb74cd6..57c2d959 100644
--- a/pkg/porter/show_test.go
+++ b/pkg/porter/show_test.go
@@ -14,17 +14,16 @@ import (
 func TestPorter_ShowBundle(t *testing.T) {
 	t.Parallel()
 
-	ref := "getporter/wordpress:v0.1.0"
 	testcases := []struct {
 		name       string
-		ref        string
+		repo       string
 		format     printer.Format
 		outputFile string
 	}{
-		{name: "plain", ref: ref, format: printer.FormatPlaintext, outputFile: "testdata/show/expected-output.txt"},
-		{name: "no reference, plain", format: printer.FormatPlaintext, outputFile: "testdata/show/no-reference-expected-output.txt"},
-		{name: "json", ref: ref, format: printer.FormatJson, outputFile: "testdata/show/expected-output.json"},
-		{name: "yaml", ref: ref, format: printer.FormatYaml, outputFile: "testdata/show/expected-output.yaml"},
+		{name: "plain", repo: "getporter/wordpress", format: printer.FormatPlaintext, outputFile: "testdata/show/expected-output.txt"},
+		{name: "no reference, plain", repo: "", format: printer.FormatPlaintext, outputFile: "testdata/show/no-reference-expected-output.txt"},
+		{name: "json", repo: "getporter/wordpress", format: printer.FormatJson, outputFile: "testdata/show/expected-output.json"},
+		{name: "yaml", repo: "getporter/wordpress", format: printer.FormatYaml, outputFile: "testdata/show/expected-output.yaml"},
 	}
 
 	for _, tc := range testcases {
@@ -78,22 +77,25 @@ func TestPorter_ShowBundle(t *testing.T) {
 				},
 			}
 			i := p.TestClaims.CreateInstallation(claims.NewInstallation("dev", "mywordpress"), p.TestClaims.SetMutableInstallationValues, func(i *claims.Installation) {
-				if tc.ref != "" {
-					i.TrackBundle(cnab.MustParseOCIReference(tc.ref))
-				}
+				i.BundleVersion = "0.1.0"
+				i.BundleRepository = tc.repo
+				i.BundleDigest = "sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9"
 				i.Labels = map[string]string{
 					"io.cnab/app":        "wordpress",
 					"io.cnab/appVersion": "v1.2.3",
 				}
 				i.Parameters = map[string]interface{}{"token": "top-secret", "logLevel": 3}
 			})
-			run := p.TestClaims.CreateRun(i.NewRun(cnab.ActionUpgrade), p.TestClaims.SetMutableRunValues, func(r *claims.Run) {
+			r := p.TestClaims.CreateRun(i.NewRun(cnab.ActionUpgrade), p.TestClaims.SetMutableRunValues, func(r *claims.Run) {
 				r.Bundle = b
-				r.BundleReference = tc.ref
-				r.BundleDigest = "sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9"
+				if tc.repo != "" {
+					r.BundleReference = tc.repo + ":0.1.0"
+				}
+
 			})
-			result := p.TestClaims.CreateResult(run.NewResult(cnab.StatusSucceeded), p.TestClaims.SetMutableResultValues)
-			i.ApplyResult(run, result)
+			i.Status.RunID = r.ID
+			i.Status.Action = r.Action
+			i.Status.ResultStatus = cnab.StatusSucceeded
 			i.Status.InstallationCompleted = true
 			require.NoError(t, p.TestClaims.UpdateInstallation(i))
 
diff --git a/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/cnab/bundle.json b/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/cnab/bundle.json
new file mode 120000
index 00000000..7226f9aa
--- /dev/null
+++ b/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/cnab/bundle.json
@@ -0,0 +1,1 @@
+../../../../../../build/testdata/bundles/wordpress/.cnab/bundle.json
\ No newline at end of file
diff --git a/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/porter.yaml b/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/porter.yaml
new file mode 120000
index 00000000..9fcef72e
--- /dev/null
+++ b/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/porter.yaml
@@ -0,0 +1,1 @@
+../../../../../build/testdata/bundles/wordpress/porter.yaml
\ No newline at end of file
diff --git a/pkg/porter/testdata/cache/887e7e65e39277f8744bd00278760b06/cnab/bundle.json b/pkg/porter/testdata/cache/887e7e65e39277f8744bd00278760b06/cnab/bundle.json
new file mode 100644
index 00000000..2b92c625
--- /dev/null
+++ b/pkg/porter/testdata/cache/887e7e65e39277f8744bd00278760b06/cnab/bundle.json
@@ -0,0 +1,56 @@
+{
+    "credentials": {
+        "kubeconfig": {
+            "path": "/root/.kube/config"
+        }
+    },
+    "description": "",
+    "images": null,
+    "invocationImages": [{
+        "contentDigest": "sha256:f858bc025ad34099fe67ebe6152e03b4c91b34cc7a77d1aa10aaf1dc1389c2c2",
+        "image": "jeremyrickard/porter-mysql@sha256:f858bc025ad34099fe67ebe6152e03b4c91b34cc7a77d1aa10aaf1dc1389c2c2",
+        "imageType": "docker"
+    }],
+    "name": "mysql",
+    "parameters": {
+        "database-name": {
+            "defaultValue": "mydb",
+            "destination": {
+                "env": "DATABASE_NAME"
+            },
+            "type": "string"
+        },
+        "mysql-name": {
+            "defaultValue": "porter-ci-mysql",
+            "destination": {
+                "env": "MYSQL-NAME"
+            },
+            "type": "string"
+        },
+        "mysql-user": {
+            "destination": {
+                "env": "MYSQL_USER"
+            },
+            "required": true,
+            "type": "string"
+        },
+        "namespace": {
+            "defaultValue": "",
+            "destination": {
+                "env": "NAMESPACE"
+            },
+            "type": "string"
+        },
+        "porter-debug": {
+            "defaultValue": false,
+            "destination": {
+                "env": "PORTER_DEBUG"
+            },
+            "metadata": {
+                "description": "Print debug information from Porter when executing the bundle"
+            },
+            "type": "bool"
+        }
+    },
+    "version": "0.1.0"
+}
\ No newline at end of file
diff --git a/pkg/porter/testdata/cache/README.md b/pkg/porter/testdata/cache/README.md
new file mode 100644
index 00000000..7fa9483e
--- /dev/null
+++ b/pkg/porter/testdata/cache/README.md
@@ -0,0 +1,8 @@
+# Cache Cheat Sheet
+
+| Tag | Cache Key |
+| --- | --------- |
+| `getporter/porter-hello:v0.1.1` | `dfae9ef8480ec49ba194a97c8743b0e9` |
+| `deislabs/kubekahn:1.0` | `887e7e65e39277f8744bd00278760b06` |
+| `localhost:5000/mysql:v0.1.3` | `e05a608d47d6d138009ac09093e267b0` |
+| `localhost:5000/wordpress:v0.1.3` | `1e6469dbfdf8c060d9ac32a3991e8e15` |
diff --git a/pkg/porter/testdata/cache/dfae9ef8480ec49ba194a97c8743b0e9/cnab/bundle.json b/pkg/porter/testdata/cache/dfae9ef8480ec49ba194a97c8743b0e9/cnab/bundle.json
new file mode 120000
index 00000000..58b3f24c
--- /dev/null
+++ b/pkg/porter/testdata/cache/dfae9ef8480ec49ba194a97c8743b0e9/cnab/bundle.json
@@ -0,0 +1,1 @@
+../../../../../../examples/hello/.cnab/bundle.json
\ No newline at end of file
diff --git a/pkg/porter/testdata/cache/dfae9ef8480ec49ba194a97c8743b0e9/porter.yaml b/pkg/porter/testdata/cache/dfae9ef8480ec49ba194a97c8743b0e9/porter.yaml
new file mode 120000
index 00000000..b666f042
--- /dev/null
+++ b/pkg/porter/testdata/cache/dfae9ef8480ec49ba194a97c8743b0e9/porter.yaml
@@ -0,0 +1,1 @@
+../../../../../examples/hello/porter.yaml
\ No newline at end of file
diff --git a/pkg/porter/testdata/cache/e05a608d47d6d138009ac09093e267b0/cnab/bundle.json b/pkg/porter/testdata/cache/e05a608d47d6d138009ac09093e267b0/cnab/bundle.json
new file mode 120000
index 00000000..8ac600bb
--- /dev/null
+++ b/pkg/porter/testdata/cache/e05a608d47d6d138009ac09093e267b0/cnab/bundle.json
@@ -0,0 +1,1 @@
+../../../../../../build/testdata/bundles/mysql/.cnab/bundle.json
\ No newline at end of file
diff --git a/pkg/porter/testdata/cache/e05a608d47d6d138009ac09093e267b0/porter.yaml b/pkg/porter/testdata/cache/e05a608d47d6d138009ac09093e267b0/porter.yaml
new file mode 120000
index 00000000..90726e7b
--- /dev/null
+++ b/pkg/porter/testdata/cache/e05a608d47d6d138009ac09093e267b0/porter.yaml
@@ -0,0 +1,1 @@
+../../../../../build/testdata/bundles/mysql/porter.yaml
\ No newline at end of file
diff --git a/pkg/porter/testdata/show/expected-output.json b/pkg/porter/testdata/show/expected-output.json
index 76ba60dc..c407dddb 100644
--- a/pkg/porter/testdata/show/expected-output.json
+++ b/pkg/porter/testdata/show/expected-output.json
@@ -6,6 +6,7 @@
   "modified": "2020-04-18T01:02:03.000000004Z",
   "bundleRepository": "getporter/wordpress",
   "bundleVersion": "0.1.0",
+  "bundleDigest": "sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9",
   "labels": {
     "io.cnab/app": "wordpress",
     "io.cnab/appVersion": "v1.2.3"
@@ -17,11 +18,8 @@
   "status": {
     "runId": "1",
     "action": "upgrade",
-    "resultId": "2",
+    "resultId": "",
     "resultStatus": "succeeded",
-    "installationCompleted": true,
-    "bundleReference": "getporter/wordpress:v0.1.0",
-    "bundleVersion": "0.1.0",
-    "bundleDigest": "sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9"
+    "installationCompleted": true
   }
 }
diff --git a/pkg/porter/testdata/show/expected-output.txt b/pkg/porter/testdata/show/expected-output.txt
index 2d194567..abc22e03 100644
--- a/pkg/porter/testdata/show/expected-output.txt
+++ b/pkg/porter/testdata/show/expected-output.txt
@@ -2,10 +2,9 @@ Name: mywordpress
 Namespace: dev
 Created: 2020-04-18
 Modified: 2020-04-18
-
-Bundle:
-  Repository: getporter/wordpress
-  Version: 0.1.0
+Bundle: getporter/wordpress
+Version: 0.1.0
+Digest: sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9
 
 Labels:
   io.cnab/app: wordpress
@@ -19,8 +18,5 @@ Parameters:
   token     unknown  top-secret  
 
 Status:
-  Reference: getporter/wordpress:v0.1.0
-  Version: 0.1.0
   Last Action: upgrade
   Status: succeeded
-  Digest: sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9
diff --git a/pkg/porter/testdata/show/expected-output.yaml b/pkg/porter/testdata/show/expected-output.yaml
index 72d64c98..b38a8784 100644
--- a/pkg/porter/testdata/show/expected-output.yaml
+++ b/pkg/porter/testdata/show/expected-output.yaml
@@ -5,6 +5,7 @@ created: 2020-04-18T01:02:03.000000004Z
 modified: 2020-04-18T01:02:03.000000004Z
 bundleRepository: getporter/wordpress
 bundleVersion: 0.1.0
+bundleDigest: sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9
 labels:
   io.cnab/app: wordpress
   io.cnab/appVersion: v1.2.3
@@ -14,9 +15,6 @@ parameters:
 status:
   runId: "1"
   action: upgrade
-  resultId: "2"
+  resultId: ""
   resultStatus: succeeded
   installationCompleted: true
-  bundleReference: getporter/wordpress:v0.1.0
-  bundleVersion: 0.1.0
-  bundleDigest: sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9
diff --git a/pkg/porter/testdata/show/no-reference-expected-output.txt b/pkg/porter/testdata/show/no-reference-expected-output.txt
index ab98ee96..3d8800f0 100644
--- a/pkg/porter/testdata/show/no-reference-expected-output.txt
+++ b/pkg/porter/testdata/show/no-reference-expected-output.txt
@@ -2,6 +2,8 @@ Name: mywordpress
 Namespace: dev
 Created: 2020-04-18
 Modified: 2020-04-18
+Version: 0.1.0
+Digest: sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9
 
 Labels:
   io.cnab/app: wordpress
@@ -15,8 +17,5 @@ Parameters:
   token     unknown  top-secret  
 
 Status:
-  Reference: 
-  Version: 0.1.0
   Last Action: upgrade
   Status: succeeded
-  Digest: sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9
diff --git a/tests/integration/rebuild_test.go b/tests/integration/rebuild_test.go
index 84fe4c04..99e4888e 100644
--- a/tests/integration/rebuild_test.go
+++ b/tests/integration/rebuild_test.go
@@ -89,7 +89,7 @@ func TestRebuild_GenerateCredentialsNewBundle(t *testing.T) {
 
 	credentialOptions := porter.CredentialOptions{}
 	credentialOptions.Silent = true
-	err := credentialOptions.Validate([]string{}, p.Porter)
+	err := credentialOptions.Validate([]string{}, p.Context)
 	require.NoError(t, err)
 	err = p.GenerateCredentials(credentialOptions)
 	assert.NoError(t, err)
@@ -111,7 +111,7 @@ func TestRebuild_GenerateCredentialsExistingBundle(t *testing.T) {
 
 	credentialOptions := porter.CredentialOptions{}
 	credentialOptions.Silent = true
-	err := credentialOptions.Validate([]string{}, p.Porter)
+	err := credentialOptions.Validate([]string{}, p.Context)
 	require.NoError(t, err)
 	err = p.GenerateCredentials(credentialOptions)
 	require.NoError(t, err)