diff --git a/cmd/factory.go b/cmd/factory.go index 2a3468d1f..4d8a79194 100644 --- a/cmd/factory.go +++ b/cmd/factory.go @@ -45,6 +45,10 @@ func (f Factory) New(args []string) (Cmd, error) { opts.Deployment = cmd.BoshOpts.DeploymentOpt } + if opts, ok := command.(*TasksOpts); ok { + opts.Deployment = cmd.BoshOpts.DeploymentOpt + } + if len(extraArgs) > 0 { errMsg := "Command '%T' does not support extra arguments: %s" return fmt.Errorf(errMsg, command, strings.Join(extraArgs, ", ")) diff --git a/cmd/factory_test.go b/cmd/factory_test.go index 419c2fbbb..a843920a4 100644 --- a/cmd/factory_test.go +++ b/cmd/factory_test.go @@ -186,6 +186,16 @@ var _ = Describe("Factory", func() { }) }) + Describe("tasks command", func() { + It("is passed the deployment flag", func() { + cmd, err := factory.New([]string{"tasks", "--deployment", "deployment"}) + Expect(err).ToNot(HaveOccurred()) + + opts := cmd.Opts.(*TasksOpts) + Expect(opts.Deployment).To(Equal("deployment")) + }) + }) + Describe("help options", func() { It("has a help flag", func() { _, err := factory.New([]string{"--help"}) diff --git a/cmd/opts.go b/cmd/opts.go index c2777e4cf..c76fd8080 100644 --- a/cmd/opts.go +++ b/cmd/opts.go @@ -206,9 +206,9 @@ type TaskArgs struct { } type TasksOpts struct { - Recent *int `long:"recent" short:"r" description:"Number of tasks to show" optional:"true" optional-value:"30"` - - All bool `long:"all" short:"a" description:"Include all task types (ssh, logs, vms, etc)"` + Recent *int `long:"recent" short:"r" description:"Number of tasks to show" optional:"true" optional-value:"30"` + All bool `long:"all" short:"a" description:"Include all task types (ssh, logs, vms, etc)"` + Deployment string cmd } diff --git a/cmd/task.go b/cmd/task.go index cffcfe5d0..691903881 100644 --- a/cmd/task.go +++ b/cmd/task.go @@ -31,7 +31,7 @@ func (c TaskCmd) Run(opts TaskOpts) error { var err error if opts.Args.ID == 0 { - tasks, err := c.director.CurrentTasks(opts.All) + tasks, err := c.director.CurrentTasks(boshdir.TasksFilter{All: opts.All}) if err != nil { return err } diff --git a/cmd/task_test.go b/cmd/task_test.go index fa6387863..8442ff55e 100644 --- a/cmd/task_test.go +++ b/cmd/task_test.go @@ -242,13 +242,13 @@ var _ = Describe("TaskCmd", func() { It("filters tasks based on 'all' option", func() { err := act() Expect(err).ToNot(HaveOccurred()) - Expect(director.CurrentTasksArgsForCall(0)).To(Equal(false)) + Expect(director.CurrentTasksArgsForCall(0)).To(Equal(boshdir.TasksFilter{})) opts.All = true err = act() Expect(err).ToNot(HaveOccurred()) - Expect(director.CurrentTasksArgsForCall(1)).To(Equal(true)) + Expect(director.CurrentTasksArgsForCall(1)).To(Equal(boshdir.TasksFilter{All: true})) }) It("returns error if there are no current tasks", func() { diff --git a/cmd/tasks.go b/cmd/tasks.go index f54531d50..1b6f7578f 100644 --- a/cmd/tasks.go +++ b/cmd/tasks.go @@ -16,10 +16,15 @@ func NewTasksCmd(ui boshui.UI, director boshdir.Director) TasksCmd { } func (c TasksCmd) Run(opts TasksOpts) error { + filter := boshdir.TasksFilter{ + All: opts.All, + Deployment: opts.Deployment, + } + if opts.Recent != nil { - return c.printTable(c.director.RecentTasks(*opts.Recent, opts.All)) + return c.printTable(c.director.RecentTasks(*opts.Recent, filter)) } - return c.printTable(c.director.CurrentTasks(opts.All)) + return c.printTable(c.director.CurrentTasks(filter)) } func (c TasksCmd) printTable(tasks []boshdir.Task, err error) error { diff --git a/cmd/tasks_test.go b/cmd/tasks_test.go index 20163c818..a71c3a962 100644 --- a/cmd/tasks_test.go +++ b/cmd/tasks_test.go @@ -113,18 +113,22 @@ var _ = Describe("TasksCmd", func() { })) }) - It("filters tasks based on 'all' option", func() { + It("filters tasks based options", func() { director.CurrentTasksReturns(nil, nil) err := act() Expect(err).ToNot(HaveOccurred()) - Expect(director.CurrentTasksArgsForCall(0)).To(Equal(false)) + Expect(director.CurrentTasksArgsForCall(0)).To(Equal(boshdir.TasksFilter{})) opts.All = true + opts.Deployment = "deployment" err = act() Expect(err).ToNot(HaveOccurred()) - Expect(director.CurrentTasksArgsForCall(1)).To(Equal(true)) + Expect(director.CurrentTasksArgsForCall(1)).To(Equal(boshdir.TasksFilter{ + All: true, + Deployment: "deployment", + })) }) It("returns error if tasks cannot be retrieved", func() { @@ -216,18 +220,22 @@ var _ = Describe("TasksCmd", func() { })) }) - It("filters tasks based on 'all' option", func() { + It("filters tasks based on options", func() { director.RecentTasksReturns(nil, nil) Expect(act()).ToNot(HaveOccurred()) - _, includeAll := director.RecentTasksArgsForCall(0) - Expect(includeAll).To(Equal(false)) + _, filter := director.RecentTasksArgsForCall(0) + Expect(filter).To(Equal(boshdir.TasksFilter{})) opts.All = true + opts.Deployment = "deployment" Expect(act()).ToNot(HaveOccurred()) - _, includeAll = director.RecentTasksArgsForCall(1) - Expect(includeAll).To(Equal(true)) + _, filter = director.RecentTasksArgsForCall(1) + Expect(filter).To(Equal(boshdir.TasksFilter{ + All: true, + Deployment: "deployment", + })) }) It("requests specific number of tasks", func() { diff --git a/director/fakes/fake_director.go b/director/fakes/fake_director.go index 93dde19e8..b1feab8c3 100644 --- a/director/fakes/fake_director.go +++ b/director/fakes/fake_director.go @@ -30,20 +30,20 @@ type FakeDirector struct { result1 []director.Lock result2 error } - CurrentTasksStub func(includeAll bool) ([]director.Task, error) + CurrentTasksStub func(director.TasksFilter) ([]director.Task, error) currentTasksMutex sync.RWMutex currentTasksArgsForCall []struct { - includeAll bool + arg1 director.TasksFilter } currentTasksReturns struct { result1 []director.Task result2 error } - RecentTasksStub func(limit int, includeAll bool) ([]director.Task, error) + RecentTasksStub func(int, director.TasksFilter) ([]director.Task, error) recentTasksMutex sync.RWMutex recentTasksArgsForCall []struct { - limit int - includeAll bool + arg1 int + arg2 director.TasksFilter } recentTasksReturns struct { result1 []director.Task @@ -342,14 +342,14 @@ func (fake *FakeDirector) LocksReturns(result1 []director.Lock, result2 error) { }{result1, result2} } -func (fake *FakeDirector) CurrentTasks(includeAll bool) ([]director.Task, error) { +func (fake *FakeDirector) CurrentTasks(arg1 director.TasksFilter) ([]director.Task, error) { fake.currentTasksMutex.Lock() fake.currentTasksArgsForCall = append(fake.currentTasksArgsForCall, struct { - includeAll bool - }{includeAll}) + arg1 director.TasksFilter + }{arg1}) fake.currentTasksMutex.Unlock() if fake.CurrentTasksStub != nil { - return fake.CurrentTasksStub(includeAll) + return fake.CurrentTasksStub(arg1) } else { return fake.currentTasksReturns.result1, fake.currentTasksReturns.result2 } @@ -361,10 +361,10 @@ func (fake *FakeDirector) CurrentTasksCallCount() int { return len(fake.currentTasksArgsForCall) } -func (fake *FakeDirector) CurrentTasksArgsForCall(i int) bool { +func (fake *FakeDirector) CurrentTasksArgsForCall(i int) director.TasksFilter { fake.currentTasksMutex.RLock() defer fake.currentTasksMutex.RUnlock() - return fake.currentTasksArgsForCall[i].includeAll + return fake.currentTasksArgsForCall[i].arg1 } func (fake *FakeDirector) CurrentTasksReturns(result1 []director.Task, result2 error) { @@ -375,15 +375,15 @@ func (fake *FakeDirector) CurrentTasksReturns(result1 []director.Task, result2 e }{result1, result2} } -func (fake *FakeDirector) RecentTasks(limit int, includeAll bool) ([]director.Task, error) { +func (fake *FakeDirector) RecentTasks(arg1 int, arg2 director.TasksFilter) ([]director.Task, error) { fake.recentTasksMutex.Lock() fake.recentTasksArgsForCall = append(fake.recentTasksArgsForCall, struct { - limit int - includeAll bool - }{limit, includeAll}) + arg1 int + arg2 director.TasksFilter + }{arg1, arg2}) fake.recentTasksMutex.Unlock() if fake.RecentTasksStub != nil { - return fake.RecentTasksStub(limit, includeAll) + return fake.RecentTasksStub(arg1, arg2) } else { return fake.recentTasksReturns.result1, fake.recentTasksReturns.result2 } @@ -395,10 +395,10 @@ func (fake *FakeDirector) RecentTasksCallCount() int { return len(fake.recentTasksArgsForCall) } -func (fake *FakeDirector) RecentTasksArgsForCall(i int) (int, bool) { +func (fake *FakeDirector) RecentTasksArgsForCall(i int) (int, director.TasksFilter) { fake.recentTasksMutex.RLock() defer fake.recentTasksMutex.RUnlock() - return fake.recentTasksArgsForCall[i].limit, fake.recentTasksArgsForCall[i].includeAll + return fake.recentTasksArgsForCall[i].arg1, fake.recentTasksArgsForCall[i].arg2 } func (fake *FakeDirector) RecentTasksReturns(result1 []director.Task, result2 error) { diff --git a/director/interfaces.go b/director/interfaces.go index e147b87d8..9aef33ad6 100644 --- a/director/interfaces.go +++ b/director/interfaces.go @@ -16,8 +16,8 @@ type Director interface { Locks() ([]Lock, error) - CurrentTasks(includeAll bool) ([]Task, error) - RecentTasks(limit int, includeAll bool) ([]Task, error) + CurrentTasks(TasksFilter) ([]Task, error) + RecentTasks(int, TasksFilter) ([]Task, error) FindTask(int) (Task, error) Events(EventsFilter) ([]Event, error) @@ -157,6 +157,11 @@ type Stemcell interface { Delete(force bool) error } +type TasksFilter struct { + All bool + Deployment string +} + type Task interface { ID() int StartedAt() time.Time diff --git a/director/tasks.go b/director/tasks.go index 4b4d756e7..49b6e1dab 100644 --- a/director/tasks.go +++ b/director/tasks.go @@ -2,6 +2,7 @@ package director import ( "fmt" + gourl "net/url" "time" bosherr "github.com/cloudfoundry/bosh-utils/errors" @@ -72,10 +73,10 @@ func NewTaskFromResp(client Client, r TaskResp) TaskImpl { } } -func (d DirectorImpl) CurrentTasks(includeAll bool) ([]Task, error) { +func (d DirectorImpl) CurrentTasks(filter TasksFilter) ([]Task, error) { tasks := []Task{} - taskResps, err := d.client.CurrentTasks(includeAll) + taskResps, err := d.client.CurrentTasks(filter) if err != nil { return tasks, err } @@ -87,10 +88,10 @@ func (d DirectorImpl) CurrentTasks(includeAll bool) ([]Task, error) { return tasks, nil } -func (d DirectorImpl) RecentTasks(limit int, includeAll bool) ([]Task, error) { +func (d DirectorImpl) RecentTasks(limit int, filter TasksFilter) ([]Task, error) { tasks := []Task{} - taskResps, err := d.client.RecentTasks(limit, includeAll) + taskResps, err := d.client.RecentTasks(limit, filter) if err != nil { return tasks, err } @@ -131,11 +132,19 @@ func (t TaskImpl) RawOutput(taskReporter TaskReporter) error { return t.client.TaskOutput(t.id, "raw", taskReporter) } -func (c Client) CurrentTasks(includeAll bool) ([]TaskResp, error) { +func (c Client) CurrentTasks(filter TasksFilter) ([]TaskResp, error) { var tasks []TaskResp - path := "/tasks?state=processing,cancelling,queued&verbose=" + - c.taskVerbosity(includeAll) + query := gourl.Values{} + + query.Add("state", "processing,cancelling,queued") + query.Add("verbose", c.taskVerbosity(filter.All)) + + if len(filter.Deployment) > 0 { + query.Add("deployment", filter.Deployment) + } + + path := fmt.Sprintf("/tasks?%s", query.Encode()) err := c.clientRequest.Get(path, &tasks) if err != nil { @@ -145,11 +154,19 @@ func (c Client) CurrentTasks(includeAll bool) ([]TaskResp, error) { return tasks, nil } -func (c Client) RecentTasks(limit int, includeAll bool) ([]TaskResp, error) { +func (c Client) RecentTasks(limit int, filter TasksFilter) ([]TaskResp, error) { var tasks []TaskResp - path := fmt.Sprintf("/tasks?limit=%d&verbose=%s", - limit, c.taskVerbosity(includeAll)) + query := gourl.Values{} + + query.Add("limit", fmt.Sprintf("%d", limit)) + query.Add("verbose", c.taskVerbosity(filter.All)) + + if len(filter.Deployment) > 0 { + query.Add("deployment", filter.Deployment) + } + + path := fmt.Sprintf("/tasks?%s", query.Encode()) err := c.clientRequest.Get(path, &tasks) if err != nil { diff --git a/director/tasks_test.go b/director/tasks_test.go index 55c00a64c..f00d31778 100644 --- a/director/tasks_test.go +++ b/director/tasks_test.go @@ -58,7 +58,7 @@ var _ = Describe("Director", func() { ), ) - tasks, err := director.CurrentTasks(false) + tasks, err := director.CurrentTasks(TasksFilter{}) Expect(err).ToNot(HaveOccurred()) Expect(tasks).To(HaveLen(2)) @@ -90,14 +90,27 @@ var _ = Describe("Director", func() { ), ) - _, err := director.CurrentTasks(true) + _, err := director.CurrentTasks(TasksFilter{All: true}) + Expect(err).ToNot(HaveOccurred()) + }) + + It("includes tasks for specific deployment when requested", func() { + server.AppendHandlers( + ghttp.CombineHandlers( + ghttp.VerifyRequest("GET", "/tasks", "state=processing,cancelling,queued&verbose=1&deployment=deployment"), + ghttp.VerifyBasicAuth("username", "password"), + ghttp.RespondWith(http.StatusOK, "[]"), + ), + ) + + _, err := director.CurrentTasks(TasksFilter{Deployment: "deployment"}) Expect(err).ToNot(HaveOccurred()) }) It("returns error if response is non-200", func() { AppendBadRequest(ghttp.VerifyRequest("GET", "/tasks"), server) - _, err := director.CurrentTasks(false) + _, err := director.CurrentTasks(TasksFilter{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring( "Finding current tasks: Director responded with non-successful status code")) @@ -111,7 +124,7 @@ var _ = Describe("Director", func() { ), ) - _, err := director.CurrentTasks(false) + _, err := director.CurrentTasks(TasksFilter{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring( "Finding current tasks: Unmarshaling Director response")) @@ -149,7 +162,7 @@ var _ = Describe("Director", func() { ), ) - tasks, err := director.RecentTasks(10, false) + tasks, err := director.RecentTasks(10, TasksFilter{}) Expect(err).ToNot(HaveOccurred()) Expect(tasks).To(HaveLen(2)) @@ -181,14 +194,27 @@ var _ = Describe("Director", func() { ), ) - _, err := director.RecentTasks(10, true) + _, err := director.RecentTasks(10, TasksFilter{All: true}) + Expect(err).ToNot(HaveOccurred()) + }) + + It("includes tasks for specific deployment when requested", func() { + server.AppendHandlers( + ghttp.CombineHandlers( + ghttp.VerifyRequest("GET", "/tasks", "limit=10&verbose=1&deployment=deployment"), + ghttp.VerifyBasicAuth("username", "password"), + ghttp.RespondWith(http.StatusOK, "[]"), + ), + ) + + _, err := director.RecentTasks(10, TasksFilter{Deployment: "deployment"}) Expect(err).ToNot(HaveOccurred()) }) It("returns error if response is non-200", func() { AppendBadRequest(ghttp.VerifyRequest("GET", "/tasks"), server) - _, err := director.RecentTasks(10, false) + _, err := director.RecentTasks(10, TasksFilter{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring( "Finding recent tasks: Director responded with non-successful status code")) @@ -202,7 +228,7 @@ var _ = Describe("Director", func() { ), ) - _, err := director.RecentTasks(10, false) + _, err := director.RecentTasks(10, TasksFilter{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring( "Finding recent tasks: Unmarshaling Director response"))