mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-21 22:41:42 +00:00
This commit creates a new version of the alpha configuration functions framework. Goals include: - Make it easy to build multi-version APIs with the framework (not previously facilitated at all). - Simplify the framework's APIs where redundant configuration options exist (leaving the most powerful, replacing others with helpers to maintain usability they provided). - Make the Framework's APIs more consistent (e.g. between the various template types, usage of kio.Filter, field names) - Decouple responsibilities (e.g. command creation, resource list processing, generation of templating functions). - Make the framework even more powerfully pluggable (e.g. any kio.Filter can be a selector, and the selector the framework provides is itself a filter built from reusable abstractions). - Improve documentation. - Make container patches merge fields (notably list fields like `env`) correctly.
220 lines
7.4 KiB
Go
220 lines
7.4 KiB
Go
// Copyright 2021 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package framework
|
|
|
|
import (
|
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
// Selector matches resources. A resource matches if and only if ALL of the Selector fields
|
|
// match the resource. An empty Selector matches all resources.
|
|
type Selector struct {
|
|
// Names is a list of metadata.names to match. If empty match all names.
|
|
// e.g. Names: ["foo", "bar"] matches if `metadata.name` is either "foo" or "bar".
|
|
Names []string `json:"names" yaml:"names"`
|
|
|
|
// Namespaces is a list of metadata.namespaces to match. If empty match all namespaces.
|
|
// e.g. Namespaces: ["foo", "bar"] matches if `metadata.namespace` is either "foo" or "bar".
|
|
Namespaces []string `json:"namespaces" yaml:"namespaces"`
|
|
|
|
// Kinds is a list of kinds to match. If empty match all kinds.
|
|
// e.g. Kinds: ["foo", "bar"] matches if `kind` is either "foo" or "bar".
|
|
Kinds []string `json:"kinds" yaml:"kinds"`
|
|
|
|
// APIVersions is a list of apiVersions to match. If empty apply match all apiVersions.
|
|
// e.g. APIVersions: ["foo/v1", "bar/v1"] matches if `apiVersion` is either "foo/v1" or "bar/v1".
|
|
APIVersions []string `json:"apiVersions" yaml:"apiVersions"`
|
|
|
|
// Labels is a collection of labels to match. All labels must match exactly.
|
|
// e.g. Labels: {"foo": "bar", "baz": "buz"] matches if BOTH "foo" and "baz" labels match.
|
|
Labels map[string]string `json:"labels" yaml:"labels"`
|
|
|
|
// Annotations is a collection of annotations to match. All annotations must match exactly.
|
|
// e.g. Annotations: {"foo": "bar", "baz": "buz"] matches if BOTH "foo" and "baz" annotations match.
|
|
Annotations map[string]string `json:"annotations" yaml:"annotations"`
|
|
|
|
// ResourceMatcher is an arbitrary function used to match resources.
|
|
// Selector matches if the function returns true.
|
|
ResourceMatcher func(*yaml.RNode) bool
|
|
|
|
// TemplateData if present will cause the selector values to be parsed as templates
|
|
// and rendered using TemplateData before they are used.
|
|
TemplateData interface{}
|
|
|
|
// FailOnEmptyMatch makes the selector return an error when no items are selected.
|
|
FailOnEmptyMatch bool
|
|
}
|
|
|
|
// Filter implements kio.Filter, returning only those items from the list that the selector
|
|
// matches.
|
|
func (s *Selector) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
andSel := AndSelector{TemplateData: s.TemplateData, FailOnEmptyMatch: s.FailOnEmptyMatch}
|
|
if s.Names != nil {
|
|
andSel.Matchers = append(andSel.Matchers, NameMatcher(s.Names...))
|
|
}
|
|
if s.Namespaces != nil {
|
|
andSel.Matchers = append(andSel.Matchers, NamespaceMatcher(s.Namespaces...))
|
|
}
|
|
if s.Kinds != nil {
|
|
andSel.Matchers = append(andSel.Matchers, KindMatcher(s.Kinds...))
|
|
}
|
|
if s.APIVersions != nil {
|
|
andSel.Matchers = append(andSel.Matchers, APIVersionMatcher(s.APIVersions...))
|
|
}
|
|
if s.Labels != nil {
|
|
andSel.Matchers = append(andSel.Matchers, LabelMatcher(s.Labels))
|
|
}
|
|
if s.Annotations != nil {
|
|
andSel.Matchers = append(andSel.Matchers, AnnotationMatcher(s.Annotations))
|
|
}
|
|
if s.ResourceMatcher != nil {
|
|
andSel.Matchers = append(andSel.Matchers, ResourceMatcherFunc(s.ResourceMatcher))
|
|
}
|
|
return andSel.Filter(items)
|
|
}
|
|
|
|
// MatchAll is a shorthand for building an AndSelector from a list of ResourceMatchers.
|
|
func MatchAll(matchers ...ResourceMatcher) *AndSelector {
|
|
return &AndSelector{Matchers: matchers}
|
|
}
|
|
|
|
// MatchAny is a shorthand for building an OrSelector from a list of ResourceMatchers.
|
|
func MatchAny(matchers ...ResourceMatcher) *OrSelector {
|
|
return &OrSelector{Matchers: matchers}
|
|
}
|
|
|
|
// OrSelector is a kio.Filter that selects resources when that match at least one of its embedded
|
|
// matchers.
|
|
type OrSelector struct {
|
|
// Matchers is the list of ResourceMatchers to try on the input resources.
|
|
Matchers []ResourceMatcher
|
|
// TemplateData, if present, is used to initialize any matchers that implement
|
|
// ResourceTemplateMatcher.
|
|
TemplateData interface{}
|
|
// FailOnEmptyMatch makes the selector return an error when no items are selected.
|
|
FailOnEmptyMatch bool
|
|
}
|
|
|
|
// Match implements ResourceMatcher so that OrSelectors can be composed
|
|
func (s *OrSelector) Match(item *yaml.RNode) bool {
|
|
for _, matcher := range s.Matchers {
|
|
if matcher.Match(item) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Filter implements kio.Filter, returning only those items from the list that the selector
|
|
// matches.
|
|
func (s *OrSelector) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
if err := initMatcherTemplates(s.Matchers, s.TemplateData); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var selectedItems []*yaml.RNode
|
|
for i := range items {
|
|
for _, matcher := range s.Matchers {
|
|
if matcher.Match(items[i]) {
|
|
selectedItems = append(selectedItems, items[i])
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if s.FailOnEmptyMatch && len(selectedItems) == 0 {
|
|
return nil, errors.Errorf("selector did not select any items")
|
|
}
|
|
return selectedItems, nil
|
|
}
|
|
|
|
// DefaultTemplateData makes OrSelector a ResourceTemplateMatcher.
|
|
// Although it does not contain templates itself, this allows it to support ResourceTemplateMatchers
|
|
// when being used as a matcher itself.
|
|
func (s *OrSelector) DefaultTemplateData(data interface{}) {
|
|
if s.TemplateData == nil {
|
|
s.TemplateData = data
|
|
}
|
|
}
|
|
|
|
func (s *OrSelector) InitTemplates() error {
|
|
return initMatcherTemplates(s.Matchers, s.TemplateData)
|
|
}
|
|
|
|
func initMatcherTemplates(matchers []ResourceMatcher, data interface{}) error {
|
|
for _, matcher := range matchers {
|
|
if tm, ok := matcher.(ResourceTemplateMatcher); ok {
|
|
tm.DefaultTemplateData(data)
|
|
if err := tm.InitTemplates(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var _ ResourceTemplateMatcher = &OrSelector{}
|
|
|
|
// OrSelector is a kio.Filter that selects resources when that match all of its embedded
|
|
// matchers.
|
|
type AndSelector struct {
|
|
// Matchers is the list of ResourceMatchers to try on the input resources.
|
|
Matchers []ResourceMatcher
|
|
// TemplateData, if present, is used to initialize any matchers that implement
|
|
// ResourceTemplateMatcher.
|
|
TemplateData interface{}
|
|
// FailOnEmptyMatch makes the selector return an error when no items are selected.
|
|
FailOnEmptyMatch bool
|
|
}
|
|
|
|
// Match implements ResourceMatcher so that AndSelectors can be composed
|
|
func (s *AndSelector) Match(item *yaml.RNode) bool {
|
|
for _, matcher := range s.Matchers {
|
|
if !matcher.Match(item) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Filter implements kio.Filter, returning only those items from the list that the selector
|
|
// matches.
|
|
func (s *AndSelector) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
if err := initMatcherTemplates(s.Matchers, s.TemplateData); err != nil {
|
|
return nil, err
|
|
}
|
|
var selectedItems []*yaml.RNode
|
|
for i := range items {
|
|
isSelected := true
|
|
for _, matcher := range s.Matchers {
|
|
if !matcher.Match(items[i]) {
|
|
isSelected = false
|
|
break
|
|
}
|
|
}
|
|
if isSelected {
|
|
selectedItems = append(selectedItems, items[i])
|
|
}
|
|
}
|
|
if s.FailOnEmptyMatch && len(selectedItems) == 0 {
|
|
return nil, errors.Errorf("selector did not select any items")
|
|
}
|
|
return selectedItems, nil
|
|
}
|
|
|
|
// DefaultTemplateData makes AndSelector a ResourceTemplateMatcher.
|
|
// Although it does not contain templates itself, this allows it to support ResourceTemplateMatchers
|
|
// when being used as a matcher itself.
|
|
func (s *AndSelector) DefaultTemplateData(data interface{}) {
|
|
if s.TemplateData == nil {
|
|
s.TemplateData = data
|
|
}
|
|
}
|
|
|
|
func (s *AndSelector) InitTemplates() error {
|
|
return initMatcherTemplates(s.Matchers, s.TemplateData)
|
|
}
|
|
|
|
var _ ResourceTemplateMatcher = &AndSelector{}
|