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