github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+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 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 *v3actionfakes.FakeCloudControllerClient 21 fakeConfig *v3actionfakes.FakeConfig 22 fakeNOAAClient *v3actionfakes.FakeNOAAClient 23 ) 24 25 BeforeEach(func() { 26 actor, fakeCloudControllerClient, fakeConfig, _, _ = NewTestActor() 27 fakeNOAAClient = new(v3actionfakes.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 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("GetStreamingLogsForApplicationByNameAndSpace", func() { 320 When("the application can be found", func() { 321 var ( 322 expectedAppGUID string 323 324 messages <-chan *LogMessage 325 logErrs <-chan error 326 ) 327 328 AfterEach(func() { 329 Eventually(messages).Should(BeClosed()) 330 Eventually(logErrs).Should(BeClosed()) 331 }) 332 333 BeforeEach(func() { 334 expectedAppGUID = "some-app-guid" 335 336 fakeCloudControllerClient.GetApplicationsReturns( 337 []ccv3.Application{ 338 { 339 Name: "some-app", 340 GUID: expectedAppGUID, 341 }, 342 }, 343 ccv3.Warnings{"some-app-warnings"}, 344 nil, 345 ) 346 347 fakeConfig.DialTimeoutReturns(60 * time.Minute) 348 349 fakeNOAAClient.TailingLogsStub = func(appGUID string, authToken string) (<-chan *events.LogMessage, <-chan error) { 350 Expect(appGUID).To(Equal(expectedAppGUID)) 351 Expect(authToken).To(Equal("AccessTokenForTest")) 352 353 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 354 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 355 356 eventStream := make(chan *events.LogMessage) 357 errStream := make(chan error, 1) 358 359 go func() { 360 defer close(eventStream) 361 defer close(errStream) 362 363 onConnectOrOnRetry() 364 365 outMessage := events.LogMessage_OUT 366 ts1 := int64(10) 367 sourceType := "some-source-type" 368 sourceInstance := "some-source-instance" 369 370 eventStream <- &events.LogMessage{ 371 Message: []byte("message-1"), 372 MessageType: &outMessage, 373 Timestamp: &ts1, 374 SourceType: &sourceType, 375 SourceInstance: &sourceInstance, 376 } 377 378 errMessage := events.LogMessage_ERR 379 ts2 := int64(20) 380 381 eventStream <- &events.LogMessage{ 382 Message: []byte("message-2"), 383 MessageType: &errMessage, 384 Timestamp: &ts2, 385 SourceType: &sourceType, 386 SourceInstance: &sourceInstance, 387 } 388 }() 389 390 return eventStream, errStream 391 } 392 }) 393 394 It("converts them to log messages and passes them through the messages channel", func() { 395 var err error 396 var warnings Warnings 397 messages, logErrs, warnings, err = actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 398 399 Expect(err).ToNot(HaveOccurred()) 400 Expect(warnings).To(ConsistOf("some-app-warnings")) 401 402 message := <-messages 403 Expect(message.Message()).To(Equal("message-1")) 404 Expect(message.Type()).To(Equal("OUT")) 405 Expect(message.Timestamp()).To(Equal(time.Unix(0, 10))) 406 Expect(message.SourceType()).To(Equal("some-source-type")) 407 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 408 409 message = <-messages 410 Expect(message.Message()).To(Equal("message-2")) 411 Expect(message.Type()).To(Equal("ERR")) 412 Expect(message.Timestamp()).To(Equal(time.Unix(0, 20))) 413 Expect(message.SourceType()).To(Equal("some-source-type")) 414 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 415 }) 416 }) 417 418 When("finding the application errors", func() { 419 var expectedErr error 420 421 BeforeEach(func() { 422 expectedErr = errors.New("ZOMG") 423 fakeCloudControllerClient.GetApplicationsReturns( 424 nil, 425 ccv3.Warnings{"some-app-warnings"}, 426 expectedErr, 427 ) 428 }) 429 430 It("returns error and warnings", func() { 431 _, _, warnings, err := actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 432 Expect(err).To(MatchError(expectedErr)) 433 Expect(warnings).To(ConsistOf("some-app-warnings")) 434 435 Expect(fakeNOAAClient.TailingLogsCallCount()).To(Equal(0)) 436 }) 437 }) 438 }) 439 })