github.com/swisscom/cloudfoundry-cli@v7.1.0+incompatible/integration/v6/experimental/v3_scale_command_test.go (about) 1 package experimental 2 3 import ( 4 "fmt" 5 "strings" 6 7 "code.cloudfoundry.org/cli/integration/helpers" 8 . "github.com/onsi/ginkgo" 9 . "github.com/onsi/gomega" 10 . "github.com/onsi/gomega/gbytes" 11 . "github.com/onsi/gomega/gexec" 12 ) 13 14 var _ = Describe("v3-scale command", func() { 15 var ( 16 orgName string 17 spaceName string 18 appName string 19 userName string 20 ) 21 22 BeforeEach(func() { 23 orgName = helpers.NewOrgName() 24 spaceName = helpers.NewSpaceName() 25 appName = helpers.PrefixedRandomName("app") 26 userName, _ = helpers.GetCredentials() 27 }) 28 29 Describe("help", func() { 30 When("--help flag is set", func() { 31 It("displays command usage to output", func() { 32 session := helpers.CF("v3-scale", "--help") 33 34 Eventually(session).Should(Say("NAME:")) 35 Eventually(session).Should(Say("v3-scale - Change or view the instance count, disk space limit, and memory limit for an app")) 36 37 Eventually(session).Should(Say("USAGE:")) 38 Eventually(session).Should(Say(`cf v3-scale APP_NAME \[--process PROCESS\] \[-i INSTANCES\] \[-k DISK\] \[-m MEMORY\]`)) 39 40 Eventually(session).Should(Say("OPTIONS:")) 41 Eventually(session).Should(Say(`-f\s+Force restart of app without prompt`)) 42 Eventually(session).Should(Say(`-i\s+Number of instances`)) 43 Eventually(session).Should(Say(`-k\s+Disk limit \(e\.g\. 256M, 1024M, 1G\)`)) 44 Eventually(session).Should(Say(`-m\s+Memory limit \(e\.g\. 256M, 1024M, 1G\)`)) 45 Eventually(session).Should(Say(`--process\s+App process to scale \(Default: web\)`)) 46 47 Eventually(session).Should(Say("ENVIRONMENT:")) 48 Eventually(session).Should(Say(`CF_STARTUP_TIMEOUT=5\s+Max wait time for app instance startup, in minutes`)) 49 50 Eventually(session).Should(Exit(0)) 51 }) 52 }) 53 }) 54 55 It("displays the experimental warning", func() { 56 session := helpers.CF("v3-scale", appName) 57 Eventually(session.Err).Should(Say("This command is in EXPERIMENTAL stage and may change without notice")) 58 Eventually(session).Should(Exit()) 59 }) 60 61 When("the environment is not setup correctly", func() { 62 When("no API endpoint is set", func() { 63 BeforeEach(func() { 64 helpers.UnsetAPI() 65 }) 66 67 It("fails with no API endpoint set message", func() { 68 session := helpers.CF("v3-scale", appName) 69 Eventually(session).Should(Say("FAILED")) 70 Eventually(session.Err).Should(Say(`No API endpoint set\. Use 'cf login' or 'cf api' to target an endpoint\.`)) 71 Eventually(session).Should(Exit(1)) 72 }) 73 }) 74 75 When("not logged in", func() { 76 BeforeEach(func() { 77 helpers.LogoutCF() 78 }) 79 80 It("fails with not logged in message", func() { 81 session := helpers.CF("v3-scale", appName) 82 Eventually(session).Should(Say("FAILED")) 83 Eventually(session.Err).Should(Say(`Not logged in\. Use 'cf login' or 'cf login --sso' to log in\.`)) 84 Eventually(session).Should(Exit(1)) 85 }) 86 }) 87 88 When("there is no org set", func() { 89 BeforeEach(func() { 90 helpers.LogoutCF() 91 helpers.LoginCF() 92 }) 93 94 It("fails with no org targeted error message", func() { 95 session := helpers.CF("v3-scale", appName) 96 Eventually(session).Should(Say("FAILED")) 97 Eventually(session.Err).Should(Say(`No org targeted, use 'cf target -o ORG' to target an org\.`)) 98 Eventually(session).Should(Exit(1)) 99 }) 100 }) 101 102 When("there is no space set", func() { 103 BeforeEach(func() { 104 helpers.LogoutCF() 105 helpers.LoginCF() 106 helpers.TargetOrg(ReadOnlyOrg) 107 }) 108 109 It("fails with no space targeted error message", func() { 110 session := helpers.CF("v3-scale", appName) 111 Eventually(session).Should(Say("FAILED")) 112 Eventually(session.Err).Should(Say(`No space targeted, use 'cf target -s SPACE' to target a space\.`)) 113 Eventually(session).Should(Exit(1)) 114 }) 115 }) 116 }) 117 118 When("the environment is set up correctly", func() { 119 BeforeEach(func() { 120 helpers.SetupCF(orgName, spaceName) 121 }) 122 123 AfterEach(func() { 124 helpers.QuickDeleteOrg(orgName) 125 }) 126 127 When("the app name is not provided", func() { 128 It("tells the user that the app name is required, prints help text, and exits 1", func() { 129 session := helpers.CF("v3-scale") 130 131 Eventually(session.Err).Should(Say("Incorrect Usage: the required argument `APP_NAME` was not provided")) 132 Eventually(session).Should(Say("NAME:")) 133 Eventually(session).Should(Exit(1)) 134 }) 135 }) 136 137 When("the app does not exist", func() { 138 It("displays app not found and exits 1", func() { 139 invalidAppName := "invalid-app-name" 140 session := helpers.CF("v3-scale", invalidAppName) 141 Eventually(session.Err).Should(Say("App '%s' not found", invalidAppName)) 142 Eventually(session).Should(Say("FAILED")) 143 Eventually(session).Should(Exit(1)) 144 }) 145 }) 146 147 When("the app exists", func() { 148 BeforeEach(func() { 149 helpers.WithProcfileApp(func(appDir string) { 150 Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir}, "v3-push", appName)).Should(Exit(0)) 151 }) 152 }) 153 154 When("scale option flags are not provided", func() { 155 It("displays the current scale properties for all processes", func() { 156 session := helpers.CF("v3-scale", appName) 157 158 Eventually(session).Should(Say(`Showing current scale of app %s in org %s / space %s as %s\.\.\.`, appName, orgName, spaceName, userName)) 159 Consistently(session).ShouldNot(Say("Scaling")) 160 Consistently(session).ShouldNot(Say("This will cause the app to restart")) 161 Consistently(session).ShouldNot(Say("Stopping")) 162 Consistently(session).ShouldNot(Say("Starting")) 163 Consistently(session).ShouldNot(Say("Waiting")) 164 Eventually(session).Should(Exit(0)) 165 166 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 167 Expect(len(appTable.Processes)).To(Equal(2)) 168 169 processSummary := appTable.Processes[0] 170 Expect(processSummary.Type).To(Equal("web")) 171 Expect(processSummary.InstanceCount).To(Equal("1/1")) 172 173 instanceSummary := processSummary.Instances[0] 174 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 175 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 176 177 Expect(appTable.Processes[1].Type).To(Equal("console")) 178 Expect(appTable.Processes[1].InstanceCount).To(Equal("0/0")) 179 }) 180 }) 181 182 When("only one scale option flag is provided", func() { 183 It("scales the app accordingly", func() { 184 By("verifying we start with a single instance") 185 session := helpers.CF("v3-scale", appName) 186 Eventually(session).Should(Exit(0)) 187 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 188 Expect(appTable.Processes).To(HaveLen(2)) 189 190 By("scaling to 3 instances") 191 session = helpers.CF("v3-scale", appName, "-i", "3") 192 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 193 Consistently(session).ShouldNot(Say("This will cause the app to restart")) 194 Consistently(session).ShouldNot(Say("Stopping")) 195 Consistently(session).ShouldNot(Say("Starting")) 196 Eventually(session).Should(Exit(0)) 197 198 updatedAppTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 199 Expect(updatedAppTable.Processes).To(HaveLen(2)) 200 201 processSummary := updatedAppTable.Processes[0] 202 instanceSummary := processSummary.Instances[0] 203 Expect(processSummary.Type).To(Equal("web")) 204 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/3`)) 205 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 206 207 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 208 By("scaling memory to 64M") 209 buffer := NewBuffer() 210 211 _, err := buffer.Write([]byte("y\n")) 212 Expect(err).NotTo(HaveOccurred()) 213 214 session = helpers.CFWithStdin(buffer, "v3-scale", appName, "-m", "64M") 215 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 216 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 217 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 218 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 219 Eventually(session).Should(Exit(0)) 220 221 updatedAppTable = helpers.ParseV3AppProcessTable(session.Out.Contents()) 222 Expect(updatedAppTable.Processes).To(HaveLen(2)) 223 224 processSummary = updatedAppTable.Processes[0] 225 instanceSummary = processSummary.Instances[0] 226 Expect(processSummary.Type).To(Equal("web")) 227 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/3`)) 228 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 64M`)) 229 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 230 231 By("scaling disk to 92M") 232 buffer = NewBuffer() 233 234 _, err = buffer.Write([]byte("y\n")) 235 Expect(err).NotTo(HaveOccurred()) 236 237 session = helpers.CFWithStdin(buffer, "v3-scale", appName, "-k", "92M") 238 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 239 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 240 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 241 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 242 Eventually(session).Should(Exit(0)) 243 244 updatedAppTable = helpers.ParseV3AppProcessTable(session.Out.Contents()) 245 Expect(updatedAppTable.Processes).To(HaveLen(2)) 246 247 processSummary = updatedAppTable.Processes[0] 248 instanceSummary = processSummary.Instances[0] 249 Expect(processSummary.Type).To(Equal("web")) 250 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/3`)) 251 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 64M`)) 252 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 92M`)) 253 254 By("scaling to 0 instances") 255 session = helpers.CF("v3-scale", appName, "-i", "0") 256 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 257 Consistently(session).ShouldNot(Say("This will cause the app to restart")) 258 Consistently(session).ShouldNot(Say("Stopping")) 259 Consistently(session).ShouldNot(Say("Starting")) 260 Eventually(session).Should(Exit(0)) 261 262 updatedAppTable = helpers.ParseV3AppProcessTable(session.Out.Contents()) 263 Expect(updatedAppTable.Processes).To(BeEmpty()) 264 }) 265 266 When("the user chooses not to restart the app", func() { 267 It("cancels the scale", func() { 268 buffer := NewBuffer() 269 270 _, err := buffer.Write([]byte("n\n")) 271 Expect(err).NotTo(HaveOccurred()) 272 273 session := helpers.CFWithStdin(buffer, "v3-scale", appName, "-i", "2", "-k", "90M") 274 Eventually(session).Should(Say("This will cause the app to restart")) 275 Consistently(session).ShouldNot(Say("Stopping")) 276 Consistently(session).ShouldNot(Say("Starting")) 277 Eventually(session).Should(Say("Scaling cancelled")) 278 Consistently(session).ShouldNot(Say("Waiting for app to start\\.\\.\\.")) 279 Eventually(session).Should(Exit(0)) 280 281 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 282 Expect(appTable.Processes).To(BeEmpty()) 283 }) 284 }) 285 }) 286 287 When("all scale option flags are provided", func() { 288 When("the app starts successfully", func() { 289 It("scales the app accordingly", func() { 290 buffer := NewBuffer() 291 292 _, err := buffer.Write([]byte("y\n")) 293 Expect(err).NotTo(HaveOccurred()) 294 295 session := helpers.CFWithStdin(buffer, "v3-scale", appName, "-i", "2", "-k", "120M", "-m", "60M") 296 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 297 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 298 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 299 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 300 Eventually(session).Should(Exit(0)) 301 302 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 303 Expect(appTable.Processes).To(HaveLen(2)) 304 305 processSummary := appTable.Processes[0] 306 instanceSummary := processSummary.Instances[0] 307 Expect(processSummary.Type).To(Equal("web")) 308 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/2`)) 309 Expect(instanceSummary.State).To(MatchRegexp(`running|starting`)) 310 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 60M`)) 311 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 120M`)) 312 }) 313 }) 314 315 When("the app does not start successfully", func() { 316 It("scales the app and displays the app summary", func() { 317 buffer := NewBuffer() 318 319 _, err := buffer.Write([]byte("y\n")) 320 Expect(err).NotTo(HaveOccurred()) 321 322 session := helpers.CFWithStdin(buffer, "v3-scale", appName, "-i", "2", "-k", "10M", "-m", "6M") 323 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 324 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 325 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 326 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 327 Eventually(session).Should(Exit(0)) 328 329 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 330 Expect(appTable.Processes).To(HaveLen(2)) 331 332 processSummary := appTable.Processes[0] 333 instanceSummary := processSummary.Instances[0] 334 Expect(processSummary.Type).To(Equal("web")) 335 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/2`)) 336 Expect(instanceSummary.State).To(MatchRegexp(`crashed`)) 337 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 6M`)) 338 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 10M`)) 339 }) 340 }) 341 }) 342 343 PWhen("the provided scale options are the same as the existing scale properties", func() { 344 var ( 345 session *Session 346 currentInstances string 347 maxMemory string 348 maxDiskSize string 349 ) 350 351 BeforeEach(func() { 352 session = helpers.CF("v3-scale", appName) 353 Eventually(session).Should(Exit(0)) 354 355 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 356 instanceSummary := appTable.Processes[0].Instances[0] 357 currentInstances = string(len(appTable.Processes[0].Instances)) 358 maxMemory = strings.Fields(instanceSummary.Memory)[2] 359 maxDiskSize = strings.Fields(instanceSummary.Disk)[2] 360 }) 361 362 It("the action should be a no-op", func() { 363 session = helpers.CF("v3-scale", appName, "-i", currentInstances, "-m", maxMemory, "-k", maxDiskSize) 364 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 365 Consistently(session).ShouldNot(Say("This will cause the app to restart")) 366 Consistently(session).ShouldNot(Say("Stopping")) 367 Consistently(session).ShouldNot(Say("Starting")) 368 Consistently(session).ShouldNot(Say("Waiting for app to start")) 369 Eventually(session).Should(Exit(0)) 370 371 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 372 Expect(appTable.Processes).To(HaveLen(1)) 373 374 newProcessSummary := appTable.Processes[0] 375 newInstanceSummary := newProcessSummary.Instances[0] 376 Expect(newProcessSummary.Type).To(Equal("web")) 377 Expect(newProcessSummary.InstanceCount).To(MatchRegexp(fmt.Sprintf(`\d/%s`, currentInstances))) 378 Expect(newInstanceSummary.Memory).To(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]? of %s`, maxMemory))) 379 Expect(newInstanceSummary.Disk).To(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]? of %s`, maxDiskSize))) 380 }) 381 }) 382 383 When("the process flag is provided", func() { 384 It("scales the requested process", func() { 385 session := helpers.CF("v3-scale", appName, "-i", "2", "--process", "console") 386 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 387 Eventually(session).Should(Exit(0)) 388 389 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 390 Expect(appTable.Processes).To(HaveLen(2)) 391 392 processSummary := appTable.Processes[1] 393 instanceSummary := processSummary.Instances[0] 394 Expect(processSummary.Instances).To(HaveLen(2)) 395 Expect(processSummary.Type).To(Equal("console")) 396 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/2`)) 397 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 398 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 399 }) 400 }) 401 }) 402 }) 403 404 When("invalid scale option values are provided", func() { 405 When("a negative value is passed to a flag argument", func() { 406 It("outputs an error message to the user, provides help text, and exits 1", func() { 407 session := helpers.CF("v3-scale", "some-app", "-i=-5") 408 Eventually(session.Err).Should(Say("Incorrect Usage: invalid argument for flag '-i' \\(expected int > 0\\)")) 409 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 410 Eventually(session).Should(Exit(1)) 411 412 session = helpers.CF("v3-scale", "some-app", "-k=-5") 413 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 414 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 415 Eventually(session).Should(Exit(1)) 416 417 session = helpers.CF("v3-scale", "some-app", "-m=-5") 418 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 419 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 420 Eventually(session).Should(Exit(1)) 421 }) 422 }) 423 424 When("a non-integer value is passed to a flag argument", func() { 425 It("outputs an error message to the user, provides help text, and exits 1", func() { 426 session := helpers.CF("v3-scale", "some-app", "-i", "not-an-integer") 427 Eventually(session.Err).Should(Say("Incorrect Usage: invalid argument for flag '-i' \\(expected int > 0\\)")) 428 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 429 Eventually(session).Should(Exit(1)) 430 431 session = helpers.CF("v3-scale", "some-app", "-k", "not-an-integer") 432 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 433 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 434 Eventually(session).Should(Exit(1)) 435 436 session = helpers.CF("v3-scale", "some-app", "-m", "not-an-integer") 437 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 438 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 439 Eventually(session).Should(Exit(1)) 440 }) 441 }) 442 443 When("the unit of measurement is not provided", func() { 444 It("outputs an error message to the user, provides help text, and exits 1", func() { 445 session := helpers.CF("v3-scale", "some-app", "-k", "9") 446 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 447 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 448 Eventually(session).Should(Exit(1)) 449 450 session = helpers.CF("v3-scale", "some-app", "-m", "7") 451 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 452 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 453 Eventually(session).Should(Exit(1)) 454 }) 455 }) 456 }) 457 })