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