diff --git a/pkg/cnab/extensions/solver.go b/pkg/cnab/extensions/solver.go index 453bde1f0..637f88301 100644 --- a/pkg/cnab/extensions/solver.go +++ b/pkg/cnab/extensions/solver.go @@ -1,12 +1,11 @@ package extensions import ( - "fmt" "sort" - "strings" "github.com/Masterminds/semver" "github.com/cnabio/cnab-go/bundle" + "github.com/docker/distribution/reference" "github.com/google/go-containerregistry/pkg/crane" "github.com/pkg/errors" ) @@ -30,14 +29,14 @@ func (s *DependencySolver) ResolveDependencies(bun *bundle.Bundle) ([]Dependency q := make([]DependencyLock, 0, len(deps.Requires)) for alias, dep := range deps.Requires { - bundle := strings.Split(dep.Bundle, ":")[0] - version, err := s.ResolveVersion(alias, dep) + ref, err := s.ResolveVersion(alias, dep) if err != nil { return nil, err } + lock := DependencyLock{ Alias: alias, - Tag: fmt.Sprintf("%s:%s", bundle, version), + Tag: reference.FamiliarString(ref), } q = append(q, lock) } @@ -45,18 +44,29 @@ func (s *DependencySolver) ResolveDependencies(bun *bundle.Bundle) ([]Dependency return q, nil } -func (s *DependencySolver) ResolveVersion(alias string, dep Dependency) (string, error) { +// ResolveVersion returns the bundle name, its version and any error. +func (s *DependencySolver) ResolveVersion(alias string, dep Dependency) (reference.NamedTagged, error) { + ref, err := reference.ParseNormalizedNamed(dep.Bundle) + if err != nil { + return nil, errors.Wrapf(err, "error parsing dependency (%s) bundle %q as OCI reference", alias, dep.Bundle) + } + // Here is where we could split out this logic into multiple strategy funcs / structs if necessary if dep.Version == nil || len(dep.Version.Ranges) == 0 { - parts := strings.Split(dep.Bundle, ":") - if len(parts) > 1 { - return strings.Join(parts[1:], ""), nil - } else { - return s.determineDefaultTag(dep) + // Check if they specified an explicit tag in referenced bundle already + if taggedRef, ok := ref.(reference.NamedTagged); ok { + return taggedRef, nil } + + tag, err := s.determineDefaultTag(dep) + if err != nil { + return nil, err + } + + return reference.WithTag(ref, tag) } - return "", errors.Errorf("not implemented: dependency version range specified for %s", alias) + return nil, errors.Errorf("not implemented: dependency version range specified for %s", alias) } func (s *DependencySolver) determineDefaultTag(dep Dependency) (string, error) { diff --git a/pkg/cnab/extensions/solver_test.go b/pkg/cnab/extensions/solver_test.go index 3d13ab0ad..64f614ecc 100644 --- a/pkg/cnab/extensions/solver_test.go +++ b/pkg/cnab/extensions/solver_test.go @@ -18,6 +18,9 @@ func TestDependencySolver_ResolveDependencies(t *testing.T) { "mysql": { Bundle: "getporter/mysql:5.7", }, + "nginx": { + Bundle: "localhost:5000/nginx:1.19", + }, }, }, }, @@ -27,11 +30,15 @@ func TestDependencySolver_ResolveDependencies(t *testing.T) { locks, err := s.ResolveDependencies(bun) require.NoError(t, err) - require.Len(t, locks, 1) + require.Len(t, locks, 2) + + mysql := locks[0] + assert.Equal(t, "mysql", mysql.Alias) + assert.Equal(t, "getporter/mysql:5.7", mysql.Tag) - lock := locks[0] - assert.Equal(t, "mysql", lock.Alias) - assert.Equal(t, "getporter/mysql:5.7", lock.Tag) + nginx := locks[1] + assert.Equal(t, "nginx", nginx.Alias) + assert.Equal(t, "localhost:5000/nginx:1.19", nginx.Tag) } func TestDependencySolver_ResolveVersion(t *testing.T) { @@ -42,10 +49,10 @@ func TestDependencySolver_ResolveVersion(t *testing.T) { wantError string }{ {name: "pinned version", - dep: Dependency{"mysql:5.7", nil}, + dep: Dependency{Bundle: "mysql:5.7"}, wantVersion: "5.7"}, {name: "unimplemented range", - dep: Dependency{"mysql", &DependencyVersion{Ranges: []string{"1 - 1.5"}}}, + dep: Dependency{Bundle: "mysql", Version: &DependencyVersion{Ranges: []string{"1 - 1.5"}}}, wantError: "not implemented"}, {name: "default tag to latest", dep: Dependency{Bundle: "getporterci/porter-test-only-latest"}, @@ -54,10 +61,10 @@ func TestDependencySolver_ResolveVersion(t *testing.T) { dep: Dependency{Bundle: "getporterci/porter-test-no-default-tag"}, wantError: "no tag was specified"}, {name: "default tag to highest semver", - dep: Dependency{"getporterci/porter-test-with-versions", &DependencyVersion{Ranges: nil, AllowPrereleases: true}}, + dep: Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &DependencyVersion{Ranges: nil, AllowPrereleases: true}}, wantVersion: "v1.3-beta1"}, {name: "default tag to highest semver, explicitly excluding prereleases", - dep: Dependency{"getporterci/porter-test-with-versions", &DependencyVersion{Ranges: nil, AllowPrereleases: false}}, + dep: Dependency{Bundle: "getporterci/porter-test-with-versions", Version: &DependencyVersion{Ranges: nil, AllowPrereleases: false}}, wantVersion: "v1.2"}, {name: "default tag to highest semver, excluding prereleases by default", dep: Dependency{Bundle: "getporterci/porter-test-with-versions"}, @@ -75,7 +82,7 @@ func TestDependencySolver_ResolveVersion(t *testing.T) { } else { require.NoError(t, err, "ResolveVersion should not have returned an error") - assert.Equal(t, tc.wantVersion, version, "incorrect version resolved") + assert.Equal(t, tc.wantVersion, version.Tag(), "incorrect version resolved") } }) }