k8s.io/client-go@v0.22.2/tools/events/eventseries_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package events 18 19 import ( 20 "strconv" 21 "testing" 22 "time" 23 24 "os" 25 "strings" 26 27 v1 "k8s.io/api/core/v1" 28 eventsv1 "k8s.io/api/events/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 k8sruntime "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/util/wait" 32 "k8s.io/client-go/kubernetes/scheme" 33 restclient "k8s.io/client-go/rest" 34 ref "k8s.io/client-go/tools/reference" 35 ) 36 37 type testEventSeriesSink struct { 38 OnCreate func(e *eventsv1.Event) (*eventsv1.Event, error) 39 OnUpdate func(e *eventsv1.Event) (*eventsv1.Event, error) 40 OnPatch func(e *eventsv1.Event, p []byte) (*eventsv1.Event, error) 41 } 42 43 // Create records the event for testing. 44 func (t *testEventSeriesSink) Create(e *eventsv1.Event) (*eventsv1.Event, error) { 45 if t.OnCreate != nil { 46 return t.OnCreate(e) 47 } 48 return e, nil 49 } 50 51 // Update records the event for testing. 52 func (t *testEventSeriesSink) Update(e *eventsv1.Event) (*eventsv1.Event, error) { 53 if t.OnUpdate != nil { 54 return t.OnUpdate(e) 55 } 56 return e, nil 57 } 58 59 // Patch records the event for testing. 60 func (t *testEventSeriesSink) Patch(e *eventsv1.Event, p []byte) (*eventsv1.Event, error) { 61 if t.OnPatch != nil { 62 return t.OnPatch(e, p) 63 } 64 return e, nil 65 } 66 67 func TestEventSeriesf(t *testing.T) { 68 hostname, _ := os.Hostname() 69 70 testPod := &v1.Pod{ 71 ObjectMeta: metav1.ObjectMeta{ 72 Name: "foo", 73 Namespace: "baz", 74 UID: "bar", 75 }, 76 } 77 78 regarding, err := ref.GetPartialReference(scheme.Scheme, testPod, ".spec.containers[1]") 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 related, err := ref.GetPartialReference(scheme.Scheme, testPod, ".spec.containers[0]") 84 if err != nil { 85 t.Fatal(err) 86 } 87 88 expectedEvent := &eventsv1.Event{ 89 ObjectMeta: metav1.ObjectMeta{ 90 Name: "foo", 91 Namespace: "baz", 92 }, 93 EventTime: metav1.MicroTime{time.Now()}, 94 ReportingController: "eventTest", 95 ReportingInstance: "eventTest-" + hostname, 96 Action: "started", 97 Reason: "test", 98 Regarding: *regarding, 99 Related: related, 100 Note: "some verbose message: 1", 101 Type: v1.EventTypeNormal, 102 } 103 104 isomorphicEvent := expectedEvent.DeepCopy() 105 106 nonIsomorphicEvent := expectedEvent.DeepCopy() 107 nonIsomorphicEvent.Action = "stopped" 108 109 expectedEvent.Series = &eventsv1.EventSeries{Count: 1} 110 table := []struct { 111 regarding k8sruntime.Object 112 related k8sruntime.Object 113 actual *eventsv1.Event 114 elements []interface{} 115 expect *eventsv1.Event 116 expectUpdate bool 117 }{ 118 { 119 regarding: regarding, 120 related: related, 121 actual: isomorphicEvent, 122 elements: []interface{}{1}, 123 expect: expectedEvent, 124 expectUpdate: true, 125 }, 126 { 127 regarding: regarding, 128 related: related, 129 actual: nonIsomorphicEvent, 130 elements: []interface{}{1}, 131 expect: nonIsomorphicEvent, 132 expectUpdate: false, 133 }, 134 } 135 136 stopCh := make(chan struct{}) 137 138 createEvent := make(chan *eventsv1.Event) 139 updateEvent := make(chan *eventsv1.Event) 140 patchEvent := make(chan *eventsv1.Event) 141 142 testEvents := testEventSeriesSink{ 143 OnCreate: func(event *eventsv1.Event) (*eventsv1.Event, error) { 144 createEvent <- event 145 return event, nil 146 }, 147 OnUpdate: func(event *eventsv1.Event) (*eventsv1.Event, error) { 148 updateEvent <- event 149 return event, nil 150 }, 151 OnPatch: func(event *eventsv1.Event, patch []byte) (*eventsv1.Event, error) { 152 // event we receive is already patched, usually the sink uses it only to retrieve the name and namespace, here 153 // we'll use it directly 154 patchEvent <- event 155 return event, nil 156 }, 157 } 158 eventBroadcaster := newBroadcaster(&testEvents, 0, map[eventKey]*eventsv1.Event{}) 159 recorder := eventBroadcaster.NewRecorder(scheme.Scheme, "eventTest") 160 broadcaster := eventBroadcaster.(*eventBroadcasterImpl) 161 // Don't call StartRecordingToSink, as we don't need neither refreshing event 162 // series nor finishing them in this tests and additional events updated would 163 // race with our expected ones. 164 broadcaster.startRecordingEvents(stopCh) 165 recorder.Eventf(regarding, related, isomorphicEvent.Type, isomorphicEvent.Reason, isomorphicEvent.Action, isomorphicEvent.Note, []interface{}{1}) 166 // read from the chan as this was needed only to populate the cache 167 <-createEvent 168 for index, item := range table { 169 actual := item.actual 170 recorder.Eventf(item.regarding, item.related, actual.Type, actual.Reason, actual.Action, actual.Note, item.elements) 171 // validate event 172 if item.expectUpdate { 173 actualEvent := <-patchEvent 174 t.Logf("%v - validating event affected by patch request", index) 175 validateEvent(strconv.Itoa(index), true, actualEvent, item.expect, t) 176 } else { 177 actualEvent := <-createEvent 178 t.Logf("%v - validating event affected by a create request", index) 179 validateEvent(strconv.Itoa(index), false, actualEvent, item.expect, t) 180 } 181 } 182 close(stopCh) 183 } 184 185 func validateEvent(messagePrefix string, expectedUpdate bool, actualEvent *eventsv1.Event, expectedEvent *eventsv1.Event, t *testing.T) { 186 recvEvent := *actualEvent 187 188 // Just check that the timestamp was set. 189 if recvEvent.EventTime.IsZero() { 190 t.Errorf("%v - timestamp wasn't set: %#v", messagePrefix, recvEvent) 191 } 192 193 if expectedUpdate { 194 if recvEvent.Series == nil { 195 t.Errorf("%v - Series was nil but expected: %#v", messagePrefix, recvEvent.Series) 196 197 } else { 198 if recvEvent.Series.Count != expectedEvent.Series.Count { 199 t.Errorf("%v - Series mismatch actual was: %#v but expected: %#v", messagePrefix, recvEvent.Series, expectedEvent.Series) 200 } 201 } 202 203 // Check that name has the right prefix. 204 if n, en := recvEvent.Name, expectedEvent.Name; !strings.HasPrefix(n, en) { 205 t.Errorf("%v - Name '%v' does not contain prefix '%v'", messagePrefix, n, en) 206 } 207 } else { 208 if recvEvent.Series != nil { 209 t.Errorf("%v - series was expected to be nil but was: %#v", messagePrefix, recvEvent.Series) 210 } 211 } 212 213 } 214 215 func TestFinishSeries(t *testing.T) { 216 hostname, _ := os.Hostname() 217 testPod := &v1.Pod{ 218 ObjectMeta: metav1.ObjectMeta{ 219 SelfLink: "/api/v1/namespaces/baz/pods/foo", 220 Name: "foo", 221 Namespace: "baz", 222 UID: "bar", 223 }, 224 } 225 regarding, err := ref.GetPartialReference(scheme.Scheme, testPod, ".spec.containers[1]") 226 if err != nil { 227 t.Fatal(err) 228 } 229 related, err := ref.GetPartialReference(scheme.Scheme, testPod, ".spec.containers[0]") 230 if err != nil { 231 t.Fatal(err) 232 } 233 LastObservedTime := metav1.MicroTime{Time: time.Now().Add(-9 * time.Minute)} 234 235 createEvent := make(chan *eventsv1.Event, 10) 236 updateEvent := make(chan *eventsv1.Event, 10) 237 patchEvent := make(chan *eventsv1.Event, 10) 238 testEvents := testEventSeriesSink{ 239 OnCreate: func(event *eventsv1.Event) (*eventsv1.Event, error) { 240 createEvent <- event 241 return event, nil 242 }, 243 OnUpdate: func(event *eventsv1.Event) (*eventsv1.Event, error) { 244 updateEvent <- event 245 return event, nil 246 }, 247 OnPatch: func(event *eventsv1.Event, patch []byte) (*eventsv1.Event, error) { 248 // event we receive is already patched, usually the sink uses it 249 // only to retrieve the name and namespace, here we'll use it directly 250 patchEvent <- event 251 return event, nil 252 }, 253 } 254 cache := map[eventKey]*eventsv1.Event{} 255 eventBroadcaster := newBroadcaster(&testEvents, 0, cache).(*eventBroadcasterImpl) 256 recorder := eventBroadcaster.NewRecorder(scheme.Scheme, "k8s.io/kube-foo").(*recorderImpl) 257 cachedEvent := recorder.makeEvent(regarding, related, metav1.MicroTime{time.Now()}, v1.EventTypeNormal, "test", "some verbose message: 1", "eventTest", "eventTest-"+hostname, "started") 258 nonFinishedEvent := cachedEvent.DeepCopy() 259 nonFinishedEvent.ReportingController = "nonFinished-controller" 260 cachedEvent.Series = &eventsv1.EventSeries{ 261 Count: 10, 262 LastObservedTime: LastObservedTime, 263 } 264 cache[getKey(cachedEvent)] = cachedEvent 265 cache[getKey(nonFinishedEvent)] = nonFinishedEvent 266 eventBroadcaster.finishSeries() 267 select { 268 case actualEvent := <-patchEvent: 269 t.Logf("validating event affected by patch request") 270 eventBroadcaster.mu.Lock() 271 defer eventBroadcaster.mu.Unlock() 272 if len(cache) != 1 { 273 t.Errorf("cache should be empty, but instead got a size of %v", len(cache)) 274 } 275 if !actualEvent.Series.LastObservedTime.Equal(&cachedEvent.Series.LastObservedTime) { 276 t.Errorf("series was expected be seen with LastObservedTime %v, but instead got %v ", cachedEvent.Series.LastObservedTime, actualEvent.Series.LastObservedTime) 277 } 278 // check that we emitted only one event 279 if len(patchEvent) != 0 || len(createEvent) != 0 || len(updateEvent) != 0 { 280 t.Errorf("exactly one event should be emitted, but got %v", len(patchEvent)) 281 } 282 case <-time.After(wait.ForeverTestTimeout): 283 t.Fatalf("timeout after %v", wait.ForeverTestTimeout) 284 } 285 } 286 287 func TestRefreshExistingEventSeries(t *testing.T) { 288 hostname, _ := os.Hostname() 289 testPod := &v1.Pod{ 290 ObjectMeta: metav1.ObjectMeta{ 291 SelfLink: "/api/v1/namespaces/baz/pods/foo", 292 Name: "foo", 293 Namespace: "baz", 294 UID: "bar", 295 }, 296 } 297 regarding, err := ref.GetPartialReference(scheme.Scheme, testPod, ".spec.containers[1]") 298 if err != nil { 299 t.Fatal(err) 300 } 301 related, err := ref.GetPartialReference(scheme.Scheme, testPod, ".spec.containers[0]") 302 if err != nil { 303 t.Fatal(err) 304 } 305 LastObservedTime := metav1.MicroTime{Time: time.Now().Add(-9 * time.Minute)} 306 createEvent := make(chan *eventsv1.Event, 10) 307 updateEvent := make(chan *eventsv1.Event, 10) 308 patchEvent := make(chan *eventsv1.Event, 10) 309 310 table := []struct { 311 patchFunc func(event *eventsv1.Event, patch []byte) (*eventsv1.Event, error) 312 }{ 313 { 314 patchFunc: func(event *eventsv1.Event, patch []byte) (*eventsv1.Event, error) { 315 // event we receive is already patched, usually the sink uses it 316 //only to retrieve the name and namespace, here we'll use it directly. 317 patchEvent <- event 318 return event, nil 319 }, 320 }, 321 { 322 patchFunc: func(event *eventsv1.Event, patch []byte) (*eventsv1.Event, error) { 323 // we simulate an apiserver error here 324 patchEvent <- nil 325 return nil, &restclient.RequestConstructionError{} 326 }, 327 }, 328 } 329 for _, item := range table { 330 testEvents := testEventSeriesSink{ 331 OnCreate: func(event *eventsv1.Event) (*eventsv1.Event, error) { 332 createEvent <- event 333 return event, nil 334 }, 335 OnUpdate: func(event *eventsv1.Event) (*eventsv1.Event, error) { 336 updateEvent <- event 337 return event, nil 338 }, 339 OnPatch: item.patchFunc, 340 } 341 cache := map[eventKey]*eventsv1.Event{} 342 eventBroadcaster := newBroadcaster(&testEvents, 0, cache).(*eventBroadcasterImpl) 343 recorder := eventBroadcaster.NewRecorder(scheme.Scheme, "k8s.io/kube-foo").(*recorderImpl) 344 cachedEvent := recorder.makeEvent(regarding, related, metav1.MicroTime{time.Now()}, v1.EventTypeNormal, "test", "some verbose message: 1", "eventTest", "eventTest-"+hostname, "started") 345 cachedEvent.Series = &eventsv1.EventSeries{ 346 Count: 10, 347 LastObservedTime: LastObservedTime, 348 } 349 cacheKey := getKey(cachedEvent) 350 cache[cacheKey] = cachedEvent 351 352 eventBroadcaster.refreshExistingEventSeries() 353 select { 354 case <-patchEvent: 355 t.Logf("validating event affected by patch request") 356 eventBroadcaster.mu.Lock() 357 defer eventBroadcaster.mu.Unlock() 358 if len(cache) != 1 { 359 t.Errorf("cache should be with same size, but instead got a size of %v", len(cache)) 360 } 361 // check that we emitted only one event 362 if len(patchEvent) != 0 || len(createEvent) != 0 || len(updateEvent) != 0 { 363 t.Errorf("exactly one event should be emitted, but got %v", len(patchEvent)) 364 } 365 cacheEvent, exists := cache[cacheKey] 366 367 if cacheEvent == nil || !exists { 368 t.Errorf("expected event to exist and not being nil, but instead event: %v and exists: %v", cacheEvent, exists) 369 } 370 case <-time.After(wait.ForeverTestTimeout): 371 t.Fatalf("timeout after %v", wait.ForeverTestTimeout) 372 } 373 } 374 }