consul/state: persisting health checks works

This commit is contained in:
Ryan Uber 2015-08-24 20:51:07 -07:00 committed by James Phillips
parent 1d4a6ea790
commit cf569f4848
2 changed files with 113 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package state package state
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -9,6 +10,16 @@ import (
"github.com/hashicorp/go-memdb" "github.com/hashicorp/go-memdb"
) )
var (
// ErrMissingNode is the error returned when trying an operation
// which requires a node registration but none exists.
ErrMissingNode = errors.New("Missing node registration")
// ErrMissingService is the error we return if trying an
// operation which requires a service but none exists.
ErrMissingService = errors.New("Missing service registration")
)
// StateStore is where we store all of Consul's state, including // StateStore is where we store all of Consul's state, including
// records of node registrations, services, checks, key/value // records of node registrations, services, checks, key/value
// pairs and more. The DB is entirely in-memory and is constructed // pairs and more. The DB is entirely in-memory and is constructed
@ -322,3 +333,65 @@ func (s *StateStore) deleteNodeServiceTxn(idx uint64, nodeID, serviceID string,
// TODO: watch trigger // TODO: watch trigger
return nil return nil
} }
// EnsureCheck is used to store a check registration in the db.
func (s *StateStore) EnsureCheck(idx uint64, hc *structs.HealthCheck) error {
tx := s.db.Txn(true)
defer tx.Abort()
// Call the check registration
if err := s.ensureCheckTxn(idx, hc, tx); err != nil {
return err
}
tx.Commit()
return nil
}
// ensureCheckTransaction is used as the inner method to handle inserting
// a health check into the state store. It ensures safety against inserting
// checks with no matching node or service.
func (s *StateStore) ensureCheckTxn(idx uint64, hc *structs.HealthCheck, tx *memdb.Txn) error {
// Use the default check status if none was provided
if hc.Status == "" {
hc.Status = structs.HealthCritical
}
// Get the node
node, err := tx.First("nodes", "id", hc.Node)
if err != nil {
return fmt.Errorf("failed node lookup: %s", err)
}
if node == nil {
return ErrMissingNode
}
// If the check is associated with a service, check that we have
// a registration for the service.
if hc.ServiceID != "" {
service, err := tx.First("services", "id", hc.Node, hc.ServiceID)
if err != nil {
return fmt.Errorf("failed service lookup: %s", err)
}
if service == nil {
return ErrMissingService
}
// Copy in the service name
hc.ServiceName = service.(*structs.ServiceNode).ServiceName
}
// TODO: invalidate sessions if status == critical
// Persist the check registration in the db
if err := tx.Insert("services", hc); err != nil {
return fmt.Errorf("failed inserting service: %s", err)
}
if err := tx.Insert("index", &IndexEntry{"checks", idx}); err != nil {
return fmt.Errorf("failed updating index: %s", err)
}
// TODO: trigger watches
return nil
}

View File

@ -274,3 +274,43 @@ func TestStateStore_DeleteNodeService(t *testing.T) {
t.Fatalf("bad index: %d", idx) t.Fatalf("bad index: %d", idx)
} }
} }
func TestStateStore_EnsureCheck(t *testing.T) {
s := testStateStore(t)
// Create a node and insert it
node := &structs.Node{
Node: "node1",
Address: "1.1.1.1",
}
if err := s.EnsureNode(1, node); err != nil {
t.Fatalf("err: %s", err)
}
// Create a service and insert it
service := &structs.NodeService{
ID: "service1",
Service: "redis",
Tags: []string{"prod"},
Address: "1.1.1.1",
Port: 1111,
}
if err := s.EnsureService(2, "node1", service); err != nil {
t.Fatalf("err: %s", err)
}
// Create a check associated with the node and insert it
check := &structs.HealthCheck{
Node: "node1",
CheckID: "check1",
Name: "redis check",
Status: structs.HealthPassing,
Notes: "test check",
Output: "aaa",
ServiceID: "service1",
ServiceName: "redis",
}
if err := s.EnsureCheck(3, check); err != nil {
t.Fatalf("err: %s", err)
}
}