diff --git a/nomad/acl_endpoint.go b/nomad/acl_endpoint.go index e77cd1048..6c0d0d7fe 100644 --- a/nomad/acl_endpoint.go +++ b/nomad/acl_endpoint.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "time" metrics "github.com/armon/go-metrics" @@ -578,12 +579,14 @@ func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.G // Determine if we are deleting local or global tokens hasGlobal := false allGlobal := true + nonexistentTokens := make([]string, 0) for _, accessor := range args.AccessorIDs { token, err := state.ACLTokenByAccessorID(nil, accessor) if err != nil { return fmt.Errorf("token lookup failed: %v", err) } if token == nil { + nonexistentTokens = append(nonexistentTokens, accessor) continue } if token.Global { @@ -593,6 +596,10 @@ func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.G } } + if len(nonexistentTokens) != 0 { + return fmt.Errorf("Cannot delete nonexistent tokens: %v", strings.Join(nonexistentTokens, ", ")) + } + // Disallow mixed requests with global and non-global tokens since we forward // the entire request as a single batch. if hasGlobal { diff --git a/nomad/acl_endpoint_test.go b/nomad/acl_endpoint_test.go index 8c668aa75..944f7c5a8 100644 --- a/nomad/acl_endpoint_test.go +++ b/nomad/acl_endpoint_test.go @@ -907,6 +907,33 @@ func TestACLEndpoint_DeleteTokens(t *testing.T) { assert.NotEqual(t, uint64(0), resp.Index) } +func TestACLEndpoint_DeleteTokens_WithNonexistantToken(t *testing.T) { + t.Parallel() + assert := assert.New(t) + + s1, root := testACLServer(t, nil) + defer s1.Shutdown() + codec := rpcClient(t, s1) + testutil.WaitForLeader(t, s1.RPC) + + nonExistentToken := mock.ACLToken() + + // Lookup the policies + req := &structs.ACLTokenDeleteRequest{ + AccessorIDs: []string{nonExistentToken.AccessorID}, + WriteRequest: structs.WriteRequest{ + Region: "global", + AuthToken: root.SecretID, + }, + } + var resp structs.GenericResponse + err := msgpackrpc.CallWithCodec(codec, "ACL.DeleteTokens", req, &resp) + + assert.NotNil(err) + expectedError := fmt.Sprintf("Cannot delete nonexistent tokens: %s", nonExistentToken.AccessorID) + assert.Contains(expectedError, err.Error()) +} + func TestACLEndpoint_Bootstrap(t *testing.T) { t.Parallel() s1 := testServer(t, func(c *Config) {