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