update platforms Parse to return platform spec, decouple matcher
Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
		| @@ -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) { | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jess Valarezo
					Jess Valarezo