Adds complete ACL coverage for non-utility agent endpoints.

This is a checkpoint - we need to complete some unit tests for agent/acl.go.
pull/2594/head
James Phillips 2016-12-14 14:16:46 -08:00
parent 01b6766099
commit 03f40116f4
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
3 changed files with 807 additions and 32 deletions

View File

@ -10,7 +10,9 @@ import (
"github.com/armon/go-metrics"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/types"
"github.com/hashicorp/golang-lru"
"github.com/hashicorp/serf/serf"
)
// There's enough behavior difference with client-side ACLs that we've
@ -249,3 +251,202 @@ func (a *Agent) resolveToken(id string) (acl.ACL, error) {
// This will look in the cache and fetch from the servers if necessary.
return a.acls.lookupACL(a, id)
}
// vetServiceRegister makes sure the service registration action is allowed by
// the given token.
func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) error {
// Resolve the token and bail if ACLs aren't enabled.
acl, err := a.resolveToken(token)
if err != nil {
return err
}
if acl == nil {
return nil
}
// Vet the service itself.
if !acl.ServiceWrite(service.Service) {
return permissionDeniedErr
}
// Vet any service that might be getting overwritten.
services := a.state.Services()
if existing, ok := services[service.ID]; ok {
if !acl.ServiceWrite(existing.Service) {
return permissionDeniedErr
}
}
return nil
}
// vetServiceUpdate makes sure the service update action is allowed by the given
// token.
func (a *Agent) vetServiceUpdate(token string, serviceID string) error {
// Resolve the token and bail if ACLs aren't enabled.
acl, err := a.resolveToken(token)
if err != nil {
return err
}
if acl == nil {
return nil
}
// Vet any changes based on the existing services's info.
services := a.state.Services()
if existing, ok := services[serviceID]; ok {
if !acl.ServiceWrite(existing.Service) {
return permissionDeniedErr
}
} else {
return fmt.Errorf("Unknown service %q", serviceID)
}
return nil
}
// vetCheckRegister makes sure the check registration action is allowed by the
// given token.
func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error {
// Resolve the token and bail if ACLs aren't enabled.
acl, err := a.resolveToken(token)
if err != nil {
return err
}
if acl == nil {
return nil
}
// Vet the check itself.
if len(check.ServiceName) > 0 {
if !acl.ServiceWrite(check.ServiceName) {
return permissionDeniedErr
}
} else {
if !acl.NodeWrite(a.config.NodeName) {
return permissionDeniedErr
}
}
// Vet any check that might be getting overwritten.
checks := a.state.Checks()
if existing, ok := checks[check.CheckID]; ok {
if len(existing.ServiceName) > 0 {
if !acl.ServiceWrite(existing.ServiceName) {
return permissionDeniedErr
}
} else {
if !acl.NodeWrite(a.config.NodeName) {
return permissionDeniedErr
}
}
}
return nil
}
// vetCheckUpdate makes sure that a check update is allowed by the given token.
func (a *Agent) vetCheckUpdate(token string, checkID types.CheckID) error {
// Resolve the token and bail if ACLs aren't enabled.
acl, err := a.resolveToken(token)
if err != nil {
return err
}
if acl == nil {
return nil
}
// Vet any changes based on the existing check's info.
checks := a.state.Checks()
if existing, ok := checks[checkID]; ok {
if len(existing.ServiceName) > 0 {
if !acl.ServiceWrite(existing.ServiceName) {
return permissionDeniedErr
}
} else {
if !acl.NodeWrite(a.config.NodeName) {
return permissionDeniedErr
}
}
} else {
return fmt.Errorf("Unknown check %q", checkID)
}
return nil
}
// filterMembers redacts members that the token doesn't have access to.
func (a *Agent) filterMembers(token string, members *[]serf.Member) error {
// Resolve the token and bail if ACLs aren't enabled.
acl, err := a.resolveToken(token)
if err != nil {
return err
}
if acl == nil {
return nil
}
// Filter out members based on the node policy.
m := *members
for i := 0; i < len(m); i++ {
node := m[i].Name
if acl.NodeRead(node) {
continue
}
a.logger.Printf("[DEBUG] agent: dropping node %q from result due to ACLs", node)
m = append(m[:i], m[i+1:]...)
i--
}
*members = m
return nil
}
// filterServices redacts services that the token doesn't have access to.
func (a *Agent) filterServices(token string, services *map[string]*structs.NodeService) error {
// Resolve the token and bail if ACLs aren't enabled.
acl, err := a.resolveToken(token)
if err != nil {
return err
}
if acl == nil {
return nil
}
// Filter out services based on the service policy.
for id, service := range *services {
if acl.ServiceRead(service.Service) {
continue
}
a.logger.Printf("[DEBUG] agent: dropping service %q from result due to ACLs", id)
delete(*services, id)
}
return nil
}
// filterChecks redacts checks that the token doesn't have access to.
func (a *Agent) filterChecks(token string, checks *map[types.CheckID]*structs.HealthCheck) error {
// Resolve the token and bail if ACLs aren't enabled.
acl, err := a.resolveToken(token)
if err != nil {
return err
}
if acl == nil {
return nil
}
// Filter out checks based on the node or service policy.
for id, check := range *checks {
if len(check.ServiceName) > 0 {
if acl.ServiceRead(check.ServiceName) {
continue
}
} else {
if acl.NodeRead(a.config.NodeName) {
continue
}
}
a.logger.Printf("[DEBUG] agent: dropping check %q from result due to ACLs", id)
delete(*checks, id)
}
return nil
}

