diff --git a/command/agent/agent.go b/command/agent/agent.go index 5ec4409f1d..1a22f859ad 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -1027,7 +1027,7 @@ func serviceMaintCheckID(serviceID string) string { // EnableServiceMaintenance will register a false health check against the given // service ID with critical status. This will exclude the service from queries. -func (a *Agent) EnableServiceMaintenance(serviceID string) error { +func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error { service, ok := a.state.Services()[serviceID] if !ok { return fmt.Errorf("No service registered with ID %q", serviceID) @@ -1039,12 +1039,17 @@ func (a *Agent) EnableServiceMaintenance(serviceID string) error { return nil } + // Use default notes if no reason provided + if reason == "" { + reason = "Maintenance mode is enabled for this service" + } + // Create and register the critical health check check := &structs.HealthCheck{ Node: a.config.NodeName, CheckID: checkID, Name: "Service Maintenance Mode", - Notes: "Maintenance mode is enabled for this service", + Notes: reason, ServiceID: service.ID, ServiceName: service.Service, Status: structs.HealthCritical, @@ -1076,18 +1081,23 @@ func (a *Agent) DisableServiceMaintenance(serviceID string) error { } // EnableNodeMaintenance places a node into maintenance mode. -func (a *Agent) EnableNodeMaintenance() { +func (a *Agent) EnableNodeMaintenance(reason string) { // Ensure node maintenance is not already enabled if _, ok := a.state.Checks()[nodeMaintCheckID]; ok { return } + // Use a default notes value + if reason == "" { + reason = "Maintenance mode is enabled for this node" + } + // Create and register the node maintenance check check := &structs.HealthCheck{ Node: a.config.NodeName, CheckID: nodeMaintCheckID, Name: "Node Maintenance Mode", - Notes: "Maintenance mode is enabled for this node", + Notes: reason, Status: structs.HealthCritical, } a.AddCheck(check, nil, true) diff --git a/command/agent/agent_endpoint.go b/command/agent/agent_endpoint.go index 817426286b..0636b01c83 100644 --- a/command/agent/agent_endpoint.go +++ b/command/agent/agent_endpoint.go @@ -220,7 +220,8 @@ func (s *HTTPServer) AgentServiceMaintenance(resp http.ResponseWriter, req *http } if enable { - if err = s.agent.EnableServiceMaintenance(serviceID); err != nil { + reason := params.Get("reason") + if err = s.agent.EnableServiceMaintenance(serviceID, reason); err != nil { resp.WriteHeader(404) resp.Write([]byte(err.Error())) } @@ -257,7 +258,7 @@ func (s *HTTPServer) AgentNodeMaintenance(resp http.ResponseWriter, req *http.Re } if enable { - s.agent.EnableNodeMaintenance() + s.agent.EnableNodeMaintenance(params.Get("reason")) } else { s.agent.DisableNodeMaintenance() } diff --git a/command/agent/agent_endpoint_test.go b/command/agent/agent_endpoint_test.go index 189b90a3c1..c54f832f92 100644 --- a/command/agent/agent_endpoint_test.go +++ b/command/agent/agent_endpoint_test.go @@ -566,7 +566,7 @@ func TestHTTPAgent_EnableServiceMaintenance(t *testing.T) { } // Force the service into maintenance mode - req, _ := http.NewRequest("PUT", "/v1/agent/service/maintenance/test?enable=true", nil) + req, _ := http.NewRequest("PUT", "/v1/agent/service/maintenance/test?enable=true&reason=broken", nil) resp := httptest.NewRecorder() if _, err := srv.AgentServiceMaintenance(resp, req); err != nil { t.Fatalf("err: %s", err) @@ -577,9 +577,15 @@ func TestHTTPAgent_EnableServiceMaintenance(t *testing.T) { // Ensure the maintenance check was registered checkID := serviceMaintCheckID("test") - if _, ok := srv.agent.state.Checks()[checkID]; !ok { + check, ok := srv.agent.state.Checks()[checkID] + if !ok { t.Fatalf("should have registered maintenance check") } + + // Ensure the reason was set in notes + if check.Notes != "broken" { + t.Fatalf("bad: %#v", check) + } } func TestHTTPAgent_DisableServiceMaintenance(t *testing.T) { @@ -598,7 +604,7 @@ func TestHTTPAgent_DisableServiceMaintenance(t *testing.T) { } // Force the service into maintenance mode - if err := srv.agent.EnableServiceMaintenance("test"); err != nil { + if err := srv.agent.EnableServiceMaintenance("test", ""); err != nil { t.Fatalf("err: %s", err) } @@ -653,7 +659,8 @@ func TestHTTPAgent_EnableNodeMaintenance(t *testing.T) { defer srv.agent.Shutdown() // Force the node into maintenance mode - req, _ := http.NewRequest("PUT", "/v1/agent/self/maintenance?enable=true", nil) + req, _ := http.NewRequest( + "PUT", "/v1/agent/self/maintenance?enable=true&reason=broken", nil) resp := httptest.NewRecorder() if _, err := srv.AgentNodeMaintenance(resp, req); err != nil { t.Fatalf("err: %s", err) @@ -663,9 +670,15 @@ func TestHTTPAgent_EnableNodeMaintenance(t *testing.T) { } // Ensure the maintenance check was registered - if _, ok := srv.agent.state.Checks()[nodeMaintCheckID]; !ok { + check, ok := srv.agent.state.Checks()[nodeMaintCheckID] + if !ok { t.Fatalf("should have registered maintenance check") } + + // Ensure the reason was set in notes + if check.Notes != "broken" { + t.Fatalf("bad: %#v", check) + } } func TestHTTPAgent_DisableNodeMaintenance(t *testing.T) { @@ -675,7 +688,7 @@ func TestHTTPAgent_DisableNodeMaintenance(t *testing.T) { defer srv.agent.Shutdown() // Force the node into maintenance mode - srv.agent.EnableNodeMaintenance() + srv.agent.EnableNodeMaintenance("") // Leave maintenance mode req, _ := http.NewRequest("PUT", "/v1/agent/self/maintenance?enable=false", nil) diff --git a/command/agent/agent_test.go b/command/agent/agent_test.go index 5279b25c47..a09d789666 100644 --- a/command/agent/agent_test.go +++ b/command/agent/agent_test.go @@ -916,16 +916,22 @@ func TestAgent_ServiceMaintenanceMode(t *testing.T) { } // Enter maintenance mode for the service - if err := agent.EnableServiceMaintenance("redis"); err != nil { + if err := agent.EnableServiceMaintenance("redis", "broken"); err != nil { t.Fatalf("err: %s", err) } // Make sure the critical health check was added checkID := serviceMaintCheckID("redis") - if _, ok := agent.state.Checks()[checkID]; !ok { + check, ok := agent.state.Checks()[checkID] + if !ok { t.Fatalf("should have registered critical maintenance check") } + // Ensure the reason was set in notes + if check.Notes != "broken" { + t.Fatalf("bad: %#v", check) + } + // Leave maintenance mode if err := agent.DisableServiceMaintenance("redis"); err != nil { t.Fatalf("err: %s", err) @@ -935,6 +941,20 @@ func TestAgent_ServiceMaintenanceMode(t *testing.T) { if _, ok := agent.state.Checks()[checkID]; ok { t.Fatalf("should have deregistered maintenance check") } + + // Enter service maintenance mode without providing a reason + if err := agent.EnableServiceMaintenance("redis", ""); err != nil { + t.Fatalf("err: %s", err) + } + + // Ensure the check was registered with the default notes + check, ok = agent.state.Checks()[checkID] + if !ok { + t.Fatalf("should have registered critical check") + } + if check.Notes == "" { + t.Fatalf("bad: %#v", check) + } } func TestAgent_NodeMaintenanceMode(t *testing.T) { @@ -944,13 +964,19 @@ func TestAgent_NodeMaintenanceMode(t *testing.T) { defer agent.Shutdown() // Enter maintenance mode for the node - agent.EnableNodeMaintenance() + agent.EnableNodeMaintenance("broken") // Make sure the critical health check was added - if _, ok := agent.state.Checks()[nodeMaintCheckID]; !ok { + check, ok := agent.state.Checks()[nodeMaintCheckID] + if !ok { t.Fatalf("should have registered critical node check") } + // Ensure the reason was set in notes + if check.Notes != "broken" { + t.Fatalf("bad: %#v", check) + } + // Leave maintenance mode agent.DisableNodeMaintenance() @@ -958,4 +984,16 @@ func TestAgent_NodeMaintenanceMode(t *testing.T) { if _, ok := agent.state.Checks()[nodeMaintCheckID]; ok { t.Fatalf("should have deregistered critical node check") } + + // Enter maintenance mode without passing a reason + agent.EnableNodeMaintenance("") + + // Make sure the check was registered with the default note + check, ok = agent.state.Checks()[nodeMaintCheckID] + if !ok { + t.Fatalf("should have registered critical node check") + } + if check.Notes == "" { + t.Fatalf("bad: %#v", check) + } }