Chase casting types.CheckID to a string into the state_store.

It turns out the indexer can only use strings as arguments when
creating a query.  Cast `types.CheckID` to a `string` before calling
into `memdb`.

Ideally the indexer would be smart enough to do this at compile-time,
but I need to look into how to do this without reflection and the
runtime package.  For the time being statically cast `types.CheckID`
to a `string` at the call sites.
This commit is contained in:
Sean Chittenden 2016-06-07 15:24:51 -05:00
parent 63adcbd5ef
commit e9a2f5b40c
No known key found for this signature in database
GPG Key ID: 4EBC9DC16C2E5E16
12 changed files with 80 additions and 55 deletions

View File

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/testutil" "github.com/hashicorp/consul/testutil"
"github.com/hashicorp/consul/types"
"github.com/hashicorp/serf/serf" "github.com/hashicorp/serf/serf"
) )
@ -61,7 +62,7 @@ func TestHTTPAgentChecks(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Err: %v", err) t.Fatalf("Err: %v", err)
} }
val := obj.(map[string]*structs.HealthCheck) val := obj.(map[types.CheckID]*structs.HealthCheck)
if len(val) != 1 { if len(val) != 1 {
t.Fatalf("bad checks: %v", obj) t.Fatalf("bad checks: %v", obj)
} }
@ -294,21 +295,22 @@ func TestHTTPAgentRegisterCheck(t *testing.T) {
} }
// Ensure we have a check mapping // Ensure we have a check mapping
if _, ok := srv.agent.state.Checks()["test"]; !ok { checkID := types.CheckID("test")
if _, ok := srv.agent.state.Checks()[checkID]; !ok {
t.Fatalf("missing test check") t.Fatalf("missing test check")
} }
if _, ok := srv.agent.checkTTLs["test"]; !ok { if _, ok := srv.agent.checkTTLs[checkID]; !ok {
t.Fatalf("missing test check ttl") t.Fatalf("missing test check ttl")
} }
// Ensure the token was configured // Ensure the token was configured
if token := srv.agent.state.CheckToken("test"); token == "" { if token := srv.agent.state.CheckToken(checkID); token == "" {
t.Fatalf("missing token") t.Fatalf("missing token")
} }
// By default, checks start in critical state. // By default, checks start in critical state.
state := srv.agent.state.Checks()["test"] state := srv.agent.state.Checks()[checkID]
if state.Status != structs.HealthCritical { if state.Status != structs.HealthCritical {
t.Fatalf("bad: %v", state) t.Fatalf("bad: %v", state)
} }
@ -343,15 +345,16 @@ func TestHTTPAgentRegisterCheckPassing(t *testing.T) {
} }
// Ensure we have a check mapping // Ensure we have a check mapping
if _, ok := srv.agent.state.Checks()["test"]; !ok { checkID := types.CheckID("test")
if _, ok := srv.agent.state.Checks()[checkID]; !ok {
t.Fatalf("missing test check") t.Fatalf("missing test check")
} }
if _, ok := srv.agent.checkTTLs["test"]; !ok { if _, ok := srv.agent.checkTTLs[checkID]; !ok {
t.Fatalf("missing test check ttl") t.Fatalf("missing test check ttl")
} }
state := srv.agent.state.Checks()["test"] state := srv.agent.state.Checks()[checkID]
if state.Status != structs.HealthPassing { if state.Status != structs.HealthPassing {
t.Fatalf("bad: %v", state) t.Fatalf("bad: %v", state)
} }

View File

