Skip to content

Commit 321bfd6

Browse files
committed
Apply feedback
1 parent a4a07dc commit 321bfd6

File tree

7 files changed

+49
-24
lines changed

7 files changed

+49
-24
lines changed

internal/fixture/state.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func (s *State) UpdateObject(tv *typed.TypedValue, version fieldpath.APIVersion,
100100
return nil
101101
}
102102

103-
// Update the current state with the passed in object
103+
// Set the current state with the passed in object
104104
func (s *State) Update(obj typed.YAMLObject, version fieldpath.APIVersion, manager string) error {
105105
tv, err := s.Parser.FromYAML(FixTabsOrDie(obj))
106106
if err != nil {
@@ -308,7 +308,7 @@ func (f ForceApplyObject) preprocess(parser typed.ParseableType) (Operation, err
308308
return f, nil
309309
}
310310

311-
// Update is a type of operation. It is a controller type of
311+
// Set is a type of operation. It is a controller type of
312312
// update. Errors are passed along.
313313
type Update struct {
314314
Manager string

merge/real_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func BenchmarkOperations(b *testing.B) {
101101
},
102102
},
103103
{
104-
name: "Update",
104+
name: "Set",
105105
ops: []Operation{
106106
Update{
107107
Manager: "controller",

merge/update.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa
114114
return managers, compare, nil
115115
}
116116

117-
// Update is the method you should call once you've merged your final
117+
// Set is the method you should call once you've merged your final
118118
// object on CREATE/UPDATE/PATCH verbs. newObject must be the object
119119
// that you intend to persist (after applying the patch if this is for a
120120
// PATCH call), and liveObject must be the original object (empty if

typed/union.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ func (u *union) Normalize(old, new, out value.Map) error {
243243
return fmt.Errorf("multiple fields set without discriminator change: %v", ns)
244244
}
245245

246-
// Update discriminiator if it needs to be deduced.
246+
// Set discriminiator if it needs to be deduced.
247247
if u.deduceInvalidDiscriminator && len(ns) == 1 {
248248
u.d.Set(out, u.dn.toDiscriminated(*ns.One()))
249249
}
@@ -265,7 +265,7 @@ func (u *union) NormalizeApply(applied, merged, out value.Map) error {
265265
return fmt.Errorf("applied discriminator (%v) doesn't match applied field (%v)", u.d.Get(applied), *as.One())
266266
}
267267

268-
// Update discriminiator if needed
268+
// Set discriminiator if needed
269269
if u.deduceInvalidDiscriminator {
270270
u.d.Set(out, u.dn.toDiscriminated(*as.One()))
271271
}

value/jsontagutil.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,24 @@ limitations under the License.
1717
package value
1818

1919
import (
20+
"fmt"
2021
"reflect"
2122
"strings"
2223
)
2324

2425
// TODO: This implements the same functionality as https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/runtime/converter.go#L236
2526
// but is based on the highly efficient approach from https://golang.org/src/encoding/json/encode.go
2627

27-
func lookupJsonTags(f reflect.StructField) (string, bool, bool) {
28-
var name string
28+
func lookupJsonTags(f reflect.StructField) (name string, omit bool, inline bool, omitempty bool) {
2929
tag := f.Tag.Get("json")
3030
if tag == "-" {
31-
return f.Name, false, false
31+
return "", true, false, false
3232
}
3333
name, opts := parseTag(tag)
3434
if name == "" {
3535
name = f.Name
3636
}
37-
return name, opts.Contains("inline"), opts.Contains("omitempty")
37+
return name, false, opts.Contains("inline"), opts.Contains("omitempty")
3838
}
3939

4040
func isZero(v reflect.Value) bool {
@@ -51,6 +51,8 @@ func isZero(v reflect.Value) bool {
5151
return v.Float() == 0
5252
case reflect.Interface, reflect.Ptr:
5353
return v.IsNil()
54+
case reflect.Chan, reflect.Func:
55+
panic(fmt.Sprintf("unsupported type: %v", v.Type()))
5456
}
5557
return false
5658
}

value/structreflect.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,21 @@ func (c *structCache) Get(t reflect.Type) (map[string]*fieldCacheEntry, bool) {
5353
return entry, ok
5454
}
5555

56-
// Update sets the fieldCacheEntry for the given type via a copy-on-write update to the struct cache.
57-
func (c *structCache) Update(t reflect.Type, m map[string]*fieldCacheEntry) {
56+
// Set sets the fieldCacheEntry for the given type via a copy-on-write update to the struct cache.
57+
func (c *structCache) Set(t reflect.Type, m map[string]*fieldCacheEntry) {
5858
c.mu.Lock()
5959
defer c.mu.Unlock()
6060

61-
oldCacheMap := c.value.Load().(structCacheMap)
62-
newCacheMap := make(structCacheMap, len(oldCacheMap)+1)
63-
for k, v := range oldCacheMap {
61+
currentCacheMap := c.value.Load().(structCacheMap)
62+
63+
if _, ok := currentCacheMap[t]; ok {
64+
// Bail if the entry has been set while waiting for lock acquisition.
65+
// This is safe since setting entries is idempotent.
66+
return
67+
}
68+
69+
newCacheMap := make(structCacheMap, len(currentCacheMap)+1)
70+
for k, v := range currentCacheMap {
6471
newCacheMap[k] = v
6572
}
6673
newCacheMap[t] = m
@@ -99,14 +106,17 @@ func getStructCacheEntry(t reflect.Type) structCacheEntry {
99106
hints := map[string]*fieldCacheEntry{}
100107
buildStructCacheEntry(t, hints, nil)
101108

102-
reflectStructCache.Update(t, hints)
109+
reflectStructCache.Set(t, hints)
103110
return hints
104111
}
105112

106113
func buildStructCacheEntry(t reflect.Type, infos map[string]*fieldCacheEntry, fieldPath [][]int) {
107114
for i := 0; i < t.NumField(); i++ {
108115
field := t.Field(i)
109-
jsonName, isInline, isOmitempty := lookupJsonTags(field)
116+
jsonName, omit, isInline, isOmitempty := lookupJsonTags(field)
117+
if omit {
118+
continue
119+
}
110120
if isInline {
111121
buildStructCacheEntry(field.Type, infos, append(fieldPath, field.Index))
112122
continue
@@ -131,19 +141,19 @@ func (r structReflect) Length() int {
131141
}
132142

133143
func (r structReflect) Get(key string) (Value, bool) {
134-
if val, ok := r.findJsonNameField(key); ok {
144+
if val, ok, _ := r.findJsonNameField(key); ok {
135145
return mustWrapValueReflect(val), true
136146
}
137147
return nil, false
138148
}
139149

140150
func (r structReflect) Has(key string) bool {
141-
_, ok := r.findJsonNameField(key)
151+
_, ok, _ := r.findJsonNameField(key)
142152
return ok
143153
}
144154

145155
func (r structReflect) Set(key string, val Value) {
146-
fieldVal, ok := r.findJsonNameField(key)
156+
fieldVal, ok, _ := r.findJsonNameField(key)
147157
if !ok {
148158
panic(fmt.Sprintf("key %s may not be set on struct %T: field does not exist", key, r.Value.Interface()))
149159
}
@@ -155,14 +165,17 @@ func (r structReflect) Set(key string, val Value) {
155165
}
156166

157167
func (r structReflect) Delete(key string) {
158-
fieldVal, ok := r.findJsonNameField(key)
168+
fieldVal, ok, omitempty := r.findJsonNameField(key)
159169
if !ok {
160170
panic(fmt.Sprintf("key %s may not be deleted on struct %T: field does not exist", key, r.Value.Interface()))
161171
}
162172
if !fieldVal.CanSet() {
163173
// See https://blog.golang.org/laws-of-reflection for details on why a struct may not be settable
164174
panic(fmt.Sprintf("key %s may not be deleted on struct: %T: struct is not settable", key, r.Value.Interface()))
165175
}
176+
if fieldVal.Kind() != reflect.Ptr && !omitempty {
177+
panic(fmt.Sprintf("key %s may not be deleted on struct: %T: value is neither a pointer nor an omitempty field", key, r.Value.Interface()))
178+
}
166179
fieldVal.Set(reflect.Zero(fieldVal.Type()))
167180
}
168181

@@ -228,11 +241,11 @@ func (r structReflect) findJsonNameFieldAndNotEmpty(jsonName string) (reflect.Va
228241
return fieldVal, !omit
229242
}
230243

231-
func (r structReflect) findJsonNameField(jsonName string) (reflect.Value, bool) {
244+
func (r structReflect) findJsonNameField(jsonName string) (val reflect.Value, ok bool, omitEmpty bool) {
232245
structCacheEntry, ok := getStructCacheEntry(r.Value.Type())[jsonName]
233246
if !ok {
234-
return reflect.Value{}, false
247+
return reflect.Value{}, false, false
235248
}
236249
fieldVal := structCacheEntry.getFieldFromStruct(r.Value)
237-
return fieldVal, true
250+
return fieldVal, true, structCacheEntry.isOmitEmpty
238251
}

value/valuereflect_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ type testBasicStruct struct {
179179
I int64 `json:"int"`
180180
S string
181181
}
182+
type testOmitStruct struct {
183+
I int64 `json:"-"`
184+
S string
185+
}
182186
type testInlineStruct struct {
183187
Inline T `json:",inline"`
184188
S string
@@ -213,6 +217,12 @@ func TestReflectStruct(t *testing.T) {
213217
expectedMap: map[string]interface{}{"int": int64(10), "S": "string"},
214218
expectedUnstructured: map[string]interface{}{"int": int64(10), "S": "string"},
215219
},
220+
{
221+
name: "omit",
222+
val: testOmitStruct{I: 10, S: "string"},
223+
expectedMap: map[string]interface{}{"S": "string"},
224+
expectedUnstructured: map[string]interface{}{"S": "string"},
225+
},
216226
{
217227
name: "inline",
218228
val: &testInlineStruct{Inline: T{I: 10}, S: "string"},

0 commit comments

Comments
 (0)