// Package match implements Warehouse matchers on top of ggcr/v1/match package match import ( "errors" "fmt" "strings" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/match" "github.com/google/go-containerregistry/pkg/v1/partial" wh "edge-infra.dev/pkg/f8n/warehouse" "edge-infra.dev/pkg/f8n/warehouse/cluster" ) // TODO: doc strings, tests var ( // ErrMultipleMatches occurs when more than one matching result was discovered. ErrMultipleMatches = errors.New("multiple matches found") // ErrNoMatches occurs when a single match is requested and none are found. ErrNoMatches = errors.New("no match found") ) // Local type aliases type Matcher = match.Matcher var ( Annotation = match.Annotation Digests = match.Digests ) // Multiple returns a Matcher that executes all input matchers. If all return // true, a match is returned. func Multiple(matchers ...Matcher) Matcher { return func(desc v1.Descriptor) bool { matched := true for _, m := range matchers { if !m(desc) { matched = false } } return matched } } // Provider returns a Matcher that returns true if the descriptor references // an artifact that supports the input provider. func Provider(p cluster.Provider) Matcher { return func(desc v1.Descriptor) bool { providers := desc.Annotations[wh.AnnotationClusterProviders] switch { case providers == "": return false case strings.Contains(providers, p.String()): return true default: return false } } } // Name matches on the ref name annotation which refers to the package name that // the descriptor points to (e.g., for provider variants or dependencies). func RefName(n string) Matcher { return Annotation(wh.AnnotationRefName, n) } // Dependencies performs an inverse match on the ref name annotation to identify // dependencies on other packages. func Dependencies(pkgName string) Matcher { return func(desc v1.Descriptor) bool { if desc.Annotations == nil { return false } if desc.Annotations[wh.AnnotationRefName] == pkgName { return false } return true } } // FindImage searches for exactly one matching v1.Image in the input idx. // // If more than one is found, ErrMultipleMatches is returned. func FindImage(idx v1.ImageIndex, m Matcher) (v1.Image, error) { d, err := partial.FindImages(idx, m) if err != nil { return nil, err } switch { case len(d) > 1: return nil, fmt.Errorf("%w", ErrMultipleMatches) case len(d) == 1: return d[0], nil default: return nil, fmt.Errorf("%w", ErrNoMatches) } } // FindIndex searches for exactly one matching v1.ImageIndex in the input idx. // // If more than one is found, ErrMultipleMatches is returned. func FindIndex(idx v1.ImageIndex, m Matcher) (v1.ImageIndex, error) { d, err := partial.FindIndexes(idx, m) if err != nil { return nil, err } switch { case len(d) > 1: return nil, fmt.Errorf("%w", ErrMultipleMatches) case len(d) == 1: return d[0], nil default: return nil, fmt.Errorf("%w", ErrNoMatches) } } // FindManifest searches for exactly one matching v1.Descriptor in the input idx. // // If more than one is found, ErrMultipleMatches is returned. func FindManifest(idx v1.ImageIndex, m Matcher) (v1.Descriptor, error) { d, err := partial.FindManifests(idx, m) if err != nil { return v1.Descriptor{}, err } switch { case len(d) > 1: return v1.Descriptor{}, fmt.Errorf("%w", ErrMultipleMatches) case len(d) == 1: return d[0], nil default: return v1.Descriptor{}, fmt.Errorf("%w", ErrNoMatches) } } // FindIndexes is a local alias for partial.FindIndexes func FindIndexes(idx v1.ImageIndex, m Matcher) ([]v1.ImageIndex, error) { return partial.FindIndexes(idx, m) } // FindImages is a local alias for partial.FindImages func FindImages(idx v1.ImageIndex, m Matcher) ([]v1.Image, error) { return partial.FindImages(idx, m) } // FindManifests is a local alias for partial.FindManifests func FindManifests(idx v1.ImageIndex, m Matcher) ([]v1.Descriptor, error) { return partial.FindManifests(idx, m) }