github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/actor/v7action/logging_test.go (about) 1 package v7action_test 2 3 import ( 4 "errors" 5 "time" 6 7 "code.cloudfoundry.org/cli/actor/actionerror" 8 . "code.cloudfoundry.org/cli/actor/v7action" 9 "code.cloudfoundry.org/cli/actor/v7action/v7actionfakes" 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 *v7actionfakes.FakeCloudControllerClient 21 fakeConfig *v7actionfakes.FakeConfig 22 fakeNOAAClient *v7actionfakes.FakeNOAAClient 23 ) 24 25 BeforeEach(func() { 26 actor, fakeCloudControllerClient, fakeConfig, _, _ = NewTestActor() 27 fakeNOAAClient = new(v7actionfakes.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 errStream <- err1 216 errStream <- err2 217 }() 218 219 return eventStream, errStream 220 } 221 }) 222 223 It("passes them through the errors channel", func() { 224 Eventually(errs).Should(Receive(Equal(err1))) 225 Eventually(errs).Should(Receive(Equal(err2))) 226 }) 227 }) 228 229 Describe("NOAA's RetryError", func() { 230 When("NOAA is able to recover", func() { 231 BeforeEach(func() { 232 fakeConfig.DialTimeoutReturns(60 * time.Minute) 233 234 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 235 eventStream := make(chan *events.LogMessage) 236 errStream := make(chan error, 1) 237 238 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 239 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 240 241 go func() { 242 defer close(eventStream) 243 defer close(errStream) 244 245 // can be called multiple times. Should be resilient to that 246 onConnectOrOnRetry() 247 errStream <- noaaErrors.NewRetryError(errors.New("error 1")) 248 onConnectOrOnRetry() 249 250 outMessage := events.LogMessage_OUT 251 ts1 := int64(10) 252 sourceType := "some-source-type" 253 sourceInstance := "some-source-instance" 254 255 eventStream <- &events.LogMessage{ 256 Message: []byte("message-1"), 257 MessageType: &outMessage, 258 Timestamp: &ts1, 259 SourceType: &sourceType, 260 SourceInstance: &sourceInstance, 261 } 262 }() 263 264 return eventStream, errStream 265 } 266 }) 267 268 It("continues without issue", func() { 269 Eventually(messages).Should(Receive()) 270 Consistently(errs).ShouldNot(Receive()) 271 }) 272 }) 273 274 When("NOAA has trouble connecting", func() { 275 BeforeEach(func() { 276 fakeConfig.DialTimeoutReturns(time.Microsecond) 277 fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) { 278 eventStream := make(chan *events.LogMessage) 279 errStream := make(chan error, 1) 280 281 go func() { 282 defer close(eventStream) 283 defer close(errStream) 284 285 // explicitly skip the on call to simulate ready never being triggered 286 287 errStream <- noaaErrors.NewRetryError(errors.New("error 1")) 288 289 outMessage := events.LogMessage_OUT 290 ts1 := int64(10) 291 sourceType := "some-source-type" 292 sourceInstance := "some-source-instance" 293 294 eventStream <- &events.LogMessage{ 295 Message: []byte("message-1"), 296 MessageType: &outMessage, 297 Timestamp: &ts1, 298 SourceType: &sourceType, 299 SourceInstance: &sourceInstance, 300 } 301 }() 302 303 return eventStream, errStream 304 } 305 }) 306 307 It("returns a NOAATimeoutError and continues", func() { 308 Eventually(errs).Should(Receive(MatchError(actionerror.NOAATimeoutError{}))) 309 Eventually(messages).Should(Receive()) 310 311 Expect(fakeConfig.DialTimeoutCallCount()).To(Equal(1)) 312 }) 313 }) 314 }) 315 }) 316 }) 317 318 Describe("GetStreamingLogsForApplicationByNameAndSpace", func() { 319 When("the application can be found", func() { 320 var ( 321 expectedAppGUID string 322 323 messages <-chan *LogMessage 324 logErrs <-chan error 325 ) 326 327 AfterEach(func() { 328 Eventually(messages).Should(BeClosed()) 329 Eventually(logErrs).Should(BeClosed()) 330 }) 331 332 BeforeEach(func() { 333 expectedAppGUID = "some-app-guid" 334 335 fakeCloudControllerClient.GetApplicationsReturns( 336 []ccv3.Application{ 337 { 338 Name: "some-app", 339 GUID: expectedAppGUID, 340 }, 341 }, 342 ccv3.Warnings{"some-app-warnings"}, 343 nil, 344 ) 345 346 fakeConfig.DialTimeoutReturns(60 * time.Minute) 347 348 fakeNOAAClient.TailingLogsStub = func(appGUID string, authToken string) (<-chan *events.LogMessage, <-chan error) { 349 Expect(appGUID).To(Equal(expectedAppGUID)) 350 Expect(authToken).To(BeEmpty()) 351 352 Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1)) 353 onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0) 354 355 eventStream := make(chan *events.LogMessage) 356 errStream := make(chan error, 1) 357 358 go func() { 359 defer close(eventStream) 360 defer close(errStream) 361 362 onConnectOrOnRetry() 363 364 outMessage := events.LogMessage_OUT 365 ts1 := int64(10) 366 sourceType := "some-source-type" 367 sourceInstance := "some-source-instance" 368 369 eventStream <- &events.LogMessage{ 370 Message: []byte("message-1"), 371 MessageType: &outMessage, 372 Timestamp: &ts1, 373 SourceType: &sourceType, 374 SourceInstance: &sourceInstance, 375 } 376 377 errMessage := events.LogMessage_ERR 378 ts2 := int64(20) 379 380 eventStream <- &events.LogMessage{ 381 Message: []byte("message-2"), 382 MessageType: &errMessage, 383 Timestamp: &ts2, 384 SourceType: &sourceType, 385 SourceInstance: &sourceInstance, 386 } 387 }() 388 389 return eventStream, errStream 390 } 391 }) 392 393 It("converts them to log messages and passes them through the messages channel", func() { 394 var err error 395 var warnings Warnings 396 messages, logErrs, warnings, err = actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 397 398 Expect(err).ToNot(HaveOccurred()) 399 Expect(warnings).To(ConsistOf("some-app-warnings")) 400 401 message := <-messages 402 Expect(message.Message()).To(Equal("message-1")) 403 Expect(message.Type()).To(Equal("OUT")) 404 Expect(message.Timestamp()).To(Equal(time.Unix(0, 10))) 405 Expect(message.SourceType()).To(Equal("some-source-type")) 406 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 407 408 message = <-messages 409 Expect(message.Message()).To(Equal("message-2")) 410 Expect(message.Type()).To(Equal("ERR")) 411 Expect(message.Timestamp()).To(Equal(time.Unix(0, 20))) 412 Expect(message.SourceType()).To(Equal("some-source-type")) 413 Expect(message.SourceInstance()).To(Equal("some-source-instance")) 414 }) 415 }) 416 417 When("finding the application errors", func() { 418 var expectedErr error 419 420 BeforeEach(func() { 421 expectedErr = errors.New("ZOMG") 422 fakeCloudControllerClient.GetApplicationsReturns( 423 nil, 424 ccv3.Warnings{"some-app-warnings"}, 425 expectedErr, 426 ) 427 }) 428 429 It("returns error and warnings", func() { 430 _, _, warnings, err := actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient) 431 Expect(err).To(MatchError(expectedErr)) 432 Expect(warnings).To(ConsistOf("some-app-warnings")) 433 434 Expect(fakeNOAAClient.TailingLogsCallCount()).To(Equal(0)) 435 }) 436 }) 437 }) 438 })