github.com/marwan-at-work/consul@v1.4.5/api/agent.go (about)

     1  package api
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  )
     9  
    10  // ServiceKind is the kind of service being registered.
    11  type ServiceKind string
    12  
    13  const (
    14  	// ServiceKindTypical is a typical, classic Consul service. This is
    15  	// represented by the absence of a value. This was chosen for ease of
    16  	// backwards compatibility: existing services in the catalog would
    17  	// default to the typical service.
    18  	ServiceKindTypical ServiceKind = ""
    19  
    20  	// ServiceKindConnectProxy is a proxy for the Connect feature. This
    21  	// service proxies another service within Consul and speaks the connect
    22  	// protocol.
    23  	ServiceKindConnectProxy ServiceKind = "connect-proxy"
    24  )
    25  
    26  // ProxyExecMode is the execution mode for a managed Connect proxy.
    27  type ProxyExecMode string
    28  
    29  const (
    30  	// ProxyExecModeDaemon indicates that the proxy command should be long-running
    31  	// and should be started and supervised by the agent until it's target service
    32  	// is deregistered.
    33  	ProxyExecModeDaemon ProxyExecMode = "daemon"
    34  
    35  	// ProxyExecModeScript indicates that the proxy command should be invoke to
    36  	// completion on each change to the configuration of lifecycle event. The
    37  	// script typically fetches the config and certificates from the agent API and
    38  	// then configures an externally managed daemon, perhaps starting and stopping
    39  	// it if necessary.
    40  	ProxyExecModeScript ProxyExecMode = "script"
    41  )
    42  
    43  // UpstreamDestType is the type of upstream discovery mechanism.
    44  type UpstreamDestType string
    45  
    46  const (
    47  	// UpstreamDestTypeService discovers instances via healthy service lookup.
    48  	UpstreamDestTypeService UpstreamDestType = "service"
    49  
    50  	// UpstreamDestTypePreparedQuery discovers instances via prepared query
    51  	// execution.
    52  	UpstreamDestTypePreparedQuery UpstreamDestType = "prepared_query"
    53  )
    54  
    55  // AgentCheck represents a check known to the agent
    56  type AgentCheck struct {
    57  	Node        string
    58  	CheckID     string
    59  	Name        string
    60  	Status      string
    61  	Notes       string
    62  	Output      string
    63  	ServiceID   string
    64  	ServiceName string
    65  	Definition  HealthCheckDefinition
    66  }
    67  
    68  // AgentWeights represent optional weights for a service
    69  type AgentWeights struct {
    70  	Passing int
    71  	Warning int
    72  }
    73  
    74  // AgentService represents a service known to the agent
    75  type AgentService struct {
    76  	Kind              ServiceKind `json:",omitempty"`
    77  	ID                string
    78  	Service           string
    79  	Tags              []string
    80  	Meta              map[string]string
    81  	Port              int
    82  	Address           string
    83  	Weights           AgentWeights
    84  	EnableTagOverride bool
    85  	CreateIndex       uint64 `json:",omitempty"`
    86  	ModifyIndex       uint64 `json:",omitempty"`
    87  	ContentHash       string `json:",omitempty"`
    88  	// DEPRECATED (ProxyDestination) - remove this field
    89  	ProxyDestination string                          `json:",omitempty"`
    90  	Proxy            *AgentServiceConnectProxyConfig `json:",omitempty"`
    91  	Connect          *AgentServiceConnect            `json:",omitempty"`
    92  }
    93  
    94  // AgentServiceChecksInfo returns information about a Service and its checks
    95  type AgentServiceChecksInfo struct {
    96  	AggregatedStatus string
    97  	Service          *AgentService
    98  	Checks           HealthChecks
    99  }
   100  
   101  // AgentServiceConnect represents the Connect configuration of a service.
   102  type AgentServiceConnect struct {
   103  	Native         bool                      `json:",omitempty"`
   104  	Proxy          *AgentServiceConnectProxy `json:",omitempty"`
   105  	SidecarService *AgentServiceRegistration `json:",omitempty"`
   106  }
   107  
   108  // AgentServiceConnectProxy represents the Connect Proxy configuration of a
   109  // service.
   110  type AgentServiceConnectProxy struct {
   111  	ExecMode  ProxyExecMode          `json:",omitempty"`
   112  	Command   []string               `json:",omitempty"`
   113  	Config    map[string]interface{} `json:",omitempty"`
   114  	Upstreams []Upstream             `json:",omitempty"`
   115  }
   116  
   117  // AgentServiceConnectProxyConfig is the proxy configuration in a connect-proxy
   118  // ServiceDefinition or response.
   119  type AgentServiceConnectProxyConfig struct {
   120  	DestinationServiceName string
   121  	DestinationServiceID   string                 `json:",omitempty"`
   122  	LocalServiceAddress    string                 `json:",omitempty"`
   123  	LocalServicePort       int                    `json:",omitempty"`
   124  	Config                 map[string]interface{} `json:",omitempty"`
   125  	Upstreams              []Upstream
   126  }
   127  
   128  // AgentMember represents a cluster member known to the agent
   129  type AgentMember struct {
   130  	Name        string
   131  	Addr        string
   132  	Port        uint16
   133  	Tags        map[string]string
   134  	Status      int
   135  	ProtocolMin uint8
   136  	ProtocolMax uint8
   137  	ProtocolCur uint8
   138  	DelegateMin uint8
   139  	DelegateMax uint8
   140  	DelegateCur uint8
   141  }
   142  
   143  // AllSegments is used to select for all segments in MembersOpts.
   144  const AllSegments = "_all"
   145  
   146  // MembersOpts is used for querying member information.
   147  type MembersOpts struct {
   148  	// WAN is whether to show members from the WAN.
   149  	WAN bool
   150  
   151  	// Segment is the LAN segment to show members for. Setting this to the
   152  	// AllSegments value above will show members in all segments.
   153  	Segment string
   154  }
   155  
   156  // AgentServiceRegistration is used to register a new service
   157  type AgentServiceRegistration struct {
   158  	Kind              ServiceKind       `json:",omitempty"`
   159  	ID                string            `json:",omitempty"`
   160  	Name              string            `json:",omitempty"`
   161  	Tags              []string          `json:",omitempty"`
   162  	Port              int               `json:",omitempty"`
   163  	Address           string            `json:",omitempty"`
   164  	EnableTagOverride bool              `json:",omitempty"`
   165  	Meta              map[string]string `json:",omitempty"`
   166  	Weights           *AgentWeights     `json:",omitempty"`
   167  	Check             *AgentServiceCheck
   168  	Checks            AgentServiceChecks
   169  	// DEPRECATED (ProxyDestination) - remove this field
   170  	ProxyDestination string                          `json:",omitempty"`
   171  	Proxy            *AgentServiceConnectProxyConfig `json:",omitempty"`
   172  	Connect          *AgentServiceConnect            `json:",omitempty"`
   173  }
   174  
   175  // AgentCheckRegistration is used to register a new check
   176  type AgentCheckRegistration struct {
   177  	ID        string `json:",omitempty"`
   178  	Name      string `json:",omitempty"`
   179  	Notes     string `json:",omitempty"`
   180  	ServiceID string `json:",omitempty"`
   181  	AgentServiceCheck
   182  }
   183  
   184  // AgentServiceCheck is used to define a node or service level check
   185  type AgentServiceCheck struct {
   186  	CheckID           string              `json:",omitempty"`
   187  	Name              string              `json:",omitempty"`
   188  	Args              []string            `json:"ScriptArgs,omitempty"`
   189  	DockerContainerID string              `json:",omitempty"`
   190  	Shell             string              `json:",omitempty"` // Only supported for Docker.
   191  	Interval          string              `json:",omitempty"`
   192  	Timeout           string              `json:",omitempty"`
   193  	TTL               string              `json:",omitempty"`
   194  	HTTP              string              `json:",omitempty"`
   195  	Header            map[string][]string `json:",omitempty"`
   196  	Method            string              `json:",omitempty"`
   197  	TCP               string              `json:",omitempty"`
   198  	Status            string              `json:",omitempty"`
   199  	Notes             string              `json:",omitempty"`
   200  	TLSSkipVerify     bool                `json:",omitempty"`
   201  	GRPC              string              `json:",omitempty"`
   202  	GRPCUseTLS        bool                `json:",omitempty"`
   203  	AliasNode         string              `json:",omitempty"`
   204  	AliasService      string              `json:",omitempty"`
   205  
   206  	// In Consul 0.7 and later, checks that are associated with a service
   207  	// may also contain this optional DeregisterCriticalServiceAfter field,
   208  	// which is a timeout in the same Go time format as Interval and TTL. If
   209  	// a check is in the critical state for more than this configured value,
   210  	// then its associated service (and all of its associated checks) will
   211  	// automatically be deregistered.
   212  	DeregisterCriticalServiceAfter string `json:",omitempty"`
   213  }
   214  type AgentServiceChecks []*AgentServiceCheck
   215  
   216  // AgentToken is used when updating ACL tokens for an agent.
   217  type AgentToken struct {
   218  	Token string
   219  }
   220  
   221  // Metrics info is used to store different types of metric values from the agent.
   222  type MetricsInfo struct {
   223  	Timestamp string
   224  	Gauges    []GaugeValue
   225  	Points    []PointValue
   226  	Counters  []SampledValue
   227  	Samples   []SampledValue
   228  }
   229  
   230  // GaugeValue stores one value that is updated as time goes on, such as
   231  // the amount of memory allocated.
   232  type GaugeValue struct {
   233  	Name   string
   234  	Value  float32
   235  	Labels map[string]string
   236  }
   237  
   238  // PointValue holds a series of points for a metric.
   239  type PointValue struct {
   240  	Name   string
   241  	Points []float32
   242  }
   243  
   244  // SampledValue stores info about a metric that is incremented over time,
   245  // such as the number of requests to an HTTP endpoint.
   246  type SampledValue struct {
   247  	Name   string
   248  	Count  int
   249  	Sum    float64
   250  	Min    float64
   251  	Max    float64
   252  	Mean   float64
   253  	Stddev float64
   254  	Labels map[string]string
   255  }
   256  
   257  // AgentAuthorizeParams are the request parameters for authorizing a request.
   258  type AgentAuthorizeParams struct {
   259  	Target           string
   260  	ClientCertURI    string
   261  	ClientCertSerial string
   262  }
   263  
   264  // AgentAuthorize is the response structure for Connect authorization.
   265  type AgentAuthorize struct {
   266  	Authorized bool
   267  	Reason     string
   268  }
   269  
   270  // ConnectProxyConfig is the response structure for agent-local proxy
   271  // configuration.
   272  type ConnectProxyConfig struct {
   273  	ProxyServiceID    string
   274  	TargetServiceID   string
   275  	TargetServiceName string
   276  	ContentHash       string
   277  	// DEPRECATED(managed-proxies) - this struct is re-used for sidecar configs
   278  	// but they don't need ExecMode or Command
   279  	ExecMode  ProxyExecMode `json:",omitempty"`
   280  	Command   []string      `json:",omitempty"`
   281  	Config    map[string]interface{}
   282  	Upstreams []Upstream
   283  }
   284  
   285  // Upstream is the response structure for a proxy upstream configuration.
   286  type Upstream struct {
   287  	DestinationType      UpstreamDestType `json:",omitempty"`
   288  	DestinationNamespace string           `json:",omitempty"`
   289  	DestinationName      string
   290  	Datacenter           string                 `json:",omitempty"`
   291  	LocalBindAddress     string                 `json:",omitempty"`
   292  	LocalBindPort        int                    `json:",omitempty"`
   293  	Config               map[string]interface{} `json:",omitempty"`
   294  }
   295  
   296  // Agent can be used to query the Agent endpoints
   297  type Agent struct {
   298  	c *Client
   299  
   300  	// cache the node name
   301  	nodeName string
   302  }
   303  
   304  // Agent returns a handle to the agent endpoints
   305  func (c *Client) Agent() *Agent {
   306  	return &Agent{c: c}
   307  }
   308  
   309  // Self is used to query the agent we are speaking to for
   310  // information about itself
   311  func (a *Agent) Self() (map[string]map[string]interface{}, error) {
   312  	r := a.c.newRequest("GET", "/v1/agent/self")
   313  	_, resp, err := requireOK(a.c.doRequest(r))
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  	defer resp.Body.Close()
   318  
   319  	var out map[string]map[string]interface{}
   320  	if err := decodeBody(resp, &out); err != nil {
   321  		return nil, err
   322  	}
   323  	return out, nil
   324  }
   325  
   326  // Host is used to retrieve information about the host the
   327  // agent is running on such as CPU, memory, and disk. Requires
   328  // a operator:read ACL token.
   329  func (a *Agent) Host() (map[string]interface{}, error) {
   330  	r := a.c.newRequest("GET", "/v1/agent/host")
   331  	_, resp, err := requireOK(a.c.doRequest(r))
   332  	if err != nil {
   333  		return nil, err
   334  	}
   335  	defer resp.Body.Close()
   336  
   337  	var out map[string]interface{}
   338  	if err := decodeBody(resp, &out); err != nil {
   339  		return nil, err
   340  	}
   341  	return out, nil
   342  }
   343  
   344  // Metrics is used to query the agent we are speaking to for
   345  // its current internal metric data
   346  func (a *Agent) Metrics() (*MetricsInfo, error) {
   347  	r := a.c.newRequest("GET", "/v1/agent/metrics")
   348  	_, resp, err := requireOK(a.c.doRequest(r))
   349  	if err != nil {
   350  		return nil, err
   351  	}
   352  	defer resp.Body.Close()
   353  
   354  	var out *MetricsInfo
   355  	if err := decodeBody(resp, &out); err != nil {
   356  		return nil, err
   357  	}
   358  	return out, nil
   359  }
   360  
   361  // Reload triggers a configuration reload for the agent we are connected to.
   362  func (a *Agent) Reload() error {
   363  	r := a.c.newRequest("PUT", "/v1/agent/reload")
   364  	_, resp, err := requireOK(a.c.doRequest(r))
   365  	if err != nil {
   366  		return err
   367  	}
   368  	resp.Body.Close()
   369  	return nil
   370  }
   371  
   372  // NodeName is used to get the node name of the agent
   373  func (a *Agent) NodeName() (string, error) {
   374  	if a.nodeName != "" {
   375  		return a.nodeName, nil
   376  	}
   377  	info, err := a.Self()
   378  	if err != nil {
   379  		return "", err
   380  	}
   381  	name := info["Config"]["NodeName"].(string)
   382  	a.nodeName = name
   383  	return name, nil
   384  }
   385  
   386  // Checks returns the locally registered checks
   387  func (a *Agent) Checks() (map[string]*AgentCheck, error) {
   388  	r := a.c.newRequest("GET", "/v1/agent/checks")
   389  	_, resp, err := requireOK(a.c.doRequest(r))
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  	defer resp.Body.Close()
   394  
   395  	var out map[string]*AgentCheck
   396  	if err := decodeBody(resp, &out); err != nil {
   397  		return nil, err
   398  	}
   399  	return out, nil
   400  }
   401  
   402  // Services returns the locally registered services
   403  func (a *Agent) Services() (map[string]*AgentService, error) {
   404  	r := a.c.newRequest("GET", "/v1/agent/services")
   405  	_, resp, err := requireOK(a.c.doRequest(r))
   406  	if err != nil {
   407  		return nil, err
   408  	}
   409  	defer resp.Body.Close()
   410  
   411  	var out map[string]*AgentService
   412  	if err := decodeBody(resp, &out); err != nil {
   413  		return nil, err
   414  	}
   415  
   416  	return out, nil
   417  }
   418  
   419  // AgentHealthServiceByID returns for a given serviceID: the aggregated health status, the service definition or an error if any
   420  // - If the service is not found, will return status (critical, nil, nil)
   421  // - If the service is found, will return (critical|passing|warning), AgentServiceChecksInfo, nil)
   422  // - In all other cases, will return an error
   423  func (a *Agent) AgentHealthServiceByID(serviceID string) (string, *AgentServiceChecksInfo, error) {
   424  	path := fmt.Sprintf("/v1/agent/health/service/id/%v", url.PathEscape(serviceID))
   425  	r := a.c.newRequest("GET", path)
   426  	r.params.Add("format", "json")
   427  	r.header.Set("Accept", "application/json")
   428  	_, resp, err := a.c.doRequest(r)
   429  	if err != nil {
   430  		return "", nil, err
   431  	}
   432  	defer resp.Body.Close()
   433  	// Service not Found
   434  	if resp.StatusCode == http.StatusNotFound {
   435  		return HealthCritical, nil, nil
   436  	}
   437  	var out *AgentServiceChecksInfo
   438  	if err := decodeBody(resp, &out); err != nil {
   439  		return HealthCritical, out, err
   440  	}
   441  	switch resp.StatusCode {
   442  	case http.StatusOK:
   443  		return HealthPassing, out, nil
   444  	case http.StatusTooManyRequests:
   445  		return HealthWarning, out, nil
   446  	case http.StatusServiceUnavailable:
   447  		return HealthCritical, out, nil
   448  	}
   449  	return HealthCritical, out, fmt.Errorf("Unexpected Error Code %v for %s", resp.StatusCode, path)
   450  }
   451  
   452  // AgentHealthServiceByName returns for a given service name: the aggregated health status for all services
   453  // having the specified name.
   454  // - If no service is not found, will return status (critical, [], nil)
   455  // - If the service is found, will return (critical|passing|warning), []api.AgentServiceChecksInfo, nil)
   456  // - In all other cases, will return an error
   457  func (a *Agent) AgentHealthServiceByName(service string) (string, []AgentServiceChecksInfo, error) {
   458  	path := fmt.Sprintf("/v1/agent/health/service/name/%v", url.PathEscape(service))
   459  	r := a.c.newRequest("GET", path)
   460  	r.params.Add("format", "json")
   461  	r.header.Set("Accept", "application/json")
   462  	_, resp, err := a.c.doRequest(r)
   463  	if err != nil {
   464  		return "", nil, err
   465  	}
   466  	defer resp.Body.Close()
   467  	// Service not Found
   468  	if resp.StatusCode == http.StatusNotFound {
   469  		return HealthCritical, nil, nil
   470  	}
   471  	var out []AgentServiceChecksInfo
   472  	if err := decodeBody(resp, &out); err != nil {
   473  		return HealthCritical, out, err
   474  	}
   475  	switch resp.StatusCode {
   476  	case http.StatusOK:
   477  		return HealthPassing, out, nil
   478  	case http.StatusTooManyRequests:
   479  		return HealthWarning, out, nil
   480  	case http.StatusServiceUnavailable:
   481  		return HealthCritical, out, nil
   482  	}
   483  	return HealthCritical, out, fmt.Errorf("Unexpected Error Code %v for %s", resp.StatusCode, path)
   484  }
   485  
   486  // Service returns a locally registered service instance and allows for
   487  // hash-based blocking.
   488  //
   489  // Note that this uses an unconventional blocking mechanism since it's
   490  // agent-local state. That means there is no persistent raft index so we block
   491  // based on object hash instead.
   492  func (a *Agent) Service(serviceID string, q *QueryOptions) (*AgentService, *QueryMeta, error) {
   493  	r := a.c.newRequest("GET", "/v1/agent/service/"+serviceID)
   494  	r.setQueryOptions(q)
   495  	rtt, resp, err := requireOK(a.c.doRequest(r))
   496  	if err != nil {
   497  		return nil, nil, err
   498  	}
   499  	defer resp.Body.Close()
   500  
   501  	qm := &QueryMeta{}
   502  	parseQueryMeta(resp, qm)
   503  	qm.RequestTime = rtt
   504  
   505  	var out *AgentService
   506  	if err := decodeBody(resp, &out); err != nil {
   507  		return nil, nil, err
   508  	}
   509  
   510  	return out, qm, nil
   511  }
   512  
   513  // Members returns the known gossip members. The WAN
   514  // flag can be used to query a server for WAN members.
   515  func (a *Agent) Members(wan bool) ([]*AgentMember, error) {
   516  	r := a.c.newRequest("GET", "/v1/agent/members")
   517  	if wan {
   518  		r.params.Set("wan", "1")
   519  	}
   520  	_, resp, err := requireOK(a.c.doRequest(r))
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  	defer resp.Body.Close()
   525  
   526  	var out []*AgentMember
   527  	if err := decodeBody(resp, &out); err != nil {
   528  		return nil, err
   529  	}
   530  	return out, nil
   531  }
   532  
   533  // MembersOpts returns the known gossip members and can be passed
   534  // additional options for WAN/segment filtering.
   535  func (a *Agent) MembersOpts(opts MembersOpts) ([]*AgentMember, error) {
   536  	r := a.c.newRequest("GET", "/v1/agent/members")
   537  	r.params.Set("segment", opts.Segment)
   538  	if opts.WAN {
   539  		r.params.Set("wan", "1")
   540  	}
   541  
   542  	_, resp, err := requireOK(a.c.doRequest(r))
   543  	if err != nil {
   544  		return nil, err
   545  	}
   546  	defer resp.Body.Close()
   547  
   548  	var out []*AgentMember
   549  	if err := decodeBody(resp, &out); err != nil {
   550  		return nil, err
   551  	}
   552  	return out, nil
   553  }
   554  
   555  // ServiceRegister is used to register a new service with
   556  // the local agent
   557  func (a *Agent) ServiceRegister(service *AgentServiceRegistration) error {
   558  	r := a.c.newRequest("PUT", "/v1/agent/service/register")
   559  	r.obj = service
   560  	_, resp, err := requireOK(a.c.doRequest(r))
   561  	if err != nil {
   562  		return err
   563  	}
   564  	resp.Body.Close()
   565  	return nil
   566  }
   567  
   568  // ServiceDeregister is used to deregister a service with
   569  // the local agent
   570  func (a *Agent) ServiceDeregister(serviceID string) error {
   571  	r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
   572  	_, resp, err := requireOK(a.c.doRequest(r))
   573  	if err != nil {
   574  		return err
   575  	}
   576  	resp.Body.Close()
   577  	return nil
   578  }
   579  
   580  // PassTTL is used to set a TTL check to the passing state.
   581  //
   582  // DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
   583  // The client interface will be removed in 0.8 or changed to use
   584  // UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
   585  func (a *Agent) PassTTL(checkID, note string) error {
   586  	return a.updateTTL(checkID, note, "pass")
   587  }
   588  
   589  // WarnTTL is used to set a TTL check to the warning state.
   590  //
   591  // DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
   592  // The client interface will be removed in 0.8 or changed to use
   593  // UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
   594  func (a *Agent) WarnTTL(checkID, note string) error {
   595  	return a.updateTTL(checkID, note, "warn")
   596  }
   597  
   598  // FailTTL is used to set a TTL check to the failing state.
   599  //
   600  // DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
   601  // The client interface will be removed in 0.8 or changed to use
   602  // UpdateTTL()'s endpoint and the server endpoints will be removed in 0.9.
   603  func (a *Agent) FailTTL(checkID, note string) error {
   604  	return a.updateTTL(checkID, note, "fail")
   605  }
   606  
   607  // updateTTL is used to update the TTL of a check. This is the internal
   608  // method that uses the old API that's present in Consul versions prior to
   609  // 0.6.4. Since Consul didn't have an analogous "update" API before it seemed
   610  // ok to break this (former) UpdateTTL in favor of the new UpdateTTL below,
   611  // but keep the old Pass/Warn/Fail methods using the old API under the hood.
   612  //
   613  // DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
   614  // The client interface will be removed in 0.8 and the server endpoints will
   615  // be removed in 0.9.
   616  func (a *Agent) updateTTL(checkID, note, status string) error {
   617  	switch status {
   618  	case "pass":
   619  	case "warn":
   620  	case "fail":
   621  	default:
   622  		return fmt.Errorf("Invalid status: %s", status)
   623  	}
   624  	endpoint := fmt.Sprintf("/v1/agent/check/%s/%s", status, checkID)
   625  	r := a.c.newRequest("PUT", endpoint)
   626  	r.params.Set("note", note)
   627  	_, resp, err := requireOK(a.c.doRequest(r))
   628  	if err != nil {
   629  		return err
   630  	}
   631  	resp.Body.Close()
   632  	return nil
   633  }
   634  
   635  // checkUpdate is the payload for a PUT for a check update.
   636  type checkUpdate struct {
   637  	// Status is one of the api.Health* states: HealthPassing
   638  	// ("passing"), HealthWarning ("warning"), or HealthCritical
   639  	// ("critical").
   640  	Status string
   641  
   642  	// Output is the information to post to the UI for operators as the
   643  	// output of the process that decided to hit the TTL check. This is
   644  	// different from the note field that's associated with the check
   645  	// itself.
   646  	Output string
   647  }
   648  
   649  // UpdateTTL is used to update the TTL of a check. This uses the newer API
   650  // that was introduced in Consul 0.6.4 and later. We translate the old status
   651  // strings for compatibility (though a newer version of Consul will still be
   652  // required to use this API).
   653  func (a *Agent) UpdateTTL(checkID, output, status string) error {
   654  	switch status {
   655  	case "pass", HealthPassing:
   656  		status = HealthPassing
   657  	case "warn", HealthWarning:
   658  		status = HealthWarning
   659  	case "fail", HealthCritical:
   660  		status = HealthCritical
   661  	default:
   662  		return fmt.Errorf("Invalid status: %s", status)
   663  	}
   664  
   665  	endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
   666  	r := a.c.newRequest("PUT", endpoint)
   667  	r.obj = &checkUpdate{
   668  		Status: status,
   669  		Output: output,
   670  	}
   671  
   672  	_, resp, err := requireOK(a.c.doRequest(r))
   673  	if err != nil {
   674  		return err
   675  	}
   676  	resp.Body.Close()
   677  	return nil
   678  }
   679  
   680  // CheckRegister is used to register a new check with
   681  // the local agent
   682  func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
   683  	r := a.c.newRequest("PUT", "/v1/agent/check/register")
   684  	r.obj = check
   685  	_, resp, err := requireOK(a.c.doRequest(r))
   686  	if err != nil {
   687  		return err
   688  	}
   689  	resp.Body.Close()
   690  	return nil
   691  }
   692  
   693  // CheckDeregister is used to deregister a check with
   694  // the local agent
   695  func (a *Agent) CheckDeregister(checkID string) error {
   696  	r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
   697  	_, resp, err := requireOK(a.c.doRequest(r))
   698  	if err != nil {
   699  		return err
   700  	}
   701  	resp.Body.Close()
   702  	return nil
   703  }
   704  
   705  // Join is used to instruct the agent to attempt a join to
   706  // another cluster member
   707  func (a *Agent) Join(addr string, wan bool) error {
   708  	r := a.c.newRequest("PUT", "/v1/agent/join/"+addr)
   709  	if wan {
   710  		r.params.Set("wan", "1")
   711  	}
   712  	_, resp, err := requireOK(a.c.doRequest(r))
   713  	if err != nil {
   714  		return err
   715  	}
   716  	resp.Body.Close()
   717  	return nil
   718  }
   719  
   720  // Leave is used to have the agent gracefully leave the cluster and shutdown
   721  func (a *Agent) Leave() error {
   722  	r := a.c.newRequest("PUT", "/v1/agent/leave")
   723  	_, resp, err := requireOK(a.c.doRequest(r))
   724  	if err != nil {
   725  		return err
   726  	}
   727  	resp.Body.Close()
   728  	return nil
   729  }
   730  
   731  // ForceLeave is used to have the agent eject a failed node
   732  func (a *Agent) ForceLeave(node string) error {
   733  	r := a.c.newRequest("PUT", "/v1/agent/force-leave/"+node)
   734  	_, resp, err := requireOK(a.c.doRequest(r))
   735  	if err != nil {
   736  		return err
   737  	}
   738  	resp.Body.Close()
   739  	return nil
   740  }
   741  
   742  // ConnectAuthorize is used to authorize an incoming connection
   743  // to a natively integrated Connect service.
   744  func (a *Agent) ConnectAuthorize(auth *AgentAuthorizeParams) (*AgentAuthorize, error) {
   745  	r := a.c.newRequest("POST", "/v1/agent/connect/authorize")
   746  	r.obj = auth
   747  	_, resp, err := requireOK(a.c.doRequest(r))
   748  	if err != nil {
   749  		return nil, err
   750  	}
   751  	defer resp.Body.Close()
   752  
   753  	var out AgentAuthorize
   754  	if err := decodeBody(resp, &out); err != nil {
   755  		return nil, err
   756  	}
   757  	return &out, nil
   758  }
   759  
   760  // ConnectCARoots returns the list of roots.
   761  func (a *Agent) ConnectCARoots(q *QueryOptions) (*CARootList, *QueryMeta, error) {
   762  	r := a.c.newRequest("GET", "/v1/agent/connect/ca/roots")
   763  	r.setQueryOptions(q)
   764  	rtt, resp, err := requireOK(a.c.doRequest(r))
   765  	if err != nil {
   766  		return nil, nil, err
   767  	}
   768  	defer resp.Body.Close()
   769  
   770  	qm := &QueryMeta{}
   771  	parseQueryMeta(resp, qm)
   772  	qm.RequestTime = rtt
   773  
   774  	var out CARootList
   775  	if err := decodeBody(resp, &out); err != nil {
   776  		return nil, nil, err
   777  	}
   778  	return &out, qm, nil
   779  }
   780  
   781  // ConnectCALeaf gets the leaf certificate for the given service ID.
   782  func (a *Agent) ConnectCALeaf(serviceID string, q *QueryOptions) (*LeafCert, *QueryMeta, error) {
   783  	r := a.c.newRequest("GET", "/v1/agent/connect/ca/leaf/"+serviceID)
   784  	r.setQueryOptions(q)
   785  	rtt, resp, err := requireOK(a.c.doRequest(r))
   786  	if err != nil {
   787  		return nil, nil, err
   788  	}
   789  	defer resp.Body.Close()
   790  
   791  	qm := &QueryMeta{}
   792  	parseQueryMeta(resp, qm)
   793  	qm.RequestTime = rtt
   794  
   795  	var out LeafCert
   796  	if err := decodeBody(resp, &out); err != nil {
   797  		return nil, nil, err
   798  	}
   799  	return &out, qm, nil
   800  }
   801  
   802  // ConnectProxyConfig gets the configuration for a local managed proxy instance.
   803  //
   804  // Note that this uses an unconventional blocking mechanism since it's
   805  // agent-local state. That means there is no persistent raft index so we block
   806  // based on object hash instead.
   807  func (a *Agent) ConnectProxyConfig(proxyServiceID string, q *QueryOptions) (*ConnectProxyConfig, *QueryMeta, error) {
   808  	r := a.c.newRequest("GET", "/v1/agent/connect/proxy/"+proxyServiceID)
   809  	r.setQueryOptions(q)
   810  	rtt, resp, err := requireOK(a.c.doRequest(r))
   811  	if err != nil {
   812  		return nil, nil, err
   813  	}
   814  	defer resp.Body.Close()
   815  
   816  	qm := &QueryMeta{}
   817  	parseQueryMeta(resp, qm)
   818  	qm.RequestTime = rtt
   819  
   820  	var out ConnectProxyConfig
   821  	if err := decodeBody(resp, &out); err != nil {
   822  		return nil, nil, err
   823  	}
   824  	return &out, qm, nil
   825  }
   826  
   827  // EnableServiceMaintenance toggles service maintenance mode on
   828  // for the given service ID.
   829  func (a *Agent) EnableServiceMaintenance(serviceID, reason string) error {
   830  	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
   831  	r.params.Set("enable", "true")
   832  	r.params.Set("reason", reason)
   833  	_, resp, err := requireOK(a.c.doRequest(r))
   834  	if err != nil {
   835  		return err
   836  	}
   837  	resp.Body.Close()
   838  	return nil
   839  }
   840  
   841  // DisableServiceMaintenance toggles service maintenance mode off
   842  // for the given service ID.
   843  func (a *Agent) DisableServiceMaintenance(serviceID string) error {
   844  	r := a.c.newRequest("PUT", "/v1/agent/service/maintenance/"+serviceID)
   845  	r.params.Set("enable", "false")
   846  	_, resp, err := requireOK(a.c.doRequest(r))
   847  	if err != nil {
   848  		return err
   849  	}
   850  	resp.Body.Close()
   851  	return nil
   852  }
   853  
   854  // EnableNodeMaintenance toggles node maintenance mode on for the
   855  // agent we are connected to.
   856  func (a *Agent) EnableNodeMaintenance(reason string) error {
   857  	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
   858  	r.params.Set("enable", "true")
   859  	r.params.Set("reason", reason)
   860  	_, resp, err := requireOK(a.c.doRequest(r))
   861  	if err != nil {
   862  		return err
   863  	}
   864  	resp.Body.Close()
   865  	return nil
   866  }
   867  
   868  // DisableNodeMaintenance toggles node maintenance mode off for the
   869  // agent we are connected to.
   870  func (a *Agent) DisableNodeMaintenance() error {
   871  	r := a.c.newRequest("PUT", "/v1/agent/maintenance")
   872  	r.params.Set("enable", "false")
   873  	_, resp, err := requireOK(a.c.doRequest(r))
   874  	if err != nil {
   875  		return err
   876  	}
   877  	resp.Body.Close()
   878  	return nil
   879  }
   880  
   881  // Monitor returns a channel which will receive streaming logs from the agent
   882  // Providing a non-nil stopCh can be used to close the connection and stop the
   883  // log stream. An empty string will be sent down the given channel when there's
   884  // nothing left to stream, after which the caller should close the stopCh.
   885  func (a *Agent) Monitor(loglevel string, stopCh <-chan struct{}, q *QueryOptions) (chan string, error) {
   886  	r := a.c.newRequest("GET", "/v1/agent/monitor")
   887  	r.setQueryOptions(q)
   888  	if loglevel != "" {
   889  		r.params.Add("loglevel", loglevel)
   890  	}
   891  	_, resp, err := requireOK(a.c.doRequest(r))
   892  	if err != nil {
   893  		return nil, err
   894  	}
   895  
   896  	logCh := make(chan string, 64)
   897  	go func() {
   898  		defer resp.Body.Close()
   899  
   900  		scanner := bufio.NewScanner(resp.Body)
   901  		for {
   902  			select {
   903  			case <-stopCh:
   904  				close(logCh)
   905  				return
   906  			default:
   907  			}
   908  			if scanner.Scan() {
   909  				// An empty string signals to the caller that
   910  				// the scan is done, so make sure we only emit
   911  				// that when the scanner says it's done, not if
   912  				// we happen to ingest an empty line.
   913  				if text := scanner.Text(); text != "" {
   914  					logCh <- text
   915  				} else {
   916  					logCh <- " "
   917  				}
   918  			} else {
   919  				logCh <- ""
   920  			}
   921  		}
   922  	}()
   923  
   924  	return logCh, nil
   925  }
   926  
   927  // UpdateACLToken updates the agent's "acl_token". See updateToken for more
   928  // details.
   929  //
   930  // DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateDefaultACLToken for v1.4.3 and above
   931  func (a *Agent) UpdateACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
   932  	return a.updateToken("acl_token", token, q)
   933  }
   934  
   935  // UpdateACLAgentToken updates the agent's "acl_agent_token". See updateToken
   936  // for more details.
   937  //
   938  // DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateAgentACLToken for v1.4.3 and above
   939  func (a *Agent) UpdateACLAgentToken(token string, q *WriteOptions) (*WriteMeta, error) {
   940  	return a.updateToken("acl_agent_token", token, q)
   941  }
   942  
   943  // UpdateACLAgentMasterToken updates the agent's "acl_agent_master_token". See
   944  // updateToken for more details.
   945  //
   946  // DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateAgentMasterACLToken for v1.4.3 and above
   947  func (a *Agent) UpdateACLAgentMasterToken(token string, q *WriteOptions) (*WriteMeta, error) {
   948  	return a.updateToken("acl_agent_master_token", token, q)
   949  }
   950  
   951  // UpdateACLReplicationToken updates the agent's "acl_replication_token". See
   952  // updateToken for more details.
   953  //
   954  // DEPRECATED (ACL-Legacy-Compat) - Prefer UpdateReplicationACLToken for v1.4.3 and above
   955  func (a *Agent) UpdateACLReplicationToken(token string, q *WriteOptions) (*WriteMeta, error) {
   956  	return a.updateToken("acl_replication_token", token, q)
   957  }
   958  
   959  // UpdateDefaultACLToken updates the agent's "default" token. See updateToken
   960  // for more details
   961  func (a *Agent) UpdateDefaultACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
   962  	return a.updateTokenFallback("default", "acl_token", token, q)
   963  }
   964  
   965  // UpdateAgentACLToken updates the agent's "agent" token. See updateToken
   966  // for more details
   967  func (a *Agent) UpdateAgentACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
   968  	return a.updateTokenFallback("agent", "acl_agent_token", token, q)
   969  }
   970  
   971  // UpdateAgentMasterACLToken updates the agent's "agent_master" token. See updateToken
   972  // for more details
   973  func (a *Agent) UpdateAgentMasterACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
   974  	return a.updateTokenFallback("agent_master", "acl_agent_master_token", token, q)
   975  }
   976  
   977  // UpdateReplicationACLToken updates the agent's "replication" token. See updateToken
   978  // for more details
   979  func (a *Agent) UpdateReplicationACLToken(token string, q *WriteOptions) (*WriteMeta, error) {
   980  	return a.updateTokenFallback("replication", "acl_replication_token", token, q)
   981  }
   982  
   983  // updateToken can be used to update one of an agent's ACL tokens after the agent has
   984  // started. The tokens are may not be persisted, so will need to be updated again if
   985  // the agent is restarted unless the agent is configured to persist them.
   986  func (a *Agent) updateToken(target, token string, q *WriteOptions) (*WriteMeta, error) {
   987  	meta, _, err := a.updateTokenOnce(target, token, q)
   988  	return meta, err
   989  }
   990  
   991  func (a *Agent) updateTokenFallback(target, fallback, token string, q *WriteOptions) (*WriteMeta, error) {
   992  	meta, status, err := a.updateTokenOnce(target, token, q)
   993  	if err != nil && status == 404 {
   994  		meta, _, err = a.updateTokenOnce(fallback, token, q)
   995  	}
   996  	return meta, err
   997  }
   998  
   999  func (a *Agent) updateTokenOnce(target, token string, q *WriteOptions) (*WriteMeta, int, error) {
  1000  	r := a.c.newRequest("PUT", fmt.Sprintf("/v1/agent/token/%s", target))
  1001  	r.setWriteOptions(q)
  1002  	r.obj = &AgentToken{Token: token}
  1003  	rtt, resp, err := requireOK(a.c.doRequest(r))
  1004  	if err != nil {
  1005  		return nil, resp.StatusCode, err
  1006  	}
  1007  	resp.Body.Close()
  1008  
  1009  	wm := &WriteMeta{RequestTime: rtt}
  1010  	return wm, resp.StatusCode, nil
  1011  }