github.com/arunkumar7540/cli@v6.45.0+incompatible/actor/v2action/logging_test.go (about) 1 package v2action_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 . "code.cloudfoundry.org/cli/actor/v2action" 9 "code.cloudfoundry.org/cli/actor/v2action/v2actionfakes" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2" 11 noaaErrors "github.com/cloudfoundry/noaa/errors" 12 "github.com/cloudfoundry/sonde-go/events" 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 ) 16 17 var _ = Describe("Logging Actions", func() { 18 var ( 19 actor *Actor 20 fakeCloudControllerClient *v2actionfakes.FakeCloudControllerClient 21 fakeConfig *v2actionfakes.FakeConfig 22 fakeNOAAClient *v2actionfakes.FakeNOAAClient 23 ) 24 25 BeforeEach(func() { 26 actor, fakeCloudControllerClient, _, fakeConfig = NewTestActor() 27 fakeNOAAClient = new(v2actionfakes.FakeNOAAClient) 28 fakeConfig.AccessTokenReturns("AccessTokenForTest") 29 }) 30 31 Describe("LogMessage", func() { 32 Describe("Staging", func() { 33 When("the log is a staging log", func() { 34 It("returns true", func() { 35 message := NewLogMessage("", 0, time.Now(), "STG", "") 36 Expect(message.Staging()).To(BeTrue()) 37 }) 38 }) 39 40 When("the log is any other kind of log", func() { 41 It("returns true", func() { 42 message := NewLogMessage("", 0, time.Now(), "APP", "") 43 Expect(message.Staging()).To(BeFalse()) 44 }) 45 }) 46 }) 47 }) 48 49 Describe("GetStreamingLogs", func() { 50 var ( 51 expectedAppGUID string 52 53 messages <-chan *LogMessage 54 errs <-chan error 55 56 message *LogMessage 57 ) 58 59 BeforeEach(func() { 60 expectedAppGUID = "some-app-guid" 61 }) 62 63 AfterEach(func() { 64 Eventually(messages).Should(BeClosed()) 65 Eventually(errs).Should(BeClosed()) 66 }) 67 68 JustBeforeEach(func() { 69 messages, errs = actor.GetStreamingLogs(expectedAppGUID, fakeNOAAClient) 70 }) 71 72 When("receiving events", func() { 73 BeforeEach(func() { 74 fakeConfig.DialTimeoutReturns(60 * time.Minute) 75 76 fakeNOAAClient.TailingLogsStub = func(appGUID string, authToken string) (<-chan *events.LogMessage, <-chan error) { 77 Expect(appGUID).To(Equal(expectedAppGUID)) 78 Expect(authToken).To(Equal("AccessTokenForTest")) 79 80 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 81 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 82 83 eventStream := make(chan *events.LogMessage) 84 errStream := make(chan error, 1) 85 86 go func() { 87 defer close(eventStream) 88 defer close(errStream) 89 onConnectOrOnRetry() 90 91 outMessage := events.LogMessage_OUT 92 ts1 := int64(10) 93 sourceType := "some-source-type" 94 sourceInstance := "some-source-instance" 95 96 eventStream <- &events.LogMessage{ 97 Message: []byte("message-1"), 98 MessageType: &outMessage, 99 Timestamp: &ts1, 100 SourceType: &sourceType, 101 SourceInstance: &sourceInstance, 102 } 103 104 errMessage := events.LogMessage_ERR 105 ts2 := int64(20) 106 107 eventStream <- &events.LogMessage{ 108 Message: []byte("message-2"), 109 MessageType: &errMessage, 110 Timestamp: &ts2, 111 SourceType: &sourceType, 112 SourceInstance: &sourceInstance, 113 } 114 115 ts3 := int64(0) 116 eventStream <- &events.LogMessage{ 117 Message: []byte("message-3"), 118 MessageType: &outMessage, 119 Timestamp: &ts3, 120 SourceType: &sourceType, 121 SourceInstance: &sourceInstance, 122 } 123 124 ts4 := int64(15) 125 eventStream <- &events.LogMessage{ 126 Message: []byte("message-4"), 127 MessageType: &errMessage, 128 Timestamp: &ts4, 129 SourceType: &sourceType, 130 SourceInstance: &sourceInstance, 131 } 132 }() 133 134 return eventStream, errStream 135 } 136 }) 137 138 It("converts them to log messages, sorts them, and passes them through the messages channel", func() { 139 Eventually(messages).Should(Receive(&message)) 140 Expect(message.Message()).To(Equal("message-3")) 141 Expect(message.Type()).To(Equal("OUT")) 142 Expect(message.Timestamp()).To(Equal(time.Unix(0, 0))) 143 Expect(message.SourceType()).To(Equal("some-source-type")) 144 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 145 146 Eventually(messages).Should(Receive(&message)) 147 Expect(message.Message()).To(Equal("message-1")) 148 Expect(message.Type()).To(Equal("OUT")) 149 Expect(message.Timestamp()).To(Equal(time.Unix(0, 10))) 150 151 Eventually(messages).Should(Receive(&message)) 152 Expect(message.Message()).To(Equal("message-4")) 153 Expect(message.Type()).To(Equal("ERR")) 154 Expect(message.Timestamp()).To(Equal(time.Unix(0, 15))) 155 156 Eventually(messages).Should(Receive(&message)) 157 Expect(message.Message()).To(Equal("message-2")) 158 Expect(message.Type()).To(Equal("ERR")) 159 Expect(message.Timestamp()).To(Equal(time.Unix(0, 20))) 160 }) 161 }) 162 163 When("receiving errors", func() { 164 var ( 165 err1 error 166 err2 error 167 168 waiting chan bool 169 ) 170 171 Describe("nil error", func() { 172 BeforeEach(func() { 173 fakeConfig.DialTimeoutReturns(time.Minute) 174 175 waiting = make(chan bool) 176 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 177 eventStream := make(chan *events.LogMessage) 178 errStream := make(chan error, 1) 179 180 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 181 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 182 183 go func() { 184 defer close(eventStream) 185 defer close(errStream) 186 onConnectOrOnRetry() 187 188 errStream <- nil 189 close(waiting) 190 }() 191 192 return eventStream, errStream 193 } 194 }) 195 196 It("does not pass the nil along", func() { 197 Eventually(waiting).Should(BeClosed()) 198 Consistently(errs).ShouldNot(Receive()) 199 }) 200 }) 201 202 Describe("unexpected error", func() { 203 BeforeEach(func() { 204 fakeConfig.DialTimeoutReturns(time.Microsecond) // tests don't care about this timeout, ignore it 205 206 err1 = errors.New("ZOMG") 207 err2 = errors.New("Fiddlesticks") 208 209 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 210 eventStream := make(chan *events.LogMessage) 211 errStream := make(chan error, 1) 212 213 go func() { 214 defer close(eventStream) 215 defer close(errStream) 216 217 errStream <- err1 218 errStream <- err2 219 }() 220 221 return eventStream, errStream 222 } 223 }) 224 225 It("passes them through the errors channel", func() { 226 Eventually(errs).Should(Receive(Equal(err1))) 227 Eventually(errs).Should(Receive(Equal(err2))) 228 }) 229 }) 230 231 Describe("NOAA's RetryError", func() { 232 When("NOAA is able to recover", func() { 233 BeforeEach(func() { 234 fakeConfig.DialTimeoutReturns(60 * time.Minute) 235 236 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 237 eventStream := make(chan *events.LogMessage) 238 errStream := make(chan error, 1) 239 240 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 241 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 242 243 go func() { 244 defer close(eventStream) 245 defer close(errStream) 246 247 // can be called multiple times. Should be resilient to that 248 onConnectOrOnRetry() 249 errStream <- noaaErrors.NewRetryError(errors.New("error 1")) 250 onConnectOrOnRetry() 251 252 outMessage := events.LogMessage_OUT 253 ts1 := int64(10) 254 sourceType := "some-source-type" 255 sourceInstance := "some-source-instance" 256 257 eventStream <- &events.LogMessage{ 258 Message: []byte("message-1"), 259 MessageType: &outMessage, 260 Timestamp: &ts1, 261 SourceType: &sourceType, 262 SourceInstance: &sourceInstance, 263 } 264 }() 265 266 return eventStream, errStream 267 } 268 }) 269 270 It("continues without issue", func() { 271 Eventually(messages).Should(Receive()) 272 Consistently(errs).ShouldNot(Receive()) 273 }) 274 }) 275 276 When("NOAA has trouble connecting", func() { 277 BeforeEach(func() { 278 fakeConfig.DialTimeoutReturns(time.Microsecond) 279 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 280 eventStream := make(chan *events.LogMessage) 281 errStream := make(chan error, 1) 282 283 go func() { 284 defer close(eventStream) 285 defer close(errStream) 286 287 // explicitly skip the on call to simulate ready never being triggered 288 289 errStream <- noaaErrors.NewRetryError(errors.New("error 1")) 290 291 outMessage := events.LogMessage_OUT 292 ts1 := int64(10) 293 sourceType := "some-source-type" 294 sourceInstance := "some-source-instance" 295 296 eventStream <- &events.LogMessage{ 297 Message: []byte("message-1"), 298 MessageType: &outMessage, 299 Timestamp: &ts1, 300 SourceType: &sourceType, 301 SourceInstance: &sourceInstance, 302 } 303 }() 304 305 return eventStream, errStream 306 } 307 }) 308 309 It("returns a NOAATimeoutError and continues", func() { 310 Eventually(errs).Should(Receive(MatchError(actionerror.NOAATimeoutError{}))) 311 Eventually(messages).Should(Receive()) 312 313 Expect(fakeConfig.DialTimeoutCallCount()).To(Equal(1)) 314 }) 315 }) 316 }) 317 }) 318 }) 319 320 Describe("GetRecentLogsForApplicationByNameAndSpace", func() { 321 When("the application can be found", func() { 322 BeforeEach(func() { 323 fakeCloudControllerClient.GetApplicationsReturns( 324 []ccv2.Application{ 325 { 326 Name: "some-app", 327 GUID: "some-app-guid", 328 }, 329 }, 330 ccv2.Warnings{"some-app-warnings"}, 331 nil, 332 ) 333 }) 334 335 When("NOAA returns logs", func() { 336 BeforeEach(func() { 337 outMessage := events.LogMessage_OUT 338 ts1 := int64(10) 339 ts2 := int64(20) 340 sourceType := "some-source-type" 341 sourceInstance := "some-source-instance" 342 343 var messages []*events.LogMessage 344 messages = append(messages, &events.LogMessage{ 345 Message: []byte("message-2"), 346 MessageType: &outMessage, 347 Timestamp: &ts2, 348 SourceType: &sourceType, 349 SourceInstance: &sourceInstance, 350 }) 351 messages = append(messages, &events.LogMessage{ 352 Message: []byte("message-1"), 353 MessageType: &outMessage, 354 Timestamp: &ts1, 355 SourceType: &sourceType, 356 SourceInstance: &sourceInstance, 357 }) 358 359 fakeNOAAClient.RecentLogsReturns(messages, nil) 360 }) 361 362 It("passes a nonempty access token to the NOAA client", func() { 363 actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 364 _, accessToken := fakeNOAAClient.RecentLogsArgsForCall(0) 365 Expect(accessToken).To(Equal("AccessTokenForTest")) 366 }) 367 368 It("returns all the recent logs and warnings", func() { 369 messages, warnings, err := actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 370 Expect(err).ToNot(HaveOccurred()) 371 Expect(warnings).To(ConsistOf("some-app-warnings")) 372 Expect(messages[0].Message()).To(Equal("message-1")) 373 Expect(messages[0].Type()).To(Equal("OUT")) 374 Expect(messages[0].Timestamp()).To(Equal(time.Unix(0, 10))) 375 Expect(messages[0].SourceType()).To(Equal("some-source-type")) 376 Expect(messages[0].SourceInstance()).To(Equal("some-source-instance")) 377 378 Expect(messages[1].Message()).To(Equal("message-2")) 379 Expect(messages[1].Type()).To(Equal("OUT")) 380 Expect(messages[1].Timestamp()).To(Equal(time.Unix(0, 20))) 381 Expect(messages[1].SourceType()).To(Equal("some-source-type")) 382 Expect(messages[1].SourceInstance()).To(Equal("some-source-instance")) 383 }) 384 }) 385 386 When("NOAA errors", func() { 387 var expectedErr error 388 389 BeforeEach(func() { 390 expectedErr = errors.New("ZOMG") 391 fakeNOAAClient.RecentLogsReturns(nil, expectedErr) 392 }) 393 394 It("returns error and warnings", func() { 395 _, warnings, err := actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 396 Expect(err).To(MatchError(expectedErr)) 397 Expect(warnings).To(ConsistOf("some-app-warnings")) 398 }) 399 }) 400 }) 401 402 When("finding the application errors", func() { 403 var expectedErr error 404 405 BeforeEach(func() { 406 expectedErr = errors.New("ZOMG") 407 fakeCloudControllerClient.GetApplicationsReturns( 408 nil, 409 ccv2.Warnings{"some-app-warnings"}, 410 expectedErr, 411 ) 412 }) 413 414 It("returns error and warnings", func() { 415 _, warnings, err := actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 416 Expect(err).To(MatchError(expectedErr)) 417 Expect(warnings).To(ConsistOf("some-app-warnings")) 418 419 Expect(fakeNOAAClient.RecentLogsCallCount()).To(Equal(0)) 420 }) 421 }) 422 }) 423 424 Describe("GetStreamingLogsForApplicationByNameAndSpace", func() { 425 When("the application can be found", func() { 426 var ( 427 expectedAppGUID string 428 429 messages <-chan *LogMessage 430 logErrs <-chan error 431 ) 432 433 AfterEach(func() { 434 Eventually(messages).Should(BeClosed()) 435 Eventually(logErrs).Should(BeClosed()) 436 }) 437 438 BeforeEach(func() { 439 expectedAppGUID = "some-app-guid" 440 441 fakeCloudControllerClient.GetApplicationsReturns( 442 []ccv2.Application{ 443 { 444 Name: "some-app", 445 GUID: expectedAppGUID, 446 }, 447 }, 448 ccv2.Warnings{"some-app-warnings"}, 449 nil, 450 ) 451 452 fakeConfig.DialTimeoutReturns(60 * time.Minute) 453 454 fakeNOAAClient.TailingLogsStub = func(appGUID string, authToken string) (<-chan *events.LogMessage, <-chan error) { 455 Expect(appGUID).To(Equal(expectedAppGUID)) 456 Expect(authToken).To(Equal("AccessTokenForTest")) 457 458 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 459 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 460 461 eventStream := make(chan *events.LogMessage) 462 errStream := make(chan error, 1) 463 464 go func() { 465 defer close(eventStream) 466 defer close(errStream) 467 468 onConnectOrOnRetry() 469 470 outMessage := events.LogMessage_OUT 471 ts1 := int64(10) 472 sourceType := "some-source-type" 473 sourceInstance := "some-source-instance" 474 475 eventStream <- &events.LogMessage{ 476 Message: []byte("message-1"), 477 MessageType: &outMessage, 478 Timestamp: &ts1, 479 SourceType: &sourceType, 480 SourceInstance: &sourceInstance, 481 } 482 483 errMessage := events.LogMessage_ERR 484 ts2 := int64(20) 485 486 eventStream <- &events.LogMessage{ 487 Message: []byte("message-2"), 488 MessageType: &errMessage, 489 Timestamp: &ts2, 490 SourceType: &sourceType, 491 SourceInstance: &sourceInstance, 492 } 493 }() 494 495 return eventStream, errStream 496 } 497 }) 498 499 It("converts them to log messages and passes them through the messages channel", func() { 500 var err error 501 var warnings Warnings 502 messages, logErrs, warnings, err = actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 503 504 Expect(err).ToNot(HaveOccurred()) 505 Expect(warnings).To(ConsistOf("some-app-warnings")) 506 507 message := <-messages 508 Expect(message.Message()).To(Equal("message-1")) 509 Expect(message.Type()).To(Equal("OUT")) 510 Expect(message.Timestamp()).To(Equal(time.Unix(0, 10))) 511 Expect(message.SourceType()).To(Equal("some-source-type")) 512 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 513 514 message = <-messages 515 Expect(message.Message()).To(Equal("message-2")) 516 Expect(message.Type()).To(Equal("ERR")) 517 Expect(message.Timestamp()).To(Equal(time.Unix(0, 20))) 518 Expect(message.SourceType()).To(Equal("some-source-type")) 519 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 520 }) 521 }) 522 523 When("finding the application errors", func() { 524 var expectedErr error 525 526 BeforeEach(func() { 527 expectedErr = errors.New("ZOMG") 528 fakeCloudControllerClient.GetApplicationsReturns( 529 nil, 530 ccv2.Warnings{"some-app-warnings"}, 531 expectedErr, 532 ) 533 }) 534 535 It("returns error and warnings", func() { 536 _, _, warnings, err := actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 537 Expect(err).To(MatchError(expectedErr)) 538 Expect(warnings).To(ConsistOf("some-app-warnings")) 539 540 Expect(fakeNOAAClient.TailingLogsCallCount()).To(Equal(0)) 541 }) 542 }) 543 }) 544 })