// Copyright 2021 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 package types import ( "fmt" "strings" "sigs.k8s.io/kustomize/kyaml/resid" ) const DefaultReplacementFieldPath = "metadata.name" // Replacement defines how to perform a substitution // where it is from and where it is to. type Replacement struct { // The source of the value. Source *SourceSelector `json:"source,omitempty" yaml:"source,omitempty"` // The N fields to write the value to. Targets []*TargetSelector `json:"targets,omitempty" yaml:"targets,omitempty"` // Used to define an static value SourceValue *string `json:"sourceValue,omitempty" yaml:"sourceValue,omitempty"` } // SourceSelector is the source of the replacement transformer. type SourceSelector struct { // A specific object to read it from. resid.ResId `json:",inline,omitempty" yaml:",inline,omitempty"` // Structured field path expected in the allowed object. FieldPath string `json:"fieldPath,omitempty" yaml:"fieldPath,omitempty"` // Used to refine the interpretation of the field. Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"` } func (s *SourceSelector) String() string { if s == nil { return "" } result := []string{s.ResId.String()} if s.FieldPath != "" { result = append(result, s.FieldPath) } if opts := s.Options.String(); opts != "" { result = append(result, opts) } return strings.Join(result, ":") } // TargetSelector specifies fields in one or more objects. type TargetSelector struct { // Include objects that match this. Select *Selector `json:"select" yaml:"select"` // From the allowed set, remove objects that match this. Reject []*Selector `json:"reject,omitempty" yaml:"reject,omitempty"` // Structured field paths expected in each allowed object. FieldPaths []string `json:"fieldPaths,omitempty" yaml:"fieldPaths,omitempty"` // Used to refine the interpretation of the field. Options *FieldOptions `json:"options,omitempty" yaml:"options,omitempty"` } type TargetSelectorRegex struct { targetSelector *TargetSelector selectRegex *SelectorRegex rejectRegex []*SelectorRegex } func NewTargetSelectorRegex(ts *TargetSelector) (*TargetSelectorRegex, error) { tsr := new(TargetSelectorRegex) tsr.targetSelector = ts var err error tsr.selectRegex, err = NewSelectorRegex(ts.Select) if err != nil { return nil, err } rej := []*SelectorRegex{} for _, r := range ts.Reject { rr, err := NewSelectorRegex(r) if err != nil { return nil, err } rej = append(rej, rr) } tsr.rejectRegex = rej return tsr, nil } func (tsr *TargetSelectorRegex) Selects(id resid.ResId) bool { return tsr.selectRegex.MatchGvk(id.Gvk) && tsr.selectRegex.MatchName(id.Name) && tsr.selectRegex.MatchNamespace(id.Namespace) } func (tsr *TargetSelectorRegex) RejectsAny(ids []resid.ResId) bool { for _, r := range tsr.rejectRegex { if r.selector.ResId.IsEmpty() { continue } for _, id := range ids { if r.MatchGvk(id.Gvk) && r.MatchName(id.Name) && r.MatchNamespace(id.Namespace) { return true } } } return false } // FieldOptions refine the interpretation of FieldPaths. type FieldOptions struct { // Used to split/join the field. Delimiter string `json:"delimiter,omitempty" yaml:"delimiter,omitempty"` // Which position in the split to consider. Index int `json:"index,omitempty" yaml:"index,omitempty"` // TODO (#3492): Implement use of this option // None, Base64, URL, Hex, etc Encoding string `json:"encoding,omitempty" yaml:"encoding,omitempty"` // If field missing, add it. Create bool `json:"create,omitempty" yaml:"create,omitempty"` } func (fo *FieldOptions) String() string { if fo == nil || (fo.Delimiter == "" && !fo.Create) { return "" } return fmt.Sprintf("%s(%d), create=%t", fo.Delimiter, fo.Index, fo.Create) }