github.com/diptanu/nomad@v0.5.7-0.20170516172507-d72e86cbe3d9/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() (*AgentSelf, error) {
    41  	var out *AgentSelf
    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(self *AgentSelf) {
    59  	if a.nodeName == "" {
    60  		a.nodeName = self.Member.Name
    61  	}
    62  	if a.datacenter == "" {
    63  		if val, ok := self.Config["Datacenter"]; ok {
    64  			a.datacenter, _ = val.(string)
    65  		}
    66  	}
    67  	if a.region == "" {
    68  		if val, ok := self.Config["Region"]; ok {
    69  			a.region, _ = val.(string)
    70  		}
    71  	}
    72  }
    73  
    74  // NodeName is used to query the Nomad agent for its node name.
    75  func (a *Agent) NodeName() (string, error) {
    76  	// Return from cache if we have it
    77  	if a.nodeName != "" {
    78  		return a.nodeName, nil
    79  	}
    80  
    81  	// Query the node name
    82  	_, err := a.Self()
    83  	return a.nodeName, err
    84  }
    85  
    86  // Datacenter is used to return the name of the datacenter which
    87  // the agent is a member of.
    88  func (a *Agent) Datacenter() (string, error) {
    89  	// Return from cache if we have it
    90  	if a.datacenter != "" {
    91  		return a.datacenter, nil
    92  	}
    93  
    94  	// Query the agent for the DC
    95  	_, err := a.Self()
    96  	return a.datacenter, err
    97  }
    98  
    99  // Region is used to look up the region the agent is in.
   100  func (a *Agent) Region() (string, error) {
   101  	// Return from cache if we have it
   102  	if a.region != "" {
   103  		return a.region, nil
   104  	}
   105  
   106  	// Query the agent for the region
   107  	_, err := a.Self()
   108  	return a.region, err
   109  }
   110  
   111  // Join is used to instruct a server node to join another server
   112  // via the gossip protocol. Multiple addresses may be specified.
   113  // We attempt to join all of the hosts in the list. Returns the
   114  // number of nodes successfully joined and any error. If one or
   115  // more nodes have a successful result, no error is returned.
   116  func (a *Agent) Join(addrs ...string) (int, error) {
   117  	// Accumulate the addresses
   118  	v := url.Values{}
   119  	for _, addr := range addrs {
   120  		v.Add("address", addr)
   121  	}
   122  
   123  	// Send the join request
   124  	var resp joinResponse
   125  	_, err := a.client.write("/v1/agent/join?"+v.Encode(), nil, &resp, nil)
   126  	if err != nil {
   127  		return 0, fmt.Errorf("failed joining: %s", err)
   128  	}
   129  	if resp.Error != "" {
   130  		return 0, fmt.Errorf("failed joining: %s", resp.Error)
   131  	}
   132  	return resp.NumJoined, nil
   133  }
   134  
   135  // Members is used to query all of the known server members
   136  func (a *Agent) Members() (*ServerMembers, error) {
   137  	var resp *ServerMembers
   138  
   139  	// Query the known members
   140  	_, err := a.client.query("/v1/agent/members", &resp, nil)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	return resp, nil
   145  }
   146  
   147  // ForceLeave is used to eject an existing node from the cluster.
   148  func (a *Agent) ForceLeave(node string) error {
   149  	_, err := a.client.write("/v1/agent/force-leave?node="+node, nil, nil, nil)
   150  	return err
   151  }
   152  
   153  // Servers is used to query the list of servers on a client node.
   154  func (a *Agent) Servers() ([]string, error) {
   155  	var resp []string
   156  	_, err := a.client.query("/v1/agent/servers", &resp, nil)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	return resp, nil
   161  }
   162  
   163  // SetServers is used to update the list of servers on a client node.
   164  func (a *Agent) SetServers(addrs []string) error {
   165  	// Accumulate the addresses
   166  	v := url.Values{}
   167  	for _, addr := range addrs {
   168  		v.Add("address", addr)
   169  	}
   170  
   171  	_, err := a.client.write("/v1/agent/servers?"+v.Encode(), nil, nil, nil)
   172  	return err
   173  }
   174  
   175  // ListKeys returns the list of installed keys
   176  func (a *Agent) ListKeys() (*KeyringResponse, error) {
   177  	var resp KeyringResponse
   178  	_, err := a.client.query("/v1/agent/keyring/list", &resp, nil)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	return &resp, nil
   183  }
   184  
   185  // InstallKey installs a key in the keyrings of all the serf members
   186  func (a *Agent) InstallKey(key string) (*KeyringResponse, error) {
   187  	args := KeyringRequest{
   188  		Key: key,
   189  	}
   190  	var resp KeyringResponse
   191  	_, err := a.client.write("/v1/agent/keyring/install", &args, &resp, nil)
   192  	return &resp, err
   193  }
   194  
   195  // UseKey uses a key from the keyring of serf members
   196  func (a *Agent) UseKey(key string) (*KeyringResponse, error) {
   197  	args := KeyringRequest{
   198  		Key: key,
   199  	}
   200  	var resp KeyringResponse
   201  	_, err := a.client.write("/v1/agent/keyring/use", &args, &resp, nil)
   202  	return &resp, err
   203  }
   204  
   205  // RemoveKey removes a particular key from keyrings of serf members
   206  func (a *Agent) RemoveKey(key string) (*KeyringResponse, error) {
   207  	args := KeyringRequest{
   208  		Key: key,
   209  	}
   210  	var resp KeyringResponse
   211  	_, err := a.client.write("/v1/agent/keyring/remove", &args, &resp, nil)
   212  	return &resp, err
   213  }
   214  
   215  // joinResponse is used to decode the response we get while
   216  // sending a member join request.
   217  type joinResponse struct {
   218  	NumJoined int    `json:"num_joined"`
   219  	Error     string `json:"error"`
   220  }
   221  
   222  type ServerMembers struct {
   223  	ServerName   string
   224  	ServerRegion string
   225  	ServerDC     string
   226  	Members      []*AgentMember
   227  }
   228  
   229  type AgentSelf struct {
   230  	Config map[string]interface{}       `json:"config"`
   231  	Member AgentMember                  `json:"member"`
   232  	Stats  map[string]map[string]string `json:"stats"`
   233  }
   234  
   235  // AgentMember represents a cluster member known to the agent
   236  type AgentMember struct {
   237  	Name        string
   238  	Addr        string
   239  	Port        uint16
   240  	Tags        map[string]string
   241  	Status      string
   242  	ProtocolMin uint8
   243  	ProtocolMax uint8
   244  	ProtocolCur uint8
   245  	DelegateMin uint8
   246  	DelegateMax uint8
   247  	DelegateCur uint8
   248  }
   249  
   250  // AgentMembersNameSort implements sort.Interface for []*AgentMembersNameSort
   251  // based on the Name, DC and Region
   252  type AgentMembersNameSort []*AgentMember
   253  
   254  func (a AgentMembersNameSort) Len() int      { return len(a) }
   255  func (a AgentMembersNameSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
   256  func (a AgentMembersNameSort) Less(i, j int) bool {
   257  	if a[i].Tags["region"] != a[j].Tags["region"] {
   258  		return a[i].Tags["region"] < a[j].Tags["region"]
   259  	}
   260  
   261  	if a[i].Tags["dc"] != a[j].Tags["dc"] {
   262  		return a[i].Tags["dc"] < a[j].Tags["dc"]
   263  	}
   264  
   265  	return a[i].Name < a[j].Name
   266  
   267  }