github.com/arunkumar7540/cli@v6.45.0+incompatible/actor/v3action/zdt_test.go (about)

     1  package v3action_test
     2  
     3  import (
     4  	"code.cloudfoundry.org/cli/actor/actionerror"
     5  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
     6  
     7  	"errors"
     8  	"fmt"
     9  	"time"
    10  
    11  	. "code.cloudfoundry.org/cli/actor/v3action"
    12  	"code.cloudfoundry.org/cli/actor/v3action/v3actionfakes"
    13  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("v3-zdt-push", func() {
    19  
    20  	var (
    21  		actor                     *Actor
    22  		fakeCloudControllerClient *v3actionfakes.FakeCloudControllerClient
    23  		fakeConfig                *v3actionfakes.FakeConfig
    24  	)
    25  
    26  	BeforeEach(func() {
    27  		fakeCloudControllerClient = new(v3actionfakes.FakeCloudControllerClient)
    28  		fakeConfig = new(v3actionfakes.FakeConfig)
    29  		actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil)
    30  	})
    31  
    32  	Describe("CancelDeploymentByAppNameAndSpace", func() {
    33  		var (
    34  			app ccv3.Application
    35  		)
    36  
    37  		BeforeEach(func() {
    38  			app = ccv3.Application{GUID: "app-guid"}
    39  			fakeCloudControllerClient.GetApplicationsReturns([]ccv3.Application{app}, ccv3.Warnings{"getapp-warning"}, nil)
    40  			fakeCloudControllerClient.GetDeploymentsReturns([]ccv3.Deployment{{GUID: "deployment-guid"}}, ccv3.Warnings{"getdep-warning"}, nil)
    41  			fakeCloudControllerClient.CancelDeploymentReturns(ccv3.Warnings{"cancel-warning"}, nil)
    42  		})
    43  
    44  		It("cancels the appropriate deployment", func() {
    45  			warnings, err := actor.CancelDeploymentByAppNameAndSpace("app-name", "space-guid")
    46  			Expect(err).NotTo(HaveOccurred())
    47  			Expect(warnings).To(ConsistOf(Warnings{"getapp-warning", "getdep-warning", "cancel-warning"}))
    48  			Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
    49  				ccv3.Query{Key: ccv3.NameFilter, Values: []string{"app-name"}},
    50  				ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"space-guid"}},
    51  			))
    52  			Expect(fakeCloudControllerClient.GetDeploymentsArgsForCall(0)).To(ConsistOf(
    53  				ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{"app-guid"}},
    54  				ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}},
    55  				ccv3.Query{Key: ccv3.OrderBy, Values: []string{"-created_at"}},
    56  			))
    57  			Expect(fakeCloudControllerClient.CancelDeploymentArgsForCall(0)).To(Equal("deployment-guid"))
    58  		})
    59  
    60  		Context("when no deployments are found", func() {
    61  			BeforeEach(func() {
    62  				fakeCloudControllerClient.GetDeploymentsReturns([]ccv3.Deployment{}, nil, nil)
    63  			})
    64  
    65  			It("errors appropriately", func() {
    66  				_, err := actor.CancelDeploymentByAppNameAndSpace("app-name", "space-guid")
    67  				Expect(err).To(MatchError("failed to find a deployment for that app"))
    68  			})
    69  		})
    70  
    71  		Context("when we fail while searching for app", func() {
    72  			BeforeEach(func() {
    73  				fakeCloudControllerClient.GetApplicationsReturns(nil, nil, errors.New("banana"))
    74  			})
    75  
    76  			It("errors appropriately", func() {
    77  				_, err := actor.CancelDeploymentByAppNameAndSpace("app-name", "space-guid")
    78  				Expect(err).To(MatchError("banana"))
    79  			})
    80  		})
    81  
    82  		Context("when we fail while searching for the apps current deployment", func() {
    83  			BeforeEach(func() {
    84  				fakeCloudControllerClient.GetDeploymentsReturns(nil, nil, errors.New("vegetable"))
    85  			})
    86  
    87  			It("errors appropriately", func() {
    88  				_, err := actor.CancelDeploymentByAppNameAndSpace("app-name", "space-guid")
    89  				Expect(err).To(MatchError("vegetable"))
    90  			})
    91  		})
    92  	})
    93  
    94  	Describe("CreateApplicationDeployment", func() {
    95  
    96  		Context("When there is no error", func() {
    97  
    98  			BeforeEach(func() {
    99  				fakeCloudControllerClient.CreateApplicationDeploymentReturns("some-deployment-guid", ccv3.Warnings{"create-deployment-warning"}, nil)
   100  			})
   101  
   102  			It("Returns the deployment GUID when it is non empty", func() {
   103  				deploymentGUID, warnings, err := actor.CreateDeployment("some-app-guid", "some-droplet-guid")
   104  				Expect(deploymentGUID).To(Equal("some-deployment-guid"))
   105  				Expect(warnings).To(ConsistOf("create-deployment-warning"))
   106  				Expect(err).To(BeNil())
   107  			})
   108  		})
   109  
   110  		Context("When an error occurs", func() {
   111  
   112  			BeforeEach(func() {
   113  				fakeCloudControllerClient.CreateApplicationDeploymentReturns("", ccv3.Warnings{"create-deployment-warning"}, errors.New("failed create"))
   114  			})
   115  
   116  			It("Returns an error if an error occurred", func() {
   117  				deploymentGUID, warnings, err := actor.CreateDeployment("some-app-guid", "some-droplet-guid")
   118  				Expect(deploymentGUID).To(Equal(""))
   119  				Expect(warnings).To(ConsistOf("create-deployment-warning"))
   120  				Expect(err).To(MatchError(errors.New("failed create")))
   121  			})
   122  		})
   123  
   124  	})
   125  
   126  	Describe("GetDeploymentState", func() {
   127  
   128  		Context("when there is no error", func() {
   129  
   130  			BeforeEach(func() {
   131  				resultDeployment := ccv3.Deployment{State: constant.DeploymentDeploying}
   132  				fakeCloudControllerClient.GetDeploymentReturns(resultDeployment, ccv3.Warnings{"create-deployment-warning"}, nil)
   133  			})
   134  
   135  			It("returns a state of X", func() {
   136  				deploymentState, warnings, err := actor.GetDeploymentState("some-deployment-guid")
   137  				Expect(deploymentState).To(Equal(constant.DeploymentDeploying))
   138  				Expect(warnings).To(ConsistOf("create-deployment-warning"))
   139  				Expect(err).To(BeNil())
   140  			})
   141  		})
   142  	})
   143  
   144  	Describe("PollDeployment", func() {
   145  		var warningsChannel chan Warnings
   146  		var allWarnings Warnings
   147  		var funcDone chan interface{}
   148  
   149  		BeforeEach(func() {
   150  			fakeConfig.StartupTimeoutReturns(time.Second)
   151  			fakeConfig.PollingIntervalReturns(0)
   152  			warningsChannel = make(chan Warnings)
   153  			allWarnings = Warnings{}
   154  			funcDone = make(chan interface{})
   155  			go func() {
   156  				for {
   157  					select {
   158  					case warnings := <-warningsChannel:
   159  						allWarnings = append(allWarnings, warnings...)
   160  					case <-funcDone:
   161  						return
   162  					}
   163  				}
   164  			}()
   165  		})
   166  
   167  		const myDeploymentGUID = "another-great-deployment-guid"
   168  
   169  		Context("When the deployment eventually deploys", func() {
   170  			BeforeEach(func() {
   171  				getDeploymentCallCount := 0
   172  
   173  				fakeCloudControllerClient.GetDeploymentStub = func(deploymentGuid string) (ccv3.Deployment, ccv3.Warnings, error) {
   174  					defer func() { getDeploymentCallCount++ }()
   175  					if getDeploymentCallCount == 0 {
   176  						return ccv3.Deployment{State: constant.DeploymentDeploying},
   177  							ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   178  							nil
   179  					} else {
   180  						return ccv3.Deployment{State: constant.DeploymentDeployed},
   181  							ccv3.Warnings{fmt.Sprintf("get-process-warning-%d", getDeploymentCallCount+2)},
   182  							nil
   183  					}
   184  				}
   185  			})
   186  
   187  			It("returns a nil error", func() {
   188  				err := actor.PollDeployment(myDeploymentGUID, warningsChannel)
   189  				funcDone <- nil
   190  				Expect(err).To(BeNil())
   191  				Expect(allWarnings).To(ConsistOf("get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   192  			})
   193  		})
   194  		Context("When the deployment is cancelled", func() {
   195  			BeforeEach(func() {
   196  				getDeploymentCallCount := 0
   197  
   198  				fakeCloudControllerClient.GetDeploymentStub = func(deploymentGuid string) (ccv3.Deployment, ccv3.Warnings, error) {
   199  					defer func() { getDeploymentCallCount++ }()
   200  					if getDeploymentCallCount == 0 {
   201  						return ccv3.Deployment{State: constant.DeploymentDeploying},
   202  							ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   203  							nil
   204  					} else {
   205  						return ccv3.Deployment{State: constant.DeploymentCanceled},
   206  							ccv3.Warnings{fmt.Sprintf("get-process-warning-%d", getDeploymentCallCount+2)},
   207  							nil
   208  					}
   209  				}
   210  			})
   211  			It("throws a deployment canceled error", func() {
   212  				err := actor.PollDeployment(myDeploymentGUID, warningsChannel)
   213  				funcDone <- nil
   214  				Expect(err).To(MatchError(errors.New("Deployment has been canceled")))
   215  				Expect(allWarnings).To(ConsistOf("get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   216  			})
   217  
   218  		})
   219  
   220  		Context("When waiting for the deployment to finish times out", func() {
   221  			BeforeEach(func() {
   222  				fakeConfig.StartupTimeoutReturns(time.Millisecond)
   223  				fakeConfig.PollingIntervalReturns(time.Millisecond * 2)
   224  				fakeCloudControllerClient.GetDeploymentReturns(ccv3.Deployment{State: constant.DeploymentDeploying}, ccv3.Warnings{"some-deployment-warning"}, nil)
   225  			})
   226  
   227  			It("Throws a timeout error", func() {
   228  				err := actor.PollDeployment(myDeploymentGUID, warningsChannel)
   229  				funcDone <- nil
   230  				Expect(err).To(MatchError(actionerror.StartupTimeoutError{}))
   231  				Expect(allWarnings).To(ConsistOf("some-deployment-warning"))
   232  			})
   233  		})
   234  	})
   235  
   236  	Describe("ZeroDowntimePollStart", func() {
   237  		var warningsChannel chan Warnings
   238  		var allWarnings Warnings
   239  		var funcDone chan interface{}
   240  
   241  		BeforeEach(func() {
   242  			warningsChannel = make(chan Warnings)
   243  			funcDone = make(chan interface{})
   244  			allWarnings = Warnings{}
   245  			go func() {
   246  				for {
   247  					select {
   248  					case warnings := <-warningsChannel:
   249  						allWarnings = append(allWarnings, warnings...)
   250  					case <-funcDone:
   251  						return
   252  					}
   253  				}
   254  			}()
   255  		})
   256  
   257  		Context("when getting the application processes fails", func() {
   258  			BeforeEach(func() {
   259  				fakeCloudControllerClient.GetApplicationProcessesReturns(nil, ccv3.Warnings{"get-app-warning-1", "get-app-warning-2"}, errors.New("some-error"))
   260  			})
   261  
   262  			It("returns the error and all warnings", func() {
   263  				err := actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   264  				funcDone <- nil
   265  				Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-app-warning-2"))
   266  				Expect(err).To(MatchError(errors.New("some-error")))
   267  			})
   268  		})
   269  
   270  		Context("when getting the application processes succeeds", func() {
   271  			var processes []ccv3.Process
   272  
   273  			BeforeEach(func() {
   274  				fakeConfig.StartupTimeoutReturns(time.Second)
   275  				fakeConfig.PollingIntervalReturns(0)
   276  				processes = []ccv3.Process{
   277  					{GUID: "web-guid", Type: "web"},
   278  					{GUID: "web-ish-guid", Type: "web-deployment-efg456"},
   279  				}
   280  			})
   281  
   282  			JustBeforeEach(func() {
   283  				fakeCloudControllerClient.GetApplicationProcessesReturns(
   284  					processes,
   285  					ccv3.Warnings{"get-app-warning-1"}, nil)
   286  			})
   287  
   288  			Context("when the polling times out", func() {
   289  				BeforeEach(func() {
   290  					fakeConfig.StartupTimeoutReturns(time.Millisecond)
   291  					fakeConfig.PollingIntervalReturns(time.Millisecond * 2)
   292  					fakeCloudControllerClient.GetProcessInstancesReturns(
   293  						[]ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}},
   294  						ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   295  						nil,
   296  					)
   297  				})
   298  
   299  				It("returns the timeout error", func() {
   300  					err := actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   301  					funcDone <- nil
   302  
   303  					Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   304  					Expect(err).To(MatchError(actionerror.StartupTimeoutError{}))
   305  				})
   306  
   307  				It("gets polling and timeout values from the config", func() {
   308  					actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   309  					funcDone <- nil
   310  
   311  					Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   312  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   313  				})
   314  			})
   315  
   316  			Context("when getting the process instances errors", func() {
   317  				BeforeEach(func() {
   318  					fakeCloudControllerClient.GetProcessInstancesReturns(
   319  						nil,
   320  						ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   321  						errors.New("some-error"),
   322  					)
   323  				})
   324  
   325  				It("returns the error", func() {
   326  					err := actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   327  					funcDone <- nil
   328  
   329  					Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   330  					Expect(err).To(MatchError("some-error"))
   331  				})
   332  			})
   333  
   334  			Context("when getting the process instances succeeds", func() {
   335  				var (
   336  					processInstanceCallCount  int
   337  					processInstancesCallGuids []string
   338  					initialInstanceStates     []ccv3.ProcessInstance
   339  					eventualInstanceStates    []ccv3.ProcessInstance
   340  					pollStartErr              error
   341  				)
   342  
   343  				BeforeEach(func() {
   344  					processInstanceCallCount = 0
   345  					processInstancesCallGuids = []string{}
   346  
   347  					fakeCloudControllerClient.GetProcessInstancesStub = func(processGuid string) ([]ccv3.ProcessInstance, ccv3.Warnings, error) {
   348  						processInstancesCallGuids = append(processInstancesCallGuids, processGuid)
   349  						defer func() { processInstanceCallCount++ }()
   350  						if processInstanceCallCount == 0 {
   351  							return initialInstanceStates,
   352  								ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   353  								nil
   354  						} else {
   355  							return eventualInstanceStates,
   356  								ccv3.Warnings{fmt.Sprintf("get-process-warning-%d", processInstanceCallCount+2)},
   357  								nil
   358  						}
   359  					}
   360  				})
   361  
   362  				Context("when there are no instances for the deploying process", func() {
   363  					BeforeEach(func() {
   364  						initialInstanceStates = []ccv3.ProcessInstance{}
   365  					})
   366  
   367  					It("should not return an error", func() {
   368  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   369  						funcDone <- nil
   370  
   371  						Expect(pollStartErr).NotTo(HaveOccurred())
   372  					})
   373  
   374  					It("should only call GetProcessInstances once before exiting", func() {
   375  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   376  						funcDone <- nil
   377  
   378  						Expect(processInstanceCallCount).To(Equal(1))
   379  					})
   380  
   381  					It("should return correct warnings", func() {
   382  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   383  						funcDone <- nil
   384  
   385  						Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   386  					})
   387  				})
   388  
   389  				Context("when the deploying process has at least one running instance by the second call", func() {
   390  					BeforeEach(func() {
   391  						initialInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}}
   392  						eventualInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceRunning}}
   393  					})
   394  
   395  					It("should not return an error", func() {
   396  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   397  						funcDone <- nil
   398  
   399  						Expect(pollStartErr).NotTo(HaveOccurred())
   400  					})
   401  
   402  					It("should call GetProcessInstances twice", func() {
   403  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   404  						funcDone <- nil
   405  
   406  						Expect(processInstanceCallCount).To(Equal(2))
   407  					})
   408  
   409  					It("should return correct warnings", func() {
   410  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   411  						funcDone <- nil
   412  
   413  						Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   414  					})
   415  
   416  					It("should only call GetProcessInstances for the webish process", func() {
   417  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   418  						funcDone <- nil
   419  
   420  						Expect(processInstancesCallGuids).To(ConsistOf("web-ish-guid", "web-ish-guid"))
   421  					})
   422  				})
   423  
   424  				Context("when there is no webish process", func() {
   425  					BeforeEach(func() {
   426  						fakeConfig.StartupTimeoutReturns(time.Second)
   427  						fakeConfig.PollingIntervalReturns(0)
   428  						processes = []ccv3.Process{
   429  							{GUID: "web-guid", Type: "web"},
   430  							{GUID: "worker-guid", Type: "worker"},
   431  						}
   432  					})
   433  
   434  					It("should not return an error", func() {
   435  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   436  						funcDone <- nil
   437  
   438  						Expect(pollStartErr).NotTo(HaveOccurred())
   439  					})
   440  
   441  					It("should call not call GetProcessInstances, because the deploy has already succeeded", func() {
   442  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   443  						funcDone <- nil
   444  
   445  						Expect(processInstanceCallCount).To(Equal(0))
   446  					})
   447  
   448  					It("should return correct warnings", func() {
   449  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   450  						funcDone <- nil
   451  
   452  						Expect(allWarnings).To(ConsistOf("get-app-warning-1"))
   453  					})
   454  				})
   455  
   456  				Context("when all of the instances have crashed by the second call", func() {
   457  					BeforeEach(func() {
   458  						initialInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}}
   459  						eventualInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceCrashed}, {State: constant.ProcessInstanceCrashed}, {State: constant.ProcessInstanceCrashed}}
   460  					})
   461  
   462  					It("should not return an error", func() {
   463  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   464  						funcDone <- nil
   465  
   466  						Expect(pollStartErr).NotTo(HaveOccurred())
   467  					})
   468  
   469  					It("should call GetProcessInstances twice", func() {
   470  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   471  						funcDone <- nil
   472  
   473  						Expect(processInstanceCallCount).To(Equal(2))
   474  					})
   475  
   476  					It("should return correct warnings", func() {
   477  						pollStartErr = actor.ZeroDowntimePollStart("some-guid", warningsChannel)
   478  						funcDone <- nil
   479  
   480  						Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   481  					})
   482  				})
   483  			})
   484  
   485  		})
   486  	})
   487  })