mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
Updating generic registry to return UID while deleting the object
This commit is contained in:
parent
885ddcc138
commit
44fc88cecd
@ -425,6 +425,11 @@ type StatusDetails struct {
|
|||||||
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
|
||||||
// +optional
|
// +optional
|
||||||
Kind string `json:"kind,omitempty" protobuf:"bytes,3,opt,name=kind"`
|
Kind string `json:"kind,omitempty" protobuf:"bytes,3,opt,name=kind"`
|
||||||
|
// UID of the resource.
|
||||||
|
// (when there is a single resource which can be described).
|
||||||
|
// More info: http://kubernetes.io/docs/user-guide/identifiers#uids
|
||||||
|
// +optional
|
||||||
|
UID types.UID `json:"uid,omitempty" protobuf:"bytes,6,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"`
|
||||||
// The Causes array includes more details associated with the StatusReason
|
// The Causes array includes more details associated with the StatusReason
|
||||||
// failure. Not all StatusReasons may provide detailed causes.
|
// failure. Not all StatusReasons may provide detailed causes.
|
||||||
// +optional
|
// +optional
|
||||||
|
@ -1059,7 +1059,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco
|
|||||||
type resultFunc func() (runtime.Object, error)
|
type resultFunc func() (runtime.Object, error)
|
||||||
|
|
||||||
// finishRequest makes a given resultFunc asynchronous and handles errors returned by the response.
|
// finishRequest makes a given resultFunc asynchronous and handles errors returned by the response.
|
||||||
// Any api.Status object returned is considered an "error", which interrupts the normal response flow.
|
// An api.Status object with status != success is considered an "error", which interrupts the normal response flow.
|
||||||
func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, err error) {
|
func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, err error) {
|
||||||
// these channels need to be buffered to prevent the goroutine below from hanging indefinitely
|
// these channels need to be buffered to prevent the goroutine below from hanging indefinitely
|
||||||
// when the select statement reads something other than the one the goroutine sends on.
|
// when the select statement reads something other than the one the goroutine sends on.
|
||||||
@ -1083,7 +1083,9 @@ func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object,
|
|||||||
select {
|
select {
|
||||||
case result = <-ch:
|
case result = <-ch:
|
||||||
if status, ok := result.(*metav1.Status); ok {
|
if status, ok := result.(*metav1.Status); ok {
|
||||||
return nil, errors.FromObject(status)
|
if status.Status != metav1.StatusSuccess {
|
||||||
|
return nil, errors.FromObject(status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
case err = <-errCh:
|
case err = <-errCh:
|
||||||
|
@ -597,3 +597,62 @@ func TestParseTimeout(t *testing.T) {
|
|||||||
t.Errorf("10s timeout produced: %v", d)
|
t.Errorf("10s timeout produced: %v", d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFinishRequest(t *testing.T) {
|
||||||
|
exampleObj := &example.Pod{}
|
||||||
|
exampleErr := fmt.Errorf("error")
|
||||||
|
successStatusObj := &metav1.Status{Status: metav1.StatusSuccess, Message: "success message"}
|
||||||
|
errorStatusObj := &metav1.Status{Status: metav1.StatusFailure, Message: "error message"}
|
||||||
|
testcases := []struct {
|
||||||
|
timeout time.Duration
|
||||||
|
fn resultFunc
|
||||||
|
expectedObj runtime.Object
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// Expected obj is returned.
|
||||||
|
timeout: time.Second,
|
||||||
|
fn: func() (runtime.Object, error) {
|
||||||
|
return exampleObj, nil
|
||||||
|
},
|
||||||
|
expectedObj: exampleObj,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Expected error is returned.
|
||||||
|
timeout: time.Second,
|
||||||
|
fn: func() (runtime.Object, error) {
|
||||||
|
return nil, exampleErr
|
||||||
|
},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedErr: exampleErr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Successful status object is returned as expected.
|
||||||
|
timeout: time.Second,
|
||||||
|
fn: func() (runtime.Object, error) {
|
||||||
|
return successStatusObj, nil
|
||||||
|
},
|
||||||
|
expectedObj: successStatusObj,
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Error status object is converted to StatusError.
|
||||||
|
timeout: time.Second,
|
||||||
|
fn: func() (runtime.Object, error) {
|
||||||
|
return errorStatusObj, nil
|
||||||
|
},
|
||||||
|
expectedObj: nil,
|
||||||
|
expectedErr: apierrors.FromObject(errorStatusObj),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, tc := range testcases {
|
||||||
|
obj, err := finishRequest(tc.timeout, tc.fn)
|
||||||
|
if (err == nil && tc.expectedErr != nil) || (err != nil && tc.expectedErr == nil) || (err != nil && tc.expectedErr != nil && err.Error() != tc.expectedErr.Error()) {
|
||||||
|
t.Errorf("%d: unexpected err. expected: %v, got: %v", i, tc.expectedErr, err)
|
||||||
|
}
|
||||||
|
if !apiequality.Semantic.DeepEqual(obj, tc.expectedObj) {
|
||||||
|
t.Errorf("%d: unexpected obj. expected %#v, got %#v", tc.expectedObj, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -992,7 +992,20 @@ func (e *Store) finalizeDelete(obj runtime.Object, runHooks bool) (runtime.Objec
|
|||||||
}
|
}
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
return &metav1.Status{Status: metav1.StatusSuccess}, nil
|
// Return information about the deleted object, which enables clients to
|
||||||
|
// verify that the object was actually deleted and not waiting for finalizers.
|
||||||
|
accessor, err := meta.Accessor(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
details := &metav1.StatusDetails{
|
||||||
|
Name: accessor.GetName(),
|
||||||
|
Group: e.QualifiedResource.Group,
|
||||||
|
Kind: e.QualifiedResource.Resource, // Yes we set Kind field to resource.
|
||||||
|
UID: accessor.GetUID(),
|
||||||
|
}
|
||||||
|
status := &metav1.Status{Status: metav1.StatusSuccess, Details: details}
|
||||||
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch makes a matcher for the given label and field, and calls
|
// Watch makes a matcher for the given label and field, and calls
|
||||||
|
@ -1542,3 +1542,29 @@ func newTestGenericStoreRegistry(t *testing.T, scheme *runtime.Scheme, hasCacheE
|
|||||||
Storage: s,
|
Storage: s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFinalizeDelete(t *testing.T) {
|
||||||
|
// Verify that it returns the expected Status.
|
||||||
|
_, s := NewTestGenericStoreRegistry(t)
|
||||||
|
obj := &example.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: "random-uid"},
|
||||||
|
}
|
||||||
|
result, err := s.finalizeDelete(obj, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %s", err)
|
||||||
|
}
|
||||||
|
returnedObj := result.(*metav1.Status)
|
||||||
|
|
||||||
|
expectedObj := &metav1.Status{
|
||||||
|
Status: metav1.StatusSuccess,
|
||||||
|
Details: &metav1.StatusDetails{
|
||||||
|
Name: "foo",
|
||||||
|
Group: s.QualifiedResource.Group,
|
||||||
|
Kind: s.QualifiedResource.Resource,
|
||||||
|
UID: "random-uid",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !apiequality.Semantic.DeepEqual(expectedObj, returnedObj) {
|
||||||
|
t.Errorf("unexpected obj. expected %#v, got %#v", expectedObj, returnedObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user