From 2b79c125f23584da8d3679798623b2b8028a2dce Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Wed, 8 Jan 2014 12:15:01 -0800 Subject: [PATCH] Implementing the health methods with tests --- consul/state_store.go | 69 ++++++++++++++--- consul/state_store_test.go | 152 ++++++++++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 10 deletions(-) diff --git a/consul/state_store.go b/consul/state_store.go index 5a543d1ae3..565ad88629 100644 --- a/consul/state_store.go +++ b/consul/state_store.go @@ -388,7 +388,6 @@ func parseServiceNodes(tx *MDBTxn, table *MDBTable, res []interface{}, err error if err != nil { panic(fmt.Errorf("Failed to get node services: %v", err)) } - println(fmt.Sprintf("res: %#v", res)) nodes := make(structs.ServiceNodes, len(res)) for i, r := range res { @@ -408,8 +407,47 @@ func parseServiceNodes(tx *MDBTxn, table *MDBTable, res []interface{}, err error } // EnsureCheck is used to create a check or updates it's state -func (s *StateStore) EnsureCheck() error { - return nil +func (s *StateStore) EnsureCheck(check *structs.HealthCheck) error { + // Ensure we have a status + if check.Status == "" { + check.Status = structs.HealthUnknown + } + + // Start the txn + tx, err := s.tables.StartTxn(false) + if err != nil { + panic(fmt.Errorf("Failed to start txn: %v", err)) + } + defer tx.Abort() + + // Ensure the node exists + res, err := s.nodeTable.GetTxn(tx, "id", check.Node) + if err != nil { + return err + } + if len(res) == 0 { + return fmt.Errorf("Missing node registration") + } + + // Ensure the service exists if specified + if check.ServiceID != "" { + res, err = s.serviceTable.GetTxn(tx, "id", check.Node, check.ServiceID) + if err != nil { + return err + } + if len(res) == 0 { + return fmt.Errorf("Missing service registration") + } + // Ensure we set the correct service + srv := res[0].(*structs.ServiceNode) + check.ServiceName = srv.ServiceName + } + + // Ensure the check is set + if err := s.checkTable.InsertTxn(tx, check); err != nil { + return err + } + return tx.Commit() } // DeleteNodeCheck is used to delete a node health check @@ -419,18 +457,31 @@ func (s *StateStore) DeleteNodeCheck(node, id string) error { } // NodeChecks is used to get all the checks for a node -func (s *StateStore) NodeChecks(name string) structs.HealthChecks { - return nil +func (s *StateStore) NodeChecks(node string) structs.HealthChecks { + return parseHealthChecks(s.checkTable.Get("id", node)) } // ServiceChecks is used to get all the checks for a service -func (s *StateStore) ServiceChecks(name string) structs.HealthChecks { - return nil +func (s *StateStore) ServiceChecks(service string) structs.HealthChecks { + return parseHealthChecks(s.checkTable.Get("service", service)) } // CheckInState is used to get all the checks for a service in a given state -func (s *StateStore) ChecksInState(name string) structs.HealthChecks { - return nil +func (s *StateStore) ChecksInState(state string) structs.HealthChecks { + return parseHealthChecks(s.checkTable.Get("status", state)) +} + +// parseHealthChecks is used to handle the resutls of a Get against +// the checkTable +func parseHealthChecks(res []interface{}, err error) structs.HealthChecks { + if err != nil { + panic(fmt.Errorf("Failed to get checks: %v", err)) + } + results := make([]*structs.HealthCheck, len(res)) + for i, r := range res { + results[i] = r.(*structs.HealthCheck) + } + return results } // Snapshot is used to create a point in time snapshot diff --git a/consul/state_store_test.go b/consul/state_store_test.go index 7950fdc87b..ac1dda7441 100644 --- a/consul/state_store_test.go +++ b/consul/state_store_test.go @@ -2,6 +2,7 @@ package consul import ( "github.com/hashicorp/consul/consul/structs" + "reflect" "sort" "testing" ) @@ -163,6 +164,17 @@ func TestDeleteNodeService(t *testing.T) { t.Fatalf("err: %v", err) } + check := &structs.HealthCheck{ + Node: "foo", + CheckID: "api", + Name: "Can connect", + Status: structs.HealthPassing, + ServiceID: "api", + } + if err := store.EnsureCheck(check); err != nil { + t.Fatalf("err: %v") + } + if err := store.DeleteNodeService("foo", "api"); err != nil { t.Fatalf("err: %v", err) } @@ -172,6 +184,11 @@ func TestDeleteNodeService(t *testing.T) { if ok { t.Fatalf("has api: %#v", services) } + + checks := store.NodeChecks("foo") + if len(checks) != 0 { + t.Fatalf("has check: %#v", checks) + } } func TestDeleteNodeService_One(t *testing.T) { @@ -223,8 +240,19 @@ func TestDeleteNode(t *testing.T) { t.Fatalf("err: %v") } + check := &structs.HealthCheck{ + Node: "foo", + CheckID: "db", + Name: "Can connect", + Status: structs.HealthPassing, + ServiceID: "api", + } + if err := store.EnsureCheck(check); err != nil { + t.Fatalf("err: %v", err) + } + if err := store.DeleteNode("foo"); err != nil { - t.Fatalf("err: %v") + t.Fatalf("err: %v", err) } services := store.NodeServices("foo") @@ -233,6 +261,11 @@ func TestDeleteNode(t *testing.T) { t.Fatalf("has api: %#v", services) } + checks := store.NodeChecks("foo") + if len(checks) > 0 { + t.Fatalf("has checks: %v", checks) + } + found, _ := store.GetNode("foo") if found { t.Fatalf("found node") @@ -504,3 +537,120 @@ func TestStoreSnapshot(t *testing.T) { t.Fatalf("bad: %v", services) } } + +func TestEnsureCheck(t *testing.T) { + store, err := NewStateStore() + if err != nil { + t.Fatalf("err: %v", err) + } + defer store.Close() + + if err := store.EnsureNode(structs.Node{"foo", "127.0.0.1"}); err != nil { + t.Fatalf("err: %v", err) + } + if err := store.EnsureService("foo", "db1", "db", "master", 8000); err != nil { + t.Fatalf("err: %v") + } + check := &structs.HealthCheck{ + Node: "foo", + CheckID: "db", + Name: "Can connect", + Status: structs.HealthPassing, + ServiceID: "db1", + } + if err := store.EnsureCheck(check); err != nil { + t.Fatalf("err: %v") + } + + check2 := &structs.HealthCheck{ + Node: "foo", + CheckID: "memory", + Name: "memory utilization", + Status: structs.HealthWarning, + } + if err := store.EnsureCheck(check2); err != nil { + t.Fatalf("err: %v") + } + + checks := store.NodeChecks("foo") + if len(checks) != 2 { + t.Fatalf("bad: %v", checks) + } + if !reflect.DeepEqual(checks[0], check) { + t.Fatalf("bad: %v", checks[0]) + } + if !reflect.DeepEqual(checks[1], check2) { + t.Fatalf("bad: %v", checks[1]) + } + + checks = store.ServiceChecks("db") + if len(checks) != 1 { + t.Fatalf("bad: %v", checks) + } + if !reflect.DeepEqual(checks[0], check) { + t.Fatalf("bad: %v", checks[0]) + } + + checks = store.ChecksInState(structs.HealthPassing) + if len(checks) != 1 { + t.Fatalf("bad: %v", checks) + } + if !reflect.DeepEqual(checks[0], check) { + t.Fatalf("bad: %v", checks[0]) + } + + checks = store.ChecksInState(structs.HealthWarning) + if len(checks) != 1 { + t.Fatalf("bad: %v", checks) + } + if !reflect.DeepEqual(checks[0], check2) { + t.Fatalf("bad: %v", checks[0]) + } +} + +func TestDeleteNodeCheck(t *testing.T) { + store, err := NewStateStore() + if err != nil { + t.Fatalf("err: %v", err) + } + defer store.Close() + + if err := store.EnsureNode(structs.Node{"foo", "127.0.0.1"}); err != nil { + t.Fatalf("err: %v", err) + } + if err := store.EnsureService("foo", "db1", "db", "master", 8000); err != nil { + t.Fatalf("err: %v") + } + check := &structs.HealthCheck{ + Node: "foo", + CheckID: "db", + Name: "Can connect", + Status: structs.HealthPassing, + ServiceID: "db1", + } + if err := store.EnsureCheck(check); err != nil { + t.Fatalf("err: %v") + } + + check2 := &structs.HealthCheck{ + Node: "foo", + CheckID: "memory", + Name: "memory utilization", + Status: structs.HealthWarning, + } + if err := store.EnsureCheck(check2); err != nil { + t.Fatalf("err: %v") + } + + if err := store.DeleteNodeCheck("foo", "db"); err != nil { + t.Fatalf("err: %v", err) + } + + checks := store.NodeChecks("foo") + if len(checks) != 1 { + t.Fatalf("bad: %v", checks) + } + if !reflect.DeepEqual(checks[0], check2) { + t.Fatalf("bad: %v", checks[0]) + } +}