diff --git a/command/agent/agent.go b/command/agent/agent.go index ecdfb21dd..138f1799c 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -19,6 +19,7 @@ import ( "github.com/hashicorp/consul/consul/state" "github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/types" "github.com/hashicorp/serf/coordinate" "github.com/hashicorp/serf/serf" ) @@ -74,19 +75,19 @@ type Agent struct { state localState // checkMonitors maps the check ID to an associated monitor - checkMonitors map[structs.CheckID]*CheckMonitor + checkMonitors map[types.CheckID]*CheckMonitor // checkHTTPs maps the check ID to an associated HTTP check - checkHTTPs map[structs.CheckID]*CheckHTTP + checkHTTPs map[types.CheckID]*CheckHTTP // checkTCPs maps the check ID to an associated TCP check - checkTCPs map[structs.CheckID]*CheckTCP + checkTCPs map[types.CheckID]*CheckTCP // checkTTLs maps the check ID to an associated check TTL - checkTTLs map[structs.CheckID]*CheckTTL + checkTTLs map[types.CheckID]*CheckTTL // checkDockers maps the check ID to an associated Docker Exec based check - checkDockers map[structs.CheckID]*CheckDocker + checkDockers map[types.CheckID]*CheckDocker // checkLock protects updates to the check* maps checkLock sync.Mutex @@ -176,11 +177,11 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) { config: config, logger: log.New(logOutput, "", log.LstdFlags), logOutput: logOutput, - checkMonitors: make(map[structs.CheckID]*CheckMonitor), - checkTTLs: make(map[structs.CheckID]*CheckTTL), - checkHTTPs: make(map[structs.CheckID]*CheckHTTP), - checkTCPs: make(map[structs.CheckID]*CheckTCP), - checkDockers: make(map[structs.CheckID]*CheckDocker), + checkMonitors: make(map[types.CheckID]*CheckMonitor), + checkTTLs: make(map[types.CheckID]*CheckTTL), + checkHTTPs: make(map[types.CheckID]*CheckHTTP), + checkTCPs: make(map[types.CheckID]*CheckTCP), + checkDockers: make(map[types.CheckID]*CheckDocker), eventCh: make(chan serf.UserEvent, 1024), eventBuf: make([]*UserEvent, 256), shutdownCh: make(chan struct{}), @@ -724,7 +725,7 @@ func (a *Agent) persistCheck(check *structs.HealthCheck, chkType *CheckType) err } // purgeCheck removes a persisted check definition file from the data dir -func (a *Agent) purgeCheck(checkID structs.CheckID) error { +func (a *Agent) purgeCheck(checkID types.CheckID) error { checkPath := filepath.Join(a.config.DataDir, checksDir, checkIDHash(checkID)) if _, err := os.Stat(checkPath); err == nil { return os.Remove(checkPath) @@ -791,7 +792,7 @@ func (a *Agent) AddService(service *structs.NodeService, chkTypes CheckTypes, pe } check := &structs.HealthCheck{ Node: a.config.NodeName, - CheckID: structs.CheckID(checkID), + CheckID: types.CheckID(checkID), Name: fmt.Sprintf("Service '%s' check", service.Service), Status: structs.HealthCritical, Notes: chkType.Notes, @@ -998,7 +999,7 @@ func (a *Agent) AddCheck(check *structs.HealthCheck, chkType *CheckType, persist // RemoveCheck is used to remove a health check. // The agent will make a best effort to ensure it is deregistered -func (a *Agent) RemoveCheck(checkID structs.CheckID, persist bool) error { +func (a *Agent) RemoveCheck(checkID types.CheckID, persist bool) error { // Validate CheckID if checkID == "" { return fmt.Errorf("CheckID missing") @@ -1041,7 +1042,7 @@ func (a *Agent) RemoveCheck(checkID structs.CheckID, persist bool) error { // UpdateCheck is used to update the status of a check. // This can only be used with checks of the TTL type. -func (a *Agent) UpdateCheck(checkID structs.CheckID, status, output string) error { +func (a *Agent) UpdateCheck(checkID types.CheckID, status, output string) error { a.checkLock.Lock() defer a.checkLock.Unlock() @@ -1129,7 +1130,7 @@ func (a *Agent) loadCheckState(check *structs.HealthCheck) error { } // purgeCheckState is used to purge the state of a check from the data dir -func (a *Agent) purgeCheckState(checkID structs.CheckID) error { +func (a *Agent) purgeCheckState(checkID types.CheckID) error { file := filepath.Join(a.config.DataDir, checkStateDir, checkIDHash(checkID)) err := os.Remove(file) if os.IsNotExist(err) { @@ -1393,22 +1394,22 @@ func (a *Agent) unloadChecks() error { // snapshotCheckState is used to snapshot the current state of the health // checks. This is done before we reload our checks, so that we can properly // restore into the same state. -func (a *Agent) snapshotCheckState() map[structs.CheckID]*structs.HealthCheck { +func (a *Agent) snapshotCheckState() map[types.CheckID]*structs.HealthCheck { return a.state.Checks() } // restoreCheckState is used to reset the health state based on a snapshot. // This is done after we finish the reload to avoid any unnecessary flaps // in health state and potential session invalidations. -func (a *Agent) restoreCheckState(snap map[structs.CheckID]*structs.HealthCheck) { +func (a *Agent) restoreCheckState(snap map[types.CheckID]*structs.HealthCheck) { for id, check := range snap { a.state.UpdateCheck(id, check.Status, check.Output) } } // serviceMaintCheckID returns the ID of a given service's maintenance check -func serviceMaintCheckID(serviceID string) structs.CheckID { - return structs.CheckID(fmt.Sprintf("%s:%s", serviceMaintCheckPrefix, serviceID)) +func serviceMaintCheckID(serviceID string) types.CheckID { + return types.CheckID(fmt.Sprintf("%s:%s", serviceMaintCheckPrefix, serviceID)) } // EnableServiceMaintenance will register a false health check against the given diff --git a/command/agent/agent_endpoint.go b/command/agent/agent_endpoint.go index 729fa88fc..83e2e94cb 100644 --- a/command/agent/agent_endpoint.go +++ b/command/agent/agent_endpoint.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hashicorp/consul/consul/structs" + "github.com/hashicorp/consul/types" "github.com/hashicorp/serf/coordinate" "github.com/hashicorp/serf/serf" ) @@ -130,7 +131,7 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ } func (s *HTTPServer) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) { - checkID := structs.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/deregister/")) + checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/deregister/")) if err := s.agent.RemoveCheck(checkID, true); err != nil { return nil, err } @@ -139,7 +140,7 @@ func (s *HTTPServer) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Re } func (s *HTTPServer) AgentCheckPass(resp http.ResponseWriter, req *http.Request) (interface{}, error) { - checkID := structs.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/pass/")) + checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/pass/")) note := req.URL.Query().Get("note") if err := s.agent.UpdateCheck(checkID, structs.HealthPassing, note); err != nil { return nil, err @@ -149,7 +150,7 @@ func (s *HTTPServer) AgentCheckPass(resp http.ResponseWriter, req *http.Request) } func (s *HTTPServer) AgentCheckWarn(resp http.ResponseWriter, req *http.Request) (interface{}, error) { - checkID := structs.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/warn/")) + checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/warn/")) note := req.URL.Query().Get("note") if err := s.agent.UpdateCheck(checkID, structs.HealthWarning, note); err != nil { return nil, err @@ -159,7 +160,7 @@ func (s *HTTPServer) AgentCheckWarn(resp http.ResponseWriter, req *http.Request) } func (s *HTTPServer) AgentCheckFail(resp http.ResponseWriter, req *http.Request) (interface{}, error) { - checkID := structs.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/fail/")) + checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/fail/")) note := req.URL.Query().Get("note") if err := s.agent.UpdateCheck(checkID, structs.HealthCritical, note); err != nil { return nil, err @@ -212,7 +213,7 @@ func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Reques update.Output[:CheckBufSize], CheckBufSize, total) } - checkID := structs.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/update/")) + checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/update/")) if err := s.agent.UpdateCheck(checkID, update.Status, update.Output); err != nil { return nil, err } diff --git a/command/agent/check.go b/command/agent/check.go index 578517a50..a0102b991 100644 --- a/command/agent/check.go +++ b/command/agent/check.go @@ -16,6 +16,7 @@ import ( docker "github.com/fsouza/go-dockerclient" "github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/types" "github.com/hashicorp/go-cleanhttp" ) @@ -90,7 +91,7 @@ func (c *CheckType) IsDocker() bool { // to notify when a check has a status update. The update // should take care to be idempotent. type CheckNotifier interface { - UpdateCheck(checkID structs.CheckID, status, output string) + UpdateCheck(checkID types.CheckID, status, output string) } // CheckMonitor is used to periodically invoke a script to @@ -98,7 +99,7 @@ type CheckNotifier interface { // nagios plugins and expects the output in the same format. type CheckMonitor struct { Notify CheckNotifier - CheckID structs.CheckID + CheckID types.CheckID Script string Interval time.Duration Timeout time.Duration @@ -231,7 +232,7 @@ func (c *CheckMonitor) check() { // automatically set to critical. type CheckTTL struct { Notify CheckNotifier - CheckID structs.CheckID + CheckID types.CheckID TTL time.Duration Logger *log.Logger @@ -322,7 +323,7 @@ type persistedCheck struct { // expiration timestamp which is used to determine staleness on later // agent restarts. type persistedCheckState struct { - CheckID structs.CheckID + CheckID types.CheckID Output string Status string Expires int64 @@ -336,7 +337,7 @@ type persistedCheckState struct { // or if the request returns an error type CheckHTTP struct { Notify CheckNotifier - CheckID structs.CheckID + CheckID types.CheckID HTTP string Interval time.Duration Timeout time.Duration @@ -462,7 +463,7 @@ func (c *CheckHTTP) check() { // The check is critical if the connection returns an error type CheckTCP struct { Notify CheckNotifier - CheckID structs.CheckID + CheckID types.CheckID TCP string Interval time.Duration Timeout time.Duration @@ -553,7 +554,7 @@ type DockerClient interface { // with nagios plugins and expects the output in the same format. type CheckDocker struct { Notify CheckNotifier - CheckID structs.CheckID + CheckID types.CheckID Script string DockerContainerID string Shell string diff --git a/command/agent/check_test.go b/command/agent/check_test.go index 2e23c75c2..53d203eeb 100644 --- a/command/agent/check_test.go +++ b/command/agent/check_test.go @@ -18,15 +18,16 @@ import ( docker "github.com/fsouza/go-dockerclient" "github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/testutil" + "github.com/hashicorp/consul/types" ) type MockNotify struct { - state map[structs.CheckID]string - updates map[structs.CheckID]int - output map[structs.CheckID]string + state map[types.CheckID]string + updates map[types.CheckID]int + output map[types.CheckID]string } -func (m *MockNotify) UpdateCheck(id structs.CheckID, status, output string) { +func (m *MockNotify) UpdateCheck(id types.CheckID, status, output string) { m.state[id] = status old := m.updates[id] m.updates[id] = old + 1 @@ -35,13 +36,13 @@ func (m *MockNotify) UpdateCheck(id structs.CheckID, status, output string) { func expectStatus(t *testing.T, script, status string) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckMonitor{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: script, Interval: 10 * time.Millisecond, Logger: log.New(os.Stderr, "", log.LstdFlags), @@ -84,13 +85,13 @@ func TestCheckMonitor_BadCmd(t *testing.T) { func TestCheckMonitor_Timeout(t *testing.T) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckMonitor{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: "sleep 1 && exit 0", Interval: 10 * time.Millisecond, Timeout: 5 * time.Millisecond, @@ -114,13 +115,13 @@ func TestCheckMonitor_Timeout(t *testing.T) { func TestCheckMonitor_RandomStagger(t *testing.T) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckMonitor{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: "exit 0", Interval: 25 * time.Millisecond, Logger: log.New(os.Stderr, "", log.LstdFlags), @@ -143,13 +144,13 @@ func TestCheckMonitor_RandomStagger(t *testing.T) { func TestCheckMonitor_LimitOutput(t *testing.T) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckMonitor{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: "od -N 81920 /dev/urandom", Interval: 25 * time.Millisecond, Logger: log.New(os.Stderr, "", log.LstdFlags), @@ -168,13 +169,13 @@ func TestCheckMonitor_LimitOutput(t *testing.T) { func TestCheckTTL(t *testing.T) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckTTL{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), TTL: 100 * time.Millisecond, Logger: log.New(os.Stderr, "", log.LstdFlags), } @@ -229,13 +230,13 @@ func mockHTTPServer(responseCode int) *httptest.Server { func expectHTTPStatus(t *testing.T, url string, status string) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckHTTP{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), HTTP: url, Interval: 10 * time.Millisecond, Logger: log.New(os.Stderr, "", log.LstdFlags), @@ -329,14 +330,14 @@ func TestCheckHTTPTimeout(t *testing.T) { defer server.Close() mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckHTTP{ Notify: mock, - CheckID: structs.CheckID("bar"), + CheckID: types.CheckID("bar"), HTTP: server.URL, Timeout: 5 * time.Millisecond, Interval: 10 * time.Millisecond, @@ -360,7 +361,7 @@ func TestCheckHTTPTimeout(t *testing.T) { func TestCheckHTTP_disablesKeepAlives(t *testing.T) { check := &CheckHTTP{ - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), HTTP: "http://foo.bar/baz", Interval: 10 * time.Second, Logger: log.New(os.Stderr, "", log.LstdFlags), @@ -395,13 +396,13 @@ func mockTCPServer(network string) net.Listener { func expectTCPStatus(t *testing.T, tcp string, status string) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckTCP{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), TCP: tcp, Interval: 10 * time.Millisecond, Logger: log.New(os.Stderr, "", log.LstdFlags), @@ -575,13 +576,13 @@ func (d *fakeDockerClientWithExecInfoErrors) InspectExec(id string) (*docker.Exe func expectDockerCheckStatus(t *testing.T, dockerClient DockerClient, status string, output string) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckDocker{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: "/health.sh", DockerContainerID: "54432bad1fc7", Shell: "/bin/sh", @@ -635,13 +636,13 @@ func TestDockerCheckWhenExecInfoFails(t *testing.T) { func TestDockerCheckDefaultToSh(t *testing.T) { os.Setenv("SHELL", "") mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckDocker{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: "/health.sh", DockerContainerID: "54432bad1fc7", Interval: 10 * time.Millisecond, @@ -659,14 +660,14 @@ func TestDockerCheckDefaultToSh(t *testing.T) { func TestDockerCheckUseShellFromEnv(t *testing.T) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } os.Setenv("SHELL", "/bin/bash") check := &CheckDocker{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: "/health.sh", DockerContainerID: "54432bad1fc7", Interval: 10 * time.Millisecond, @@ -685,13 +686,13 @@ func TestDockerCheckUseShellFromEnv(t *testing.T) { func TestDockerCheckTruncateOutput(t *testing.T) { mock := &MockNotify{ - state: make(map[structs.CheckID]string), - updates: make(map[structs.CheckID]int), - output: make(map[structs.CheckID]string), + state: make(map[types.CheckID]string), + updates: make(map[types.CheckID]int), + output: make(map[types.CheckID]string), } check := &CheckDocker{ Notify: mock, - CheckID: structs.CheckID("foo"), + CheckID: types.CheckID("foo"), Script: "/health.sh", DockerContainerID: "54432bad1fc7", Shell: "/bin/sh", diff --git a/command/agent/local.go b/command/agent/local.go index 11d85271c..66a05721c 100644 --- a/command/agent/local.go +++ b/command/agent/local.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/consul/consul" "github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/types" ) const ( @@ -56,12 +57,12 @@ type localState struct { serviceTokens map[string]string // Checks tracks the local checks - checks map[structs.CheckID]*structs.HealthCheck - checkStatus map[structs.CheckID]syncStatus - checkTokens map[structs.CheckID]string + checks map[types.CheckID]*structs.HealthCheck + checkStatus map[types.CheckID]syncStatus + checkTokens map[types.CheckID]string // Used to track checks that are being deferred - deferCheck map[structs.CheckID]*time.Timer + deferCheck map[types.CheckID]*time.Timer // consulCh is used to inform of a change to the known // consul nodes. This may be used to retry a sync run @@ -79,10 +80,10 @@ func (l *localState) Init(config *Config, logger *log.Logger) { l.services = make(map[string]*structs.NodeService) l.serviceStatus = make(map[string]syncStatus) l.serviceTokens = make(map[string]string) - l.checks = make(map[structs.CheckID]*structs.HealthCheck) - l.checkStatus = make(map[structs.CheckID]syncStatus) - l.checkTokens = make(map[structs.CheckID]string) - l.deferCheck = make(map[structs.CheckID]*time.Timer) + l.checks = make(map[types.CheckID]*structs.HealthCheck) + l.checkStatus = make(map[types.CheckID]syncStatus) + l.checkTokens = make(map[types.CheckID]string) + l.deferCheck = make(map[types.CheckID]*time.Timer) l.consulCh = make(chan struct{}, 1) l.triggerCh = make(chan struct{}, 1) } @@ -193,14 +194,14 @@ func (l *localState) Services() map[string]*structs.NodeService { // CheckToken is used to return the configured health check token, or // if none is configured, the default agent ACL token. -func (l *localState) CheckToken(id structs.CheckID) string { +func (l *localState) CheckToken(id types.CheckID) string { l.RLock() defer l.RUnlock() return l.checkToken(id) } // checkToken returns an ACL token associated with a check. -func (l *localState) checkToken(id structs.CheckID) string { +func (l *localState) checkToken(id types.CheckID) string { token := l.checkTokens[id] if token == "" { token = l.config.ACLToken @@ -226,7 +227,7 @@ func (l *localState) AddCheck(check *structs.HealthCheck, token string) { // RemoveCheck is used to remove a health check from the local state. // The agent will make a best effort to ensure it is deregistered -func (l *localState) RemoveCheck(checkID structs.CheckID) { +func (l *localState) RemoveCheck(checkID types.CheckID) { l.Lock() defer l.Unlock() @@ -237,7 +238,7 @@ func (l *localState) RemoveCheck(checkID structs.CheckID) { } // UpdateCheck is used to update the status of a check -func (l *localState) UpdateCheck(checkID structs.CheckID, status, output string) { +func (l *localState) UpdateCheck(checkID types.CheckID, status, output string) { l.Lock() defer l.Unlock() @@ -282,8 +283,8 @@ func (l *localState) UpdateCheck(checkID structs.CheckID, status, output string) // Checks returns the locally registered checks that the // agent is aware of and are being kept in sync with the server -func (l *localState) Checks() map[structs.CheckID]*structs.HealthCheck { - checks := make(map[structs.CheckID]*structs.HealthCheck) +func (l *localState) Checks() map[types.CheckID]*structs.HealthCheck { + checks := make(map[types.CheckID]*structs.HealthCheck) l.RLock() defer l.RUnlock() @@ -406,7 +407,7 @@ func (l *localState) setSyncState() error { } // Index the remote health checks to improve efficiency - checkIndex := make(map[structs.CheckID]*structs.HealthCheck, len(checks)) + checkIndex := make(map[types.CheckID]*structs.HealthCheck, len(checks)) for _, check := range checks { checkIndex[check.CheckID] = check } @@ -546,7 +547,7 @@ func (l *localState) deleteService(id string) error { } // deleteCheck is used to delete a service from the server -func (l *localState) deleteCheck(id structs.CheckID) error { +func (l *localState) deleteCheck(id types.CheckID) error { if id == "" { return fmt.Errorf("CheckID missing") } @@ -619,7 +620,7 @@ func (l *localState) syncService(id string) error { } // syncCheck is used to sync a check to the server -func (l *localState) syncCheck(id structs.CheckID) error { +func (l *localState) syncCheck(id types.CheckID) error { // Pull in the associated service if any check := l.checks[id] var service *structs.NodeService diff --git a/command/agent/structs.go b/command/agent/structs.go index 8ccdfd1fc..442b4b296 100644 --- a/command/agent/structs.go +++ b/command/agent/structs.go @@ -2,6 +2,7 @@ package agent import ( "github.com/hashicorp/consul/consul/structs" + "github.com/hashicorp/consul/types" ) // ServiceDefinition is used to JSON decode the Service definitions @@ -44,7 +45,7 @@ func (s *ServiceDefinition) CheckTypes() (checks CheckTypes) { // ChecKDefinition is used to JSON decode the Check definitions type CheckDefinition struct { - ID structs.CheckID + ID types.CheckID Name string Notes string ServiceID string @@ -66,7 +67,7 @@ func (c *CheckDefinition) HealthCheck(node string) *structs.HealthCheck { health.Status = c.Status } if health.CheckID == "" && health.Name != "" { - health.CheckID = structs.CheckID(health.Name) + health.CheckID = types.CheckID(health.Name) } return health } diff --git a/command/agent/util.go b/command/agent/util.go index 152939ee1..3916c70c9 100644 --- a/command/agent/util.go +++ b/command/agent/util.go @@ -12,7 +12,7 @@ import ( "strconv" "time" - "github.com/hashicorp/consul/consul/structs" + "github.com/hashicorp/consul/types" "github.com/hashicorp/go-msgpack/codec" ) @@ -72,9 +72,9 @@ func stringHash(s string) string { return fmt.Sprintf("%x", md5.Sum([]byte(s))) } -// checkIDHash returns a simple md5sum for a structs.CheckID. -func checkIDHash(checkID structs.CheckID) string { - return fmt.Sprintf("%x", md5.Sum([]byte(string(checkID)))) +// checkIDHash returns a simple md5sum for a types.CheckID. +func checkIDHash(checkID types.CheckID) string { + return stringHash(string(checkID)) } // FilePermissions is an interface which allows a struct to set diff --git a/consul/catalog_endpoint.go b/consul/catalog_endpoint.go index 9b5d2b47c..06f81993b 100644 --- a/consul/catalog_endpoint.go +++ b/consul/catalog_endpoint.go @@ -6,6 +6,7 @@ import ( "github.com/armon/go-metrics" "github.com/hashicorp/consul/consul/structs" + "github.com/hashicorp/consul/types" ) // Catalog endpoint is used to manipulate the service catalog @@ -57,7 +58,7 @@ func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error } for _, check := range args.Checks { if check.CheckID == "" && check.Name != "" { - check.CheckID = structs.CheckID(check.Name) + check.CheckID = types.CheckID(check.Name) } if check.Node == "" { check.Node = args.Node diff --git a/consul/state/state_store.go b/consul/state/state_store.go index 1d4adabbc..bf5334a32 100644 --- a/consul/state/state_store.go +++ b/consul/state/state_store.go @@ -7,6 +7,7 @@ import ( "time" "github.com/hashicorp/consul/consul/structs" + "github.com/hashicorp/consul/types" "github.com/hashicorp/go-memdb" "github.com/hashicorp/serf/coordinate" ) @@ -594,7 +595,7 @@ func (s *StateStore) deleteNodeTxn(tx *memdb.Txn, idx uint64, nodeID string) err if err != nil { return fmt.Errorf("failed check lookup: %s", err) } - var cids []structs.CheckID + var cids []types.CheckID for check := checks.Next(); check != nil; check = checks.Next() { cids = append(cids, check.(*structs.HealthCheck).CheckID) } @@ -917,7 +918,7 @@ func (s *StateStore) deleteServiceTxn(tx *memdb.Txn, idx uint64, watches *DumbWa if err != nil { return fmt.Errorf("failed service check lookup: %s", err) } - var cids []structs.CheckID + var cids []types.CheckID for check := checks.Next(); check != nil; check = checks.Next() { cids = append(cids, check.(*structs.HealthCheck).CheckID) } @@ -1119,7 +1120,7 @@ func (s *StateStore) parseChecks(idx uint64, iter memdb.ResultIterator) (uint64, } // DeleteCheck is used to delete a health check registration. -func (s *StateStore) DeleteCheck(idx uint64, node string, id structs.CheckID) error { +func (s *StateStore) DeleteCheck(idx uint64, node string, id types.CheckID) error { tx := s.db.Txn(true) defer tx.Abort() @@ -1136,7 +1137,7 @@ func (s *StateStore) DeleteCheck(idx uint64, node string, id structs.CheckID) er // deleteCheckTxn is the inner method used to call a health // check deletion within an existing transaction. -func (s *StateStore) deleteCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager, node string, id structs.CheckID) error { +func (s *StateStore) deleteCheckTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager, node string, id types.CheckID) error { // Try to retrieve the existing health check. hc, err := tx.First("checks", "id", node, id) if err != nil { diff --git a/consul/state/state_store_test.go b/consul/state/state_store_test.go index 9f9c60d69..99ee6c7b5 100644 --- a/consul/state/state_store_test.go +++ b/consul/state/state_store_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/types" "github.com/hashicorp/serf/coordinate" ) @@ -82,7 +83,7 @@ func testRegisterService(t *testing.T, s *StateStore, idx uint64, nodeID, servic } func testRegisterCheck(t *testing.T, s *StateStore, idx uint64, - nodeID string, serviceID string, checkID structs.CheckID, state string) { + nodeID string, serviceID string, checkID types.CheckID, state string) { chk := &structs.HealthCheck{ Node: nodeID, CheckID: checkID, diff --git a/consul/structs/structs.go b/consul/structs/structs.go index 379dfcd96..c69db8ea2 100644 --- a/consul/structs/structs.go +++ b/consul/structs/structs.go @@ -8,6 +8,7 @@ import ( "time" "github.com/hashicorp/consul/acl" + "github.com/hashicorp/consul/types" "github.com/hashicorp/go-msgpack/codec" "github.com/hashicorp/serf/coordinate" ) @@ -181,7 +182,7 @@ type DeregisterRequest struct { Datacenter string Node string ServiceID string - CheckID CheckID + CheckID types.CheckID WriteRequest } @@ -364,19 +365,16 @@ type NodeServices struct { Services map[string]*NodeService } -// CheckID is a strongly typed string -type CheckID string - // HealthCheck represents a single check on a given node type HealthCheck struct { Node string - CheckID CheckID // Unique per-node ID - Name string // Check name - Status string // The current check status - Notes string // Additional notes with the status - Output string // Holds output of script runs - ServiceID string // optional associated service - ServiceName string // optional service name + CheckID types.CheckID // Unique per-node ID + Name string // Check name + Status string // The current check status + Notes string // Additional notes with the status + Output string // Holds output of script runs + ServiceID string // optional associated service + ServiceName string // optional service name RaftIndex } diff --git a/types/README.md b/types/README.md new file mode 100644 index 000000000..da662f4a1 --- /dev/null +++ b/types/README.md @@ -0,0 +1,39 @@ +# Consul `types` Package + +The Go language has a strong type system built into the language. The +`types` package corrals named types into a single package that is terminal in +`go`'s import graph. The `types` package should not have any downstream +dependencies. Each subsystem that defines its own set of types exists in its +own file, but all types are defined in the same package. + +# Why + +> Everything should be made as simple as possible, but not simpler. + +`string` is a useful container and underlying type for identifiers, however +the `string` type is effectively opaque to the compiler in terms of how a +given string is intended to be used. For instance, there is nothing +preventing the following from happening: + +```go +// `map` of Widgets, looked up by ID +var widgetLookup map[string]*Widget +// ... +var widgetID string = "widgetID" +w, found := widgetLookup[widgetID] + +// Bad! +var widgetName string = "name of widget" +w, found := widgetLookup[widgetName] +``` + +but this class of problem is entirely preventable: + +```go +type WidgetID string +var widgetLookup map[WidgetID]*Widget +var widgetName +``` + +TL;DR: intentions and idioms aren't statically checked by compilers. The +`types` package uses Go's strong type system to prevent this class of bug. diff --git a/types/checks.go b/types/checks.go new file mode 100644 index 000000000..25a136b4f --- /dev/null +++ b/types/checks.go @@ -0,0 +1,5 @@ +package types + +// CheckID is a strongly typed string used to uniquely represent a Consul +// Check on an Agent (a CheckID is not globally unique). +type CheckID string