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 }