update platforms Parse to return platform spec, decouple matcher
Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
parent
fd2e1f6dec
commit
ac8008437a
@ -193,10 +193,11 @@ func FilterPlatform(platform string, f HandlerFunc) HandlerFunc {
|
|||||||
|
|
||||||
var descs []ocispec.Descriptor
|
var descs []ocispec.Descriptor
|
||||||
if platform != "" && isMultiPlatform(desc.MediaType) {
|
if platform != "" && isMultiPlatform(desc.MediaType) {
|
||||||
matcher, err := platforms.Parse(platform)
|
p, err := platforms.Parse(platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
matcher := platforms.NewMatcher(p)
|
||||||
|
|
||||||
for _, d := range children {
|
for _, d := range children {
|
||||||
if d.Platform == nil || matcher.Match(*d.Platform) {
|
if d.Platform == nil || matcher.Match(*d.Platform) {
|
||||||
|
@ -131,13 +131,13 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
|
|||||||
var (
|
var (
|
||||||
matcher platforms.Matcher
|
matcher platforms.Matcher
|
||||||
m *ocispec.Manifest
|
m *ocispec.Manifest
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
if platform != "" {
|
if platform != "" {
|
||||||
matcher, err = platforms.Parse(platform)
|
p, err := platforms.Parse(platform)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ocispec.Manifest{}, err
|
return ocispec.Manifest{}, err
|
||||||
}
|
}
|
||||||
|
matcher = platforms.NewMatcher(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
if err := Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
||||||
|
@ -122,18 +122,27 @@ var (
|
|||||||
|
|
||||||
// Matcher matches platforms specifications, provided by an image or runtime.
|
// Matcher matches platforms specifications, provided by an image or runtime.
|
||||||
type Matcher interface {
|
type Matcher interface {
|
||||||
Spec() specs.Platform
|
|
||||||
Match(platform specs.Platform) bool
|
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 {
|
type matcher struct {
|
||||||
specs.Platform
|
specs.Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *matcher) Spec() specs.Platform {
|
|
||||||
return m.Platform
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *matcher) Match(platform specs.Platform) bool {
|
func (m *matcher) Match(platform specs.Platform) bool {
|
||||||
normalized := Normalize(platform)
|
normalized := Normalize(platform)
|
||||||
return m.OS == normalized.OS &&
|
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
|
// 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
|
// back to the known set of architectures. The missing component will be
|
||||||
// inferred based on the local environment.
|
// inferred based on the local environment.
|
||||||
//
|
func Parse(specifier string) (specs.Platform, error) {
|
||||||
// Applications should opt to use `Match` over directly parsing specifiers.
|
|
||||||
func Parse(specifier string) (Matcher, error) {
|
|
||||||
if strings.Contains(specifier, "*") {
|
if strings.Contains(specifier, "*") {
|
||||||
// TODO(stevvooe): need to work out exact wildcard handling
|
// 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, "/")
|
parts := strings.Split(specifier, "/")
|
||||||
|
|
||||||
for _, part := range parts {
|
for _, part := range parts {
|
||||||
if !specifierRe.MatchString(part) {
|
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
|
p.Architecture = runtime.GOARCH
|
||||||
if p.Architecture == "arm" {
|
if p.Architecture == "arm" {
|
||||||
// TODO(stevvooe): Resolve arm variant, if not v6 (default)
|
// 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], "")
|
p.Architecture, p.Variant = normalizeArch(parts[0], "")
|
||||||
if isKnownArch(p.Architecture) {
|
if isKnownArch(p.Architecture) {
|
||||||
p.OS = runtime.GOOS
|
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:
|
case 2:
|
||||||
// In this case, we treat as a regular os/arch pair. We don't care
|
// In this case, we treat as a regular os/arch pair. We don't care
|
||||||
// about whether or not we know of the platform.
|
// about whether or not we know of the platform.
|
||||||
p.OS = normalizeOS(parts[0])
|
p.OS = normalizeOS(parts[0])
|
||||||
p.Architecture, p.Variant = normalizeArch(parts[1], "")
|
p.Architecture, p.Variant = normalizeArch(parts[1], "")
|
||||||
|
|
||||||
return &matcher{p}, nil
|
return p, nil
|
||||||
case 3:
|
case 3:
|
||||||
// we have a fully specified variant, this is rare
|
// we have a fully specified variant, this is rare
|
||||||
p.OS = normalizeOS(parts[0])
|
p.OS = normalizeOS(parts[0])
|
||||||
p.Architecture, p.Variant = normalizeArch(parts[1], parts[2])
|
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.
|
// Format returns a string specifier from the provided platform specification.
|
||||||
|
@ -191,15 +191,17 @@ func TestParseSelector(t *testing.T) {
|
|||||||
if testcase.skip {
|
if testcase.skip {
|
||||||
t.Skip("this case is not yet supported")
|
t.Skip("this case is not yet supported")
|
||||||
}
|
}
|
||||||
m, err := Parse(testcase.input)
|
p, err := Parse(testcase.input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(m.Spec(), testcase.expected) {
|
if !reflect.DeepEqual(p, testcase.expected) {
|
||||||
t.Fatalf("platform did not match expected: %#v != %#v", m.Spec(), 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.
|
// ensure that match works on the input to the output.
|
||||||
if ok := m.Match(testcase.expected); !ok {
|
if ok := m.Match(testcase.expected); !ok {
|
||||||
t.Fatalf("expected specifier %q matches %v", testcase.input, testcase.expected)
|
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)
|
t.Fatalf("unexpected matcher string: %q != %q", fmt.Sprint(m), testcase.formatted)
|
||||||
}
|
}
|
||||||
|
|
||||||
formatted := Format(m.Spec())
|
formatted := Format(p)
|
||||||
if formatted != testcase.formatted {
|
if formatted != testcase.formatted {
|
||||||
t.Fatalf("unexpected format: %q != %q", 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)
|
t.Fatalf("error parsing formatted output: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if Format(reparsed.Spec()) != formatted {
|
if Format(reparsed) != formatted {
|
||||||
t.Fatalf("normalized output did not survive the round trip: %v != %v", Format(reparsed.Spec()), formatted)
|
t.Fatalf("normalized output did not survive the round trip: %v != %v", Format(reparsed), formatted)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user