github.com/maier/nomad@v0.4.1-0.20161110003312-a9e3d0b8549d/api/agent.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  )
     7  
     8  // Agent encapsulates an API client which talks to Nomad's
     9  // agent endpoints for a specific node.
    10  type Agent struct {
    11  	client *Client
    12  
    13  	// Cache static agent info
    14  	nodeName   string
    15  	datacenter string
    16  	region     string
    17  }
    18  
    19  // KeyringResponse is a unified key response and can be used for install,
    20  // remove, use, as well as listing key queries.
    21  type KeyringResponse struct {
    22  	Messages map[string]string
    23  	Keys     map[string]int
    24  	NumNodes int
    25  }
    26  
    27  // KeyringRequest is request objects for serf key operations.
    28  type KeyringRequest struct {
    29  	Key string
    30  }
    31  
    32  // Agent returns a new agent which can be used to query
    33  // the agent-specific endpoints.
    34  func (c *Client) Agent() *Agent {
    35  	return &Agent{client: c}
    36  }
    37  
    38  // Self is used to query the /v1/agent/self endpoint and
    39  // returns information specific to the running agent.
    40  func (a *Agent) Self() (map[string]map[string]interface{}, error) {
    41  	var out map[string]map[string]interface{}
    42  
    43  	// Query the self endpoint on the agent
    44  	_, err := a.client.query("/v1/agent/self", &out, nil)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("failed querying self endpoint: %s", err)
    47  	}
    48  
    49  	// Populate the cache for faster queries
    50  	a.populateCache(out)
    51  
    52  	return out, nil
    53  }
    54  
    55  // populateCache is used to insert various pieces of static
    56  // data into the agent handle. This is used during subsequent
    57  // lookups for the same data later on to save the round trip.
    58  func (a *Agent) populateCache(info map[string]map[string]interface{}) {
    59  	if a.nodeName == "" {
    60  		a.nodeName, _ = info["member"]["Name"].(string)
    61  	}
    62  	if tags, ok := info["member"]["Tags"].(map[string]interface{}); ok {
    63  		if a.datacenter == "" {
    64  			a.datacenter, _ = tags["dc"].(string)
    65  		}
    66  		if a.region == "" {
    67  			a.region, _ = tags["region"].(string)
    68  		}
    69  	}
    70  }
    71  
    72  // NodeName is used to query the Nomad agent for its node name.
    73  func (a *Agent) NodeName() (string, error) {
    74  	// Return from cache if we have it
    75  	if a.nodeName != "" {
    76  		return a.nodeName, nil
    77  	}
    78  
    79  	// Query the node name
    80  	_, err := a.Self()
    81  	return a.nodeName, err
    82  }
    83  
    84  // Datacenter is used to return the name of the datacenter which
    85  // the agent is a member of.
    86  func (a *Agent) Datacenter() (string, error) {
    87  	// Return from cache if we have it
    88  	if a.datacenter != "" {
    89  		return a.datacenter, nil
    90  	}
    91  
    92  	// Query the agent for the DC
    93  	_, err := a.Self()
    94  	return a.datacenter, err
    95  }
    96  
    97  // Region is used to look up the region the agent is in.
    98  func (a *Agent) Region() (string, error) {
    99  	// Return from cache if we have it
   100  	if a.region != "" {
   101  		return a.region, nil
   102  	}
   103  
   104  	// Query the agent for the region
   105  	_, err := a.Self()
   106  	return a.region, err
   107  }
   108  
   109  // Join is used to instruct a server node to join another server
   110  // via the gossip protocol. Multiple addresses may be specified.
   111  // We attempt to join all of the hosts in the list. Returns the
   112  // number of nodes successfully joined and any error. If one or
   113  // more nodes have a successful result, no error is returned.
   114  func (a *Agent) Join(addrs ...string) (int, error) {
   115  	// Accumulate the addresses
   116  	v := url.Values{}
   117  	for _, addr := range addrs {
   118  		v.Add("address", addr)
   119  	}
   120  
   121  	// Send the join request
   122  	var resp joinResponse
   123  	_, err := a.client.write("/v1/agent/join?"+v.Encode(), nil, &resp, nil)
   124  	if err != nil {
   125  		return 0, fmt.Errorf("failed joining: %s", err)
   126  	}
   127  	if resp.Error != "" {
   128  		return 0, fmt.Errorf("failed joining: %s", resp.Error)
   129  	}
   130  	return resp.NumJoined, nil
   131  }
   132  
   133  // Members is used to query all of the known server members
   134  func (a *Agent) Members() (*ServerMembers, error) {
   135  	var resp *ServerMembers
   136  
   137  	// Query the known members
   138  	_, err := a.client.query("/v1/agent/members", &resp, nil)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	return resp, nil
   143  }
   144  
   145  // ForceLeave is used to eject an existing node from the cluster.
   146  func (a *Agent) ForceLeave(node string) error {
   147  	_, err := a.client.write("/v1/agent/force-leave?node="+node, nil, nil, nil)
   148  	return err
   149  }
   150  
   151  // Servers is used to query the list of servers on a client node.
   152  func (a *Agent) Servers() ([]string, error) {
   153  	var resp []string
   154  	_, err := a.client.query("/v1/agent/servers", &resp, nil)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return resp, nil
   159  }
   160  
   161  // SetServers is used to update the list of servers on a client node.
   162  func (a *Agent) SetServers(addrs []string) error {
   163  	// Accumulate the addresses
   164  	v := url.Values{}
   165  	for _, addr := range addrs {
   166  		v.Add("address", addr)
   167  	}
   168  
   169  	_, err := a.client.write("/v1/agent/servers?"+v.Encode(), nil, nil, nil)
   170  	return err
   171  }
   172  
   173  // ListKeys returns the list of installed keys
   174  func (a *Agent) ListKeys() (*KeyringResponse, error) {
   175  	var resp KeyringResponse
   176  	_, err := a.client.query("/v1/agent/keyring/list", &resp, nil)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	return &resp, nil
   181  }
   182  
   183  // InstallKey installs a key in the keyrings of all the serf members
   184  func (a *Agent) InstallKey(key string) (*KeyringResponse, error) {
   185  	args := KeyringRequest{
   186  		Key: key,
   187  	}
   188  	var resp KeyringResponse
   189  	_, err := a.client.write("/v1/agent/keyring/install", &args, &resp, nil)
   190  	return &resp, err
   191  }
   192  
   193  // UseKey uses a key from the keyring of serf members
   194  func (a *Agent) UseKey(key string) (*KeyringResponse, error) {
   195  	args := KeyringRequest{
   196  		Key: key,
   197  	}
   198  	var resp KeyringResponse
   199  	_, err := a.client.write("/v1/agent/keyring/use", &args, &resp, nil)
   200  	return &resp, err
   201  }
   202  
   203  // RemoveKey removes a particular key from keyrings of serf members
   204  func (a *Agent) RemoveKey(key string) (*KeyringResponse, error) {
   205  	args := KeyringRequest{
   206  		Key: key,
   207  	}
   208  	var resp KeyringResponse
   209  	_, err := a.client.write("/v1/agent/keyring/remove", &args, &resp, nil)
   210  	return &resp, err
   211  }
   212  
   213  // joinResponse is used to decode the response we get while
   214  // sending a member join request.
   215  type joinResponse struct {
   216  	NumJoined int    `json:"num_joined"`
   217  	Error     string `json:"error"`
   218  }
   219  
   220  type ServerMembers struct {
   221  	ServerName string
   222  	Region     string
   223  	DC         string
   224  	Members    []*AgentMember
   225  }
   226  
   227  // AgentMember represents a cluster member known to the agent
   228  type AgentMember struct {
   229  	Name        string
   230  	Addr        string
   231  	Port        uint16
   232  	Tags        map[string]string
   233  	Status      string
   234  	ProtocolMin uint8
   235  	ProtocolMax uint8
   236  	ProtocolCur uint8
   237  	DelegateMin uint8
   238  	DelegateMax uint8
   239  	DelegateCur uint8
   240  }
   241  
   242  // AgentMembersNameSort implements sort.Interface for []*AgentMembersNameSort
   243  // based on the Name, DC and Region
   244  type AgentMembersNameSort []*AgentMember
   245  
   246  func (a AgentMembersNameSort) Len() int      { return len(a) }
   247  func (a AgentMembersNameSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   248  func (a AgentMembersNameSort) Less(i, j int) bool {
   249  	if a[i].Tags["region"] != a[j].Tags["region"] {
   250  		return a[i].Tags["region"] < a[j].Tags["region"]
   251  	}
   252  
   253  	if a[i].Tags["dc"] != a[j].Tags["dc"] {
   254  		return a[i].Tags["dc"] < a[j].Tags["dc"]
   255  	}
   256  
   257  	return a[i].Name < a[j].Name
   258  
   259  }