Skip to content

Commit

Permalink
Parse dependency references using docker library
Browse files Browse the repository at this point in the history
Fix a bug in how we parse references to bundle dependencies by switching
from parsing based on the presence of a colon to using the docker
references library instead, which properly handles ports in the registry
domain.
  • Loading branch information
carolynvs-msft committed Jun 23, 2020
1 parent 18b4a63 commit cc011db
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 21 deletions.
34 changes: 22 additions & 12 deletions pkg/cnab/extensions/solver.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand All @@ -30,33 +29,44 @@ 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)
}

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) {
Expand Down
25 changes: 16 additions & 9 deletions pkg/cnab/extensions/solver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ func TestDependencySolver_ResolveDependencies(t *testing.T) {
"mysql": {
Bundle: "getporter/mysql:5.7",
},
"nginx": {
Bundle: "localhost:5000/nginx:1.19",
},
},
},
},
Expand All @@ -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) {
Expand All @@ -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"},
Expand All @@ -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"},
Expand All @@ -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")
}
})
}
Expand Down

0 comments on commit cc011db

Please sign in to comment.