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 })