@ -192,17 +192,17 @@ func (l *localState) Services() map[string]*structs.NodeService {
return services return services
} }
// CheckToken is used to return the configured health check token, or // CheckToken is used to return the configured health check token for a
// if none is configured, the default agent ACL token. // Check, or if none is configured, the default agent ACL token.
func (l *localState) CheckToken(id types.CheckID) string { func (l *localState) CheckToken(checkID types.CheckID) string {
l.RLock() l.RLock()
defer l.RUnlock() defer l.RUnlock()
return l.checkToken(id) return l.checkToken(checkID)
} }
// checkToken returns an ACL token associated with a check. // checkToken returns an ACL token associated with a check.
func (l *localState) checkToken(id types.CheckID) string { func (l *localState) checkToken(checkID types.CheckID) string {
token := l.checkTokens[id] token := l.checkTokens[checkID]
if token == "" { if token == "" {
token = l.config.ACLToken token = l.config.ACLToken
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/hashicorp/consul/consul" "github.com/hashicorp/consul/consul"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/types"
) )
const ( const (
@ -38,7 +39,7 @@ func (s *HTTPServer) SessionCreate(resp http.ResponseWriter, req *http.Request)
Op: structs.SessionCreate, Op: structs.SessionCreate,
Session: structs.Session{ Session: structs.Session{
Node: s.agent.config.NodeName, Node: s.agent.config.NodeName,
Checks: []string{consul.SerfCheckID}, Checks: []types.CheckID{consul.SerfCheckID},
LockDelay: 15 * time.Second, LockDelay: 15 * time.Second,
Behavior: structs.SessionKeysRelease, Behavior: structs.SessionKeysRelease,
TTL: "", TTL: "",

View File

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/consul/consul" "github.com/hashicorp/consul/consul"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/types"
) )
func TestSessionCreate(t *testing.T) { func TestSessionCreate(t *testing.T) {
@ -38,7 +39,7 @@ func TestSessionCreate(t *testing.T) {
raw := map[string]interface{}{ raw := map[string]interface{}{
"Name": "my-cool-session", "Name": "my-cool-session",
"Node": srv.agent.config.NodeName, "Node": srv.agent.config.NodeName,
"Checks": []string{consul.SerfCheckID, "consul"}, "Checks": []types.CheckID{consul.SerfCheckID, "consul"},
"LockDelay": "20s", "LockDelay": "20s",
} }
enc.Encode(raw) enc.Encode(raw)
@ -86,7 +87,7 @@ func TestSessionCreateDelete(t *testing.T) {
raw := map[string]interface{}{ raw := map[string]interface{}{
"Name": "my-cool-session", "Name": "my-cool-session",
"Node": srv.agent.config.NodeName, "Node": srv.agent.config.NodeName,
"Checks": []string{consul.SerfCheckID, "consul"}, "Checks": []types.CheckID{consul.SerfCheckID, "consul"},
"LockDelay": "20s", "LockDelay": "20s",
"Behavior": structs.SessionKeysDelete, "Behavior": structs.SessionKeysDelete,
} }

View File

@ -43,7 +43,7 @@ func (s *ServiceDefinition) CheckTypes() (checks CheckTypes) {
return return
} }
// ChecKDefinition is used to JSON decode the Check definitions // CheckDefinition is used to JSON decode the Check definitions
type CheckDefinition struct { type CheckDefinition struct {
ID types.CheckID ID types.CheckID
Name string Name string

View File

@ -117,7 +117,7 @@ func (c *MaintCommand) Run(args []string) int {
c.Ui.Output(" Name: " + nodeName) c.Ui.Output(" Name: " + nodeName)
c.Ui.Output(" Reason: " + check.Notes) c.Ui.Output(" Reason: " + check.Notes)
c.Ui.Output("") c.Ui.Output("")
} else if strings.HasPrefix(check.CheckID, "_service_maintenance:") { } else if strings.HasPrefix(string(check.CheckID), "_service_maintenance:") {
c.Ui.Output("Service:") c.Ui.Output("Service:")
c.Ui.Output(" ID: " + check.ServiceID) c.Ui.Output(" ID: " + check.ServiceID)
c.Ui.Output(" Reason: " + check.Notes) c.Ui.Output(" Reason: " + check.Notes)

View File

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/consul/consul/state" "github.com/hashicorp/consul/consul/state"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/types"
"github.com/hashicorp/go-uuid" "github.com/hashicorp/go-uuid"
"github.com/hashicorp/raft" "github.com/hashicorp/raft"
) )
@ -860,7 +861,7 @@ func TestFSM_SessionCreate_Destroy(t *testing.T) {
Session: structs.Session{ Session: structs.Session{
ID: generateUUID(), ID: generateUUID(),
Node: "foo", Node: "foo",
Checks: []string{"web"}, Checks: []types.CheckID{"web"},
}, },
} }
buf, err := structs.Encode(structs.SessionRequestType, req) buf, err := structs.Encode(structs.SessionRequestType, req)

View File

@ -10,18 +10,19 @@ import (
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
"github.com/hashicorp/consul/consul/agent" "github.com/hashicorp/consul/consul/agent"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/types"
"github.com/hashicorp/raft" "github.com/hashicorp/raft"
"github.com/hashicorp/serf/serf" "github.com/hashicorp/serf/serf"
) )
const ( const (
SerfCheckID = "serfHealth" SerfCheckID types.CheckID = "serfHealth"
SerfCheckName = "Serf Health Status" SerfCheckName = "Serf Health Status"
SerfCheckAliveOutput = "Agent alive and reachable" SerfCheckAliveOutput = "Agent alive and reachable"
SerfCheckFailedOutput = "Agent not live or unreachable" SerfCheckFailedOutput = "Agent not live or unreachable"
ConsulServiceID = "consul" ConsulServiceID = "consul"
ConsulServiceName = "consul" ConsulServiceName = "consul"
newLeaderEvent = "consul:new-leader" newLeaderEvent = "consul:new-leader"
) )
// monitorLeadership is used to monitor if we acquire or lose our role // monitorLeadership is used to monitor if we acquire or lose our role

View File

@ -83,7 +83,7 @@ type IndexEntry struct {
// store and thus it is not exported. // store and thus it is not exported.
type sessionCheck struct { type sessionCheck struct {
Node string Node string
CheckID string CheckID types.CheckID
Session string Session string
} }
@ -969,7 +969,7 @@ func (s *StateStore) EnsureCheck(idx uint64, hc *structs.HealthCheck) error {
func (s *StateStore) ensureCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager, func (s *StateStore) ensureCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager,
hc *structs.HealthCheck) error { hc *structs.HealthCheck) error {
// Check if we have an existing health check // Check if we have an existing health check
existing, err := tx.First("checks", "id", hc.Node, hc.CheckID) existing, err := tx.First("checks", "id", hc.Node, string(hc.CheckID))
if err != nil { if err != nil {
return fmt.Errorf("failed health check lookup: %s", err) return fmt.Errorf("failed health check lookup: %s", err)
} }
@ -1014,7 +1014,7 @@ func (s *StateStore) ensureCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatc
// Delete any sessions for this check if the health is critical. // Delete any sessions for this check if the health is critical.
if hc.Status == structs.HealthCritical { if hc.Status == structs.HealthCritical {
mappings, err := tx.Get("session_checks", "node_check", hc.Node, hc.CheckID) mappings, err := tx.Get("session_checks", "node_check", hc.Node, string(hc.CheckID))
if err != nil { if err != nil {
return fmt.Errorf("failed session checks lookup: %s", err) return fmt.Errorf("failed session checks lookup: %s", err)
} }
@ -1120,13 +1120,13 @@ func (s *StateStore) parseChecks(idx uint64, iter memdb.ResultIterator) (uint64,
} }
// DeleteCheck is used to delete a health check registration. // DeleteCheck is used to delete a health check registration.
func (s *StateStore) DeleteCheck(idx uint64, node string, id types.CheckID) error { func (s *StateStore) DeleteCheck(idx uint64, node string, checkID types.CheckID) error {
tx := s.db.Txn(true) tx := s.db.Txn(true)
defer tx.Abort() defer tx.Abort()
// Call the check deletion // Call the check deletion
watches := NewDumbWatchManager(s.tableWatches) watches := NewDumbWatchManager(s.tableWatches)
if err := s.deleteCheckTxn(tx, idx, watches, node, id); err != nil { if err := s.deleteCheckTxn(tx, idx, watches, node, checkID); err != nil {
return err return err
} }
@ -1137,9 +1137,9 @@ func (s *StateStore) DeleteCheck(idx uint64, node string, id types.CheckID) erro
// deleteCheckTxn is the inner method used to call a health // deleteCheckTxn is the inner method used to call a health
// check deletion within an existing transaction. // check deletion within an existing transaction.
func (s *StateStore) deleteCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager, node string, id types.CheckID) error { func (s *StateStore) deleteCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager, node string, checkID types.CheckID) error {
// Try to retrieve the existing health check. // Try to retrieve the existing health check.
hc, err := tx.First("checks", "id", node, id) hc, err := tx.First("checks", "id", node, string(checkID))
if err != nil { if err != nil {
return fmt.Errorf("check lookup failed: %s", err) return fmt.Errorf("check lookup failed: %s", err)
} }
@ -1156,7 +1156,7 @@ func (s *StateStore) deleteCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatc
} }
// Delete any sessions for this check. // Delete any sessions for this check.
mappings, err := tx.Get("session_checks", "node_check", node, id) mappings, err := tx.Get("session_checks", "node_check", node, string(checkID))
if err != nil { if err != nil {
return fmt.Errorf("failed session checks lookup: %s", err) return fmt.Errorf("failed session checks lookup: %s", err)
} }
@ -1413,7 +1413,7 @@ func (s *StateStore) sessionCreateTxn(tx *memdb.Txn, idx uint64, sess *structs.S
// Go over the session checks and ensure they exist. // Go over the session checks and ensure they exist.
for _, checkID := range sess.Checks { for _, checkID := range sess.Checks {
check, err := tx.First("checks", "id", sess.Node, checkID) check, err := tx.First("checks", "id", sess.Node, string(checkID))
if err != nil { if err != nil {
return fmt.Errorf("failed check lookup: %s", err) return fmt.Errorf("failed check lookup: %s", err)
} }

View File

@ -96,7 +96,7 @@ func testRegisterCheck(t *testing.T, s *StateStore, idx uint64,
tx := s.db.Txn(false) tx := s.db.Txn(false)
defer tx.Abort() defer tx.Abort()
c, err := tx.First("checks", "id", nodeID, checkID) c, err := tx.First("checks", "id", nodeID, string(checkID))
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -2151,7 +2151,7 @@ func TestStateStore_SessionCreate_SessionGet(t *testing.T) {
sess = &structs.Session{ sess = &structs.Session{
ID: testUUID(), ID: testUUID(),
Node: "node1", Node: "node1",
Checks: []string{"check1"}, Checks: []types.CheckID{"check1"},
} }
err = s.SessionCreate(3, sess) err = s.SessionCreate(3, sess)
if err == nil || !strings.Contains(err.Error(), "Missing check") { if err == nil || !strings.Contains(err.Error(), "Missing check") {
@ -2176,7 +2176,7 @@ func TestStateStore_SessionCreate_SessionGet(t *testing.T) {
sess2 := &structs.Session{ sess2 := &structs.Session{
ID: testUUID(), ID: testUUID(),
Node: "node1", Node: "node1",
Checks: []string{"check1", "check2"}, Checks: []types.CheckID{"check1", "check2"},
} }
if err := s.SessionCreate(6, sess2); err != nil { if err := s.SessionCreate(6, sess2); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -2210,7 +2210,7 @@ func TestStateStore_SessionCreate_SessionGet(t *testing.T) {
for i, check := 0, checks.Next(); check != nil; i, check = i+1, checks.Next() { for i, check := 0, checks.Next(); check != nil; i, check = i+1, checks.Next() {
expectCheck := &sessionCheck{ expectCheck := &sessionCheck{
Node: "node1", Node: "node1",
CheckID: fmt.Sprintf("check%d", i+1), CheckID: types.CheckID(fmt.Sprintf("check%d", i+1)),
Session: sess2.ID, Session: sess2.ID,
} }
if actual := check.(*sessionCheck); !reflect.DeepEqual(actual, expectCheck) { if actual := check.(*sessionCheck); !reflect.DeepEqual(actual, expectCheck) {
@ -2408,7 +2408,7 @@ func TestStateStore_Session_Snapshot_Restore(t *testing.T) {
ID: session1, ID: session1,
Node: "node1", Node: "node1",
Behavior: structs.SessionKeysDelete, Behavior: structs.SessionKeysDelete,
Checks: []string{"check1"}, Checks: []types.CheckID{"check1"},
}, },
&structs.Session{ &structs.Session{
ID: testUUID(), ID: testUUID(),
@ -2625,7 +2625,7 @@ func TestStateStore_Session_Invalidate_DeleteService(t *testing.T) {
session := &structs.Session{ session := &structs.Session{
ID: testUUID(), ID: testUUID(),
Node: "foo", Node: "foo",
Checks: []string{"api"}, Checks: []types.CheckID{"api"},
} }
if err := s.SessionCreate(14, session); err != nil { if err := s.SessionCreate(14, session); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
@ -2673,7 +2673,7 @@ func TestStateStore_Session_Invalidate_Critical_Check(t *testing.T) {
session := &structs.Session{ session := &structs.Session{
ID: testUUID(), ID: testUUID(),
Node: "foo", Node: "foo",
Checks: []string{"bar"}, Checks: []types.CheckID{"bar"},
} }
if err := s.SessionCreate(14, session); err != nil { if err := s.SessionCreate(14, session); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
@ -2720,7 +2720,7 @@ func TestStateStore_Session_Invalidate_DeleteCheck(t *testing.T) {
session := &structs.Session{ session := &structs.Session{
ID: testUUID(), ID: testUUID(),
Node: "foo", Node: "foo",
Checks: []string{"bar"}, Checks: []types.CheckID{"bar"},
} }
if err := s.SessionCreate(14, session); err != nil { if err := s.SessionCreate(14, session); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)

View File

@ -618,7 +618,7 @@ type Session struct {
ID string ID string
Name string Name string
Node string Node string
Checks []string Checks []types.CheckID
LockDelay time.Duration LockDelay time.Duration
Behavior SessionBehavior // What to do when session is invalidated Behavior SessionBehavior // What to do when session is invalidated
TTL string TTL string

View File

@ -5,6 +5,8 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"github.com/hashicorp/consul/types"
) )
func TestEncodeDecode(t *testing.T) { func TestEncodeDecode(t *testing.T) {
@ -184,7 +186,7 @@ func TestStructs_HealthCheck_IsSame(t *testing.T) {
t.Fatalf("should not care about Raft fields") t.Fatalf("should not care about Raft fields")
} }
check := func(field *string) { checkCheckIDField := func(field *types.CheckID) {
if !hc.IsSame(other) || !other.IsSame(hc) { if !hc.IsSame(other) || !other.IsSame(hc) {
t.Fatalf("should be the same") t.Fatalf("should be the same")
} }
@ -201,15 +203,31 @@ func TestStructs_HealthCheck_IsSame(t *testing.T) {
} }
} }
check(&other.Node) checkStringField := func(field *string) {
checkStr := string(other.CheckID) if !hc.IsSame(other) || !other.IsSame(hc) {
check(&checkStr) t.Fatalf("should be the same")
check(&other.Name) }
check(&other.Status)
check(&other.Notes) old := *field
check(&other.Output) *field = "XXX"
check(&other.ServiceID) if hc.IsSame(other) || other.IsSame(hc) {
check(&other.ServiceName) t.Fatalf("should not be the same")
}
*field = old
if !hc.IsSame(other) || !other.IsSame(hc) {
t.Fatalf("should be the same")
}
}
checkStringField(&other.Node)
checkCheckIDField(&other.CheckID)
checkStringField(&other.Name)
checkStringField(&other.Status)
checkStringField(&other.Notes)
checkStringField(&other.Output)
checkStringField(&other.ServiceID)
checkStringField(&other.ServiceName)
} }
func TestStructs_HealthCheck_Clone(t *testing.T) { func TestStructs_HealthCheck_Clone(t *testing.T) {