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 | 		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) | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jess Valarezo
					Jess Valarezo