mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
K8s/Unstructured: Avoid panic in DeepCopy (#99840)
This commit is contained in:
@@ -2,6 +2,7 @@ package v0alpha1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -63,7 +64,7 @@ func (u *Unstructured) DeepCopy() *Unstructured {
|
||||
}
|
||||
out := new(Unstructured)
|
||||
*out = *u
|
||||
out.Object = runtime.DeepCopyJSON(u.Object)
|
||||
out.Object = deepCopyJSONValue(u.Object).(map[string]interface{})
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -72,6 +73,57 @@ func (u *Unstructured) DeepCopyInto(out *Unstructured) {
|
||||
*out = *clone
|
||||
}
|
||||
|
||||
// Copied from:
|
||||
//
|
||||
// runtime.DeepCopyJSON(u.Object)
|
||||
//
|
||||
// BUT this avoids panic on int
|
||||
func deepCopyJSONValue(x interface{}) interface{} {
|
||||
switch x := x.(type) {
|
||||
case map[string]interface{}:
|
||||
if x == nil {
|
||||
// Typed nil - an interface{} that contains a type map[string]interface{} with a value of nil
|
||||
return x
|
||||
}
|
||||
clone := make(map[string]interface{}, len(x))
|
||||
for k, v := range x {
|
||||
clone[k] = deepCopyJSONValue(v)
|
||||
}
|
||||
return clone
|
||||
case []interface{}:
|
||||
if x == nil {
|
||||
// Typed nil - an interface{} that contains a type []interface{} with a value of nil
|
||||
return x
|
||||
}
|
||||
clone := make([]interface{}, len(x))
|
||||
for i, v := range x {
|
||||
clone[i] = deepCopyJSONValue(v)
|
||||
}
|
||||
return clone
|
||||
case string, int64, bool, float64, nil, json.Number:
|
||||
return x
|
||||
|
||||
// Keep more numbers
|
||||
case int, int8, int16, int32, float32, uint, uint16, uint32, uint64, uint8:
|
||||
return x
|
||||
|
||||
case runtime.Object:
|
||||
return x.DeepCopyObject()
|
||||
|
||||
default:
|
||||
// fallback to reflection
|
||||
val := reflect.ValueOf(x).Elem()
|
||||
cpy := reflect.New(val.Type())
|
||||
cpy.Elem().Set(val)
|
||||
|
||||
// Using the <obj>, <ok> for the type conversion ensures that it doesn't panic if it can't be converted
|
||||
if obj, ok := cpy.Interface().(runtime.Object); ok {
|
||||
return obj
|
||||
}
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Unstructured) Set(field string, value interface{}) {
|
||||
if u.Object == nil {
|
||||
u.Object = make(map[string]interface{})
|
||||
|
||||
44
pkg/apimachinery/apis/common/v0alpha1/unstructured_test.go
Normal file
44
pkg/apimachinery/apis/common/v0alpha1/unstructured_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestDeepCopyJSON(t *testing.T) {
|
||||
obj := &Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"int": int(2),
|
||||
"int16": int16(2),
|
||||
"int32": int32(2),
|
||||
"uint64": uint64(2),
|
||||
"ref": &ObjectReference{
|
||||
Resource: "x",
|
||||
},
|
||||
"array": []any{
|
||||
int(1), int64(2), "hello",
|
||||
},
|
||||
"string": "hello",
|
||||
"bool": true,
|
||||
"map": map[string]any{
|
||||
"x": &ObjectReference{
|
||||
Resource: "x",
|
||||
},
|
||||
},
|
||||
"object": &v1.APIGroup{
|
||||
Name: "HELLO",
|
||||
},
|
||||
},
|
||||
}
|
||||
before, err := json.Marshal(obj)
|
||||
require.NoError(t, err)
|
||||
|
||||
clone := obj.DeepCopy()
|
||||
after, err := json.Marshal(clone)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.JSONEq(t, string(before), string(after))
|
||||
}
|
||||
Reference in New Issue
Block a user