github.com/loafoe/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  })