Repository: getporter/porter
Pull request #1345 (2020-10-27 19:59:07)

Update unit tests to use cached bundle testdata
# What does this change
When I was helping with #1329 I realized that some of our unit tests were hitting a live docker registry, not using the cached bundles. Also we had stale entries in the cache testdata for an older version of our test bundles.

This change updates our unit tests to use the test registry by default, updates the test bundles and their cache.

# What issue does it fix
N/A

# Notes for the reviewer


# Checklist
- [ ] Unit Tests
- [ ] Documentation
- [ ] Schema (porter.yaml)


carolynvs: /azp run porter
azure-pipelines[bot]: <samp>
Azure Pipelines successfully started running 1 pipeline(s).<br>

</samp>

Patch:
diff --git a/examples/hello/.cnab/bundle.json b/examples/hello/.cnab/bundle.json
new file mode 100644
index 00000000..8078a8fd
--- /dev/null
+++ b/examples/hello/.cnab/bundle.json
@@ -0,0 +1,1 @@
+{"custom":{"sh.porter":{"manifest":"IyBUaGlzIGlzIHRoZSBjb25maWd1cmF0aW9uIGZvciBQb3J0ZXIKIyBZb3UgbXVzdCBkZWZpbmUgc3RlcHMgZm9yIGVhY2ggYWN0aW9uLCBidXQgdGhlIHJlc3QgaXMgb3B0aW9uYWwKIyBTZWUgaHR0cHM6Ly9wb3J0ZXIuc2gvYXV0aG9yLWJ1bmRsZXMgZm9yIGRvY3VtZW50YXRpb24gb24gaG93IHRvIGNvbmZpZ3VyZSB5b3VyIGJ1bmRsZQojIFVuY29tbWVudCBvdXQgdGhlIHNlY3Rpb25zIGJlbG93IHRvIHRha2UgZnVsbCBhZHZhbnRhZ2Ugb2Ygd2hhdCBQb3J0ZXIgY2FuIGRvIQoKbmFtZTogSEVMTE8KdmVyc2lvbjogMC4xLjAKZGVzY3JpcHRpb246ICJBbiBleGFtcGxlIFBvcnRlciBjb25maWd1cmF0aW9uIgojIFRPRE86IHVwZGF0ZSB0aGUgcmVnaXN0cnkgdG8geW91ciBvd24sIGUuZy4gbXlyZWdpc3RyeS9wb3J0ZXItaGVsbG8KdGFnOiBnZXRwb3J0ZXIvcG9ydGVyLWhlbGxvCgojIElmIHlvdSB3YW50IHRvIGN1c3RvbWl6ZSB0aGUgRG9ja2VyZmlsZSBpbiB1c2UsIHVuY29tbWVudCB0aGUgbGluZSBiZWxvdyBhbmQgdXBkYXRlIHRoZSByZWZlcmVuY2VkIGZpbGUuIAojIFNlZSBodHRwczovL3BvcnRlci5zaC9jdXN0b20tZG9ja2VyZmlsZS8KI2RvY2tlcmZpbGU6IERvY2tlcmZpbGUudG1wbAoKbWl4aW5zOgogIC0gZXhlYwoKaW5zdGFsbDoKICAtIGV4ZWM6CiAgICAgIGRlc2NyaXB0aW9uOiAiSW5zdGFsbCBIZWxsbyBXb3JsZCIKICAgICAgY29tbWFuZDogLi9oZWxwZXJzLnNoCiAgICAgIGFyZ3VtZW50czoKICAgICAgICAtIGluc3RhbGwKCnVwZ3JhZGU6CiAgLSBleGVjOgogICAgICBkZXNjcmlwdGlvbjogIldvcmxkIDIuMCIKICAgICAgY29tbWFuZDogLi9oZWxwZXJzLnNoCiAgICAgIGFyZ3VtZW50czoKICAgICAgICAtIHVwZ3JhZGUKCnVuaW5zdGFsbDoKICAtIGV4ZWM6CiAgICAgIGRlc2NyaXB0aW9uOiAiVW5pbnN0YWxsIEhlbGxvIFdvcmxkIgogICAgICBjb21tYW5kOiAuL2hlbHBlcnMuc2gKICAgICAgYXJndW1lbnRzOgogICAgICAgIC0gdW5pbnN0YWxsCgojIEJlbG93IGlzIGFuIGV4YW1wbGUgb2YgaG93IHRvIGRlZmluZSBjcmVkZW50aWFscwojIFNlZSBodHRwczovL3BvcnRlci5zaC9hdXRob3ItYnVuZGxlcy8jY3JlZGVudGlhbHMKI2NyZWRlbnRpYWxzOgojICAtIG5hbWU6IGt1YmVjb25maWcKIyAgICBwYXRoOiAvcm9vdC8ua3ViZS9jb25maWcKIyAgLSBuYW1lOiB1c2VybmFtZQojICAgIGVudjogVVNFUk5BTUUKCiMgQmVsb3cgaXMgYW4gZXhhbXBsZSBvZiBob3cgdG8gZGVmaW5lIHBhcmFtZXRlcnMKIyBTZWUgaHR0cHM6Ly9wb3J0ZXIuc2gvYXV0aG9yLWJ1bmRsZXMvI3BhcmFtZXRlcnMKI3BhcmFtZXRlcnM6CiMgIC0gbmFtZTogbXlzcWxfdXNlcgojICAgIHR5cGU6IHN0cmluZwojICAgIGRlZmF1bHQ6IHdvcmRwcmVzcwo=","manifestDigest":"233efd874264b49c3e65fc82259966a47eb4a693f7f0933c3a2d778620b462a7","mixins":{"exec":{}}}},"definitions":{"porter-debug-parameter":{"$comment":"porter-internal","$id":"https://porter.sh/generated-bundle/#porter-debug","default":false,"description":"Print debug information from Porter when executing the bundle","type":"boolean"}},"description":"An example Porter configuration","invocationImages":[{"image":"getporter/porter-hello-installer:v0.1.0","imageType":"docker"}],"name":"HELLO","parameters":{"porter-debug":{"definition":"porter-debug-parameter","description":"Print debug information from Porter when executing the bundle","destination":{"env":"PORTER_DEBUG"}}},"schemaVersion":"v1.0.0","version":"0.1.0"}
\ No newline at end of file
diff --git a/examples/hello/.dockerignore b/examples/hello/.dockerignore
new file mode 100644
index 00000000..2919244c
--- /dev/null
+++ b/examples/hello/.dockerignore
@@ -0,0 +1,4 @@
+# See https://docs.docker.com/engine/reference/builder/#dockerignore-file
+# Put files here that you don't want copied into your bundle's invocation image
+.gitignore
+Dockerfile.tmpl
diff --git a/examples/hello/.gitignore b/examples/hello/.gitignore
new file mode 100644
index 00000000..35e26477
--- /dev/null
+++ b/examples/hello/.gitignore
@@ -0,0 +1,2 @@
+Dockerfile
+.cnab/
diff --git a/examples/hello/Dockerfile.tmpl b/examples/hello/Dockerfile.tmpl
new file mode 100644
index 00000000..58356b38
--- /dev/null
+++ b/examples/hello/Dockerfile.tmpl
@@ -0,0 +1,21 @@
+FROM debian:stretch
+
+ARG BUNDLE_DIR
+
+RUN apt-get update && apt-get install -y ca-certificates
+
+# This is a template Dockerfile for the bundle's invocation image
+# You can customize it to use different base images, install tools and copy configuration files.
+#
+# Porter will use it as a template and append lines to it for the mixins
+# and to set the CMD appropriately for the CNAB specification.
+#
+# Add the following line to porter.yaml to instruct Porter to use this template
+# dockerfile: Dockerfile.tmpl
+
+# You can control where the mixin's Dockerfile lines are inserted into this file by moving "# PORTER_MIXINS" line
+# another location in this file. If you remove that line, the mixins generated content is appended to this file.
+# PORTER_MIXINS
+
+# Use the BUNDLE_DIR build argument to copy files into the bundle
+COPY . $BUNDLE_DIR
diff --git a/examples/hello/README.md b/examples/hello/README.md
new file mode 100644
index 00000000..3aca73ec
--- /dev/null
+++ b/examples/hello/README.md
@@ -0,0 +1,51 @@
+# My First Bundle
+
+This is an empty bundle that porter has created to get you started!
+
+# Contents
+
+## porter.yaml
+
+This is the porter manifest. See https://porter.sh/author-bundles/ for 
+details on every field and how to configure your bundle. This is a required
+file.
+
+## helpers.sh
+
+This is a bash script where you can place helper functions that you can call
+from your porter.yaml file.
+
+## README.md
+
+This explains the files created by `porter create`. It is not used by porter and
+can be deleted.
+
+## Dockerfile.tmpl
+
+This is a template Dockerfile for the bundle's invocation image. You can
+customize it to use different base images, install tools and copy configuration
+files. Porter will use it as a template and append lines to it for the mixin and to set
+the CMD appropriately for the CNAB specification. You can delete this file if you don't
+need it.
+
+Add the following line to **porter.yaml** to enable the Dockerfile template:
+
+```yaml
+dockerfile: Dockerfile.tmpl
+```
+
+By default, the Dockerfile template is disabled and Porter automatically copies
+all of the files in the current directory into the bundle's invocation image. When
+you use a custom Dockerfile template, you must manually copy files into the bundle
+using COPY statements in the Dockerfile template.
+
+## .gitignore
+
+This is a default file that we provide to help remind you which files are
+generated by Porter, and shouldn't be committed to source control. You can
+delete it if you don't need it.
+
+## .dockerignore
+
+This is a default file that controls which files are copied into the bundle's
+invocation image by default. You can delete it if you don't need it.
\ No newline at end of file
diff --git a/examples/hello/helpers.sh b/examples/hello/helpers.sh
new file mode 100755
index 00000000..788fee55
--- /dev/null
+++ b/examples/hello/helpers.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+install() {
+  echo Hello World
+}
+
+upgrade() {
+  echo World 2.0
+}
+
+uninstall() {
+  echo Goodbye World
+}
+
+# Call the requested function and pass the arguments as-is
+"$@"
diff --git a/examples/hello/porter.yaml b/examples/hello/porter.yaml
new file mode 100644
index 00000000..fdb48846
--- /dev/null
+++ b/examples/hello/porter.yaml
@@ -0,0 +1,53 @@
+# This is the configuration for Porter
+# You must define steps for each action, but the rest is optional
+# See https://porter.sh/author-bundles for documentation on how to configure your bundle
+# Uncomment out the sections below to take full advantage of what Porter can do!
+
+name: HELLO
+version: 0.1.0
+description: "An example Porter configuration"
+# TODO: update the registry to your own, e.g. myregistry/porter-hello
+tag: getporter/porter-hello
+
+# If you want to customize the Dockerfile in use, uncomment the line below and update the referenced file. 
+# See https://porter.sh/custom-dockerfile/
+#dockerfile: Dockerfile.tmpl
+
+mixins:
+  - exec
+
+install:
+  - exec:
+      description: "Install Hello World"
+      command: ./helpers.sh
+      arguments:
+        - install
+
+upgrade:
+  - exec:
+      description: "World 2.0"
+      command: ./helpers.sh
+      arguments:
+        - upgrade
+
+uninstall:
+  - exec:
+      description: "Uninstall Hello World"
+      command: ./helpers.sh
+      arguments:
+        - uninstall
+
+# Below is an example of how to define credentials
+# See https://porter.sh/author-bundles/#credentials
+#credentials:
+#  - name: kubeconfig
+#    path: /root/.kube/config
+#  - name: username
+#    env: USERNAME
+
+# Below is an example of how to define parameters
+# See https://porter.sh/author-bundles/#parameters
+#parameters:
+#  - name: mysql_user
+#    type: string
+#    default: wordpress
diff --git a/pkg/context/helpers.go b/pkg/context/helpers.go
index fe1ef714..7e4917b3 100644
--- a/pkg/context/helpers.go
+++ b/pkg/context/helpers.go
@@ -93,8 +93,6 @@ func (c *TestContext) Cleanup() {
 }
 
 func (c *TestContext) AddTestFile(src, dest string) []byte {
-	c.T.Helper()
-
 	data, err := ioutil.ReadFile(src)
 	if err != nil {
 		c.T.Fatal(err)
@@ -113,8 +111,6 @@ func (c *TestContext) AddTestFileContents(file []byte, dest string) error {
 }
 
 func (c *TestContext) AddTestDirectory(srcDir, destDir string) {
-	c.T.Helper()
-
 	err := filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
 		if err != nil {
 			return err
@@ -141,8 +137,6 @@ func (c *TestContext) AddTestDirectory(srcDir, destDir string) {
 }
 
 func (c *TestContext) AddTestDriver(src, name string) string {
-	c.T.Helper()
-
 	data, err := ioutil.ReadFile(src)
 	if err != nil {
 		c.T.Fatal(err)
diff --git a/pkg/porter/helpers.go b/pkg/porter/helpers.go
index 324c5ee7..e74a4fc8 100644
--- a/pkg/porter/helpers.go
+++ b/pkg/porter/helpers.go
@@ -10,6 +10,7 @@ import (
 
 	"get.porter.sh/porter/pkg/cache"
 	"get.porter.sh/porter/pkg/claims"
+	cnabtooci "get.porter.sh/porter/pkg/cnab/cnab-to-oci"
 	cnabprovider "get.porter.sh/porter/pkg/cnab/provider"
 	"get.porter.sh/porter/pkg/config"
 	"get.porter.sh/porter/pkg/credentials"
@@ -30,6 +31,7 @@ type TestPorter struct {
 	TestCredentials *credentials.TestCredentialProvider
 	TestParameters  *parameters.TestParameterProvider
 	TestCache       *cache.TestCache
+	TestRegistry    *cnabtooci.TestRegistry
 
 	// original directory where the test was being executed
 	TestDir string
@@ -45,6 +47,7 @@ func NewTestPorter(t *testing.T) *TestPorter {
 	testParameters := parameters.NewTestParameterProvider(t, tc)
 	testCache := cache.NewTestCache(cache.New(tc.Config))
 	testClaims := claims.NewTestClaimProvider(t)
+	testRegistry := cnabtooci.NewTestRegistry()
 
 	p := New()
 	p.Config = tc.Config
@@ -56,6 +59,7 @@ func NewTestPorter(t *testing.T) *TestPorter {
 	p.Credentials = testCredentials
 	p.Parameters = testParameters
 	p.CNAB = cnabprovider.NewTestRuntimeWithConfig(tc, testClaims, testCredentials, testParameters)
+	p.Registry = testRegistry
 
 	return &TestPorter{
 		Porter:          p,
@@ -64,6 +68,7 @@ func NewTestPorter(t *testing.T) *TestPorter {
 		TestCredentials: &testCredentials,
 		TestParameters:  &testParameters,
 		TestCache:       testCache,
+		TestRegistry:    testRegistry,
 	}
 }
 


Test patch:
diff --git a/build/testdata/bundles/mysql/.cnab/bundle.json b/build/testdata/bundles/mysql/.cnab/bundle.json
index 01196bc9..1a04e57f 100644
--- a/build/testdata/bundles/mysql/.cnab/bundle.json
+++ b/build/testdata/bundles/mysql/.cnab/bundle.json
@@ -1,1 +1,1 @@
-{"actions":{"ping":{"description":"ping","modifies":true}},"credentials":{"kubeconfig":{"path":"/root/.kube/config","required":true}},"custom":{"sh.porter":{"manifest":"bWl4aW5zOgotIGV4ZWMKLSBoZWxtCgpuYW1lOiBteXNxbAp2ZXJzaW9uOiAwLjEuMwpyZWdpc3RyeTogbG9jYWxob3N0OjUwMDAKCmNyZWRlbnRpYWxzOgotIG5hbWU6IGt1YmVjb25maWcKICBwYXRoOiAvcm9vdC8ua3ViZS9jb25maWcKCnBhcmFtZXRlcnM6Ci0gbmFtZTogZGF0YWJhc2UtbmFtZQogIHR5cGU6IHN0cmluZwogIGRlZmF1bHQ6IG15ZGIKICBlbnY6IERBVEFCQVNFX05BTUUKLSBuYW1lOiBteXNxbC11c2VyCiAgdHlwZTogc3RyaW5nCiAgZGVmYXVsdDogbXlzcWwtYWRtaW4KICBlbnY6IE1ZU1FMX1VTRVIKLSBuYW1lOiBuYW1lc3BhY2UKICB0eXBlOiBzdHJpbmcKICBkZWZhdWx0OiAnJwotIG5hbWU6IG15c3FsLW5hbWUKICB0eXBlOiBzdHJpbmcKICBkZWZhdWx0OiBwb3J0ZXItY2ktbXlzcWwKCmluc3RhbGw6Ci0gaGVsbToKICAgIGRlc2NyaXB0aW9uOiAiSW5zdGFsbCBNeVNRTCIKICAgIG5hbWU6ICJ7eyBidW5kbGUucGFyYW1ldGVycy5teXNxbC1uYW1lIH19IgogICAgY2hhcnQ6IHN0YWJsZS9teXNxbAogICAgdmVyc2lvbjogMS42LjIKICAgIG5hbWVzcGFjZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm5hbWVzcGFjZSB9fSIKICAgIHJlcGxhY2U6IHRydWUKICAgIHNldDoKICAgICAgbXlzcWxEYXRhYmFzZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLmRhdGFiYXNlLW5hbWV9fSIKICAgICAgbXlzcWxVc2VyOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMubXlzcWwtdXNlciB9fSIKICAgIG91dHB1dHM6CiAgICAtIG5hbWU6IG15c3FsLXJvb3QtcGFzc3dvcmQKICAgICAgc2VjcmV0OiAie3sgYnVuZGxlLnBhcmFtZXRlcnMubXlzcWwtbmFtZSB9fSIKICAgICAga2V5OiBteXNxbC1yb290LXBhc3N3b3JkCiAgICAtIG5hbWU6IG15c3FsLXBhc3N3b3JkCiAgICAgIHNlY3JldDogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm15c3FsLW5hbWUgfX0iCiAgICAgIGtleTogbXlzcWwtcGFzc3dvcmQKCnBpbmc6CiAgLSBleGVjOgogICAgICBkZXNjcmlwdGlvbjogIlBpbmciCiAgICAgIGNvbW1hbmQ6IC4vaGVscGVycy5zaAogICAgICBhcmd1bWVudHM6CiAgICAgICAgLSBwaW5nCgp1cGdyYWRlOgogIC0gaGVsbToKICAgICAgZGVzY3JpcHRpb246ICJVcGdyYWRlIE15U1FMIgogICAgICBuYW1lOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMubXlzcWwtbmFtZSB9fSIKICAgICAgbmFtZXNwYWNlOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMubmFtZXNwYWNlIH19IgogICAgICBjaGFydDogc3RhYmxlL215c3FsCiAgICAgIHZlcnNpb246IDEuNi4yCiAgICAgIG91dHB1dHM6CiAgICAgIC0gbmFtZTogbXlzcWwtcm9vdC1wYXNzd29yZAogICAgICAgIHNlY3JldDogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm15c3FsLW5hbWUgfX0iCiAgICAgICAga2V5OiBteXNxbC1yb290LXBhc3N3b3JkCiAgICAgIC0gbmFtZTogbXlzcWwtcGFzc3dvcmQKICAgICAgICBzZWNyZXQ6ICJ7eyBidW5kbGUucGFyYW1ldGVycy5teXNxbC1uYW1lIH19IgogICAgICAgIGtleTogbXlzcWwtcGFzc3dvcmQKCnVuaW5zdGFsbDoKLSBoZWxtOgogICAgZGVzY3JpcHRpb246ICJVbmluc3RhbGwgTXlTUUwiCiAgICBwdXJnZTogdHJ1ZQogICAgcmVsZWFzZXM6CiAgICAgIC0gInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm15c3FsLW5hbWUgfX0iCgpvdXRwdXRzOgogIC0gbmFtZTogbXlzcWwtcGFzc3dvcmQKICAgIGRlc2NyaXB0aW9uOiAiVGhlIG15c3FsIGRhdGFiYXNlIHBhc3N3b3JkIgogICAgdHlwZTogc3RyaW5nCiAgICBkZWZhdWx0OiAiZGVmYXVsdC1wYXNzd29yZCIKICAgIGFwcGx5VG86CiAgICAgIC0gaW5zdGFsbAogICAgICAtIHVwZ3JhZGUKICAgIHNlbnNpdGl2ZTogdHJ1ZQogIC0gbmFtZTogbXlzcWwtcm9vdC1wYXNzd29yZAogICAgZGVzY3JpcHRpb246ICJUaGUgbXlzcWwgcm9vdCB1c2VyIHBhc3N3b3JkIgogICAgZGVmYXVsdDogImRlZmF1bHQtcm9vdC1wYXNzd29yZCIKICAgIHR5cGU6IHN0cmluZwogICAgYXBwbHlUbzoKICAgICAgLSBpbnN0YWxsCiAgICAgIC0gdXBncmFkZQogICAgc2Vuc2l0aXZlOiB0cnVlCg==","manifestDigest":"4d064f140c4d639bc00b75a52f07a98bc2122804b66096b03896494f14a07450","mixins":{"exec":{},"helm":{}}}},"definitions":{"database-name-parameter":{"default":"mydb","type":"string"},"mysql-name-parameter":{"default":"porter-ci-mysql","type":"string"},"mysql-password-output":{"default":"default-password","description":"The mysql database password","type":"string","writeOnly":true},"mysql-root-password-output":{"default":"default-root-password","description":"The mysql root user password","type":"string","writeOnly":true},"mysql-user-parameter":{"default":"mysql-admin","type":"string"},"namespace-parameter":{"default":"","type":"string"},"porter-debug-parameter":{"$comment":"porter-internal","$id":"https://porter.sh/generated-bundle/#porter-debug","default":false,"description":"Print debug information from Porter when executing the bundle","type":"boolean"}},"description":"","invocationImages":[{"image":"localhost:5000/mysql-installer:v0.1.3","imageType":"docker"}],"name":"mysql","outputs":{"mysql-password":{"applyTo":["install","upgrade"],"definition":"mysql-password-output","description":"The mysql database password","path":"/cnab/app/outputs/mysql-password"},"mysql-root-password":{"applyTo":["install","upgrade"],"definition":"mysql-root-password-output","description":"The mysql root user password","path":"/cnab/app/outputs/mysql-root-password"}},"parameters":{"database-name":{"definition":"database-name-parameter","destination":{"env":"DATABASE_NAME"}},"mysql-name":{"definition":"mysql-name-parameter","destination":{"env":"MYSQL_NAME"}},"mysql-user":{"definition":"mysql-user-parameter","destination":{"env":"MYSQL_USER"}},"namespace":{"definition":"namespace-parameter","destination":{"env":"NAMESPACE"}},"porter-debug":{"definition":"porter-debug-parameter","description":"Print debug information from Porter when executing the bundle","destination":{"env":"PORTER_DEBUG"}}},"schemaVersion":"v1.0.0","version":"0.1.3"}
\ No newline at end of file
+{"actions":{"ping":{"description":"ping","modifies":true}},"credentials":{"kubeconfig":{"path":"/root/.kube/config","required":true}},"custom":{"sh.porter":{"manifest":"bWl4aW5zOgotIGV4ZWMKLSBoZWxtCgpuYW1lOiBteXNxbAp2ZXJzaW9uOiAiMC4xLjMiCnJlZ2lzdHJ5OiAibG9jYWxob3N0OjUwMDAiCgpjcmVkZW50aWFsczoKLSBuYW1lOiBrdWJlY29uZmlnCiAgcGF0aDogL3Jvb3QvLmt1YmUvY29uZmlnCgpwYXJhbWV0ZXJzOgotIG5hbWU6IGRhdGFiYXNlLW5hbWUKICB0eXBlOiBzdHJpbmcKICBkZWZhdWx0OiBteWRiCiAgZW52OiBEQVRBQkFTRV9OQU1FCi0gbmFtZTogbXlzcWwtdXNlcgogIHR5cGU6IHN0cmluZwogIGRlZmF1bHQ6IG15c3FsLWFkbWluCiAgZW52OiBNWVNRTF9VU0VSCi0gbmFtZTogbmFtZXNwYWNlCiAgdHlwZTogc3RyaW5nCiAgZGVmYXVsdDogJycKLSBuYW1lOiBteXNxbC1uYW1lCiAgdHlwZTogc3RyaW5nCiAgZGVmYXVsdDogcG9ydGVyLWNpLW15c3FsCgppbnN0YWxsOgotIGhlbG06CiAgICBkZXNjcmlwdGlvbjogIkluc3RhbGwgTXlTUUwiCiAgICBuYW1lOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMubXlzcWwtbmFtZSB9fSIKICAgIGNoYXJ0OiBzdGFibGUvbXlzcWwKICAgIHZlcnNpb246IDEuNi4yCiAgICBuYW1lc3BhY2U6ICJ7eyBidW5kbGUucGFyYW1ldGVycy5uYW1lc3BhY2UgfX0iCiAgICByZXBsYWNlOiB0cnVlCiAgICBzZXQ6CiAgICAgIG15c3FsRGF0YWJhc2U6ICJ7eyBidW5kbGUucGFyYW1ldGVycy5kYXRhYmFzZS1uYW1lfX0iCiAgICAgIG15c3FsVXNlcjogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm15c3FsLXVzZXIgfX0iCiAgICBvdXRwdXRzOgogICAgLSBuYW1lOiBteXNxbC1yb290LXBhc3N3b3JkCiAgICAgIHNlY3JldDogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm15c3FsLW5hbWUgfX0iCiAgICAgIGtleTogbXlzcWwtcm9vdC1wYXNzd29yZAogICAgLSBuYW1lOiBteXNxbC1wYXNzd29yZAogICAgICBzZWNyZXQ6ICJ7eyBidW5kbGUucGFyYW1ldGVycy5teXNxbC1uYW1lIH19IgogICAgICBrZXk6IG15c3FsLXBhc3N3b3JkCgpwaW5nOgogIC0gZXhlYzoKICAgICAgZGVzY3JpcHRpb246ICJQaW5nIgogICAgICBjb21tYW5kOiAuL2hlbHBlcnMuc2gKICAgICAgYXJndW1lbnRzOgogICAgICAgIC0gcGluZwoKdXBncmFkZToKICAtIGhlbG06CiAgICAgIGRlc2NyaXB0aW9uOiAiVXBncmFkZSBNeVNRTCIKICAgICAgbmFtZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm15c3FsLW5hbWUgfX0iCiAgICAgIG5hbWVzcGFjZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm5hbWVzcGFjZSB9fSIKICAgICAgY2hhcnQ6IHN0YWJsZS9teXNxbAogICAgICB2ZXJzaW9uOiAxLjYuMgogICAgICBvdXRwdXRzOgogICAgICAtIG5hbWU6IG15c3FsLXJvb3QtcGFzc3dvcmQKICAgICAgICBzZWNyZXQ6ICJ7eyBidW5kbGUucGFyYW1ldGVycy5teXNxbC1uYW1lIH19IgogICAgICAgIGtleTogbXlzcWwtcm9vdC1wYXNzd29yZAogICAgICAtIG5hbWU6IG15c3FsLXBhc3N3b3JkCiAgICAgICAgc2VjcmV0OiAie3sgYnVuZGxlLnBhcmFtZXRlcnMubXlzcWwtbmFtZSB9fSIKICAgICAgICBrZXk6IG15c3FsLXBhc3N3b3JkCgp1bmluc3RhbGw6Ci0gaGVsbToKICAgIGRlc2NyaXB0aW9uOiAiVW5pbnN0YWxsIE15U1FMIgogICAgcHVyZ2U6IHRydWUKICAgIHJlbGVhc2VzOgogICAgICAtICJ7eyBidW5kbGUucGFyYW1ldGVycy5teXNxbC1uYW1lIH19IgoKb3V0cHV0czoKICAtIG5hbWU6IG15c3FsLXBhc3N3b3JkCiAgICBkZXNjcmlwdGlvbjogIlRoZSBteXNxbCBkYXRhYmFzZSBwYXNzd29yZCIKICAgIHR5cGU6IHN0cmluZwogICAgZGVmYXVsdDogImRlZmF1bHQtcGFzc3dvcmQiCiAgICBhcHBseVRvOgogICAgICAtIGluc3RhbGwKICAgICAgLSB1cGdyYWRlCiAgICBzZW5zaXRpdmU6IHRydWUKICAtIG5hbWU6IG15c3FsLXJvb3QtcGFzc3dvcmQKICAgIGRlc2NyaXB0aW9uOiAiVGhlIG15c3FsIHJvb3QgdXNlciBwYXNzd29yZCIKICAgIGRlZmF1bHQ6ICJkZWZhdWx0LXJvb3QtcGFzc3dvcmQiCiAgICB0eXBlOiBzdHJpbmcKICAgIGFwcGx5VG86CiAgICAgIC0gaW5zdGFsbAogICAgICAtIHVwZ3JhZGUKICAgIHNlbnNpdGl2ZTogdHJ1ZQo=","manifestDigest":"16131a330a1d3bce91580ed8a843960b063d4a205b6c5c821bc57ef809d47434","mixins":{"exec":{},"helm":{}}}},"definitions":{"database-name-parameter":{"default":"mydb","type":"string"},"mysql-name-parameter":{"default":"porter-ci-mysql","type":"string"},"mysql-password-output":{"default":"default-password","description":"The mysql database password","type":"string","writeOnly":true},"mysql-root-password-output":{"default":"default-root-password","description":"The mysql root user password","type":"string","writeOnly":true},"mysql-user-parameter":{"default":"mysql-admin","type":"string"},"namespace-parameter":{"default":"","type":"string"},"porter-debug-parameter":{"$comment":"porter-internal","$id":"https://porter.sh/generated-bundle/#porter-debug","default":false,"description":"Print debug information from Porter when executing the bundle","type":"boolean"}},"description":"","invocationImages":[{"image":"localhost:5000/mysql-installer:v0.1.3","imageType":"docker"}],"name":"mysql","outputs":{"mysql-password":{"applyTo":["install","upgrade"],"definition":"mysql-password-output","description":"The mysql database password","path":"/cnab/app/outputs/mysql-password"},"mysql-root-password":{"applyTo":["install","upgrade"],"definition":"mysql-root-password-output","description":"The mysql root user password","path":"/cnab/app/outputs/mysql-root-password"}},"parameters":{"database-name":{"definition":"database-name-parameter","destination":{"env":"DATABASE_NAME"}},"mysql-name":{"definition":"mysql-name-parameter","destination":{"env":"MYSQL_NAME"}},"mysql-user":{"definition":"mysql-user-parameter","destination":{"env":"MYSQL_USER"}},"namespace":{"definition":"namespace-parameter","destination":{"env":"NAMESPACE"}},"porter-debug":{"definition":"porter-debug-parameter","description":"Print debug information from Porter when executing the bundle","destination":{"env":"PORTER_DEBUG"}}},"schemaVersion":"v1.0.0","version":"0.1.3"}
\ No newline at end of file
diff --git a/build/testdata/bundles/mysql/porter.yaml b/build/testdata/bundles/mysql/porter.yaml
index a8e45185..e42cec82 100644
--- a/build/testdata/bundles/mysql/porter.yaml
+++ b/build/testdata/bundles/mysql/porter.yaml
@@ -3,8 +3,8 @@ mixins:
 - helm
 
 name: mysql
-version: 0.1.3
-registry: localhost:5000
+version: "0.1.3"
+registry: "localhost:5000"
 
 credentials:
 - name: kubeconfig
diff --git a/build/testdata/bundles/wordpress/.cnab/bundle.json b/build/testdata/bundles/wordpress/.cnab/bundle.json
index 5100d59e..9153d1bd 100644
--- a/build/testdata/bundles/wordpress/.cnab/bundle.json
+++ b/build/testdata/bundles/wordpress/.cnab/bundle.json
@@ -1,1 +1,1 @@
-{"actions":{"ping":{"description":"ping","modifies":true}},"credentials":{"kubeconfig":{"path":"/root/.kube/config","required":true}},"custom":{"io.cnab.dependencies":{"requires":{"mysql":{"bundle":"getporter/mysql:v0.1.3"}}},"sh.porter":{"manifest":"bWl4aW5zOgotIGV4ZWMKLSBoZWxtCgpuYW1lOiB3b3JkcHJlc3MKdmVyc2lvbjogMC4xLjMKdGFnOiBnZXRwb3J0ZXIvd29yZHByZXNzCgpkZXBlbmRlbmNpZXM6CiAgbXlzcWw6CiAgICB0YWc6IGdldHBvcnRlci9teXNxbDp2MC4xLjMKICAgIHBhcmFtZXRlcnM6CiAgICAgIGRhdGFiYXNlLW5hbWU6IHdvcmRwcmVzcwogICAgICBteXNxbC11c2VyOiB3b3JkcHJlc3MKCmNyZWRlbnRpYWxzOgotIG5hbWU6IGt1YmVjb25maWcKICBwYXRoOiAvcm9vdC8ua3ViZS9jb25maWcKCnBhcmFtZXRlcnM6Ci0gbmFtZTogd29yZHByZXNzLW5hbWUKICB0eXBlOiBzdHJpbmcKICBkZWZhdWx0OiBwb3J0ZXItY2ktd29yZHByZXNzCiAgZW52OiBXT1JEUFJFU1NfTkFNRQotIG5hbWU6IHdvcmRwcmVzcy1wYXNzd29yZAogIHR5cGU6IHN0cmluZwogIHNlbnNpdGl2ZTogdHJ1ZQotIG5hbWU6IG5hbWVzcGFjZQogIHR5cGU6IHN0cmluZwogIGRlZmF1bHQ6ICcnCgppbnN0YWxsOgogIC0gaGVsbToKICAgICAgZGVzY3JpcHRpb246ICJJbnN0YWxsIFdvcmRwcmVzcyIKICAgICAgbmFtZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLndvcmRwcmVzcy1uYW1lIH19IgogICAgICBjaGFydDogc3RhYmxlL3dvcmRwcmVzcwogICAgICBuYW1lc3BhY2U6ICJ7eyBidW5kbGUucGFyYW1ldGVycy5uYW1lc3BhY2UgfX0iCiAgICAgIHJlcGxhY2U6IHRydWUKICAgICAgc2V0OgogICAgICAgIHdvcmRwcmVzc1Bhc3N3b3JkOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMud29yZHByZXNzLXBhc3N3b3JkIH19IgogICAgICAgICNleHRlcm5hbERhdGFiYXNlLmRhdGFiYXNlOiAie3sgYnVuZGxlLmRlcGVuZGVuY2llcy5teXNxbC5wYXJhbWV0ZXJzLmRhdGFiYXNlLW5hbWUgfX0iCiAgICAgICAgI2V4dGVybmFsRGF0YWJhc2UuaG9zdDogInt7IGJ1bmRsZS5kZXBlbmRlbmNpZXMubXlzcWwub3V0cHV0cy5zZXJ2aWNlcyB9fSIKICAgICAgICAjZXh0ZXJuYWxEYXRhYmFzZS51c2VyOiAie3sgYnVuZGxlLmRlcGVuZGVuY2llcy5teXNxbC5wYXJhbWV0ZXJzLm15c3FsLXVzZXIgfX0iCiAgICAgICAgZXh0ZXJuYWxEYXRhYmFzZS5wYXNzd29yZDogInt7IGJ1bmRsZS5kZXBlbmRlbmNpZXMubXlzcWwub3V0cHV0cy5teXNxbC1wYXNzd29yZCB9fSIKICAgICAgICBleHRlcm5hbERhdGFiYXNlLnBvcnQ6IDMzMDYKICAgICAgICBtYXJpYWRiLmVuYWJsZWQ6IGZhbHNlCiAgICAgIG91dHB1dHM6CiAgICAgICAgLSBuYW1lOiB3b3JkcHJlc3MtcGFzc3dvcmQKICAgICAgICAgIHNlY3JldDogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLndvcmRwcmVzcy1uYW1lIH19IgogICAgICAgICAga2V5OiB3b3JkcHJlc3MtcGFzc3dvcmQKCnVwZ3JhZGU6CiAgLSBoZWxtOgogICAgICBkZXNjcmlwdGlvbjogIlVwZ3JhZGUgV29yZHByZXNzIgogICAgICBuYW1lOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMud29yZHByZXNzLW5hbWUgfX0iCiAgICAgIG5hbWVzcGFjZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm5hbWVzcGFjZSB9fSIKICAgICAgY2hhcnQ6IHN0YWJsZS93b3JkcHJlc3MKICAgICAgb3V0cHV0czoKICAgICAgICAtIG5hbWU6IHdvcmRwcmVzcy1wYXNzd29yZAogICAgICAgICAgc2VjcmV0OiAie3sgYnVuZGxlLnBhcmFtZXRlcnMud29yZHByZXNzLW5hbWUgfX0iCiAgICAgICAgICBrZXk6IHdvcmRwcmVzcy1wYXNzd29yZAoKcGluZzoKICAtIGV4ZWM6CiAgICAgIGRlc2NyaXB0aW9uOiAiUGluZyIKICAgICAgY29tbWFuZDogLi9oZWxwZXJzLnNoCiAgICAgIGFyZ3VtZW50czoKICAgICAgICAtIHBpbmcKCnVuaW5zdGFsbDoKICAtIGhlbG06CiAgICAgIGRlc2NyaXB0aW9uOiAiVW5pbnN0YWxsIFdvcmRwcmVzcyIKICAgICAgcHVyZ2U6IHRydWUKICAgICAgcmVsZWFzZXM6IAogICAgICAgIC0gInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLndvcmRwcmVzcy1uYW1lIH19IgoKb3V0cHV0czoKICAtIG5hbWU6IHdvcmRwcmVzcy1wYXNzd29yZAogICAgZGVzY3JpcHRpb246ICJUaGUgV29yZHByZXNzIGluc3RhbGxhdGlvbiBwYXNzd29yZCIKICAgIHR5cGU6IHN0cmluZwogICAgZGVmYXVsdDogImRlZmF1bHQtcGFzc3dvcmQiCiAgICBhcHBseVRvOgogICAgICAtICJpbnN0YWxsIgogICAgICAtICJ1cGdyYWRlIgogICAgc2Vuc2l0aXZlOiB0cnVl","manifestDigest":"c309a8a749f0701f56fae5dae613013deec4ed143413d889c7675ab82523c7d0","mixins":{"exec":{},"helm":{}}}},"definitions":{"namespace-parameter":{"default":"","type":"string"},"porter-debug-parameter":{"$id":"https://porter.sh/generated-bundle/#porter-debug","default":false,"description":"Print debug information from Porter when executing the bundle","type":"boolean"},"wordpress-name-parameter":{"default":"porter-ci-wordpress","type":"string"},"wordpress-password-output":{"default":"default-password","description":"The Wordpress installation password","type":"string","writeOnly":true},"wordpress-password-parameter":{"type":"string","writeOnly":true}},"description":"","invocationImages":[{"image":"getporter/wordpress-installer:v0.1.3","imageType":"docker"}],"name":"wordpress","outputs":{"wordpress-password":{"applyTo":["install","upgrade"],"definition":"wordpress-password-output","description":"The Wordpress installation password","path":"/cnab/app/outputs/wordpress-password"}},"parameters":{"namespace":{"definition":"namespace-parameter","destination":{"env":"NAMESPACE"}},"porter-debug":{"definition":"porter-debug-parameter","description":"Print debug information from Porter when executing the bundle","destination":{"env":"PORTER_DEBUG"}},"wordpress-name":{"definition":"wordpress-name-parameter","destination":{"env":"WORDPRESS_NAME"}},"wordpress-password":{"definition":"wordpress-password-parameter","destination":{"env":"WORDPRESS_PASSWORD"},"required":true}},"requiredExtensions":["io.cnab.dependencies"],"schemaVersion":"v1.0.0","version":"0.1.3"}
\ No newline at end of file
+{"actions":{"ping":{"description":"ping","modifies":true}},"credentials":{"kubeconfig":{"path":"/root/.kube/config","required":true}},"custom":{"io.cnab.dependencies":{"requires":{"mysql":{"bundle":"localhost:5000/mysql:v0.1.3","name":"mysql"}},"sequence":["mysql"]},"io.cnab.parameter-sources":{"porter-mysql-mysql-password-dep-output":{"priority":["dependencies.output"],"sources":{"dependencies.output":{"dependency":"mysql","name":"mysql-password"}}}},"sh.porter":{"manifest":"bWl4aW5zOgotIGV4ZWMKLSBoZWxtCgpuYW1lOiB3b3JkcHJlc3MKcmVnaXN0cnk6ICJsb2NhbGhvc3Q6NTAwMCIKdmVyc2lvbjogMC4xLjMKCmRlcGVuZGVuY2llczoKICAtIG5hbWU6IG15c3FsCiAgICByZWZlcmVuY2U6IGxvY2FsaG9zdDo1MDAwL215c3FsOnYwLjEuMwogICAgcGFyYW1ldGVyczoKICAgICAgZGF0YWJhc2UtbmFtZTogd29yZHByZXNzCiAgICAgIG15c3FsLXVzZXI6IHdvcmRwcmVzcwoKY3JlZGVudGlhbHM6Ci0gbmFtZToga3ViZWNvbmZpZwogIHBhdGg6IC9yb290Ly5rdWJlL2NvbmZpZwoKcGFyYW1ldGVyczoKLSBuYW1lOiB3b3JkcHJlc3MtbmFtZQogIHR5cGU6IHN0cmluZwogIGRlZmF1bHQ6IHBvcnRlci1jaS13b3JkcHJlc3MKICBlbnY6IFdPUkRQUkVTU19OQU1FCi0gbmFtZTogd29yZHByZXNzLXBhc3N3b3JkCiAgdHlwZTogc3RyaW5nCiAgc2Vuc2l0aXZlOiB0cnVlCiAgYXBwbHlUbzoKICAgIC0gaW5zdGFsbAogICAgLSB1cGdyYWRlCi0gbmFtZTogbmFtZXNwYWNlCiAgdHlwZTogc3RyaW5nCiAgZGVmYXVsdDogJycKCmluc3RhbGw6CiAgLSBoZWxtOgogICAgICBkZXNjcmlwdGlvbjogIkluc3RhbGwgV29yZHByZXNzIgogICAgICBuYW1lOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMud29yZHByZXNzLW5hbWUgfX0iCiAgICAgIGNoYXJ0OiBzdGFibGUvd29yZHByZXNzCiAgICAgIG5hbWVzcGFjZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm5hbWVzcGFjZSB9fSIKICAgICAgcmVwbGFjZTogdHJ1ZQogICAgICBzZXQ6CiAgICAgICAgd29yZHByZXNzUGFzc3dvcmQ6ICJ7eyBidW5kbGUucGFyYW1ldGVycy53b3JkcHJlc3MtcGFzc3dvcmQgfX0iCiAgICAgICAgZXh0ZXJuYWxEYXRhYmFzZS5wYXNzd29yZDogInt7IGJ1bmRsZS5kZXBlbmRlbmNpZXMubXlzcWwub3V0cHV0cy5teXNxbC1wYXNzd29yZCB9fSIKICAgICAgICBleHRlcm5hbERhdGFiYXNlLnBvcnQ6IDMzMDYKICAgICAgICBtYXJpYWRiLmVuYWJsZWQ6IGZhbHNlCiAgICAgIG91dHB1dHM6CiAgICAgICAgLSBuYW1lOiB3b3JkcHJlc3MtcGFzc3dvcmQKICAgICAgICAgIHNlY3JldDogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLndvcmRwcmVzcy1uYW1lIH19IgogICAgICAgICAga2V5OiB3b3JkcHJlc3MtcGFzc3dvcmQKCnVwZ3JhZGU6CiAgLSBoZWxtOgogICAgICBkZXNjcmlwdGlvbjogIlVwZ3JhZGUgV29yZHByZXNzIgogICAgICBuYW1lOiAie3sgYnVuZGxlLnBhcmFtZXRlcnMud29yZHByZXNzLW5hbWUgfX0iCiAgICAgIG5hbWVzcGFjZTogInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLm5hbWVzcGFjZSB9fSIKICAgICAgY2hhcnQ6IHN0YWJsZS93b3JkcHJlc3MKICAgICAgb3V0cHV0czoKICAgICAgICAtIG5hbWU6IHdvcmRwcmVzcy1wYXNzd29yZAogICAgICAgICAgc2VjcmV0OiAie3sgYnVuZGxlLnBhcmFtZXRlcnMud29yZHByZXNzLW5hbWUgfX0iCiAgICAgICAgICBrZXk6IHdvcmRwcmVzcy1wYXNzd29yZAoKcGluZzoKICAtIGV4ZWM6CiAgICAgIGRlc2NyaXB0aW9uOiAiUGluZyIKICAgICAgY29tbWFuZDogLi9oZWxwZXJzLnNoCiAgICAgIGFyZ3VtZW50czoKICAgICAgICAtIHBpbmcKCnVuaW5zdGFsbDoKICAtIGhlbG06CiAgICAgIGRlc2NyaXB0aW9uOiAiVW5pbnN0YWxsIFdvcmRwcmVzcyIKICAgICAgcHVyZ2U6IHRydWUKICAgICAgcmVsZWFzZXM6IAogICAgICAgIC0gInt7IGJ1bmRsZS5wYXJhbWV0ZXJzLndvcmRwcmVzcy1uYW1lIH19IgoKb3V0cHV0czoKICAtIG5hbWU6IHdvcmRwcmVzcy1wYXNzd29yZAogICAgZGVzY3JpcHRpb246ICJUaGUgV29yZHByZXNzIGluc3RhbGxhdGlvbiBwYXNzd29yZCIKICAgIHR5cGU6IHN0cmluZwogICAgZGVmYXVsdDogImRlZmF1bHQtcGFzc3dvcmQiCiAgICBhcHBseVRvOgogICAgICAtICJpbnN0YWxsIgogICAgICAtICJ1cGdyYWRlIgogICAgc2Vuc2l0aXZlOiB0cnVl","manifestDigest":"69aa4f2ccd94c51c76b18e9c7297a7271a2aef7a038c6d320672c432e1e39b0b","mixins":{"exec":{},"helm":{}}}},"definitions":{"namespace-parameter":{"default":"","type":"string"},"porter-debug-parameter":{"$comment":"porter-internal","$id":"https://porter.sh/generated-bundle/#porter-debug","default":false,"description":"Print debug information from Porter when executing the bundle","type":"boolean"},"porter-mysql-mysql-password-dep-output":{"$comment":"porter-internal","$id":"https://porter.sh/generated-bundle/#porter-parameter-source-definition"},"wordpress-name-parameter":{"default":"porter-ci-wordpress","type":"string"},"wordpress-password-output":{"default":"default-password","description":"The Wordpress installation password","type":"string","writeOnly":true},"wordpress-password-parameter":{"type":"string","writeOnly":true}},"description":"","invocationImages":[{"image":"localhost:5000/wordpress-installer:v0.1.3","imageType":"docker"}],"name":"wordpress","outputs":{"wordpress-password":{"applyTo":["install","upgrade"],"definition":"wordpress-password-output","description":"The Wordpress installation password","path":"/cnab/app/outputs/wordpress-password"}},"parameters":{"namespace":{"definition":"namespace-parameter","destination":{"env":"NAMESPACE"}},"porter-debug":{"definition":"porter-debug-parameter","description":"Print debug information from Porter when executing the bundle","destination":{"env":"PORTER_DEBUG"}},"porter-mysql-mysql-password-dep-output":{"definition":"porter-mysql-mysql-password-dep-output","description":"Wires up the mysql dependency mysql-password output for use as a parameter. Porter internal parameter that should not be set manually.","destination":{"env":"PORTER_MYSQL_MYSQL_PASSWORD_DEP_OUTPUT"}},"wordpress-name":{"definition":"wordpress-name-parameter","destination":{"env":"WORDPRESS_NAME"}},"wordpress-password":{"applyTo":["install","upgrade"],"definition":"wordpress-password-parameter","destination":{"env":"WORDPRESS_PASSWORD"},"required":true}},"requiredExtensions":["io.cnab.dependencies","io.cnab.parameter-sources"],"schemaVersion":"v1.0.0","version":"0.1.3"}
\ No newline at end of file
diff --git a/build/testdata/bundles/wordpress/porter.yaml b/build/testdata/bundles/wordpress/porter.yaml
index df85b1cc..3beaf4dc 100644
--- a/build/testdata/bundles/wordpress/porter.yaml
+++ b/build/testdata/bundles/wordpress/porter.yaml
@@ -3,7 +3,7 @@ mixins:
 - helm
 
 name: wordpress
-registry: localhost:5000
+registry: "localhost:5000"
 version: 0.1.3
 
 dependencies:
diff --git a/pkg/manifest/testdata/bundles/nginx/porter.yaml b/pkg/manifest/testdata/bundles/nginx/porter.yaml
index 3870d804..a4bc63b4 100644
--- a/pkg/manifest/testdata/bundles/nginx/porter.yaml
+++ b/pkg/manifest/testdata/bundles/nginx/porter.yaml
@@ -3,7 +3,7 @@ mixins:
 
 name: nginx
 version: 1.19
-registry: localhost:5000
+registry: "localhost:5000"
 
 install:
   - exec:
diff --git a/pkg/porter/install_test.go b/pkg/porter/install_test.go
index ffac09ec..8fb38fba 100644
--- a/pkg/porter/install_test.go
+++ b/pkg/porter/install_test.go
@@ -135,7 +135,7 @@ func TestPorter_InstallBundle_WithDepsFromTag(t *testing.T) {
 
 	opts := NewInstallOptions()
 	opts.Driver = DebugDriver
-	opts.Tag = "getporter/wordpress:v0.1.2"
+	opts.Tag = "localhost:5000/wordpress:v0.1.3"
 	opts.CredentialIdentifiers = []string{"wordpress"}
 	opts.Params = []string{"wordpress-password=mypassword"}
 	err = opts.Validate(nil, p.Porter)
diff --git a/pkg/porter/lifecycle_test.go b/pkg/porter/lifecycle_test.go
index 09260cfc..43d3631b 100644
--- a/pkg/porter/lifecycle_test.go
+++ b/pkg/porter/lifecycle_test.go
@@ -1,12 +1,15 @@
 package porter
 
 import (
+	"errors"
 	"os"
 	"path"
 	"path/filepath"
 	"testing"
 
 	"get.porter.sh/porter/pkg/config"
+	"github.com/cnabio/cnab-go/bundle"
+	"github.com/cnabio/cnab-to-oci/relocation"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -39,7 +42,11 @@ func TestBundlePullUpdateOpts_bundleCached(t *testing.T) {
 
 func TestBundlePullUpdateOpts_pullError(t *testing.T) {
 	p := NewTestPorter(t)
-	p.TestConfig.SetupPorterHome()
+
+	p.TestRegistry.MockPullBundle = func(tag string, insecureRegistry bool) (bun bundle.Bundle, reloMap *relocation.ImageRelocationMap, err error) {
+		return bundle.Bundle{}, nil, errors.New("unable to pull bundle deislabs/kubekahn:latest")
+	}
+
 	b := &BundleActionOptions{
 		BundlePullOptions: BundlePullOptions{
 			Tag: "deislabs/kubekahn:latest",
@@ -187,6 +194,8 @@ func TestManifestIgnoredWithTag(t *testing.T) {
 
 func TestInstallFromTag_ManageFromClaim(t *testing.T) {
 	p := NewTestPorter(t)
+	cacheDir, _ := p.Cache.GetCacheDir()
+	p.TestConfig.TestContext.AddTestDirectory("testdata/cache", cacheDir)
 
 	installOpts := NewInstallOptions()
 	installOpts.Name = "hello"
diff --git a/pkg/porter/testdata/cache/bfa9f2b528338f9ac1bc9b8b984c3997/cnab/bundle.json b/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/cnab/bundle.json
similarity index 100%
rename from pkg/porter/testdata/cache/bfa9f2b528338f9ac1bc9b8b984c3997/cnab/bundle.json
rename to pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/cnab/bundle.json
diff --git a/pkg/porter/testdata/cache/bfa9f2b528338f9ac1bc9b8b984c3997/porter.yaml b/pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/porter.yaml
similarity index 100%
rename from pkg/porter/testdata/cache/bfa9f2b528338f9ac1bc9b8b984c3997/porter.yaml
rename to pkg/porter/testdata/cache/1e6469dbfdf8c060d9ac32a3991e8e15/porter.yaml
diff --git a/pkg/porter/testdata/cache/README.md b/pkg/porter/testdata/cache/README.md
index d7e36d5a..16e7bd85 100644
--- a/pkg/porter/testdata/cache/README.md
+++ b/pkg/porter/testdata/cache/README.md
@@ -1,5 +1,8 @@
 # Cache Cheat Sheet
 
-* deislabs/kubekahn:1.0 -> 887e7e65e39277f8744bd00278760b06
-* getporter/mysql:v0.1.2 -> 972978d3b715212b783a70fdc3449584
-* getporter/wordpress:v0.1.2 -> bfa9f2b528338f9ac1bc9b8b984c3997
+| 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