github.com/bigcommerce/nomad@v0.9.3-bc/command/agent/agent_endpoint.go (about)

     1  package agent
     2  
     3  import (
     4  	"encoding/json"
     5  	"net"
     6  	"net/http"
     7  	"sort"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/nomad/acl"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  	"github.com/hashicorp/serf/serf"
    13  	"github.com/mitchellh/copystructure"
    14  )
    15  
    16  type Member struct {
    17  	Name        string
    18  	Addr        net.IP
    19  	Port        uint16
    20  	Tags        map[string]string
    21  	Status      string
    22  	ProtocolMin uint8
    23  	ProtocolMax uint8
    24  	ProtocolCur uint8
    25  	DelegateMin uint8
    26  	DelegateMax uint8
    27  	DelegateCur uint8
    28  }
    29  
    30  func nomadMember(m serf.Member) Member {
    31  	return Member{
    32  		Name:        m.Name,
    33  		Addr:        m.Addr,
    34  		Port:        m.Port,
    35  		Tags:        m.Tags,
    36  		Status:      m.Status.String(),
    37  		ProtocolMin: m.ProtocolMin,
    38  		ProtocolMax: m.ProtocolMax,
    39  		ProtocolCur: m.ProtocolCur,
    40  		DelegateMin: m.DelegateMin,
    41  		DelegateMax: m.DelegateMax,
    42  		DelegateCur: m.DelegateCur,
    43  	}
    44  }
    45  
    46  func (s *HTTPServer) AgentSelfRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
    47  	if req.Method != "GET" {
    48  		return nil, CodedError(405, ErrInvalidMethod)
    49  	}
    50  
    51  	var secret string
    52  	s.parseToken(req, &secret)
    53  
    54  	var aclObj *acl.ACL
    55  	var err error
    56  
    57  	// Get the member as a server
    58  	var member serf.Member
    59  	if srv := s.agent.Server(); srv != nil {
    60  		member = srv.LocalMember()
    61  		aclObj, err = srv.ResolveToken(secret)
    62  	} else {
    63  		// Not a Server; use the Client for token resolution
    64  		aclObj, err = s.agent.Client().ResolveToken(secret)
    65  	}
    66  
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	// Check agent read permissions
    72  	if aclObj != nil && !aclObj.AllowAgentRead() {
    73  		return nil, structs.ErrPermissionDenied
    74  	}
    75  
    76  	self := agentSelf{
    77  		Member: nomadMember(member),
    78  		Stats:  s.agent.Stats(),
    79  	}
    80  	if ac, err := copystructure.Copy(s.agent.config); err != nil {
    81  		return nil, CodedError(500, err.Error())
    82  	} else {
    83  		self.Config = ac.(*Config)
    84  	}
    85  
    86  	if self.Config != nil && self.Config.Vault != nil && self.Config.Vault.Token != "" {
    87  		self.Config.Vault.Token = "<redacted>"
    88  	}
    89  
    90  	return self, nil
    91  }
    92  
    93  func (s *HTTPServer) AgentJoinRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
    94  	if req.Method != "PUT" && req.Method != "POST" {
    95  		return nil, CodedError(405, ErrInvalidMethod)
    96  	}
    97  	srv := s.agent.Server()
    98  	if srv == nil {
    99  		return nil, CodedError(501, ErrInvalidMethod)
   100  	}
   101  
   102  	// Get the join addresses
   103  	query := req.URL.Query()
   104  	addrs := query["address"]
   105  	if len(addrs) == 0 {
   106  		return nil, CodedError(400, "missing address to join")
   107  	}
   108  
   109  	// Attempt the join
   110  	num, err := srv.Join(addrs)
   111  	var errStr string
   112  	if err != nil {
   113  		errStr = err.Error()
   114  	}
   115  	return joinResult{num, errStr}, nil
   116  }
   117  
   118  func (s *HTTPServer) AgentMembersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   119  	if req.Method != "GET" {
   120  		return nil, CodedError(405, ErrInvalidMethod)
   121  	}
   122  
   123  	args := &structs.GenericRequest{}
   124  	if s.parse(resp, req, &args.Region, &args.QueryOptions) {
   125  		return nil, nil
   126  	}
   127  
   128  	var out structs.ServerMembersResponse
   129  	if err := s.agent.RPC("Status.Members", args, &out); err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	return out, nil
   134  }
   135  
   136  func (s *HTTPServer) AgentForceLeaveRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   137  	if req.Method != "PUT" && req.Method != "POST" {
   138  		return nil, CodedError(405, ErrInvalidMethod)
   139  	}
   140  	srv := s.agent.Server()
   141  	if srv == nil {
   142  		return nil, CodedError(501, ErrInvalidMethod)
   143  	}
   144  
   145  	var secret string
   146  	s.parseToken(req, &secret)
   147  
   148  	// Check agent write permissions
   149  	if aclObj, err := s.agent.Server().ResolveToken(secret); err != nil {
   150  		return nil, err
   151  	} else if aclObj != nil && !aclObj.AllowAgentWrite() {
   152  		return nil, structs.ErrPermissionDenied
   153  	}
   154  
   155  	// Get the node to eject
   156  	node := req.URL.Query().Get("node")
   157  	if node == "" {
   158  		return nil, CodedError(400, "missing node to force leave")
   159  	}
   160  
   161  	// Attempt remove
   162  	err := srv.RemoveFailedNode(node)
   163  	return nil, err
   164  }
   165  
   166  // AgentServersRequest is used to query the list of servers used by the Nomad
   167  // Client for RPCs.  This endpoint can also be used to update the list of
   168  // servers for a given agent.
   169  func (s *HTTPServer) AgentServersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   170  	switch req.Method {
   171  	case "PUT", "POST":
   172  		return s.updateServers(resp, req)
   173  	case "GET":
   174  		return s.listServers(resp, req)
   175  	default:
   176  		return nil, CodedError(405, ErrInvalidMethod)
   177  	}
   178  }
   179  
   180  func (s *HTTPServer) listServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   181  	client := s.agent.Client()
   182  	if client == nil {
   183  		return nil, CodedError(501, ErrInvalidMethod)
   184  	}
   185  
   186  	var secret string
   187  	s.parseToken(req, &secret)
   188  
   189  	// Check agent read permissions
   190  	if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil {
   191  		return nil, err
   192  	} else if aclObj != nil && !aclObj.AllowAgentRead() {
   193  		return nil, structs.ErrPermissionDenied
   194  	}
   195  
   196  	peers := s.agent.client.GetServers()
   197  	sort.Strings(peers)
   198  	return peers, nil
   199  }
   200  
   201  func (s *HTTPServer) updateServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   202  	client := s.agent.Client()
   203  	if client == nil {
   204  		return nil, CodedError(501, ErrInvalidMethod)
   205  	}
   206  
   207  	// Get the servers from the request
   208  	servers := req.URL.Query()["address"]
   209  	if len(servers) == 0 {
   210  		return nil, CodedError(400, "missing server address")
   211  	}
   212  
   213  	var secret string
   214  	s.parseToken(req, &secret)
   215  
   216  	// Check agent write permissions
   217  	if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil {
   218  		return nil, err
   219  	} else if aclObj != nil && !aclObj.AllowAgentWrite() {
   220  		return nil, structs.ErrPermissionDenied
   221  	}
   222  
   223  	// Set the servers list into the client
   224  	s.agent.logger.Trace("adding servers to the client's primary server list", "servers", servers, "path", "/v1/agent/servers", "method", "PUT")
   225  	if _, err := client.SetServers(servers); err != nil {
   226  		s.agent.logger.Error("failed adding servers to client's server list", "servers", servers, "error", err, "path", "/v1/agent/servers", "method", "PUT")
   227  		//TODO is this the right error to return?
   228  		return nil, CodedError(400, err.Error())
   229  	}
   230  	return nil, nil
   231  }
   232  
   233  // KeyringOperationRequest allows an operator to install/delete/use keys
   234  func (s *HTTPServer) KeyringOperationRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   235  	srv := s.agent.Server()
   236  	if srv == nil {
   237  		return nil, CodedError(501, ErrInvalidMethod)
   238  	}
   239  
   240  	var secret string
   241  	s.parseToken(req, &secret)
   242  
   243  	// Check agent write permissions
   244  	if aclObj, err := srv.ResolveToken(secret); err != nil {
   245  		return nil, err
   246  	} else if aclObj != nil && !aclObj.AllowAgentWrite() {
   247  		return nil, structs.ErrPermissionDenied
   248  	}
   249  
   250  	kmgr := srv.KeyManager()
   251  	var sresp *serf.KeyResponse
   252  	var err error
   253  
   254  	// Get the key from the req body
   255  	var args structs.KeyringRequest
   256  
   257  	//Get the op
   258  	op := strings.TrimPrefix(req.URL.Path, "/v1/agent/keyring/")
   259  
   260  	switch op {
   261  	case "list":
   262  		sresp, err = kmgr.ListKeys()
   263  	case "install":
   264  		if err := decodeBody(req, &args); err != nil {
   265  			return nil, CodedError(500, err.Error())
   266  		}
   267  		sresp, err = kmgr.InstallKey(args.Key)
   268  	case "use":
   269  		if err := decodeBody(req, &args); err != nil {
   270  			return nil, CodedError(500, err.Error())
   271  		}
   272  		sresp, err = kmgr.UseKey(args.Key)
   273  	case "remove":
   274  		if err := decodeBody(req, &args); err != nil {
   275  			return nil, CodedError(500, err.Error())
   276  		}
   277  		sresp, err = kmgr.RemoveKey(args.Key)
   278  	default:
   279  		return nil, CodedError(404, "resource not found")
   280  	}
   281  
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  	kresp := structs.KeyringResponse{
   286  		Messages: sresp.Messages,
   287  		Keys:     sresp.Keys,
   288  		NumNodes: sresp.NumNodes,
   289  	}
   290  	return kresp, nil
   291  }
   292  
   293  type agentSelf struct {
   294  	Config *Config                      `json:"config"`
   295  	Member Member                       `json:"member,omitempty"`
   296  	Stats  map[string]map[string]string `json:"stats"`
   297  }
   298  
   299  type joinResult struct {
   300  	NumJoined int    `json:"num_joined"`
   301  	Error     string `json:"error"`
   302  }
   303  
   304  func (s *HTTPServer) HealthRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   305  	if req.Method != "GET" {
   306  		return nil, CodedError(405, ErrInvalidMethod)
   307  	}
   308  
   309  	var args structs.GenericRequest
   310  	if s.parse(resp, req, &args.Region, &args.QueryOptions) {
   311  		return nil, nil
   312  	}
   313  
   314  	health := healthResponse{}
   315  	getClient := true
   316  	getServer := true
   317  
   318  	// See if we're checking a specific agent type and default to failing
   319  	if healthType, ok := req.URL.Query()["type"]; ok {
   320  		getClient = false
   321  		getServer = false
   322  		for _, ht := range healthType {
   323  			switch ht {
   324  			case "client":
   325  				getClient = true
   326  				health.Client = &healthResponseAgent{
   327  					Ok:      false,
   328  					Message: "client not enabled",
   329  				}
   330  			case "server":
   331  				getServer = true
   332  				health.Server = &healthResponseAgent{
   333  					Ok:      false,
   334  					Message: "server not enabled",
   335  				}
   336  			}
   337  		}
   338  	}
   339  
   340  	// If we should check the client and it exists assume it's healthy
   341  	if client := s.agent.Client(); getClient && client != nil {
   342  		if len(client.GetServers()) == 0 {
   343  			health.Client = &healthResponseAgent{
   344  				Ok:      false,
   345  				Message: "no known servers",
   346  			}
   347  		} else {
   348  			health.Client = &healthResponseAgent{
   349  				Ok:      true,
   350  				Message: "ok",
   351  			}
   352  		}
   353  	}
   354  
   355  	// If we should check the server and it exists, see if there's a leader
   356  	if server := s.agent.Server(); getServer && server != nil {
   357  		health.Server = &healthResponseAgent{
   358  			Ok:      true,
   359  			Message: "ok",
   360  		}
   361  
   362  		leader := ""
   363  		if err := s.agent.RPC("Status.Leader", &args, &leader); err != nil {
   364  			health.Server.Ok = false
   365  			health.Server.Message = err.Error()
   366  		} else if leader == "" {
   367  			health.Server.Ok = false
   368  			health.Server.Message = "no leader"
   369  		}
   370  	}
   371  
   372  	if health.ok() {
   373  		return &health, nil
   374  	}
   375  
   376  	jsonResp, err := json.Marshal(&health)
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  	return nil, CodedError(500, string(jsonResp))
   381  }
   382  
   383  type healthResponse struct {
   384  	Client *healthResponseAgent `json:"client,omitempty"`
   385  	Server *healthResponseAgent `json:"server,omitempty"`
   386  }
   387  
   388  // ok returns true as long as neither Client nor Server have Ok=false.
   389  func (h healthResponse) ok() bool {
   390  	if h.Client != nil && !h.Client.Ok {
   391  		return false
   392  	}
   393  	if h.Server != nil && !h.Server.Ok {
   394  		return false
   395  	}
   396  	return true
   397  }
   398  
   399  type healthResponseAgent struct {
   400  	Ok      bool   `json:"ok"`
   401  	Message string `json:"message,omitempty"`
   402  }