Repository: getporter/porter
Pull request #2658 (2023-03-27 19:15:51)

Refactor CNAB package in preparation for dependencies v2
* Rename some CNAB package files to match their contents
* Consolidate the required extension logic into the extensions.go file so it's easier to find.
* Move v1 implementation of CNAB dependencies into separate package


Patch:
diff --git a/pkg/cnab/bundle.go b/pkg/cnab/bundle_reference.go
similarity index 100%
rename from pkg/cnab/bundle.go
rename to pkg/cnab/bundle_reference.go
diff --git a/pkg/cnab/bundle_test.go b/pkg/cnab/bundle_reference_test.go
similarity index 100%
rename from pkg/cnab/bundle_test.go
rename to pkg/cnab/bundle_reference_test.go
diff --git a/pkg/cnab/config-adapter/adapter.go b/pkg/cnab/config-adapter/adapter.go
index a77999f6..f231d903 100644
--- a/pkg/cnab/config-adapter/adapter.go
+++ b/pkg/cnab/config-adapter/adapter.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 
 	"get.porter.sh/porter/pkg/cnab"
-	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
 	"get.porter.sh/porter/pkg/config"
 	"get.porter.sh/porter/pkg/experimental"
 	"get.porter.sh/porter/pkg/manifest"
@@ -417,30 +417,27 @@ func (c *ManifestConverter) generateDependencies() (interface{}, string, error)
 		panic("the dependencies-v2 experimental flag was specified but is not yet implemented")
 	}
 
-	deps, err := c.generateDependenciesV1()
-	if err != nil {
-		return nil, "", err
-	}
+	deps := c.generateDependenciesV1()
 	return deps, cnab.DependenciesV1ExtensionKey, nil
 }
 
