Browse code

Fix deep copy for api.ResourceQuotasStatusByNamespace

Jordan Liggitt authored on 2016/10/28 02:36:26
Showing 3 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,74 @@
0
+package api_test
1
+
2
+import (
3
+	"reflect"
4
+	"testing"
5
+
6
+	kapi "k8s.io/kubernetes/pkg/api"
7
+	"k8s.io/kubernetes/pkg/api/resource"
8
+
9
+	"github.com/openshift/origin/pkg/quota/api"
10
+	_ "github.com/openshift/origin/pkg/quota/api/install"
11
+)
12
+
13
+func TestDeepCopy(t *testing.T) {
14
+	make := func() *api.ClusterResourceQuota {
15
+		q := resource.Quantity{}
16
+		q.Set(100)
17
+		crq := &api.ClusterResourceQuota{}
18
+		crq.Status.Namespaces.Insert("ns1", kapi.ResourceQuotaStatus{Hard: kapi.ResourceList{"a": q.DeepCopy()}, Used: kapi.ResourceList{"a": q.DeepCopy()}})
19
+		crq.Status.Namespaces.Insert("ns2", kapi.ResourceQuotaStatus{Hard: kapi.ResourceList{"b": q.DeepCopy()}, Used: kapi.ResourceList{"b": q.DeepCopy()}})
20
+		return crq
21
+	}
22
+
23
+	check := make()
24
+
25
+	original := make()
26
+	if !reflect.DeepEqual(check, original) {
27
+		t.Error("before mutation of copy, check and original should be identical but are not, likely failure in deepequal")
28
+	}
29
+	if !kapi.Semantic.DeepEqual(check, original) {
30
+		t.Error("before mutation of copy, check and original should be identical but are not, likely failure in deepequal")
31
+	}
32
+
33
+	copiedObj, err := kapi.Scheme.Copy(original)
34
+	if err != nil {
35
+		t.Fatal(err)
36
+	}
37
+	copied := copiedObj.(*api.ClusterResourceQuota)
38
+	if !reflect.DeepEqual(copied, original) {
39
+		t.Error("before mutation of copy, copied and original should be identical but are not, likely failure in deepequal")
40
+	}
41
+	if !kapi.Semantic.DeepEqual(copied, original) {
42
+		t.Error("before mutation of copy, copied and original should be identical but are not, likely failure in deepequal")
43
+	}
44
+
45
+	// Mutate the copy
46
+	for e := copied.Status.Namespaces.OrderedKeys().Front(); e != nil; e = e.Next() {
47
+		k := e.Value.(string)
48
+		ns, _ := copied.Status.Namespaces.Get(k)
49
+		for k2, v2 := range ns.Hard {
50
+			v2.Set(v2.Value() + 2)
51
+			ns.Hard[k2] = v2
52
+		}
53
+		for k2, v2 := range ns.Used {
54
+			v2.Set(v2.Value() + 1)
55
+			ns.Used[k2] = v2
56
+		}
57
+		copied.Status.Namespaces.Insert(k, ns)
58
+	}
59
+
60
+	if !reflect.DeepEqual(check, original) {
61
+		t.Error("after mutation of copy, check and original should be identical but are not, likely failure in deep copy (ensure custom DeepCopy is being used)")
62
+	}
63
+	if !kapi.Semantic.DeepEqual(check, original) {
64
+		t.Error("after mutation of copy, check and original should be identical but are not, likely failure in deep copy (ensure custom DeepCopy is being used)")
65
+	}
66
+
67
+	if reflect.DeepEqual(original, copied) {
68
+		t.Error("after mutation of copy, original and copied should be different but are not, likely failure in deep copy (ensure custom DeepCopy is being used)")
69
+	}
70
+	if kapi.Semantic.DeepEqual(original, copied) {
71
+		t.Error("after mutation of copy, original and copied should be different but are not, likely failure in deep copy (ensure custom DeepCopy is being used)")
72
+	}
73
+}
... ...
@@ -2,6 +2,7 @@ package api
2 2
 
3 3
 import (
4 4
 	"container/list"
5
+	"reflect"
5 6
 
6 7
 	kapi "k8s.io/kubernetes/pkg/api"
7 8
 	"k8s.io/kubernetes/pkg/api/unversioned"
... ...
@@ -115,6 +116,42 @@ func (o *ResourceQuotasStatusByNamespace) OrderedKeys() *list.List {
115 115
 	return o.orderedMap.OrderedKeys()
116 116
 }
117 117
 
118
+// DeepCopy implements a custom copy to correctly handle unexported fields
119
+// Must match "func (t T) DeepCopy() T" for the deep copy generator to use it
120
+func (o ResourceQuotasStatusByNamespace) DeepCopy() ResourceQuotasStatusByNamespace {
121
+	out := ResourceQuotasStatusByNamespace{}
122
+	for e := o.OrderedKeys().Front(); e != nil; e = e.Next() {
123
+		namespace := e.Value.(string)
124
+		instatus, _ := o.Get(namespace)
125
+		if outstatus, err := kapi.Scheme.DeepCopy(instatus); err != nil {
126
+			panic(err) // should never happen
127
+		} else {
128
+			out.Insert(namespace, outstatus.(kapi.ResourceQuotaStatus))
129
+		}
130
+	}
131
+	return out
132
+}
133
+
134
+func init() {
135
+	// Tell the reflection package how to compare our unexported type
136
+	if err := kapi.Semantic.AddFuncs(
137
+		func(o1, o2 ResourceQuotasStatusByNamespace) bool {
138
+			return reflect.DeepEqual(o1.orderedMap, o2.orderedMap)
139
+		},
140
+		func(o1, o2 *ResourceQuotasStatusByNamespace) bool {
141
+			if o1 == nil && o2 == nil {
142
+				return true
143
+			}
144
+			if (o1 == nil) != (o2 == nil) {
145
+				return false
146
+			}
147
+			return reflect.DeepEqual(o1.orderedMap, o2.orderedMap)
148
+		},
149
+	); err != nil {
150
+		panic(err)
151
+	}
152
+}
153
+
118 154
 // orderedMap is a very simple ordering a map tracking insertion order.  It allows fast and stable serializations
119 155
 // for our encoding.  You could probably do something fancier with pointers to interfaces, but I didn't.
120 156
 type orderedMap struct {
... ...
@@ -156,9 +156,7 @@ func DeepCopy_api_ClusterResourceQuotaStatus(in interface{}, out interface{}, c
156 156
 		if err := pkg_api.DeepCopy_api_ResourceQuotaStatus(&in.Total, &out.Total, c); err != nil {
157 157
 			return err
158 158
 		}
159
-		if err := DeepCopy_api_ResourceQuotasStatusByNamespace(&in.Namespaces, &out.Namespaces, c); err != nil {
160
-			return err
161
-		}
159
+		out.Namespaces = in.Namespaces.DeepCopy()
162 160
 		return nil
163 161
 	}
164 162
 }