Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 90 additions & 22 deletions controller/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ func normalizeTargetResources(cr *comparisonResult) ([]*unstructured.Unstructure
}
// generate a minimal patch that uses the fields from targetPatch (template)
// with livePatch values
patch, err := compilePatch(targetPatch, livePatch)
patch, err := compilePatch(targetPatch, livePatch, live.Object)
if err != nil {
return nil, err
}
Expand All @@ -426,7 +426,7 @@ func normalizeTargetResources(cr *comparisonResult) ([]*unstructured.Unstructure

// compilePatch will generate a patch using the fields from templatePatch with
// the values from valuePatch.
func compilePatch(templatePatch, valuePatch []byte) ([]byte, error) {
func compilePatch(templatePatch, valuePatch []byte, liveObjectMap map[string]interface{}) ([]byte, error) {
templateMap := make(map[string]interface{})
err := json.Unmarshal(templatePatch, &templateMap)
if err != nil {
Expand All @@ -437,49 +437,117 @@ func compilePatch(templatePatch, valuePatch []byte) ([]byte, error) {
if err != nil {
return nil, err
}
resultMap := intersectMap(templateMap, valueMap)
resultMap := intersectMap(templateMap, valueMap, liveObjectMap)
return json.Marshal(resultMap)
}

// intersectMap will return map with the fields intersection from the 2 provided
// maps populated with the valueMap values.
func intersectMap(templateMap, valueMap map[string]interface{}) map[string]interface{} {
func intersectMap(templateMap, valueMap, liveMap map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{})
for k, v := range templateMap {
if innerTMap, ok := v.(map[string]interface{}); ok {
if innerVMap, ok := valueMap[k].(map[string]interface{}); ok {
result[k] = intersectMap(innerTMap, innerVMap)
if innerLMap, ok := liveMap[k].(map[string]interface{}); ok {
result[k] = intersectMap(innerTMap, innerVMap, innerLMap)
}
} else if _, ok := liveMap[k].(map[string]interface{}); !ok {
result[k] = innerTMap
}
} else if innerTSlice, ok := v.([]interface{}); ok {
if innerVSlice, ok := valueMap[k].([]interface{}); ok {
items := []interface{}{}
for idx, innerTSliceValue := range innerTSlice {
if idx < len(innerVSlice) {
if innerLSlice, ok := liveMap[k].([]interface{}); ok {
items := []interface{}{}
mergeKeyFieldName := "name"

for idx, innerTSliceValue := range innerTSlice {
if tSliceValueMap, ok := innerTSliceValue.(map[string]interface{}); ok {
if vSliceValueMap, ok := innerVSlice[idx].(map[string]interface{}); ok {
item := intersectMap(tSliceValueMap, vSliceValueMap)
items = append(items, item)
if idx < len(innerVSlice) {
if vSliceValueMap, ok := innerVSlice[idx].(map[string]interface{}); ok {
if lSliceValueMap, ok := innerLSlice[idx].(map[string]interface{}); ok {
mergedMap := map[string]interface{}{}
if _, ok := tSliceValueMap[mergeKeyFieldName].(string); ok {
if _, ok := vSliceValueMap[mergeKeyFieldName].(string); ok {
for tKey, tItem := range tSliceValueMap {
if vItem, ok := vSliceValueMap[tKey].(map[string]interface{}); ok {
if lItem, ok := lSliceValueMap[tKey].(map[string]interface{}); ok {
item := intersectMap(tItem.(map[string]interface{}), vItem, lItem)
mergedMap[tKey] = item
}
} else if vItem, ok := vSliceValueMap[tKey].([]interface{}); ok {
mergeKeys := []string{}
innerItems := []interface{}{}
mergeKeyItems := map[string]interface{}{}

for _, tItemEl := range tItem.([]interface{}) {
if tItemElMap, ok := tItemEl.(map[string]interface{}); ok {
if tItemElName, ok := tItemElMap[mergeKeyFieldName].(string); ok {
if _, ok := mergeKeyItems[tItemElName]; !ok {
mergeKeys = append(mergeKeys, tItemElName)
}
mergeKeyItems[tItemElName] = tItemElMap
}
}
}

for _, vItemEl := range vItem {
if vItemElMap, ok := vItemEl.(map[string]interface{}); ok {
if vItemElName, ok := vItemElMap[mergeKeyFieldName].(string); ok {
if _, ok := mergeKeyItems[vItemElName]; ok {
mergeKeyItems[vItemElName] = vItemElMap
}
}
}
}

if len(mergeKeys) > 0 {
for _, key := range mergeKeys {
innerItems = append(innerItems, mergeKeyItems[key])
}
mergedMap[tKey] = innerItems
} else {
// TODO: What should happen if there are no merge keys
// and there are array entries within both tItem and vItem?
mergedMap[tKey] = tItem
}
} else if vItem, ok := vSliceValueMap[tKey]; ok {
mergedMap[tKey] = vItem
} else {
mergedMap[tKey] = tItem
}
}
}

items = append(items, mergedMap)
}
} else {
item := intersectMap(tSliceValueMap, vSliceValueMap, lSliceValueMap)
items = append(items, item)
}
}
} else {
items = append(items, tSliceValueMap)
}
} else {
items = append(items, innerVSlice[idx])
items = append(items, tSliceValueMap)
}
}
if len(items) > 0 {
result[k] = items
}
}
if len(items) > 0 {
result[k] = items
}
}
} else {
if _, ok := valueMap[k]; ok {
result[k] = valueMap[k]
} else {
result[k] = innerTSlice
}
} else if _, ok := valueMap[k]; ok {
result[k] = valueMap[k]
}
}
return result
}

// getMergePatch calculates and returns the patch between the original and the
// modified unstructures.
// modified unstructured.
func getMergePatch(original, modified *unstructured.Unstructured) ([]byte, error) {
originalJSON, err := original.MarshalJSON()
if err != nil {
Expand All @@ -493,7 +561,7 @@ func getMergePatch(original, modified *unstructured.Unstructured) ([]byte, error
}

// applyMergePatch will apply the given patch in the obj and return the patched
// unstructure.
// unstructured.
func applyMergePatch(obj *unstructured.Unstructured, patch []byte) (*unstructured.Unstructured, error) {
originalJSON, err := obj.MarshalJSON()
if err != nil {
Expand All @@ -513,7 +581,7 @@ func applyMergePatch(obj *unstructured.Unstructured, patch []byte) (*unstructure

// hasSharedResourceCondition will check if the Application has any resource that has already
// been synced by another Application. If the resource is found in another Application it returns
// true along with a human readable message of which specific resource has this condition.
// true along with a human-readable message of which specific resource has this condition.
func hasSharedResourceCondition(app *v1alpha1.Application) (bool, string) {
for _, condition := range app.Status.Conditions {
if condition.Type == v1alpha1.ApplicationConditionSharedResourceWarning {
Expand Down
Loading