github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+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 buffer.Write([]byte("y\n")) 211 session = helpers.CFWithStdin(buffer, "v3-scale", appName, "-m", "64M") 212 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 213 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 214 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 215 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 216 Eventually(session).Should(Exit(0)) 217 218 updatedAppTable = helpers.ParseV3AppProcessTable(session.Out.Contents()) 219 Expect(updatedAppTable.Processes).To(HaveLen(2)) 220 221 processSummary = updatedAppTable.Processes[0] 222 instanceSummary = processSummary.Instances[0] 223 Expect(processSummary.Type).To(Equal("web")) 224 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/3`)) 225 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 64M`)) 226 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 227 228 By("scaling disk to 92M") 229 buffer = NewBuffer() 230 buffer.Write([]byte("y\n")) 231 session = helpers.CFWithStdin(buffer, "v3-scale", appName, "-k", "92M") 232 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 233 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 234 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 235 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 236 Eventually(session).Should(Exit(0)) 237 238 updatedAppTable = helpers.ParseV3AppProcessTable(session.Out.Contents()) 239 Expect(updatedAppTable.Processes).To(HaveLen(2)) 240 241 processSummary = updatedAppTable.Processes[0] 242 instanceSummary = processSummary.Instances[0] 243 Expect(processSummary.Type).To(Equal("web")) 244 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/3`)) 245 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 64M`)) 246 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 92M`)) 247 248 By("scaling to 0 instances") 249 session = helpers.CF("v3-scale", appName, "-i", "0") 250 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 251 Consistently(session).ShouldNot(Say("This will cause the app to restart")) 252 Consistently(session).ShouldNot(Say("Stopping")) 253 Consistently(session).ShouldNot(Say("Starting")) 254 Eventually(session).Should(Exit(0)) 255 256 updatedAppTable = helpers.ParseV3AppProcessTable(session.Out.Contents()) 257 Expect(updatedAppTable.Processes).To(BeEmpty()) 258 }) 259 260 When("the user chooses not to restart the app", func() { 261 It("cancels the scale", func() { 262 buffer := NewBuffer() 263 buffer.Write([]byte("n\n")) 264 session := helpers.CFWithStdin(buffer, "v3-scale", appName, "-i", "2", "-k", "90M") 265 Eventually(session).Should(Say("This will cause the app to restart")) 266 Consistently(session).ShouldNot(Say("Stopping")) 267 Consistently(session).ShouldNot(Say("Starting")) 268 Eventually(session).Should(Say("Scaling cancelled")) 269 Consistently(session).ShouldNot(Say("Waiting for app to start\\.\\.\\.")) 270 Eventually(session).Should(Exit(0)) 271 272 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 273 Expect(appTable.Processes).To(BeEmpty()) 274 }) 275 }) 276 }) 277 278 When("all scale option flags are provided", func() { 279 When("the app starts successfully", func() { 280 It("scales the app accordingly", func() { 281 buffer := NewBuffer() 282 buffer.Write([]byte("y\n")) 283 session := helpers.CFWithStdin(buffer, "v3-scale", appName, "-i", "2", "-k", "120M", "-m", "60M") 284 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 285 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 286 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 287 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 288 Eventually(session).Should(Exit(0)) 289 290 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 291 Expect(appTable.Processes).To(HaveLen(2)) 292 293 processSummary := appTable.Processes[0] 294 instanceSummary := processSummary.Instances[0] 295 Expect(processSummary.Type).To(Equal("web")) 296 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/2`)) 297 Expect(instanceSummary.State).To(MatchRegexp(`running|starting`)) 298 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 60M`)) 299 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 120M`)) 300 }) 301 }) 302 303 When("the app does not start successfully", func() { 304 It("scales the app and displays the app summary", func() { 305 buffer := NewBuffer() 306 buffer.Write([]byte("y\n")) 307 session := helpers.CFWithStdin(buffer, "v3-scale", appName, "-i", "2", "-k", "10M", "-m", "6M") 308 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 309 Eventually(session).Should(Say("This will cause the app to restart\\. Are you sure you want to scale %s\\? \\[yN\\]:", appName)) 310 Eventually(session).Should(Say("Stopping app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 311 Eventually(session).Should(Say("Starting app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 312 Eventually(session).Should(Exit(0)) 313 314 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 315 Expect(appTable.Processes).To(HaveLen(2)) 316 317 processSummary := appTable.Processes[0] 318 instanceSummary := processSummary.Instances[0] 319 Expect(processSummary.Type).To(Equal("web")) 320 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/2`)) 321 Expect(instanceSummary.State).To(MatchRegexp(`crashed`)) 322 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 6M`)) 323 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of 10M`)) 324 }) 325 }) 326 }) 327 328 PWhen("the provided scale options are the same as the existing scale properties", func() { 329 var ( 330 session *Session 331 currentInstances string 332 maxMemory string 333 maxDiskSize string 334 ) 335 336 BeforeEach(func() { 337 session = helpers.CF("v3-scale", appName) 338 Eventually(session).Should(Exit(0)) 339 340 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 341 instanceSummary := appTable.Processes[0].Instances[0] 342 currentInstances = string(len(appTable.Processes[0].Instances)) 343 maxMemory = strings.Fields(instanceSummary.Memory)[2] 344 maxDiskSize = strings.Fields(instanceSummary.Disk)[2] 345 }) 346 347 It("the action should be a no-op", func() { 348 session = helpers.CF("v3-scale", appName, "-i", currentInstances, "-m", maxMemory, "-k", maxDiskSize) 349 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 350 Consistently(session).ShouldNot(Say("This will cause the app to restart")) 351 Consistently(session).ShouldNot(Say("Stopping")) 352 Consistently(session).ShouldNot(Say("Starting")) 353 Consistently(session).ShouldNot(Say("Waiting for app to start")) 354 Eventually(session).Should(Exit(0)) 355 356 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 357 Expect(appTable.Processes).To(HaveLen(1)) 358 359 newProcessSummary := appTable.Processes[0] 360 newInstanceSummary := newProcessSummary.Instances[0] 361 Expect(newProcessSummary.Type).To(Equal("web")) 362 Expect(newProcessSummary.InstanceCount).To(MatchRegexp(fmt.Sprintf(`\d/%s`, currentInstances))) 363 Expect(newInstanceSummary.Memory).To(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]? of %s`, maxMemory))) 364 Expect(newInstanceSummary.Disk).To(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]? of %s`, maxDiskSize))) 365 }) 366 }) 367 368 When("the process flag is provided", func() { 369 It("scales the requested process", func() { 370 session := helpers.CF("v3-scale", appName, "-i", "2", "--process", "console") 371 Eventually(session).Should(Say("Scaling app %s in org %s / space %s as %s\\.\\.\\.", appName, orgName, spaceName, userName)) 372 Eventually(session).Should(Exit(0)) 373 374 appTable := helpers.ParseV3AppProcessTable(session.Out.Contents()) 375 Expect(appTable.Processes).To(HaveLen(2)) 376 377 processSummary := appTable.Processes[1] 378 instanceSummary := processSummary.Instances[0] 379 Expect(processSummary.Instances).To(HaveLen(2)) 380 Expect(processSummary.Type).To(Equal("console")) 381 Expect(processSummary.InstanceCount).To(MatchRegexp(`\d/2`)) 382 Expect(instanceSummary.Memory).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 383 Expect(instanceSummary.Disk).To(MatchRegexp(`\d+(\.\d+)?[KMG]? of \d+[KMG]`)) 384 }) 385 }) 386 }) 387 }) 388 389 When("invalid scale option values are provided", func() { 390 When("a negative value is passed to a flag argument", func() { 391 It("outputs an error message to the user, provides help text, and exits 1", func() { 392 session := helpers.CF("v3-scale", "some-app", "-i=-5") 393 Eventually(session.Err).Should(Say("Incorrect Usage: invalid argument for flag '-i' \\(expected int > 0\\)")) 394 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 395 Eventually(session).Should(Exit(1)) 396 397 session = helpers.CF("v3-scale", "some-app", "-k=-5") 398 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 399 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 400 Eventually(session).Should(Exit(1)) 401 402 session = helpers.CF("v3-scale", "some-app", "-m=-5") 403 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 404 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 405 Eventually(session).Should(Exit(1)) 406 }) 407 }) 408 409 When("a non-integer value is passed to a flag argument", func() { 410 It("outputs an error message to the user, provides help text, and exits 1", func() { 411 session := helpers.CF("v3-scale", "some-app", "-i", "not-an-integer") 412 Eventually(session.Err).Should(Say("Incorrect Usage: invalid argument for flag '-i' \\(expected int > 0\\)")) 413 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 414 Eventually(session).Should(Exit(1)) 415 416 session = helpers.CF("v3-scale", "some-app", "-k", "not-an-integer") 417 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 418 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 419 Eventually(session).Should(Exit(1)) 420 421 session = helpers.CF("v3-scale", "some-app", "-m", "not-an-integer") 422 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 423 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 424 Eventually(session).Should(Exit(1)) 425 }) 426 }) 427 428 When("the unit of measurement is not provided", func() { 429 It("outputs an error message to the user, provides help text, and exits 1", func() { 430 session := helpers.CF("v3-scale", "some-app", "-k", "9") 431 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 432 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 433 Eventually(session).Should(Exit(1)) 434 435 session = helpers.CF("v3-scale", "some-app", "-m", "7") 436 Eventually(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like M, MB, G, or GB")) 437 Eventually(session).Should(Say("cf v3-scale APP_NAME")) // help 438 Eventually(session).Should(Exit(1)) 439 }) 440 }) 441 }) 442 })