update platforms Parse to return platform spec, decouple matcher

Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
Jess Valarezo 2018-02-27 14:21:49 -08:00
parent fd2e1f6dec
commit ac8008437a
4 changed files with 36 additions and 26 deletions

View File

@ -193,10 +193,11 @@ func FilterPlatform(platform string, f HandlerFunc) HandlerFunc {
var descs []ocispec.Descriptor
if platform != "" && isMultiPlatform(desc.MediaType) {
matcher, err := platforms.Parse(platform)
p, err := platforms.Parse(platform)
if err != nil {
return nil, err
}
matcher := platforms.NewMatcher(p)
for _, d := range children {
if d.Platform == nil || matcher.Match(*d.Platform) {

View File

@ -131,13 +131,13 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
var (
matcher platforms.Matcher
m *ocispec.Manifest
err error
)
if platform != "" {
matcher, err = platforms.Parse(platform)
p, err := platforms.Parse(platform)
if err != nil {
return ocispec.Manifest{}, err
}
matcher = platforms.NewMatcher(p)
}
if err := Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {

View File

@ -122,18 +122,27 @@ var (
// Matcher matches platforms specifications, provided by an image or runtime.
type Matcher interface {
Spec() specs.Platform
Match(platform specs.Platform) bool
}
// NewMatcher returns a simple matcher based on the provided platform
// specification. The returned matcher only looks for equality based on os,
// architecture and variant.
//
// One may implement their own matcher if this doesn't provide the the required
// functionality.
//
// Applications should opt to use `Match` over directly parsing specifiers.
func NewMatcher(platform specs.Platform) Matcher {
return &matcher{
Platform: platform,
}
}
type matcher struct {
specs.Platform
}
func (m *matcher) Spec() specs.Platform {
return m.Platform
}
func (m *matcher) Match(platform specs.Platform) bool {
normalized := Normalize(platform)
return m.OS == normalized.OS &&
@ -153,19 +162,17 @@ func (m *matcher) String() string {
// value will be matched against the known set of operating systems, then fall
// back to the known set of architectures. The missing component will be
// inferred based on the local environment.
//
// Applications should opt to use `Match` over directly parsing specifiers.
func Parse(specifier string) (Matcher, error) {
func Parse(specifier string) (specs.Platform, error) {
if strings.Contains(specifier, "*") {
// TODO(stevvooe): need to work out exact wildcard handling
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: wildcards not yet supported", specifier)
return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: wildcards not yet supported", specifier)
}
parts := strings.Split(specifier, "/")
for _, part := range parts {
if !specifierRe.MatchString(part) {
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q is an invalid component of %q: platform specifier component must match %q", part, specifier, specifierRe.String())
return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q is an invalid component of %q: platform specifier component must match %q", part, specifier, specifierRe.String())
}
}
@ -183,35 +190,35 @@ func Parse(specifier string) (Matcher, error) {
p.Architecture = runtime.GOARCH
if p.Architecture == "arm" {
// TODO(stevvooe): Resolve arm variant, if not v6 (default)
return nil, errors.Wrapf(errdefs.ErrNotImplemented, "arm support not fully implemented")
return specs.Platform{}, errors.Wrapf(errdefs.ErrNotImplemented, "arm support not fully implemented")
}
return &matcher{p}, nil
return p, nil
}
p.Architecture, p.Variant = normalizeArch(parts[0], "")
if isKnownArch(p.Architecture) {
p.OS = runtime.GOOS
return &matcher{p}, nil
return p, nil
}
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: unknown operating system or architecture", specifier)
return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: unknown operating system or architecture", specifier)
case 2:
// In this case, we treat as a regular os/arch pair. We don't care
// about whether or not we know of the platform.
p.OS = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], "")
return &matcher{p}, nil
return p, nil
case 3:
// we have a fully specified variant, this is rare
p.OS = normalizeOS(parts[0])
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
return &matcher{p}, nil
return p, nil
}
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: cannot parse platform specifier", specifier)
return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: cannot parse platform specifier", specifier)
}
// Format returns a string specifier from the provided platform specification.

View File

@ -191,15 +191,17 @@ func TestParseSelector(t *testing.T) {
if testcase.skip {
t.Skip("this case is not yet supported")
}
m, err := Parse(testcase.input)
p, err := Parse(testcase.input)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(m.Spec(), testcase.expected) {
t.Fatalf("platform did not match expected: %#v != %#v", m.Spec(), testcase.expected)
if !reflect.DeepEqual(p, testcase.expected) {
t.Fatalf("platform did not match expected: %#v != %#v", p, testcase.expected)
}
m := NewMatcher(p)
// ensure that match works on the input to the output.
if ok := m.Match(testcase.expected); !ok {
t.Fatalf("expected specifier %q matches %v", testcase.input, testcase.expected)
@ -209,7 +211,7 @@ func TestParseSelector(t *testing.T) {
t.Fatalf("unexpected matcher string: %q != %q", fmt.Sprint(m), testcase.formatted)
}
formatted := Format(m.Spec())
formatted := Format(p)
if formatted != testcase.formatted {
t.Fatalf("unexpected format: %q != %q", formatted, testcase.formatted)
}
@ -220,8 +222,8 @@ func TestParseSelector(t *testing.T) {
t.Fatalf("error parsing formatted output: %v", err)
}
if Format(reparsed.Spec()) != formatted {
t.Fatalf("normalized output did not survive the round trip: %v != %v", Format(reparsed.Spec()), formatted)
if Format(reparsed) != formatted {
t.Fatalf("normalized output did not survive the round trip: %v != %v", Format(reparsed), formatted)
}
})
}