Skip to content

Commit

Permalink
Add GetLocatorType() and tests
Browse files Browse the repository at this point in the history
- Introduces `LocatorType` to help categorize different types of `--buildpack` string values

Signed-off-by: Andrew Meyer <[email protected]>
Signed-off-by: Micah Young <[email protected]>
  • Loading branch information
ameyer-pivotal authored and Javier Romero and Andrew Meyer committed Feb 12, 2020
1 parent aa7a463 commit 55a5121
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 0 deletions.
82 changes: 82 additions & 0 deletions internal/buildpack/locatortype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package buildpack

import (
"fmt"
"strings"

"github.com/google/go-containerregistry/pkg/name"

"github.com/buildpacks/pack/internal/paths"
"github.com/buildpacks/pack/internal/style"
)

type LocatorType int

const (
InvalidLocator = iota
FromBuilderLocator
URILocator
IDLocator
PackageLocator
)

const fromBuilderPrefix = "from=builder"

func (l LocatorType) String() string {
return []string{
"InvalidLocator",
"FromBuilderLocator",
"URILocator",
"IDLocator",
"PackageLocator",
}[l]
}

// GetLocatorType determines which type of locator is designated by the given input.
// If a type cannot be determined, `INVALID_LOCATOR` will be returned. If an error
// is encountered, it will be returned.
func GetLocatorType(locator string, idsFromBuilder []string) (LocatorType, error) {
if locator == fromBuilderPrefix {
return FromBuilderLocator, nil
}

if strings.HasPrefix(locator, fromBuilderPrefix+":") {
if !builderMatchFound(locator, idsFromBuilder) {
return InvalidLocator, fmt.Errorf("%s is not a valid identifier", style.Symbol(locator))
}
return IDLocator, nil
}

if paths.IsURI(locator) {
return URILocator, nil
}

exists, err := paths.Exists(locator)
if err != nil {
return InvalidLocator, err
}
if exists {
return URILocator, nil
}

if builderMatchFound(locator, idsFromBuilder) {
return IDLocator, nil
}

if _, err := name.ParseReference(locator); err == nil {
return PackageLocator, nil
}

return InvalidLocator, nil
}

func builderMatchFound(locator string, candidates []string) bool {
id, version := ParseIDLocator(locator)
for _, c := range candidates {
candID, candVer := ParseIDLocator(c)
if id == candID && (version == "" || version == candVer) {
return true
}
}
return false
}
129 changes: 129 additions & 0 deletions internal/buildpack/locatortype_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package buildpack_test

import (
"fmt"
"path/filepath"
"testing"

"github.com/heroku/color"
"github.com/sclevine/spec"
"github.com/sclevine/spec/report"

"github.com/buildpacks/pack/internal/buildpack"
h "github.com/buildpacks/pack/testhelpers"
)

func TestGetLocatorType(t *testing.T) {
color.Disable(true)
defer color.Disable(false)
spec.Run(t, "testGetLocatorType", testGetLocatorType, spec.Parallel(), spec.Report(report.Terminal{}))
}

func testGetLocatorType(t *testing.T, when spec.G, it spec.S) {
type testCase struct {
locator string
builderIDs []string
expectedType buildpack.LocatorType
expectedErr string
localPath string
}

var localPath = func(path string) string {
return filepath.Join("testdata", path)
}

for _, tc := range []testCase{
{
locator: "from=builder",
expectedType: buildpack.FromBuilderLocator,
},
{
locator: "from=builder:some-bp",
builderIDs: []string{"some-bp@some-version"},
expectedType: buildpack.IDLocator,
},
{
locator: "from=builder:some-bp",
builderIDs: nil,
expectedErr: "'from=builder:some-bp' is not a valid identifier",
},
{
locator: "from=builder:some-bp@some-other-version",
builderIDs: []string{"some-bp@some-version"},
expectedErr: "'from=builder:some-bp@some-other-version' is not a valid identifier",
},
{
locator: "some-bp",
builderIDs: []string{"some-bp"},
expectedType: buildpack.IDLocator,
},
{
locator: localPath("some-bp"),
builderIDs: []string{localPath("some-bp") + "@some-version"},
localPath: localPath("some-bp"),
expectedType: buildpack.URILocator,
},
{
locator: "https://example.com/buildpack.tgz",
expectedType: buildpack.URILocator,
},
{
locator: "cnbs/some-bp",
builderIDs: nil,
localPath: "",
expectedType: buildpack.PackageLocator,
},
{
locator: "cnbs/some-bp@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
expectedType: buildpack.PackageLocator,
},
{
locator: "cnbs/some-bp:some-tag",
expectedType: buildpack.PackageLocator,
},
{
locator: "cnbs/some-bp:some-tag@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
expectedType: buildpack.PackageLocator,
},
{
locator: "registry.com/cnbs/some-bp",
expectedType: buildpack.PackageLocator,
},
{
locator: "registry.com/cnbs/some-bp@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
expectedType: buildpack.PackageLocator,
},
{
locator: "registry.com/cnbs/some-bp:some-tag",
expectedType: buildpack.PackageLocator,
},
{
locator: "registry.com/cnbs/some-bp:some-tag@sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
expectedType: buildpack.PackageLocator,
},
} {
tc := tc

desc := fmt.Sprintf("locator is %s", tc.locator)
if len(tc.builderIDs) > 0 {
desc += fmt.Sprintf(" and builder has IDs %s", tc.builderIDs)
}
if tc.localPath != "" {
desc += fmt.Sprintf(" and a local path exists at '%s'", tc.localPath)
}

when(desc, func() {
it(fmt.Sprintf("should return '%s'", tc.expectedType), func() {
actualType, actualErr := buildpack.GetLocatorType(tc.locator, tc.builderIDs)

if tc.expectedErr == "" {
h.AssertNil(t, actualErr)
} else {
h.AssertError(t, actualErr, tc.expectedErr)
}

h.AssertEq(t, actualType, tc.expectedType)
})
})
}
}
13 changes: 13 additions & 0 deletions internal/buildpack/parsename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package buildpack

import "strings"

// ParseIDLocator parses a buildpack locator of the form <id>@<version> into its ID and version.
// If version is omitted, the version returned will be empty. Any "from=builder:" prefix will be ignored.
func ParseIDLocator(locator string) (id string, version string) {
parts := strings.Split(strings.TrimPrefix(locator, fromBuilderPrefix+":"), "@")
if len(parts) == 2 {
return parts[0], parts[1]
}
return parts[0], ""
}
Empty file.
10 changes: 10 additions & 0 deletions internal/paths/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ func IsDir(path string) (bool, error) {
return fileInfo.IsDir(), nil
}

func Exists(path string) (bool, error) {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false, nil // does not exist
}
return false, err // some other error
}
return true, nil
}

func FilePathToURI(path string) (string, error) {
var err error
if !filepath.IsAbs(path) {
Expand Down

0 comments on commit 55a5121

Please sign in to comment.