consul/state: basic acl set/get/delete

This commit is contained in:
Ryan Uber 2015-09-06 21:13:45 -07:00 committed by James Phillips
parent 391d4eed57
commit 16188e7604
3 changed files with 162 additions and 7 deletions

View File

@ -23,6 +23,10 @@ var (
// ErrMissingSessionID is returned when a session registration
// is attempted with an empty session ID.
ErrMissingSessionID = errors.New("Missing session ID")
// ErrMissingACLID is returned when a session set is called on
// a session with an empty ID.
ErrMissingACLID = errors.New("Missing ACL ID")
)
// StateStore is where we store all of Consul's state, including
@ -897,7 +901,7 @@ func (s *StateStore) KVSDeleteCAS(idx, cidx uint64, key string) (bool, error) {
return false, fmt.Errorf("failed kvs lookup: %s", err)
}
// If the existing index does not match the provided CAS
// If the existing index does not match the provided CAS
// index arg, then we shouldn't update anything and can safely
// return early here.
e, ok := entry.(*structs.DirEntry)
@ -1179,3 +1183,66 @@ func (s *StateStore) sessionDestroyTxn(idx uint64, sessionID string, tx *memdb.T
return nil
}
// ACLSet is used to insert an ACL rule into the state store.
func (s *StateStore) ACLSet(idx uint64, acl *structs.ACL) error {
tx := s.db.Txn(true)
defer tx.Abort()
// Call set on the ACL
if err := s.aclSetTxn(idx, acl, tx); err != nil {
return err
}
tx.Commit()
return nil
}
// aclSetTxn is the inner method used to insert an ACL rule with the
// proper indexes into the state store.
func (s *StateStore) aclSetTxn(idx uint64, acl *structs.ACL, tx *memdb.Txn) error {
// Check that the ID is set
if acl.ID == "" {
return ErrMissingACLID
}
// Check for an existing ACL
existing, err := tx.First("acls", "id", acl.ID)
if err != nil {
return fmt.Errorf("failed acl lookup: %s", err)
}
// Set the indexes
if existing != nil {
acl.CreateIndex = existing.(*structs.ACL).CreateIndex
acl.ModifyIndex = idx
} else {
acl.CreateIndex = idx
acl.ModifyIndex = idx
}
// Insert the ACL
if err := tx.Insert("acls", acl); err != nil {
return fmt.Errorf("failed inserting acl: %s", err)
}
if err := tx.Insert("index", &IndexEntry{"acls", idx}); err != nil {
return fmt.Errorf("failed updating index: %s", err)
}
return nil
}
// ACLGet is used to look up an existing ACL by ID.
func (s *StateStore) ACLGet(aclID string) (*structs.ACL, error) {
tx := s.db.Txn(false)
defer tx.Abort()
// Query for the existing ACL
acl, err := tx.First("acls", "id", aclID)
if err != nil {
return nil, fmt.Errorf("failed acl lookup: %s", err)
}
if acl != nil {
return acl.(*structs.ACL), nil
}
return nil, nil
}

View File

@ -1477,3 +1477,91 @@ func TestStateStore_SessionDestroy(t *testing.T) {
t.Fatalf("bad index: %d", idx)
}
}
func TestStateStore_ACLSet(t *testing.T) {
s := testStateStore(t)
// Querying ACL's with no results returns nil
res, err := s.ACLGet("nope")
if res != nil || err != nil {
t.Fatalf("expected (nil, nil), got: (%#v, %#v)", res, err)
}
// Inserting an ACL with empty ID is disallowed
if err := s.ACLSet(1, &structs.ACL{}); err == nil {
t.Fatalf("expected %#v, got: %#v", ErrMissingACLID, err)
}
// Index is not updated if nothing is saved
if idx := s.maxIndex("acls"); idx != 0 {
t.Fatalf("bad index: %d", idx)
}
// Inserting valid ACL works
acl := &structs.ACL{
ID: "acl1",
Name: "First ACL",
Type: structs.ACLTypeClient,
Rules: "rules1",
}
if err := s.ACLSet(1, acl); err != nil {
t.Fatalf("err: %s", err)
}
// Check that the index was updated
if idx := s.maxIndex("acls"); idx != 1 {
t.Fatalf("err: %s", err)
}
// Retrieve the ACL again
result, err := s.ACLGet("acl1")
if err != nil {
t.Fatalf("err: %s", err)
}
// Check that the ACL matches the result
expect := &structs.ACL{
ID: "acl1",
Name: "First ACL",
Type: structs.ACLTypeClient,
Rules: "rules1",
RaftIndex: structs.RaftIndex{
CreateIndex: 1,
ModifyIndex: 1,
},
}
if !reflect.DeepEqual(result, expect) {
t.Fatalf("bad: %#v", result)
}
// Update the ACL
acl = &structs.ACL{
ID: "acl1",
Name: "First ACL",
Type: structs.ACLTypeClient,
Rules: "rules2",
}
if err := s.ACLSet(2, acl); err != nil {
t.Fatalf("err: %s", err)
}
// Index was updated
if idx := s.maxIndex("acls"); idx != 2 {
t.Fatalf("bad: %d", idx)
}
// ACL was updated and matches expected value
expect = &structs.ACL{
ID: "acl1",
Name: "First ACL",
Type: structs.ACLTypeClient,
Rules: "rules2",
RaftIndex: structs.RaftIndex{
CreateIndex: 1,
ModifyIndex: 2,
},
}
if !reflect.DeepEqual(acl, expect) {
t.Fatalf("bad: %#v", acl)
}
}

View File

@ -479,12 +479,12 @@ type IndexedSessions struct {
// ACL is used to represent a token and it's rules
type ACL struct {
CreateIndex uint64
ModifyIndex uint64
ID string
Name string
Type string
Rules string
ID string
Name string
Type string
Rules string
RaftIndex
}
type ACLs []*ACL