Add PolicyReadByName for API (#6615)

This commit is contained in:
Alejandro Baez 2020-03-25 10:34:24 -04:00 committed by GitHub
parent 0c5c97205f
commit 7d68d7eaa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 144 additions and 4 deletions

View File

@ -217,7 +217,7 @@ func (s *HTTPServer) ACLPolicyCRUD(resp http.ResponseWriter, req *http.Request)
switch req.Method {
case "GET":
fn = s.ACLPolicyRead
fn = s.ACLPolicyReadByID
case "PUT":
fn = s.ACLPolicyWrite
@ -237,10 +237,11 @@ func (s *HTTPServer) ACLPolicyCRUD(resp http.ResponseWriter, req *http.Request)
return fn(resp, req, policyID)
}
func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) {
func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request, policyID, policyName string) (interface{}, error) {
args := structs.ACLPolicyGetRequest{
Datacenter: s.agent.config.Datacenter,
PolicyID: policyID,
PolicyName: policyName,
}
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
return nil, nil
@ -268,6 +269,23 @@ func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request,
return out.Policy, nil
}
func (s *HTTPServer) ACLPolicyReadByName(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
if s.checkACLDisabled(resp, req) {
return nil, nil
}
policyName := strings.TrimPrefix(req.URL.Path, "/v1/acl/policy/name/")
if policyName == "" {
return nil, BadRequestError{Reason: "Missing policy Name"}
}
return s.ACLPolicyRead(resp, req, "", policyName)
}
func (s *HTTPServer) ACLPolicyReadByID(resp http.ResponseWriter, req *http.Request, policyID string) (interface{}, error) {
return s.ACLPolicyRead(resp, req, policyID, "")
}
func (s *HTTPServer) ACLPolicyCreate(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
if s.checkACLDisabled(resp, req) {
return nil, nil

View File

@ -374,6 +374,17 @@ func TestACL_HTTP(t *testing.T) {
require.True(t, ok)
require.Equal(t, policyMap[idMap["policy-read-all-nodes"]], policy)
})
t.Run("Read Name", func(t *testing.T) {
policyName := "read-all-nodes"
req, _ := http.NewRequest("GET", "/v1/acl/policy/name/"+policyName+"?token=root", nil)
resp := httptest.NewRecorder()
raw, err := a.srv.ACLPolicyReadByName(resp, req)
require.NoError(t, err)
policy, ok := raw.(*structs.ACLPolicy)
require.True(t, ok)
require.Equal(t, policyMap[idMap["policy-"+policyName]], policy)
})
})
t.Run("Role", func(t *testing.T) {

View File

@ -951,7 +951,16 @@ func (a *ACL) PolicyRead(args *structs.ACLPolicyGetRequest, reply *structs.ACLPo
return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta,
func(ws memdb.WatchSet, state *state.Store) error {
index, policy, err := state.ACLPolicyGetByID(ws, args.PolicyID, &args.EnterpriseMeta)
var (
index uint64
policy *structs.ACLPolicy
err error
)
if args.PolicyID != "" {
index, policy, err = state.ACLPolicyGetByID(ws, args.PolicyID, &args.EnterpriseMeta)
} else {
index, policy, err = state.ACLPolicyGetByName(ws, args.PolicyName, &args.EnterpriseMeta)
}
if err != nil {
return err

View File

@ -2240,6 +2240,45 @@ func TestACLEndpoint_PolicyRead(t *testing.T) {
}
}
func TestACLEndpoint_PolicyReadByName(t *testing.T) {
t.Parallel()
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.ACLDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
codec := rpcClient(t, s1)
defer codec.Close()
testrpc.WaitForLeader(t, s1.RPC, "dc1")
policy, err := upsertTestPolicy(codec, "root", "dc1")
if err != nil {
t.Fatalf("err: %v", err)
}
acl := ACL{srv: s1}
req := structs.ACLPolicyGetRequest{
Datacenter: "dc1",
PolicyName: policy.Name,
QueryOptions: structs.QueryOptions{Token: "root"},
}
resp := structs.ACLPolicyResponse{}
err = acl.PolicyRead(&req, &resp)
if err != nil {
t.Fatalf("err: %v", err)
}
if !reflect.DeepEqual(resp.Policy, policy) {
t.Fatalf("tokens are not equal: %v != %v", resp.Policy, policy)
}
}
func TestACLEndpoint_PolicyBatchRead(t *testing.T) {
t.Parallel()

View File

@ -14,6 +14,7 @@ func init() {
registerEndpoint("/v1/acl/policies", []string{"GET"}, (*HTTPServer).ACLPolicyList)
registerEndpoint("/v1/acl/policy", []string{"PUT"}, (*HTTPServer).ACLPolicyCreate)
registerEndpoint("/v1/acl/policy/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLPolicyCRUD)
registerEndpoint("/v1/acl/policy/name/", []string{"GET"}, (*HTTPServer).ACLPolicyReadByName)
registerEndpoint("/v1/acl/roles", []string{"GET"}, (*HTTPServer).ACLRoleList)
registerEndpoint("/v1/acl/role", []string{"PUT"}, (*HTTPServer).ACLRoleCreate)
registerEndpoint("/v1/acl/role/name/", []string{"GET"}, (*HTTPServer).ACLRoleReadByName)

View File

@ -1238,7 +1238,8 @@ func (r *ACLPolicyDeleteRequest) RequestDatacenter() string {
// ACLPolicyGetRequest is used at the RPC layer to perform policy read operations
type ACLPolicyGetRequest struct {
PolicyID string // id used for the policy lookup
PolicyID string // id used for the policy lookup (one of PolicyID or PolicyName is allowed)
PolicyName string // name used for the policy lookup (one of PolicyID or PolicyName is allowed)
Datacenter string // The datacenter to perform the request within
EnterpriseMeta
QueryOptions

View File

@ -666,6 +666,32 @@ func (a *ACL) PolicyRead(policyID string, q *QueryOptions) (*ACLPolicy, *QueryMe
return &out, qm, nil
}
// PolicyReadByName retrieves the policy details including the rule set with name.
func (a *ACL) PolicyReadByName(policyName string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
r := a.c.newRequest("GET", "/v1/acl/policy/name/"+url.QueryEscape(policyName))
r.setQueryOptions(q)
found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
if err != nil {
return nil, nil, err
}
defer resp.Body.Close()
qm := &QueryMeta{}
parseQueryMeta(resp, qm)
qm.RequestTime = rtt
if !found {
return nil, qm, nil
}
var out ACLPolicy
if err := decodeBody(resp, &out); err != nil {
return nil, nil, err
}
return &out, qm, nil
}
// PolicyList retrieves a listing of all policies. The listing does not include the
// rules for any policy as those should be retrieved by subsequent calls to PolicyRead.
func (a *ACL) PolicyList(q *QueryOptions) ([]*ACLPolicyListEntry, *QueryMeta, error) {

View File

@ -205,6 +205,41 @@ func TestAPI_ACLPolicy_CreateReadDelete(t *testing.T) {
require.Error(t, err)
}
func TestAPI_ACLPolicy_CreateReadByNameDelete(t *testing.T) {
t.Parallel()
c, s := makeACLClient(t)
defer s.Stop()
acl := c.ACL()
created, wm, err := acl.PolicyCreate(&ACLPolicy{
Name: "test-policy",
Description: "test-policy description",
Rules: `node_prefix "" { policy = "read" }`,
Datacenters: []string{"dc1"},
}, nil)
require.NoError(t, err)
require.NotNil(t, created)
require.NotEqual(t, "", created.ID)
require.NotEqual(t, 0, wm.RequestTime)
read, qm, err := acl.PolicyReadByName(created.Name, nil)
require.NoError(t, err)
require.NotEqual(t, 0, qm.LastIndex)
require.True(t, qm.KnownLeader)
require.Equal(t, created, read)
wm, err = acl.PolicyDelete(created.ID, nil)
require.NoError(t, err)
require.NotEqual(t, 0, wm.RequestTime)
read, _, err = acl.PolicyRead(created.ID, nil)
require.Nil(t, read)
require.Error(t, err)
}
func TestAPI_ACLPolicy_CreateUpdate(t *testing.T) {
t.Parallel()
c, s := makeACLClient(t)