Permalink
Browse files

Implement RPC status and summary methods

Includes cli output formatting

Change-Id: I617289a5faf9655e2835a5fc351f8b258d7a51f1
  • Loading branch information...
1 parent eb2b8da commit 9003452163d0e90e41d9d18b24215ee2fac4d41b @dougm dougm committed Aug 24, 2012
Showing with 354 additions and 24 deletions.
  1. +100 −19 api.go
  2. +126 −1 api_test.go
  3. +108 −0 cli_formatter.go
  4. +14 −3 control.go
  5. +6 −1 gonit/main.go
View
119 api.go
@@ -4,7 +4,7 @@ package gonit
import (
"errors"
- "fmt"
+ "github.com/cloudfoundry/gosigar"
)
// until stubs are implemented
@@ -14,21 +14,31 @@ type API struct {
control *Control
}
+type ProcessSummary struct {
+ Name string
+ Running bool
+ ControlState processState
+}
+
type ProcessStatus struct {
- Name string
- Type int
- Mode int
- Status int
- // XXX cpu, mem, etc
+ Summary ProcessSummary
+ Pid int
+ State sigar.ProcState
+ Time sigar.ProcTime
+ Mem sigar.ProcMem
}
type SystemStatus struct {
// XXX load, cpu, mem, swap, etc
}
type ProcessGroupStatus struct {
- Name string
- Processs []ProcessStatus
+ Name string
+ Group []ProcessStatus
+}
+
+type Summary struct {
+ Processes []ProcessSummary
}
type About struct {
@@ -92,24 +102,56 @@ func (a *API) UnmonitorProcess(name string, r *ActionResult) error {
return a.control.callAction(name, r, ACTION_UNMONITOR)
}
+func (c *Control) processSummary(process *Process, summary *ProcessSummary) {
+ summary.Name = process.Name
+ summary.Running = process.IsRunning()
+ summary.ControlState = *c.State(process)
+}
+
+func (c *Control) processStatus(process *Process, status *ProcessStatus) error {
+ c.processSummary(process, &status.Summary)
+
+ if !status.Summary.Running {
+ return nil
+ }
+
+ pid, err := process.Pid()
+ if err != nil {
+ return err
+ }
+ status.Pid = pid
+
+ status.State.Get(pid)
+ status.Time.Get(pid)
+ status.Mem.Get(pid)
+
+ return nil
+}
+
func (a *API) StatusProcess(name string, r *ProcessStatus) error {
- return notimpl
+ process, err := a.control.Config().FindProcess(name)
+
+ if err != nil {
+ return err
+ }
+
+ return a.control.processStatus(process, r)
}
// *Group methods apply to a service group
func (c *Control) groupAction(name string, r *ActionResult, action int) error {
- group, exists := c.Config().ProcessGroups[name]
+ group, err := c.Config().FindGroup(name)
- if exists {
- for name := range group.Processes {
- c.callAction(name, r, action)
- }
- return nil
+ if err != nil {
+ return &ActionError{err}
}
- err := fmt.Errorf("process group %q does not exist", name)
- return &ActionError{err}
+ for name := range group.Processes {
+ c.callAction(name, r, action)
+ }
+
+ return nil
}
func (a *API) StartGroup(name string, r *ActionResult) error {
@@ -132,8 +174,29 @@ func (a *API) UnmonitorGroup(name string, r *ActionResult) error {
return a.control.groupAction(name, r, ACTION_UNMONITOR)
}
+func (c *Control) groupStatus(group *ProcessGroup,
+ groupStatus *ProcessGroupStatus) error {
+
+ for _, process := range group.Processes {
+ status := ProcessStatus{}
+ c.processStatus(process, &status)
+ groupStatus.Group = append(groupStatus.Group, status)
+ }
+
+ return nil
+}
+
func (a *API) StatusGroup(name string, r *ProcessGroupStatus) error {
- return notimpl
+ group, err := a.control.Config().FindGroup(name)
+
+ if err != nil {
+ return err
+ }
+
+ r.Name = name
+ a.control.groupStatus(group, r)
+
+ return nil
}
// *All methods apply to all services
@@ -168,7 +231,25 @@ func (a *API) UnmonitorAll(unused interface{}, r *ActionResult) error {
}
func (a *API) StatusAll(name string, r *ProcessGroupStatus) error {
- return notimpl
+ r.Name = name
+
+ for _, processGroup := range a.control.Config().ProcessGroups {
+ a.control.groupStatus(processGroup, r)
+ }
+
+ return nil
+}
+
+func (a *API) Summary(unused interface{}, s *Summary) error {
+ for _, group := range a.control.Config().ProcessGroups {
+ for _, process := range group.Processes {
+ summary := ProcessSummary{}
+ a.control.processSummary(process, &summary)
+ s.Processes = append(s.Processes, summary)
+ }
+ }
+
+ return nil
}
// server info
View
@@ -3,18 +3,23 @@
package gonit_test
import (
+ "bytes"
"github.com/bmizerany/assert"
. "github.com/cloudfoundry/gonit"
"github.com/cloudfoundry/gonit/test/helper"
+ "io/ioutil"
+ "math"
"net/rpc"
+ "os"
"strings"
"testing"
)
var rpcName = "TestAPI"
+var config = &ConfigManager{}
func init() {
- rpc.RegisterName(rpcName, NewAPI(nil))
+ rpc.RegisterName(rpcName, NewAPI(config))
}
func isActionError(err error) bool {
@@ -66,3 +71,123 @@ func TestAbout(t *testing.T) {
assert.Equal(t, nil, err)
}
+
+func tmpPidFile(t *testing.T, pid int) string {
+ file, err := ioutil.TempFile("", "api_test")
+ if err != nil {
+ t.Error(err)
+ }
+ if err := WritePidFile(pid, file.Name()); err != nil {
+ t.Fatal(err)
+ }
+ return file.Name()
+}
+
+// simple exercise of CliFormatters
+func testCliPrint(t *testing.T, value CliFormatter) {
+ buf := new(bytes.Buffer)
+ value.Print(buf)
+ assert.NotEqual(t, 0, buf.Len())
+}
+
+func TestStatus(t *testing.T) {
+ nprocesses := 0
+
+ type testProcess struct {
+ name string
+ pid int
+ ppid int
+ running bool
+ }
+
+ // use pid/ppid of the go test process to test
+ // Running, sigar.ProcState, etc.
+ pid := os.Getpid()
+ ppid := os.Getppid()
+
+ groups := []struct {
+ name string
+ processes []testProcess
+ }{
+ {
+ name: "a_group",
+ processes: []testProcess{
+ {"a_process", pid, ppid, true},
+ {"x_process", math.MaxInt32, -1, false},
+ },
+ },
+ {
+ name: "b_group",
+ processes: []testProcess{
+ {"b_process", pid, ppid, true},
+ },
+ },
+ }
+
+ for _, group := range groups {
+ for _, process := range group.processes {
+ // write pidfile for use by process.IsRunning()
+ pidfile := tmpPidFile(t, process.pid)
+ defer os.Remove(pidfile)
+
+ config.AddProcess(group.name, &Process{
+ Name: process.name,
+ Pidfile: pidfile,
+ })
+
+ nprocesses++
+ }
+ }
+
+ err := helper.WithRpcServer(func(c *rpc.Client) {
+ statusGroup := rpcName + ".StatusGroup"
+ statusProcess := rpcName + ".StatusProcess"
+
+ // should get an error if group does not exist
+ err := c.Call(statusGroup, "enogroup", &ProcessGroupStatus{})
+ assert.NotEqual(t, nil, err)
+
+ // should get an error if process does not exist
+ err = c.Call(statusProcess, "enoprocess", &ProcessStatus{})
+ assert.NotEqual(t, nil, err)
+
+ for _, group := range groups {
+ gstat := &ProcessGroupStatus{}
+ err := c.Call(statusGroup, group.name, gstat)
+ assert.Equal(t, nil, err)
+ assert.Equal(t, len(group.processes), len(gstat.Group))
+ testCliPrint(t, gstat)
+
+ for _, process := range group.processes {
+ stat := &ProcessStatus{}
+ err := c.Call(statusProcess, process.name, stat)
+ assert.Equal(t, nil, err)
+
+ running := stat.Summary.Running
+ assert.Equal(t, process.running, running)
+ testCliPrint(t, stat)
+
+ if !running {
+ continue
+ }
+
+ assert.Equal(t, process.pid, stat.Pid)
+ assert.Equal(t, process.ppid, stat.State.Ppid)
+ }
+ }
+
+ all := &ProcessGroupStatus{}
+ err = c.Call(rpcName+".StatusAll", "", all)
+ assert.Equal(t, nil, err)
+ assert.Equal(t, nprocesses, len(all.Group))
+ testCliPrint(t, all)
+
+ summary := &Summary{}
+ err = c.Call(rpcName+".Summary", "", summary)
+ assert.Equal(t, nil, err)
+ assert.Equal(t, nprocesses, len(summary.Processes))
+ testCliPrint(t, summary)
+ })
+
+ assert.Equal(t, nil, err)
+}
Oops, something went wrong.

0 comments on commit 9003452

Please sign in to comment.