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