github.com/karlem/nomad@v0.10.2-rc1/command/agent/agent_endpoint.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/http"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/docker/docker/pkg/ioutils"
    16  	log "github.com/hashicorp/go-hclog"
    17  	"github.com/hashicorp/nomad/acl"
    18  	cstructs "github.com/hashicorp/nomad/client/structs"
    19  	"github.com/hashicorp/nomad/nomad/structs"
    20  	"github.com/hashicorp/serf/serf"
    21  	"github.com/mitchellh/copystructure"
    22  	"github.com/ugorji/go/codec"
    23  )
    24  
    25  type Member struct {
    26  	Name        string
    27  	Addr        net.IP
    28  	Port        uint16
    29  	Tags        map[string]string
    30  	Status      string
    31  	ProtocolMin uint8
    32  	ProtocolMax uint8
    33  	ProtocolCur uint8
    34  	DelegateMin uint8
    35  	DelegateMax uint8
    36  	DelegateCur uint8
    37  }
    38  
    39  func nomadMember(m serf.Member) Member {
    40  	return Member{
    41  		Name:        m.Name,
    42  		Addr:        m.Addr,
    43  		Port:        m.Port,
    44  		Tags:        m.Tags,
    45  		Status:      m.Status.String(),
    46  		ProtocolMin: m.ProtocolMin,
    47  		ProtocolMax: m.ProtocolMax,
    48  		ProtocolCur: m.ProtocolCur,
    49  		DelegateMin: m.DelegateMin,
    50  		DelegateMax: m.DelegateMax,
    51  		DelegateCur: m.DelegateCur,
    52  	}
    53  }
    54  
    55  func (s *HTTPServer) AgentSelfRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
    56  	if req.Method != "GET" {
    57  		return nil, CodedError(405, ErrInvalidMethod)
    58  	}
    59  
    60  	var secret string
    61  	s.parseToken(req, &secret)
    62  
    63  	var aclObj *acl.ACL
    64  	var err error
    65  
    66  	// Get the member as a server
    67  	var member serf.Member
    68  	if srv := s.agent.Server(); srv != nil {
    69  		member = srv.LocalMember()
    70  		aclObj, err = srv.ResolveToken(secret)
    71  	} else {
    72  		// Not a Server; use the Client for token resolution
    73  		aclObj, err = s.agent.Client().ResolveToken(secret)
    74  	}
    75  
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	// Check agent read permissions
    81  	if aclObj != nil && !aclObj.AllowAgentRead() {
    82  		return nil, structs.ErrPermissionDenied
    83  	}
    84  
    85  	self := agentSelf{
    86  		Member: nomadMember(member),
    87  		Stats:  s.agent.Stats(),
    88  	}
    89  	if ac, err := copystructure.Copy(s.agent.config); err != nil {
    90  		return nil, CodedError(500, err.Error())
    91  	} else {
    92  		self.Config = ac.(*Config)
    93  	}
    94  
    95  	if self.Config != nil && self.Config.Vault != nil && self.Config.Vault.Token != "" {
    96  		self.Config.Vault.Token = "<redacted>"
    97  	}
    98  
    99  	if self.Config != nil && self.Config.ACL != nil && self.Config.ACL.ReplicationToken != "" {
   100  		self.Config.ACL.ReplicationToken = "<redacted>"
   101  	}
   102  
   103  	if self.Config != nil && self.Config.Consul != nil && self.Config.Consul.Token != "" {
   104  		self.Config.Consul.Token = "<redacted>"
   105  	}
   106  
   107  	if self.Config != nil && self.Config.Telemetry != nil && self.Config.Telemetry.CirconusAPIToken != "" {
   108  		self.Config.Telemetry.CirconusAPIToken = "<redacted>"
   109  	}
   110  
   111  	return self, nil
   112  }
   113  
   114  func (s *HTTPServer) AgentJoinRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   115  	if req.Method != "PUT" && req.Method != "POST" {
   116  		return nil, CodedError(405, ErrInvalidMethod)
   117  	}
   118  	srv := s.agent.Server()
   119  	if srv == nil {
   120  		return nil, CodedError(501, ErrInvalidMethod)
   121  	}
   122  
   123  	// Get the join addresses
   124  	query := req.URL.Query()
   125  	addrs := query["address"]
   126  	if len(addrs) == 0 {
   127  		return nil, CodedError(400, "missing address to join")
   128  	}
   129  
   130  	// Attempt the join
   131  	num, err := srv.Join(addrs)
   132  	var errStr string
   133  	if err != nil {
   134  		errStr = err.Error()
   135  	}
   136  	return joinResult{num, errStr}, nil
   137  }
   138  
   139  func (s *HTTPServer) AgentMembersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   140  	if req.Method != "GET" {
   141  		return nil, CodedError(405, ErrInvalidMethod)
   142  	}
   143  
   144  	args := &structs.GenericRequest{}
   145  	if s.parse(resp, req, &args.Region, &args.QueryOptions) {
   146  		return nil, nil
   147  	}
   148  
   149  	var out structs.ServerMembersResponse
   150  	if err := s.agent.RPC("Status.Members", args, &out); err != nil {
   151  		return nil, err
   152  	}
   153  
   154  	return out, nil
   155  }
   156  
   157  func (s *HTTPServer) AgentMonitor(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   158  	var secret string
   159  	s.parseToken(req, &secret)
   160  
   161  	// Check agent read permissions
   162  	if aclObj, err := s.agent.Server().ResolveToken(secret); err != nil {
   163  		return nil, err
   164  	} else if aclObj != nil && !aclObj.AllowAgentRead() {
   165  		return nil, structs.ErrPermissionDenied
   166  	}
   167  
   168  	// Get the provided loglevel.
   169  	logLevel := req.URL.Query().Get("log_level")
   170  	if logLevel == "" {
   171  		logLevel = "INFO"
   172  	}
   173  
   174  	if log.LevelFromString(logLevel) == log.NoLevel {
   175  		return nil, CodedError(400, fmt.Sprintf("Unknown log level: %s", logLevel))
   176  	}
   177  
   178  	logJSON := false
   179  	logJSONStr := req.URL.Query().Get("log_json")
   180  	if logJSONStr != "" {
   181  		parsed, err := strconv.ParseBool(logJSONStr)
   182  		if err != nil {
   183  			return nil, CodedError(400, fmt.Sprintf("Unknown option for log json: %v", err))
   184  		}
   185  		logJSON = parsed
   186  	}
   187  
   188  	plainText := false
   189  	plainTextStr := req.URL.Query().Get("plain")
   190  	if plainTextStr != "" {
   191  		parsed, err := strconv.ParseBool(plainTextStr)
   192  		if err != nil {
   193  			return nil, CodedError(400, fmt.Sprintf("Unknown option for plain: %v", err))
   194  		}
   195  		plainText = parsed
   196  	}
   197  
   198  	nodeID := req.URL.Query().Get("node_id")
   199  	// Build the request and parse the ACL token
   200  	args := cstructs.MonitorRequest{
   201  		NodeID:    nodeID,
   202  		ServerID:  req.URL.Query().Get("server_id"),
   203  		LogLevel:  logLevel,
   204  		LogJSON:   logJSON,
   205  		PlainText: plainText,
   206  	}
   207  
   208  	// if node and server were requested return error
   209  	if args.NodeID != "" && args.ServerID != "" {
   210  		return nil, CodedError(400, "Cannot target node and server simultaneously")
   211  	}
   212  
   213  	s.parse(resp, req, &args.QueryOptions.Region, &args.QueryOptions)
   214  
   215  	// Make the RPC
   216  	var handler structs.StreamingRpcHandler
   217  	var handlerErr error
   218  	if nodeID != "" {
   219  		// Determine the handler to use
   220  		useLocalClient, useClientRPC, useServerRPC := s.rpcHandlerForNode(nodeID)
   221  		if useLocalClient {
   222  			handler, handlerErr = s.agent.Client().StreamingRpcHandler("Agent.Monitor")
   223  		} else if useClientRPC {
   224  			handler, handlerErr = s.agent.Client().RemoteStreamingRpcHandler("Agent.Monitor")
   225  		} else if useServerRPC {
   226  			handler, handlerErr = s.agent.Server().StreamingRpcHandler("Agent.Monitor")
   227  		} else {
   228  			handlerErr = CodedError(400, "No local Node and node_id not provided")
   229  		}
   230  	} else {
   231  		// No node id we want to monitor this server
   232  		handler, handlerErr = s.agent.Server().StreamingRpcHandler("Agent.Monitor")
   233  	}
   234  
   235  	if handlerErr != nil {
   236  		return nil, CodedError(500, handlerErr.Error())
   237  	}
   238  	httpPipe, handlerPipe := net.Pipe()
   239  	decoder := codec.NewDecoder(httpPipe, structs.MsgpackHandle)
   240  	encoder := codec.NewEncoder(httpPipe, structs.MsgpackHandle)
   241  
   242  	ctx, cancel := context.WithCancel(req.Context())
   243  	go func() {
   244  		<-ctx.Done()
   245  		httpPipe.Close()
   246  	}()
   247  
   248  	// Create an output that gets flushed on every write
   249  	output := ioutils.NewWriteFlusher(resp)
   250  
   251  	// create an error channel to handle errors
   252  	errCh := make(chan HTTPCodedError, 2)
   253  
   254  	// stream response
   255  	go func() {
   256  		defer cancel()
   257  
   258  		// Send the request
   259  		if err := encoder.Encode(args); err != nil {
   260  			errCh <- CodedError(500, err.Error())
   261  			return
   262  		}
   263  
   264  		for {
   265  			select {
   266  			case <-ctx.Done():
   267  				errCh <- nil
   268  				return
   269  			default:
   270  			}
   271  
   272  			var res cstructs.StreamErrWrapper
   273  			if err := decoder.Decode(&res); err != nil {
   274  				errCh <- CodedError(500, err.Error())
   275  				return
   276  			}
   277  			decoder.Reset(httpPipe)
   278  
   279  			if err := res.Error; err != nil {
   280  				if err.Code != nil {
   281  					errCh <- CodedError(int(*err.Code), err.Error())
   282  					return
   283  				}
   284  			}
   285  
   286  			if _, err := io.Copy(output, bytes.NewReader(res.Payload)); err != nil {
   287  				errCh <- CodedError(500, err.Error())
   288  				return
   289  			}
   290  		}
   291  	}()
   292  
   293  	handler(handlerPipe)
   294  	cancel()
   295  	codedErr := <-errCh
   296  
   297  	if codedErr != nil &&
   298  		(codedErr == io.EOF ||
   299  			strings.Contains(codedErr.Error(), "closed") ||
   300  			strings.Contains(codedErr.Error(), "EOF")) {
   301  		codedErr = nil
   302  	}
   303  	return nil, codedErr
   304  }
   305  
   306  func (s *HTTPServer) AgentForceLeaveRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   307  	if req.Method != "PUT" && req.Method != "POST" {
   308  		return nil, CodedError(405, ErrInvalidMethod)
   309  	}
   310  	srv := s.agent.Server()
   311  	if srv == nil {
   312  		return nil, CodedError(501, ErrInvalidMethod)
   313  	}
   314  
   315  	var secret string
   316  	s.parseToken(req, &secret)
   317  
   318  	// Check agent write permissions
   319  	if aclObj, err := s.agent.Server().ResolveToken(secret); err != nil {
   320  		return nil, err
   321  	} else if aclObj != nil && !aclObj.AllowAgentWrite() {
   322  		return nil, structs.ErrPermissionDenied
   323  	}
   324  
   325  	// Get the node to eject
   326  	node := req.URL.Query().Get("node")
   327  	if node == "" {
   328  		return nil, CodedError(400, "missing node to force leave")
   329  	}
   330  
   331  	// Attempt remove
   332  	err := srv.RemoveFailedNode(node)
   333  	return nil, err
   334  }
   335  
   336  // AgentServersRequest is used to query the list of servers used by the Nomad
   337  // Client for RPCs.  This endpoint can also be used to update the list of
   338  // servers for a given agent.
   339  func (s *HTTPServer) AgentServersRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   340  	switch req.Method {
   341  	case "PUT", "POST":
   342  		return s.updateServers(resp, req)
   343  	case "GET":
   344  		return s.listServers(resp, req)
   345  	default:
   346  		return nil, CodedError(405, ErrInvalidMethod)
   347  	}
   348  }
   349  
   350  func (s *HTTPServer) listServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   351  	client := s.agent.Client()
   352  	if client == nil {
   353  		return nil, CodedError(501, ErrInvalidMethod)
   354  	}
   355  
   356  	var secret string
   357  	s.parseToken(req, &secret)
   358  
   359  	// Check agent read permissions
   360  	if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil {
   361  		return nil, err
   362  	} else if aclObj != nil && !aclObj.AllowAgentRead() {
   363  		return nil, structs.ErrPermissionDenied
   364  	}
   365  
   366  	peers := s.agent.client.GetServers()
   367  	sort.Strings(peers)
   368  	return peers, nil
   369  }
   370  
   371  func (s *HTTPServer) updateServers(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   372  	client := s.agent.Client()
   373  	if client == nil {
   374  		return nil, CodedError(501, ErrInvalidMethod)
   375  	}
   376  
   377  	// Get the servers from the request
   378  	servers := req.URL.Query()["address"]
   379  	if len(servers) == 0 {
   380  		return nil, CodedError(400, "missing server address")
   381  	}
   382  
   383  	var secret string
   384  	s.parseToken(req, &secret)
   385  
   386  	// Check agent write permissions
   387  	if aclObj, err := s.agent.Client().ResolveToken(secret); err != nil {
   388  		return nil, err
   389  	} else if aclObj != nil && !aclObj.AllowAgentWrite() {
   390  		return nil, structs.ErrPermissionDenied
   391  	}
   392  
   393  	// Set the servers list into the client
   394  	s.agent.logger.Trace("adding servers to the client's primary server list", "servers", servers, "path", "/v1/agent/servers", "method", "PUT")
   395  	if _, err := client.SetServers(servers); err != nil {
   396  		s.agent.logger.Error("failed adding servers to client's server list", "servers", servers, "error", err, "path", "/v1/agent/servers", "method", "PUT")
   397  		//TODO is this the right error to return?
   398  		return nil, CodedError(400, err.Error())
   399  	}
   400  	return nil, nil
   401  }
   402  
   403  // KeyringOperationRequest allows an operator to install/delete/use keys
   404  func (s *HTTPServer) KeyringOperationRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   405  	srv := s.agent.Server()
   406  	if srv == nil {
   407  		return nil, CodedError(501, ErrInvalidMethod)
   408  	}
   409  
   410  	var secret string
   411  	s.parseToken(req, &secret)
   412  
   413  	// Check agent write permissions
   414  	if aclObj, err := srv.ResolveToken(secret); err != nil {
   415  		return nil, err
   416  	} else if aclObj != nil && !aclObj.AllowAgentWrite() {
   417  		return nil, structs.ErrPermissionDenied
   418  	}
   419  
   420  	kmgr := srv.KeyManager()
   421  	var sresp *serf.KeyResponse
   422  	var err error
   423  
   424  	// Get the key from the req body
   425  	var args structs.KeyringRequest
   426  
   427  	//Get the op
   428  	op := strings.TrimPrefix(req.URL.Path, "/v1/agent/keyring/")
   429  
   430  	switch op {
   431  	case "list":
   432  		sresp, err = kmgr.ListKeys()
   433  	case "install":
   434  		if err := decodeBody(req, &args); err != nil {
   435  			return nil, CodedError(500, err.Error())
   436  		}
   437  		sresp, err = kmgr.InstallKey(args.Key)
   438  	case "use":
   439  		if err := decodeBody(req, &args); err != nil {
   440  			return nil, CodedError(500, err.Error())
   441  		}
   442  		sresp, err = kmgr.UseKey(args.Key)
   443  	case "remove":
   444  		if err := decodeBody(req, &args); err != nil {
   445  			return nil, CodedError(500, err.Error())
   446  		}
   447  		sresp, err = kmgr.RemoveKey(args.Key)
   448  	default:
   449  		return nil, CodedError(404, "resource not found")
   450  	}
   451  
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  	kresp := structs.KeyringResponse{
   456  		Messages: sresp.Messages,
   457  		Keys:     sresp.Keys,
   458  		NumNodes: sresp.NumNodes,
   459  	}
   460  	return kresp, nil
   461  }
   462  
   463  type agentSelf struct {
   464  	Config *Config                      `json:"config"`
   465  	Member Member                       `json:"member,omitempty"`
   466  	Stats  map[string]map[string]string `json:"stats"`
   467  }
   468  
   469  type joinResult struct {
   470  	NumJoined int    `json:"num_joined"`
   471  	Error     string `json:"error"`
   472  }
   473  
   474  func (s *HTTPServer) HealthRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
   475  	if req.Method != "GET" {
   476  		return nil, CodedError(405, ErrInvalidMethod)
   477  	}
   478  
   479  	var args structs.GenericRequest
   480  	if s.parse(resp, req, &args.Region, &args.QueryOptions) {
   481  		return nil, nil
   482  	}
   483  
   484  	health := healthResponse{}
   485  	getClient := true
   486  	getServer := true
   487  
   488  	// See if we're checking a specific agent type and default to failing
   489  	if healthType, ok := req.URL.Query()["type"]; ok {
   490  		getClient = false
   491  		getServer = false
   492  		for _, ht := range healthType {
   493  			switch ht {
   494  			case "client":
   495  				getClient = true
   496  				health.Client = &healthResponseAgent{
   497  					Ok:      false,
   498  					Message: "client not enabled",
   499  				}
   500  			case "server":
   501  				getServer = true
   502  				health.Server = &healthResponseAgent{
   503  					Ok:      false,
   504  					Message: "server not enabled",
   505  				}
   506  			}
   507  		}
   508  	}
   509  
   510  	// If we should check the client and it exists assume it's healthy
   511  	if client := s.agent.Client(); getClient && client != nil {
   512  		if len(client.GetServers()) == 0 {
   513  			health.Client = &healthResponseAgent{
   514  				Ok:      false,
   515  				Message: "no known servers",
   516  			}
   517  		} else {
   518  			health.Client = &healthResponseAgent{
   519  				Ok:      true,
   520  				Message: "ok",
   521  			}
   522  		}
   523  	}
   524  
   525  	// If we should check the server and it exists, see if there's a leader
   526  	if server := s.agent.Server(); getServer && server != nil {
   527  		health.Server = &healthResponseAgent{
   528  			Ok:      true,
   529  			Message: "ok",
   530  		}
   531  
   532  		leader := ""
   533  		if err := s.agent.RPC("Status.Leader", &args, &leader); err != nil {
   534  			health.Server.Ok = false
   535  			health.Server.Message = err.Error()
   536  		} else if leader == "" {
   537  			health.Server.Ok = false
   538  			health.Server.Message = "no leader"
   539  		}
   540  	}
   541  
   542  	if health.ok() {
   543  		return &health, nil
   544  	}
   545  
   546  	jsonResp, err := json.Marshal(&health)
   547  	if err != nil {
   548  		return nil, err
   549  	}
   550  	return nil, CodedError(500, string(jsonResp))
   551  }
   552  
   553  type healthResponse struct {
   554  	Client *healthResponseAgent `json:"client,omitempty"`
   555  	Server *healthResponseAgent `json:"server,omitempty"`
   556  }
   557  
   558  // ok returns true as long as neither Client nor Server have Ok=false.
   559  func (h healthResponse) ok() bool {
   560  	if h.Client != nil && !h.Client.Ok {
   561  		return false
   562  	}
   563  	if h.Server != nil && !h.Server.Ok {
   564  		return false
   565  	}
   566  	return true
   567  }
   568  
   569  type healthResponseAgent struct {
   570  	Ok      bool   `json:"ok"`
   571  	Message string `json:"message,omitempty"`
   572  }