diff --git a/command/agent/agent.go b/command/agent/agent.go index d5ef05af4c..dfdf10cf38 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -29,6 +29,9 @@ const ( "If Consul was not shut down properly, the socket file may " + "be left behind. If the path looks correct, remove the file " + "and try again." + + // The ID of the faux health check for maintenance mode + maintCheckID = "_maintenance_" ) /* @@ -995,3 +998,66 @@ func (a *Agent) unloadChecks() error { return nil } + +func (a *Agent) EnableServiceMaintenance(serviceID string) error { + var service *structs.NodeService + for _, svc := range a.state.Services() { + if svc.ID == serviceID { + service = svc + } + } + + // Ensure the service exists + if service == nil { + return fmt.Errorf("No service registered with ID %q", serviceID) + } + + // Ensure maintenance mode is not already enabled + for _, check := range a.state.Checks() { + if check.CheckID == maintCheckID { + return fmt.Errorf("Maintenance mode already enabled for service %q", serviceID) + } + } + + // Create and register the critical health check + check := &structs.HealthCheck{ + Node: a.config.NodeName, + CheckID: maintCheckID, + Name: "Service Maintenance Mode", + Notes: "Maintenance mode is enabled for this service", + ServiceID: service.ID, + ServiceName: service.Service, + Status: structs.HealthCritical, + } + a.state.AddCheck(check) + + return nil +} + +func (a *Agent) DisableServiceMaintenance(serviceID string) error { + var service *structs.NodeService + for _, svc := range a.state.Services() { + if svc.ID == serviceID { + service = svc + } + } + + // Ensure the service exists + if service == nil { + return fmt.Errorf("No service registered with ID %q", serviceID) + } + + // Ensure maintenance mode is enabled + for _, check := range a.state.Checks() { + if check.CheckID == maintCheckID { + goto DEREGISTER + } + } + return fmt.Errorf("Maintenance mode not enabled for service %q", serviceID) + +DEREGISTER: + // Deregister the maintenance check + a.state.RemoveCheck(maintCheckID) + + return nil +} diff --git a/command/agent/agent_endpoint.go b/command/agent/agent_endpoint.go index 4ee70343fd..3e6426f803 100644 --- a/command/agent/agent_endpoint.go +++ b/command/agent/agent_endpoint.go @@ -176,3 +176,40 @@ func (s *HTTPServer) AgentDeregisterService(resp http.ResponseWriter, req *http. serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/deregister/") return nil, s.agent.RemoveService(serviceID, true) } + +func (s *HTTPServer) AgentServiceMaintenance(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + // Ensure we have a service ID + serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/maintenance/") + if serviceID == "" { + resp.WriteHeader(400) + resp.Write([]byte("Missing service ID")) + return nil, nil + } + + // Ensure we have some action + params := req.URL.Query() + if _, ok := params["enable"]; !ok { + resp.WriteHeader(400) + resp.Write([]byte("Missing value for enable")) + return nil, nil + } + + var enable bool + raw := params.Get("enable") + switch raw { + case "true": + enable = true + case "false": + enable = false + default: + resp.WriteHeader(400) + resp.Write([]byte(fmt.Sprintf("Invalid value for enable: %q", raw))) + return nil, nil + } + + if enable { + return nil, s.agent.EnableServiceMaintenance(serviceID) + } else { + return nil, s.agent.DisableServiceMaintenance(serviceID) + } +} diff --git a/command/agent/http.go b/command/agent/http.go index 708fe5b174..80363002a1 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -195,6 +195,7 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) { s.mux.HandleFunc("/v1/agent/service/register", s.wrap(s.AgentRegisterService)) s.mux.HandleFunc("/v1/agent/service/deregister/", s.wrap(s.AgentDeregisterService)) + s.mux.HandleFunc("/v1/agent/service/maintenance/", s.wrap(s.AgentServiceMaintenance)) s.mux.HandleFunc("/v1/event/fire/", s.wrap(s.EventFire)) s.mux.HandleFunc("/v1/event/list", s.wrap(s.EventList))