api: enable query options on agent endpoints

This PR adds support for setting QueryOptions on a few agent API
endpoints. Nomad needs to be able to set the Namespace field on
these endpoints to:
 - query for services / checks in a namespace
 - deregister services / checks in a namespace
 - update TTL status on checks in a namespace
This commit is contained in:
Seth Hoenig 2021-01-29 10:42:19 -06:00
parent beba9b9228
commit dcbe30ef08
3 changed files with 106 additions and 4 deletions

4
.changelog/9903.txt Normal file
View File

@ -0,0 +1,4 @@
```release-note:improvement
api: Enable setting query options on agent endpoints.
```

View File

@ -509,7 +509,14 @@ func (a *Agent) Checks() (map[string]*AgentCheck, error) {
// ChecksWithFilter returns a subset of the locally registered checks that match
// the given filter expression
func (a *Agent) ChecksWithFilter(filter string) (map[string]*AgentCheck, error) {
return a.ChecksWithFilterOpts(filter, nil)
}
// ChecksWithFilterOpts returns a subset of the locally registered checks that match
// the given filter expression and QueryOptions.
func (a *Agent) ChecksWithFilterOpts(filter string, q *QueryOptions) (map[string]*AgentCheck, error) {
r := a.c.newRequest("GET", "/v1/agent/checks")
r.setQueryOptions(q)
r.filterQuery(filter)
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
@ -532,7 +539,14 @@ func (a *Agent) Services() (map[string]*AgentService, error) {
// ServicesWithFilter returns a subset of the locally registered services that match
// the given filter expression
func (a *Agent) ServicesWithFilter(filter string) (map[string]*AgentService, error) {
return a.ServicesWithFilterOpts(filter, nil)
}
// ServicesWithFilterOpts returns a subset of the locally registered services that match
// the given filter expression and QueryOptions.
func (a *Agent) ServicesWithFilterOpts(filter string, q *QueryOptions) (map[string]*AgentService, error) {
r := a.c.newRequest("GET", "/v1/agent/services")
r.setQueryOptions(q)
r.filterQuery(filter)
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
@ -727,6 +741,19 @@ func (a *Agent) ServiceDeregister(serviceID string) error {
return nil
}
// ServiceDeregisterOpts is used to deregister a service with
// the local agent with QueryOptions.
func (a *Agent) ServiceDeregisterOpts(serviceID string, q *QueryOptions) error {
r := a.c.newRequest("PUT", "/v1/agent/service/deregister/"+serviceID)
r.setQueryOptions(q)
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
return err
}
resp.Body.Close()
return nil
}
// PassTTL is used to set a TTL check to the passing state.
//
// DEPRECATION NOTICE: This interface is deprecated in favor of UpdateTTL().
@ -801,6 +828,10 @@ type checkUpdate struct {
// 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 {
return a.UpdateTTLOpts(checkID, output, status, nil)
}
func (a *Agent) UpdateTTLOpts(checkID, output, status string, q *QueryOptions) error {
switch status {
case "pass", HealthPassing:
status = HealthPassing
@ -814,6 +845,7 @@ func (a *Agent) UpdateTTL(checkID, output, status string) error {
endpoint := fmt.Sprintf("/v1/agent/check/update/%s", checkID)
r := a.c.newRequest("PUT", endpoint)
r.setQueryOptions(q)
r.obj = &checkUpdate{
Status: status,
Output: output,
@ -843,7 +875,14 @@ func (a *Agent) CheckRegister(check *AgentCheckRegistration) error {
// CheckDeregister is used to deregister a check with
// the local agent
func (a *Agent) CheckDeregister(checkID string) error {
return a.CheckDeregisterOpts(checkID, nil)
}
// CheckDeregisterOpts is used to deregister a check with
// the local agent using query options
func (a *Agent) CheckDeregisterOpts(checkID string, q *QueryOptions) error {
r := a.c.newRequest("PUT", "/v1/agent/check/deregister/"+checkID)
r.setQueryOptions(q)
_, resp, err := requireOK(a.c.doRequest(r))
if err != nil {
return err

View File

@ -333,7 +333,7 @@ func TestAPI_AgentServices(t *testing.T) {
}
}
func TestAPI_AgentServicesWithFilter(t *testing.T) {
func TestAPI_AgentServicesWithFilterOpts(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()
@ -362,7 +362,8 @@ func TestAPI_AgentServicesWithFilter(t *testing.T) {
}
require.NoError(t, agent.ServiceRegister(reg))
services, err := agent.ServicesWithFilter("foo in Tags")
opts := &QueryOptions{Namespace: defaultNamespace}
services, err := agent.ServicesWithFilterOpts("foo in Tags", opts)
require.NoError(t, err)
require.Len(t, services, 1)
_, ok := services["foo2"]
@ -852,6 +853,63 @@ func TestAPI_AgentSetTTLStatus(t *testing.T) {
}
}
func TestAPI_AgentUpdateTTLOpts(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()
agent := c.Agent()
s.WaitForSerfCheck(t)
reg := &AgentServiceRegistration{
Name: "foo",
Check: &AgentServiceCheck{
TTL: "15s",
},
}
if err := agent.ServiceRegister(reg); err != nil {
t.Fatalf("err: %v", err)
}
verify := func(status, output string) {
checks, err := agent.Checks()
if err != nil {
t.Fatalf("err: %v", err)
}
chk, ok := checks["service:foo"]
if !ok {
t.Fatalf("missing check: %v", checks)
}
if chk.Status != status {
t.Fatalf("Bad: %#v", chk)
}
if chk.Output != output {
t.Fatalf("Bad: %#v", chk)
}
}
opts := &QueryOptions{Namespace: defaultNamespace}
if err := agent.UpdateTTLOpts("service:foo", "foo", HealthWarning, opts); err != nil {
t.Fatalf("err: %v", err)
}
verify(HealthWarning, "foo")
if err := agent.UpdateTTLOpts("service:foo", "bar", HealthPassing, opts); err != nil {
t.Fatalf("err: %v", err)
}
verify(HealthPassing, "bar")
if err := agent.UpdateTTL("service:foo", "baz", HealthCritical); err != nil {
t.Fatalf("err: %v", err)
}
verify(HealthCritical, "baz")
if err := agent.ServiceDeregister("foo"); err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAPI_AgentChecks(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
@ -887,7 +945,7 @@ func TestAPI_AgentChecks(t *testing.T) {
}
}
func TestAPI_AgentChecksWithFilter(t *testing.T) {
func TestAPI_AgentChecksWithFilterOpts(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
defer s.Stop()
@ -905,7 +963,8 @@ func TestAPI_AgentChecksWithFilter(t *testing.T) {
reg.TTL = "15s"
require.NoError(t, agent.CheckRegister(reg))
checks, err := agent.ChecksWithFilter("Name == foo")
opts := &QueryOptions{Namespace: defaultNamespace}
checks, err := agent.ChecksWithFilterOpts("Name == foo", opts)
require.NoError(t, err)
require.Len(t, checks, 1)
_, ok := checks["foo"]