From 4f1a8e5e38d084422a6c146e3f428e10e5e10355 Mon Sep 17 00:00:00 2001 From: Caleb Woodbine Date: Mon, 6 Jan 2020 02:04:05 +0000 Subject: [PATCH 1/6] Add: test to ensure that an event can be fetched, patched, deleted, and listed --- test/e2e/framework/events/events.go | 206 ++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/test/e2e/framework/events/events.go b/test/e2e/framework/events/events.go index 10612c0a937..5165dd3eee2 100644 --- a/test/e2e/framework/events/events.go +++ b/test/e2e/framework/events/events.go @@ -18,6 +18,7 @@ package events import ( "context" + "encoding/json" "fmt" "strings" "time" @@ -25,8 +26,213 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/kubernetes/test/e2e/framework" + + "github.com/onsi/ginkgo" + "k8s.io/apimachinery/pkg/types" ) +// Action is a function to be performed by the system. +type Action func() error + +var _ = ginkgo.Describe("[sig-api-machinery] Events", func() { + f := framework.NewDefaultFramework("events") + + ginkgo.It("should ensure that an event can be fetched, patched, deleted, and listed", func() { + eventTestName := "event-test" + + ginkgo.By("creating a test event") + // create a test event in test namespace + _, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Create(&v1.Event{ + ObjectMeta: metav1.ObjectMeta{ + Name: eventTestName, + Labels: map[string]string{ + "testevent-constant": "true", + }, + }, + Message: "This is a test event", + Reason: "Test", + Type: "Normal", + Count: 1, + InvolvedObject: v1.ObjectReference{ + Namespace: f.Namespace.Name, + }, + }) + framework.ExpectNoError(err, "failed to create test event") + + ginkgo.By("listing all events in all namespaces") + // get a list of Events in all namespaces to ensure endpoint coverage + eventsList, err := f.ClientSet.CoreV1().Events("").List(metav1.ListOptions{ + LabelSelector: "testevent-constant=true", + }) + framework.ExpectNoError(err, "failed list all events") + + foundCreatedEvent := false + var eventCreatedName string + for _, val := range eventsList.Items { + if val.ObjectMeta.Name == eventTestName && val.ObjectMeta.Namespace == f.Namespace.Name { + foundCreatedEvent = true + eventCreatedName = val.ObjectMeta.Name + break + } + } + framework.ExpectEqual(foundCreatedEvent, true, "unable to find the test event") + + ginkgo.By("patching the test event") + // patch the event's message + eventPatchMessage := "This is a test event - patched" + eventPatch, err := json.Marshal(map[string]interface{}{ + "message": eventPatchMessage, + }) + framework.ExpectNoError(err, "failed to marshal the patch JSON payload") + + _, err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Patch(eventTestName, types.StrategicMergePatchType, []byte(eventPatch)) + framework.ExpectNoError(err, "failed to patch the test event") + + ginkgo.By("fetching the test event") + // get event by name + event, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Get(eventCreatedName, metav1.GetOptions{}) + framework.ExpectNoError(err, "failed to fetch the test event") + framework.ExpectEqual(event.Message, eventPatchMessage, "test event message does not match patch message") + + ginkgo.By("deleting the test event") + // delete original event + err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Delete(eventCreatedName, &metav1.DeleteOptions{}) + framework.ExpectNoError(err, "failed to delete the test event") + + ginkgo.By("listing all events in all namespaces") + // get a list of Events list namespace + eventsList, err = f.ClientSet.CoreV1().Events("").List(metav1.ListOptions{ + LabelSelector: "testevent-constant=true", + }) + framework.ExpectNoError(err, "fail to list all events") + foundCreatedEvent = false + for _, val := range eventsList.Items { + if val.ObjectMeta.Name == eventTestName && val.ObjectMeta.Namespace == f.Namespace.Name { + foundCreatedEvent = true + break + } + } + framework.ExpectEqual(foundCreatedEvent, false, "failed to find test event") + }) +}) + +// ObserveNodeUpdateAfterAction returns true if a node update matching the predicate was emitted +// from the system after performing the supplied action. +func ObserveNodeUpdateAfterAction(c clientset.Interface, nodeName string, nodePredicate func(*v1.Node) bool, action Action) (bool, error) { + observedMatchingNode := false + nodeSelector := fields.OneTermEqualSelector("metadata.name", nodeName) + informerStartedChan := make(chan struct{}) + var informerStartedGuard sync.Once + + _, controller := cache.NewInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + options.FieldSelector = nodeSelector.String() + ls, err := c.CoreV1().Nodes().List(options) + return ls, err + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + // Signal parent goroutine that watching has begun. + defer informerStartedGuard.Do(func() { close(informerStartedChan) }) + options.FieldSelector = nodeSelector.String() + w, err := c.CoreV1().Nodes().Watch(options) + return w, err + }, + }, + &v1.Node{}, + 0, + cache.ResourceEventHandlerFuncs{ + UpdateFunc: func(oldObj, newObj interface{}) { + n, ok := newObj.(*v1.Node) + framework.ExpectEqual(ok, true) + if nodePredicate(n) { + observedMatchingNode = true + } + }, + }, + ) + + // Start the informer and block this goroutine waiting for the started signal. + informerStopChan := make(chan struct{}) + defer func() { close(informerStopChan) }() + go controller.Run(informerStopChan) + <-informerStartedChan + + // Invoke the action function. + err := action() + if err != nil { + return false, err + } + + // Poll whether the informer has found a matching node update with a timeout. + // Wait up 2 minutes polling every second. + timeout := 2 * time.Minute + interval := 1 * time.Second + err = wait.Poll(interval, timeout, func() (bool, error) { + return observedMatchingNode, nil + }) + return err == nil, err +} + +// ObserveEventAfterAction returns true if an event matching the predicate was emitted +// from the system after performing the supplied action. +func ObserveEventAfterAction(c clientset.Interface, ns string, eventPredicate func(*v1.Event) bool, action Action) (bool, error) { + observedMatchingEvent := false + informerStartedChan := make(chan struct{}) + var informerStartedGuard sync.Once + + // Create an informer to list/watch events from the test framework namespace. + _, controller := cache.NewInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + ls, err := c.CoreV1().Events(ns).List(options) + return ls, err + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + // Signal parent goroutine that watching has begun. + defer informerStartedGuard.Do(func() { close(informerStartedChan) }) + w, err := c.CoreV1().Events(ns).Watch(options) + return w, err + }, + }, + &v1.Event{}, + 0, + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + e, ok := obj.(*v1.Event) + ginkgo.By(fmt.Sprintf("Considering event: \nType = [%s], Name = [%s], Reason = [%s], Message = [%s]", e.Type, e.Name, e.Reason, e.Message)) + framework.ExpectEqual(ok, true) + if eventPredicate(e) { + observedMatchingEvent = true + } + }, + }, + ) + + // Start the informer and block this goroutine waiting for the started signal. + informerStopChan := make(chan struct{}) + defer func() { close(informerStopChan) }() + go controller.Run(informerStopChan) + <-informerStartedChan + + // Invoke the action function. + err := action() + if err != nil { + return false, err + } + + // Poll whether the informer has found a matching event with a timeout. + // Wait up 2 minutes polling every second. + timeout := 2 * time.Minute + interval := 1 * time.Second + err = wait.Poll(interval, timeout, func() (bool, error) { + return observedMatchingEvent, nil + }) + return err == nil, err +} + // WaitTimeoutForEvent waits the given timeout duration for an event to occur. func WaitTimeoutForEvent(c clientset.Interface, namespace, eventSelector, msg string, timeout time.Duration) error { interval := 2 * time.Second From 9a8e1a1462bd99e0163934030f96ea0d018a52c1 Mon Sep 17 00:00:00 2001 From: Caleb Woodbine Date: Tue, 28 Jan 2020 19:14:17 +0000 Subject: [PATCH 2/6] Fix: types.go in BUILD file --- test/e2e/framework/events/BUILD | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/e2e/framework/events/BUILD b/test/e2e/framework/events/BUILD index ebac16a84d2..ffbe4ee3c44 100644 --- a/test/e2e/framework/events/BUILD +++ b/test/e2e/framework/events/BUILD @@ -7,6 +7,9 @@ go_library( visibility = ["//visibility:public"], deps = [ "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", ], From 54f9654799ffd3a25bc4f068c7b18eb1dde5dd49 Mon Sep 17 00:00:00 2001 From: Caleb Woodbine Date: Wed, 26 Feb 2020 16:10:25 +1300 Subject: [PATCH 3/6] Fix build failure; Remove unrelated code --- test/e2e/framework/events/BUILD | 5 +- test/e2e/framework/events/events.go | 131 ++-------------------------- 2 files changed, 11 insertions(+), 125 deletions(-) diff --git a/test/e2e/framework/events/BUILD b/test/e2e/framework/events/BUILD index ffbe4ee3c44..671c711dcac 100644 --- a/test/e2e/framework/events/BUILD +++ b/test/e2e/framework/events/BUILD @@ -7,11 +7,12 @@ go_library( visibility = ["//visibility:public"], deps = [ "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", + "//staging/src/k8s.io/client-go/tools/cache:go_default_library", + "//test/e2e/framework:go_default_library", + "//vendor/github.com/onsi/ginkgo:go_default_library", ], ) diff --git a/test/e2e/framework/events/events.go b/test/e2e/framework/events/events.go index 5165dd3eee2..4b060d3fffe 100644 --- a/test/e2e/framework/events/events.go +++ b/test/e2e/framework/events/events.go @@ -23,10 +23,10 @@ import ( "strings" "time" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" "k8s.io/kubernetes/test/e2e/framework" "github.com/onsi/ginkgo" @@ -44,7 +44,7 @@ var _ = ginkgo.Describe("[sig-api-machinery] Events", func() { ginkgo.By("creating a test event") // create a test event in test namespace - _, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Create(&v1.Event{ + _, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Create(context.TODO(), &v1.Event{ ObjectMeta: metav1.ObjectMeta{ Name: eventTestName, Labels: map[string]string{ @@ -58,12 +58,12 @@ var _ = ginkgo.Describe("[sig-api-machinery] Events", func() { InvolvedObject: v1.ObjectReference{ Namespace: f.Namespace.Name, }, - }) + }, metav1.CreateOptions{}) framework.ExpectNoError(err, "failed to create test event") ginkgo.By("listing all events in all namespaces") // get a list of Events in all namespaces to ensure endpoint coverage - eventsList, err := f.ClientSet.CoreV1().Events("").List(metav1.ListOptions{ + eventsList, err := f.ClientSet.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{ LabelSelector: "testevent-constant=true", }) framework.ExpectNoError(err, "failed list all events") @@ -87,23 +87,23 @@ var _ = ginkgo.Describe("[sig-api-machinery] Events", func() { }) framework.ExpectNoError(err, "failed to marshal the patch JSON payload") - _, err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Patch(eventTestName, types.StrategicMergePatchType, []byte(eventPatch)) + _, err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Patch(context.TODO(), eventTestName, types.StrategicMergePatchType, []byte(eventPatch), metav1.PatchOptions{}) framework.ExpectNoError(err, "failed to patch the test event") ginkgo.By("fetching the test event") // get event by name - event, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Get(eventCreatedName, metav1.GetOptions{}) + event, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Get(context.TODO(), eventCreatedName, metav1.GetOptions{}) framework.ExpectNoError(err, "failed to fetch the test event") framework.ExpectEqual(event.Message, eventPatchMessage, "test event message does not match patch message") ginkgo.By("deleting the test event") // delete original event - err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Delete(eventCreatedName, &metav1.DeleteOptions{}) + err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Delete(context.TODO(), eventCreatedName, &metav1.DeleteOptions{}) framework.ExpectNoError(err, "failed to delete the test event") ginkgo.By("listing all events in all namespaces") // get a list of Events list namespace - eventsList, err = f.ClientSet.CoreV1().Events("").List(metav1.ListOptions{ + eventsList, err = f.ClientSet.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{ LabelSelector: "testevent-constant=true", }) framework.ExpectNoError(err, "fail to list all events") @@ -118,121 +118,6 @@ var _ = ginkgo.Describe("[sig-api-machinery] Events", func() { }) }) -// ObserveNodeUpdateAfterAction returns true if a node update matching the predicate was emitted -// from the system after performing the supplied action. -func ObserveNodeUpdateAfterAction(c clientset.Interface, nodeName string, nodePredicate func(*v1.Node) bool, action Action) (bool, error) { - observedMatchingNode := false - nodeSelector := fields.OneTermEqualSelector("metadata.name", nodeName) - informerStartedChan := make(chan struct{}) - var informerStartedGuard sync.Once - - _, controller := cache.NewInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - options.FieldSelector = nodeSelector.String() - ls, err := c.CoreV1().Nodes().List(options) - return ls, err - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - // Signal parent goroutine that watching has begun. - defer informerStartedGuard.Do(func() { close(informerStartedChan) }) - options.FieldSelector = nodeSelector.String() - w, err := c.CoreV1().Nodes().Watch(options) - return w, err - }, - }, - &v1.Node{}, - 0, - cache.ResourceEventHandlerFuncs{ - UpdateFunc: func(oldObj, newObj interface{}) { - n, ok := newObj.(*v1.Node) - framework.ExpectEqual(ok, true) - if nodePredicate(n) { - observedMatchingNode = true - } - }, - }, - ) - - // Start the informer and block this goroutine waiting for the started signal. - informerStopChan := make(chan struct{}) - defer func() { close(informerStopChan) }() - go controller.Run(informerStopChan) - <-informerStartedChan - - // Invoke the action function. - err := action() - if err != nil { - return false, err - } - - // Poll whether the informer has found a matching node update with a timeout. - // Wait up 2 minutes polling every second. - timeout := 2 * time.Minute - interval := 1 * time.Second - err = wait.Poll(interval, timeout, func() (bool, error) { - return observedMatchingNode, nil - }) - return err == nil, err -} - -// ObserveEventAfterAction returns true if an event matching the predicate was emitted -// from the system after performing the supplied action. -func ObserveEventAfterAction(c clientset.Interface, ns string, eventPredicate func(*v1.Event) bool, action Action) (bool, error) { - observedMatchingEvent := false - informerStartedChan := make(chan struct{}) - var informerStartedGuard sync.Once - - // Create an informer to list/watch events from the test framework namespace. - _, controller := cache.NewInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - ls, err := c.CoreV1().Events(ns).List(options) - return ls, err - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - // Signal parent goroutine that watching has begun. - defer informerStartedGuard.Do(func() { close(informerStartedChan) }) - w, err := c.CoreV1().Events(ns).Watch(options) - return w, err - }, - }, - &v1.Event{}, - 0, - cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - e, ok := obj.(*v1.Event) - ginkgo.By(fmt.Sprintf("Considering event: \nType = [%s], Name = [%s], Reason = [%s], Message = [%s]", e.Type, e.Name, e.Reason, e.Message)) - framework.ExpectEqual(ok, true) - if eventPredicate(e) { - observedMatchingEvent = true - } - }, - }, - ) - - // Start the informer and block this goroutine waiting for the started signal. - informerStopChan := make(chan struct{}) - defer func() { close(informerStopChan) }() - go controller.Run(informerStopChan) - <-informerStartedChan - - // Invoke the action function. - err := action() - if err != nil { - return false, err - } - - // Poll whether the informer has found a matching event with a timeout. - // Wait up 2 minutes polling every second. - timeout := 2 * time.Minute - interval := 1 * time.Second - err = wait.Poll(interval, timeout, func() (bool, error) { - return observedMatchingEvent, nil - }) - return err == nil, err -} - // WaitTimeoutForEvent waits the given timeout duration for an event to occur. func WaitTimeoutForEvent(c clientset.Interface, namespace, eventSelector, msg string, timeout time.Duration) error { interval := 2 * time.Second From be7332e91761c40b0e2b2c3c0e781ecc3243c63e Mon Sep 17 00:00:00 2001 From: Caleb Woodbine Date: Wed, 26 Feb 2020 16:54:20 +1300 Subject: [PATCH 4/6] Fix BUILD --- test/e2e/framework/events/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/framework/events/BUILD b/test/e2e/framework/events/BUILD index 671c711dcac..e7f503cfe61 100644 --- a/test/e2e/framework/events/BUILD +++ b/test/e2e/framework/events/BUILD @@ -6,11 +6,11 @@ go_library( importpath = "k8s.io/kubernetes/test/e2e/framework/events", visibility = ["//visibility:public"], deps = [ + "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/tools/cache:go_default_library", "//test/e2e/framework:go_default_library", "//vendor/github.com/onsi/ginkgo:go_default_library", ], From 916d96e42838b93d6cd4bd264908d0c2855bd7ff Mon Sep 17 00:00:00 2001 From: Caleb Woodbine Date: Wed, 1 Apr 2020 09:49:23 +1300 Subject: [PATCH 5/6] Update test/e2e/framework/events/events.go Co-Authored-By: Aaron Crickenberger --- test/e2e/framework/events/events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/framework/events/events.go b/test/e2e/framework/events/events.go index 4b060d3fffe..08476cd74ba 100644 --- a/test/e2e/framework/events/events.go +++ b/test/e2e/framework/events/events.go @@ -114,7 +114,7 @@ var _ = ginkgo.Describe("[sig-api-machinery] Events", func() { break } } - framework.ExpectEqual(foundCreatedEvent, false, "failed to find test event") + framework.ExpectEqual(foundCreatedEvent, false, "should not have found test event after deletion") }) }) From 2b325f07f497e9a5ea4cf3d14b4db2eba6dbfa24 Mon Sep 17 00:00:00 2001 From: Caleb Woodbine Date: Wed, 1 Apr 2020 11:00:20 +1300 Subject: [PATCH 6/6] Fix DeleteOptions value --- test/e2e/framework/events/events.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/framework/events/events.go b/test/e2e/framework/events/events.go index 08476cd74ba..4890d87f77d 100644 --- a/test/e2e/framework/events/events.go +++ b/test/e2e/framework/events/events.go @@ -98,7 +98,7 @@ var _ = ginkgo.Describe("[sig-api-machinery] Events", func() { ginkgo.By("deleting the test event") // delete original event - err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Delete(context.TODO(), eventCreatedName, &metav1.DeleteOptions{}) + err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Delete(context.TODO(), eventCreatedName, metav1.DeleteOptions{}) framework.ExpectNoError(err, "failed to delete the test event") ginkgo.By("listing all events in all namespaces")