Repository: getporter/porter
Pull request #1753 (2021-09-08 15:29:34)

Refactoring before adding reconcilation
# What does this change
**Only store parameter overrides on the installation record**

Installation.Parameters should reflect parameter values that the user selected, similar to --param on the command line. It shouldn't contain any resolved values, such as from parameter sets as we already track those on the installation.ParameterSets field.

**Include calculated parameters in the json/yaml output**

When we output an installation with porter installation show, the data available in the plaintext version should be comparable to what's in the json/yaml output. Now that we are displaying all resolved parameters, but only storing the user-provided parameters on the installation record, we need to output more than just the installation record for those output formats.

I have changed DisplayInstallation to better wrap an Installation record, and then put all calculated fields into _calculated so that people can easily tell which fields are associated with the record (and can be changed when imported) and which ones are informational only.

**Fix test database creation**
The timeout was set to 0 when checking if there was an existing dev database that we could reuse. I thought 0 meant no timeout, but it means "timeout immediately". So we were always skipping the existing dev database. I've set a default in the database plugin so that we can't accidentally immediately timeout again.

**Create an installation record for dependencies**

When I refactored, it causes a bunch of errors when running a bundle with dependencies because I wasn't creating an explicit installation record for the dependency.

I have updated the code so that an installation is created and labeled with the following so that later we can filter them out if we want to:

```
sh.porter.parentInstallation = installation namespace/name
```

**Fix how we resolve parameters for dependencies**
This ensures that we use the relocated resolveParameters function when running a dependency, and that the final set of parameter used for the root bundle includes resolved parameters from the dependencies.

**Print params/creds before the bundle is run**
Print the params and creds passed to a bundle, including their values. Make sure that sensitive values are censored.

I was having a hell of a time debugging and once we have #1729 merged, it's super useful.

# What issue does it fix
This is refactoring prep-work for the new reconcillation capabilities of porter installation apply. The installation record should only contain _desired state_ and not include any status values. Otherwise it's really hard to reconcile and determine when changes should be made.

# Notes for the reviewer
N/A

# Checklist
- [ ] Unit Tests
- [ ] Documentation
- [ ] Schema (porter.yaml)



Patch:
diff --git a/pkg/claims/installation.go b/pkg/claims/installation.go
index 8f8afb5d..25ee4d1b 100644
--- a/pkg/claims/installation.go
+++ b/pkg/claims/installation.go
@@ -55,7 +55,7 @@ type Installation struct {
 	// Labels applied to the installation.
 	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty" toml:"labels,omitempty"`
 
-	// Parameters specified by the user through overrides or parameter sets.
+	// Parameters specified by the user through overrides.
 	// Does not include defaults, or values resolved from parameter sources.
 	Parameters map[string]interface{} `json:"parameters,omitempty" yaml:"parameters,omitempty" toml:"parameters,omitempty"`
 
@@ -220,6 +220,14 @@ func (i *Installation) TrackBundle(ref cnab.OCIReference) {
 	}
 }
 
