Merge pull request #4424 from koba1t/feature/allow_setting_every_array_element_in_replacements

Allow setting every array element in replacements
This commit is contained in:
Kubernetes Prow Robot
2022-02-09 17:03:46 -08:00
committed by GitHub
5 changed files with 342 additions and 32 deletions

View File

@@ -782,6 +782,19 @@ func IsListIndex(p string) bool {
return strings.HasPrefix(p, "[") && strings.HasSuffix(p, "]")
}
// IsIdxNumber returns true if p is an index number.
// e.g. 1
func IsIdxNumber(p string) bool {
idx, err := strconv.Atoi(p)
return err == nil && idx >= 0
}
// IsWildcard returns true if p is matching every elements.
// e.g. "*"
func IsWildcard(p string) bool {
return p == "*"
}
// SplitIndexNameValue splits a lookup part Val index into the field name
// and field value to match.
// e.g. splits [name=nginx] into (name, nginx)

View File

@@ -5,6 +5,7 @@ package yaml
import (
"regexp"
"strconv"
"strings"
)
@@ -42,9 +43,10 @@ type PathMatcher struct {
// This is useful for if the nodes are to be printed in FlowStyle.
StripComments bool
val *RNode
field string
matchRegex string
val *RNode
field string
matchRegex string
indexNumber int
}
func (p *PathMatcher) stripComments(n *Node) {
@@ -79,14 +81,49 @@ func (p *PathMatcher) filter(rn *RNode) (*RNode, error) {
return p.val, nil
}
if IsIdxNumber(p.Path[0]) {
return p.doIndexSeq(rn)
}
if IsListIndex(p.Path[0]) {
// match seq elements
return p.doSeq(rn)
}
if IsWildcard(p.Path[0]) {
// match every elements (*)
return p.doMatchEvery(rn)
}
// match a field
return p.doField(rn)
}
func (p *PathMatcher) doMatchEvery(rn *RNode) (*RNode, error) {
if err := rn.VisitElements(p.visitEveryElem); err != nil {
return nil, err
}
return p.val, nil
}
func (p *PathMatcher) visitEveryElem(elem *RNode) error {
fieldName := p.Path[0]
// recurse on the matching element
pm := &PathMatcher{Path: p.Path[1:]}
add, err := pm.filter(elem)
for k, v := range pm.Matches {
p.Matches[k] = v
}
if err != nil || add == nil {
return err
}
p.append(fieldName, add.Content()...)
return nil
}
func (p *PathMatcher) doField(rn *RNode) (*RNode, error) {
// lookup the field
field, err := rn.Pipe(Get(p.Path[0]))
@@ -102,6 +139,36 @@ func (p *PathMatcher) doField(rn *RNode) (*RNode, error) {
return p.val, err
}
// doIndexSeq iterates over a sequence and appends elements matching the index p.Val
func (p *PathMatcher) doIndexSeq(rn *RNode) (*RNode, error) {
// parse to index number
idx, err := strconv.Atoi(p.Path[0])
if err != nil {
return nil, err
}
p.indexNumber = idx
elements, err := rn.Elements()
if err != nil {
return nil, err
}
// get target element
element := elements[idx]
// recurse on the matching element
pm := &PathMatcher{Path: p.Path[1:]}
add, err := pm.filter(element)
for k, v := range pm.Matches {
p.Matches[k] = v
}
if err != nil || add == nil {
return nil, err
}
p.append("", add.Content()...)
return p.val, nil
}
// doSeq iterates over a sequence and appends elements matching the path regex to p.Val
func (p *PathMatcher) doSeq(rn *RNode) (*RNode, error) {
// parse the field + match pair

View File

@@ -77,6 +77,12 @@ spec:
{[]string{
"spec", "template", "spec", "containers", "[name=s.*]", "ports", "[containerPort=.*2]"},
""},
{[]string{
"spec", "template", "spec", "containers", "*", "image"},
"- nginx:1.7.9\n- sidecar:1.0.0\n"},
{[]string{
"spec", "template", "spec", "containers", "*", "ports", "*"},
"- containerPort: 80\n- containerPort: 8081\n- containerPort: 9090\n"},
}
for i, u := range updates {
result, err := node.Pipe(&PathMatcher{Path: u.path})