agent: Adding ACL Policy endpoints
This commit is contained in:
parent
d52e099fc2
commit
18e6053b58
111
command/agent/acl_endpoint.go
Normal file
111
command/agent/acl_endpoint.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
func (s *HTTPServer) ACLPoliciesRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
if req.Method != "GET" {
|
||||
return nil, CodedError(405, ErrInvalidMethod)
|
||||
}
|
||||
|
||||
args := structs.ACLPolicyListRequest{}
|
||||
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var out structs.ACLPolicyListResponse
|
||||
if err := s.agent.RPC("ACL.ListPolicies", &args, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setMeta(resp, &out.QueryMeta)
|
||||
if out.Policies == nil {
|
||||
out.Policies = make([]*structs.ACLPolicyListStub, 0)
|
||||
}
|
||||
return out.Policies, nil
|
||||
}
|
||||
|
||||
func (s *HTTPServer) ACLPolicySpecificRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
name := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/")
|
||||
if len(name) == 0 {
|
||||
return nil, CodedError(400, "Missing Policy Name")
|
||||
}
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
return s.aclPolicyQuery(resp, req, name)
|
||||
case "PUT", "POST":
|
||||
return s.aclPolicyUpdate(resp, req, name)
|
||||
case "DELETE":
|
||||
return s.aclPolicyDelete(resp, req, name)
|
||||
default:
|
||||
return nil, CodedError(405, ErrInvalidMethod)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HTTPServer) aclPolicyQuery(resp http.ResponseWriter, req *http.Request,
|
||||
policyName string) (interface{}, error) {
|
||||
args := structs.ACLPolicySpecificRequest{
|
||||
Name: policyName,
|
||||
}
|
||||
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var out structs.SingleACLPolicyResponse
|
||||
if err := s.agent.RPC("ACL.GetPolicy", &args, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
setMeta(resp, &out.QueryMeta)
|
||||
if out.Policy == nil {
|
||||
return nil, CodedError(404, "ACL policy not found")
|
||||
}
|
||||
return out.Policy, nil
|
||||
}
|
||||
|
||||
func (s *HTTPServer) aclPolicyUpdate(resp http.ResponseWriter, req *http.Request,
|
||||
policyName string) (interface{}, error) {
|
||||
// Parse the policy
|
||||
var policy structs.ACLPolicy
|
||||
if err := decodeBody(req, &policy); err != nil {
|
||||
return nil, CodedError(500, err.Error())
|
||||
}
|
||||
|
||||
// Ensure the policy name matches
|
||||
if policy.Name != policyName {
|
||||
return nil, CodedError(400, "ACL policy name does not match request path")
|
||||
}
|
||||
|
||||
// Format the request
|
||||
args := structs.ACLPolicyUpsertRequest{
|
||||
Policies: []*structs.ACLPolicy{&policy},
|
||||
}
|
||||
s.parseRegion(req, &args.Region)
|
||||
|
||||
var out structs.GenericResponse
|
||||
if err := s.agent.RPC("ACL.UpsertPolicies", &args, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setIndex(resp, out.Index)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *HTTPServer) aclPolicyDelete(resp http.ResponseWriter, req *http.Request,
|
||||
policyName string) (interface{}, error) {
|
||||
|
||||
args := structs.ACLPolicyDeleteRequest{
|
||||
Names: []string{policyName},
|
||||
}
|
||||
s.parseRegion(req, &args.Region)
|
||||
|
||||
var out structs.GenericResponse
|
||||
if err := s.agent.RPC("ACL.DeletePolicies", &args, &out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setIndex(resp, out.Index)
|
||||
return nil, nil
|
||||
}
|
175
command/agent/acl_endpoint_test.go
Normal file
175
command/agent/acl_endpoint_test.go
Normal file
|
@ -0,0 +1,175 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/nomad/nomad/mock"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHTTP_ACLPolicyList(t *testing.T) {
|
||||
t.Parallel()
|
||||
httpTest(t, nil, func(s *TestAgent) {
|
||||
p1 := mock.ACLPolicy()
|
||||
p2 := mock.ACLPolicy()
|
||||
p3 := mock.ACLPolicy()
|
||||
args := structs.ACLPolicyUpsertRequest{
|
||||
Policies: []*structs.ACLPolicy{p1, p2, p3},
|
||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
||||
}
|
||||
var resp structs.GenericResponse
|
||||
if err := s.Agent.RPC("ACL.UpsertPolicies", &args, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("GET", "/v1/acl/policies", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
respW := httptest.NewRecorder()
|
||||
|
||||
// Make the request
|
||||
obj, err := s.Server.ACLPoliciesRequest(respW, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check for the index
|
||||
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
|
||||
t.Fatalf("missing index")
|
||||
}
|
||||
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
|
||||
t.Fatalf("missing known leader")
|
||||
}
|
||||
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
|
||||
t.Fatalf("missing last contact")
|
||||
}
|
||||
|
||||
// Check the output
|
||||
n := obj.([]*structs.ACLPolicyListStub)
|
||||
if len(n) != 3 {
|
||||
t.Fatalf("bad: %#v", n)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_ACLPolicyQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
httpTest(t, nil, func(s *TestAgent) {
|
||||
p1 := mock.ACLPolicy()
|
||||
args := structs.ACLPolicyUpsertRequest{
|
||||
Policies: []*structs.ACLPolicy{p1},
|
||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
||||
}
|
||||
var resp structs.GenericResponse
|
||||
if err := s.Agent.RPC("ACL.UpsertPolicies", &args, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("GET", "/v1/acl/policy/"+p1.Name, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
respW := httptest.NewRecorder()
|
||||
|
||||
// Make the request
|
||||
obj, err := s.Server.ACLPolicySpecificRequest(respW, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check for the index
|
||||
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
|
||||
t.Fatalf("missing index")
|
||||
}
|
||||
if respW.HeaderMap.Get("X-Nomad-KnownLeader") != "true" {
|
||||
t.Fatalf("missing known leader")
|
||||
}
|
||||
if respW.HeaderMap.Get("X-Nomad-LastContact") == "" {
|
||||
t.Fatalf("missing last contact")
|
||||
}
|
||||
|
||||
// Check the output
|
||||
n := obj.(*structs.ACLPolicy)
|
||||
if n.Name != p1.Name {
|
||||
t.Fatalf("bad: %#v", n)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_ACLPolicyCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
httpTest(t, nil, func(s *TestAgent) {
|
||||
// Make the HTTP request
|
||||
p1 := mock.ACLPolicy()
|
||||
buf := encodeReq(p1)
|
||||
req, err := http.NewRequest("PUT", "/v1/acl/policy/"+p1.Name, buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
respW := httptest.NewRecorder()
|
||||
|
||||
// Make the request
|
||||
obj, err := s.Server.ACLPolicySpecificRequest(respW, req)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, obj)
|
||||
|
||||
// Check for the index
|
||||
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
|
||||
t.Fatalf("missing index")
|
||||
}
|
||||
|
||||
// Check policy was created
|
||||
state := s.Agent.server.State()
|
||||
out, err := state.ACLPolicyByName(nil, p1.Name)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, out)
|
||||
|
||||
p1.CreateIndex, p1.ModifyIndex = out.CreateIndex, out.ModifyIndex
|
||||
assert.Equal(t, p1.Name, out.Name)
|
||||
assert.Equal(t, p1, out)
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTP_ACLPolicyDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
httpTest(t, nil, func(s *TestAgent) {
|
||||
p1 := mock.ACLPolicy()
|
||||
args := structs.ACLPolicyUpsertRequest{
|
||||
Policies: []*structs.ACLPolicy{p1},
|
||||
WriteRequest: structs.WriteRequest{Region: "global"},
|
||||
}
|
||||
var resp structs.GenericResponse
|
||||
if err := s.Agent.RPC("ACL.UpsertPolicies", &args, &resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Make the HTTP request
|
||||
req, err := http.NewRequest("DELETE", "/v1/acl/policy/"+p1.Name, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
respW := httptest.NewRecorder()
|
||||
|
||||
// Make the request
|
||||
obj, err := s.Server.ACLPolicySpecificRequest(respW, req)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, obj)
|
||||
|
||||
// Check for the index
|
||||
if respW.HeaderMap.Get("X-Nomad-Index") == "" {
|
||||
t.Fatalf("missing index")
|
||||
}
|
||||
|
||||
// Check policy was created
|
||||
state := s.Agent.server.State()
|
||||
out, err := state.ACLPolicyByName(nil, p1.Name)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, out)
|
||||
})
|
||||
}
|
|
@ -148,6 +148,9 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
|
|||
s.mux.HandleFunc("/v1/deployments", s.wrap(s.DeploymentsRequest))
|
||||
s.mux.HandleFunc("/v1/deployment/", s.wrap(s.DeploymentSpecificRequest))
|
||||
|
||||
s.mux.HandleFunc("/v1/acl/policies", s.wrap(s.ACLPoliciesRequest))
|
||||
s.mux.HandleFunc("/v1/acl/policy/", s.wrap(s.ACLPolicySpecificRequest))
|
||||
|
||||
s.mux.HandleFunc("/v1/client/fs/", s.wrap(s.FsRequest))
|
||||
s.mux.HandleFunc("/v1/client/stats", s.wrap(s.ClientStatsRequest))
|
||||
s.mux.HandleFunc("/v1/client/allocation/", s.wrap(s.ClientAllocRequest))
|
||||
|
|
|
@ -340,7 +340,8 @@ func PlanResult() *structs.PlanResult {
|
|||
|
||||
func ACLPolicy() *structs.ACLPolicy {
|
||||
return &structs.ACLPolicy{
|
||||
Name: fmt.Sprintf("policy-%s", structs.GenerateUUID()),
|
||||
Name: fmt.Sprintf("policy-%s", structs.GenerateUUID()),
|
||||
Description: "Super cool policy!",
|
||||
Rules: `
|
||||
namespace "default" {
|
||||
policy = "write"
|
||||
|
|
Loading…
Reference in a new issue