Skip to content

Commit

Permalink
Add run-task
Browse files Browse the repository at this point in the history
* update help text to suggest using ='s with -1 to avoid flag parsing problem

Signed-off-by: Ben Fuller <benjaminf@vmware.com>
Signed-off-by: Matthew Kocher <mkocher@vmware.com>
  • Loading branch information
rroberts2222 authored and mkocher committed Aug 19, 2022
1 parent 98c6bce commit d4c3e4b
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 30 deletions.
2 changes: 1 addition & 1 deletion command/v7/push_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ type PushCommand struct {
HealthCheckHTTPEndpoint string `long:"endpoint" description:"Valid path on the app for an HTTP health check. Only used when specifying --health-check-type=http"`
HealthCheckType flag.HealthCheckType `long:"health-check-type" short:"u" description:"Application health check type. Defaults to 'port'. 'http' requires a valid endpoint, for example, '/health'."`
Instances flag.Instances `long:"instances" short:"i" description:"Number of instances"`
LogRateLimit string `long:"log-rate-limit" short:"l" description:"Log rate limit per second, in bytes (e.g. 128B, 4K, 1M). -1 represents unlimited."`
LogRateLimit string `long:"log-rate-limit" short:"l" description:"Log rate limit per second, in bytes (e.g. 128B, 4K, 1M). -l=-1 represents unlimited"`
PathToManifest flag.ManifestPathWithExistenceCheck `long:"manifest" short:"f" description:"Path to manifest"`
Memory string `long:"memory" short:"m" description:"Memory limit (e.g. 256M, 1024M, 1G)"`
NoManifest bool `long:"no-manifest" description:"Ignore manifest file"`
Expand Down
20 changes: 12 additions & 8 deletions command/v7/run_task_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import (
type RunTaskCommand struct {
BaseCommand

RequiredArgs flag.RunTaskArgsV7 `positional-args:"yes"`
Command string `long:"command" short:"c" description:"The command to execute"`
Disk flag.Megabytes `short:"k" description:"Disk limit (e.g. 256M, 1024M, 1G)"`
Memory flag.Megabytes `short:"m" description:"Memory limit (e.g. 256M, 1024M, 1G)"`
Name string `long:"name" description:"Name to give the task (generated if omitted)"`
Process string `long:"process" description:"Process type to use as a template for command, memory, and disk for the created task."`
usage interface{} `usage:"CF_NAME run-task APP_NAME [--command COMMAND] [-k DISK] [-m MEMORY] [--name TASK_NAME] [--process PROCESS_TYPE]\n\nTIP:\n Use 'cf logs' to display the logs of the app and all its tasks. If your task name is unique, grep this command's output for the task name to view task-specific logs.\n\nEXAMPLES:\n CF_NAME run-task my-app --command \"bundle exec rake db:migrate\" --name migrate\n\n CF_NAME run-task my-app --process batch_job\n\n CF_NAME run-task my-app"`
relatedCommands interface{} `related_commands:"logs, tasks, terminate-task"`
RequiredArgs flag.RunTaskArgsV7 `positional-args:"yes"`
Command string `long:"command" short:"c" description:"The command to execute"`
Disk flag.Megabytes `short:"k" description:"Disk limit (e.g. 256M, 1024M, 1G)"`
LogRateLimit flag.BytesWithUnlimited `short:"l" description:"Log rate limit per second, in bytes (e.g. 128B, 4K, 1M). -l=-1 represents unlimited"`
Memory flag.Megabytes `short:"m" description:"Memory limit (e.g. 256M, 1024M, 1G)"`
Name string `long:"name" description:"Name to give the task (generated if omitted)"`
Process string `long:"process" description:"Process type to use as a template for command, memory, and disk for the created task."`
usage interface{} `usage:"CF_NAME run-task APP_NAME [--command COMMAND] [-k DISK] [-m MEMORY] [-l LOG_RATE_LIMIT] [--name TASK_NAME] [--process PROCESS_TYPE]\n\nTIP:\n Use 'cf logs' to display the logs of the app and all its tasks. If your task name is unique, grep this command's output for the task name to view task-specific logs.\n\nEXAMPLES:\n CF_NAME run-task my-app --command \"bundle exec rake db:migrate\" --name migrate\n\n CF_NAME run-task my-app --process batch_job\n\n CF_NAME run-task my-app"`
relatedCommands interface{} `related_commands:"logs, tasks, terminate-task"`
}

func (cmd RunTaskCommand) Execute(args []string) error {
Expand Down Expand Up @@ -59,6 +60,9 @@ func (cmd RunTaskCommand) Execute(args []string) error {
if cmd.Memory.IsSet {
inputTask.MemoryInMB = cmd.Memory.Value
}
if cmd.LogRateLimit.IsSet {
inputTask.LogRateLimitInBPS = cmd.LogRateLimit.Value
}
if cmd.Command == "" && cmd.Process == "" {
cmd.Process = "task"
}
Expand Down
43 changes: 43 additions & 0 deletions command/v7/run_task_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,49 @@ var _ = Describe("run-task Command", func() {
})
})

When("task log rate limit is provided", func() {
BeforeEach(func() {
cmd.Name = "some-task-name"
cmd.LogRateLimit = flag.BytesWithUnlimited{Value: 1024 * 5, IsSet: true}
fakeActor.RunTaskReturns(
resources.Task{
Name: "some-task-name",
SequenceID: 3,
},
v7action.Warnings{"get-application-warning-3"},
nil)
})

It("creates a new task and outputs all warnings", func() {
Expect(executeErr).ToNot(HaveOccurred())

Expect(fakeActor.GetApplicationByNameAndSpaceCallCount()).To(Equal(1))
appName, spaceGUID := fakeActor.GetApplicationByNameAndSpaceArgsForCall(0)
Expect(appName).To(Equal("some-app-name"))
Expect(spaceGUID).To(Equal("some-space-guid"))

Expect(fakeActor.RunTaskCallCount()).To(Equal(1))
appGUID, task := fakeActor.RunTaskArgsForCall(0)
Expect(appGUID).To(Equal("some-app-guid"))
Expect(task).To(Equal(resources.Task{
Command: "some command",
Name: "some-task-name",
LogRateLimitInBPS: 1024 * 5,
}))

Expect(testUI.Out).To(Say("Creating task for app some-app-name in org some-org / space some-space as some-user..."))
Expect(testUI.Out).To(Say("Task has been submitted successfully for execution."))
Expect(testUI.Out).To(Say("OK"))

Expect(testUI.Out).To(Say(`task name:\s+some-task-name`))
Expect(testUI.Out).To(Say(`task id:\s+3`))

Expect(testUI.Err).To(Say("get-application-warning-1"))
Expect(testUI.Err).To(Say("get-application-warning-2"))
Expect(testUI.Err).To(Say("get-application-warning-3"))
})
})

When("process is provided", func() {
BeforeEach(func() {
cmd.Name = "some-task-name"
Expand Down
2 changes: 1 addition & 1 deletion command/v7/scale_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type ScaleCommand struct {
Force bool `long:"force" short:"f" description:"Force restart of app without prompt"`
Instances flag.Instances `long:"instances" short:"i" required:"false" description:"Number of instances"`
DiskLimit flag.Megabytes `short:"k" required:"false" description:"Disk limit (e.g. 256M, 1024M, 1G)"`
LogRateLimit flag.BytesWithUnlimited `short:"l" required:"false" description:"Log rate limit (e.g. 256M, 1024M, 1G)"`
LogRateLimit flag.BytesWithUnlimited `short:"l" required:"false" description:"Log rate limit per second, in bytes (e.g. 128B, 4K, 1M). -l=-1 represents unlimited"`
MemoryLimit flag.Megabytes `short:"m" required:"false" description:"Memory limit (e.g. 256M, 1024M, 1G)"`
ProcessType string `long:"process" default:"web" description:"App process to scale"`
usage interface{} `usage:"CF_NAME scale APP_NAME [--process PROCESS] [-i INSTANCES] [-k DISK] [-m MEMORY] [-l LOG_RATE_LIMIT] [-f]\n\n Modifying the app's disk, memory, or log rate will cause the app to restart."`
Expand Down
2 changes: 1 addition & 1 deletion integration/helpers/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,6 @@ func WaitForLogRateLimitToTakeEffect(appName string, processIndex int, instanceI
session := CF("app", appName)
Eventually(session).Should(Exit(0))
appTable := ParseV3AppProcessTable(session.Out.Contents())
return appTable.Processes[processIndex].Instances[instanceIndex].Disk
return appTable.Processes[processIndex].Instances[instanceIndex].LogRate
}).Should(MatchRegexp(fmt.Sprintf(`\d+(\.\d+)?[KMG]?/s of %s/s`, expectedLogRateLimit)))
}
30 changes: 29 additions & 1 deletion integration/v7/isolated/run_task_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"path"

"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
"code.cloudfoundry.org/cli/integration/helpers"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
Expand All @@ -19,7 +20,7 @@ var _ = Describe("run-task command", func() {
Expect(session).To(Say("NAME:"))
Expect(session).To(Say(" run-task - Run a one-off task on an app"))
Expect(session).To(Say("USAGE:"))
Expect(session).To(Say(` cf run-task APP_NAME \[--command COMMAND\] \[-k DISK] \[-m MEMORY\] \[--name TASK_NAME\] \[--process PROCESS_TYPE\]`))
Expect(session).To(Say(` cf run-task APP_NAME \[--command COMMAND\] \[-k DISK] \[-m MEMORY\] \[-l LOG_RATE_LIMIT\] \[--name TASK_NAME\] \[--process PROCESS_TYPE\]`))
Expect(session).To(Say("TIP:"))
Expect(session).To(Say(" Use 'cf logs' to display the logs of the app and all its tasks. If your task name is unique, grep this command's output for the task name to view task-specific logs."))
Expect(session).To(Say("EXAMPLES:"))
Expand All @@ -29,6 +30,7 @@ var _ = Describe("run-task command", func() {
Expect(session).To(Say("OPTIONS:"))
Expect(session).To(Say(` --command, -c\s+The command to execute`))
Expect(session).To(Say(` -k Disk limit \(e\.g\. 256M, 1024M, 1G\)`))
Expect(session).To(Say(` -l Log rate limit per second, in bytes \(e\.g\. 128B, 4K, 1M\). -l=-1 represents unlimited`))
Expect(session).To(Say(` -m Memory limit \(e\.g\. 256M, 1024M, 1G\)`))
Expect(session).To(Say(` --name Name to give the task \(generated if omitted\)`))
Expect(session).To(Say(` --process Process type to use as a template for command, memory, and disk for the created task`))
Expand Down Expand Up @@ -130,6 +132,32 @@ var _ = Describe("run-task command", func() {
})
})

When("log rate limit is provided", func() {
BeforeEach(func() {
helpers.SkipIfVersionLessThan(ccversion.MinVersionLogRateLimitingV3)
})
When("the provided log rate limit is invalid", func() {
It("displays error and exits 1", func() {
session := helpers.CF("run-task", appName, "--command", "echo hi", "-l", "invalid")
Eventually(session).Should(Exit(1))
Expect(session.Err).Should(Say("Byte quantity must be an integer with a unit of measurement like B, K, KB, M, MB, G, or GB"))

})
})

When("the provided log rate limit is valid", func() {
It("runs the task with the provided log rate limit", func() {
logRateLimit := 1024
session := helpers.CF("run-task", appName, "--command", "echo hi", "-l", fmt.Sprintf("%dB", logRateLimit))
Eventually(session).Should(Exit(0))

session = helpers.CF("tasks", appName, "-v")
Eventually(session).Should(Exit(0))
Expect(session).To(Say("\"log_rate_limit_in_bytes_per_second\": %d", logRateLimit))
})
})
})

When("task memory is provided", func() {
When("the provided memory is invalid", func() {
It("displays error and exits 1", func() {
Expand Down
15 changes: 10 additions & 5 deletions integration/v7/isolated/scale_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
. "code.cloudfoundry.org/cli/cf/util/testhelpers/matchers"

"code.cloudfoundry.org/cli/integration/helpers"
Expand All @@ -30,13 +31,13 @@ var _ = Describe("scale command", func() {

Describe("help", func() {
When("--help flag is set", func() {
FIt("appears in cf help -a", func() {
It("appears in cf help -a", func() {
session := helpers.CF("help", "-a")
Eventually(session).Should(Exit(0))
Expect(session).To(HaveCommandInCategoryWithDescription("scale", "APPS", "Change or view the instance count, disk space limit, memory limit, and log rate limit for an app"))
})

FIt("displays command usage to output", func() {
It("displays command usage to output", func() {
session := helpers.CF("scale", "--help")

Eventually(session).Should(Say("NAME:"))
Expand All @@ -50,7 +51,7 @@ var _ = Describe("scale command", func() {
Eventually(session).Should(Say(`-f\s+Force restart of app without prompt`))
Eventually(session).Should(Say(`-i\s+Number of instances`))
Eventually(session).Should(Say(`-k\s+Disk limit \(e\.g\. 256M, 1024M, 1G\)`))
Eventually(session).Should(Say(`-l\s+Log rate limit \(e\.g\. 256M, 1024M, 1G\)`))
Eventually(session).Should(Say(`-l\s+Log rate limit per second, in bytes \(e\.g\. 128B, 4K, 1M\). -l=-1 represents unlimited`))
Eventually(session).Should(Say(`-m\s+Memory limit \(e\.g\. 256M, 1024M, 1G\)`))
Eventually(session).Should(Say(`--process\s+App process to scale \(Default: web\)`))

Expand Down Expand Up @@ -231,6 +232,10 @@ var _ = Describe("scale command", func() {
})

When("Scaling the log rate limit", func() {
BeforeEach(func() {
helpers.SkipIfVersionLessThan(ccversion.MinVersionLogRateLimitingV3)
})

It("scales log rate limit to 1M", func() {
buffer := NewBuffer()
_, err := buffer.Write([]byte("y\n"))
Expand All @@ -243,7 +248,7 @@ var _ = Describe("scale command", func() {
Expect(session).To(Say(`Starting app %s in org %s / space %s as %s\.\.\.`, appName, orgName, spaceName, userName))
Expect(session).To(Say(`Instances starting\.\.\.`))

helpers.WaitForLogRateLimitToTakeEffect(appName, 0, 0, false, "512M")
helpers.WaitForLogRateLimitToTakeEffect(appName, 0, 0, false, "1M")
})

When("-f flag provided", func() {
Expand All @@ -252,7 +257,7 @@ var _ = Describe("scale command", func() {
Eventually(session).Should(Exit(0))
Expect(session).To(Say("Scaling app %s in org %s / space %s as %s...", appName, orgName, spaceName, userName))

helpers.WaitForLogRateLimitToTakeEffect(appName, 0, 0, false, "512M")
helpers.WaitForLogRateLimitToTakeEffect(appName, 0, 0, false, "1M")
})
})
})
Expand Down
2 changes: 1 addition & 1 deletion integration/v7/push/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ var _ = Describe("help", func() {
Eventually(session).Should(Say(`--endpoint`))
Eventually(session).Should(Say(`--health-check-type, -u`))
Eventually(session).Should(Say(`--instances, -i`))
Eventually(session).Should(Say(`--log-rate-limit, -l\s+Log rate limit per second, in bytes \(e.g. 128B, 4K, 1M\). -1 represents unlimited.`))
Eventually(session).Should(Say(`--log-rate-limit, -l\s+Log rate limit per second, in bytes \(e.g. 128B, 4K, 1M\). -l=-1 represents unlimited`))
Eventually(session).Should(Say(`--manifest, -f`))
Eventually(session).Should(Say(`--memory, -m`))
Eventually(session).Should(Say(`--no-manifest`))
Expand Down
13 changes: 1 addition & 12 deletions integration/v7/push/log_rate_limit_flag_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package push

import (
"time"

"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
"code.cloudfoundry.org/cli/integration/helpers"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gbytes"
. "github.com/onsi/gomega/gexec"
)

Expand All @@ -32,15 +29,7 @@ var _ = Describe("push with log rate limit flag", func() {
Eventually(session).Should(Exit(0))
})

time.Sleep(5 * time.Second)
session := helpers.CF("app", appName)
Eventually(session).Should(Say(`name:\s+%s`, appName))
Eventually(session).Should(Say(`last uploaded:\s+%s`, helpers.ReadableDateTimeRegex))
//TODO: check output of push command
// Eventually(session).Should(Say(`log rate usage per second:\s+5K`))
// Eventually(session).Should(Say(`\s+state\s+since\s+cpu\s+memory\s+disk\s+log rate per second`))
Eventually(session).Should(Say(`#0\s+running\s+\d{4}-[01]\d-[0-3]\dT[0-2][0-9]:[0-5]\d:[0-5]\dZ`))
Eventually(session).Should(Exit(0))
helpers.WaitForLogRateLimitToTakeEffect(appName, 0, 0, false, "5K")
})
})
})
2 changes: 2 additions & 0 deletions resources/task_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ type Task struct {
DiskInMB uint64 `json:"disk_in_mb,omitempty"`
// GUID represents the unique task identifier.
GUID string `json:"guid,omitempty"`
// LogRateLimitInBPS represents the log rate limit in bytes allocated for the task.
LogRateLimitInBPS int `json:"log_rate_limit_in_bytes_per_second,omitempty"`
// MemoryInMB represents the memory in MB allocated for the task.
MemoryInMB uint64 `json:"memory_in_mb,omitempty"`
// Name represents the name of the task.
Expand Down

0 comments on commit d4c3e4b

Please sign in to comment.