2015-01-06 18:40:00 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2016-11-16 21:45:26 +00:00
|
|
|
"bufio"
|
2015-01-06 18:40:00 +00:00
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
// AgentCheck represents a check known to the agent
|
|
|
|
type AgentCheck struct {
|
|
|
|
Node string
|
|
|
|
CheckID string
|
|
|
|
Name string
|
|
|
|
Status string
|
|
|
|
Notes string
|
|
|
|
Output string
|
|
|
|
ServiceID string
|
|
|
|
ServiceName string
|
|
|
|
}
|
|
|
|
|
|
|
|
// AgentService represents a service known to the agent
|
|
|
|
type AgentService struct {
|
2016-02-16 19:45:29 +00:00
|
|
|
ID string
|
|
|
|
Service string
|
|
|
|
Tags []string
|
|
|
|
Port int
|
|
|
|
Address string
|
|
|
|
EnableTagOverride bool
|
2015-01-06 18:40:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AgentMember represents a cluster member known to the agent
|
|
|
|
type AgentMember struct {
|
|
|
|
Name string
|
|
|
|
Addr string
|
|
|
|
Port uint16
|
|
|
|
Tags map[string]string
|
|
|
|
Status int
|
|
|
|
ProtocolMin uint8
|
|
|
|
ProtocolMax uint8
|
|
|
|
ProtocolCur uint8
|
|
|
|
DelegateMin uint8
|
|
|
|
DelegateMax uint8
|
|
|
|
DelegateCur uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
// AgentServiceRegistration is used to register a new service
|
|
|
|
type AgentServiceRegistration struct {
|
2016-02-16 19:45:29 +00:00
|
|
|
ID string `json:",omitempty"`
|
|
|
|
Name string `json:",omitempty"`
|
|
|
|
Tags []string `json:",omitempty"`
|
|
|
|
Port int `json:",omitempty"`
|
|
|
|
Address string `json:",omitempty"`
|
|
|
|
EnableTagOverride bool `json:",omitempty"`
|
|
|
|
Check *AgentServiceCheck
|
|
|
|
Checks AgentServiceChecks
|
2015-01-06 18:40:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AgentCheckRegistration is used to register a new check
|
|
|
|
type AgentCheckRegistration struct {
|
2015-01-14 03:18:46 +00:00
|
|
|
ID string `json:",omitempty"`
|
|
|
|
Name string `json:",omitempty"`
|
|
|
|
Notes string `json:",omitempty"`
|
|
|
|
ServiceID string `json:",omitempty"`
|
2015-01-06 18:40:00 +00:00
|
|
|
AgentServiceCheck
|
|
|
|
}
|
|
|
|
|
2016-08-16 07:05:55 +00:00
|
|
|
// AgentServiceCheck is used to define a node or service level check
|
2015-01-06 18:40:00 +00:00
|
|
|
type AgentServiceCheck struct {
|
2015-11-18 15:40:02 +00:00
|
|
|
Script string `json:",omitempty"`
|
|
|
|
DockerContainerID string `json:",omitempty"`
|
|
|
|
Shell string `json:",omitempty"` // Only supported for Docker.
|
|
|
|
Interval string `json:",omitempty"`
|
|
|
|
Timeout string `json:",omitempty"`
|
|
|
|
TTL string `json:",omitempty"`
|
|
|
|
HTTP string `json:",omitempty"`
|
|
|
|
TCP string `json:",omitempty"`
|
|
|
|
Status string `json:",omitempty"`
|
2016-11-18 00:33:50 +00:00
|
|
|
Notes string `json:",omitempty"`
|
2016-11-21 10:02:38 +00:00
|
|
|
TLSSkipVerify bool `json:",omitempty"`
|
2016-08-16 07:05:55 +00:00
|
|
|
|
2016-08-16 16:27:20 +00:00
|
|
|
// In Consul 0.7 and later, checks that are associated with a service
|
|
|
|
// may also contain this optional DeregisterCriticalServiceAfter field,
|
|
|
|
// which is a timeout in the same Go time format as Interval and TTL. If
|
|
|
|
// a check is in the critical state for more than this configured value,
|
|
|
|
// then its associated service (and all of its associated checks) will
|
2016-08-16 07:05:55 +00:00
|
|
|
// automatically be deregistered.
|
|
|
|
DeregisterCriticalServiceAfter string `json:",omitempty"`
|
2015-01-06 18:40:00 +00:00
|
|
|
}
|
2015-01-14 03:18:46 +00:00
|
|
|
type AgentServiceChecks []*AgentServiceCheck
|
2015-01-06 18:40:00 +00:00
|
|
|
|
|
|
|
// Agent can be used to query the Agent endpoints
|
|
|
|
type Agent struct {
|
|
|
|
c *Client
|
|
|
|
|
|
|
|
// cache the node name
|
|
|
|
nodeName string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Agent returns a handle to the agent endpoints
|
|
|
|
func (c *Client) Agent() *Agent {
|
|
|
|
return &Agent{c: c}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Self is used to query the agent we are speaking to for
|
|
|
|
// information about itself
|
|
|
|
func (a *Agent) Self() (map[string]map[string]interface{}, error) {
|
|
|
|
r := a.c.newRequest("GET", "/v1/agent/self")
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
var out map[string]map[string]interface{}
|
|
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2016-11-30 18:29:42 +00:00
|
|
|
// Reload triggers a configuration reload for the agent we are connected to.
|
|
|
|
func (a *Agent) Reload() error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/reload")
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-01-06 18:40:00 +00:00
|
|
|
// NodeName is used to get the node name of the agent
|
|
|
|
func (a *Agent) NodeName() (string, error) {
|
|
|
|
if a.nodeName != "" {
|
|
|
|
return a.nodeName, nil
|
|
|
|
}
|
|
|
|
info, err := a.Self()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
name := info["Config"]["NodeName"].(string)
|
|
|
|
a.nodeName = name
|
|
|
|
return name, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks returns the locally registered checks
|
|
|
|
func (a *Agent) Checks() (map[string]*AgentCheck, error) {
|
|
|
|
r := a.c.newRequest("GET", "/v1/agent/checks")
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
var out map[string]*AgentCheck
|
|
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Services returns the locally registered services
|
|
|
|
func (a *Agent) Services() (map[string]*AgentService, error) {
|
|
|
|
r := a.c.newRequest("GET", "/v1/agent/services")
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
var out map[string]*AgentService
|
|
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Members returns the known gossip members. The WAN
|
|
|
|
// flag can be used to query a server for WAN members.
|
|
|
|
func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
|
|
|
|
r := a.c.newRequest("GET", "/v1/agent/members")
|
|
|
|
if wan {
|
|
|
|
r.params.Set("wan", "1")
|
|
|
|
}
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
var out []*AgentMember
|
|
|
|
if err := decodeBody(resp, &out); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceRegister is used to register a new service with
|
|
|
|
// the local agent
|
|
|
|
func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/service/register")
|
|
|
|
r.obj = service
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceDeregister is used to deregister a service with
|
|
|
|
// the local agent
|
|
|
|
func (a *Agent) ServiceDeregister(serviceID string) error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-23 23:01:59 +00:00
|
|
|
// PassTTL is used to set a TTL check to the passing state.
|
|
|
|
//
|
|
|
|
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
|
|
|
// The client interface will be removed in 0.8 or changed to use
|
|
|
|
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
|
2015-01-06 18:40:00 +00:00
|
|
|
func (a *Agent) PassTTL(checkID, note string) error {
|
2016-03-04 23:18:25 +00:00
|
|
|
return a.updateTTL(checkID, note, "pass")
|
2015-01-06 18:40:00 +00:00
|
|
|
}
|
|
|
|
|
2016-04-23 23:01:59 +00:00
|
|
|
// WarnTTL is used to set a TTL check to the warning state.
|
|
|
|
//
|
|
|
|
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
|
|
|
// The client interface will be removed in 0.8 or changed to use
|
|
|
|
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
|
2015-01-06 18:40:00 +00:00
|
|
|
func (a *Agent) WarnTTL(checkID, note string) error {
|
2016-03-04 23:18:25 +00:00
|
|
|
return a.updateTTL(checkID, note, "warn")
|
2015-01-06 18:40:00 +00:00
|
|
|
}
|
|
|
|
|
2016-04-23 23:01:59 +00:00
|
|
|
// FailTTL is used to set a TTL check to the failing state.
|
|
|
|
//
|
|
|
|
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
|
|
|
// The client interface will be removed in 0.8 or changed to use
|
|
|
|
// UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
|
2015-01-06 18:40:00 +00:00
|
|
|
func (a *Agent) FailTTL(checkID, note string) error {
|
2016-03-04 23:18:25 +00:00
|
|
|
return a.updateTTL(checkID, note, "fail")
|
2015-01-06 18:40:00 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 23:18:25 +00:00
|
|
|
// updateTTL is used to update the TTL of a check. This is the internal
|
2016-04-23 23:01:59 +00:00
|
|
|
// method that uses the old API that's present in Consul versions prior to
|
|
|
|
// 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
|
|
|
|
// ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
|
|
|
|
// but keep the old Pass/Warn/Fail methods using the old API under the hood.
|
|
|
|
//
|
|
|
|
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
|
|
|
|
// The client interface will be removed in 0.8 and the server endpoints will
|
|
|
|
// be removed in 0.9.
|
2016-03-04 23:18:25 +00:00
|
|
|
func (a *Agent) updateTTL(checkID, note, status string) error {
|
2015-01-06 18:40:00 +00:00
|
|
|
switch status {
|
|
|
|
case "pass":
|
|
|
|
case "warn":
|
|
|
|
case "fail":
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Invalid status: %s", status)
|
|
|
|
}
|
|
|
|
endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
|
|
|
|
r := a.c.newRequest("PUT", endpoint)
|
|
|
|
r.params.Set("note", note)
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-04 23:18:25 +00:00
|
|
|
// checkUpdate is the payload for a PUT for a check update.
|
|
|
|
type checkUpdate struct {
|
2016-04-24 03:18:19 +00:00
|
|
|
// Status is one of the api.Health* states: HealthPassing
|
2016-04-23 23:01:59 +00:00
|
|
|
// ("passing"), HealthWarning ("warning"), or HealthCritical
|
|
|
|
// ("critical").
|
2016-03-04 23:18:25 +00:00
|
|
|
Status string
|
|
|
|
|
|
|
|
// Output is the information to post to the UI for operators as the
|
|
|
|
// output of the process that decided to hit the TTL check. This is
|
|
|
|
// different from the note field that's associated with the check
|
|
|
|
// itself.
|
|
|
|
Output string
|
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateTTL is used to update the TTL of a check. This uses the newer API
|
|
|
|
// that was introduced in Consul 0.6.4 and later. We translate the old status
|
|
|
|
// strings for compatibility (though a newer version of Consul will still be
|
|
|
|
// required to use this API).
|
|
|
|
func (a *Agent) UpdateTTL(checkID, output, status string) error {
|
|
|
|
switch status {
|
2016-03-24 18:26:07 +00:00
|
|
|
case "pass", HealthPassing:
|
|
|
|
status = HealthPassing
|
|
|
|
case "warn", HealthWarning:
|
|
|
|
status = HealthWarning
|
|
|
|
case "fail", HealthCritical:
|
|
|
|
status = HealthCritical
|
2016-03-04 23:18:25 +00:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("Invalid status: %s", status)
|
|
|
|
}
|
|
|
|
|
|
|
|
endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
|
|
|
|
r := a.c.newRequest("PUT", endpoint)
|
|
|
|
r.obj = &checkUpdate{
|
|
|
|
Status: status,
|
|
|
|
Output: output,
|
|
|
|
}
|
|
|
|
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-01-06 18:40:00 +00:00
|
|
|
// CheckRegister is used to register a new check with
|
|
|
|
// the local agent
|
|
|
|
func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/check/register")
|
|
|
|
r.obj = check
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CheckDeregister is used to deregister a check with
|
|
|
|
// the local agent
|
|
|
|
func (a *Agent) CheckDeregister(checkID string) error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Join is used to instruct the agent to attempt a join to
|
|
|
|
// another cluster member
|
|
|
|
func (a *Agent) Join(addr string, wan bool) error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
|
|
|
|
if wan {
|
|
|
|
r.params.Set("wan", "1")
|
|
|
|
}
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-30 18:29:42 +00:00
|
|
|
// Leave is used to have the agent gracefully leave the cluster and shutdown
|
|
|
|
func (a *Agent) Leave() error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/leave")
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-01-06 18:40:00 +00:00
|
|
|
// ForceLeave is used to have the agent eject a failed node
|
|
|
|
func (a *Agent) ForceLeave(node string) error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
2015-01-21 18:51:26 +00:00
|
|
|
|
|
|
|
// EnableServiceMaintenance toggles service maintenance mode on
|
|
|
|
// for the given service ID.
|
2015-01-21 21:02:47 +00:00
|
|
|
func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
|
2015-01-21 18:51:26 +00:00
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
|
|
|
|
r.params.Set("enable", "true")
|
2015-01-21 21:02:47 +00:00
|
|
|
r.params.Set("reason", reason)
|
2015-01-21 18:51:26 +00:00
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DisableServiceMaintenance toggles service maintenance mode off
|
|
|
|
// for the given service ID.
|
|
|
|
func (a *Agent) DisableServiceMaintenance(serviceID string) error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
|
|
|
|
r.params.Set("enable", "false")
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnableNodeMaintenance toggles node maintenance mode on for the
|
|
|
|
// agent we are connected to.
|
2015-01-21 21:02:47 +00:00
|
|
|
func (a *Agent) EnableNodeMaintenance(reason string) error {
|
2015-01-21 18:51:26 +00:00
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/maintenance")
|
|
|
|
r.params.Set("enable", "true")
|
2015-01-21 21:02:47 +00:00
|
|
|
r.params.Set("reason", reason)
|
2015-01-21 18:51:26 +00:00
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DisableNodeMaintenance toggles node maintenance mode off for the
|
|
|
|
// agent we are connected to.
|
|
|
|
func (a *Agent) DisableNodeMaintenance() error {
|
|
|
|
r := a.c.newRequest("PUT", "/v1/agent/maintenance")
|
|
|
|
r.params.Set("enable", "false")
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
resp.Body.Close()
|
|
|
|
return nil
|
|
|
|
}
|
2016-11-16 21:45:26 +00:00
|
|
|
|
|
|
|
// Monitor returns a channel which will receive streaming logs from the agent
|
|
|
|
// Providing a non-nil stopCh can be used to close the connection and stop the
|
|
|
|
// log stream
|
2016-11-28 21:13:49 +00:00
|
|
|
func (a *Agent) Monitor(loglevel string, stopCh chan struct{}, q *QueryOptions) (chan string, error) {
|
2016-11-16 21:45:26 +00:00
|
|
|
r := a.c.newRequest("GET", "/v1/agent/monitor")
|
2016-11-28 21:13:49 +00:00
|
|
|
r.setQueryOptions(q)
|
2016-11-16 21:45:26 +00:00
|
|
|
if loglevel != "" {
|
|
|
|
r.params.Add("loglevel", loglevel)
|
|
|
|
}
|
|
|
|
_, resp, err := requireOK(a.c.doRequest(r))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
logCh := make(chan string, 64)
|
|
|
|
go func() {
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(resp.Body)
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-stopCh:
|
|
|
|
close(logCh)
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
if scanner.Scan() {
|
|
|
|
logCh <- scanner.Text()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return logCh, nil
|
|
|
|
}
|