github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/command/v6/restage_command_test.go (about)

     1  package v6_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/v2v3action"
    10  	"code.cloudfoundry.org/cli/actor/v3action"
    11  	"code.cloudfoundry.org/cli/command/commandfakes"
    12  	"code.cloudfoundry.org/cli/command/translatableerror"
    13  	. "code.cloudfoundry.org/cli/command/v6"
    14  	"code.cloudfoundry.org/cli/command/v6/shared/sharedfakes"
    15  	"code.cloudfoundry.org/cli/command/v6/v6fakes"
    16  	"code.cloudfoundry.org/cli/types"
    17  	"code.cloudfoundry.org/cli/util/configv3"
    18  	"code.cloudfoundry.org/cli/util/ui"
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  	. "github.com/onsi/gomega/gbytes"
    22  )
    23  
    24  var _ = Describe("Restage Command", func() {
    25  	var (
    26  		cmd                         RestageCommand
    27  		testUI                      *ui.UI
    28  		fakeConfig                  *commandfakes.FakeConfig
    29  		fakeSharedActor             *commandfakes.FakeSharedActor
    30  		fakeApplicationSummaryActor *sharedfakes.FakeApplicationSummaryActor
    31  		fakeActor                   *v6fakes.FakeRestageActor
    32  		binaryName                  string
    33  		executeErr                  error
    34  	)
    35  
    36  	BeforeEach(func() {
    37  		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
    38  		fakeConfig = new(commandfakes.FakeConfig)
    39  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    40  		fakeActor = new(v6fakes.FakeRestageActor)
    41  		fakeApplicationSummaryActor = new(sharedfakes.FakeApplicationSummaryActor)
    42  
    43  		cmd = RestageCommand{
    44  			UI:                      testUI,
    45  			Config:                  fakeConfig,
    46  			SharedActor:             fakeSharedActor,
    47  			Actor:                   fakeActor,
    48  			ApplicationSummaryActor: fakeApplicationSummaryActor,
    49  		}
    50  
    51  		cmd.RequiredArgs.AppName = "some-app"
    52  
    53  		binaryName = "faceman"
    54  		fakeConfig.BinaryNameReturns(binaryName)
    55  
    56  		var err error
    57  		testUI.TimezoneLocation, err = time.LoadLocation("America/Los_Angeles")
    58  		Expect(err).NotTo(HaveOccurred())
    59  
    60  		fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
    61  			messages := make(chan *v2action.LogMessage)
    62  			logErrs := make(chan error)
    63  			appState := make(chan v2action.ApplicationStateChange)
    64  			warnings := make(chan string)
    65  			errs := make(chan error)
    66  
    67  			go func() {
    68  				appState <- v2action.ApplicationStateStaging
    69  				appState <- v2action.ApplicationStateStarting
    70  				close(messages)
    71  				close(logErrs)
    72  				close(appState)
    73  				close(warnings)
    74  				close(errs)
    75  			}()
    76  
    77  			return messages, logErrs, appState, warnings, errs
    78  		}
    79  	})
    80  
    81  	JustBeforeEach(func() {
    82  		executeErr = cmd.Execute(nil)
    83  	})
    84  
    85  	When("checking target fails", func() {
    86  		BeforeEach(func() {
    87  			fakeSharedActor.CheckTargetReturns(actionerror.NotLoggedInError{BinaryName: binaryName})
    88  		})
    89  
    90  		It("returns an error if the check fails", func() {
    91  			Expect(executeErr).To(MatchError(actionerror.NotLoggedInError{BinaryName: "faceman"}))
    92  
    93  			Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
    94  			checkTargetedOrg, checkTargetedSpace := fakeSharedActor.CheckTargetArgsForCall(0)
    95  			Expect(checkTargetedOrg).To(BeTrue())
    96  			Expect(checkTargetedSpace).To(BeTrue())
    97  		})
    98  	})
    99  
   100  	When("the user is logged in, and org and space are targeted", func() {
   101  		BeforeEach(func() {
   102  			fakeConfig.HasTargetedOrganizationReturns(true)
   103  			fakeConfig.TargetedOrganizationReturns(configv3.Organization{Name: "some-org"})
   104  			fakeConfig.HasTargetedSpaceReturns(true)
   105  			fakeConfig.TargetedSpaceReturns(configv3.Space{
   106  				GUID: "some-space-guid",
   107  				Name: "some-space"})
   108  			fakeConfig.CurrentUserReturns(
   109  				configv3.User{Name: "some-user"},
   110  				nil)
   111  		})
   112  
   113  		When("getting the current user returns an error", func() {
   114  			var expectedErr error
   115  
   116  			BeforeEach(func() {
   117  				expectedErr = errors.New("getting current user error")
   118  				fakeConfig.CurrentUserReturns(
   119  					configv3.User{},
   120  					expectedErr)
   121  			})
   122  
   123  			It("returns the error", func() {
   124  				Expect(executeErr).To(MatchError(expectedErr))
   125  			})
   126  		})
   127  
   128  		It("displays flavor text", func() {
   129  			Expect(testUI.Err).To(Say("This action will cause app downtime\\."))
   130  			Expect(testUI.Out).To(Say("Restaging app some-app in org some-org / space some-space as some-user..."))
   131  		})
   132  
   133  		When("the app does *not* exists", func() {
   134  			BeforeEach(func() {
   135  				fakeActor.GetApplicationByNameAndSpaceReturns(
   136  					v2action.Application{},
   137  					v2action.Warnings{"warning-1", "warning-2"},
   138  					actionerror.ApplicationNotFoundError{Name: "some-app"},
   139  				)
   140  			})
   141  
   142  			It("returns back an error", func() {
   143  				Expect(executeErr).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"}))
   144  
   145  				Expect(testUI.Err).To(Say("warning-1"))
   146  				Expect(testUI.Err).To(Say("warning-2"))
   147  			})
   148  		})
   149  
   150  		When("the app exists", func() {
   151  			BeforeEach(func() {
   152  				fakeActor.GetApplicationByNameAndSpaceReturns(
   153  					v2action.Application{GUID: "app-guid"},
   154  					v2action.Warnings{"warning-1", "warning-2"},
   155  					nil,
   156  				)
   157  			})
   158  
   159  			It("stages the app", func() {
   160  				Expect(executeErr).ToNot(HaveOccurred())
   161  
   162  				Expect(testUI.Err).To(Say("warning-1"))
   163  				Expect(testUI.Err).To(Say("warning-2"))
   164  
   165  				Expect(fakeActor.RestageApplicationCallCount()).To(Equal(1))
   166  				app, _ := fakeActor.RestageApplicationArgsForCall(0)
   167  				Expect(app.GUID).To(Equal("app-guid"))
   168  			})
   169  
   170  			When("passed an appStarting message", func() {
   171  				BeforeEach(func() {
   172  					fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   173  						messages := make(chan *v2action.LogMessage)
   174  						logErrs := make(chan error)
   175  						appState := make(chan v2action.ApplicationStateChange)
   176  						warnings := make(chan string)
   177  						errs := make(chan error)
   178  
   179  						go func() {
   180  							messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1")
   181  							messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1")
   182  							appState <- v2action.ApplicationStateStaging
   183  							appState <- v2action.ApplicationStateStarting
   184  							close(messages)
   185  							close(logErrs)
   186  							close(appState)
   187  							close(warnings)
   188  							close(errs)
   189  						}()
   190  
   191  						return messages, logErrs, appState, warnings, errs
   192  					}
   193  				})
   194  
   195  				It("displays the log", func() {
   196  					Expect(executeErr).ToNot(HaveOccurred())
   197  					Expect(testUI.Out).To(Say("log message 1"))
   198  					Expect(testUI.Out).To(Say("log message 2"))
   199  					Expect(testUI.Out).To(Say("Waiting for app to start..."))
   200  				})
   201  			})
   202  
   203  			When("passed a log message", func() {
   204  				BeforeEach(func() {
   205  					fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   206  						messages := make(chan *v2action.LogMessage)
   207  						logErrs := make(chan error)
   208  						appState := make(chan v2action.ApplicationStateChange)
   209  						warnings := make(chan string)
   210  						errs := make(chan error)
   211  
   212  						go func() {
   213  							messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1")
   214  							messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1")
   215  							messages <- v2action.NewLogMessage("log message 3", 1, time.Unix(0, 0), "Something else", "1")
   216  							close(messages)
   217  							close(logErrs)
   218  							close(appState)
   219  							close(warnings)
   220  							close(errs)
   221  						}()
   222  
   223  						return messages, logErrs, appState, warnings, errs
   224  					}
   225  				})
   226  
   227  				It("displays the log", func() {
   228  					Expect(executeErr).ToNot(HaveOccurred())
   229  					Expect(testUI.Out).To(Say("log message 1"))
   230  					Expect(testUI.Out).To(Say("log message 2"))
   231  					Expect(testUI.Out).ToNot(Say("log message 3"))
   232  				})
   233  			})
   234  
   235  			When("passed an log err", func() {
   236  				Context("NOAA connection times out/closes", func() {
   237  					BeforeEach(func() {
   238  						fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   239  							messages := make(chan *v2action.LogMessage)
   240  							logErrs := make(chan error)
   241  							appState := make(chan v2action.ApplicationStateChange)
   242  							warnings := make(chan string)
   243  							errs := make(chan error)
   244  
   245  							go func() {
   246  								messages <- v2action.NewLogMessage("log message 1", 1, time.Unix(0, 0), "STG", "1")
   247  								messages <- v2action.NewLogMessage("log message 2", 1, time.Unix(0, 0), "STG", "1")
   248  								messages <- v2action.NewLogMessage("log message 3", 1, time.Unix(0, 0), "STG", "1")
   249  								logErrs <- actionerror.NOAATimeoutError{}
   250  								close(messages)
   251  								close(logErrs)
   252  								close(appState)
   253  								close(warnings)
   254  								close(errs)
   255  							}()
   256  
   257  							return messages, logErrs, appState, warnings, errs
   258  						}
   259  
   260  						v3ApplicationSummary := v3action.ApplicationSummary{
   261  							Application: v3action.Application{
   262  								Name: "some-app",
   263  							},
   264  							ProcessSummaries: v3action.ProcessSummaries{
   265  								{
   266  									Process: v3action.Process{
   267  										Type:       "aba",
   268  										Command:    *types.NewFilteredString("some-command-1"),
   269  										MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   270  										DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
   271  									},
   272  								},
   273  								{
   274  									Process: v3action.Process{
   275  										Type:       "console",
   276  										Command:    *types.NewFilteredString("some-command-2"),
   277  										MemoryInMB: types.NullUint64{Value: 16, IsSet: true},
   278  										DiskInMB:   types.NullUint64{Value: 512, IsSet: true},
   279  									},
   280  								},
   281  							},
   282  						}
   283  						applicationSummary := v2v3action.ApplicationSummary{
   284  							ApplicationSummary: v3ApplicationSummary,
   285  						}
   286  						warnings := []string{"app-summary-warning"}
   287  
   288  						fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns(applicationSummary, warnings, nil)
   289  					})
   290  
   291  					It("displays a warning and continues until app has started", func() {
   292  						Expect(executeErr).To(BeNil())
   293  						Expect(testUI.Out).To(Say("message 1"))
   294  						Expect(testUI.Out).To(Say("message 2"))
   295  						Expect(testUI.Out).To(Say("message 3"))
   296  						Expect(testUI.Err).To(Say("timeout connecting to log server, no log will be shown"))
   297  						Expect(testUI.Out).To(Say(`name:\s+some-app`))
   298  					})
   299  				})
   300  
   301  				Context("an unexpected error occurs", func() {
   302  					var expectedErr error
   303  
   304  					BeforeEach(func() {
   305  						expectedErr = errors.New("err log message")
   306  						fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   307  							messages := make(chan *v2action.LogMessage)
   308  							logErrs := make(chan error)
   309  							appState := make(chan v2action.ApplicationStateChange)
   310  							warnings := make(chan string)
   311  							errs := make(chan error)
   312  
   313  							go func() {
   314  								logErrs <- expectedErr
   315  								close(messages)
   316  								close(logErrs)
   317  								close(appState)
   318  								close(warnings)
   319  								close(errs)
   320  							}()
   321  
   322  							return messages, logErrs, appState, warnings, errs
   323  						}
   324  					})
   325  
   326  					It("displays the error and continues to poll", func() {
   327  						Expect(executeErr).NotTo(HaveOccurred())
   328  						Expect(testUI.Err).To(Say(expectedErr.Error()))
   329  					})
   330  				})
   331  			})
   332  
   333  			When("passed a warning", func() {
   334  				Context("while NOAA is still logging", func() {
   335  					BeforeEach(func() {
   336  						fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   337  							messages := make(chan *v2action.LogMessage)
   338  							logErrs := make(chan error)
   339  							appState := make(chan v2action.ApplicationStateChange)
   340  							warnings := make(chan string)
   341  							errs := make(chan error)
   342  
   343  							go func() {
   344  								warnings <- "warning 1"
   345  								warnings <- "warning 2"
   346  								close(messages)
   347  								close(logErrs)
   348  								close(appState)
   349  								close(warnings)
   350  								close(errs)
   351  							}()
   352  
   353  							return messages, logErrs, appState, warnings, errs
   354  						}
   355  					})
   356  
   357  					It("displays the warnings to STDERR", func() {
   358  						Expect(executeErr).ToNot(HaveOccurred())
   359  						Expect(testUI.Err).To(Say("warning 1"))
   360  						Expect(testUI.Err).To(Say("warning 2"))
   361  					})
   362  				})
   363  
   364  				Context("while NOAA is no longer logging", func() {
   365  					BeforeEach(func() {
   366  						fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   367  							messages := make(chan *v2action.LogMessage)
   368  							logErrs := make(chan error)
   369  							appState := make(chan v2action.ApplicationStateChange)
   370  							warnings := make(chan string)
   371  							errs := make(chan error)
   372  
   373  							go func() {
   374  								warnings <- "warning 1"
   375  								warnings <- "warning 2"
   376  								logErrs <- actionerror.NOAATimeoutError{}
   377  								close(messages)
   378  								close(logErrs)
   379  								warnings <- "warning 3"
   380  								warnings <- "warning 4"
   381  								close(appState)
   382  								close(warnings)
   383  								close(errs)
   384  							}()
   385  
   386  							return messages, logErrs, appState, warnings, errs
   387  						}
   388  					})
   389  
   390  					It("displays the warnings to STDERR", func() {
   391  						Expect(executeErr).ToNot(HaveOccurred())
   392  						Expect(testUI.Err).To(Say("warning 1"))
   393  						Expect(testUI.Err).To(Say("warning 2"))
   394  						Expect(testUI.Err).To(Say("timeout connecting to log server, no log will be shown"))
   395  						Expect(testUI.Err).To(Say("warning 3"))
   396  						Expect(testUI.Err).To(Say("warning 4"))
   397  					})
   398  				})
   399  			})
   400  
   401  			When("passed an API err", func() {
   402  				var apiErr error
   403  
   404  				BeforeEach(func() {
   405  					fakeActor.RestageApplicationStub = func(app v2action.Application, client v2action.NOAAClient) (<-chan *v2action.LogMessage, <-chan error, <-chan v2action.ApplicationStateChange, <-chan string, <-chan error) {
   406  						messages := make(chan *v2action.LogMessage)
   407  						logErrs := make(chan error)
   408  						appState := make(chan v2action.ApplicationStateChange)
   409  						warnings := make(chan string)
   410  						errs := make(chan error)
   411  
   412  						go func() {
   413  							errs <- apiErr
   414  							close(messages)
   415  							close(logErrs)
   416  							close(appState)
   417  							close(warnings)
   418  							close(errs)
   419  						}()
   420  
   421  						return messages, logErrs, appState, warnings, errs
   422  					}
   423  				})
   424  
   425  				Context("an unexpected error", func() {
   426  					BeforeEach(func() {
   427  						apiErr = errors.New("err log message")
   428  					})
   429  
   430  					It("stops logging and returns the error", func() {
   431  						Expect(executeErr).To(MatchError(apiErr))
   432  					})
   433  				})
   434  
   435  				Context("staging failed", func() {
   436  					BeforeEach(func() {
   437  						apiErr = actionerror.StagingFailedError{Reason: "Something, but not nothing"}
   438  					})
   439  
   440  					It("stops logging and returns StagingFailedError", func() {
   441  						Expect(executeErr).To(MatchError(translatableerror.StagingFailedError{Message: "Something, but not nothing"}))
   442  					})
   443  				})
   444  
   445  				Context("staging timed out", func() {
   446  					BeforeEach(func() {
   447  						apiErr = actionerror.StagingTimeoutError{AppName: "some-app", Timeout: time.Nanosecond}
   448  					})
   449  
   450  					It("stops logging and returns StagingTimeoutError", func() {
   451  						Expect(executeErr).To(MatchError(translatableerror.StagingTimeoutError{AppName: "some-app", Timeout: time.Nanosecond}))
   452  					})
   453  				})
   454  
   455  				When("the app instance crashes", func() {
   456  					BeforeEach(func() {
   457  						apiErr = actionerror.ApplicationInstanceCrashedError{Name: "some-app"}
   458  					})
   459  
   460  					It("stops logging and returns UnsuccessfulStartError", func() {
   461  						Expect(executeErr).To(MatchError(translatableerror.UnsuccessfulStartError{AppName: "some-app", BinaryName: "faceman"}))
   462  					})
   463  				})
   464  
   465  				When("the app instance flaps", func() {
   466  					BeforeEach(func() {
   467  						apiErr = actionerror.ApplicationInstanceFlappingError{Name: "some-app"}
   468  					})
   469  
   470  					It("stops logging and returns UnsuccessfulStartError", func() {
   471  						Expect(executeErr).To(MatchError(translatableerror.UnsuccessfulStartError{AppName: "some-app", BinaryName: "faceman"}))
   472  					})
   473  				})
   474  
   475  				Context("starting timeout", func() {
   476  					BeforeEach(func() {
   477  						apiErr = actionerror.StartupTimeoutError{Name: "some-app"}
   478  					})
   479  
   480  					It("stops logging and returns StartupTimeoutError", func() {
   481  						Expect(executeErr).To(MatchError(translatableerror.StartupTimeoutError{AppName: "some-app", BinaryName: "faceman"}))
   482  					})
   483  				})
   484  			})
   485  
   486  			When("the app finishes starting", func() {
   487  				var (
   488  					applicationSummary v2v3action.ApplicationSummary
   489  				)
   490  
   491  				BeforeEach(func() {
   492  					v3ApplicationSummary := v3action.ApplicationSummary{
   493  						Application: v3action.Application{
   494  							Name: "some-app",
   495  						},
   496  						ProcessSummaries: v3action.ProcessSummaries{
   497  							{
   498  								Process: v3action.Process{
   499  									Type:       "aba",
   500  									Command:    *types.NewFilteredString("some-command-1"),
   501  									MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
   502  									DiskInMB:   types.NullUint64{Value: 1024, IsSet: true},
   503  								},
   504  							},
   505  							{
   506  								Process: v3action.Process{
   507  									Type:       "console",
   508  									Command:    *types.NewFilteredString("some-command-2"),
   509  									MemoryInMB: types.NullUint64{Value: 16, IsSet: true},
   510  									DiskInMB:   types.NullUint64{Value: 512, IsSet: true},
   511  								},
   512  							},
   513  						},
   514  					}
   515  					applicationSummary = v2v3action.ApplicationSummary{
   516  						ApplicationSummary: v3ApplicationSummary,
   517  					}
   518  
   519  					fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceReturns(
   520  						applicationSummary,
   521  						v2v3action.Warnings{"combo-summary-warning"},
   522  						nil)
   523  
   524  				})
   525  
   526  				It("uses the multiprocess display", func() {
   527  					Expect(executeErr).ToNot(HaveOccurred())
   528  
   529  					Expect(fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceCallCount()).To(Equal(1))
   530  					passedAppName, spaceGUID, withObfuscatedValues := fakeApplicationSummaryActor.GetApplicationSummaryByNameAndSpaceArgsForCall(0)
   531  					Expect(passedAppName).To(Equal("some-app"))
   532  					Expect(spaceGUID).To(Equal("some-space-guid"))
   533  					Expect(withObfuscatedValues).To(BeTrue())
   534  
   535  					Expect(testUI.Out).To(Say(`name:\s+%s`, "some-app"))
   536  					Expect(testUI.Out).To(Say(`type:\s+aba`))
   537  					Expect(testUI.Out).To(Say(`instances:\s+0/0`))
   538  					Expect(testUI.Out).To(Say(`memory usage:\s+32M`))
   539  					Expect(testUI.Out).To(Say(`start command:\s+some-command-1`))
   540  					Expect(testUI.Out).To(Say(`type:\s+console`))
   541  					Expect(testUI.Out).To(Say(`instances:\s+0/0`))
   542  					Expect(testUI.Out).To(Say(`memory usage:\s+16M`))
   543  					Expect(testUI.Out).To(Say(`start command:\s+some-command-2`))
   544  
   545  					Expect(testUI.Err).To(Say("combo-summary-warning"))
   546  				})
   547  			})
   548  		})
   549  	})
   550  })