-func (c *ManifestConverter) generateDependenciesV1() (*depsv1.Dependencies, error) {
+func (c *ManifestConverter) generateDependenciesV1() *depsv1ext.Dependencies {
 	if len(c.Manifest.Dependencies.Requires) == 0 {
-		return nil, nil
+		return nil
 	}
 
-	deps := &depsv1.Dependencies{
+	deps := &depsv1ext.Dependencies{
 		Sequence: make([]string, 0, len(c.Manifest.Dependencies.Requires)),
-		Requires: make(map[string]depsv1.Dependency, len(c.Manifest.Dependencies.Requires)),
+		Requires: make(map[string]depsv1ext.Dependency, len(c.Manifest.Dependencies.Requires)),
 	}
 
 	for _, dep := range c.Manifest.Dependencies.Requires {
-		dependencyRef := depsv1.Dependency{
+		dependencyRef := depsv1ext.Dependency{
 			Name:   dep.Name,
 			Bundle: dep.Bundle.Reference,
 		}
 		if len(dep.Bundle.Version) > 0 {
-			dependencyRef.Version = &depsv1.DependencyVersion{
+			dependencyRef.Version = &depsv1ext.DependencyVersion{
 				Ranges: []string{dep.Bundle.Version},
 			}
 
@@ -454,7 +451,7 @@ func (c *ManifestConverter) generateDependenciesV1() (*depsv1.Dependencies, erro
 		deps.Requires[dep.Name] = dependencyRef
 	}
 
-	return deps, nil
+	return deps
 }
 
 func (c *ManifestConverter) generateParameterSources(b *cnab.ExtendedBundle) cnab.ParameterSources {
diff --git a/pkg/cnab/config-adapter/doc.go b/pkg/cnab/config-adapter/doc.go
index fa52e028..7da1f640 100644
--- a/pkg/cnab/config-adapter/doc.go
+++ b/pkg/cnab/config-adapter/doc.go
@@ -1,1 +1,2 @@
+// Package configadapter converts a Porter manifest (porter.yaml) to a CNAB bundle.json
 package configadapter
diff --git a/pkg/cnab/dependencies/v1/doc.go b/pkg/cnab/dependencies/v1/doc.go
new file mode 100644
index 00000000..b3fc9a00
--- /dev/null
+++ b/pkg/cnab/dependencies/v1/doc.go
@@ -0,0 +1,2 @@
+// Package v1 contains the implementation for v1 of the CNAB Dependencies specification.
+package v1
diff --git a/pkg/cnab/solver.go b/pkg/cnab/dependencies/v1/solver.go
similarity index 77%
rename from pkg/cnab/solver.go
rename to pkg/cnab/dependencies/v1/solver.go
index d8bbc5eb..814498c7 100644
--- a/pkg/cnab/solver.go
+++ b/pkg/cnab/dependencies/v1/solver.go
@@ -1,10 +1,12 @@
-package cnab
+package v1
 
 import (
 	"fmt"
 	"sort"
 
-	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+	"get.porter.sh/porter/pkg/cnab"
+
+	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
 	"github.com/Masterminds/semver/v3"
 	"github.com/google/go-containerregistry/pkg/crane"
 )
@@ -18,7 +20,7 @@ type DependencyLock struct {
 type DependencySolver struct {
 }
 
-func (s *DependencySolver) ResolveDependencies(bun ExtendedBundle) ([]DependencyLock, error) {
+func (s *DependencySolver) ResolveDependencies(bun cnab.ExtendedBundle) ([]DependencyLock, error) {
 	if !bun.HasDependenciesV1() {
 		return nil, nil
 	}
@@ -49,10 +51,10 @@ func (s *DependencySolver) ResolveDependencies(bun ExtendedBundle) ([]Dependency
 }
 
 // ResolveVersion returns the bundle name, its version and any error.
-func (s *DependencySolver) ResolveVersion(name string, dep depsv1.Dependency) (OCIReference, error) {
-	ref, err := ParseOCIReference(dep.Bundle)
+func (s *DependencySolver) ResolveVersion(name string, dep depsv1ext.Dependency) (cnab.OCIReference, error) {
+	ref, err := cnab.ParseOCIReference(dep.Bundle)
 	if err != nil {
-		return OCIReference{}, fmt.Errorf("error parsing dependency (%s) bundle %q as OCI reference: %w", name, dep.Bundle, err)
+		return cnab.OCIReference{}, fmt.Errorf("error parsing dependency (%s) bundle %q as OCI reference: %w", name, dep.Bundle, err)
 	}
 
 	// Here is where we could split out this logic into multiple strategy funcs / structs if necessary
@@ -64,16 +66,16 @@ func (s *DependencySolver) ResolveVersion(name string, dep depsv1.Dependency) (O
 
 		tag, err := s.determineDefaultTag(dep)
 		if err != nil {
-			return OCIReference{}, err
+			return cnab.OCIReference{}, err
 		}
 
 		return ref.WithTag(tag)
 	}
 
-	return OCIReference{}, fmt.Errorf("not implemented: dependency version range specified for %s: %w", name, err)
+	return cnab.OCIReference{}, fmt.Errorf("not implemented: dependency version range specified for %s: %w", name, err)
 }
 
-func (s *DependencySolver) determineDefaultTag(dep depsv1.Dependency) (string, error) {
+func (s *DependencySolver) determineDefaultTag(dep depsv1ext.Dependency) (string, error) {
 	tags, err := crane.ListTags(dep.Bundle)
 	if err != nil {
 		return "", fmt.Errorf("error listing tags for %s: %w", dep.Bundle, err)
diff --git a/pkg/cnab/dependencies_v1.go b/pkg/cnab/dependencies_v1.go
index 2169edc3..acb567f8 100644
--- a/pkg/cnab/dependencies_v1.go
+++ b/pkg/cnab/dependencies_v1.go
@@ -5,7 +5,7 @@ import (
 	"errors"
 	"fmt"
 
-	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
 )
 
 const (
@@ -32,15 +32,15 @@ var DependenciesV1Extension = RequiredExtension{
 // ReadDependenciesV1 is a convenience method for returning a bonafide
 // Dependencies reference after reading from the applicable section from
 // the provided bundle
-func (b ExtendedBundle) ReadDependenciesV1() (depsv1.Dependencies, error) {
+func (b ExtendedBundle) ReadDependenciesV1() (depsv1ext.Dependencies, error) {
 	raw, err := b.DependencyV1Reader()
 	if err != nil {
-		return depsv1.Dependencies{}, err
+		return depsv1ext.Dependencies{}, err
 	}
 
-	deps, ok := raw.(depsv1.Dependencies)
+	deps, ok := raw.(depsv1ext.Dependencies)
 	if !ok {
-		return depsv1.Dependencies{}, errors.New("unable to read dependencies extension data")
+		return depsv1ext.Dependencies{}, errors.New("unable to read dependencies extension data")
 	}
 
 	// Return the dependencies
@@ -61,7 +61,7 @@ func (b ExtendedBundle) DependencyV1Reader() (interface{}, error) {
 		return nil, fmt.Errorf("could not marshal the untyped dependencies extension data %q: %w", string(dataB), err)
 	}
 
-	deps := depsv1.Dependencies{}
+	deps := depsv1ext.Dependencies{}
 	err = json.Unmarshal(dataB, &deps)
 	if err != nil {
 		return nil, fmt.Errorf("could not unmarshal the dependencies extension %q: %w", string(dataB), err)
diff --git a/pkg/cnab/extensions.go b/pkg/cnab/extensions.go
index b1698ff9..74379dde 100644
--- a/pkg/cnab/extensions.go
+++ b/pkg/cnab/extensions.go
@@ -1,5 +1,7 @@
 package cnab
 
+import "fmt"
+
 const (
 	// PorterExtension is the key for all Porter configuration stored the the custom section of bundles.
 	PorterExtension = "sh.porter"
@@ -15,6 +17,68 @@ const (
 	PorterInternal = "porter-internal"
 )
 
+// RequiredExtension represents a required extension that is known and supported by Porter
+type RequiredExtension struct {
+	Shorthand string
+	Key       string
+	Schema    string
+	Reader    func(b ExtendedBundle) (interface{}, error)
+}
+
+// SupportedExtensions represent a listing of the current required extensions
+// that Porter supports
+var SupportedExtensions = []RequiredExtension{
+	DependenciesV1Extension,
+	DockerExtension,
+	FileParameterExtension,
+	ParameterSourcesExtension,
+}
+
+// ProcessedExtensions represents a map of the extension name to the
+// processed extension configuration
+type ProcessedExtensions map[string]interface{}
+
+// ProcessRequiredExtensions checks all required extensions in the provided
+// bundle and makes sure Porter supports them.
+//
+// If an unsupported required extension is found, an error is returned.
+//
+// For each supported required extension, the configuration for that extension
+// is read and returned in the form of a map of the extension name to
+// the extension configuration
+func (b ExtendedBundle) ProcessRequiredExtensions() (ProcessedExtensions, error) {
+	processed := ProcessedExtensions{}
+	for _, reqExt := range b.RequiredExtensions {
+		supportedExtension, err := GetSupportedExtension(reqExt)
+		if err != nil {
+			return processed, err
+		}
+
+		raw, err := supportedExtension.Reader(b)
+		if err != nil {
+			return processed, fmt.Errorf("unable to process extension: %s: %w", reqExt, err)
+		}
+
+		processed[supportedExtension.Key] = raw
+	}
+
+	return processed, nil
+}
+
+// GetSupportedExtension returns a supported extension according to the
+// provided name, or an error
+func GetSupportedExtension(e string) (*RequiredExtension, error) {
+	for _, ext := range SupportedExtensions {
+		// TODO(v1) we should only check for the key in v1.0.0
+		// We are checking for both because of a bug in the cnab dependencies spec
+		// https://github.com/cnabio/cnab-spec/issues/403
+		if e == ext.Key || e == ext.Shorthand {
+			return &ext, nil
+		}
+	}
+	return nil, fmt.Errorf("unsupported required extension: %s", e)
+}
+
 // SupportsExtension checks if the bundle supports the specified CNAB extension.
 func (b ExtendedBundle) SupportsExtension(key string) bool {
 	for _, ext := range b.RequiredExtensions {
diff --git a/pkg/cnab/extensions/dependencies/v1/doc.go b/pkg/cnab/extensions/dependencies/v1/doc.go
new file mode 100644
index 00000000..b739e8b5
--- /dev/null
+++ b/pkg/cnab/extensions/dependencies/v1/doc.go
@@ -0,0 +1,2 @@
+// Package v1 defines the v1 CNAB Dependency specification, io.cnab.dependencies.
+package v1
diff --git a/pkg/cnab/reference.go b/pkg/cnab/oci_reference.go
similarity index 100%
rename from pkg/cnab/reference.go
rename to pkg/cnab/oci_reference.go
diff --git a/pkg/cnab/reference_test.go b/pkg/cnab/oci_reference_test.go
similarity index 100%
rename from pkg/cnab/reference_test.go
rename to pkg/cnab/oci_reference_test.go
diff --git a/pkg/cnab/required.go b/pkg/cnab/required.go
deleted file mode 100644
index 98888eba..00000000
--- a/pkg/cnab/required.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package cnab
-
-import "fmt"
-
-// RequiredExtension represents a required extension that is known and supported by Porter
-type RequiredExtension struct {
-	Shorthand string
-	Key       string
-	Schema    string
-	Reader    func(b ExtendedBundle) (interface{}, error)
-}
-
-// SupportedExtensions represent a listing of the current required extensions
-// that Porter supports
-var SupportedExtensions = []RequiredExtension{
-	DependenciesV1Extension,
-	DockerExtension,
-	FileParameterExtension,
-	ParameterSourcesExtension,
-}
-
-// ProcessedExtensions represents a map of the extension name to the
-// processed extension configuration
-type ProcessedExtensions map[string]interface{}
-
-// ProcessRequiredExtensions checks all required extensions in the provided
-// bundle and makes sure Porter supports them.
-//
-// If an unsupported required extension is found, an error is returned.
-//
-// For each supported required extension, the configuration for that extension
-// is read and returned in the form of a map of the extension name to
-// the extension configuration
-func (b ExtendedBundle) ProcessRequiredExtensions() (ProcessedExtensions, error) {
-	processed := ProcessedExtensions{}
-	for _, reqExt := range b.RequiredExtensions {
-		supportedExtension, err := GetSupportedExtension(reqExt)
-		if err != nil {
-			return processed, err
-		}
-
-		raw, err := supportedExtension.Reader(b)
-		if err != nil {
-			return processed, fmt.Errorf("unable to process extension: %s: %w", reqExt, err)
-		}
-
-		processed[supportedExtension.Key] = raw
-	}
-
-	return processed, nil
-}
-
-// GetSupportedExtension returns a supported extension according to the
-// provided name, or an error
-func GetSupportedExtension(e string) (*RequiredExtension, error) {
-	for _, ext := range SupportedExtensions {
-		// TODO(v1) we should only check for the key in v1.0.0
-		// We are checking for both because of a bug in the cnab dependencies spec
-		// https://github.com/cnabio/cnab-spec/issues/403
-		if e == ext.Key || e == ext.Shorthand {
-			return &ext, nil
-		}
-	}
-	return nil, fmt.Errorf("unsupported required extension: %s", e)
-}
diff --git a/pkg/porter/dependencies.go b/pkg/porter/dependencies.go
index e9f0a7d4..a6ed1283 100644
--- a/pkg/porter/dependencies.go
+++ b/pkg/porter/dependencies.go
@@ -52,7 +52,7 @@ func newDependencyExecutioner(p *Porter, installation storage.Installation, acti
 }
 
 type queuedDependency struct {
-	cnab.DependencyLock
+	depsv1.DependencyLock
 	BundleReference cnab.BundleReference
 	Parameters      map[string]string
 
@@ -158,7 +158,7 @@ func (e *dependencyExecutioner) identifyDependencies(ctx context.Context) error
 		return span.Error(errors.New("identifyDependencies failed to load the bundle because no bundle was specified. Please report this bug to https://github.com/getporter/porter/issues/new/choose"))
 	}
 
-	solver := &cnab.DependencySolver{}
+	solver := &depsv1.DependencySolver{}
 	locks, err := solver.ResolveDependencies(bun)
 	if err != nil {
 		return span.Error(err)
diff --git a/pkg/porter/explain.go b/pkg/porter/explain.go
index b231b023..4c5ba19f 100644
--- a/pkg/porter/explain.go
+++ b/pkg/porter/explain.go
@@ -7,6 +7,8 @@ import (
 	"strconv"
 	"strings"
 
+	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+
 	"get.porter.sh/porter/pkg/cnab"
 	configadapter "get.porter.sh/porter/pkg/cnab/config-adapter"
 	"get.porter.sh/porter/pkg/portercontext"
@@ -190,7 +192,7 @@ func generatePrintable(bun cnab.ExtendedBundle, action string) (*PrintableBundle
 		stamp = configadapter.Stamp{}
 	}
 
-	solver := &cnab.DependencySolver{}
+	solver := &depsv1.DependencySolver{}
 	deps, err := solver.ResolveDependencies(bun)
 	if err != nil {
 		return nil, fmt.Errorf("error resolving bundle dependencies: %w", err)
diff --git a/pkg/porter/parameters.go b/pkg/porter/parameters.go
index a7bc7459..510fa983 100644
--- a/pkg/porter/parameters.go
+++ b/pkg/porter/parameters.go
@@ -11,8 +11,9 @@ import (
 	"strings"
 	"time"
 
-	"get.porter.sh/porter/pkg/cnab"
 	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+
+	"get.porter.sh/porter/pkg/cnab"
 	"get.porter.sh/porter/pkg/editor"
 	"get.porter.sh/porter/pkg/encoding"
 	"get.porter.sh/porter/pkg/generator"


Test patch:
diff --git a/pkg/cnab/config-adapter/adapter_test.go b/pkg/cnab/config-adapter/adapter_test.go
index 60f71eea..2ac7a80a 100644
--- a/pkg/cnab/config-adapter/adapter_test.go
+++ b/pkg/cnab/config-adapter/adapter_test.go
@@ -8,7 +8,7 @@ import (
 	"testing"
 
 	"get.porter.sh/porter/pkg/cnab"
-	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
 	"get.porter.sh/porter/pkg/config"
 	"get.porter.sh/porter/pkg/manifest"
 	"get.porter.sh/porter/pkg/mixin"
@@ -86,6 +86,7 @@ func TestManifestConverter_ToBundle(t *testing.T) {
 	assert.Contains(t, bun.Definitions, "porter-debug-parameter", "porter-debug definition was not defined")
 
 	assert.True(t, bun.HasDependenciesV1(), "DependenciesV1 was not populated")
+	assert.Contains(t, bun.RequiredExtensions, "io.cnab.dependencies")
 
 	assert.Len(t, bun.Outputs, 1, "expected one output for the bundle state")
 }
@@ -533,29 +534,29 @@ func TestManifestConverter_generateBundleOutputs(t *testing.T) {
 	require.Equal(t, wantDefinitions, defs)
 }
 
-func TestManifestConverter_generateDependencies(t *testing.T) {
+func TestManifestConverter_generateDependenciesv1(t *testing.T) {
 	t.Parallel()
 
 	testcases := []struct {
 		name    string
-		wantDep depsv1.Dependency
+		wantDep depsv1ext.Dependency
 	}{
-		{"no-version", depsv1.Dependency{
+		{"no-version", depsv1ext.Dependency{
 			Name:   "mysql",
 			Bundle: "getporter/azure-mysql:5.7",
 		}},
-		{"no-ranges, uses prerelease", depsv1.Dependency{
+		{"no-ranges, uses prerelease", depsv1ext.Dependency{
 			Name:   "ad",
 			Bundle: "getporter/azure-active-directory",
-			Version: &depsv1.DependencyVersion{
+			Version: &depsv1ext.DependencyVersion{
 				AllowPrereleases: true,
 				Ranges:           []string{"1.0.0-0"},
 			},
 		}},
-		{"with-ranges", depsv1.Dependency{
+		{"with-ranges", depsv1ext.Dependency{
 			Name:   "storage",
 			Bundle: "getporter/azure-blob-storage",
-			Version: &depsv1.DependencyVersion{
+			Version: &depsv1ext.DependencyVersion{
 				Ranges: []string{
 					"1.x - 2,2.1 - 3.x",
 				},
@@ -581,12 +582,12 @@ func TestManifestConverter_generateDependencies(t *testing.T) {
 			depsExt, depsExtKey, err := a.generateDependencies()
 			require.NoError(t, err)
 			require.Equal(t, cnab.DependenciesV1ExtensionKey, depsExtKey, "expected the v1 dependencies extension key")
-			require.IsType(t, &depsv1.Dependencies{}, depsExt, "expected a v1 dependencies extension section")
-			deps := depsExt.(*depsv1.Dependencies)
+			require.IsType(t, &depsv1ext.Dependencies{}, depsExt, "expected a v1 dependencies extension section")
+			deps := depsExt.(*depsv1ext.Dependencies)
 			require.Len(t, deps.Requires, 3, "incorrect number of dependencies were generated")
 			require.Equal(t, []string{"mysql", "ad", "storage"}, deps.Sequence, "incorrect sequence was generated")
 
-			var dep *depsv1.Dependency
+			var dep *depsv1ext.Dependency
 			for _, d := range deps.Requires {
 				if d.Bundle == tc.wantDep.Bundle {
 					dep = &d
diff --git a/pkg/cnab/solver_test.go b/pkg/cnab/dependencies/v1/solver_test.go
similarity index 65%
rename from pkg/cnab/solver_test.go
rename to pkg/cnab/dependencies/v1/solver_test.go
index 416d6555..d0e4b93c 100644
--- a/pkg/cnab/solver_test.go
+++ b/pkg/cnab/dependencies/v1/solver_test.go
@@ -1,9 +1,10 @@
-package cnab
+package v1
 
 import (
 	"testing"
 
-	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+	"get.porter.sh/porter/pkg/cnab"
+	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
 	"github.com/cnabio/cnab-go/bundle"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -12,10 +13,10 @@ import (
 func TestDependencySolver_ResolveDependencies(t *testing.T) {
 	t.Parallel()
 
-	bun := NewBundle(bundle.Bundle{
+	bun := cnab.NewBundle(bundle.Bundle{
 		Custom: map[string]interface{}{
-			DependenciesV1ExtensionKey: depsv1.Dependencies{
-				Requires: map[string]depsv1.Dependency{
+			cnab.DependenciesV1ExtensionKey: depsv1ext.Dependencies{
+				Requires: map[string]depsv1ext.Dependency{
 					"mysql": {
 						Bundle: "getporter/mysql:5.7",
 					},
@@ -52,30 +53,30 @@ func TestDependencySolver_ResolveVersion(t *testing.T) {
 
 	testcases := []struct {
 		name        string
-		dep         depsv1.Dependency
+		dep         depsv1ext.Dependency
 		wantVersion string
 		wantError   string
 	}{
 		{name: "pinned version",
-			dep:         depsv1.Dependency{Bundle: "mysql:5.7"},
+			dep:         depsv1ext.Dependency{Bundle: "mysql:5.7"},
 			wantVersion: "5.7"},
 		{name: "unimplemented range",
-			dep:       depsv1.Dependency{Bundle: "mysql", Version: &depsv1.DependencyVersion{Ranges: []string{"1 - 1.5"}}},
+			dep:       depsv1ext.Dependency{Bundle: "mysql", Version: &depsv1ext.DependencyVersion{Ranges: []string{"1 - 1.5"}}},
 			wantError: "not implemented"},
 		{name: "default tag to latest",
-			dep:         depsv1.Dependency{Bundle: "getporterci/porter-test-only-latest"},
+			dep:         depsv1ext.Dependency{Bundle: "getporterci/porter-test-only-latest"},
 			wantVersion: "latest"},
 		{name: "no default tag",
-			dep:       depsv1.Dependency{Bundle: "getporterci/porter-test-no-default-tag"},
+			dep:       depsv1ext.Dependency{Bundle: "getporterci/porter-test-no-default-tag"},
 			wantError: "no tag was specified"},
 		{name: "default tag to highest semver",
-			dep:         depsv1.Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &depsv1.DependencyVersion{Ranges: nil, AllowPrereleases: true}},
+			dep:         depsv1ext.Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &depsv1ext.DependencyVersion{Ranges: nil, AllowPrereleases: true}},
 			wantVersion: "v1.3-beta1"},
 		{name: "default tag to highest semver, explicitly excluding prereleases",
-			dep:         depsv1.Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &depsv1.DependencyVersion{Ranges: nil, AllowPrereleases: false}},
+			dep:         depsv1ext.Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &depsv1ext.DependencyVersion{Ranges: nil, AllowPrereleases: false}},
 			wantVersion: "v1.2"},
 		{name: "default tag to highest semver, excluding prereleases by default",
-			dep:         depsv1.Dependency{Bundle: "getporterci/porter-test-with-versions"},
+			dep:         depsv1ext.Dependency{Bundle: "getporterci/porter-test-with-versions"},
 			wantVersion: "v1.2"},
 	}
 
diff --git a/pkg/cnab/dependencies/v1/types.go b/pkg/cnab/extensions/dependencies/v1/types.go
similarity index 100%
rename from pkg/cnab/dependencies/v1/types.go
rename to pkg/cnab/extensions/dependencies/v1/types.go
diff --git a/pkg/cnab/dependencies/v1/types_test.go b/pkg/cnab/extensions/dependencies/v1/types_test.go
similarity index 100%
rename from pkg/cnab/dependencies/v1/types_test.go
rename to pkg/cnab/extensions/dependencies/v1/types_test.go
diff --git a/pkg/cnab/extensions_test.go b/pkg/cnab/extensions_test.go
index 4045244d..907018d2 100644
--- a/pkg/cnab/extensions_test.go
+++ b/pkg/cnab/extensions_test.go
@@ -1,12 +1,108 @@
 package cnab
 
 import (
+	"fmt"
 	"testing"
 
+	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
 	"github.com/cnabio/cnab-go/bundle"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
+func TestProcessRequiredExtensions(t *testing.T) {
+	t.Parallel()
+
+	t.Run("supported", func(t *testing.T) {
+		t.Parallel()
+
+		bun := ReadTestBundle(t, "testdata/bundle.json")
+		exts, err := bun.ProcessRequiredExtensions()
+		require.NoError(t, err, "could not process required extensions")
+
+		expected := ProcessedExtensions{
+			"sh.porter.file-parameters": nil,
+			"io.cnab.dependencies": depsv1ext.Dependencies{
+				Requires: map[string]depsv1ext.Dependency{
+					"storage": depsv1ext.Dependency{
+						Bundle: "somecloud/blob-storage",
+					},
+					"mysql": depsv1ext.Dependency{
+						Bundle: "somecloud/mysql",
+						Version: &depsv1ext.DependencyVersion{
+							AllowPrereleases: true,
+							Ranges:           []string{"5.7.x"},
+						},
+					},
+				},
+			},
+			"io.cnab.parameter-sources": ParameterSources{
+				"tfstate": ParameterSource{
+					Priority: []string{ParameterSourceTypeOutput},
+					Sources: ParameterSourceMap{
+						ParameterSourceTypeOutput: OutputParameterSource{"tfstate"},
+					},
+				},
+				"mysql_connstr": ParameterSource{
+					Priority: []string{ParameterSourceTypeDependencyOutput},
+					Sources: ParameterSourceMap{
+						ParameterSourceTypeDependencyOutput: DependencyOutputParameterSource{
+							Dependency: "mysql",
+							OutputName: "connstr",
+						},
+					},
+				},
+			},
+		}
+		require.Equal(t, expected, exts)
+	})
+
+	t.Run("supported unprocessable", func(t *testing.T) {
+		t.Parallel()
+
+		bun := ReadTestBundle(t, "testdata/bundle-supported-unprocessable.json")
+		_, err := bun.ProcessRequiredExtensions()
+		require.EqualError(t, err, "unable to process extension: io.cnab.docker: no custom extension configuration found")
+	})
+
+	t.Run("unsupported", func(t *testing.T) {
+		t.Parallel()
+
+		bun := ReadTestBundle(t, "testdata/bundle-unsupported-required.json")
+		_, err := bun.ProcessRequiredExtensions()
+		require.EqualError(t, err, "unsupported required extension: donuts")
+	})
+}
+
+func TestGetSupportedExtension(t *testing.T) {
+	t.Parallel()
+
+	for _, supported := range SupportedExtensions {
+		t.Run(fmt.Sprintf("%s - shorthand", supported.Shorthand), func(t *testing.T) {
+			t.Parallel()
+
+			ext, err := GetSupportedExtension(supported.Shorthand)
+			require.NoError(t, err)
+			require.Equal(t, supported.Key, ext.Key)
+		})
+
+		t.Run(fmt.Sprintf("%s - key", supported.Key), func(t *testing.T) {
+			t.Parallel()
+
+			ext, err := GetSupportedExtension(supported.Key)
+			require.NoError(t, err)
+			require.Equal(t, supported.Key, ext.Key)
+		})
+	}
+
+	t.Run("unsupported", func(t *testing.T) {
+		t.Parallel()
+
+		_, err := GetSupportedExtension("donuts")
+		require.EqualError(t, err, "unsupported required extension: donuts")
+	})
+}
+
 func TestSupportsExtension(t *testing.T) {
 	t.Run("key present", func(t *testing.T) {
 		b := NewBundle(bundle.Bundle{RequiredExtensions: []string{"io.test.thing"}})
diff --git a/pkg/cnab/required_test.go b/pkg/cnab/required_test.go
deleted file mode 100644
index 433388cd..00000000
--- a/pkg/cnab/required_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package cnab
-
-import (
-	"fmt"
-	"testing"
-
-	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
-	"github.com/stretchr/testify/require"
-)
-
-func TestProcessRequiredExtensions(t *testing.T) {
-	t.Parallel()
-
-	t.Run("supported", func(t *testing.T) {
-		t.Parallel()
-
-		bun := ReadTestBundle(t, "testdata/bundle.json")
-		exts, err := bun.ProcessRequiredExtensions()
-		require.NoError(t, err, "could not process required extensions")
-
-		expected := ProcessedExtensions{
-			"sh.porter.file-parameters": nil,
-			"io.cnab.dependencies": depsv1.Dependencies{
-				Requires: map[string]depsv1.Dependency{
-					"storage": depsv1.Dependency{
-						Bundle: "somecloud/blob-storage",
-					},
-					"mysql": depsv1.Dependency{
-						Bundle: "somecloud/mysql",
-						Version: &depsv1.DependencyVersion{
-							AllowPrereleases: true,
-							Ranges:           []string{"5.7.x"},
-						},
-					},
-				},
-			},
-			"io.cnab.parameter-sources": ParameterSources{
-				"tfstate": ParameterSource{
-					Priority: []string{ParameterSourceTypeOutput},
-					Sources: ParameterSourceMap{
-						ParameterSourceTypeOutput: OutputParameterSource{"tfstate"},
-					},
-				},
-				"mysql_connstr": ParameterSource{
-					Priority: []string{ParameterSourceTypeDependencyOutput},
-					Sources: ParameterSourceMap{
-						ParameterSourceTypeDependencyOutput: DependencyOutputParameterSource{
-							Dependency: "mysql",
-							OutputName: "connstr",
-						},
-					},
-				},
-			},
-		}
-		require.Equal(t, expected, exts)
-	})
-
-	t.Run("supported unprocessable", func(t *testing.T) {
-		t.Parallel()
-
-		bun := ReadTestBundle(t, "testdata/bundle-supported-unprocessable.json")
-		_, err := bun.ProcessRequiredExtensions()
-		require.EqualError(t, err, "unable to process extension: io.cnab.docker: no custom extension configuration found")
-	})
-
-	t.Run("unsupported", func(t *testing.T) {
-		t.Parallel()
-
-		bun := ReadTestBundle(t, "testdata/bundle-unsupported-required.json")
-		_, err := bun.ProcessRequiredExtensions()
-		require.EqualError(t, err, "unsupported required extension: donuts")
-	})
-}
-
-func TestGetSupportedExtension(t *testing.T) {
-	t.Parallel()
-
-	for _, supported := range SupportedExtensions {
-		t.Run(fmt.Sprintf("%s - shorthand", supported.Shorthand), func(t *testing.T) {
-			t.Parallel()
-
-			ext, err := GetSupportedExtension(supported.Shorthand)
-			require.NoError(t, err)
-			require.Equal(t, supported.Key, ext.Key)
-		})
-
-		t.Run(fmt.Sprintf("%s - key", supported.Key), func(t *testing.T) {
-			t.Parallel()
-
-			ext, err := GetSupportedExtension(supported.Key)
-			require.NoError(t, err)
-			require.Equal(t, supported.Key, ext.Key)
-		})
-	}
-
-	t.Run("unsupported", func(t *testing.T) {
-		t.Parallel()
-
-		_, err := GetSupportedExtension("donuts")
-		require.EqualError(t, err, "unsupported required extension: donuts")
-	})
-}
diff --git a/pkg/porter/explain_test.go b/pkg/porter/explain_test.go
index d1023454..2b958aeb 100644
--- a/pkg/porter/explain_test.go
+++ b/pkg/porter/explain_test.go
@@ -5,7 +5,7 @@ import (
 	"testing"
 
 	"get.porter.sh/porter/pkg/cnab"
-	depsv1 "get.porter.sh/porter/pkg/cnab/dependencies/v1"
+	depsv1ext "get.porter.sh/porter/pkg/cnab/extensions/dependencies/v1"
 	"get.porter.sh/porter/pkg/portercontext"
 	"get.porter.sh/porter/pkg/test"
 	"github.com/cnabio/cnab-go/bundle"
@@ -417,9 +417,9 @@ func TestExplain_generatePrintableBundleDependencies(t *testing.T) {
 	sequenceMock := []string{"nginx", "storage", "mysql"}
 	bun := cnab.NewBundle(bundle.Bundle{
 		Custom: map[string]interface{}{
-			cnab.DependenciesV1ExtensionKey: depsv1.Dependencies{
+			cnab.DependenciesV1ExtensionKey: depsv1ext.Dependencies{
 				Sequence: sequenceMock,
-				Requires: map[string]depsv1.Dependency{
+				Requires: map[string]depsv1ext.Dependency{
 					"mysql": {
 						Name:   "mysql",
 						Bundle: "somecloud/mysql:0.1.0",