mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 09:22:44 +00:00
Merge pull request #13509 from wojtek-t/refactor_etcd_delete_test
Refactor registry etcd delete tests
This commit is contained in:
commit
fcbf63f3f3
@ -42,6 +42,7 @@ type Tester struct {
|
|||||||
clusterScope bool
|
clusterScope bool
|
||||||
createOnUpdate bool
|
createOnUpdate bool
|
||||||
generatesName bool
|
generatesName bool
|
||||||
|
returnDeletedObject bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type injectErrorFunc func(err error)
|
type injectErrorFunc func(err error)
|
||||||
@ -75,6 +76,11 @@ func (t *Tester) GeneratesName() *Tester {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tester) ReturnDeletedObject() *Tester {
|
||||||
|
t.returnDeletedObject = true
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// TestNamespace returns the namespace that will be used when creating contexts.
|
// TestNamespace returns the namespace that will be used when creating contexts.
|
||||||
// Returns NamespaceNone for cluster-scoped objects.
|
// Returns NamespaceNone for cluster-scoped objects.
|
||||||
func (t *Tester) TestNamespace() string {
|
func (t *Tester) TestNamespace() string {
|
||||||
@ -125,6 +131,7 @@ type EmitFunc func(runtime.Object, string) error
|
|||||||
type GetFunc func(api.Context, runtime.Object) (runtime.Object, error)
|
type GetFunc func(api.Context, runtime.Object) (runtime.Object, error)
|
||||||
type InitWatchFunc func()
|
type InitWatchFunc func()
|
||||||
type InjectErrFunc func(err error)
|
type InjectErrFunc func(err error)
|
||||||
|
type IsErrorFunc func(err error) bool
|
||||||
type SetFunc func(api.Context, runtime.Object) error
|
type SetFunc func(api.Context, runtime.Object) error
|
||||||
type SetRVFunc func(uint64)
|
type SetRVFunc func(uint64)
|
||||||
type UpdateFunc func(runtime.Object) runtime.Object
|
type UpdateFunc func(runtime.Object) runtime.Object
|
||||||
@ -160,50 +167,45 @@ func (t *Tester) TestUpdate(valid runtime.Object, setFn SetFunc, setRVFn SetRVFu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test deleting an object.
|
// Test deleting an object.
|
||||||
// TODO(wojtek-t): Change it to use AssignFunc instead.
|
func (t *Tester) TestDelete(valid runtime.Object, setFn SetFunc, getFn GetFunc, isNotFoundFn IsErrorFunc) {
|
||||||
func (t *Tester) TestDelete(createFn func() runtime.Object, wasGracefulFn func() bool, invalid ...runtime.Object) {
|
t.testDeleteNonExist(copyOrDie(valid))
|
||||||
t.TestDeleteNonExist(createFn)
|
t.testDeleteNoGraceful(copyOrDie(valid), setFn, getFn, isNotFoundFn)
|
||||||
t.TestDeleteNoGraceful(createFn, wasGracefulFn)
|
|
||||||
t.TestDeleteInvokesValidation(invalid...)
|
|
||||||
// TODO: Test delete namespace mismatch rejection
|
|
||||||
// once #5684 is fixed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test graceful deletion.
|
// Test gracefully deleting an object.
|
||||||
// TODO(wojtek-t): Change it to use AssignFunc instead.
|
func (t *Tester) TestDeleteGraceful(valid runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) {
|
||||||
func (t *Tester) TestDeleteGraceful(createFn func() runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
|
t.testDeleteGracefulHasDefault(copyOrDie(valid), setFn, getFn, expectedGrace)
|
||||||
t.TestDeleteGracefulHasDefault(createFn(), expectedGrace, wasGracefulFn)
|
t.testDeleteGracefulWithValue(copyOrDie(valid), setFn, getFn, expectedGrace)
|
||||||
t.TestDeleteGracefulWithValue(createFn(), expectedGrace, wasGracefulFn)
|
t.testDeleteGracefulUsesZeroOnNil(copyOrDie(valid), setFn, expectedGrace)
|
||||||
t.TestDeleteGracefulUsesZeroOnNil(createFn(), 0)
|
t.testDeleteGracefulExtend(copyOrDie(valid), setFn, getFn, expectedGrace)
|
||||||
t.TestDeleteGracefulExtend(createFn(), expectedGrace, wasGracefulFn)
|
t.testDeleteGracefulImmediate(copyOrDie(valid), setFn, getFn, expectedGrace)
|
||||||
t.TestDeleteGracefulImmediate(createFn(), expectedGrace, wasGracefulFn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test getting object.
|
// Test getting object.
|
||||||
func (t *Tester) TestGet(obj runtime.Object) {
|
func (t *Tester) TestGet(valid runtime.Object) {
|
||||||
t.testGetFound(obj)
|
t.testGetFound(copyOrDie(valid))
|
||||||
t.testGetNotFound(obj)
|
t.testGetNotFound(copyOrDie(valid))
|
||||||
t.testGetMimatchedNamespace(obj)
|
t.testGetMimatchedNamespace(copyOrDie(valid))
|
||||||
if !t.clusterScope {
|
if !t.clusterScope {
|
||||||
t.testGetDifferentNamespace(obj)
|
t.testGetDifferentNamespace(copyOrDie(valid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test listing objects.
|
// Test listing objects.
|
||||||
func (t *Tester) TestList(obj runtime.Object, assignFn AssignFunc, setRVFn SetRVFunc) {
|
func (t *Tester) TestList(valid runtime.Object, assignFn AssignFunc, setRVFn SetRVFunc) {
|
||||||
t.testListError()
|
t.testListError()
|
||||||
t.testListFound(obj, assignFn)
|
t.testListFound(copyOrDie(valid), assignFn)
|
||||||
t.testListNotFound(assignFn, setRVFn)
|
t.testListNotFound(assignFn, setRVFn)
|
||||||
t.testListMatchLabels(obj, assignFn)
|
t.testListMatchLabels(copyOrDie(valid), assignFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test watching objects.
|
// Test watching objects.
|
||||||
func (t *Tester) TestWatch(
|
func (t *Tester) TestWatch(
|
||||||
obj runtime.Object, initWatchFn InitWatchFunc, injectErrFn InjectErrFunc, emitFn EmitFunc,
|
valid runtime.Object, initWatchFn InitWatchFunc, injectErrFn InjectErrFunc, emitFn EmitFunc,
|
||||||
labelsPass, labelsFail []labels.Set, fieldsPass, fieldsFail []fields.Set, actions []string) {
|
labelsPass, labelsFail []labels.Set, fieldsPass, fieldsFail []fields.Set, actions []string) {
|
||||||
t.testWatch(initWatchFn, injectErrFn)
|
t.testWatch(initWatchFn, injectErrFn)
|
||||||
t.testWatchLabels(copyOrDie(obj), initWatchFn, emitFn, labelsPass, labelsFail, actions)
|
t.testWatchLabels(copyOrDie(valid), initWatchFn, emitFn, labelsPass, labelsFail, actions)
|
||||||
t.testWatchFields(copyOrDie(obj), initWatchFn, emitFn, fieldsPass, fieldsFail, actions)
|
t.testWatchFields(copyOrDie(valid), initWatchFn, emitFn, fieldsPass, fieldsFail, actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@ -512,26 +514,40 @@ func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, setFn
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Deletion tests.
|
// Deletion tests.
|
||||||
|
|
||||||
func (t *Tester) TestDeleteInvokesValidation(invalid ...runtime.Object) {
|
func (t *Tester) testDeleteNoGraceful(obj runtime.Object, setFn SetFunc, getFn GetFunc, isNotFoundFn IsErrorFunc) {
|
||||||
for i, obj := range invalid {
|
|
||||||
objectMeta := t.getObjectMetaOrFail(obj)
|
|
||||||
ctx := t.TestContext()
|
ctx := t.TestContext()
|
||||||
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
|
|
||||||
if !errors.IsInvalid(err) {
|
foo := copyOrDie(obj)
|
||||||
t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err)
|
t.setObjectMeta(foo, "foo1")
|
||||||
|
if err := setFn(ctx, foo); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
objectMeta := t.getObjectMetaOrFail(foo)
|
||||||
|
obj, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !t.returnDeletedObject {
|
||||||
|
if status, ok := obj.(*api.Status); !ok {
|
||||||
|
t.Errorf("expected status of delete, got %v", status)
|
||||||
|
} else if status.Status != api.StatusSuccess {
|
||||||
|
t.Errorf("expected success, got: %v", status.Status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tester) TestDeleteNonExist(createFn func() runtime.Object) {
|
_, err = getFn(ctx, foo)
|
||||||
existing := createFn()
|
if err == nil || !isNotFoundFn(err) {
|
||||||
objectMeta := t.getObjectMetaOrFail(existing)
|
t.Errorf("unexpected error: %v", err)
|
||||||
context := t.TestContext()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tester) testDeleteNonExist(obj runtime.Object) {
|
||||||
|
objectMeta := t.getObjectMetaOrFail(obj)
|
||||||
|
|
||||||
t.withStorageError(&etcd.EtcdError{ErrorCode: tools.EtcdErrorCodeNotFound}, func() {
|
t.withStorageError(&etcd.EtcdError{ErrorCode: tools.EtcdErrorCodeNotFound}, func() {
|
||||||
_, err := t.storage.(rest.GracefulDeleter).Delete(context, objectMeta.Name, nil)
|
_, err := t.storage.(rest.GracefulDeleter).Delete(t.TestContext(), objectMeta.Name, nil)
|
||||||
if err == nil || !errors.IsNotFound(err) {
|
if err == nil || !errors.IsNotFound(err) {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -539,100 +555,77 @@ func (t *Tester) TestDeleteNonExist(createFn func() runtime.Object) {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
// Graceful Deletion tests.
|
// Graceful Deletion tests.
|
||||||
|
|
||||||
func (t *Tester) TestDeleteNoGraceful(createFn func() runtime.Object, wasGracefulFn func() bool) {
|
func (t *Tester) testDeleteGracefulHasDefault(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) {
|
||||||
existing := createFn()
|
ctx := t.TestContext()
|
||||||
objectMeta := t.getObjectMetaOrFail(existing)
|
|
||||||
ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
|
foo := copyOrDie(obj)
|
||||||
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(10))
|
t.setObjectMeta(foo, "foo1")
|
||||||
if err != nil {
|
if err := setFn(ctx, foo); err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name); !errors.IsNotFound(err) {
|
objectMeta := t.getObjectMetaOrFail(foo)
|
||||||
t.Errorf("unexpected error, object should not exist: %v", err)
|
|
||||||
}
|
|
||||||
if wasGracefulFn() {
|
|
||||||
t.Errorf("resource should not support graceful delete")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tester) TestDeleteGracefulHasDefault(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
|
|
||||||
objectMeta := t.getObjectMetaOrFail(existing)
|
|
||||||
ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
|
|
||||||
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
|
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, &api.DeleteOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if !wasGracefulFn() {
|
if _, err := getFn(ctx, foo); err != nil {
|
||||||
t.Errorf("did not gracefully delete resource")
|
t.Fatalf("did not gracefully delete resource", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
|
object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error, object should exist: %v", err)
|
t.Fatalf("unexpected error, object should exist: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
objectMeta, err = api.ObjectMetaFor(object)
|
objectMeta = t.getObjectMetaOrFail(object)
|
||||||
if err != nil {
|
if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != expectedGrace {
|
||||||
t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, object)
|
t.Errorf("unexpected deleted meta: %#v", objectMeta)
|
||||||
}
|
|
||||||
if objectMeta.DeletionTimestamp == nil {
|
|
||||||
t.Errorf("did not set deletion timestamp")
|
|
||||||
}
|
|
||||||
if objectMeta.DeletionGracePeriodSeconds == nil {
|
|
||||||
t.Fatalf("did not set deletion grace period seconds")
|
|
||||||
}
|
|
||||||
if *objectMeta.DeletionGracePeriodSeconds != expectedGrace {
|
|
||||||
t.Errorf("actual grace period does not match expected: %d", *objectMeta.DeletionGracePeriodSeconds)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tester) TestDeleteGracefulWithValue(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
|
func (t *Tester) testDeleteGracefulWithValue(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) {
|
||||||
objectMeta, err := api.ObjectMetaFor(existing)
|
ctx := t.TestContext()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
|
foo := copyOrDie(obj)
|
||||||
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
|
t.setObjectMeta(foo, "foo2")
|
||||||
|
if err := setFn(ctx, foo); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
objectMeta := t.getObjectMetaOrFail(foo)
|
||||||
|
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if !wasGracefulFn() {
|
if _, err := getFn(ctx, foo); err != nil {
|
||||||
t.Errorf("did not gracefully delete resource")
|
t.Fatalf("did not gracefully delete resource", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
|
object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error, object should exist: %v", err)
|
t.Errorf("unexpected error, object should exist: %v", err)
|
||||||
}
|
}
|
||||||
objectMeta, err = api.ObjectMetaFor(object)
|
objectMeta = t.getObjectMetaOrFail(object)
|
||||||
if err != nil {
|
if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != expectedGrace+2 {
|
||||||
t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, object)
|
t.Errorf("unexpected deleted meta: %#v", objectMeta)
|
||||||
}
|
|
||||||
if objectMeta.DeletionTimestamp == nil {
|
|
||||||
t.Errorf("did not set deletion timestamp")
|
|
||||||
}
|
|
||||||
if objectMeta.DeletionGracePeriodSeconds == nil {
|
|
||||||
t.Fatalf("did not set deletion grace period seconds")
|
|
||||||
}
|
|
||||||
if *objectMeta.DeletionGracePeriodSeconds != expectedGrace+2 {
|
|
||||||
t.Errorf("actual grace period does not match expected: %d", *objectMeta.DeletionGracePeriodSeconds)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tester) TestDeleteGracefulExtend(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
|
func (t *Tester) testDeleteGracefulExtend(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) {
|
||||||
objectMeta, err := api.ObjectMetaFor(existing)
|
ctx := t.TestContext()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
|
foo := copyOrDie(obj)
|
||||||
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
|
t.setObjectMeta(foo, "foo3")
|
||||||
|
if err := setFn(ctx, foo); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
objectMeta := t.getObjectMetaOrFail(foo)
|
||||||
|
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if !wasGracefulFn() {
|
if _, err := getFn(ctx, foo); err != nil {
|
||||||
t.Errorf("did not gracefully delete resource")
|
t.Fatalf("did not gracefully delete resource", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// second delete duration is ignored
|
// second delete duration is ignored
|
||||||
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
|
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace+2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -642,35 +635,29 @@ func (t *Tester) TestDeleteGracefulExtend(existing runtime.Object, expectedGrace
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error, object should exist: %v", err)
|
t.Errorf("unexpected error, object should exist: %v", err)
|
||||||
}
|
}
|
||||||
objectMeta, err = api.ObjectMetaFor(object)
|
objectMeta = t.getObjectMetaOrFail(object)
|
||||||
if err != nil {
|
if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != expectedGrace {
|
||||||
t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, object)
|
t.Errorf("unexpected deleted meta: %#v", objectMeta)
|
||||||
}
|
|
||||||
if objectMeta.DeletionTimestamp == nil {
|
|
||||||
t.Errorf("did not set deletion timestamp")
|
|
||||||
}
|
|
||||||
if objectMeta.DeletionGracePeriodSeconds == nil {
|
|
||||||
t.Fatalf("did not set deletion grace period seconds")
|
|
||||||
}
|
|
||||||
if *objectMeta.DeletionGracePeriodSeconds != expectedGrace {
|
|
||||||
t.Errorf("actual grace period does not match expected: %d", *objectMeta.DeletionGracePeriodSeconds)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tester) TestDeleteGracefulImmediate(existing runtime.Object, expectedGrace int64, wasGracefulFn func() bool) {
|
func (t *Tester) testDeleteGracefulImmediate(obj runtime.Object, setFn SetFunc, getFn GetFunc, expectedGrace int64) {
|
||||||
objectMeta, err := api.ObjectMetaFor(existing)
|
ctx := t.TestContext()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, existing)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
|
foo := copyOrDie(obj)
|
||||||
_, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
|
t.setObjectMeta(foo, "foo4")
|
||||||
|
if err := setFn(ctx, foo); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
objectMeta := t.getObjectMetaOrFail(foo)
|
||||||
|
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(expectedGrace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
if !wasGracefulFn() {
|
if _, err := getFn(ctx, foo); err != nil {
|
||||||
t.Errorf("did not gracefully delete resource")
|
t.Fatalf("did not gracefully delete resource", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// second delete is immediate, resource is deleted
|
// second delete is immediate, resource is deleted
|
||||||
out, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(0))
|
out, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, api.NewDeleteOptions(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -680,19 +667,21 @@ func (t *Tester) TestDeleteGracefulImmediate(existing runtime.Object, expectedGr
|
|||||||
if !errors.IsNotFound(err) {
|
if !errors.IsNotFound(err) {
|
||||||
t.Errorf("unexpected error, object should be deleted immediately: %v", err)
|
t.Errorf("unexpected error, object should be deleted immediately: %v", err)
|
||||||
}
|
}
|
||||||
objectMeta, err = api.ObjectMetaFor(out)
|
objectMeta = t.getObjectMetaOrFail(out)
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != 0 {
|
if objectMeta.DeletionTimestamp == nil || objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds != 0 {
|
||||||
t.Errorf("unexpected deleted meta: %#v", objectMeta)
|
t.Errorf("unexpected deleted meta: %#v", objectMeta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tester) TestDeleteGracefulUsesZeroOnNil(existing runtime.Object, expectedGrace int64) {
|
func (t *Tester) testDeleteGracefulUsesZeroOnNil(obj runtime.Object, setFn SetFunc, expectedGrace int64) {
|
||||||
objectMeta := t.getObjectMetaOrFail(existing)
|
ctx := t.TestContext()
|
||||||
ctx := api.WithNamespace(t.TestContext(), objectMeta.Namespace)
|
|
||||||
|
foo := copyOrDie(obj)
|
||||||
|
t.setObjectMeta(foo, "foo5")
|
||||||
|
if err := setFn(ctx, foo); err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
objectMeta := t.getObjectMetaOrFail(foo)
|
||||||
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
|
_, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.Name, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
@ -19,16 +19,12 @@ package etcd
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||||
@ -127,6 +123,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestDelete(validNewController())
|
||||||
|
}
|
||||||
|
|
||||||
func TestGenerationNumber(t *testing.T) {
|
func TestGenerationNumber(t *testing.T) {
|
||||||
storage, _ := newStorage(t)
|
storage, _ := newStorage(t)
|
||||||
modifiedSno := *validNewController()
|
modifiedSno := *validNewController()
|
||||||
@ -218,59 +220,3 @@ func TestWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdDeleteController(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
|
||||||
key, _ := storage.KeyFunc(ctx, validController.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
|
|
||||||
fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validController), 0)
|
|
||||||
obj, err := storage.Delete(ctx, validController.Name, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if status, ok := obj.(*api.Status); !ok {
|
|
||||||
t.Errorf("Expected status of delete, got %#v", status)
|
|
||||||
} else if status.Status != api.StatusSuccess {
|
|
||||||
t.Errorf("Expected success, got %#v", status.Status)
|
|
||||||
}
|
|
||||||
if len(fakeClient.DeletedKeys) != 1 {
|
|
||||||
t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
||||||
}
|
|
||||||
if fakeClient.DeletedKeys[0] != key {
|
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
|
||||||
key, _ := storage.KeyFunc(ctx, validController.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
rc := *validNewController()
|
|
||||||
rc.ResourceVersion = "1"
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), &rc),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return &rc
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
// If the controller is still around after trying to delete either the delete
|
|
||||||
// failed, or we're deleting it gracefully.
|
|
||||||
if fakeClient.Data[key].R.Node != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
|
||||||
|
@ -19,17 +19,13 @@ package etcd
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
|
||||||
"k8s.io/kubernetes/pkg/expapi"
|
"k8s.io/kubernetes/pkg/expapi"
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||||
@ -121,6 +117,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestDelete(validNewDaemon())
|
||||||
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
@ -158,59 +160,3 @@ func TestWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdDeleteDaemon(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
|
||||||
key, err := storage.KeyFunc(ctx, validDaemon.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
|
|
||||||
fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validDaemon), 0)
|
|
||||||
obj, err := storage.Delete(ctx, validDaemon.Name, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if status, ok := obj.(*api.Status); !ok {
|
|
||||||
t.Errorf("Expected status of delete, got %#v", status)
|
|
||||||
} else if status.Status != api.StatusSuccess {
|
|
||||||
t.Errorf("Expected success, got %#v", status.Status)
|
|
||||||
}
|
|
||||||
if len(fakeClient.DeletedKeys) != 1 {
|
|
||||||
t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
||||||
}
|
|
||||||
if fakeClient.DeletedKeys[0] != key {
|
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
|
||||||
key, _ := storage.KeyFunc(ctx, validDaemon.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
dc := validNewDaemon()
|
|
||||||
dc.ResourceVersion = "1"
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), dc),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return dc
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
// If the daemon is still around after trying to delete either the delete
|
|
||||||
// failed, or we're deleting it gracefully.
|
|
||||||
if fakeClient.Data[key].R.Node != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
|
||||||
|
@ -19,17 +19,13 @@ package etcd
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
|
||||||
"k8s.io/kubernetes/pkg/expapi"
|
"k8s.io/kubernetes/pkg/expapi"
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||||
@ -122,6 +118,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestDelete(validNewDeployment())
|
||||||
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
@ -157,58 +159,3 @@ func TestWatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdDelete(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
|
||||||
key, err := storage.KeyFunc(ctx, validDeployment.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
|
|
||||||
fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validNewDeployment()), 0)
|
|
||||||
obj, err := storage.Delete(ctx, validDeployment.Name, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if status, ok := obj.(*api.Status); !ok {
|
|
||||||
t.Errorf("Expected status of delete, got %#v", status)
|
|
||||||
} else if status.Status != api.StatusSuccess {
|
|
||||||
t.Errorf("Expected success, got %#v", status.Status)
|
|
||||||
}
|
|
||||||
if len(fakeClient.DeletedKeys) != 1 {
|
|
||||||
t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
||||||
}
|
|
||||||
if fakeClient.DeletedKeys[0] != key {
|
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
|
||||||
key, _ := storage.KeyFunc(ctx, validDeployment.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
dc := validNewDeployment()
|
|
||||||
dc.ResourceVersion = "1"
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), dc),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return dc
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
// If the deployment is still around after trying to delete either the delete
|
|
||||||
// failed, or we're deleting it gracefully.
|
|
||||||
if fakeClient.Data[key].R.Node != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
|
||||||
|
@ -20,15 +20,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
|
||||||
"k8s.io/kubernetes/pkg/util"
|
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||||
@ -93,31 +89,9 @@ func TestUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestDelete(validNewEndpoints())
|
||||||
endpoints := validChangedEndpoints()
|
|
||||||
key, _ := storage.KeyFunc(ctx, endpoints.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), endpoints),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return endpoints
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
if fakeClient.Data[key].R.Node == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fakeClient.Data[key].R.Node.TTL == 30
|
|
||||||
}
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -132,43 +106,25 @@ func TestList(t *testing.T) {
|
|||||||
test.TestList(validNewEndpoints())
|
test.TestList(validNewEndpoints())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEndpointsDecode(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
storage, _ := newStorage(t)
|
|
||||||
expected := validNewEndpoints()
|
|
||||||
body, err := testapi.Codec().Encode(expected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := storage.New()
|
|
||||||
if err := testapi.Codec().DecodeInto(body, actual); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expected, actual) {
|
|
||||||
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteEndpoints(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
endpoints := validNewEndpoints()
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
name := endpoints.Name
|
test.TestWatch(
|
||||||
key, _ := storage.KeyFunc(ctx, name)
|
validNewEndpoints(),
|
||||||
key = etcdtest.AddPrefix(key)
|
// matching labels
|
||||||
fakeClient.ChangeIndex = 1
|
[]labels.Set{},
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
// not matching labels
|
||||||
R: &etcd.Response{
|
[]labels.Set{
|
||||||
Node: &etcd.Node{
|
{"foo": "bar"},
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), endpoints),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
CreatedIndex: 1,
|
|
||||||
},
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "foo"},
|
||||||
},
|
},
|
||||||
}
|
// not matching fields
|
||||||
_, err := storage.Delete(ctx, name, nil)
|
[]fields.Set{
|
||||||
if err != nil {
|
{"metadata.name": "bar"},
|
||||||
t.Fatalf("unexpected error: %v", err)
|
{"name": "foo"},
|
||||||
}
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,17 +21,14 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
|
||||||
"k8s.io/kubernetes/pkg/expapi"
|
"k8s.io/kubernetes/pkg/expapi"
|
||||||
// Ensure that expapi/v1 package is initialized.
|
// Ensure that expapi/v1 package is initialized.
|
||||||
_ "k8s.io/kubernetes/pkg/expapi/v1"
|
_ "k8s.io/kubernetes/pkg/expapi/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||||
@ -85,30 +82,9 @@ func TestUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
autoscaler := validNewHorizontalPodAutoscaler("foo2")
|
test.TestDelete(validNewHorizontalPodAutoscaler("foo"))
|
||||||
key, _ := storage.KeyFunc(ctx, "foo2")
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), autoscaler),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return autoscaler
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
if fakeClient.Data[key].R.Node == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fakeClient.Data[key].R.Node.TTL == 30
|
|
||||||
}
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -122,3 +98,24 @@ func TestList(t *testing.T) {
|
|||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
test.TestList(validNewHorizontalPodAutoscaler("foo"))
|
test.TestList(validNewHorizontalPodAutoscaler("foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatch(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestWatch(
|
||||||
|
validNewHorizontalPodAutoscaler("foo"),
|
||||||
|
// matching labels
|
||||||
|
[]labels.Set{},
|
||||||
|
// not matching labels
|
||||||
|
[]labels.Set{
|
||||||
|
{"foo": "bar"},
|
||||||
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{},
|
||||||
|
// not matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "bar"},
|
||||||
|
{"name": "foo"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -74,13 +74,20 @@ func (autoscalerStrategy) AllowUnconditionalUpdate() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchAutoscaler returns a generic matcher for a given label and field selector.
|
func AutoscalerToSelectableFields(limitRange *expapi.HorizontalPodAutoscaler) fields.Set {
|
||||||
|
return fields.Set{}
|
||||||
|
}
|
||||||
|
|
||||||
func MatchAutoscaler(label labels.Selector, field fields.Selector) generic.Matcher {
|
func MatchAutoscaler(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||||
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
return &generic.SelectionPredicate{
|
||||||
autoscaler, ok := obj.(*expapi.HorizontalPodAutoscaler)
|
Label: label,
|
||||||
|
Field: field,
|
||||||
|
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||||
|
hpa, ok := obj.(*expapi.HorizontalPodAutoscaler)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("not a horizontal pod autoscaler")
|
return nil, nil, fmt.Errorf("given object is not a horizontal pod autoscaler.")
|
||||||
|
}
|
||||||
|
return labels.Set(hpa.ObjectMeta.Labels), AutoscalerToSelectableFields(hpa), nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return label.Matches(labels.Set(autoscaler.Labels)), nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
@ -97,6 +99,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestDelete(validNewLimitRange())
|
||||||
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
@ -108,3 +116,24 @@ func TestList(t *testing.T) {
|
|||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
test.TestList(validNewLimitRange())
|
test.TestList(validNewLimitRange())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatch(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestWatch(
|
||||||
|
validNewLimitRange(),
|
||||||
|
// matching labels
|
||||||
|
[]labels.Set{},
|
||||||
|
// not matching labels
|
||||||
|
[]labels.Set{
|
||||||
|
{"foo": "bar"},
|
||||||
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{},
|
||||||
|
// not matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "bar"},
|
||||||
|
{"name": "foo"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -22,16 +22,11 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeConnectionInfoGetter struct {
|
type fakeConnectionInfoGetter struct {
|
||||||
@ -67,12 +62,6 @@ func validNewNode() *api.Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validChangedNode() *api.Node {
|
|
||||||
node := validNewNode()
|
|
||||||
node.ResourceVersion = "1"
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope()
|
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope()
|
||||||
@ -104,31 +93,9 @@ func TestUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
ctx := api.NewContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := resttest.New(t, storage, fakeClient.SetError).ClusterScope()
|
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope()
|
||||||
|
test.TestDelete(validNewNode())
|
||||||
node := validChangedNode()
|
|
||||||
key, _ := storage.KeyFunc(ctx, node.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), node),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
if fakeClient.Data[key].R.Node == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fakeClient.Data[key].R.Node.TTL == 30
|
|
||||||
}
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -143,7 +110,7 @@ func TestList(t *testing.T) {
|
|||||||
test.TestList(validNewNode())
|
test.TestList(validNewNode())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchNodes(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope()
|
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope()
|
||||||
test.TestWatch(
|
test.TestWatch(
|
||||||
@ -167,23 +134,3 @@ func TestWatchNodes(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdDeleteNode(t *testing.T) {
|
|
||||||
ctx := api.NewContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
|
||||||
node := validNewNode()
|
|
||||||
key, _ := storage.KeyFunc(ctx, node.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), node), 0)
|
|
||||||
_, err := storage.Delete(ctx, node.Name, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fakeClient.DeletedKeys) != 1 {
|
|
||||||
t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
||||||
}
|
|
||||||
if fakeClient.DeletedKeys[0] != key {
|
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
@ -59,15 +61,6 @@ func TestCreate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectNamespace(t *testing.T, out runtime.Object) (*api.Namespace, bool) {
|
|
||||||
namespace, ok := out.(*api.Namespace)
|
|
||||||
if !ok || namespace == nil {
|
|
||||||
t.Errorf("Expected an api.Namespace object, was %#v", out)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return namespace, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateSetsFields(t *testing.T) {
|
func TestCreateSetsFields(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
namespace := validNewNamespace()
|
namespace := validNewNamespace()
|
||||||
@ -93,24 +86,10 @@ func TestCreateSetsFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNamespaceDecode(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
storage, _ := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
expected := validNewNamespace()
|
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope().ReturnDeletedObject()
|
||||||
expected.Status.Phase = api.NamespaceActive
|
test.TestDelete(validNewNamespace())
|
||||||
expected.Spec.Finalizers = []api.FinalizerName{api.FinalizerKubernetes}
|
|
||||||
body, err := testapi.Codec().Encode(expected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := storage.New()
|
|
||||||
if err := testapi.Codec().DecodeInto(body, actual); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expected, actual) {
|
|
||||||
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -125,31 +104,27 @@ func TestList(t *testing.T) {
|
|||||||
test.TestList(validNewNamespace())
|
test.TestList(validNewNamespace())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteNamespace(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
fakeClient.ChangeIndex = 1
|
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope()
|
||||||
ctx := api.NewContext()
|
test.TestWatch(
|
||||||
key, err := storage.Etcd.KeyFunc(ctx, "foo")
|
validNewNamespace(),
|
||||||
key = etcdtest.AddPrefix(key)
|
// matching labels
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
[]labels.Set{},
|
||||||
R: &etcd.Response{
|
// not matching labels
|
||||||
Node: &etcd.Node{
|
[]labels.Set{
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), &api.Namespace{
|
{"foo": "bar"},
|
||||||
ObjectMeta: api.ObjectMeta{
|
|
||||||
Name: "foo",
|
|
||||||
},
|
},
|
||||||
Status: api.NamespaceStatus{Phase: api.NamespaceActive},
|
// matching fields
|
||||||
}),
|
[]fields.Set{
|
||||||
ModifiedIndex: 1,
|
{"metadata.name": "foo"},
|
||||||
CreatedIndex: 1,
|
{"name": "foo"},
|
||||||
},
|
},
|
||||||
|
// not matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "bar"},
|
||||||
},
|
},
|
||||||
}
|
)
|
||||||
_, err = storage.Delete(api.NewContext(), "foo", nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteNamespaceWithIncompleteFinalizers(t *testing.T) {
|
func TestDeleteNamespaceWithIncompleteFinalizers(t *testing.T) {
|
||||||
|
@ -143,10 +143,11 @@ func MatchNamespace(label labels.Selector, field fields.Selector) generic.Matche
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NamespaceToSelectableFields returns a label set that represents the object
|
// NamespaceToSelectableFields returns a label set that represents the object
|
||||||
// TODO: fields are not labels, and the validation rules for them do not apply.
|
|
||||||
func NamespaceToSelectableFields(namespace *api.Namespace) labels.Set {
|
func NamespaceToSelectableFields(namespace *api.Namespace) labels.Set {
|
||||||
return labels.Set{
|
return labels.Set{
|
||||||
"name": namespace.Name,
|
"metadata.name": namespace.Name,
|
||||||
"status.phase": string(namespace.Status.Phase),
|
"status.phase": string(namespace.Status.Phase),
|
||||||
|
// This is a bug, but we need to support it for backward compatibility.
|
||||||
|
"name": namespace.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,14 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
|
||||||
@ -101,31 +100,9 @@ func TestUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
ctx := api.NewContext()
|
|
||||||
storage, _, fakeClient := newStorage(t)
|
storage, _, fakeClient := newStorage(t)
|
||||||
test := resttest.New(t, storage, fakeClient.SetError).ClusterScope()
|
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope().ReturnDeletedObject()
|
||||||
|
test.TestDelete(validNewPersistentVolume("foo"))
|
||||||
pv := validChangedPersistentVolume()
|
|
||||||
key, _ := storage.KeyFunc(ctx, pv.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), pv),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
if fakeClient.Data[key].R.Node == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fakeClient.Data[key].R.Node.TTL == 30
|
|
||||||
}
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -140,48 +117,30 @@ func TestList(t *testing.T) {
|
|||||||
test.TestList(validNewPersistentVolume("foo"))
|
test.TestList(validNewPersistentVolume("foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPersistentVolumesDecode(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
storage, _, _ := newStorage(t)
|
|
||||||
expected := validNewPersistentVolume("foo")
|
|
||||||
body, err := testapi.Codec().Encode(expected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := storage.New()
|
|
||||||
if err := testapi.Codec().DecodeInto(body, actual); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expected, actual) {
|
|
||||||
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeletePersistentVolumes(t *testing.T) {
|
|
||||||
ctx := api.NewContext()
|
|
||||||
storage, _, fakeClient := newStorage(t)
|
storage, _, fakeClient := newStorage(t)
|
||||||
persistentVolume := validNewPersistentVolume("foo")
|
test := registrytest.New(t, fakeClient, storage.Etcd).ClusterScope()
|
||||||
name := persistentVolume.Name
|
test.TestWatch(
|
||||||
key, _ := storage.KeyFunc(ctx, name)
|
validNewPersistentVolume("foo"),
|
||||||
key = etcdtest.AddPrefix(key)
|
// matching labels
|
||||||
fakeClient.ChangeIndex = 1
|
[]labels.Set{},
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
// not matching labels
|
||||||
R: &etcd.Response{
|
[]labels.Set{
|
||||||
Node: &etcd.Node{
|
{"foo": "bar"},
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), persistentVolume),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
CreatedIndex: 1,
|
|
||||||
},
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "foo"},
|
||||||
|
{"name": "foo"},
|
||||||
},
|
},
|
||||||
}
|
// not matching fields
|
||||||
_, err := storage.Delete(ctx, name, nil)
|
[]fields.Set{
|
||||||
if err != nil {
|
{"metadata.name": "bar"},
|
||||||
t.Fatalf("unexpected error: %v", err)
|
},
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdUpdateStatus(t *testing.T) {
|
func TestUpdateStatus(t *testing.T) {
|
||||||
storage, statusStorage, fakeClient := newStorage(t)
|
storage, statusStorage, fakeClient := newStorage(t)
|
||||||
fakeClient.TestIndex = true
|
fakeClient.TestIndex = true
|
||||||
|
|
||||||
|
@ -103,9 +103,10 @@ func MatchPersistentVolumes(label labels.Selector, field fields.Selector) generi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PersistentVolumeToSelectableFields returns a label set that represents the object
|
// PersistentVolumeToSelectableFields returns a label set that represents the object
|
||||||
// TODO: fields are not labels, and the validation rules for them do not apply.
|
|
||||||
func PersistentVolumeToSelectableFields(persistentvolume *api.PersistentVolume) labels.Set {
|
func PersistentVolumeToSelectableFields(persistentvolume *api.PersistentVolume) labels.Set {
|
||||||
return labels.Set{
|
return labels.Set{
|
||||||
|
"metadata.name": persistentvolume.Name,
|
||||||
|
// This is a bug, but we need to support it for backward compatibility.
|
||||||
"name": persistentvolume.Name,
|
"name": persistentvolume.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,14 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/resource"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
|
||||||
@ -59,12 +58,6 @@ func validNewPersistentVolumeClaim(name, ns string) *api.PersistentVolumeClaim {
|
|||||||
return pv
|
return pv
|
||||||
}
|
}
|
||||||
|
|
||||||
func validChangedPersistentVolumeClaim() *api.PersistentVolumeClaim {
|
|
||||||
pv := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
|
||||||
pv.ResourceVersion = "1"
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
storage, _, fakeClient := newStorage(t)
|
storage, _, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
@ -100,31 +93,9 @@ func TestUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, _, fakeClient := newStorage(t)
|
storage, _, fakeClient := newStorage(t)
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
test := registrytest.New(t, fakeClient, storage.Etcd).ReturnDeletedObject()
|
||||||
|
test.TestDelete(validNewPersistentVolumeClaim("foo", api.NamespaceDefault))
|
||||||
pv := validChangedPersistentVolumeClaim()
|
|
||||||
key, _ := storage.KeyFunc(ctx, pv.Name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), pv),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return pv
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
if fakeClient.Data[key].R.Node == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fakeClient.Data[key].R.Node.TTL == 30
|
|
||||||
}
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -139,78 +110,30 @@ func TestList(t *testing.T) {
|
|||||||
test.TestList(validNewPersistentVolumeClaim("foo", api.NamespaceDefault))
|
test.TestList(validNewPersistentVolumeClaim("foo", api.NamespaceDefault))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPersistentVolumeClaimsDecode(t *testing.T) {
|
func TestWatch(t *testing.T) {
|
||||||
storage, _, _ := newStorage(t)
|
|
||||||
expected := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
|
||||||
body, err := testapi.Codec().Encode(expected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := storage.New()
|
|
||||||
if err := testapi.Codec().DecodeInto(body, actual); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expected, actual) {
|
|
||||||
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEtcdUpdatePersistentVolumeClaims(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, _, fakeClient := newStorage(t)
|
storage, _, fakeClient := newStorage(t)
|
||||||
persistentVolume := validChangedPersistentVolumeClaim()
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestWatch(
|
||||||
key, _ := storage.KeyFunc(ctx, "foo")
|
validNewPersistentVolumeClaim("foo", api.NamespaceDefault),
|
||||||
key = etcdtest.AddPrefix(key)
|
// matching labels
|
||||||
fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), validNewPersistentVolumeClaim("foo", api.NamespaceDefault)), 0)
|
[]labels.Set{},
|
||||||
|
// not matching labels
|
||||||
_, _, err := storage.Update(ctx, persistentVolume)
|
[]labels.Set{
|
||||||
if err != nil {
|
{"foo": "bar"},
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := fakeClient.Get(key, false, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error %v", err)
|
|
||||||
}
|
|
||||||
var persistentVolumeOut api.PersistentVolumeClaim
|
|
||||||
err = testapi.Codec().DecodeInto([]byte(response.Node.Value), &persistentVolumeOut)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
persistentVolume.ObjectMeta.ResourceVersion = persistentVolumeOut.ObjectMeta.ResourceVersion
|
|
||||||
if !api.Semantic.DeepEqual(persistentVolume, &persistentVolumeOut) {
|
|
||||||
t.Errorf("Unexpected persistentVolume: %#v, expected %#v", &persistentVolumeOut, persistentVolume)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeletePersistentVolumeClaims(t *testing.T) {
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, _, fakeClient := newStorage(t)
|
|
||||||
pvClaim := validNewPersistentVolumeClaim("foo", api.NamespaceDefault)
|
|
||||||
name := pvClaim.Name
|
|
||||||
key, _ := storage.KeyFunc(ctx, name)
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
fakeClient.ChangeIndex = 1
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), pvClaim),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
CreatedIndex: 1,
|
|
||||||
},
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "foo"},
|
||||||
|
{"name": "foo"},
|
||||||
},
|
},
|
||||||
}
|
// not matching fields
|
||||||
_, err := storage.Delete(ctx, name, nil)
|
[]fields.Set{
|
||||||
if err != nil {
|
{"metadata.name": "bar"},
|
||||||
t.Fatalf("unexpected error: %v", err)
|
},
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdUpdateStatus(t *testing.T) {
|
func TestUpdateStatus(t *testing.T) {
|
||||||
storage, statusStorage, fakeClient := newStorage(t)
|
storage, statusStorage, fakeClient := newStorage(t)
|
||||||
ctx := api.NewDefaultContext()
|
ctx := api.NewDefaultContext()
|
||||||
fakeClient.TestIndex = true
|
fakeClient.TestIndex = true
|
||||||
|
@ -103,9 +103,10 @@ func MatchPersistentVolumeClaim(label labels.Selector, field fields.Selector) ge
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PersistentVolumeClaimToSelectableFields returns a label set that represents the object
|
// PersistentVolumeClaimToSelectableFields returns a label set that represents the object
|
||||||
// TODO: fields are not labels, and the validation rules for them do not apply.
|
|
||||||
func PersistentVolumeClaimToSelectableFields(persistentvolumeclaim *api.PersistentVolumeClaim) labels.Set {
|
func PersistentVolumeClaimToSelectableFields(persistentvolumeclaim *api.PersistentVolumeClaim) labels.Set {
|
||||||
return labels.Set{
|
return labels.Set{
|
||||||
|
"metadata.name": persistentvolumeclaim.Name,
|
||||||
|
// This is a bug, but we need to support it for backward compatibility.
|
||||||
"name": persistentvolumeclaim.Name,
|
"name": persistentvolumeclaim.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/errors"
|
"k8s.io/kubernetes/pkg/api/errors"
|
||||||
etcderrors "k8s.io/kubernetes/pkg/api/errors/etcd"
|
etcderrors "k8s.io/kubernetes/pkg/api/errors/etcd"
|
||||||
"k8s.io/kubernetes/pkg/api/rest"
|
"k8s.io/kubernetes/pkg/api/rest"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
"k8s.io/kubernetes/pkg/fields"
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
@ -122,50 +121,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
storage, _, _, fakeClient := newStorage(t)
|
storage, _, _, fakeClient := newStorage(t)
|
||||||
ctx := api.NewDefaultContext()
|
test := registrytest.New(t, fakeClient, storage.Etcd).ReturnDeletedObject()
|
||||||
key, _ := storage.Etcd.KeyFunc(ctx, "foo")
|
test.TestDelete(validNewPod())
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
|
||||||
|
|
||||||
expectedNode := "some-node"
|
scheduledPod := validNewPod()
|
||||||
createFn := func() runtime.Object {
|
scheduledPod.Spec.NodeName = "some-node"
|
||||||
pod := validChangedPod()
|
test.TestDeleteGraceful(scheduledPod, 30)
|
||||||
pod.Spec.NodeName = expectedNode
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), pod),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return pod
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
if fakeClient.Data[key].R.Node == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
obj, err := testapi.Codec().Decode([]byte(fakeClient.Data[key].R.Node.Value))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
pod := obj.(*api.Pod)
|
|
||||||
t.Logf("found object %#v", pod.ObjectMeta)
|
|
||||||
return pod.DeletionTimestamp != nil && pod.DeletionGracePeriodSeconds != nil && *pod.DeletionGracePeriodSeconds != 0
|
|
||||||
}
|
|
||||||
test.TestDeleteGraceful(createFn, 30, gracefulSetFn)
|
|
||||||
|
|
||||||
expectedNode = ""
|
|
||||||
test.TestDelete(createFn, gracefulSetFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectPod(t *testing.T, out runtime.Object) (*api.Pod, bool) {
|
|
||||||
pod, ok := out.(*api.Pod)
|
|
||||||
if !ok || pod == nil {
|
|
||||||
t.Errorf("Expected an api.Pod object, was %#v", out)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return pod, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateRegistryError(t *testing.T) {
|
func TestCreateRegistryError(t *testing.T) {
|
||||||
@ -200,24 +161,6 @@ func TestCreateSetsFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPodDecode(t *testing.T) {
|
|
||||||
storage, _, _, _ := newStorage(t)
|
|
||||||
expected := validNewPod()
|
|
||||||
body, err := testapi.Codec().Encode(expected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := storage.New()
|
|
||||||
if err := testapi.Codec().DecodeInto(body, actual); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expected, actual) {
|
|
||||||
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceLocation(t *testing.T) {
|
func TestResourceLocation(t *testing.T) {
|
||||||
expectedIP := "1.2.3.4"
|
expectedIP := "1.2.3.4"
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
@ -854,49 +797,3 @@ func TestEtcdUpdateStatus(t *testing.T) {
|
|||||||
t.Errorf("unexpected object: %s", util.ObjectDiff(&expected, podOut))
|
t.Errorf("unexpected object: %s", util.ObjectDiff(&expected, podOut))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdDeletePod(t *testing.T) {
|
|
||||||
storage, _, _, fakeClient := newStorage(t)
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
fakeClient.TestIndex = true
|
|
||||||
|
|
||||||
key, _ := storage.KeyFunc(ctx, "foo")
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &api.Pod{
|
|
||||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
|
||||||
Spec: api.PodSpec{NodeName: "machine"},
|
|
||||||
}), 0)
|
|
||||||
_, err := storage.Delete(ctx, "foo", api.NewDeleteOptions(0))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fakeClient.DeletedKeys) != 1 {
|
|
||||||
t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
||||||
} else if fakeClient.DeletedKeys[0] != key {
|
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEtcdDeletePodMultipleContainers(t *testing.T) {
|
|
||||||
storage, _, _, fakeClient := newStorage(t)
|
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
fakeClient.TestIndex = true
|
|
||||||
key, _ := storage.KeyFunc(ctx, "foo")
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
fakeClient.Set(key, runtime.EncodeOrDie(testapi.Codec(), &api.Pod{
|
|
||||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
|
||||||
Spec: api.PodSpec{NodeName: "machine"},
|
|
||||||
}), 0)
|
|
||||||
_, err := storage.Delete(ctx, "foo", api.NewDeleteOptions(0))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fakeClient.DeletedKeys) != 1 {
|
|
||||||
t.Errorf("Expected 1 delete, found %#v", fakeClient.DeletedKeys)
|
|
||||||
}
|
|
||||||
if fakeClient.DeletedKeys[0] != key {
|
|
||||||
t.Errorf("Unexpected key: %s, expected %s", fakeClient.DeletedKeys[0], key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
@ -87,6 +89,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd).ReturnDeletedObject()
|
||||||
|
test.TestDelete(validNewPodTemplate("foo"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
@ -98,3 +106,24 @@ func TestList(t *testing.T) {
|
|||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
test.TestList(validNewPodTemplate("foo"))
|
test.TestList(validNewPodTemplate("foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatch(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestWatch(
|
||||||
|
validNewPodTemplate("foo"),
|
||||||
|
// matching labels
|
||||||
|
[]labels.Set{},
|
||||||
|
// not matching labels
|
||||||
|
[]labels.Set{
|
||||||
|
{"foo": "bar"},
|
||||||
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{},
|
||||||
|
// not matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "bar"},
|
||||||
|
{"name": "foo"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -73,13 +73,20 @@ func (podTemplateStrategy) AllowUnconditionalUpdate() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchPodTemplate returns a generic matcher for a given label and field selector.
|
func PodTemplateToSelectableFields(podTemplate *api.PodTemplate) fields.Set {
|
||||||
|
return fields.Set{}
|
||||||
|
}
|
||||||
|
|
||||||
func MatchPodTemplate(label labels.Selector, field fields.Selector) generic.Matcher {
|
func MatchPodTemplate(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||||
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
return &generic.SelectionPredicate{
|
||||||
podObj, ok := obj.(*api.PodTemplate)
|
Label: label,
|
||||||
|
Field: field,
|
||||||
|
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||||
|
pt, ok := obj.(*api.PodTemplate)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Errorf("not a pod template")
|
return nil, nil, fmt.Errorf("given object is not a pod template.")
|
||||||
|
}
|
||||||
|
return labels.Set(pt.ObjectMeta.Labels), PodTemplateToSelectableFields(pt), nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return label.Matches(labels.Set(podObj.Labels)), nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,11 @@ func (t *Tester) GeneratesName() *Tester {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tester) ReturnDeletedObject() *Tester {
|
||||||
|
t.tester = t.tester.ReturnDeletedObject()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tester) TestCreate(valid runtime.Object, invalid ...runtime.Object) {
|
func (t *Tester) TestCreate(valid runtime.Object, invalid ...runtime.Object) {
|
||||||
t.tester.TestCreate(
|
t.tester.TestCreate(
|
||||||
valid,
|
valid,
|
||||||
@ -99,6 +104,24 @@ func (t *Tester) TestUpdate(valid runtime.Object, validUpdateFunc UpdateFunc, in
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tester) TestDelete(valid runtime.Object) {
|
||||||
|
t.tester.TestDelete(
|
||||||
|
valid,
|
||||||
|
t.setObject,
|
||||||
|
t.getObject,
|
||||||
|
isNotFoundEtcdError,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tester) TestDeleteGraceful(valid runtime.Object, expectedGrace int64) {
|
||||||
|
t.tester.TestDeleteGraceful(
|
||||||
|
valid,
|
||||||
|
t.setObject,
|
||||||
|
t.getObject,
|
||||||
|
expectedGrace,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Tester) TestGet(valid runtime.Object) {
|
func (t *Tester) TestGet(valid runtime.Object) {
|
||||||
t.tester.TestGet(valid)
|
t.tester.TestGet(valid)
|
||||||
}
|
}
|
||||||
@ -219,3 +242,11 @@ func (t *Tester) emitObject(obj runtime.Object, action string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isNotFoundEtcdError(err error) bool {
|
||||||
|
etcdError, ok := err.(*etcd.EtcdError)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return etcdError.ErrorCode == tools.EtcdErrorCodeNotFound
|
||||||
|
}
|
||||||
|
@ -30,8 +30,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
||||||
"k8s.io/kubernetes/pkg/util"
|
"k8s.io/kubernetes/pkg/util"
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
|
||||||
@ -74,15 +72,6 @@ func TestCreate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func expectResourceQuota(t *testing.T, out runtime.Object) (*api.ResourceQuota, bool) {
|
|
||||||
resourcequota, ok := out.(*api.ResourceQuota)
|
|
||||||
if !ok || resourcequota == nil {
|
|
||||||
t.Errorf("Expected an api.ResourceQuota object, was %#v", out)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return resourcequota, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateRegistryError(t *testing.T) {
|
func TestCreateRegistryError(t *testing.T) {
|
||||||
storage, _, fakeClient := newStorage(t)
|
storage, _, fakeClient := newStorage(t)
|
||||||
fakeClient.Err = fmt.Errorf("test error")
|
fakeClient.Err = fmt.Errorf("test error")
|
||||||
@ -116,49 +105,10 @@ func TestCreateSetsFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResourceQuotaDecode(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
storage, _, _ := newStorage(t)
|
|
||||||
expected := validNewResourceQuota()
|
|
||||||
body, err := testapi.Codec().Encode(expected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := storage.New()
|
|
||||||
if err := testapi.Codec().DecodeInto(body, actual); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !api.Semantic.DeepEqual(expected, actual) {
|
|
||||||
t.Errorf("mismatch: %s", util.ObjectDiff(expected, actual))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteResourceQuota(t *testing.T) {
|
|
||||||
storage, _, fakeClient := newStorage(t)
|
storage, _, fakeClient := newStorage(t)
|
||||||
fakeClient.ChangeIndex = 1
|
test := registrytest.New(t, fakeClient, storage.Etcd).ReturnDeletedObject()
|
||||||
ctx := api.NewDefaultContext()
|
test.TestDelete(validNewResourceQuota())
|
||||||
key, _ := storage.Etcd.KeyFunc(ctx, "foo")
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), &api.ResourceQuota{
|
|
||||||
ObjectMeta: api.ObjectMeta{
|
|
||||||
Name: "foo",
|
|
||||||
Namespace: api.NamespaceDefault,
|
|
||||||
},
|
|
||||||
Status: api.ResourceQuotaStatus{},
|
|
||||||
}),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
CreatedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err := storage.Delete(api.NewDefaultContext(), "foo", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -195,7 +145,7 @@ func TestWatch(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEtcdUpdateStatus(t *testing.T) {
|
func TestUpdateStatus(t *testing.T) {
|
||||||
storage, status, fakeClient := newStorage(t)
|
storage, status, fakeClient := newStorage(t)
|
||||||
ctx := api.NewDefaultContext()
|
ctx := api.NewDefaultContext()
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
@ -78,6 +80,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestDelete(validNewSecret("foo"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
@ -89,3 +97,24 @@ func TestList(t *testing.T) {
|
|||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
test.TestList(validNewSecret("foo"))
|
test.TestList(validNewSecret("foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatch(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestWatch(
|
||||||
|
validNewSecret("foo"),
|
||||||
|
// matching labels
|
||||||
|
[]labels.Set{},
|
||||||
|
// not matching labels
|
||||||
|
[]labels.Set{
|
||||||
|
{"foo": "bar"},
|
||||||
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{},
|
||||||
|
// not matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "bar"},
|
||||||
|
{"name": "foo"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -106,6 +106,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd).AllowCreateOnUpdate()
|
||||||
|
test.TestDelete(validService())
|
||||||
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd).AllowCreateOnUpdate()
|
test := registrytest.New(t, fakeClient, storage.Etcd).AllowCreateOnUpdate()
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
@ -71,6 +73,12 @@ func TestUpdate(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDelete(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd).ReturnDeletedObject()
|
||||||
|
test.TestDelete(validNewServiceAccount("foo"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
@ -82,3 +90,26 @@ func TestList(t *testing.T) {
|
|||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
test.TestList(validNewServiceAccount("foo"))
|
test.TestList(validNewServiceAccount("foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatch(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestWatch(
|
||||||
|
validNewServiceAccount("foo"),
|
||||||
|
// matching labels
|
||||||
|
[]labels.Set{},
|
||||||
|
// not matching labels
|
||||||
|
[]labels.Set{
|
||||||
|
{"foo": "bar"},
|
||||||
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "foo"},
|
||||||
|
},
|
||||||
|
// not matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "bar"},
|
||||||
|
{"name": "foo"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -20,17 +20,14 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/rest/resttest"
|
|
||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
|
||||||
"k8s.io/kubernetes/pkg/expapi"
|
"k8s.io/kubernetes/pkg/expapi"
|
||||||
// Ensure that expapi/v1 package is initialized.
|
// Ensure that expapi/v1 package is initialized.
|
||||||
_ "k8s.io/kubernetes/pkg/expapi/v1"
|
_ "k8s.io/kubernetes/pkg/expapi/v1"
|
||||||
|
"k8s.io/kubernetes/pkg/fields"
|
||||||
|
"k8s.io/kubernetes/pkg/labels"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/tools"
|
"k8s.io/kubernetes/pkg/tools"
|
||||||
"k8s.io/kubernetes/pkg/tools/etcdtest"
|
|
||||||
|
|
||||||
"github.com/coreos/go-etcd/etcd"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
|
||||||
@ -81,30 +78,9 @@ func TestUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
ctx := api.NewDefaultContext()
|
|
||||||
storage, fakeClient := newStorage(t)
|
storage, fakeClient := newStorage(t)
|
||||||
test := resttest.New(t, storage, fakeClient.SetError)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
rsrc := validNewThirdPartyResource("foo2")
|
test.TestDelete(validNewThirdPartyResource("foo"))
|
||||||
key, _ := storage.KeyFunc(ctx, "foo2")
|
|
||||||
key = etcdtest.AddPrefix(key)
|
|
||||||
createFn := func() runtime.Object {
|
|
||||||
fakeClient.Data[key] = tools.EtcdResponseWithError{
|
|
||||||
R: &etcd.Response{
|
|
||||||
Node: &etcd.Node{
|
|
||||||
Value: runtime.EncodeOrDie(testapi.Codec(), rsrc),
|
|
||||||
ModifiedIndex: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return rsrc
|
|
||||||
}
|
|
||||||
gracefulSetFn := func() bool {
|
|
||||||
if fakeClient.Data[key].R.Node == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fakeClient.Data[key].R.Node.TTL == 30
|
|
||||||
}
|
|
||||||
test.TestDeleteNoGraceful(createFn, gracefulSetFn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
@ -118,3 +94,24 @@ func TestList(t *testing.T) {
|
|||||||
test := registrytest.New(t, fakeClient, storage.Etcd)
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
test.TestList(validNewThirdPartyResource("foo"))
|
test.TestList(validNewThirdPartyResource("foo"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatch(t *testing.T) {
|
||||||
|
storage, fakeClient := newStorage(t)
|
||||||
|
test := registrytest.New(t, fakeClient, storage.Etcd)
|
||||||
|
test.TestWatch(
|
||||||
|
validNewThirdPartyResource("foo"),
|
||||||
|
// matching labels
|
||||||
|
[]labels.Set{},
|
||||||
|
// not matching labels
|
||||||
|
[]labels.Set{
|
||||||
|
{"foo": "bar"},
|
||||||
|
},
|
||||||
|
// matching fields
|
||||||
|
[]fields.Set{},
|
||||||
|
// not matching fields
|
||||||
|
[]fields.Set{
|
||||||
|
{"metadata.name": "bar"},
|
||||||
|
{"name": "foo"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user