github.com/Thanhphan1147/cloudfoundry-cli@v7.1.0+incompatible/actor/v3action/logging_test.go (about) 1 package v3action_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 . "code.cloudfoundry.org/cli/actor/v3action" 9 "code.cloudfoundry.org/cli/actor/v3action/v3actionfakes" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 11 "code.cloudfoundry.org/cli/resources" 12 noaaErrors "github.com/cloudfoundry/noaa/errors" 13 "github.com/cloudfoundry/sonde-go/events" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 ) 17 18 var _ = Describe("Logging Actions", func() { 19 var ( 20 actor *Actor 21 fakeCloudControllerClient *v3actionfakes.FakeCloudControllerClient 22 fakeConfig *v3actionfakes.FakeConfig 23 fakeNOAAClient *v3actionfakes.FakeNOAAClient 24 ) 25 26 BeforeEach(func() { 27 actor, fakeCloudControllerClient, fakeConfig, _, _ = NewTestActor() 28 fakeNOAAClient = new(v3actionfakes.FakeNOAAClient) 29 fakeConfig.AccessTokenReturns("AccessTokenForTest") 30 }) 31 32 Describe("LogMessage", func() { 33 Describe("Staging", func() { 34 When("the log is a staging log", func() { 35 It("returns true", func() { 36 message := NewLogMessage("", 0, time.Now(), "STG", "") 37 Expect(message.Staging()).To(BeTrue()) 38 }) 39 }) 40 41 When("the log is any other kind of log", func() { 42 It("returns true", func() { 43 message := NewLogMessage("", 0, time.Now(), "APP", "") 44 Expect(message.Staging()).To(BeFalse()) 45 }) 46 }) 47 }) 48 }) 49 50 Describe("GetStreamingLogs", func() { 51 var ( 52 expectedAppGUID string 53 54 messages <-chan *LogMessage 55 errs <-chan error 56 57 message *LogMessage 58 ) 59 60 BeforeEach(func() { 61 expectedAppGUID = "some-app-guid" 62 }) 63 64 AfterEach(func() { 65 Eventually(messages).Should(BeClosed()) 66 Eventually(errs).Should(BeClosed()) 67 }) 68 69 JustBeforeEach(func() { 70 messages, errs = actor.GetStreamingLogs(expectedAppGUID, fakeNOAAClient) 71 }) 72 73 When("receiving events", func() { 74 BeforeEach(func() { 75 fakeConfig.DialTimeoutReturns(60 * time.Minute) 76 77 fakeNOAAClient.TailingLogsStub = func(appGUID string, authToken string) (<-chan *events.LogMessage, <-chan error) { 78 Expect(appGUID).To(Equal(expectedAppGUID)) 79 Expect(authToken).To(Equal("AccessTokenForTest")) 80 81 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 82 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 83 84 eventStream := make(chan *events.LogMessage) 85 errStream := make(chan error, 1) 86 87 go func() { 88 defer close(eventStream) 89 defer close(errStream) 90 onConnectOrOnRetry() 91 92 outMessage := events.LogMessage_OUT 93 ts1 := int64(10) 94 sourceType := "some-source-type" 95 sourceInstance := "some-source-instance" 96 97 eventStream <- &events.LogMessage{ 98 Message: []byte("message-1"), 99 MessageType: &outMessage, 100 Timestamp: &ts1, 101 SourceType: &sourceType, 102 SourceInstance: &sourceInstance, 103 } 104 105 errMessage := events.LogMessage_ERR 106 ts2 := int64(20) 107 108 eventStream <- &events.LogMessage{ 109 Message: []byte("message-2"), 110 MessageType: &errMessage, 111 Timestamp: &ts2, 112 SourceType: &sourceType, 113 SourceInstance: &sourceInstance, 114 } 115 116 ts3 := int64(0) 117 eventStream <- &events.LogMessage{ 118 Message: []byte("message-3"), 119 MessageType: &outMessage, 120 Timestamp: &ts3, 121 SourceType: &sourceType, 122 SourceInstance: &sourceInstance, 123 } 124 125 ts4 := int64(15) 126 eventStream <- &events.LogMessage{ 127 Message: []byte("message-4"), 128 MessageType: &errMessage, 129 Timestamp: &ts4, 130 SourceType: &sourceType, 131 SourceInstance: &sourceInstance, 132 } 133 }() 134 135 return eventStream, errStream 136 } 137 }) 138 139 It("converts them to log messages, sorts them, and passes them through the messages channel", func() { 140 Eventually(messages).Should(Receive(&message)) 141 Expect(message.Message()).To(Equal("message-3")) 142 Expect(message.Type()).To(Equal("OUT")) 143 Expect(message.Timestamp()).To(Equal(time.Unix(0, 0))) 144 Expect(message.SourceType()).To(Equal("some-source-type")) 145 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 146 147 Eventually(messages).Should(Receive(&message)) 148 Expect(message.Message()).To(Equal("message-1")) 149 Expect(message.Type()).To(Equal("OUT")) 150 Expect(message.Timestamp()).To(Equal(time.Unix(0, 10))) 151 152 Eventually(messages).Should(Receive(&message)) 153 Expect(message.Message()).To(Equal("message-4")) 154 Expect(message.Type()).To(Equal("ERR")) 155 Expect(message.Timestamp()).To(Equal(time.Unix(0, 15))) 156 157 Eventually(messages).Should(Receive(&message)) 158 Expect(message.Message()).To(Equal("message-2")) 159 Expect(message.Type()).To(Equal("ERR")) 160 Expect(message.Timestamp()).To(Equal(time.Unix(0, 20))) 161 }) 162 }) 163 164 When("receiving errors", func() { 165 var ( 166 err1 error 167 err2 error 168 169 waiting chan bool 170 ) 171 172 Describe("nil error", func() { 173 BeforeEach(func() { 174 fakeConfig.DialTimeoutReturns(time.Minute) 175 176 waiting = make(chan bool) 177 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 178 eventStream := make(chan *events.LogMessage) 179 errStream := make(chan error, 1) 180 181 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 182 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 183 184 go func() { 185 defer close(eventStream) 186 defer close(errStream) 187 onConnectOrOnRetry() 188 189 errStream <- nil 190 close(waiting) 191 }() 192 193 return eventStream, errStream 194 } 195 }) 196 197 It("does not pass the nil along", func() { 198 Eventually(waiting).Should(BeClosed()) 199 Consistently(errs).ShouldNot(Receive()) 200 }) 201 }) 202 203 Describe("unexpected error", func() { 204 BeforeEach(func() { 205 fakeConfig.DialTimeoutReturns(time.Microsecond) // tests don't care about this timeout, ignore it 206 207 err1 = errors.New("ZOMG") 208 err2 = errors.New("Fiddlesticks") 209 210 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 211 eventStream := make(chan *events.LogMessage) 212 errStream := make(chan error, 1) 213 214 go func() { 215 defer close(eventStream) 216 defer close(errStream) 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("GetStreamingLogsForApplicationByNameAndSpace", func() { 321 When("the application can be found", func() { 322 var ( 323 expectedAppGUID string 324 325 messages <-chan *LogMessage 326 logErrs <-chan error 327 ) 328 329 AfterEach(func() { 330 Eventually(messages).Should(BeClosed()) 331 Eventually(logErrs).Should(BeClosed()) 332 }) 333 334 BeforeEach(func() { 335 expectedAppGUID = "some-app-guid" 336 337 fakeCloudControllerClient.GetApplicationsReturns( 338 []resources.Application{ 339 { 340 Name: "some-app", 341 GUID: expectedAppGUID, 342 }, 343 }, 344 ccv3.Warnings{"some-app-warnings"}, 345 nil, 346 ) 347 348 fakeConfig.DialTimeoutReturns(60 * time.Minute) 349 350 fakeNOAAClient.TailingLogsStub = func(appGUID string, authToken string) (<-chan *events.LogMessage, <-chan error) { 351 Expect(appGUID).To(Equal(expectedAppGUID)) 352 Expect(authToken).To(Equal("AccessTokenForTest")) 353 354 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 355 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 356 357 eventStream := make(chan *events.LogMessage) 358 errStream := make(chan error, 1) 359 360 go func() { 361 defer close(eventStream) 362 defer close(errStream) 363 364 onConnectOrOnRetry() 365 366 outMessage := events.LogMessage_OUT 367 ts1 := int64(10) 368 sourceType := "some-source-type" 369 sourceInstance := "some-source-instance" 370 371 eventStream <- &events.LogMessage{ 372 Message: []byte("message-1"), 373 MessageType: &outMessage, 374 Timestamp: &ts1, 375 SourceType: &sourceType, 376 SourceInstance: &sourceInstance, 377 } 378 379 errMessage := events.LogMessage_ERR 380 ts2 := int64(20) 381 382 eventStream <- &events.LogMessage{ 383 Message: []byte("message-2"), 384 MessageType: &errMessage, 385 Timestamp: &ts2, 386 SourceType: &sourceType, 387 SourceInstance: &sourceInstance, 388 } 389 }() 390 391 return eventStream, errStream 392 } 393 }) 394 395 It("converts them to log messages and passes them through the messages channel", func() { 396 var err error 397 var warnings Warnings 398 messages, logErrs, warnings, err = actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 399 400 Expect(err).ToNot(HaveOccurred()) 401 Expect(warnings).To(ConsistOf("some-app-warnings")) 402 403 message := <-messages 404 Expect(message.Message()).To(Equal("message-1")) 405 Expect(message.Type()).To(Equal("OUT")) 406 Expect(message.Timestamp()).To(Equal(time.Unix(0, 10))) 407 Expect(message.SourceType()).To(Equal("some-source-type")) 408 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 409 410 message = <-messages 411 Expect(message.Message()).To(Equal("message-2")) 412 Expect(message.Type()).To(Equal("ERR")) 413 Expect(message.Timestamp()).To(Equal(time.Unix(0, 20))) 414 Expect(message.SourceType()).To(Equal("some-source-type")) 415 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 416 }) 417 }) 418 419 When("finding the application errors", func() { 420 var expectedErr error 421 422 BeforeEach(func() { 423 expectedErr = errors.New("ZOMG") 424 fakeCloudControllerClient.GetApplicationsReturns( 425 nil, 426 ccv3.Warnings{"some-app-warnings"}, 427 expectedErr, 428 ) 429 }) 430 431 It("returns error and warnings", func() { 432 _, _, warnings, err := actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 433 Expect(err).To(MatchError(expectedErr)) 434 Expect(warnings).To(ConsistOf("some-app-warnings")) 435 436 Expect(fakeNOAAClient.TailingLogsCallCount()).To(Equal(0)) 437 }) 438 }) 439 }) 440 })