4e005d4753
Enforce ACL for System.GarbageCollect and System.ReconcileJobSummaries RPC endpoints.
216 lines
5.7 KiB
Go
216 lines
5.7 KiB
Go
package nomad
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
memdb "github.com/hashicorp/go-memdb"
|
|
"github.com/hashicorp/net-rpc-msgpackrpc"
|
|
"github.com/hashicorp/nomad/acl"
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/hashicorp/nomad/testutil"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestSystemEndpoint_GarbageCollect(t *testing.T) {
|
|
t.Parallel()
|
|
s1 := testServer(t, nil)
|
|
defer s1.Shutdown()
|
|
codec := rpcClient(t, s1)
|
|
testutil.WaitForLeader(t, s1.RPC)
|
|
|
|
// Insert a job that can be GC'd
|
|
state := s1.fsm.State()
|
|
job := mock.Job()
|
|
job.Type = structs.JobTypeBatch
|
|
job.Stop = true
|
|
if err := state.UpsertJob(1000, job); err != nil {
|
|
t.Fatalf("UpsertJob() failed: %v", err)
|
|
}
|
|
|
|
eval := mock.Eval()
|
|
eval.Status = structs.EvalStatusComplete
|
|
eval.JobID = job.ID
|
|
if err := state.UpsertEvals(1001, []*structs.Evaluation{eval}); err != nil {
|
|
t.Fatalf("UpsertEvals() failed: %v", err)
|
|
}
|
|
|
|
// Make the GC request
|
|
req := &structs.GenericRequest{
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
},
|
|
}
|
|
var resp structs.GenericResponse
|
|
if err := msgpackrpc.CallWithCodec(codec, "System.GarbageCollect", req, &resp); err != nil {
|
|
t.Fatalf("expect err")
|
|
}
|
|
|
|
testutil.WaitForResult(func() (bool, error) {
|
|
// Check if the job has been GC'd
|
|
ws := memdb.NewWatchSet()
|
|
exist, err := state.JobByID(ws, job.Namespace, job.ID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if exist != nil {
|
|
return false, fmt.Errorf("job %+v wasn't garbage collected", job)
|
|
}
|
|
return true, nil
|
|
}, func(err error) {
|
|
t.Fatalf("err: %s", err)
|
|
})
|
|
}
|
|
|
|
func TestSystemEndpoint_GarbageCollect_ACL(t *testing.T) {
|
|
t.Parallel()
|
|
s1, root := testACLServer(t, nil)
|
|
defer s1.Shutdown()
|
|
codec := rpcClient(t, s1)
|
|
assert := assert.New(t)
|
|
testutil.WaitForLeader(t, s1.RPC)
|
|
state := s1.fsm.State()
|
|
|
|
// Create ACL tokens
|
|
invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", mock.NodePolicy(acl.PolicyWrite))
|
|
|
|
// Make the GC request
|
|
req := &structs.GenericRequest{
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
},
|
|
}
|
|
|
|
// Try without a token and expect failure
|
|
{
|
|
var resp structs.GenericResponse
|
|
err := msgpackrpc.CallWithCodec(codec, "System.GarbageCollect", req, &resp)
|
|
assert.NotNil(err)
|
|
assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
|
|
}
|
|
|
|
// Try with an invalid token and expect failure
|
|
{
|
|
req.SecretID = invalidToken.SecretID
|
|
var resp structs.GenericResponse
|
|
err := msgpackrpc.CallWithCodec(codec, "System.GarbageCollect", req, &resp)
|
|
assert.NotNil(err)
|
|
assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
|
|
}
|
|
|
|
// Try with a management token
|
|
{
|
|
req.SecretID = root.SecretID
|
|
var resp structs.GenericResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "System.GarbageCollect", req, &resp))
|
|
}
|
|
}
|
|
|
|
func TestSystemEndpoint_ReconcileSummaries(t *testing.T) {
|
|
t.Parallel()
|
|
s1 := testServer(t, nil)
|
|
defer s1.Shutdown()
|
|
codec := rpcClient(t, s1)
|
|
testutil.WaitForLeader(t, s1.RPC)
|
|
|
|
// Insert a job that can be GC'd
|
|
state := s1.fsm.State()
|
|
s1.fsm.State()
|
|
job := mock.Job()
|
|
if err := state.UpsertJob(1000, job); err != nil {
|
|
t.Fatalf("UpsertJob() failed: %v", err)
|
|
}
|
|
|
|
// Delete the job summary
|
|
state.DeleteJobSummary(1001, job.Namespace, job.ID)
|
|
|
|
// Make the GC request
|
|
req := &structs.GenericRequest{
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
},
|
|
}
|
|
var resp structs.GenericResponse
|
|
if err := msgpackrpc.CallWithCodec(codec, "System.ReconcileJobSummaries", req, &resp); err != nil {
|
|
t.Fatalf("expect err: %v", err)
|
|
}
|
|
|
|
testutil.WaitForResult(func() (bool, error) {
|
|
// Check if Nomad has reconciled the summary for the job
|
|
ws := memdb.NewWatchSet()
|
|
summary, err := state.JobSummaryByID(ws, job.Namespace, job.ID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if summary.CreateIndex == 0 || summary.ModifyIndex == 0 {
|
|
t.Fatalf("create index: %v, modify index: %v", summary.CreateIndex, summary.ModifyIndex)
|
|
}
|
|
|
|
// setting the modifyindex and createindex of the expected summary to
|
|
// the output so that we can do deep equal
|
|
expectedSummary := structs.JobSummary{
|
|
JobID: job.ID,
|
|
Namespace: job.Namespace,
|
|
Summary: map[string]structs.TaskGroupSummary{
|
|
"web": {
|
|
Queued: 10,
|
|
},
|
|
},
|
|
ModifyIndex: summary.ModifyIndex,
|
|
CreateIndex: summary.CreateIndex,
|
|
}
|
|
if !reflect.DeepEqual(&expectedSummary, summary) {
|
|
return false, fmt.Errorf("expected: %v, actual: %v", expectedSummary, summary)
|
|
}
|
|
return true, nil
|
|
}, func(err error) {
|
|
t.Fatalf("err: %s", err)
|
|
})
|
|
}
|
|
|
|
func TestSystemEndpoint_ReconcileJobSummaries_ACL(t *testing.T) {
|
|
t.Parallel()
|
|
s1, root := testACLServer(t, nil)
|
|
defer s1.Shutdown()
|
|
codec := rpcClient(t, s1)
|
|
assert := assert.New(t)
|
|
testutil.WaitForLeader(t, s1.RPC)
|
|
state := s1.fsm.State()
|
|
|
|
// Create ACL tokens
|
|
invalidToken := mock.CreatePolicyAndToken(t, state, 1001, "test-invalid", mock.NodePolicy(acl.PolicyWrite))
|
|
|
|
// Make the request
|
|
req := &structs.GenericRequest{
|
|
QueryOptions: structs.QueryOptions{
|
|
Region: "global",
|
|
},
|
|
}
|
|
|
|
// Try without a token and expect failure
|
|
{
|
|
var resp structs.GenericResponse
|
|
err := msgpackrpc.CallWithCodec(codec, "System.ReconcileJobSummaries", req, &resp)
|
|
assert.NotNil(err)
|
|
assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
|
|
}
|
|
|
|
// Try with an invalid token and expect failure
|
|
{
|
|
req.SecretID = invalidToken.SecretID
|
|
var resp structs.GenericResponse
|
|
err := msgpackrpc.CallWithCodec(codec, "System.ReconcileJobSummaries", req, &resp)
|
|
assert.NotNil(err)
|
|
assert.Contains(err.Error(), structs.ErrPermissionDenied.Error())
|
|
}
|
|
|
|
// Try with a management token
|
|
{
|
|
req.SecretID = root.SecretID
|
|
var resp structs.GenericResponse
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "System.ReconcileJobSummaries", req, &resp))
|
|
}
|
|
}
|