Skip to content

Commit

Permalink
refactored logic to support regex with indexes
Browse files Browse the repository at this point in the history
Signed-off-by: sethiyash <[email protected]>
  • Loading branch information
sethiyash committed Mar 3, 2023
1 parent e4b8b9a commit 7edb5b2
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 74 deletions.
121 changes: 58 additions & 63 deletions pkg/kapp/resources/mod_field_copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,37 @@ func (t FieldCopyMod) ApplyFromMultiple(res Resource, srcs map[FieldCopyModSourc
return nil
}

// Make a copy of resource, to avoid modifications
// that may be done even in case when there is nothing to copy
updatedRes := res.DeepCopy()

updated, err := t.apply(updatedRes.unstructured().Object, t.Path, Path{}, srcs)
if err != nil {
return fmt.Errorf("FieldCopyMod for path '%s' on resource '%s': %s", t.Path.AsString(), res.Description(), err)
}

if updated {
res.setUnstructured(updatedRes.unstructured())
for _, src := range t.Sources {
// Make a copy of resource, to avoid modifications
// that may be done even in case when there is nothing to copy
updatedRes := res.DeepCopy()
source, found := srcs[src]
if !found {
continue
}
//updatedSrcRes := source.DeepCopy()
updated, err := t.apply(updatedRes.unstructured().Object, source.unstructured().Object, t.Path, Path{}, srcs)
if err != nil {
return fmt.Errorf("FieldCopyMod for path '%s' on resource '%s': %s", t.Path.AsString(), res.Description(), err)
}
if updated {
res.setUnstructured(updatedRes.unstructured())
}
}

return nil
}

func (t FieldCopyMod) apply(obj interface{}, path Path, fullPath Path, srcs map[FieldCopyModSource]Resource) (bool, error) {
func (t FieldCopyMod) apply(obj interface{}, srcObj interface{}, path Path, fullPath Path, srcs map[FieldCopyModSource]Resource) (bool, error) {
for i, part := range path {
isLast := len(path) == i+1
fullPath = append(fullPath, part)

switch {
case part.MapKey != nil:
srcTypedObj, ok := srcObj.(map[string]interface{})
if !ok {
return false, fmt.Errorf("Unexpected non-map found: %T", srcObj)
}
typedObj, ok := obj.(map[string]interface{})
if !ok {
return false, fmt.Errorf("Unexpected non-map found: %T", obj)
Expand All @@ -60,13 +68,21 @@ func (t FieldCopyMod) apply(obj interface{}, path Path, fullPath Path, srcs map[
return t.copyIntoMap(typedObj, fullPath, srcs)
}

var found bool
var (
found bool
srcObjFound bool
)
srcObj, srcObjFound = srcTypedObj[*part.MapKey]
if !srcObjFound || srcObj == nil {
return false, nil
}

obj, found = typedObj[*part.MapKey]
// TODO check strictness?
if !found || obj == nil {
// create empty maps if there are no downstream array indexes;
// if there are, we cannot make them anyway, so just exit
if path.ContainsNonMapKeysAndRegex() {
if path.ContainsArrayIndex() {
return false, nil
}
obj = map[string]interface{}{}
Expand All @@ -85,6 +101,11 @@ func (t FieldCopyMod) apply(obj interface{}, path Path, fullPath Path, srcs map[
return false, fmt.Errorf("Unexpected non-array found: %T", obj)
}

srcTypedObj, ok := srcObj.([]interface{})
if !ok {
return false, fmt.Errorf("Unexpected non-array found: %T", srcObj)
}

var anyUpdated bool

for objI, obj := range typedObj {
Expand All @@ -93,7 +114,7 @@ func (t FieldCopyMod) apply(obj interface{}, path Path, fullPath Path, srcs map[
newFullPath := append([]*PathPart{}, fullPath...)
newFullPath[len(newFullPath)-1] = &PathPart{ArrayIndex: &PathPartArrayIndex{Index: &objI}}

updated, err := t.apply(obj, path[i+1:], newFullPath, srcs)
updated, err := t.apply(obj, srcTypedObj[objI], path[i+1:], newFullPath, srcs)
if err != nil {
return false, err
}
Expand All @@ -110,9 +131,15 @@ func (t FieldCopyMod) apply(obj interface{}, path Path, fullPath Path, srcs map[
return false, fmt.Errorf("Unexpected non-array found: %T", obj)
}

srcTypedObj, ok := srcObj.([]interface{})
if !ok {
return false, fmt.Errorf("Unexpected non-array found: %T", srcObj)
}

if *part.ArrayIndex.Index < len(typedObj) {
obj = typedObj[*part.ArrayIndex.Index]
return t.apply(obj, path[i+1:], fullPath, srcs)
srcObj = srcTypedObj[*part.ArrayIndex.Index]
return t.apply(obj, srcObj, path[i+1:], fullPath, srcs)
}

return false, nil // index not found, nothing to append to
Expand All @@ -122,15 +149,15 @@ func (t FieldCopyMod) apply(obj interface{}, path Path, fullPath Path, srcs map[
}

case part.Regex != nil && part.Regex.Regex != nil:
matchedKeys, err := t.matchRegexWithSources(path, srcs)
matchedKeys, err := t.matchRegexWithSrcObj(*part.Regex.Regex, srcObj)
if err != nil {
return false, err
}
allUpdated := true
for _, key := range matchedKeys {
newPath := append(Path{&PathPart{MapKey: &key}}, path[i+1:]...)
newFullPath := fullPath[:len(fullPath)-1]
updated, err := t.apply(obj, newPath, newFullPath, srcs)
updated, err := t.apply(obj, srcObj, newPath, newFullPath, srcs)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -220,51 +247,19 @@ func (t FieldCopyMod) obtainValue(obj interface{}, path Path) (interface{}, bool
return obj, true, nil
}

func (t FieldCopyMod) matchRegexWithSources(path Path, srcs map[FieldCopyModSource]Resource) ([]string, error) {
for _, src := range t.Sources {
if srcRes, found := srcs[src]; found && srcRes != nil {
matchedKeys, err := t.obtainMatchingRegexKeys(srcRes.unstructured().Object, path)
if err == nil && len(matchedKeys) > 0 {
return matchedKeys, err
}
}
}
return []string{}, fmt.Errorf("Field value not present in mentioned sources %s", t.Sources)
}

func (t FieldCopyMod) obtainMatchingRegexKeys(obj interface{}, path Path) ([]string, error) {
func (t FieldCopyMod) matchRegexWithSrcObj(regexString string, srcObj interface{}) ([]string, error) {
var matchedKeys []string
for _, part := range path {
switch {
case part.MapKey != nil:
typedObj, ok := obj.(map[string]interface{})
if !ok && typedObj != nil {
return matchedKeys, fmt.Errorf("Unexpected non-map found: %T", obj)
}

var found bool
obj, found = typedObj[*part.MapKey]
if !found {
return matchedKeys, nil
}

case part.Regex != nil && part.Regex.Regex != nil:
regex, err := regexp.Compile(*part.Regex.Regex)
if err != nil {
return matchedKeys, err
}
typedObj, ok := obj.(map[string]interface{})
if !ok && typedObj != nil {
return matchedKeys, fmt.Errorf("Unexpected non-map found: %T", obj)
}
for key := range typedObj {
if regex.MatchString(key) {
matchedKeys = append(matchedKeys, key)
}
}

default:
panic(fmt.Sprintf("Unexpected path part: %#v", part))
regex, err := regexp.Compile(regexString)
if err != nil {
return matchedKeys, err
}
srcTypedObj, ok := srcObj.(map[string]interface{})
if !ok && srcTypedObj != nil {
return matchedKeys, fmt.Errorf("Unexpected non-map found: %T", srcObj)
}
for key := range srcTypedObj {
if regex.MatchString(key) {
matchedKeys = append(matchedKeys, key)
}
}
return matchedKeys, nil
Expand Down
6 changes: 3 additions & 3 deletions pkg/kapp/resources/mod_field_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (t FieldRemoveMod) apply(obj interface{}, path Path) error {
panic(fmt.Sprintf("Unknown array index: %#v", part.ArrayIndex))
}
case part.Regex != nil && part.Regex.Regex != nil:
matchedKeys, err := t.obtainMatchingRegexKeys(obj, part)
matchedKeys, err := t.obtainMatchingRegexKeys(obj, *part.Regex.Regex)
if err != nil {
return err
}
Expand All @@ -114,9 +114,9 @@ func (t FieldRemoveMod) apply(obj interface{}, path Path) error {
panic("unreachable")
}

func (t FieldRemoveMod) obtainMatchingRegexKeys(obj interface{}, part *PathPart) ([]string, error) {
func (t FieldRemoveMod) obtainMatchingRegexKeys(obj interface{}, regexString string) ([]string, error) {
var matchedKeys []string
regex, err := regexp.Compile(*part.Regex.Regex)
regex, err := regexp.Compile(regexString)
if err != nil {
return matchedKeys, err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/kapp/resources/mod_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ func (p Path) ContainsNonMapKeys() bool {
return false
}

func (p Path) ContainsNonMapKeysAndRegex() bool {
func (p Path) ContainsArrayIndex() bool {
for _, part := range p {
if part.MapKey == nil && (part.Regex == nil || part.Regex.Regex == nil) {
if part.ArrayIndex != nil {
return true
}
}
Expand Down
Loading

0 comments on commit 7edb5b2

Please sign in to comment.