View File

@ -85,26 +85,50 @@ func (s *HTTPServer) AgentReload(resp http.ResponseWriter, req *http.Request) (i
}
func (s *HTTPServer) AgentServices(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Fetch the ACL token, if any.
var token string
s.parseToken(req, &token)
services := s.agent.state.Services()
if err := s.agent.filterServices(token, &services); err != nil {
return nil, err
}
return services, nil
}
func (s *HTTPServer) AgentChecks(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Fetch the ACL token, if any.
var token string
s.parseToken(req, &token)
checks := s.agent.state.Checks()
if err := s.agent.filterChecks(token, &checks); err != nil {
return nil, err
}
return checks, nil
}
func (s *HTTPServer) AgentMembers(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Fetch the ACL token, if any.
var token string
s.parseToken(req, &token)
// Check if the WAN is being queried
wan := false
if other := req.URL.Query().Get("wan"); other != "" {
wan = true
}
var members []serf.Member
if wan {
return s.agent.WANMembers(), nil
members = s.agent.WANMembers()
} else {
return s.agent.LANMembers(), nil
members = s.agent.LANMembers()
}
if err := s.agent.filterMembers(token, &members); err != nil {
return nil, err
}
return members, nil
}
func (s *HTTPServer) AgentJoin(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
@ -188,7 +212,7 @@ const invalidCheckMessage = "Must provide TTL or Script/DockerContainerID/HTTP/T
func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
var args CheckDefinition
// Fixup the type decode of TTL or Interval
// Fixup the type decode of TTL or Interval.
decodeCB := func(raw interface{}) error {
return FixupCheckType(raw)
}
@ -198,7 +222,7 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
return nil, nil
}
// Verify the check has a name
// Verify the check has a name.
if args.Name == "" {
resp.WriteHeader(400)
resp.Write([]byte("Missing check name"))
@ -211,10 +235,10 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
return nil, nil
}
// Construct the health check
// Construct the health check.
health := args.HealthCheck(s.agent.config.NodeName)
// Verify the check type
// Verify the check type.
chkType := &args.CheckType
if !chkType.Valid() {
resp.WriteHeader(400)
@ -222,11 +246,14 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
return nil, nil
}
// Get the provided token, if any
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetCheckRegister(token, health); err != nil {
return nil, err
}
// Add the check
// Add the check.
if err := s.agent.AddCheck(health, chkType, true, token); err != nil {
return nil, err
}
@ -236,6 +263,14 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
func (s *HTTPServer) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/deregister/"))
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetCheckUpdate(token, checkID); err != nil {
return nil, err
}
if err := s.agent.RemoveCheck(checkID, true); err != nil {
return nil, err
}
@ -246,6 +281,14 @@ func (s *HTTPServer) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Re
func (s *HTTPServer) AgentCheckPass(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/pass/"))
note := req.URL.Query().Get("note")
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetCheckUpdate(token, checkID); err != nil {
return nil, err
}
if err := s.agent.updateTTLCheck(checkID, structs.HealthPassing, note); err != nil {
return nil, err
}
@ -256,6 +299,14 @@ func (s *HTTPServer) AgentCheckPass(resp http.ResponseWriter, req *http.Request)
func (s *HTTPServer) AgentCheckWarn(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/warn/"))
note := req.URL.Query().Get("note")
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetCheckUpdate(token, checkID); err != nil {
return nil, err
}
if err := s.agent.updateTTLCheck(checkID, structs.HealthWarning, note); err != nil {
return nil, err
}
@ -266,6 +317,14 @@ func (s *HTTPServer) AgentCheckWarn(resp http.ResponseWriter, req *http.Request)
func (s *HTTPServer) AgentCheckFail(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/fail/"))
note := req.URL.Query().Get("note")
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetCheckUpdate(token, checkID); err != nil {
return nil, err
}
if err := s.agent.updateTTLCheck(checkID, structs.HealthCritical, note); err != nil {
return nil, err
}
@ -318,6 +377,14 @@ func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Reques
}
checkID := types.CheckID(strings.TrimPrefix(req.URL.Path, "/v1/agent/check/update/"))
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetCheckUpdate(token, checkID); err != nil {
return nil, err
}
if err := s.agent.updateTTLCheck(checkID, update.Status, update.Output); err != nil {
return nil, err
}
@ -327,7 +394,7 @@ func (s *HTTPServer) AgentCheckUpdate(resp http.ResponseWriter, req *http.Reques
func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
var args ServiceDefinition
// Fixup the type decode of TTL or Interval if a check if provided
// Fixup the type decode of TTL or Interval if a check if provided.
decodeCB := func(raw interface{}) error {
rawMap, ok := raw.(map[string]interface{})
if !ok {
@ -360,17 +427,17 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
return nil, nil
}
// Verify the service has a name
// Verify the service has a name.
if args.Name == "" {
resp.WriteHeader(400)
resp.Write([]byte("Missing service name"))
return nil, nil
}
// Get the node service
// Get the node service.
ns := args.NodeService()
// Verify the check type
// Verify the check type.
chkTypes := args.CheckTypes()
for _, check := range chkTypes {
if check.Status != "" && !structs.ValidStatus(check.Status) {
@ -385,11 +452,14 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
}
}
// Get the provided token, if any
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetServiceRegister(token, ns); err != nil {
return nil, err
}
// Add the check
// Add the service.
if err := s.agent.AddService(ns, chkTypes, true, token); err != nil {
return nil, err
}
@ -399,6 +469,14 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
func (s *HTTPServer) AgentDeregisterService(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
serviceID := strings.TrimPrefix(req.URL.Path, "/v1/agent/service/deregister/")
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetServiceUpdate(token, serviceID); err != nil {
return nil, err
}
if err := s.agent.RemoveService(serviceID, true); err != nil {
return nil, err
}
@ -437,9 +515,12 @@ func (s *HTTPServer) AgentServiceMaintenance(resp http.ResponseWriter, req *http
return nil, nil
}
// Get the provided token, if any
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
if err := s.agent.vetServiceUpdate(token, serviceID); err != nil {
return nil, err
}
if enable {
reason := params.Get("reason")
@ -482,9 +563,16 @@ func (s *HTTPServer) AgentNodeMaintenance(resp http.ResponseWriter, req *http.Re
return nil, nil
}
// Get the provided token, if any
// Get the provided token, if any, and vet against any ACL policies.
var token string
s.parseToken(req, &token)
acl, err := s.agent.resolveToken(token)
if err != nil {
return nil, err
}
if acl != nil && !acl.NodeWrite(s.agent.config.NodeName) {
return nil, permissionDeniedErr
}
if enable {
s.agent.EnableNodeMaintenance(params.Get("reason"), token)

View File

@ -60,7 +60,12 @@ func TestAgent_Services(t *testing.T) {
}
srv.agent.state.AddService(srv1, "")
obj, err := srv.AgentServices(nil, nil)
req, err := http.NewRequest("GET", "/v1/agent/services", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentServices(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
@ -73,6 +78,47 @@ func TestAgent_Services(t *testing.T) {
}
}
func TestAgent_Services_ACLFilter(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
// Try no token.
{
req, err := http.NewRequest("GET", "/v1/agent/services", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentServices(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
val := obj.(map[string]*structs.NodeService)
if len(val) != 0 {
t.Fatalf("bad: %v", obj)
}
}
// Try the root token (we will get the implicit "consul" service).
{
req, err := http.NewRequest("GET", "/v1/agent/services?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentServices(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
val := obj.(map[string]*structs.NodeService)
if len(val) != 1 {
t.Fatalf("bad: %v", obj)
}
}
}
func TestAgent_Checks(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -87,7 +133,12 @@ func TestAgent_Checks(t *testing.T) {
}
srv.agent.state.AddCheck(chk1, "")
obj, err := srv.AgentChecks(nil, nil)
req, err := http.NewRequest("GET", "/v1/agent/checks", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentChecks(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
@ -100,6 +151,55 @@ func TestAgent_Checks(t *testing.T) {
}
}
func TestAgent_Checks_ACLFilter(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
chk1 := &structs.HealthCheck{
Node: srv.agent.config.NodeName,
CheckID: "mysql",
Name: "mysql",
Status: structs.HealthPassing,
}
srv.agent.state.AddCheck(chk1, "")
// Try no token.
{
req, err := http.NewRequest("GET", "/v1/agent/checks", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentChecks(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
val := obj.(map[types.CheckID]*structs.HealthCheck)
if len(val) != 0 {
t.Fatalf("bad checks: %v", obj)
}
}
// Try the root token.
{
req, err := http.NewRequest("GET", "/v1/agent/checks?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentChecks(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
val := obj.(map[types.CheckID]*structs.HealthCheck)
if len(val) != 1 {
t.Fatalf("bad checks: %v", obj)
}
}
}
func TestAgent_Self(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -364,6 +464,47 @@ func TestAgent_Members_WAN(t *testing.T) {
}
}
func TestAgent_Members_ACLFilter(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
// Try no token.
{
req, err := http.NewRequest("GET", "/v1/agent/members", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentMembers(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
val := obj.([]serf.Member)
if len(val) != 0 {
t.Fatalf("bad members: %v", obj)
}
}
// Try the root token.
{
req, err := http.NewRequest("GET", "/v1/agent/members?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
obj, err := srv.AgentMembers(nil, req)
if err != nil {
t.Fatalf("Err: %v", err)
}
val := obj.([]serf.Member)
if len(val) != 1 {
t.Fatalf("bad members: %v", obj)
}
}
}
func TestAgent_Join(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -480,7 +621,7 @@ func TestAgent_Join_ACLDeny(t *testing.T) {
}
_, err = srv.AgentJoin(nil, req)
if err != nil {
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
}
@ -580,7 +721,7 @@ func TestAgent_Leave_ACLDeny(t *testing.T) {
}
_, err = srv.AgentLeave(nil, req)
if err != nil {
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
}()
@ -669,7 +810,7 @@ func TestAgent_ForceLeave_ACLDeny(t *testing.T) {
}
_, err = srv.AgentForceLeave(nil, req)
if err != nil {
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
}
@ -724,7 +865,7 @@ func TestAgent_RegisterCheck(t *testing.T) {
}
}
func TestAgent_RegisterCheckPassing(t *testing.T) {
func TestAgent_RegisterCheck_Passing(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -768,7 +909,7 @@ func TestAgent_RegisterCheckPassing(t *testing.T) {
}
}
func TestAgent_RegisterCheckBadStatus(t *testing.T) {
func TestAgent_RegisterCheck_BadStatus(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -797,6 +938,41 @@ func TestAgent_RegisterCheckBadStatus(t *testing.T) {
}
}
func TestAgent_RegisterCheck_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
// Try with no token.
req, err := http.NewRequest("GET", "/v1/agent/check/register", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
args := &CheckDefinition{
Name: "test",
CheckType: CheckType{
TTL: 15 * time.Second,
},
}
req.Body = encodeReq(args)
_, err = srv.AgentRegisterCheck(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try the root token.
req, err = http.NewRequest("GET", "/v1/agent/check/register?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
req.Body = encodeReq(args)
_, err = srv.AgentRegisterCheck(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_DeregisterCheck(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -828,6 +1004,38 @@ func TestAgent_DeregisterCheck(t *testing.T) {
}
}
func TestAgent_DeregisterCheckACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
if err := srv.agent.AddCheck(chk, nil, false, ""); err != nil {
t.Fatalf("err: %v", err)
}
// Try with no token.
req, err := http.NewRequest("GET", "/v1/agent/check/deregister/test", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentDeregisterCheck(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest("GET", "/v1/agent/check/deregister/test?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentDeregisterCheck(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_PassCheck(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -860,6 +1068,39 @@ func TestAgent_PassCheck(t *testing.T) {
}
}
func TestAgent_PassCheck_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &CheckType{TTL: 15 * time.Second}
if err := srv.agent.AddCheck(chk, chkType, false, ""); err != nil {
t.Fatalf("err: %v", err)
}
// Try with no token.
req, err := http.NewRequest("GET", "/v1/agent/check/pass/test", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentCheckPass(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest("GET", "/v1/agent/check/pass/test?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentCheckPass(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_WarnCheck(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -892,6 +1133,39 @@ func TestAgent_WarnCheck(t *testing.T) {
}
}
func TestAgent_WarnCheck_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &CheckType{TTL: 15 * time.Second}
if err := srv.agent.AddCheck(chk, chkType, false, ""); err != nil {
t.Fatalf("err: %v", err)
}
// Try with no token.
req, err := http.NewRequest("GET", "/v1/agent/check/warn/test", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentCheckWarn(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest("GET", "/v1/agent/check/warn/test?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentCheckWarn(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_FailCheck(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -924,6 +1198,39 @@ func TestAgent_FailCheck(t *testing.T) {
}
}
func TestAgent_FailCheck_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &CheckType{TTL: 15 * time.Second}
if err := srv.agent.AddCheck(chk, chkType, false, ""); err != nil {
t.Fatalf("err: %v", err)
}
// Try with no token.
req, err := http.NewRequest("GET", "/v1/agent/check/fail/test", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentCheckFail(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest("GET", "/v1/agent/check/fail/test?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentCheckFail(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_UpdateCheck(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -1052,13 +1359,47 @@ func TestAgent_UpdateCheck(t *testing.T) {
}
}
func TestAgent_UpdateCheck_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
chk := &structs.HealthCheck{Name: "test", CheckID: "test"}
chkType := &CheckType{TTL: 15 * time.Second}
if err := srv.agent.AddCheck(chk, chkType, false, ""); err != nil {
t.Fatalf("err: %v", err)
}
// Try with no token.
req, err := http.NewRequest("PUT", "/v1/agent/check/update/test", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
req.Body = encodeReq(checkUpdate{structs.HealthPassing, "hello-passing"})
_, err = srv.AgentCheckUpdate(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest("PUT", "/v1/agent/check/update/test?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
req.Body = encodeReq(checkUpdate{structs.HealthPassing, "hello-passing"})
_, err = srv.AgentCheckUpdate(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_RegisterService(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
// Register node
req, err := http.NewRequest("GET", "/v1/agent/service/register?token=abc123", nil)
if err != nil {
t.Fatalf("err: %v", err)
@ -1110,6 +1451,52 @@ func TestAgent_RegisterService(t *testing.T) {
}
}
func TestAgent_RegisterService_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
args := &ServiceDefinition{
Name: "test",
Tags: []string{"master"},
Port: 8000,
Check: CheckType{
TTL: 15 * time.Second,
},
Checks: CheckTypes{
&CheckType{
TTL: 20 * time.Second,
},
&CheckType{
TTL: 30 * time.Second,
},
},
}
// Try with no token.
req, err := http.NewRequest("GET", "/v1/agent/service/register", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
req.Body = encodeReq(args)
_, err = srv.AgentRegisterService(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest("GET", "/v1/agent/service/register?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
req.Body = encodeReq(args)
_, err = srv.AgentRegisterService(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_DeregisterService(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
@ -1124,7 +1511,6 @@ func TestAgent_DeregisterService(t *testing.T) {
t.Fatalf("err: %v", err)
}
// Register node
req, err := http.NewRequest("GET", "/v1/agent/service/deregister/test", nil)
if err != nil {
t.Fatalf("err: %v", err)
@ -1148,7 +1534,42 @@ func TestAgent_DeregisterService(t *testing.T) {
}
}
func TestAgent_ServiceMaintenanceEndpoint_BadRequest(t *testing.T) {
func TestAgent_DeregisterService_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
service := &structs.NodeService{
ID: "test",
Service: "test",
}
if err := srv.agent.AddService(service, nil, false, ""); err != nil {
t.Fatalf("err: %v", err)
}
// Try without a token.
req, err := http.NewRequest("GET", "/v1/agent/service/deregister/test", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentDeregisterService(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root.
req, err = http.NewRequest("GET", "/v1/agent/service/deregister/test?token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentDeregisterService(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_ServiceMaintenance_BadRequest(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -1195,7 +1616,7 @@ func TestAgent_ServiceMaintenanceEndpoint_BadRequest(t *testing.T) {
}
}
func TestAgent_EnableServiceMaintenance(t *testing.T) {
func TestAgent_ServiceMaintenance_Enable(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -1238,7 +1659,7 @@ func TestAgent_EnableServiceMaintenance(t *testing.T) {
}
}
func TestAgent_DisableServiceMaintenance(t *testing.T) {
func TestAgent_ServiceMaintenance_Disable(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -1275,7 +1696,43 @@ func TestAgent_DisableServiceMaintenance(t *testing.T) {
}
}
func TestAgent_NodeMaintenanceEndpoint_BadRequest(t *testing.T) {
func TestAgent_ServiceMaintenance_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
// Register the service.
service := &structs.NodeService{
ID: "test",
Service: "test",
}
if err := srv.agent.AddService(service, nil, false, ""); err != nil {
t.Fatalf("err: %v", err)
}
// Try with no token.
req, err := http.NewRequest("PUT", "/v1/agent/service/maintenance/test?enable=true&reason=broken", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentServiceMaintenance(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest("PUT", "/v1/agent/service/maintenance/test?enable=true&reason=broken&token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentServiceMaintenance(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_NodeMaintenance_BadRequest(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -1302,7 +1759,7 @@ func TestAgent_NodeMaintenanceEndpoint_BadRequest(t *testing.T) {
}
}
func TestAgent_EnableNodeMaintenance(t *testing.T) {
func TestAgent_NodeMaintenance_Enable(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -1336,7 +1793,7 @@ func TestAgent_EnableNodeMaintenance(t *testing.T) {
}
}
func TestAgent_DisableNodeMaintenance(t *testing.T) {
func TestAgent_NodeMaintenance_Disable(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
@ -1361,7 +1818,36 @@ func TestAgent_DisableNodeMaintenance(t *testing.T) {
}
}
func TestAgent_RegisterServiceCheck(t *testing.T) {
func TestAgent_NodeMaintenance_ACLDeny(t *testing.T) {
dir, srv := makeHTTPServerWithACLs(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()
// Try with no token.
req, err := http.NewRequest(
"PUT", "/v1/agent/self/maintenance?enable=true&reason=broken", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentNodeMaintenance(nil, req)
if err == nil || !strings.Contains(err.Error(), permissionDenied) {
t.Fatalf("err: %v", err)
}
// Try with the root token.
req, err = http.NewRequest(
"PUT", "/v1/agent/self/maintenance?enable=true&reason=broken&token=root", nil)
if err != nil {
t.Fatalf("err: %v", err)
}
_, err = srv.AgentNodeMaintenance(nil, req)
if err != nil {
t.Fatalf("err: %v", err)
}
}
func TestAgent_RegisterCheck_Service(t *testing.T) {
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()