+// SetLabel on the installation.
+func (i *Installation) SetLabel(key string, value string) {
+	if i.Labels == nil {
+		i.Labels = make(map[string]string, 1)
+	}
+	i.Labels[key] = value
+}
+
 // InstallationStatus's purpose is to assist with making porter list be able to display everything
 // with a single database query. Do not replicate data available on Run and Result here.
 type InstallationStatus struct {
diff --git a/pkg/cnab/extended_bundle.go b/pkg/cnab/extended_bundle.go
index 5b20a8ec..5065628b 100644
--- a/pkg/cnab/extended_bundle.go
+++ b/pkg/cnab/extended_bundle.go
@@ -59,6 +59,16 @@ func (b ExtendedBundle) IsInternalOutput(name string) bool {
 	return false
 }
 
+// IsSensitiveParameter determines if the parameter contains a sensitive value.
+func (b ExtendedBundle) IsSensitiveParameter(param string) bool {
+	if param, exists := b.Parameters[param]; exists {
+		if def, exists := b.Definitions[param.Definition]; exists {
+			return def.WriteOnly != nil && *def.WriteOnly
+		}
+	}
+	return false
+}
+
 // GetParameterType determines the type of parameter accounting for
 // Porter-specific parameter types like file.
 func (b ExtendedBundle) GetParameterType(def *definition.Schema) string {
diff --git a/pkg/cnab/provider/action.go b/pkg/cnab/provider/action.go
index c5575b8d..a0676f8b 100644
--- a/pkg/cnab/provider/action.go
+++ b/pkg/cnab/provider/action.go
@@ -1,6 +1,7 @@
 package cnabprovider
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
 
@@ -30,11 +31,11 @@ type ActionArguments struct {
 	// Target Path => File Contents
 	Files map[string]string
 
-	// Params is the set of user-specified parameter values to pass to the bundle.
-	Params map[string]string
+	// Params is the fully resolved set of parameters.
+	Params map[string]interface{}
 
 	// Either a filepath to a credential file or the name of a set of a credentials.
-	// TODO: move to the installation record
+	// TODO(carolynvs): use the values on the installation record?
 	CredentialIdentifiers []string
 
 	// Driver is the CNAB-compliant driver used to run bundle actions.
@@ -119,17 +120,12 @@ func (r *Runtime) Execute(args ActionArguments) error {
 		fmt.Fprintln(r.Err)
 	}
 
-	params, err := r.loadParameters(b, args)
-	if err != nil {
-		return errors.Wrap(err, "invalid parameters")
-	}
-
 	// 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()
 	currentRun.BundleDigest = args.BundleReference.Digest.String()
-	currentRun.Parameters = params
+	currentRun.Parameters = args.Params
 
 	// Validate the action
 	if _, err := b.GetAction(currentRun.Action); err != nil {
@@ -156,7 +152,7 @@ func (r *Runtime) Execute(args ActionArguments) error {
 		}
 	}
 
-	r.printDebugInfo(creds, params)
+	r.printDebugInfo(b, creds, args.Params)
 
 	opResult, result, err := a.Run(currentRun.ToCNAB(), creds.ToCNAB(), r.ApplyConfig(args)...)
 
@@ -239,18 +235,28 @@ func (r *Runtime) appendFailedResult(opErr error, run claims.Run) error {
 	return multierror.Append(opErr, resultErr).ErrorOrNil()
 }
 
-func (r *Runtime) printDebugInfo(creds secrets.Set, params map[string]interface{}) {
+func (r *Runtime) printDebugInfo(b cnab.ExtendedBundle, creds secrets.Set, params map[string]interface{}) {
 	if r.Debug {
-		// only print out the names of the credentials, not the contents, cuz they big and sekret
-		credKeys := make([]string, 0, len(creds))
-		for k := range creds {
-			credKeys = append(credKeys, k)
+		dump := &bytes.Buffer{}
+		secrets := make([]string, 0, len(params)+len(creds))
+
+		fmt.Fprintf(dump, "params:\n")
+		for k, v := range params {
+			if b.IsSensitiveParameter(k) {
+				// TODO(carolynvs): When we consolidate our conversion logic of parameters into strings, let's use it here.
+				// https://github.com/cnabio/cnab-go/issues/270
+				secrets = append(secrets, fmt.Sprintf("%v", v))
+			}
+			fmt.Fprintf(dump, "  - %s: %v\n", k, v)
 		}
-		// param values may also be sensitive, so just print names
-		paramKeys := make([]string, 0, len(params))
-		for k := range params {
-			paramKeys = append(paramKeys, k)
+
+		fmt.Fprintf(dump, "creds:\n")
+		for k, v := range creds {
+			secrets = append(secrets, fmt.Sprintf("%v", v))
+			fmt.Fprintf(dump, "  - %s: %v\n", k, v)
 		}
-		fmt.Fprintf(r.Err, "params: %v\ncreds: %v\n", paramKeys, credKeys)
+
+		r.Context.SetSensitiveValues(secrets)
+		fmt.Fprintln(r.Err, dump.String())
 	}
 }
diff --git a/pkg/cnab/provider/parameters.go b/pkg/cnab/provider/parameters.go
deleted file mode 100644
index d3175aae..00000000
--- a/pkg/cnab/provider/parameters.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package cnabprovider
-
-import (
-	"encoding/base64"
-	"fmt"
-
-	"get.porter.sh/porter/pkg/cnab"
-	"get.porter.sh/porter/pkg/secrets"
-	"get.porter.sh/porter/pkg/storage"
-
-	"github.com/cnabio/cnab-go/bundle"
-	"github.com/cnabio/cnab-go/bundle/definition"
-	"github.com/pkg/errors"
-)
-
-// loadParameters accepts a set of parameter overrides as well as parameter set
-// files and combines both with the default parameters to create a full set
-// of parameters.
-func (r *Runtime) loadParameters(bun cnab.ExtendedBundle, args ActionArguments) (map[string]interface{}, error) {
-	mergedParams := make(secrets.Set, len(args.Params))
-	paramSources, err := r.resolveParameterSources(bun, args)
-	if err != nil {
-		return nil, err
-	}
-
-	for key, val := range paramSources {
-		mergedParams[key] = val
-	}
-
-	// Apply user supplied parameter overrides last
-	for key, rawValue := range args.Params {
-		param, ok := bun.Parameters[key]
-		if !ok {
-			return nil, fmt.Errorf("parameter %s not defined in bundle", key)
-		}
-
-		def, ok := bun.Definitions[param.Definition]
-		if !ok {
-			return nil, fmt.Errorf("definition %s not defined in bundle", param.Definition)
-		}
-
-		// Apply porter specific conversions, like retrieving file contents
-		value, err := r.getUnconvertedValueFromRaw(bun, def, key, rawValue)
-		if err != nil {
-			return nil, err
-		}
-
-		mergedParams[key] = value
-	}
-
-	// Now convert all parameters which are currently strings into the
-	// proper type for the parameter, e.g. "false" -> false
-	typedParams := make(map[string]interface{}, len(mergedParams))
-	for key, unconverted := range mergedParams {
-		param, ok := bun.Parameters[key]
-		if !ok {
-			return nil, fmt.Errorf("parameter %s not defined in bundle", key)
-		}
-
-		def, ok := bun.Definitions[param.Definition]
-		if !ok {
-			return nil, fmt.Errorf("definition %s not defined in bundle", param.Definition)
-		}
-
-		if def.Type != nil {
-			value, err := def.ConvertValue(unconverted)
-			if err != nil {
-				return nil, errors.Wrapf(err, "unable to convert parameter's %s value %s to the destination parameter type %s", key, unconverted, def.Type)
-			}
-			typedParams[key] = value
-		} else {
-			// bundle dependency parameters can be any type, not sure we have a solid way to do a typed conversion
-			typedParams[key] = unconverted
-		}
-
-	}
-
-	return bundle.ValuesOrDefaults(typedParams, &bun.Bundle, args.Action)
-}
-
-func (r *Runtime) getUnconvertedValueFromRaw(b cnab.ExtendedBundle, def *definition.Schema, key, rawValue string) (string, error) {
-	// the parameter value (via rawValue) may represent a file on the local filesystem
-	if b.IsFileType(def) {
-		if _, err := r.FileSystem.Stat(rawValue); err == nil {
-			bytes, err := r.FileSystem.ReadFile(rawValue)
-			if err != nil {
-				return "", errors.Wrapf(err, "unable to read file parameter %s", key)
-			}
-			return base64.StdEncoding.EncodeToString(bytes), nil
-		}
-	}
-	return rawValue, nil
-}
-
-func (r *Runtime) resolveParameterSources(bun cnab.ExtendedBundle, args ActionArguments) (secrets.Set, error) {
-	if r.Debug {
-		fmt.Fprintln(r.Err, "Resolving parameter sources...")
-	}
-	parameterSources, required, err := r.Extensions.GetParameterSources()
-	if err != nil {
-		return nil, err
-	}
-
-	if !required {
-		if r.Debug {
-			fmt.Fprintln(r.Err, "No parameter sources defined!")
-		}
-		return nil, nil
-	}
-
-	values := secrets.Set{}
-	for parameterName, parameterSource := range parameterSources {
-		if r.Debug {
-			fmt.Fprintln(r.Err, "Resolving parameter source", parameterName)
-		}
-		for _, rawSource := range parameterSource.ListSourcesByPriority() {
-			var installation string
-			var outputName string
-			switch source := rawSource.(type) {
-			case cnab.OutputParameterSource:
-				installation = args.Installation.Name
-				outputName = source.OutputName
-			case cnab.DependencyOutputParameterSource:
-				// TODO(carolynvs): does this need to take namespace into account
-				installation = cnab.BuildPrerequisiteInstallationName(args.Installation.Name, source.Dependency)
-				outputName = source.OutputName
-			}
-
-			output, err := r.claims.GetLastOutput(args.Installation.Namespace, installation, outputName)
-			if err != nil {
-				// When we can't find the output, skip it and let the parameter be set another way
-				if errors.Is(err, storage.ErrNotFound{}) {
-					if r.Debug {
-						fmt.Fprintf(r.Err, "No previous output found for %s from %s/%s\n", outputName, args.Installation.Namespace, installation)
-					}
-					continue
-				}
-				// Otherwise, something else has happened, perhaps bad data or connectivity problems, we can't ignore it
-				return nil, errors.Wrapf(err, "could not set parameter %s from output %s of %s", parameterName, outputName, installation)
-			}
-
-			param, ok := bun.Parameters[parameterName]
-			if !ok {
-				return nil, fmt.Errorf("parameter %s not defined in bundle", parameterName)
-			}
-
-			def, ok := bun.Definitions[param.Definition]
-			if !ok {
-				return nil, fmt.Errorf("definition %s not defined in bundle", param.Definition)
-			}
-
-			if bun.IsFileType(def) {
-				values[parameterName] = base64.StdEncoding.EncodeToString(output.Value)
-			} else {
-				values[parameterName] = string(output.Value)
-			}
-
-			if r.Debug {
-				fmt.Fprintf(r.Out, "Injected installation %s output %s as parameter %s\n", installation, outputName, parameterName)
-			}
-		}
-	}
-
-	return values, nil
-}
diff --git a/pkg/porter/action.go b/pkg/porter/action.go
index 0b0b33e2..c5e7bfb7 100644
--- a/pkg/porter/action.go
+++ b/pkg/porter/action.go
@@ -11,8 +11,8 @@ import (
 func (p *Porter) ExecuteAction(installation claims.Installation, action BundleAction) error {
 	actionOpts := action.GetOptions()
 
-	deperator := newDependencyExecutioner(p, action.GetAction())
-	err := deperator.Prepare(action)
+	deperator := newDependencyExecutioner(p, installation, action)
+	err := deperator.Prepare()
 	if err != nil {
 		return err
 	}
@@ -22,11 +22,10 @@ func (p *Porter) ExecuteAction(installation claims.Installation, action BundleAc
 		return err
 	}
 
-	actionArgs, err := p.BuildActionArgs(installation, action)
+	actionArgs, err := deperator.PrepareRootActionArguments()
 	if err != nil {
 		return err
 	}
-	deperator.PrepareRootActionArguments(&actionArgs)
 
 	fmt.Fprintf(p.Out, "%s %s...\n", action.GetActionVerb(), actionOpts.Name)
 	return p.CNAB.Execute(actionArgs)
diff --git a/pkg/porter/cnab.go b/pkg/porter/cnab.go
index 7df473bf..26af17a5 100644
--- a/pkg/porter/cnab.go
+++ b/pkg/porter/cnab.go
@@ -239,6 +239,12 @@ func (o *bundleFileOptions) validateCNABFile(cxt *context.Context) error {
 // LoadParameters validates and resolves the parameters and sets. It must be
 // called after porter has loaded the bundle definition.
 func (o *sharedOptions) LoadParameters(p *Porter) error {
+	// This is called in multiple code paths, so exit early if
+	// we have already loaded the parameters into combinedParameters
+	if o.combinedParameters != nil {
+		return nil
+	}
+
 	err := o.parseParams()
 	if err != nil {
 		return err
diff --git a/pkg/porter/dependencies.go b/pkg/porter/dependencies.go
index 22a3617c..80a01627 100644
--- a/pkg/porter/dependencies.go
+++ b/pkg/porter/dependencies.go
@@ -10,6 +10,7 @@ import (
 	"get.porter.sh/porter/pkg/context"
 	"get.porter.sh/porter/pkg/manifest"
 	"get.porter.sh/porter/pkg/runtime"
+	"get.porter.sh/porter/pkg/storage"
 	"github.com/hashicorp/go-multierror"
 	"github.com/pkg/errors"
 )
@@ -18,53 +19,53 @@ type dependencyExecutioner struct {
 	*context.Context
 	porter *Porter
 
-	Action   string
 	Manifest *manifest.Manifest
 	Resolver BundleResolver
 	CNAB     cnabprovider.CNABProvider
 	Claims   claims.Provider
 
+	parentInstallation claims.Installation
+	parentAction       BundleAction
+	parentOpts         *BundleActionOptions
+
 	// These are populated by Prepare, call it or perish in inevitable errors
-	parentAction BundleAction
-	parentOpts   *BundleActionOptions
-	parentArgs   cnabprovider.ActionArguments
-	deps         []*queuedDependency
+	parentArgs cnabprovider.ActionArguments
+	deps       []*queuedDependency
 }
 
-func newDependencyExecutioner(p *Porter, action string) *dependencyExecutioner {
+func newDependencyExecutioner(p *Porter, installation claims.Installation, action BundleAction) *dependencyExecutioner {
 	resolver := BundleResolver{
 		Cache:    p.Cache,
 		Registry: p.Registry,
 	}
 	return &dependencyExecutioner{
-		porter:   p,
-		Action:   action,
-		Context:  p.Context,
-		Manifest: p.Manifest,
-		Resolver: resolver,
-		CNAB:     p.CNAB,
-		Claims:   p.Claims,
+		porter:             p,
+		parentInstallation: installation,
+		parentAction:       action,
+		parentOpts:         action.GetOptions(),
+		Context:            p.Context,
+		Manifest:           p.Manifest,
+		Resolver:           resolver,
+		CNAB:               p.CNAB,
+		Claims:             p.Claims,
 	}
 }
 
 type queuedDependency struct {
 	cnab.DependencyLock
 	BundleReference cnab.BundleReference
-	Parameters      map[string]string
+	Parameters      map[string]interface{}
 
 	// cache of the CNAB file contents
 	cnabFileContents []byte
 }
 
-func (e *dependencyExecutioner) Prepare(parentAction BundleAction) error {
-	e.parentAction = parentAction
-	e.parentOpts = parentAction.GetOptions()
-
-	parentArgs, err := e.porter.BuildActionArgs(claims.Installation{}, parentAction)
+func (e *dependencyExecutioner) Prepare() error {
+	parentActionArgs, err := e.porter.BuildActionArgs(e.parentInstallation, e.parentAction)
 	if err != nil {
 		return err
 	}
-	e.parentArgs = parentArgs
+	e.parentArgs = parentActionArgs
 
 	err = e.identifyDependencies()
 	if err != nil {
@@ -99,7 +100,12 @@ func (e *dependencyExecutioner) Execute() error {
 
 // PrepareRootActionArguments uses information about the dependencies of a bundle to prepare
 // the execution of the root operation.
-func (e *dependencyExecutioner) PrepareRootActionArguments(args *cnabprovider.ActionArguments) {
+func (e *dependencyExecutioner) PrepareRootActionArguments() (cnabprovider.ActionArguments, error) {
+	args, err := e.porter.BuildActionArgs(e.parentInstallation, e.parentAction)
+	if err != nil {
+		return cnabprovider.ActionArguments{}, err
+	}
+
 	if args.Files == nil {
 		args.Files = make(map[string]string, 2*len(e.deps))
 	}
@@ -112,12 +118,7 @@ func (e *dependencyExecutioner) PrepareRootActionArguments(args *cnabprovider.Ac
 		args.Files[target] = string(dep.cnabFileContents)
 	}
 
-	// Remove parameters for dependencies
-	for key := range args.Params {
-		if strings.Contains(key, "#") {
-			delete(args.Params, key)
-		}
-	}
+	return args, nil
 }
 
 func (e *dependencyExecutioner) identifyDependencies() error {
@@ -216,7 +217,7 @@ func (e *dependencyExecutioner) prepareDependency(dep *queuedDependency) error {
 				}
 
 				if dep.Parameters == nil {
-					dep.Parameters = make(map[string]string, 1)
+					dep.Parameters = make(map[string]interface{}, 1)
 				}
 				dep.Parameters[paramName] = value
 			}
@@ -225,7 +226,7 @@ func (e *dependencyExecutioner) prepareDependency(dep *queuedDependency) error {
 
 	// Handle any parameter overrides for the dependency defined on the command line
 	// --param DEP#PARAM=VALUE
-	for key, value := range e.parentArgs.Params {
+	for key, value := range e.parentOpts.combinedParameters {
 		parts := strings.Split(key, "#")
 		if len(parts) > 1 && parts[0] == dep.Alias {
 			paramName := parts[1]
@@ -236,7 +237,7 @@ func (e *dependencyExecutioner) prepareDependency(dep *queuedDependency) error {
 			}
 
 			if dep.Parameters == nil {
-				dep.Parameters = make(map[string]string, 1)
+				dep.Parameters = make(map[string]interface{}, 1)
 			}
 			dep.Parameters[paramName] = value
 			delete(e.parentArgs.Params, key)
@@ -247,8 +248,23 @@ func (e *dependencyExecutioner) prepareDependency(dep *queuedDependency) error {
 }
 
 func (e *dependencyExecutioner) executeDependency(dep *queuedDependency) error {
-	depInstallation := claims.NewInstallation(e.parentOpts.Namespace,
-		cnab.BuildPrerequisiteInstallationName(e.parentOpts.Name, dep.Alias))
+	// TODO(carolynvs): We should really switch up how the deperator works so that
+	// even the root bundle uses the execution engine here. This would set up how
+	// we want dependencies and mixins as bundles to work in the future.
+
+	depName := cnab.BuildPrerequisiteInstallationName(e.parentOpts.Name, dep.Alias)
+	depInstallation, err := e.Claims.GetInstallation(e.parentOpts.Namespace, depName)
+	if err != nil {
+		if errors.Is(err, storage.ErrNotFound{}) {
+			depInstallation = claims.NewInstallation(e.parentOpts.Namespace, depName)
+			depInstallation.SetLabel("sh.porter.parentInstallation", e.parentArgs.Installation.String())
+			if err = e.Claims.InsertInstallation(depInstallation); err != nil {
+				return err
+			}
+		} else {
+			return err
+		}
+	}
 
 	depArgs := cnabprovider.ActionArguments{
 		BundleReference:       dep.BundleReference,
@@ -270,7 +286,7 @@ func (e *dependencyExecutioner) executeDependency(dep *queuedDependency) error {
 
 	var executeErrs error
 	fmt.Fprintf(e.Out, "Executing dependency %s...\n", dep.Alias)
-	err := e.CNAB.Execute(depArgs)
+	err = e.CNAB.Execute(depArgs)
 	if err != nil {
 		executeErrs = multierror.Append(executeErrs, errors.Wrapf(err, "error executing dependency %s", dep.Alias))
 
diff --git a/pkg/porter/helpers.go b/pkg/porter/helpers.go
index 125ee4fd..9840b853 100644
--- a/pkg/porter/helpers.go
+++ b/pkg/porter/helpers.go
@@ -104,11 +104,7 @@ func (p *TestPorter) SetupIntegrationTest() {
 	require.NoError(t, err, "failed to set the storage schema")
 
 	// Load test credentials, with KUBECONFIG replaced properly
-	kubeconfig := p.Getenv("KUBECONFIG")
-	if kubeconfig == "" {
-		home := p.Getenv("HOME")
-		kubeconfig = filepath.Join(home, ".kube/config")
-	}
+	kubeconfig := filepath.Join(p.RepoRoot, "kind.config")
 	ciCredsPath := filepath.Join(p.RepoRoot, "build/testdata/credentials/ci.json")
 	ciCredsB, err := p.FileSystem.ReadFile(ciCredsPath)
 	require.NoError(t, err, "could not read test credentials %s", ciCredsPath)
diff --git a/pkg/porter/install.go b/pkg/porter/install.go
index a6b8e91b..f9e8e0e6 100644
--- a/pkg/porter/install.go
+++ b/pkg/porter/install.go
@@ -96,9 +96,10 @@ func (p *Porter) applyActionOptionsToInstallation(i claims.Installation, opts *B
 		return err
 	}
 	if i.Parameters == nil {
-		i.Parameters = make(map[string]interface{}, len(opts.combinedParameters))
+		i.Parameters = make(map[string]interface{}, len(opts.parsedParams))
 	}
-	for k, v := range opts.combinedParameters {
+	// Record the user-specified parameter values
+	for k, v := range opts.parsedParams {
 		i.Parameters[k] = v
 	}
 	// Record the names of the parameter sets used
diff --git a/pkg/porter/lifecycle.go b/pkg/porter/lifecycle.go
index 5866ac22..89115d18 100644
--- a/pkg/porter/lifecycle.go
+++ b/pkg/porter/lifecycle.go
@@ -1,6 +1,9 @@
 package porter
 
 import (
+	"fmt"
+	"strings"
+
 	"get.porter.sh/porter/pkg/cache"
 	"get.porter.sh/porter/pkg/claims"
 	"get.porter.sh/porter/pkg/cnab"
@@ -154,26 +157,42 @@ func (p *Porter) BuildActionArgs(installation claims.Installation, action Bundle
 		}
 	}
 
+	// Resolve the final set of typed parameters, taking into account the user overrides, parameter sources
+	// and defaults
+	err = opts.LoadParameters(p)
+	if err != nil {
+		return cnabprovider.ActionArguments{}, err
+	}
+
+	if p.Debug {
+		fmt.Fprintln(p.Err, "resolving parameters for installation", installation)
+	}
+
+	// Do not resolve parameters from dependencies
+	params := make(map[string]string, len(opts.combinedParameters))
+	for k, v := range opts.combinedParameters {
+		if strings.Contains(k, "#") {
+			continue
+		}
+		params[k] = v
+	}
+	resolvedParams, err := p.resolveParameters(installation, bundleRef.Definition, action.GetAction(), params)
+	if err != nil {
+		return cnabprovider.ActionArguments{}, err
+	}
+
 	args := cnabprovider.ActionArguments{
 		Action:                action.GetAction(),
 		Installation:          installation,
 		BundleReference:       bundleRef,
-		Params:                make(map[string]string, len(opts.combinedParameters)), // TODO(carolynvs): set on the installation record
+		Params:                resolvedParams,
 		CredentialIdentifiers: make([]string, len(opts.CredentialIdentifiers)),
 		Driver:                opts.Driver,
 		AllowDockerHostAccess: opts.AllowAccessToDockerHost,
 	}
 
-	err = opts.LoadParameters(p)
-	if err != nil {
-		return cnabprovider.ActionArguments{}, err
-	}
-
 	// Do a safe copy so that modifications to the args aren't also made to the
 	// original options, which is confusing to debug
-	for k, v := range opts.combinedParameters {
-		args.Params[k] = v
-	}
 	copy(args.CredentialIdentifiers, opts.CredentialIdentifiers)
 
 	return args, nil
diff --git a/pkg/porter/list.go b/pkg/porter/list.go
index 87a9772e..31bbd74a 100644
--- a/pkg/porter/list.go
+++ b/pkg/porter/list.go
@@ -58,54 +58,24 @@ 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
+	claims.Installation `json:",inline" yaml:",inline"`
+
+	DisplayInstallationMetadata `json:"_calculated" yaml:"_calculated"`
+}
+
+type DisplayInstallationMetadata struct {
+	ResolvedParameters DisplayValues `json:"resolvedParameters", yaml:"resolvedParameters"`
 }
 
 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,
-	}
-
-	labels := make([]string, 0, len(installation.Labels))
-	for k, v := range installation.Labels {
-		labels = append(labels, fmt.Sprintf("%s: %s", k, v))
+		Installation: installation,
 	}
-	sort.Strings(labels)
-	di.Labels = labels
 
 	// This is unset when we are just listing installations
 	if run != nil {
 		bun := cnab.ExtendedBundle{run.Bundle}
-		di.Parameters = NewDisplayValuesFromParameters(bun, installation.Parameters)
+		di.ResolvedParameters = NewDisplayValuesFromParameters(bun, run.Parameters)
 	}
 
 	return di
@@ -163,18 +133,18 @@ func (p *Porter) PrintInstallations(opts ListOptions) error {
 		return err
 	}
 
+	var displayInstallations DisplayInstallations
+	for _, installation := range installations {
+		displayInstallations = append(displayInstallations, NewDisplayInstallation(installation, nil))
+	}
+	sort.Sort(sort.Reverse(displayInstallations))
+
 	switch opts.Format {
 	case printer.FormatJson:
-		return printer.PrintJson(p.Out, installations)
+		return printer.PrintJson(p.Out, displayInstallations)
 	case printer.FormatYaml:
-		return printer.PrintYaml(p.Out, installations)
+		return printer.PrintYaml(p.Out, displayInstallations)
 	case printer.FormatTable:
-		var displayInstallations DisplayInstallations
-		for _, installation := range installations {
-			displayInstallations = append(displayInstallations, NewDisplayInstallation(installation, nil))
-		}
-		sort.Sort(sort.Reverse(displayInstallations))
-
 		// have every row use the same "now" starting ... NOW!
 		now := time.Now()
 		tp := dtprinter.DateTimePrinter{
@@ -187,7 +157,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.Status.Action, cl.Status.ResultStatus}
 			}
 		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 74906091..34648fa7 100644
--- a/pkg/porter/parameters.go
+++ b/pkg/porter/parameters.go
@@ -1,11 +1,13 @@
 package porter
 
 import (
+	"encoding/base64"
 	"encoding/json"
 	"fmt"
 	"sort"
 	"time"
 
+	"get.porter.sh/porter/pkg/claims"
 	"get.porter.sh/porter/pkg/cnab"
 	"get.porter.sh/porter/pkg/editor"
 	"get.porter.sh/porter/pkg/encoding"
@@ -14,6 +16,8 @@ import (
 	"get.porter.sh/porter/pkg/printer"
 	"get.porter.sh/porter/pkg/secrets"
 	"get.porter.sh/porter/pkg/storage"
+	"github.com/cnabio/cnab-go/bundle"
+	"github.com/cnabio/cnab-go/bundle/definition"
 	"go.mongodb.org/mongo-driver/bson"
 
 	dtprinter "github.com/carolynvs/datetime-printer"
@@ -493,3 +497,155 @@ func (p *Porter) ParametersApply(o ApplyOptions) error {
 
 	return p.Parameters.UpsertParameterSet(params)
 }
+
+// resolveParameters accepts a set of parameter assignments and combines them
+// with parameter sources and default parameter values to create a full set
+// of parameters.
+func (p *Porter) resolveParameters(installation claims.Installation, bun cnab.ExtendedBundle, action string, params map[string]string) (map[string]interface{}, error) {
+	mergedParams := make(secrets.Set, len(params))
+	paramSources, err := p.resolveParameterSources(bun, installation)
+	if err != nil {
+		return nil, err
+	}
+
+	for key, val := range paramSources {
+		mergedParams[key] = val
+	}
+
+	// Apply user supplied parameter overrides last
+	for key, rawValue := range params {
+		param, ok := bun.Parameters[key]
+		if !ok {
+			return nil, fmt.Errorf("parameter %s not defined in bundle", key)
+		}
+
+		def, ok := bun.Definitions[param.Definition]
+		if !ok {
+			return nil, fmt.Errorf("definition %s not defined in bundle", param.Definition)
+		}
+
+		// Apply porter specific conversions, like retrieving file contents
+		value, err := p.getUnconvertedValueFromRaw(bun, def, key, rawValue)
+		if err != nil {
+			return nil, err
+		}
+
+		mergedParams[key] = value
+	}
+
+	// Now convert all parameters which are currently strings into the
+	// proper type for the parameter, e.g. "false" -> false
+	typedParams := make(map[string]interface{}, len(mergedParams))
+	for key, unconverted := range mergedParams {
+		param, ok := bun.Parameters[key]
+		if !ok {
+			return nil, fmt.Errorf("parameter %s not defined in bundle", key)
+		}
+
+		def, ok := bun.Definitions[param.Definition]
+		if !ok {
+			return nil, fmt.Errorf("definition %s not defined in bundle", param.Definition)
+		}
+
+		if def.Type != nil {
+			value, err := def.ConvertValue(unconverted)
+			if err != nil {
+				return nil, errors.Wrapf(err, "unable to convert parameter's %s value %s to the destination parameter type %s", key, unconverted, def.Type)
+			}
+			typedParams[key] = value
+		} else {
+			// bundle dependency parameters can be any type, not sure we have a solid way to do a typed conversion
+			typedParams[key] = unconverted
+		}
+
+	}
+
+	return bundle.ValuesOrDefaults(typedParams, &bun.Bundle, action)
+}
+
+func (p *Porter) getUnconvertedValueFromRaw(b cnab.ExtendedBundle, def *definition.Schema, key, rawValue string) (string, error) {
+	// the parameter value (via rawValue) may represent a file on the local filesystem
+	if b.IsFileType(def) {
+		if _, err := p.FileSystem.Stat(rawValue); err == nil {
+			bytes, err := p.FileSystem.ReadFile(rawValue)
+			if err != nil {
+				return "", errors.Wrapf(err, "unable to read file parameter %s", key)
+			}
+			return base64.StdEncoding.EncodeToString(bytes), nil
+		}
+	}
+	return rawValue, nil
+}
+
+func (p *Porter) resolveParameterSources(bun cnab.ExtendedBundle, installation claims.Installation) (secrets.Set, error) {
+	if !bun.HasParameterSources() {
+		if p.Debug {
+			fmt.Fprintln(p.Err, "No parameter sources defined, skipping")
+		}
+		return nil, nil
+	}
+
+	if p.Debug {
+		fmt.Fprintln(p.Err, "Resolving parameter sources...")
+	}
+
+	parameterSources, err := bun.ReadParameterSources()
+	if err != nil {
+		return nil, err
+	}
+
+	values := secrets.Set{}
+	for parameterName, parameterSource := range parameterSources {
+		if p.Debug {
+			fmt.Fprintln(p.Err, "Resolving parameter source", parameterName)
+		}
+		for _, rawSource := range parameterSource.ListSourcesByPriority() {
+			var installationName string
+			var outputName string
+			switch source := rawSource.(type) {
+			case cnab.OutputParameterSource:
+				installationName = installation.Name
+				outputName = source.OutputName
+			case cnab.DependencyOutputParameterSource:
+				// TODO(carolynvs): does this need to take namespace into account
+				installationName = cnab.BuildPrerequisiteInstallationName(installation.Name, source.Dependency)
+				outputName = source.OutputName
+			}
+
+			output, err := p.Claims.GetLastOutput(installation.Namespace, installationName, outputName)
+			if err != nil {
+				// When we can't find the output, skip it and let the parameter be set another way
+				if errors.Is(err, storage.ErrNotFound{}) {
+					if p.Debug {
+						fmt.Fprintf(p.Err, "No previous output found for %s from %s/%s\n", outputName, installation.Namespace, installationName)
+					}
+					continue
+				}
+				// Otherwise, something else has happened, perhaps bad data or connectivity problems, we can't ignore it
+				return nil, errors.Wrapf(err, "could not set parameter %s from output %s of %s", parameterName, outputName, installation)
+			}
+
+			param, ok := bun.Parameters[parameterName]
+			if !ok {
+				return nil, fmt.Errorf("parameter %s not defined in bundle", parameterName)
+			}
+
+			def, ok := bun.Definitions[param.Definition]
+			if !ok {
+				return nil, fmt.Errorf("definition %s not defined in bundle", param.Definition)
+			}
+
+			if bun.IsFileType(def) {
+				values[parameterName] = base64.StdEncoding.EncodeToString(output.Value)
+			} else {
+				values[parameterName] = string(output.Value)
+			}
+
+			if p.Debug {
+				fmt.Fprintf(p.Out, "Injected installation %s output %s as parameter %s\n", installation, outputName, parameterName)
+			}
+		}
+	}
+
+	return values, nil
+}
diff --git a/pkg/porter/show.go b/pkg/porter/show.go
index 3a9fa510..f3b17abb 100644
--- a/pkg/porter/show.go
+++ b/pkg/porter/show.go
@@ -2,6 +2,7 @@ package porter
 
 import (
 	"fmt"
+	"sort"
 	"time"
 
 	"get.porter.sh/porter/pkg/claims"
@@ -68,11 +69,13 @@ func (p *Porter) ShowInstallation(opts ShowOptions) error {
 		return err
 	}
 
+	displayInstallation := NewDisplayInstallation(installation, run)
+
 	switch opts.Format {
 	case printer.FormatJson:
-		return printer.PrintJson(p.Out, installation)
+		return printer.PrintJson(p.Out, displayInstallation)
 	case printer.FormatYaml:
-		return printer.PrintYaml(p.Out, installation)
+		return printer.PrintYaml(p.Out, displayInstallation)
 	case printer.FormatTable, printer.FormatPlaintext:
 		// Set up human friendly time formatter
 		now := time.Now()
@@ -80,23 +83,21 @@ func (p *Porter) ShowInstallation(opts ShowOptions) error {
 			Now: func() time.Time { return now },
 		}
 
-		displayInstallation := NewDisplayInstallation(installation, run)
-
 		// Print installation details
 		fmt.Fprintf(p.Out, "Name: %s\n", displayInstallation.Name)
 		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 != "" {
+		if displayInstallation.BundleRepository != "" {
 			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)
+			fmt.Fprintf(p.Out, "  Repository: %s\n", displayInstallation.BundleRepository)
+			if displayInstallation.BundleVersion != "" {
+				fmt.Fprintf(p.Out, "  Version: %s\n", displayInstallation.BundleVersion)
 			}
-			if displayInstallation.TrackedDigest != "" {
-				fmt.Fprintf(p.Out, "  Digest: %s\n", displayInstallation.TrackedDigest)
+			if displayInstallation.BundleDigest != "" {
+				fmt.Fprintf(p.Out, "  Digest: %s\n", displayInstallation.BundleDigest)
 			}
 		}
 
@@ -105,7 +106,14 @@ func (p *Porter) ShowInstallation(opts ShowOptions) error {
 			fmt.Fprintln(p.Out)
 			fmt.Fprintln(p.Out, "Labels:")
 
-			for _, label := range displayInstallation.Labels {
+			// Print labels in alphabetical order
+			labels := make([]string, 0, len(installation.Labels))
+			for k, v := range installation.Labels {
+				labels = append(labels, fmt.Sprintf("%s: %s", k, v))
+			}
+			sort.Strings(labels)
+
+			for _, label := range labels {
 				fmt.Fprintf(p.Out, "  %s\n", label)
 			}
 		}
@@ -115,7 +123,7 @@ func (p *Porter) ShowInstallation(opts ShowOptions) error {
 			fmt.Fprintln(p.Out)
 			fmt.Fprintln(p.Out, "Parameters:")
 
-			err = p.printDisplayValuesTable(displayInstallation.Parameters)
+			err = p.printDisplayValuesTable(displayInstallation.ResolvedParameters)
 			if err != nil {
 				return err
 			}
@@ -143,11 +151,11 @@ func (p *Porter) ShowInstallation(opts ShowOptions) error {
 		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, "  Reference: %s\n", displayInstallation.Status.BundleReference)
+			fmt.Fprintf(p.Out, "  Version: %s\n", displayInstallation.Status.BundleVersion)
+			fmt.Fprintf(p.Out, "  Last Action: %s\n", displayInstallation.Status.Action)
+			fmt.Fprintf(p.Out, "  Status: %s\n", displayInstallation.Status.ResultStatus)
+			fmt.Fprintf(p.Out, "  Digest: %s\n", displayInstallation.Status.BundleDigest)
 		}
 
 		return nil
diff --git a/pkg/porter/uninstall.go b/pkg/porter/uninstall.go
index 5d172e2a..a8902138 100644
--- a/pkg/porter/uninstall.go
+++ b/pkg/porter/uninstall.go
@@ -78,17 +78,16 @@ func (p *Porter) UninstallBundle(opts UninstallOptions) error {
 		return errors.Wrapf(err, "could not find installation %s/%s", opts.Namespace, opts.Name)
 	}
 
-	deperator := newDependencyExecutioner(p, cnab.ActionUninstall)
-	err = deperator.Prepare(opts)
+	deperator := newDependencyExecutioner(p, installation, opts)
+	err = deperator.Prepare()
 	if err != nil {
 		return err
 	}
 
-	actionArgs, err := p.BuildActionArgs(installation, opts)
+	actionArgs, err := deperator.PrepareRootActionArguments()
 	if err != nil {
 		return err
 	}
-	deperator.PrepareRootActionArguments(&actionArgs)
 
 	fmt.Fprintf(p.Out, "%s %s...\n", opts.GetActionVerb(), opts.Name)
 	err = p.CNAB.Execute(actionArgs)
diff --git a/pkg/runtime/runtime-manifest.go b/pkg/runtime/runtime-manifest.go
index 49275c4b..40ec73aa 100644
--- a/pkg/runtime/runtime-manifest.go
+++ b/pkg/runtime/runtime-manifest.go
@@ -460,6 +460,9 @@ func (m *RuntimeManifest) Initialize() error {
 			// Porter by default places parameter value into file determined by Destination.Path
 			bytes, err := m.FileSystem.ReadFile(param.Destination.Path)
 			if err != nil {
+				if os.IsNotExist(err) {
+					continue
+				}
 				return errors.Wrapf(err, "unable to acquire value for parameter %s", paramName)
 			}
 
diff --git a/pkg/storage/plugins/mongodb/mongodb.go b/pkg/storage/plugins/mongodb/mongodb.go
index 0ed6eb20..91ef54e1 100644
--- a/pkg/storage/plugins/mongodb/mongodb.go
+++ b/pkg/storage/plugins/mongodb/mongodb.go
@@ -33,10 +33,14 @@ type Store struct {
 
 // NewStore creates a new storage engine that uses MongoDB.
 func NewStore(c *portercontext.Context, cfg PluginConfig) *Store {
+	timeout := cfg.Timeout
+	if timeout <= 0 {
+		timeout = 10 // default to 10 seconds
+	}
 	return &Store{
 		Context: c,
 		url:     cfg.URL,
-		timeout: time.Duration(cfg.Timeout) * time.Second,
+		timeout: time.Duration(timeout) * time.Second,
 	}
 }
 


Test patch:
diff --git a/pkg/cnab/extended_bundle_test.go b/pkg/cnab/extended_bundle_test.go
index d0dc345c..37cb04a2 100644
--- a/pkg/cnab/extended_bundle_test.go
+++ b/pkg/cnab/extended_bundle_test.go
@@ -114,3 +114,51 @@ func TestExtendedBundle_IsInternalParameter(t *testing.T) {
 		require.True(t, bun.IsInternalParameter("porter-debug"))
 	})
 }
+
+func TestExtendedBundle_IsSensitiveParameter(t *testing.T) {
+	sensitive := true
+	bun := ExtendedBundle{bundle.Bundle{
+		Definitions: definition.Definitions{
+			"foo": &definition.Schema{
+				Type:      "string",
+				WriteOnly: &sensitive,
+			},
+			"porter-debug": &definition.Schema{
+				Type:    "string",
+				Comment: PorterInternal,
+			},
+		},
+		Parameters: map[string]bundle.Parameter{
+			"foo": {
+				Definition: "foo",
+			},
+			"baz": {
+				Definition: "baz",
+			},
+			"porter-debug": {
+				Definition: "porter-debug",
+			},
+		},
+	}}
+
+	t.Run("empty bundle", func(t *testing.T) {
+		b := ExtendedBundle{}
+		require.False(t, b.IsSensitiveParameter("foo"))
+	})
+
+	t.Run("param does not exist", func(t *testing.T) {
+		require.False(t, bun.IsSensitiveParameter("bar"))
+	})
+
+	t.Run("definition does not exist", func(t *testing.T) {
+		require.False(t, bun.IsSensitiveParameter("baz"))
+	})
+
+	t.Run("is not sensitive", func(t *testing.T) {
+		require.False(t, bun.IsSensitiveParameter("porter-debug"))
+	})
+
+	t.Run("is sensitive", func(t *testing.T) {
+		require.True(t, bun.IsSensitiveParameter("foo"))
+	})
+}
diff --git a/pkg/cnab/provider/parameters_test.go b/pkg/cnab/provider/parameters_test.go
deleted file mode 100644
index bf09de26..00000000
--- a/pkg/cnab/provider/parameters_test.go
+++ /dev/null
@@ -1,591 +0,0 @@
-package cnabprovider
-
-import (
-	"testing"
-
-	"get.porter.sh/porter/pkg/claims"
-	"get.porter.sh/porter/pkg/cnab"
-	"get.porter.sh/porter/pkg/secrets"
-	"github.com/cnabio/cnab-go/bundle"
-	"github.com/cnabio/cnab-go/bundle/definition"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func Test_loadParameters_paramNotDefined(t *testing.T) {
-	t.Parallel()
-
-	r := NewTestRuntime(t)
-	defer r.Teardown()
-
-	b := cnab.ExtendedBundle{bundle.Bundle{
-		Parameters: map[string]bundle.Parameter{},
-	}}
-
-	overrides := map[string]string{
-		"foo": "bar",
-	}
-
-	args := ActionArguments{
-		Action: "action",
-		Params: overrides,
-	}
-	_, err := r.loadParameters(b, args)
-	require.EqualError(t, err, "parameter foo not defined in bundle")
-}
-
-func Test_loadParameters_definitionNotDefined(t *testing.T) {
-	t.Parallel()
-
-	r := NewTestRuntime(t)
-	defer r.Teardown()
-
-	b := cnab.ExtendedBundle{bundle.Bundle{
-		Parameters: map[string]bundle.Parameter{
-			"foo": {
-				Definition: "foo",
-			},
-		},
-	}}
-
-	overrides := map[string]string{
-		"foo": "bar",
-	}
-
-	args := ActionArguments{
-		Action: "action",
-		Params: overrides,
-	}
-	_, err := r.loadParameters(b, args)
-	require.EqualError(t, err, "definition foo not defined in bundle")
-}
-
-func Test_loadParameters_applyTo(t *testing.T) {
-	t.Parallel()
-
-	r := NewTestRuntime(t)
-	defer r.Teardown()
-
-	// Here we set default values, but expect nil/empty
-	// values for parameters that do not apply to a given action
-	b := cnab.ExtendedBundle{bundle.Bundle{
-		Definitions: definition.Definitions{
-			"foo": &definition.Schema{
-				Type:    "string",
-				Default: "default-foo-value",
-			},
-			"bar": &definition.Schema{
-				Type:    "integer",
-				Default: "default-bar-value",
-			},
-			"true": &definition.Schema{
-				Type:    "boolean",
-				Default: "default-true-value",
-			},
-		},
-		Parameters: map[string]bundle.Parameter{
-			"foo": {
-				Definition: "foo",
-				ApplyTo: []string{
-					"action",
-				},
-			},
-			"bar": {
-				Definition: "bar",
-			},
-			"true": {
-				Definition: "true",
-				ApplyTo: []string{
-					"different-action",
-				},
-			},
-		},
-	}}
-
-	overrides := map[string]string{
-		"foo":  "FOO",
-		"bar":  "456",
-		"true": "false",
-	}
-
-	args := ActionArguments{
-		Action: "action",
-		Params: overrides,
-	}
-	params, err := r.loadParameters(b, args)
-	require.NoError(t, err)
-
-	require.Equal(t, "FOO", params["foo"], "expected param 'foo' to be updated")
-	require.Equal(t, 456, params["bar"], "expected param 'bar' to be updated")
-	require.Equal(t, nil, params["true"], "expected param 'true' to be nil as it does not apply")
-}
-
-func Test_loadParameters_applyToBundleDefaults(t *testing.T) {
-	t.Parallel()
-
-	r := NewTestRuntime(t)
-	defer r.Teardown()
-
-	b := cnab.ExtendedBundle{bundle.Bundle{
-		Definitions: definition.Definitions{
-			"foo": &definition.Schema{
-				Type:    "string",
-				Default: "foo-default",
-			},
-		},
-		Parameters: map[string]bundle.Parameter{
-			"foo": {
-				Definition: "foo",
-				ApplyTo: []string{
-					"different-action",
-				},
-			},
-		},
-	}}
-
-	args := ActionArguments{Action: "action"}
-	params, err := r.loadParameters(b, args)
-	require.NoError(t, err)
-
-	require.Equal(t, nil, params["foo"], "expected param 'foo' to be nil, regardless of the bundle default, as it does not apply")
-}
-
-func Test_loadParameters_requiredButDoesNotApply(t *testing.T) {
-	t.Parallel()
-
-	r := NewTestRuntime(t)
-	defer r.Teardown()
-
-	b := cnab.ExtendedBundle{bundle.Bundle{
-		Definitions: definition.Definitions{
-			"foo": &definition.Schema{
-				Type: "string",
-			},
-		},
-		Parameters: map[string]bundle.Parameter{
-			"foo": {
-				Definition: "foo",
-				ApplyTo: []string{
-					"different-action",
-				},
-				Required: true,
-			},
-		},
-	}}
-
-	args := ActionArguments{Action: "action"}
-	params, err := r.loadParameters(b, args)
-	require.NoError(t, err)
-
-	require.Equal(t, nil, params["foo"], "expected param 'foo' to be nil, regardless of claim value, as it does not apply")
-}
-
-func Test_loadParameters_fileParameter(t *testing.T) {
-	t.Parallel()
-
-	r := NewTestRuntime(t)
-	defer r.Teardown()
-
-	r.TestConfig.TestContext.AddTestFile("testdata/file-param", "/path/to/file")
-
-	b := cnab.ExtendedBundle{bundle.Bundle{
-		RequiredExtensions: []string{
-			cnab.FileParameterExtensionKey,
-		},
-		Definitions: definition.Definitions{
-			"foo": &definition.Schema{
-				Type:            "string",
-				ContentEncoding: "base64",
-			},
-		},
-		Parameters: map[string]bundle.Parameter{
-			"foo": {
-				Definition: "foo",
-				Required:   true,
-				Destination: &bundle.Location{
-					Path: "/tmp/foo",
-				},
-			},
-		},
-	}}
-
-	overrides := map[string]string{
-		"foo": "/path/to/file",
-	}
-
-	args := ActionArguments{
-		Action: "action",
-		Params: overrides,
-	}
-	params, err := r.loadParameters(b, args)
-	require.NoError(t, err)
-
-	require.Equal(t, "SGVsbG8gV29ybGQh", params["foo"], "expected param 'foo' to be the base64-encoded file contents")
-}
-
-func Test_loadParameters_ParameterSourcePrecedence(t *testing.T) {
-	t.Parallel()
-
-	t.Run("nothing present, use default", func(t *testing.T) {
-		t.Parallel()
-
-		r := NewTestRuntime(t)
-		defer r.Teardown()
-
-		r.TestParameters.AddTestParameters("testdata/paramset.json")
-		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
-
-		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
-		b, err := r.ProcessBundleFromFile("bundle.json")
-		require.NoError(t, err, "ProcessBundle failed")
-
-		args := ActionArguments{
-			Installation: claims.Installation{Name: "mybun"},
-			Action:       cnab.ActionUpgrade,
-		}
-		params, err := r.loadParameters(b, args)
-		require.NoError(t, err)
-		assert.Equal(t, "foo_default", params["foo"],
-			"expected param 'foo' to have default value")
-	})
-
-	t.Run("only override present", func(t *testing.T) {
-		t.Parallel()
-
-		r := NewTestRuntime(t)
-		defer r.Teardown()
-
-		r.TestParameters.AddTestParameters("testdata/paramset.json")
-		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
-
-		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
-		b, err := r.ProcessBundleFromFile("bundle.json")
-		require.NoError(t, err, "ProcessBundle failed")
-
-		overrides := map[string]string{
-			"foo": "foo_override",
-		}
-
-		args := ActionArguments{
-			Installation: claims.Installation{Name: "mybun"},
-			Action:       cnab.ActionUpgrade,
-			Params:       overrides,
-		}
-		params, err := r.loadParameters(b, args)
-		require.NoError(t, err)
-		assert.Equal(t, "foo_override", params["foo"],
-			"expected param 'foo' to have override value")
-	})
-
-	t.Run("only parameter source present", func(t *testing.T) {
-		t.Parallel()
-
-		r := NewTestRuntime(t)
-		defer r.Teardown()
-
-		r.TestParameters.AddTestParameters("testdata/paramset.json")
-		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
-
-		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
-		b, err := r.ProcessBundleFromFile("bundle.json")
-		require.NoError(t, err, "ProcessBundle failed")
-
-		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
-		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = b.Bundle })
-		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
-		r.TestClaims.CreateOutput(cr.NewOutput("foo", []byte("foo_source")))
-
-		args := ActionArguments{
-			Installation: claims.Installation{Name: "mybun"},
-			Action:       cnab.ActionUpgrade,
-		}
-		params, err := r.loadParameters(b, args)
-		require.NoError(t, err)
-		assert.Equal(t, "foo_source", params["foo"],
-			"expected param 'foo' to have parameter source value")
-	})
-
-	t.Run("override > parameter source", func(t *testing.T) {
-		t.Parallel()
-
-		r := NewTestRuntime(t)
-		defer r.Teardown()
-
-		r.TestParameters.AddTestParameters("testdata/paramset.json")
-		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
-
-		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
-		b, err := r.ProcessBundleFromFile("bundle.json")
-		require.NoError(t, err, "ProcessBundle failed")
-
-		overrides := map[string]string{
-			"foo": "foo_override",
-		}
-
-		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
-		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall))
-		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
-		r.TestClaims.CreateOutput(cr.NewOutput("foo", []byte("foo_source")))
-
-		args := ActionArguments{
-			Installation: claims.Installation{Name: "mybun"},
-			Action:       cnab.ActionUpgrade,
-			Params:       overrides,
-		}
-		params, err := r.loadParameters(b, args)
-		require.NoError(t, err)
-		assert.Equal(t, "foo_override", params["foo"],
-			"expected param 'foo' to have parameter override value")
-	})
-
-	t.Run("dependency output without type", func(t *testing.T) {
-		t.Parallel()
-
-		r := NewTestRuntime(t)
-		defer r.Teardown()
-
-		r.TestParameters.AddTestParameters("testdata/paramset.json")
-		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
-
-		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
-		b, err := r.ProcessBundleFromFile("bundle.json")
-		require.NoError(t, err, "ProcessBundle failed")
-
-		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun-mysql"))
-		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = b.Bundle })
-		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
-		r.TestClaims.CreateOutput(cr.NewOutput("connstr", []byte("connstr value")))
-
-		args := ActionArguments{
-			Installation: claims.Installation{Name: "mybun"},
-			Action:       cnab.ActionUpgrade,
-		}
-		params, err := r.loadParameters(b, args)
-		require.NoError(t, err)
-		assert.Equal(t, "connstr value", params["connstr"],
-			"expected param 'connstr' to have parameter value from the untyped dependency output")
-	})
-
-	t.Run("merge parameter values", func(t *testing.T) {
-		t.Parallel()
-
-		r := NewTestRuntime(t)
-		defer r.Teardown()
-
-		r.TestParameters.AddTestParameters("testdata/paramset.json")
-		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
-
-		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
-		b, err := r.ProcessBundleFromFile("bundle.json")
-		require.NoError(t, err, "ProcessBundle failed")
-
-		// foo is set by a user
-		// bar is set by a parameter source
-		// baz is set by the bundle default
-		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
-		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall))
-		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
-		r.TestClaims.CreateOutput(cr.NewOutput("foo", []byte("foo_source")))
-		r.TestClaims.CreateOutput(cr.NewOutput("bar", []byte("bar_source")))
-		r.TestClaims.CreateOutput(cr.NewOutput("baz", []byte("baz_source")))
-
-		args := ActionArguments{
-			Installation: claims.Installation{Name: "mybun"},
-			Action:       cnab.ActionUpgrade,
-			Params:       map[string]string{"foo": "foo_override"},
-		}
-		params, err := r.loadParameters(b, args)
-		require.NoError(t, err)
-		assert.Equal(t, "foo_override", params["foo"],
-			"expected param 'foo' to have parameter override value")
-		assert.Equal(t, "bar_source", params["bar"],
-			"expected param 'bar' to have parameter source value")
-		assert.Equal(t, "baz_default", params["baz"],
-			"expected param 'baz' to have bundle default value")
-	})
-}
-
-// This is intended to cover the matrix of cases around parameter value resolution.
-// It exercises the matrix for all supported actions.
-func Test_Paramapalooza(t *testing.T) {
-	testcases := []struct {
-		Name            string
-		Required        bool
-		Provided        bool
-		DefaultExists   bool
-		AppliesToAction bool
-		ExpectedVal     interface{}
-		ExpectedErr     string
-	}{
-		// Are you ready to enter the Matrix?
-		{Name: "required, provided, default exists, applies to action",
-			Required: true, Provided: true, DefaultExists: true, AppliesToAction: true, ExpectedVal: "my-param-value",
-		},
-		{"required, provided, default exists, does not apply to action",
-			true, true, true, false, nil, "",
-		},
-		{"required, provided, default does not exist, applies to action",
-			true, true, false, true, "my-param-value", "",
-		},
-		{"required, provided, default does not exist, does not apply to action",
-			true, true, false, false, nil, "",
-		},
-		// As of writing, bundle.ValuesOrDefaults in cnab-go requires a specific override
-		// be provided if applicable to an action.
-		// Otherwise, it errors out and does not look up/use default in this case.
-		{"required, not provided, default exists, applies to action",
-			true, false, true, true, nil, "invalid parameters: parameter \"my-param\" is required",
-		},
-		{"required, not provided, default exists, does not apply to action",
-			true, false, true, false, nil, "",
-		},
-		{"required, not provided, default does not exist, applies to action",
-			true, false, false, true, nil, "invalid parameters: parameter \"my-param\" is required",
-		},
-		{"required, not provided, default does not exist, does not apply to action",
-			true, false, false, false, nil, "",
-		},
-		{"not required, provided, default exists, applies to action",
-			false, true, true, true, "my-param-value", "",
-		},
-		{"not required, provided, default exists, does not apply to action",
-			false, true, true, false, nil, "",
-		},
-		{"not required, provided, default does not exist, applies to action",
-			false, true, false, true, "my-param-value", "",
-		},
-		{"not required, provided, default does not exist, does not apply to action",
-			false, true, false, false, nil, "",
-		},
-		{"not required, not provided, default exists, applies to action",
-			false, false, true, true, "my-param-default", "",
-		},
-		{"not required, not provided, default exists, does not apply to action",
-			false, false, true, false, nil, "",
-		},
-		{"not required, not provided, default does not exist, applies to action",
-			false, false, false, true, nil, "",
-		},
-		{"not required, not provided, default does not exist, does not apply to action",
-			false, false, false, false, nil, "",
-		},
-	}
-
-	for _, tc := range testcases {
-		t.Run(tc.Name, func(t *testing.T) {
-			actions := []string{"install", "upgrade", "uninstall", "zombies"}
-			for _, action := range actions {
-				t.Run(action, func(t *testing.T) {
-					t.Parallel()
-					tc := tc
-
-					r := NewTestRuntime(t)
-					defer r.Teardown()
-
-					bun := cnab.ExtendedBundle{bundle.Bundle{
-						Name:          "mybuns",
-						Version:       "1.0.0",
-						SchemaVersion: "v1.0.0",
-						Actions: map[string]bundle.Action{
-							"zombies": {
-								Modifies: true,
-							},
-						},
-						InvocationImages: []bundle.InvocationImage{
-							{
-								BaseImage: bundle.BaseImage{
-									Image:     "mybuns:latest",
-									ImageType: "docker",
-								},
-							},
-						},
-						Definitions: definition.Definitions{
-							"my-param": &definition.Schema{
-								Type: "string",
-							},
-						},
-						Parameters: map[string]bundle.Parameter{
-							"my-param": {
-								Definition: "my-param",
-								Required:   tc.Required,
-								Destination: &bundle.Location{
-									EnvironmentVariable: "MY_PARAM",
-								},
-							},
-						},
-					}}
-
-					if tc.DefaultExists {
-						bun.Definitions["my-param"].Default = "my-param-default"
-					}
-
-					if !tc.AppliesToAction {
-						param := bun.Parameters["my-param"]
-						param.ApplyTo = []string{"non-applicable-action"}
-						bun.Parameters["my-param"] = param
-					}
-
-					args := ActionArguments{
-						Action:          action,
-						Installation:    claims.Installation{Name: "test"},
-						BundleReference: cnab.BundleReference{Definition: bun},
-					}
-					// If param is provided (via --param/--param-file)
-					// it will be attached to args
-					if tc.Provided {
-						args.Params = map[string]string{
-							"my-param": "my-param-value",
-						}
-					}
-
-					err := r.Execute(args)
-					if tc.ExpectedErr != "" {
-						require.EqualError(t, err, tc.ExpectedErr)
-					} else {
-						require.NoError(t, err)
-
-						if action != "uninstall" {
-							// Verify the updated param value on the generated claim
-							updatedClaim, err := r.claims.GetLastRun("", "test")
-							require.NoError(t, err)
-							require.Equal(t, tc.ExpectedVal, updatedClaim.Parameters["my-param"])
-						}
-					}
-				})
-			}
-		})
-	}
-}
-
-func TestRuntime_ResolveParameterSources(t *testing.T) {
-	t.Parallel()
-
-	r := NewTestRuntime(t)
-	defer r.Teardown()
-
-	r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
-	bun, err := r.ProcessBundleFromFile("bundle.json")
-	require.NoError(t, err, "ProcessBundle failed")
-
-	i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun-mysql"))
-	c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = bun.Bundle })
-	cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
-	r.TestClaims.CreateOutput(cr.NewOutput("connstr", []byte("connstr value")))
-
-	i = r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
-	c = r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = bun.Bundle })
-	cr = r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
-	r.TestClaims.CreateOutput(cr.NewOutput("bar", []byte("bar value")))
-
-	args := ActionArguments{
-		Installation: claims.Installation{Name: "mybun"},
-	}
-	got, err := r.resolveParameterSources(bun, args)
-	require.NoError(t, err, "resolveParameterSources failed")
-
-	want := secrets.Set{
-		"bar":     "bar value",
-		"connstr": "connstr value",
-	}
-	assert.Equal(t, want, got, "resolved incorrect parameter values")
-}
diff --git a/pkg/porter/cnab_test.go b/pkg/porter/cnab_test.go
index 7ab00e43..6aee1171 100644
--- a/pkg/porter/cnab_test.go
+++ b/pkg/porter/cnab_test.go
@@ -126,8 +126,8 @@ func TestSharedOptions_ParseParamSets_viaPathOrName(t *testing.T) {
 	assert.NoError(t, err)
 
 	wantParams := map[string]string{
-		"PARAM2": "VALUE2",
-		"foo":    "foo_value",
+		"my-second-param": "VALUE2",
+		"foo":             "foo_value",
 	}
 	assert.Equal(t, wantParams, opts.parsedParamSets, "resolved unexpected parameter values")
 }
diff --git a/pkg/porter/dependencies_test.go b/pkg/porter/dependencies_test.go
deleted file mode 100644
index c095259e..00000000
--- a/pkg/porter/dependencies_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package porter
-
-import (
-	"testing"
-
-	"get.porter.sh/porter/pkg/cnab"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestDependencyExecutioner_ExecuteBeforePrepare(t *testing.T) {
-	p := NewTestPorter(t)
-	defer p.Teardown()
-
-	p.TestConfig.TestContext.AddTestDirectoryFromRoot("build/testdata/bundles/mysql", "/")
-	err := p.LoadManifestFrom("/porter.yaml")
-	require.NoError(t, err)
-
-	e := newDependencyExecutioner(p.Porter, cnab.ActionInstall)
-
-	// Try to call execute without prepare
-	err = e.Execute()
-	require.Error(t, err, "execute before prepare should return an error")
-	assert.EqualError(t, err, "Prepare must be called before Execute")
-
-	// Now make sure execute passes now that we have called execute
-	opts := NewInstallOptions()
-	opts.Driver = DebugDriver
-	opts.File = "/porter.yaml"
-	err = opts.Validate([]string{}, p.Porter)
-	require.NoError(t, err, "opts validate failed")
-	err = e.Prepare(opts)
-	require.NoError(t, err, "prepare should have succeeded")
-	err = e.Execute()
-	require.NoError(t, err, "execute should not fail when we have called prepare")
-}
diff --git a/pkg/porter/lifecycle_test.go b/pkg/porter/lifecycle_test.go
index ba8aff5b..63a873b0 100644
--- a/pkg/porter/lifecycle_test.go
+++ b/pkg/porter/lifecycle_test.go
@@ -91,7 +91,7 @@ func TestPorter_BuildActionArgs(t *testing.T) {
 					},
 					Name: "MyInstallation",
 					Params: []string{
-						"PARAM1=VALUE1",
+						"my-first-param=1",
 					},
 					ParameterSets: []string{
 						"porter-hello",
@@ -113,10 +113,11 @@ func TestPorter_BuildActionArgs(t *testing.T) {
 		args, err := p.BuildActionArgs(existingInstall, opts)
 		require.NoError(t, err, "BuildActionArgs failed")
 
-		expectedParams := map[string]string{
-			"PARAM1":       "VALUE1",
-			"PARAM2":       "VALUE2",
-			"porter-debug": "true",
+		expectedParams := map[string]interface{}{
+			"my-first-param":  1,
+			"my-second-param": "VALUE2",
+			"porter-debug":    true,
+			"porter-state":    nil,
 		}
 
 		assert.Equal(t, opts.AllowAccessToDockerHost, args.AllowDockerHostAccess, "AllowDockerHostAccess not populated correctly")
diff --git a/pkg/porter/list_test.go b/pkg/porter/list_test.go
index 39e7af15..cdbf2987 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.Status.Action, "invalid last action")
+		require.Equal(t, cnab.StatusRunning, di.Status.ResultStatus, "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.Status.Action, "invalid last action")
+		require.Empty(t, di.Status.ResultStatus, "invalid last status")
 	})
 }
 
diff --git a/pkg/porter/parameters_test.go b/pkg/porter/parameters_test.go
index 9af3adf4..6f525b91 100644
--- a/pkg/porter/parameters_test.go
+++ b/pkg/porter/parameters_test.go
@@ -4,7 +4,12 @@ import (
 	"sort"
 	"testing"
 
+	"get.porter.sh/porter/pkg/claims"
+	"get.porter.sh/porter/pkg/cnab"
 	"get.porter.sh/porter/pkg/parameters"
+	"get.porter.sh/porter/pkg/secrets"
+	"github.com/cnabio/cnab-go/bundle"
+	"github.com/cnabio/cnab-go/bundle/definition"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -83,3 +88,531 @@ func TestPorter_ListParameters(t *testing.T) {
 		assert.Len(t, results, 1)
 	})
 }
+
+func Test_loadParameters_paramNotDefined(t *testing.T) {
+	t.Parallel()
+
+	r := NewTestPorter(t)
+	defer r.Teardown()
+
+	b := cnab.ExtendedBundle{bundle.Bundle{
+		Parameters: map[string]bundle.Parameter{},
+	}}
+
+	overrides := map[string]string{
+		"foo": "bar",
+	}
+
+	i := claims.Installation{}
+	_, err := r.resolveParameters(i, b, "action", overrides)
+	require.EqualError(t, err, "parameter foo not defined in bundle")
+}
+
+func Test_loadParameters_definitionNotDefined(t *testing.T) {
+	t.Parallel()
+
+	r := NewTestPorter(t)
+	defer r.Teardown()
+
+	b := cnab.ExtendedBundle{bundle.Bundle{
+		Parameters: map[string]bundle.Parameter{
+			"foo": {
+				Definition: "foo",
+			},
+		},
+	}}
+
+	overrides := map[string]string{
+		"foo": "bar",
+	}
+
+	i := claims.Installation{}
+	_, err := r.resolveParameters(i, b, "action", overrides)
+	require.EqualError(t, err, "definition foo not defined in bundle")
+}
+
+func Test_loadParameters_applyTo(t *testing.T) {
+	t.Parallel()
+
+	r := NewTestPorter(t)
+	defer r.Teardown()
+
+	// Here we set default values, but expect nil/empty
+	// values for parameters that do not apply to a given action
+	b := cnab.ExtendedBundle{bundle.Bundle{
+		Definitions: definition.Definitions{
+			"foo": &definition.Schema{
+				Type:    "string",
+				Default: "default-foo-value",
+			},
+			"bar": &definition.Schema{
+				Type:    "integer",
+				Default: "default-bar-value",
+			},
+			"true": &definition.Schema{
+				Type:    "boolean",
+				Default: "default-true-value",
+			},
+		},
+		Parameters: map[string]bundle.Parameter{
+			"foo": {
+				Definition: "foo",
+				ApplyTo: []string{
+					"action",
+				},
+			},
+			"bar": {
+				Definition: "bar",
+			},
+			"true": {
+				Definition: "true",
+				ApplyTo: []string{
+					"different-action",
+				},
+			},
+		},
+	}}
+
+	overrides := map[string]string{
+		"foo":  "FOO",
+		"bar":  "456",
+		"true": "false",
+	}
+
+	i := claims.Installation{}
+	params, err := r.resolveParameters(i, b, "action", overrides)
+	require.NoError(t, err)
+
+	require.Equal(t, "FOO", params["foo"], "expected param 'foo' to be updated")
+	require.Equal(t, 456, params["bar"], "expected param 'bar' to be updated")
+	require.Equal(t, nil, params["true"], "expected param 'true' to be nil as it does not apply")
+}
+
+func Test_loadParameters_applyToBundleDefaults(t *testing.T) {
+	t.Parallel()
+
+	r := NewTestPorter(t)
+	defer r.Teardown()
+
+	b := cnab.ExtendedBundle{bundle.Bundle{
+		Definitions: definition.Definitions{
+			"foo": &definition.Schema{
+				Type:    "string",
+				Default: "foo-default",
+			},
+		},
+		Parameters: map[string]bundle.Parameter{
+			"foo": {
+				Definition: "foo",
+				ApplyTo: []string{
+					"different-action",
+				},
+			},
+		},
+	}}
+
+	i := claims.Installation{}
+	params, err := r.resolveParameters(i, b, "action", nil)
+	require.NoError(t, err)
+
+	require.Equal(t, nil, params["foo"], "expected param 'foo' to be nil, regardless of the bundle default, as it does not apply")
+}
+
+func Test_loadParameters_requiredButDoesNotApply(t *testing.T) {
+	t.Parallel()
+
+	r := NewTestPorter(t)
+	defer r.Teardown()
+
+	b := cnab.ExtendedBundle{bundle.Bundle{
+		Definitions: definition.Definitions{
+			"foo": &definition.Schema{
+				Type: "string",
+			},
+		},
+		Parameters: map[string]bundle.Parameter{
+			"foo": {
+				Definition: "foo",
+				ApplyTo: []string{
+					"different-action",
+				},
+				Required: true,
+			},
+		},
+	}}
+
+	i := claims.Installation{}
+	params, err := r.resolveParameters(i, b, "action", nil)
+	require.NoError(t, err)
+
+	require.Equal(t, nil, params["foo"], "expected param 'foo' to be nil, regardless of claim value, as it does not apply")
+}
+
+func Test_loadParameters_fileParameter(t *testing.T) {
+	t.Parallel()
+
+	r := NewTestPorter(t)
+	defer r.Teardown()
+
+	r.TestConfig.TestContext.AddTestFile("testdata/file-param", "/path/to/file")
+
+	b := cnab.ExtendedBundle{bundle.Bundle{
+		RequiredExtensions: []string{
+			cnab.FileParameterExtensionKey,
+		},
+		Definitions: definition.Definitions{
+			"foo": &definition.Schema{
+				Type:            "string",
+				ContentEncoding: "base64",
+			},
+		},
+		Parameters: map[string]bundle.Parameter{
+			"foo": {
+				Definition: "foo",
+				Required:   true,
+				Destination: &bundle.Location{
+					Path: "/tmp/foo",
+				},
+			},
+		},
+	}}
+
+	overrides := map[string]string{
+		"foo": "/path/to/file",
+	}
+
+	i := claims.Installation{}
+	params, err := r.resolveParameters(i, b, "action", overrides)
+	require.NoError(t, err)
+
+	require.Equal(t, "SGVsbG8gV29ybGQh", params["foo"], "expected param 'foo' to be the base64-encoded file contents")
+}
+
+func Test_loadParameters_ParameterSourcePrecedence(t *testing.T) {
+	t.Parallel()
+
+	t.Run("nothing present, use default", func(t *testing.T) {
+		t.Parallel()
+
+		r := NewTestPorter(t)
+		defer r.Teardown()
+
+		r.TestParameters.AddTestParameters("testdata/paramset.json")
+		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
+
+		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
+		b, err := cnab.LoadBundle(r.Context, "bundle.json")
+		require.NoError(t, err, "ProcessBundle failed")
+
+		i := claims.Installation{Name: "mybun"}
+		params, err := r.resolveParameters(i, b, cnab.ActionUpgrade, nil)
+		require.NoError(t, err)
+		assert.Equal(t, "foo_default", params["foo"],
+			"expected param 'foo' to have default value")
+	})
+
+	t.Run("only override present", func(t *testing.T) {
+		t.Parallel()
+
+		r := NewTestPorter(t)
+		defer r.Teardown()
+
+		r.TestParameters.AddTestParameters("testdata/paramset.json")
+		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
+
+		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
+		b, err := cnab.LoadBundle(r.Context, "bundle.json")
+		require.NoError(t, err, "ProcessBundle failed")
+
+		overrides := map[string]string{
+			"foo": "foo_override",
+		}
+
+		i := claims.Installation{Name: "mybun"}
+		params, err := r.resolveParameters(i, b, cnab.ActionUpgrade, overrides)
+		require.NoError(t, err)
+		assert.Equal(t, "foo_override", params["foo"],
+			"expected param 'foo' to have override value")
+	})
+
+	t.Run("only parameter source present", func(t *testing.T) {
+		t.Parallel()
+
+		r := NewTestPorter(t)
+		defer r.Teardown()
+
+		r.TestParameters.AddTestParameters("testdata/paramset.json")
+		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
+
+		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
+		b, err := cnab.LoadBundle(r.Context, "bundle.json")
+		require.NoError(t, err, "ProcessBundle failed")
+
+		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
+		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = b.Bundle })
+		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
+		r.TestClaims.CreateOutput(cr.NewOutput("foo", []byte("foo_source")))
+
+		params, err := r.resolveParameters(i, b, cnab.ActionUpgrade, nil)
+		require.NoError(t, err)
+		assert.Equal(t, "foo_source", params["foo"],
+			"expected param 'foo' to have parameter source value")
+	})
+
+	t.Run("override > parameter source", func(t *testing.T) {
+		t.Parallel()
+
+		r := NewTestPorter(t)
+		defer r.Teardown()
+
+		r.TestParameters.AddTestParameters("testdata/paramset.json")
+		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
+
+		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
+		b, err := cnab.LoadBundle(r.Context, "bundle.json")
+		require.NoError(t, err, "ProcessBundle failed")
+
+		overrides := map[string]string{
+			"foo": "foo_override",
+		}
+
+		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
+		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall))
+		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
+		r.TestClaims.CreateOutput(cr.NewOutput("foo", []byte("foo_source")))
+
+		params, err := r.resolveParameters(i, b, cnab.ActionUpgrade, overrides)
+		require.NoError(t, err)
+		assert.Equal(t, "foo_override", params["foo"],
+			"expected param 'foo' to have parameter override value")
+	})
+
+	t.Run("dependency output without type", func(t *testing.T) {
+		t.Parallel()
+
+		r := NewTestPorter(t)
+		defer r.Teardown()
+
+		r.TestParameters.AddTestParameters("testdata/paramset.json")
+		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
+
+		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
+		b, err := cnab.LoadBundle(r.Context, "bundle.json")
+		require.NoError(t, err, "ProcessBundle failed")
+
+		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun-mysql"))
+		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = b.Bundle })
+		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
+		r.TestClaims.CreateOutput(cr.NewOutput("connstr", []byte("connstr value")))
+
+		params, err := r.resolveParameters(i, b, cnab.ActionUpgrade, nil)
+		require.NoError(t, err)
+		assert.Equal(t, "connstr value", params["connstr"],
+			"expected param 'connstr' to have parameter value from the untyped dependency output")
+	})
+
+	t.Run("merge parameter values", func(t *testing.T) {
+		t.Parallel()
+
+		r := NewTestPorter(t)
+		defer r.Teardown()
+
+		r.TestParameters.AddTestParameters("testdata/paramset.json")
+		r.TestParameters.TestSecrets.AddSecret("foo_secret", "foo_set")
+
+		r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
+		b, err := cnab.LoadBundle(r.Context, "bundle.json")
+		require.NoError(t, err, "ProcessBundle failed")
+
+		// foo is set by a user
+		// bar is set by a parameter source
+		// baz is set by the bundle default
+		i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
+		c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall))
+		cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
+		r.TestClaims.CreateOutput(cr.NewOutput("foo", []byte("foo_source")))
+		r.TestClaims.CreateOutput(cr.NewOutput("bar", []byte("bar_source")))
+		r.TestClaims.CreateOutput(cr.NewOutput("baz", []byte("baz_source")))
+
+		overrides := map[string]string{"foo": "foo_override"}
+		params, err := r.resolveParameters(i, b, cnab.ActionUpgrade, overrides)
+		require.NoError(t, err)
+		assert.Equal(t, "foo_override", params["foo"],
+			"expected param 'foo' to have parameter override value")
+		assert.Equal(t, "bar_source", params["bar"],
+			"expected param 'bar' to have parameter source value")
+		assert.Equal(t, "baz_default", params["baz"],
+			"expected param 'baz' to have bundle default value")
+	})
+}
+
+// This is intended to cover the matrix of cases around parameter value resolution.
+// It exercises the matrix for all supported actions.
+func Test_Paramapalooza(t *testing.T) {
+	testcases := []struct {
+		Name            string
+		Required        bool
+		Provided        bool
+		DefaultExists   bool
+		AppliesToAction bool
+		ExpectedVal     interface{}
+		ExpectedErr     string
+	}{
+		// Are you ready to enter the Matrix?
+		{Name: "required, provided, default exists, applies to action",
+			Required: true, Provided: true, DefaultExists: true, AppliesToAction: true, ExpectedVal: "my-param-value",
+		},
+		{"required, provided, default exists, does not apply to action",
+			true, true, true, false, nil, "",
+		},
+		{"required, provided, default does not exist, applies to action",
+			true, true, false, true, "my-param-value", "",
+		},
+		{"required, provided, default does not exist, does not apply to action",
+			true, true, false, false, nil, "",
+		},
+		// As of writing, bundle.ValuesOrDefaults in cnab-go requires a specific override
+		// be provided if applicable to an action.
+		// Otherwise, it errors out and does not look up/use default in this case.
+		{"required, not provided, default exists, applies to action",
+			true, false, true, true, nil, "parameter \"my-param\" is required",
+		},
+		{"required, not provided, default exists, does not apply to action",
+			true, false, true, false, nil, "",
+		},
+		{"required, not provided, default does not exist, applies to action",
+			true, false, false, true, nil, "parameter \"my-param\" is required",
+		},
+		{"required, not provided, default does not exist, does not apply to action",
+			true, false, false, false, nil, "",
+		},
+		{"not required, provided, default exists, applies to action",
+			false, true, true, true, "my-param-value", "",
+		},
+		{"not required, provided, default exists, does not apply to action",
+			false, true, true, false, nil, "",
+		},
+		{"not required, provided, default does not exist, applies to action",
+			false, true, false, true, "my-param-value", "",
+		},
+		{"not required, provided, default does not exist, does not apply to action",
+			false, true, false, false, nil, "",
+		},
+		{"not required, not provided, default exists, applies to action",
+			false, false, true, true, "my-param-default", "",
+		},
+		{"not required, not provided, default exists, does not apply to action",
+			false, false, true, false, nil, "",
+		},
+		{"not required, not provided, default does not exist, applies to action",
+			false, false, false, true, nil, "",
+		},
+		{"not required, not provided, default does not exist, does not apply to action",
+			false, false, false, false, nil, "",
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.Name, func(t *testing.T) {
+			actions := []string{"install", "upgrade", "uninstall", "zombies"}
+			for _, action := range actions {
+				t.Run(action, func(t *testing.T) {
+					t.Parallel()
+					tc := tc
+
+					r := NewTestPorter(t)
+					defer r.Teardown()
+
+					bun := cnab.ExtendedBundle{bundle.Bundle{
+						Name:          "mybuns",
+						Version:       "1.0.0",
+						SchemaVersion: "v1.0.0",
+						Actions: map[string]bundle.Action{
+							"zombies": {
+								Modifies: true,
+							},
+						},
+						InvocationImages: []bundle.InvocationImage{
+							{
+								BaseImage: bundle.BaseImage{
+									Image:     "mybuns:latest",
+									ImageType: "docker",
+								},
+							},
+						},
+						Definitions: definition.Definitions{
+							"my-param": &definition.Schema{
+								Type: "string",
+							},
+						},
+						Parameters: map[string]bundle.Parameter{
+							"my-param": {
+								Definition: "my-param",
+								Required:   tc.Required,
+								Destination: &bundle.Location{
+									EnvironmentVariable: "MY_PARAM",
+								},
+							},
+						},
+					}}
+
+					if tc.DefaultExists {
+						bun.Definitions["my-param"].Default = "my-param-default"
+					}
+
+					if !tc.AppliesToAction {
+						param := bun.Parameters["my-param"]
+						param.ApplyTo = []string{"non-applicable-action"}
+						bun.Parameters["my-param"] = param
+					}
+
+					i := claims.Installation{Name: "test"}
+					overrides := map[string]string{}
+					// If param is provided (via --param/--param-file)
+					// it will be attached to args
+					if tc.Provided {
+						overrides["my-param"] = "my-param-value"
+					}
+
+					resolvedParams, err := r.resolveParameters(i, bun, action, overrides)
+					if tc.ExpectedErr != "" {
+						require.EqualError(t, err, tc.ExpectedErr)
+					} else {
+						require.NoError(t, err)
+						assert.Equal(t, tc.ExpectedVal, resolvedParams["my-param"])
+					}
+				})
+			}
+		})
+	}
+}
+
+func TestRuntime_ResolveParameterSources(t *testing.T) {
+	t.Parallel()
+
+	r := NewTestPorter(t)
+	defer r.Teardown()
+
+	r.TestConfig.TestContext.AddTestFile("testdata/bundle-with-param-sources.json", "bundle.json")
+	bun, err := cnab.LoadBundle(r.Context, "bundle.json")
+	require.NoError(t, err, "ProcessBundle failed")
+
+	i := r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun-mysql"))
+	c := r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = bun.Bundle })
+	cr := r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
+	r.TestClaims.CreateOutput(cr.NewOutput("connstr", []byte("connstr value")))
+
+	i = r.TestClaims.CreateInstallation(claims.NewInstallation("", "mybun"))
+	c = r.TestClaims.CreateRun(i.NewRun(cnab.ActionInstall), func(r *claims.Run) { r.Bundle = bun.Bundle })
+	cr = r.TestClaims.CreateResult(c.NewResult(cnab.StatusSucceeded))
+	r.TestClaims.CreateOutput(cr.NewOutput("bar", []byte("bar value")))
+
+	got, err := r.resolveParameterSources(bun, i)
+	require.NoError(t, err, "resolveParameterSources failed")
+
+	want := secrets.Set{
+		"bar":     "bar value",
+		"connstr": "connstr value",
+	}
+	assert.Equal(t, want, got, "resolved incorrect parameter values")
+}
diff --git a/pkg/porter/show_test.go b/pkg/porter/show_test.go
index 2fb74cd6..80750c43 100644
--- a/pkg/porter/show_test.go
+++ b/pkg/porter/show_test.go
@@ -85,12 +85,14 @@ func TestPorter_ShowBundle(t *testing.T) {
 					"io.cnab/app":        "wordpress",
 					"io.cnab/appVersion": "v1.2.3",
 				}
-				i.Parameters = map[string]interface{}{"token": "top-secret", "logLevel": 3}
+				i.Parameters = map[string]interface{}{"logLevel": 3}
+				i.ParameterSets = []string{"dev-env"}
 			})
 			run := 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"
+				r.Parameters = map[string]interface{}{"token": "top-secret", "logLevel": 3}
 			})
 			result := p.TestClaims.CreateResult(run.NewResult(cnab.StatusSucceeded), p.TestClaims.SetMutableResultValues)
 			i.ApplyResult(run, result)
diff --git a/pkg/cnab/provider/testdata/bundle-with-param-sources.json b/pkg/porter/testdata/bundle-with-param-sources.json
similarity index 100%
rename from pkg/cnab/provider/testdata/bundle-with-param-sources.json
rename to pkg/porter/testdata/bundle-with-param-sources.json
diff --git a/pkg/cnab/provider/testdata/file-param b/pkg/porter/testdata/file-param
similarity index 100%
rename from pkg/cnab/provider/testdata/file-param
rename to pkg/porter/testdata/file-param
diff --git a/pkg/porter/testdata/paramset2.json b/pkg/porter/testdata/paramset2.json
index 84f7078f..9e9bf3c7 100644
--- a/pkg/porter/testdata/paramset2.json
+++ b/pkg/porter/testdata/paramset2.json
@@ -4,7 +4,7 @@
   "modified": "1983-04-18T01:02:03.000000004Z",
   "parameters": [
     {
-      "name": "PARAM2",
+      "name": "my-second-param",
       "source": {
         "secret": "PARAM2_SECRET"
       }
diff --git a/pkg/porter/testdata/show/expected-output.json b/pkg/porter/testdata/show/expected-output.json
index 76ba60dc..33db796e 100644
--- a/pkg/porter/testdata/show/expected-output.json
+++ b/pkg/porter/testdata/show/expected-output.json
@@ -11,9 +11,11 @@
     "io.cnab/appVersion": "v1.2.3"
   },
   "parameters": {
-    "logLevel": 3,
-    "token": "top-secret"
+    "logLevel": 3
   },
+  "parameterSets": [
+    "dev-env"
+  ],
   "status": {
     "runId": "1",
     "action": "upgrade",
@@ -23,5 +25,21 @@
     "bundleReference": "getporter/wordpress:v0.1.0",
     "bundleVersion": "0.1.0",
     "bundleDigest": "sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9"
+  },
+  "_calculated": {
+    "resolvedParameters": [
+      {
+        "name": "logLevel",
+        "type": "integer",
+        "sensitive": false,
+        "value": 3
+      },
+      {
+        "name": "token",
+        "type": "unknown",
+        "sensitive": false,
+        "value": "top-secret"
+      }
+    ]
   }
 }
diff --git a/pkg/porter/testdata/show/expected-output.txt b/pkg/porter/testdata/show/expected-output.txt
index 2d194567..e1c2b21c 100644
--- a/pkg/porter/testdata/show/expected-output.txt
+++ b/pkg/porter/testdata/show/expected-output.txt
@@ -18,6 +18,9 @@ Parameters:
   logLevel  integer  3           
   token     unknown  top-secret  
 
+Parameter Sets:
+  - dev-env
+
 Status:
   Reference: getporter/wordpress:v0.1.0
   Version: 0.1.0
diff --git a/pkg/porter/testdata/show/expected-output.yaml b/pkg/porter/testdata/show/expected-output.yaml
index 72d64c98..8b0104fc 100644
--- a/pkg/porter/testdata/show/expected-output.yaml
+++ b/pkg/porter/testdata/show/expected-output.yaml
@@ -10,7 +10,8 @@ labels:
   io.cnab/appVersion: v1.2.3
 parameters:
   logLevel: 3
-  token: top-secret
+parameterSets:
+  - dev-env
 status:
   runId: "1"
   action: upgrade
@@ -20,3 +21,13 @@ status:
   bundleReference: getporter/wordpress:v0.1.0
   bundleVersion: 0.1.0
   bundleDigest: sha256:88d68ef0bdb9cedc6da3a8e341a33e5d2f8bb19d0cf7ec3f1060d3f9eb73cae9
+_calculated:
+  resolvedparameters:
+    - name: logLevel
+      type: integer
+      sensitive: false
+      value: 3
+    - name: token
+      type: unknown
+      sensitive: false
+      value: top-secret
diff --git a/pkg/porter/testdata/show/no-reference-expected-output.txt b/pkg/porter/testdata/show/no-reference-expected-output.txt
index ab98ee96..49e47c3f 100644
--- a/pkg/porter/testdata/show/no-reference-expected-output.txt
+++ b/pkg/porter/testdata/show/no-reference-expected-output.txt
@@ -14,6 +14,9 @@ Parameters:
   logLevel  integer  3           
   token     unknown  top-secret  
 
+Parameter Sets:
+  - dev-env
+
 Status:
   Reference: 
   Version: 0.1.0
diff --git a/pkg/storage/plugins/testplugin/plugin.go b/pkg/storage/plugins/testplugin/plugin.go
index 1ef66d6c..fa235f1d 100644
--- a/pkg/storage/plugins/testplugin/plugin.go
+++ b/pkg/storage/plugins/testplugin/plugin.go
@@ -57,7 +57,8 @@ func (s *TestStoragePlugin) Setup() error {
 
 func (s *TestStoragePlugin) useDevDatabase() error {
 	cfg := mongodb.PluginConfig{
-		URL: fmt.Sprintf("mongodb://localhost:27017/%s?connect=direct", s.database),
+		URL:     fmt.Sprintf("mongodb://localhost:27017/%s?connect=direct", s.database),
+		Timeout: 10,
 	}
 	devMongo := mongodb.NewStore(s.tc.Context, cfg)
 	err := devMongo.Connect()
diff --git a/tests/integration/dependencies_test.go b/tests/integration/dependencies_test.go
index f98220e3..085d5d6e 100644
--- a/tests/integration/dependencies_test.go
+++ b/tests/integration/dependencies_test.go
@@ -22,7 +22,7 @@ func TestDependenciesLifecycle(t *testing.T) {
 	p := porter.NewTestPorter(t)
 	defer p.Teardown()
 	p.SetupIntegrationTest()
-	p.Debug = false
+	p.Debug = true
 
 	namespace := installWordpressBundle(p)
 	defer cleanupWordpressBundle(p, namespace)