mirror of
https://github.com/rancher/norman.git
synced 2025-07-07 20:39:52 +00:00
Fix deleting keys in maps
This commit is contained in:
parent
0611d5f7a1
commit
db37c4223d
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/rancher/norman/restwatch"
|
"github.com/rancher/norman/restwatch"
|
||||||
"github.com/rancher/norman/types"
|
"github.com/rancher/norman/types"
|
||||||
"github.com/rancher/norman/types/convert"
|
"github.com/rancher/norman/types/convert"
|
||||||
|
"github.com/rancher/norman/types/convert/merge"
|
||||||
"github.com/rancher/norman/types/values"
|
"github.com/rancher/norman/types/values"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
@ -326,7 +327,7 @@ func (p *Store) Update(apiContext *types.APIContext, schema *types.Schema, data
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.toInternal(schema.Mapper, data)
|
p.toInternal(schema.Mapper, data)
|
||||||
existing = convert.APIUpdateMerge(existing, data, apiContext.Query.Get("_replace") == "true")
|
existing = merge.APIUpdateMerge(schema.InternalSchema, apiContext.Schemas, existing, data, apiContext.Query.Get("_replace") == "true")
|
||||||
|
|
||||||
values.PutValue(existing, resourceVersion, "metadata", "resourceVersion")
|
values.PutValue(existing, resourceVersion, "metadata", "resourceVersion")
|
||||||
values.PutValue(existing, namespace, "metadata", "namespace")
|
values.PutValue(existing, namespace, "metadata", "namespace")
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
package convert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func APIUpdateMerge(dest, src map[string]interface{}, replace bool) map[string]interface{} {
|
|
||||||
result := map[string]interface{}{}
|
|
||||||
if replace {
|
|
||||||
if status, ok := dest["status"]; ok {
|
|
||||||
result["status"] = status
|
|
||||||
}
|
|
||||||
if metadata, ok := dest["metadata"]; ok {
|
|
||||||
result["metadata"] = metadata
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = copyMap(dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range src {
|
|
||||||
if k == "metadata" {
|
|
||||||
result["metadata"] = mergeMetadata(ToMapInterface(dest["metadata"]), ToMapInterface(v))
|
|
||||||
} else if k == "status" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
existing, ok := dest[k]
|
|
||||||
if ok && !replace {
|
|
||||||
result[k] = merge(existing, v)
|
|
||||||
} else {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeMetadata(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
|
|
||||||
result := copyMap(dest)
|
|
||||||
|
|
||||||
labels := mergeMaps(ToMapInterface(dest["labels"]), ToMapInterface(src["labels"]))
|
|
||||||
|
|
||||||
existingAnnotation := ToMapInterface(dest["annotations"])
|
|
||||||
newAnnotation := ToMapInterface(src["annotations"])
|
|
||||||
annotations := copyMap(existingAnnotation)
|
|
||||||
|
|
||||||
for k, v := range newAnnotation {
|
|
||||||
if strings.Contains(k, "cattle.io/") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
annotations[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range existingAnnotation {
|
|
||||||
if strings.Contains(k, "cattle.io/") {
|
|
||||||
annotations[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result["labels"] = labels
|
|
||||||
result["annotations"] = annotations
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func merge(dest, src interface{}) interface{} {
|
|
||||||
sm, smOk := src.(map[string]interface{})
|
|
||||||
dm, dmOk := dest.(map[string]interface{})
|
|
||||||
if smOk && dmOk {
|
|
||||||
return mergeMaps(dm, sm)
|
|
||||||
}
|
|
||||||
return src
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeMaps(dest map[string]interface{}, src map[string]interface{}) interface{} {
|
|
||||||
result := copyMap(dest)
|
|
||||||
for k, v := range src {
|
|
||||||
result[k] = merge(dest[k], v)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyMap(src map[string]interface{}) map[string]interface{} {
|
|
||||||
result := map[string]interface{}{}
|
|
||||||
for k, v := range src {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
138
types/convert/merge/merge.go
Normal file
138
types/convert/merge/merge.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package merge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rancher/norman/types"
|
||||||
|
"github.com/rancher/norman/types/convert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func APIUpdateMerge(schema *types.Schema, schemas *types.Schemas, dest, src map[string]interface{}, replace bool) map[string]interface{} {
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
if replace {
|
||||||
|
if status, ok := dest["status"]; ok {
|
||||||
|
result["status"] = status
|
||||||
|
}
|
||||||
|
if metadata, ok := dest["metadata"]; ok {
|
||||||
|
result["metadata"] = metadata
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = copyMap(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range src {
|
||||||
|
if k == "metadata" {
|
||||||
|
result["metadata"] = mergeMetadata(convert.ToMapInterface(dest["metadata"]), convert.ToMapInterface(v))
|
||||||
|
continue
|
||||||
|
} else if k == "status" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
existing, ok := dest[k]
|
||||||
|
if ok && !replace {
|
||||||
|
result[k] = merge(k, schema, schemas, existing, v)
|
||||||
|
} else {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeProtected(dest, src map[string]interface{}) map[string]interface{} {
|
||||||
|
if src == nil {
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
result := copyMap(dest)
|
||||||
|
|
||||||
|
for k, v := range src {
|
||||||
|
if strings.Contains(k, "cattle.io/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range dest {
|
||||||
|
if strings.Contains(k, "cattle.io/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := src[k]; !ok {
|
||||||
|
delete(dest, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeMetadata(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
|
||||||
|
result := copyMap(dest)
|
||||||
|
|
||||||
|
labels := convert.ToMapInterface(dest["labels"])
|
||||||
|
srcLabels := convert.ToMapInterface(src["labels"])
|
||||||
|
labels = mergeProtected(labels, srcLabels)
|
||||||
|
|
||||||
|
annotations := convert.ToMapInterface(dest["annotations"])
|
||||||
|
srcAnnotation := convert.ToMapInterface(src["annotations"])
|
||||||
|
annotations = mergeProtected(annotations, srcAnnotation)
|
||||||
|
|
||||||
|
result["labels"] = labels
|
||||||
|
result["annotations"] = annotations
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func merge(field string, schema *types.Schema, schemas *types.Schemas, dest, src interface{}) interface{} {
|
||||||
|
if dest == nil {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
if src == nil {
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
if isMap(field, schema) {
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
|
sm, smOk := src.(map[string]interface{})
|
||||||
|
dm, dmOk := dest.(map[string]interface{})
|
||||||
|
if smOk && dmOk {
|
||||||
|
return mergeMaps(getSchema(field, schema, schemas), schemas, dm, sm)
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSchema(field string, schema *types.Schema, schemas *types.Schemas) *types.Schema {
|
||||||
|
if schema == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s := schemas.Schema(&schema.Version, schema.ResourceFields[field].Type)
|
||||||
|
if s != nil && s.InternalSchema != nil {
|
||||||
|
return s.InternalSchema
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMap(field string, schema *types.Schema) bool {
|
||||||
|
if schema == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
f := schema.ResourceFields[field]
|
||||||
|
return strings.HasPrefix(f.Type, "map[")
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeMaps(schema *types.Schema, schemas *types.Schemas, dest map[string]interface{}, src map[string]interface{}) interface{} {
|
||||||
|
result := copyMap(dest)
|
||||||
|
for k, v := range src {
|
||||||
|
result[k] = merge(k, schema, schemas, dest[k], v)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyMap(src map[string]interface{}) map[string]interface{} {
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
for k, v := range src {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
@ -95,11 +95,9 @@ type APIContext struct {
|
|||||||
ResponseWriter ResponseWriter
|
ResponseWriter ResponseWriter
|
||||||
QueryFilter QueryFilter
|
QueryFilter QueryFilter
|
||||||
SubContextAttributeProvider SubContextAttributeProvider
|
SubContextAttributeProvider SubContextAttributeProvider
|
||||||
//QueryOptions *QueryOptions
|
URLBuilder URLBuilder
|
||||||
URLBuilder URLBuilder
|
AccessControl AccessControl
|
||||||
AccessControl AccessControl
|
SubContext map[string]string
|
||||||
SubContext map[string]string
|
|
||||||
//Attributes map[string]interface{}
|
|
||||||
|
|
||||||
Request *http.Request
|
Request *http.Request
|
||||||
Response http.ResponseWriter
|
Response http.ResponseWriter
|
||||||
|
Loading…
Reference in New Issue
Block a user