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