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