github.com/kjdelisle/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 }