github.com/thomasobenaus/nomad@v0.11.1/command/agent/node_endpoint.go (about) 1 package agent 2 3 import ( 4 "net/http" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/hashicorp/nomad/api" 10 "github.com/hashicorp/nomad/nomad/structs" 11 ) 12 13 func (s *HTTPServer) NodesRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 14 if req.Method != "GET" { 15 return nil, CodedError(405, ErrInvalidMethod) 16 } 17 18 args := structs.NodeListRequest{} 19 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 20 return nil, nil 21 } 22 23 var out structs.NodeListResponse 24 if err := s.agent.RPC("Node.List", &args, &out); err != nil { 25 return nil, err 26 } 27 28 setMeta(resp, &out.QueryMeta) 29 if out.Nodes == nil { 30 out.Nodes = make([]*structs.NodeListStub, 0) 31 } 32 return out.Nodes, nil 33 } 34 35 func (s *HTTPServer) NodeSpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) { 36 path := strings.TrimPrefix(req.URL.Path, "/v1/node/") 37 switch { 38 case strings.HasSuffix(path, "/evaluate"): 39 nodeName := strings.TrimSuffix(path, "/evaluate") 40 return s.nodeForceEvaluate(resp, req, nodeName) 41 case strings.HasSuffix(path, "/allocations"): 42 nodeName := strings.TrimSuffix(path, "/allocations") 43 return s.nodeAllocations(resp, req, nodeName) 44 case strings.HasSuffix(path, "/drain"): 45 nodeName := strings.TrimSuffix(path, "/drain") 46 return s.nodeToggleDrain(resp, req, nodeName) 47 case strings.HasSuffix(path, "/eligibility"): 48 nodeName := strings.TrimSuffix(path, "/eligibility") 49 return s.nodeToggleEligibility(resp, req, nodeName) 50 case strings.HasSuffix(path, "/purge"): 51 nodeName := strings.TrimSuffix(path, "/purge") 52 return s.nodePurge(resp, req, nodeName) 53 default: 54 return s.nodeQuery(resp, req, path) 55 } 56 } 57 58 func (s *HTTPServer) nodeForceEvaluate(resp http.ResponseWriter, req *http.Request, 59 nodeID string) (interface{}, error) { 60 if req.Method != "PUT" && req.Method != "POST" { 61 return nil, CodedError(405, ErrInvalidMethod) 62 } 63 args := structs.NodeEvaluateRequest{ 64 NodeID: nodeID, 65 } 66 s.parseWriteRequest(req, &args.WriteRequest) 67 68 var out structs.NodeUpdateResponse 69 if err := s.agent.RPC("Node.Evaluate", &args, &out); err != nil { 70 return nil, err 71 } 72 setIndex(resp, out.Index) 73 return out, nil 74 } 75 76 func (s *HTTPServer) nodeAllocations(resp http.ResponseWriter, req *http.Request, 77 nodeID string) (interface{}, error) { 78 if req.Method != "GET" { 79 return nil, CodedError(405, ErrInvalidMethod) 80 } 81 args := structs.NodeSpecificRequest{ 82 NodeID: nodeID, 83 } 84 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 85 return nil, nil 86 } 87 88 var out structs.NodeAllocsResponse 89 if err := s.agent.RPC("Node.GetAllocs", &args, &out); err != nil { 90 return nil, err 91 } 92 93 setMeta(resp, &out.QueryMeta) 94 if out.Allocs == nil { 95 out.Allocs = make([]*structs.Allocation, 0) 96 } 97 for _, alloc := range out.Allocs { 98 alloc.SetEventDisplayMessages() 99 } 100 return out.Allocs, nil 101 } 102 103 func (s *HTTPServer) nodeToggleDrain(resp http.ResponseWriter, req *http.Request, 104 nodeID string) (interface{}, error) { 105 if req.Method != "PUT" && req.Method != "POST" { 106 return nil, CodedError(405, ErrInvalidMethod) 107 } 108 109 var drainRequest api.NodeUpdateDrainRequest 110 111 // COMPAT: Remove in 0.10. Allow the old style enable query param. 112 // Get the enable parameter 113 enableRaw := req.URL.Query().Get("enable") 114 var enable bool 115 if enableRaw != "" { 116 var err error 117 enable, err = strconv.ParseBool(enableRaw) 118 if err != nil { 119 return nil, CodedError(400, "invalid enable value") 120 } 121 122 // Use the force drain to have it keep the same behavior as old clients. 123 if enable { 124 drainRequest.DrainSpec = &api.DrainSpec{ 125 Deadline: -1 * time.Second, 126 } 127 } else { 128 // If drain is disabled on an old client, mark the node as eligible for backwards compatibility 129 drainRequest.MarkEligible = true 130 } 131 } else { 132 if err := decodeBody(req, &drainRequest); err != nil { 133 return nil, CodedError(400, err.Error()) 134 } 135 } 136 137 args := structs.NodeUpdateDrainRequest{ 138 NodeID: nodeID, 139 MarkEligible: drainRequest.MarkEligible, 140 } 141 if drainRequest.DrainSpec != nil { 142 args.DrainStrategy = &structs.DrainStrategy{ 143 DrainSpec: structs.DrainSpec{ 144 Deadline: drainRequest.DrainSpec.Deadline, 145 IgnoreSystemJobs: drainRequest.DrainSpec.IgnoreSystemJobs, 146 }, 147 } 148 } 149 s.parseWriteRequest(req, &args.WriteRequest) 150 151 var out structs.NodeDrainUpdateResponse 152 if err := s.agent.RPC("Node.UpdateDrain", &args, &out); err != nil { 153 return nil, err 154 } 155 setIndex(resp, out.Index) 156 return out, nil 157 } 158 159 func (s *HTTPServer) nodeToggleEligibility(resp http.ResponseWriter, req *http.Request, 160 nodeID string) (interface{}, error) { 161 if req.Method != "PUT" && req.Method != "POST" { 162 return nil, CodedError(405, ErrInvalidMethod) 163 } 164 165 var eligibilityRequest structs.NodeUpdateEligibilityRequest 166 if err := decodeBody(req, &eligibilityRequest); err != nil { 167 return nil, CodedError(400, err.Error()) 168 } 169 if eligibilityRequest.NodeID == "" { 170 eligibilityRequest.NodeID = nodeID 171 } 172 173 s.parseWriteRequest(req, &eligibilityRequest.WriteRequest) 174 175 var out structs.NodeEligibilityUpdateResponse 176 if err := s.agent.RPC("Node.UpdateEligibility", &eligibilityRequest, &out); err != nil { 177 return nil, err 178 } 179 setIndex(resp, out.Index) 180 return out, nil 181 } 182 183 func (s *HTTPServer) nodeQuery(resp http.ResponseWriter, req *http.Request, 184 nodeID string) (interface{}, error) { 185 if req.Method != "GET" { 186 return nil, CodedError(405, ErrInvalidMethod) 187 } 188 args := structs.NodeSpecificRequest{ 189 NodeID: nodeID, 190 } 191 if s.parse(resp, req, &args.Region, &args.QueryOptions) { 192 return nil, nil 193 } 194 195 var out structs.SingleNodeResponse 196 if err := s.agent.RPC("Node.GetNode", &args, &out); err != nil { 197 return nil, err 198 } 199 200 setMeta(resp, &out.QueryMeta) 201 if out.Node == nil { 202 return nil, CodedError(404, "node not found") 203 } 204 return out.Node, nil 205 } 206 207 func (s *HTTPServer) nodePurge(resp http.ResponseWriter, req *http.Request, nodeID string) (interface{}, error) { 208 if req.Method != "PUT" && req.Method != "POST" { 209 return nil, CodedError(405, ErrInvalidMethod) 210 } 211 args := structs.NodeDeregisterRequest{ 212 NodeID: nodeID, 213 } 214 s.parseWriteRequest(req, &args.WriteRequest) 215 var out structs.NodeUpdateResponse 216 if err := s.agent.RPC("Node.Deregister", &args, &out); err != nil { 217 return nil, err 218 } 219 setIndex(resp, out.Index) 220 return out, nil 221 }