github.com/arunkumar7540/cli@v6.45.0+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  		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  
   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("GetRecentLogsForApplicationByNameAndSpace", func() {
   321  		When("the application can be found", func() {
   322  			BeforeEach(func() {
   323  				fakeCloudControllerClient.GetApplicationsReturns(
   324  					[]ccv2.Application{
   325  						{
   326  							Name: "some-app",
   327  							GUID: "some-app-guid",
   328  						},
   329  					},
   330  					ccv2.Warnings{"some-app-warnings"},
   331  					nil,
   332  				)
   333  			})
   334  
   335  			When("NOAA returns logs", func() {
   336  				BeforeEach(func() {
   337  					outMessage := events.LogMessage_OUT
   338  					ts1 := int64(10)
   339  					ts2 := int64(20)
   340  					sourceType := "some-source-type"
   341  					sourceInstance := "some-source-instance"
   342  
   343  					var messages []*events.LogMessage
   344  					messages = append(messages, &events.LogMessage{
   345  						Message:        []byte("message-2"),
   346  						MessageType:    &outMessage,
   347  						Timestamp:      &ts2,
   348  						SourceType:     &sourceType,
   349  						SourceInstance: &sourceInstance,
   350  					})
   351  					messages = append(messages, &events.LogMessage{
   352  						Message:        []byte("message-1"),
   353  						MessageType:    &outMessage,
   354  						Timestamp:      &ts1,
   355  						SourceType:     &sourceType,
   356  						SourceInstance: &sourceInstance,
   357  					})
   358  
   359  					fakeNOAAClient.RecentLogsReturns(messages, nil)
   360  				})
   361  
   362  				It("passes a nonempty access token to the NOAA client", func() {
   363  					actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient)
   364  					_, accessToken := fakeNOAAClient.RecentLogsArgsForCall(0)
   365  					Expect(accessToken).To(Equal("AccessTokenForTest"))
   366  				})
   367  
   368  				It("returns all the recent logs and warnings", func() {
   369  					messages, warnings, err := actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient)
   370  					Expect(err).ToNot(HaveOccurred())
   371  					Expect(warnings).To(ConsistOf("some-app-warnings"))
   372  					Expect(messages[0].Message()).To(Equal("message-1"))
   373  					Expect(messages[0].Type()).To(Equal("OUT"))
   374  					Expect(messages[0].Timestamp()).To(Equal(time.Unix(0, 10)))
   375  					Expect(messages[0].SourceType()).To(Equal("some-source-type"))
   376  					Expect(messages[0].SourceInstance()).To(Equal("some-source-instance"))
   377  
   378  					Expect(messages[1].Message()).To(Equal("message-2"))
   379  					Expect(messages[1].Type()).To(Equal("OUT"))
   380  					Expect(messages[1].Timestamp()).To(Equal(time.Unix(0, 20)))
   381  					Expect(messages[1].SourceType()).To(Equal("some-source-type"))
   382  					Expect(messages[1].SourceInstance()).To(Equal("some-source-instance"))
   383  				})
   384  			})
   385  
   386  			When("NOAA errors", func() {
   387  				var expectedErr error
   388  
   389  				BeforeEach(func() {
   390  					expectedErr = errors.New("ZOMG")
   391  					fakeNOAAClient.RecentLogsReturns(nil, expectedErr)
   392  				})
   393  
   394  				It("returns error and warnings", func() {
   395  					_, warnings, err := actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient)
   396  					Expect(err).To(MatchError(expectedErr))
   397  					Expect(warnings).To(ConsistOf("some-app-warnings"))
   398  				})
   399  			})
   400  		})
   401  
   402  		When("finding the application errors", func() {
   403  			var expectedErr error
   404  
   405  			BeforeEach(func() {
   406  				expectedErr = errors.New("ZOMG")
   407  				fakeCloudControllerClient.GetApplicationsReturns(
   408  					nil,
   409  					ccv2.Warnings{"some-app-warnings"},
   410  					expectedErr,
   411  				)
   412  			})
   413  
   414  			It("returns error and warnings", func() {
   415  				_, warnings, err := actor.GetRecentLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient)
   416  				Expect(err).To(MatchError(expectedErr))
   417  				Expect(warnings).To(ConsistOf("some-app-warnings"))
   418  
   419  				Expect(fakeNOAAClient.RecentLogsCallCount()).To(Equal(0))
   420  			})
   421  		})
   422  	})
   423  
   424  	Describe("GetStreamingLogsForApplicationByNameAndSpace", func() {
   425  		When("the application can be found", func() {
   426  			var (
   427  				expectedAppGUID string
   428  
   429  				messages <-chan *LogMessage
   430  				logErrs  <-chan error
   431  			)
   432  
   433  			AfterEach(func() {
   434  				Eventually(messages).Should(BeClosed())
   435  				Eventually(logErrs).Should(BeClosed())
   436  			})
   437  
   438  			BeforeEach(func() {
   439  				expectedAppGUID = "some-app-guid"
   440  
   441  				fakeCloudControllerClient.GetApplicationsReturns(
   442  					[]ccv2.Application{
   443  						{
   444  							Name: "some-app",
   445  							GUID: expectedAppGUID,
   446  						},
   447  					},
   448  					ccv2.Warnings{"some-app-warnings"},
   449  					nil,
   450  				)
   451  
   452  				fakeConfig.DialTimeoutReturns(60 * time.Minute)
   453  
   454  				fakeNOAAClient.TailingLogsStub = func(appGUID string, authToken string) (<-chan *events.LogMessage, <-chan error) {
   455  					Expect(appGUID).To(Equal(expectedAppGUID))
   456  					Expect(authToken).To(Equal("AccessTokenForTest"))
   457  
   458  					Expect(fakeNOAAClient.SetOnConnectCallbackCallCount()).To(Equal(1))
   459  					onConnectOrOnRetry := fakeNOAAClient.SetOnConnectCallbackArgsForCall(0)
   460  
   461  					eventStream := make(chan *events.LogMessage)
   462  					errStream := make(chan error, 1)
   463  
   464  					go func() {
   465  						defer close(eventStream)
   466  						defer close(errStream)
   467  
   468  						onConnectOrOnRetry()
   469  
   470  						outMessage := events.LogMessage_OUT
   471  						ts1 := int64(10)
   472  						sourceType := "some-source-type"
   473  						sourceInstance := "some-source-instance"
   474  
   475  						eventStream <- &events.LogMessage{
   476  							Message:        []byte("message-1"),
   477  							MessageType:    &outMessage,
   478  							Timestamp:      &ts1,
   479  							SourceType:     &sourceType,
   480  							SourceInstance: &sourceInstance,
   481  						}
   482  
   483  						errMessage := events.LogMessage_ERR
   484  						ts2 := int64(20)
   485  
   486  						eventStream <- &events.LogMessage{
   487  							Message:        []byte("message-2"),
   488  							MessageType:    &errMessage,
   489  							Timestamp:      &ts2,
   490  							SourceType:     &sourceType,
   491  							SourceInstance: &sourceInstance,
   492  						}
   493  					}()
   494  
   495  					return eventStream, errStream
   496  				}
   497  			})
   498  
   499  			It("converts them to log messages and passes them through the messages channel", func() {
   500  				var err error
   501  				var warnings Warnings
   502  				messages, logErrs, warnings, err = actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient)
   503  
   504  				Expect(err).ToNot(HaveOccurred())
   505  				Expect(warnings).To(ConsistOf("some-app-warnings"))
   506  
   507  				message := <-messages
   508  				Expect(message.Message()).To(Equal("message-1"))
   509  				Expect(message.Type()).To(Equal("OUT"))
   510  				Expect(message.Timestamp()).To(Equal(time.Unix(0, 10)))
   511  				Expect(message.SourceType()).To(Equal("some-source-type"))
   512  				Expect(message.SourceInstance()).To(Equal("some-source-instance"))
   513  
   514  				message = <-messages
   515  				Expect(message.Message()).To(Equal("message-2"))
   516  				Expect(message.Type()).To(Equal("ERR"))
   517  				Expect(message.Timestamp()).To(Equal(time.Unix(0, 20)))
   518  				Expect(message.SourceType()).To(Equal("some-source-type"))
   519  				Expect(message.SourceInstance()).To(Equal("some-source-instance"))
   520  			})
   521  		})
   522  
   523  		When("finding the application errors", func() {
   524  			var expectedErr error
   525  
   526  			BeforeEach(func() {
   527  				expectedErr = errors.New("ZOMG")
   528  				fakeCloudControllerClient.GetApplicationsReturns(
   529  					nil,
   530  					ccv2.Warnings{"some-app-warnings"},
   531  					expectedErr,
   532  				)
   533  			})
   534  
   535  			It("returns error and warnings", func() {
   536  				_, _, warnings, err := actor.GetStreamingLogsForApplicationByNameAndSpace("some-app", "some-space-guid", fakeNOAAClient)
   537  				Expect(err).To(MatchError(expectedErr))
   538  				Expect(warnings).To(ConsistOf("some-app-warnings"))
   539  
   540  				Expect(fakeNOAAClient.TailingLogsCallCount()).To(Equal(0))
   541  			})
   542  		})
   543  	})
   544  })