Merge pull request #43346 from atlassian/fix-time-npe
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Fix pointer receiver handling in queryparams marshaler **What this PR does / why we need it**: `Time.MarshalQueryParameter()` and `Time.MarshalJSON()` try to handle nil pointer object (they call `t.IsZero()` which checks if t == nil) but fail because receiver is not a pointer so the dereference needed to pass it as receiver to these methods fails with npe. In practice this happens with `Unstructured.SetDeletionTimestamp(Unstructured.GetDeletionTimestamp())`. Here is the stacktrace of the failing test if receiver is not a pointer. ```go panic: runtime error: invalid memory address or nil pointer dereference [recovered] panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1407969] goroutine 22 [running]: testing.tRunner.func1(0xc4204f0680) /usr/local/Cellar/go/1.8/libexec/src/testing/testing.go:622 +0x29d panic(0x1485a80, 0x1782bc0) /usr/local/Cellar/go/1.8/libexec/src/runtime/panic.go:489 +0x2cf k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.(*Unstructured).SetDeletionTimestamp(0xc420030790, 0x0) /Users/ash2k/gopath/src/k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go:387 +0x29 k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.TestNilDeletionTimestamp(0xc4204f0680) /Users/ash2k/gopath/src/k8s.io/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_test.go:40 +0x4c ```
This commit is contained in:
		@@ -58,3 +58,21 @@ func TestUnstructuredList(t *testing.T) {
 | 
				
			|||||||
		t.Fatalf("unexpected fields: %#v", items[0])
 | 
							t.Fatalf("unexpected fields: %#v", items[0])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNilDeletionTimestamp(t *testing.T) {
 | 
				
			||||||
 | 
						var u Unstructured
 | 
				
			||||||
 | 
						del := u.GetDeletionTimestamp()
 | 
				
			||||||
 | 
						if del != nil {
 | 
				
			||||||
 | 
							t.Errorf("unexpected non-nil deletion timestamp: %v", del)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						u.SetDeletionTimestamp(u.GetDeletionTimestamp())
 | 
				
			||||||
 | 
						del = u.GetDeletionTimestamp()
 | 
				
			||||||
 | 
						if del != nil {
 | 
				
			||||||
 | 
							t.Errorf("unexpected non-nil deletion timestamp: %v", del)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						metadata := u.Object["metadata"].(map[string]interface{})
 | 
				
			||||||
 | 
						deletionTimestamp := metadata["deletionTimestamp"]
 | 
				
			||||||
 | 
						if deletionTimestamp != nil {
 | 
				
			||||||
 | 
							t.Errorf("unexpected deletion timestamp field: %q", deletionTimestamp)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -89,9 +89,16 @@ func customMarshalValue(value reflect.Value) (reflect.Value, bool) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	marshaler, ok := value.Interface().(Marshaler)
 | 
						marshaler, ok := value.Interface().(Marshaler)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							if !isPointerKind(value.Kind()) && value.CanAddr() {
 | 
				
			||||||
 | 
								marshaler, ok = value.Addr().Interface().(Marshaler)
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				return reflect.Value{}, false
 | 
									return reflect.Value{}, false
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return reflect.Value{}, false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Don't invoke functions on nil pointers
 | 
						// Don't invoke functions on nil pointers
 | 
				
			||||||
	// If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response
 | 
						// If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,6 +72,7 @@ type childStructs struct {
 | 
				
			|||||||
	SinceSeconds   *int64       `json:"sinceSeconds,omitempty"`
 | 
						SinceSeconds   *int64       `json:"sinceSeconds,omitempty"`
 | 
				
			||||||
	SinceTime      *metav1.Time `json:"sinceTime,omitempty"`
 | 
						SinceTime      *metav1.Time `json:"sinceTime,omitempty"`
 | 
				
			||||||
	EmptyTime      *metav1.Time `json:"emptyTime"`
 | 
						EmptyTime      *metav1.Time `json:"emptyTime"`
 | 
				
			||||||
 | 
						NonPointerTime metav1.Time  `json:"nonPointerTime"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (obj *childStructs) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
 | 
					func (obj *childStructs) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
 | 
				
			||||||
@@ -183,8 +184,9 @@ func TestConvert(t *testing.T) {
 | 
				
			|||||||
				SinceSeconds:   &sinceSeconds,
 | 
									SinceSeconds:   &sinceSeconds,
 | 
				
			||||||
				SinceTime:      &sinceTime, // test a custom marshaller
 | 
									SinceTime:      &sinceTime, // test a custom marshaller
 | 
				
			||||||
				EmptyTime:      nil,        // test a nil custom marshaller without omitempty
 | 
									EmptyTime:      nil,        // test a nil custom marshaller without omitempty
 | 
				
			||||||
 | 
									NonPointerTime: sinceTime,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "sinceTime": {"2000-01-01T12:34:56Z"}, "emptyTime": {""}},
 | 
								expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "sinceTime": {"2000-01-01T12:34:56Z"}, "emptyTime": {""}, "nonPointerTime": {"2000-01-01T12:34:56Z"}},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			input: &childStructs{
 | 
								input: &childStructs{
 | 
				
			||||||
@@ -193,8 +195,9 @@ func TestConvert(t *testing.T) {
 | 
				
			|||||||
				Previous:       true,
 | 
									Previous:       true,
 | 
				
			||||||
				SinceSeconds:   &sinceSeconds,
 | 
									SinceSeconds:   &sinceSeconds,
 | 
				
			||||||
				SinceTime:      nil, // test a nil custom marshaller with omitempty
 | 
									SinceTime:      nil, // test a nil custom marshaller with omitempty
 | 
				
			||||||
 | 
									NonPointerTime: sinceTime,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "emptyTime": {""}},
 | 
								expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "emptyTime": {""}, "nonPointerTime": {"2000-01-01T12:34:56Z"}},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user