diff --git a/tools/functional-tester/etcd-agent/agent.go b/tools/functional-tester/etcd-agent/agent.go index ecd39046a..d63143ce2 100644 --- a/tools/functional-tester/etcd-agent/agent.go +++ b/tools/functional-tester/etcd-agent/agent.go @@ -21,9 +21,20 @@ import ( "os/exec" "path" "time" + + "github.com/coreos/etcd/tools/functional-tester/etcd-agent/client" +) + +const ( + stateUninitialized = "uninitialized" + stateStarted = "started" + stateStopped = "stopped" + stateTerminated = "terminated" ) type Agent struct { + state string // the state of etcd process + cmd *exec.Cmd logfile *os.File l net.Listener @@ -43,7 +54,7 @@ func newAgent(etcd string) (*Agent, error) { return nil, err } - return &Agent{cmd: c, logfile: f}, nil + return &Agent{state: stateUninitialized, cmd: c, logfile: f}, nil } // start starts a new etcd process with the given args. @@ -51,7 +62,13 @@ func (a *Agent) start(args ...string) error { a.cmd = exec.Command(a.cmd.Path, args...) a.cmd.Stdout = a.logfile a.cmd.Stderr = a.logfile - return a.cmd.Start() + err := a.cmd.Start() + if err != nil { + return err + } + + a.state = stateStarted + return nil } // stop stops the existing etcd process the agent started. @@ -61,7 +78,13 @@ func (a *Agent) stop() error { return err } _, err = a.cmd.Process.Wait() - return err + if err != nil { + return err + + } + + a.state = stateStopped + return nil } // restart restarts the stopped etcd process. @@ -69,11 +92,22 @@ func (a *Agent) restart() error { a.cmd = exec.Command(a.cmd.Path, a.cmd.Args[1:]...) a.cmd.Stdout = a.logfile a.cmd.Stderr = a.logfile - return a.cmd.Start() + err := a.cmd.Start() + if err != nil { + return err + } + + a.state = stateStarted + return nil } func (a *Agent) cleanup() error { - a.stop() + err := a.stop() + if err != nil { + return err + } + a.state = stateUninitialized + a.logfile.Close() if err := archiveLogAndDataDir("etcd.log", a.dataDir()); err != nil { return err @@ -86,8 +120,20 @@ func (a *Agent) cleanup() error { // terminate stops the exiting etcd process the agent started // and removes the data dir. func (a *Agent) terminate() error { - a.stop() - return os.RemoveAll(a.dataDir()) + err := a.stop() + if err != nil { + return err + } + err = os.RemoveAll(a.dataDir()) + if err != nil { + return err + } + a.state = stateTerminated + return nil +} + +func (a *Agent) status() client.Status { + return client.Status{State: a.state} } func (a *Agent) dataDir() string { diff --git a/tools/functional-tester/etcd-agent/client/client.go b/tools/functional-tester/etcd-agent/client/client.go index 58ffdc273..ea92f99ea 100644 --- a/tools/functional-tester/etcd-agent/client/client.go +++ b/tools/functional-tester/etcd-agent/client/client.go @@ -16,6 +16,12 @@ package client import "net/rpc" +type Status struct { + // TODO: gather more informations + // TODO: memory usage, raft information, etc.. + State string +} + type Agent interface { ID() uint64 // Start starts a new etcd with the given args on the agent machine. @@ -30,6 +36,8 @@ type Agent interface { Terminate() error // Isoloate isolates the network of etcd Isolate() error + // Status returns the status of etcd on the agent + Status() (Status, error) } type agent struct { @@ -79,6 +87,15 @@ func (a *agent) Isolate() error { panic("not implemented") } +func (a *agent) Status() (Status, error) { + var s Status + err := a.rpcClient.Call("Agent.RPCStatus", struct{}{}, &s) + if err != nil { + return s, err + } + return s, nil +} + func (a *agent) ID() uint64 { panic("not implemented") } diff --git a/tools/functional-tester/etcd-agent/rpc.go b/tools/functional-tester/etcd-agent/rpc.go index 288a9dac1..151a428da 100644 --- a/tools/functional-tester/etcd-agent/rpc.go +++ b/tools/functional-tester/etcd-agent/rpc.go @@ -19,6 +19,8 @@ import ( "net" "net/http" "net/rpc" + + "github.com/coreos/etcd/tools/functional-tester/etcd-agent/client" ) func (a *Agent) serveRPC() { @@ -69,3 +71,8 @@ func (a *Agent) RPCTerminate(args struct{}, reply *struct{}) error { func (a *Agent) RPCIsolate(args struct{}, reply *struct{}) error { panic("not implemented") } + +func (a *Agent) RPCStatus(args struct{}, status *client.Status) error { + *status = a.status() + return nil +} diff --git a/tools/functional-tester/etcd-agent/rpc_test.go b/tools/functional-tester/etcd-agent/rpc_test.go index 3fb25263e..bda72aa9c 100644 --- a/tools/functional-tester/etcd-agent/rpc_test.go +++ b/tools/functional-tester/etcd-agent/rpc_test.go @@ -20,6 +20,8 @@ import ( "net/rpc" "os" "testing" + + "github.com/coreos/etcd/tools/functional-tester/etcd-agent/client" ) func init() { @@ -123,3 +125,42 @@ func TestRPCTerminate(t *testing.T) { t.Fatal(err) } } + +func TestRPCStatus(t *testing.T) { + c, err := rpc.DialHTTP("tcp", ":9027") + if err != nil { + t.Fatal(err) + } + + var s client.Status + err = c.Call("Agent.RPCStatus", struct{}{}, &s) + if err != nil { + t.Fatal(err) + } + if s.State != stateTerminated { + t.Errorf("state = %s, want %s", s.State, stateTerminated) + } + + dir, err := ioutil.TempDir(os.TempDir(), "etcd-agent") + if err != nil { + t.Fatal(err) + } + var pid int + err = c.Call("Agent.RPCStart", []string{"-data-dir", dir}, &pid) + if err != nil { + t.Fatal(err) + } + + err = c.Call("Agent.RPCStatus", struct{}{}, &s) + if err != nil { + t.Fatal(err) + } + if s.State != stateStarted { + t.Errorf("state = %s, want %s", s.State, stateStarted) + } + + err = c.Call("Agent.RPCTerminate", struct{}{}, nil) + if err != nil { + t.Fatal(err) + } +}