github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/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 // Agent returns a new agent which can be used to query 20 // the agent-specific endpoints. 21 func (c *Client) Agent() *Agent { 22 return &Agent{client: c} 23 } 24 25 // Self is used to query the /v1/agent/self endpoint and 26 // returns information specific to the running agent. 27 func (a *Agent) Self() (map[string]map[string]interface{}, error) { 28 var out map[string]map[string]interface{} 29 30 // Query the self endpoint on the agent 31 _, err := a.client.query("/v1/agent/self", &out, nil) 32 if err != nil { 33 return nil, fmt.Errorf("failed querying self endpoint: %s", err) 34 } 35 36 // Populate the cache for faster queries 37 a.populateCache(out) 38 39 return out, nil 40 } 41 42 // populateCache is used to insert various pieces of static 43 // data into the agent handle. This is used during subsequent 44 // lookups for the same data later on to save the round trip. 45 func (a *Agent) populateCache(info map[string]map[string]interface{}) { 46 if a.nodeName == "" { 47 a.nodeName, _ = info["member"]["Name"].(string) 48 } 49 if tags, ok := info["member"]["Tags"].(map[string]interface{}); ok { 50 if a.datacenter == "" { 51 a.datacenter, _ = tags["dc"].(string) 52 } 53 if a.region == "" { 54 a.region, _ = tags["region"].(string) 55 } 56 } 57 } 58 59 // NodeName is used to query the Nomad agent for its node name. 60 func (a *Agent) NodeName() (string, error) { 61 // Return from cache if we have it 62 if a.nodeName != "" { 63 return a.nodeName, nil 64 } 65 66 // Query the node name 67 _, err := a.Self() 68 return a.nodeName, err 69 } 70 71 // Datacenter is used to return the name of the datacenter which 72 // the agent is a member of. 73 func (a *Agent) Datacenter() (string, error) { 74 // Return from cache if we have it 75 if a.datacenter != "" { 76 return a.datacenter, nil 77 } 78 79 // Query the agent for the DC 80 _, err := a.Self() 81 return a.datacenter, err 82 } 83 84 // Region is used to look up the region the agent is in. 85 func (a *Agent) Region() (string, error) { 86 // Return from cache if we have it 87 if a.region != "" { 88 return a.region, nil 89 } 90 91 // Query the agent for the region 92 _, err := a.Self() 93 return a.region, err 94 } 95 96 // Join is used to instruct a server node to join another server 97 // via the gossip protocol. Multiple addresses may be specified. 98 // We attempt to join all of the hosts in the list. Returns the 99 // number of nodes successfully joined and any error. If one or 100 // more nodes have a successful result, no error is returned. 101 func (a *Agent) Join(addrs ...string) (int, error) { 102 // Accumulate the addresses 103 v := url.Values{} 104 for _, addr := range addrs { 105 v.Add("address", addr) 106 } 107 108 // Send the join request 109 var resp joinResponse 110 _, err := a.client.write("/v1/agent/join?"+v.Encode(), nil, &resp, nil) 111 if err != nil { 112 return 0, fmt.Errorf("failed joining: %s", err) 113 } 114 if resp.Error != "" { 115 return 0, fmt.Errorf("failed joining: %s", resp.Error) 116 } 117 return resp.NumJoined, nil 118 } 119 120 // Members is used to query all of the known server members 121 func (a *Agent) Members() ([]*AgentMember, error) { 122 var resp []*AgentMember 123 124 // Query the known members 125 _, err := a.client.query("/v1/agent/members", &resp, nil) 126 if err != nil { 127 return nil, err 128 } 129 return resp, nil 130 } 131 132 // ForceLeave is used to eject an existing node from the cluster. 133 func (a *Agent) ForceLeave(node string) error { 134 _, err := a.client.write("/v1/agent/force-leave?node="+node, nil, nil, nil) 135 return err 136 } 137 138 // Servers is used to query the list of servers on a client node. 139 func (a *Agent) Servers() ([]string, error) { 140 var resp []string 141 _, err := a.client.query("/v1/agent/servers", &resp, nil) 142 if err != nil { 143 return nil, err 144 } 145 return resp, nil 146 } 147 148 // SetServers is used to update the list of servers on a client node. 149 func (a *Agent) SetServers(addrs []string) error { 150 // Accumulate the addresses 151 v := url.Values{} 152 for _, addr := range addrs { 153 v.Add("address", addr) 154 } 155 156 _, err := a.client.write("/v1/agent/servers?"+v.Encode(), nil, nil, nil) 157 return err 158 } 159 160 // joinResponse is used to decode the response we get while 161 // sending a member join request. 162 type joinResponse struct { 163 NumJoined int `json:"num_joined"` 164 Error string `json:"error"` 165 } 166 167 // AgentMember represents a cluster member known to the agent 168 type AgentMember struct { 169 Name string 170 Addr string 171 Port uint16 172 Tags map[string]string 173 Status string 174 ProtocolMin uint8 175 ProtocolMax uint8 176 ProtocolCur uint8 177 DelegateMin uint8 178 DelegateMax uint8 179 DelegateCur uint8 180 }