diff --git a/nomad/structs/funcs.go b/nomad/structs/funcs.go index 837437cc4..71f8f0f23 100644 --- a/nomad/structs/funcs.go +++ b/nomad/structs/funcs.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "fmt" "math" + "sort" "strings" multierror "github.com/hashicorp/go-multierror" @@ -273,6 +274,9 @@ func ACLPolicyListHash(policies []*ACLPolicy) string { // CompileACLObject compiles a set of ACL policies into an ACL object with a cache func CompileACLObject(cache *lru.TwoQueueCache, policies []*ACLPolicy) (*acl.ACL, error) { + // Sort the policies to ensure consistent ordering + sort.Sort(ACLPolicyList(policies)) + // Determine the cache key cacheKey := ACLPolicyListHash(policies) aclRaw, ok := cache.Get(cacheKey) @@ -300,3 +304,18 @@ func CompileACLObject(cache *lru.TwoQueueCache, policies []*ACLPolicy) (*acl.ACL cache.Add(cacheKey, aclObj) return aclObj, nil } + +// ACLPolicyList is used to sort a set of policies by name +type ACLPolicyList []*ACLPolicy + +func (l ACLPolicyList) Len() int { + return len(l) +} + +func (l ACLPolicyList) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +func (l ACLPolicyList) Less(i, j int) bool { + return l[i].Name < l[j].Name +} diff --git a/nomad/structs/funcs_test.go b/nomad/structs/funcs_test.go index 480c78583..7ae921fc0 100644 --- a/nomad/structs/funcs_test.go +++ b/nomad/structs/funcs_test.go @@ -366,4 +366,12 @@ func TestCompileACLObject(t *testing.T) { if aclObj == aclObj3 { t.Fatalf("unexpected same object") } + + // Should be order independent + aclObj4, err := CompileACLObject(cache, []*ACLPolicy{p2, p1}) + assert.Nil(t, err) + assert.NotNil(t, aclObj4) + if aclObj3 != aclObj4 { + t.Fatalf("expected same object") + } }