-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: added first draft of replacement between delimiter #5617
base: master
Are you sure you want to change the base?
Changes from 2 commits
45f492a
4210131
f4441a8
255e8d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ package replacement | |
import ( | ||
"fmt" | ||
"strings" | ||
"regexp" | ||
|
||
"sigs.k8s.io/kustomize/api/internal/utils" | ||
"sigs.k8s.io/kustomize/api/resource" | ||
|
@@ -39,6 +40,10 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { | |
} | ||
|
||
func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, error) { | ||
if r.Source.FullText != ""{ | ||
rn := yaml.NewScalarRNode(r.Source.FullText) | ||
return rn, nil | ||
} | ||
source, err := selectSourceNode(nodes, r.Source) | ||
if err != nil { | ||
return nil, err | ||
|
@@ -93,7 +98,24 @@ func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode, | |
if rn.YNode().Kind != yaml.ScalarNode { | ||
return nil, fmt.Errorf("delimiter option can only be used with scalar nodes") | ||
} | ||
value := strings.Split(yaml.GetValue(rn), options.Delimiter) | ||
value := []string{} | ||
if options.EndDelimiter == "" { | ||
value = strings.Split(yaml.GetValue(rn), options.Delimiter) | ||
} else { | ||
mapper := func(s string) string { | ||
s = strings.ReplaceAll(s, options.Delimiter, "") | ||
s = strings.ReplaceAll(s, options.EndDelimiter, "") | ||
return s | ||
} | ||
if options.Delimiter == "" { | ||
return nil, fmt.Errorf("delimiter needs to be set if enddelimiter is set") | ||
} | ||
re := regexp.MustCompile(regexp.QuoteMeta(options.Delimiter) + `(.*?)` + regexp.QuoteMeta(options.EndDelimiter)) | ||
dv := re.FindAllString(yaml.GetValue(rn), -1) | ||
for _, s := range dv { | ||
value = append(value, mapper(s)) | ||
} | ||
} | ||
if options.Index >= len(value) || options.Index < 0 { | ||
return nil, fmt.Errorf("options.index %d is out of bounds for value %s", options.Index, yaml.GetValue(rn)) | ||
} | ||
|
@@ -220,22 +242,20 @@ func fieldRetrievalError(fieldPath string, isCreate bool) string { | |
|
||
func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *yaml.RNode) error { | ||
value = value.Copy() | ||
if options != nil && options.Delimiter != "" { | ||
if options != nil && (options.Delimiter != "" || options.FullText != "") { | ||
if targetField.YNode().Kind != yaml.ScalarNode { | ||
return fmt.Errorf("delimiter option can only be used with scalar nodes") | ||
} | ||
tv := strings.Split(targetField.YNode().Value, options.Delimiter) | ||
v := yaml.GetValue(value) | ||
// TODO: Add a way to remove an element | ||
switch { | ||
case options.Index < 0: // prefix | ||
tv = append([]string{v}, tv...) | ||
case options.Index >= len(tv): // suffix | ||
tv = append(tv, v) | ||
default: // replace an element | ||
tv[options.Index] = v | ||
if options.FullText != "" { | ||
value.YNode().Value = getByRegex(options.FullText, targetField.YNode().Value, v, options.Index) | ||
} else if options.Delimiter != "" && options.EndDelimiter != "" { | ||
regex := regexp.QuoteMeta(options.Delimiter) + `(.*?)` + regexp.QuoteMeta(options.EndDelimiter) | ||
source := options.Delimiter + v + options.EndDelimiter | ||
value.YNode().Value = getByRegex(regex, targetField.YNode().Value, source, options.Index) | ||
} else { | ||
value.YNode().Value = getByDelimiter(options.Delimiter, targetField.YNode().Value, v, options.Index) | ||
} | ||
value.YNode().Value = strings.Join(tv, options.Delimiter) | ||
} | ||
|
||
if targetField.YNode().Kind == yaml.ScalarNode { | ||
|
@@ -247,3 +267,32 @@ func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value * | |
|
||
return nil | ||
} | ||
|
||
func getByDelimiter(delimiter string, target string, source string, index int) string { | ||
tv := strings.Split(target, delimiter) | ||
// TODO: Add a way to remove an element | ||
switch { | ||
case index < 0: // prefix | ||
tv = append([]string{source}, tv...) | ||
case index >= len(tv): // suffix | ||
tv = append(tv, source) | ||
default: // replace an element | ||
tv[index] = source | ||
} | ||
return strings.Join(tv, delimiter) | ||
} | ||
|
||
func getByRegex(regex string, target string, source string, index int) string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since both these are fragile string manipulation methods, can we have separate tests to verify these and ensure we are capturing the edge cases? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @varshaprasad96 I've written some edge case tests but if you want I can add some tests that handle these functions in specific EDIT: I've added some tests for the regex function. I hope this is what you imagined. Let me know if you want/need more tests |
||
re := regexp.MustCompile(regex) | ||
counter := 0 | ||
res := re.ReplaceAllStringFunc(target, func(str string) string { | ||
if counter != index && index >= 0 { | ||
return str | ||
} | ||
|
||
counter++ | ||
return re.ReplaceAllString(str, source) | ||
}) | ||
return res | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be a bit fragile:
If not for nested delimiters, for the other cases, can we do any kind of pre-processing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@varshaprasad96 thank you for the feedback.
Delimiter
comes alone, in this case it stays like it is today, theEndDelimiter
should not be used without aDelimiter
if so we return an error and if they are used in combination, the value in between them is loaded. If I'm not mistaken all cases should be handled.EndDelimiter
is set and theDelimiter
is empty they swap values. Do you have another suggestion for preprocessing?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we do have a check for that. But what happens when there are unequal number of delimiters and end-delimiters? The regex would still compile, but I'm wondering that there would be some unexpected behaviours? Which is why would it be better if we count for each of those occurrences to ensure they work appear as pairs?
I think this looks good the way it is for now, where we error out if delimiter is not set when end delimiter is.
Can we have checks to ensure that they are in the order as expected, especially when there is a nested scenario - where we have multiple of delimiters and enddelimiters?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@varshaprasad96 I will look into this this week and give you a feedback. If I see a possibility to ensure the order and the amount of delimiters I would implement it so you can review it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @varshaprasad96 I've looked into it and wanted to ask if you could provide a nested example for me. As I have seen the delimiters are used in
FieldOptions
https://github.com/kubernetes-sigs/kustomize/blob/master/api/types/replacement.go#L67 which consists of primitive datatypes variables, therefore I don't see how multiple delimiters or nesting could accure. The replacement consists of oneSourceSelector
and multipleTargetSelector
everyone of these types contain exactly oneFieldOptions
type.If I am missing something just tell me and I will look into it.