2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2016-02-20 23:50:41 +00:00
|
|
|
package nomad
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2016-08-03 23:08:30 +00:00
|
|
|
"reflect"
|
2016-02-20 23:50:41 +00:00
|
|
|
"testing"
|
|
|
|
|
2017-02-08 05:22:48 +00:00
|
|
|
memdb "github.com/hashicorp/go-memdb"
|
2023-04-17 21:02:05 +00:00
|
|
|
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc/v2"
|
2017-10-10 17:51:38 +00:00
|
|
|
"github.com/hashicorp/nomad/acl"
|
2022-03-15 12:42:43 +00:00
|
|
|
"github.com/hashicorp/nomad/ci"
|
2016-02-20 23:50:41 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
"github.com/hashicorp/nomad/testutil"
|
2017-10-10 17:51:38 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2016-02-20 23:50:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestSystemEndpoint_GarbageCollect(t *testing.T) {
|
2022-03-15 12:42:43 +00:00
|
|
|
ci.Parallel(t)
|
2019-12-04 00:15:11 +00:00
|
|
|
|
|
|
|
s1, cleanupS1 := TestServer(t, nil)
|
|
|
|
defer cleanupS1()
|
2016-02-20 23:50:41 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
testutil.WaitForLeader(t, s1.RPC)
|
|
|
|
|
|
|
|
// Insert a job that can be GC'd
|
|
|
|
state := s1.fsm.State()
|
|
|
|
job := mock.Job()
|
2016-03-24 01:02:01 +00:00
|
|
|
job.Type = structs.JobTypeBatch
|
2017-04-15 23:47:19 +00:00
|
|
|
job.Stop = true
|
2023-04-11 13:45:08 +00:00
|
|
|
if err := state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, job); err != nil {
|
2017-04-15 23:47:19 +00:00
|
|
|
t.Fatalf("UpsertJob() failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
eval := mock.Eval()
|
|
|
|
eval.Status = structs.EvalStatusComplete
|
|
|
|
eval.JobID = job.ID
|
2020-10-19 13:30:15 +00:00
|
|
|
if err := state.UpsertEvals(structs.MsgTypeTestSetup, 1001, []*structs.Evaluation{eval}); err != nil {
|
2017-04-15 23:47:19 +00:00
|
|
|
t.Fatalf("UpsertEvals() failed: %v", err)
|
2016-02-20 23:50:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2017-02-08 05:22:48 +00:00
|
|
|
ws := memdb.NewWatchSet()
|
2017-09-07 23:56:15 +00:00
|
|
|
exist, err := state.JobByID(ws, job.Namespace, job.ID)
|
2016-02-20 23:50:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
if exist != nil {
|
2017-04-15 23:47:19 +00:00
|
|
|
return false, fmt.Errorf("job %+v wasn't garbage collected", job)
|
2016-02-20 23:50:41 +00:00
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}, func(err error) {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
})
|
|
|
|
}
|
2016-08-03 23:08:30 +00:00
|
|
|
|
2017-10-10 17:51:38 +00:00
|
|
|
func TestSystemEndpoint_GarbageCollect_ACL(t *testing.T) {
|
2022-03-15 12:42:43 +00:00
|
|
|
ci.Parallel(t)
|
2019-12-04 00:15:11 +00:00
|
|
|
|
|
|
|
s1, root, cleanupS1 := TestACLServer(t, nil)
|
|
|
|
defer cleanupS1()
|
2017-10-10 17:51:38 +00:00
|
|
|
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
|
|
|
|
{
|
2017-10-12 22:16:33 +00:00
|
|
|
req.AuthToken = invalidToken.SecretID
|
2017-10-10 17:51:38 +00:00
|
|
|
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
|
|
|
|
{
|
2017-10-12 22:16:33 +00:00
|
|
|
req.AuthToken = root.SecretID
|
2017-10-10 17:51:38 +00:00
|
|
|
var resp structs.GenericResponse
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "System.GarbageCollect", req, &resp))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 23:08:30 +00:00
|
|
|
func TestSystemEndpoint_ReconcileSummaries(t *testing.T) {
|
2022-03-15 12:42:43 +00:00
|
|
|
ci.Parallel(t)
|
2019-12-04 00:15:11 +00:00
|
|
|
|
|
|
|
s1, cleanupS1 := TestServer(t, nil)
|
|
|
|
defer cleanupS1()
|
2016-08-03 23:08:30 +00:00
|
|
|
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()
|
2023-04-11 13:45:08 +00:00
|
|
|
if err := state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, job); err != nil {
|
2016-08-03 23:08:30 +00:00
|
|
|
t.Fatalf("UpsertJob() failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the job summary
|
2017-09-07 23:56:15 +00:00
|
|
|
state.DeleteJobSummary(1001, job.Namespace, job.ID)
|
2016-08-03 23:08:30 +00:00
|
|
|
|
|
|
|
// 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
|
2017-02-08 05:22:48 +00:00
|
|
|
ws := memdb.NewWatchSet()
|
2017-09-07 23:56:15 +00:00
|
|
|
summary, err := state.JobSummaryByID(ws, job.Namespace, job.ID)
|
2016-08-03 23:08:30 +00:00
|
|
|
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{
|
2017-09-07 23:56:15 +00:00
|
|
|
JobID: job.ID,
|
|
|
|
Namespace: job.Namespace,
|
2016-08-03 23:08:30 +00:00
|
|
|
Summary: map[string]structs.TaskGroupSummary{
|
2017-09-26 22:26:33 +00:00
|
|
|
"web": {
|
2016-08-03 23:08:30 +00:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
}
|
2017-10-10 17:51:38 +00:00
|
|
|
|
|
|
|
func TestSystemEndpoint_ReconcileJobSummaries_ACL(t *testing.T) {
|
2022-03-15 12:42:43 +00:00
|
|
|
ci.Parallel(t)
|
2019-12-04 00:15:11 +00:00
|
|
|
|
|
|
|
s1, root, cleanupS1 := TestACLServer(t, nil)
|
|
|
|
defer cleanupS1()
|
2017-10-10 17:51:38 +00:00
|
|
|
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
|
|
|
|
{
|
2017-10-12 22:16:33 +00:00
|
|
|
req.AuthToken = invalidToken.SecretID
|
2017-10-10 17:51:38 +00:00
|
|
|
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
|
|
|
|
{
|
2017-10-12 22:16:33 +00:00
|
|
|
req.AuthToken = root.SecretID
|
2017-10-10 17:51:38 +00:00
|
|
|
var resp structs.GenericResponse
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "System.ReconcileJobSummaries", req, &resp))
|
|
|
|
}
|
|
|
|
}
|