github.com/bigcommerce/nomad@v0.9.3-bc/nomad/periodic_endpoint_test.go (about) 1 package nomad 2 3 import ( 4 "testing" 5 6 memdb "github.com/hashicorp/go-memdb" 7 msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc" 8 "github.com/hashicorp/nomad/acl" 9 "github.com/hashicorp/nomad/nomad/mock" 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/hashicorp/nomad/testutil" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 func TestPeriodicEndpoint_Force(t *testing.T) { 16 t.Parallel() 17 s1 := TestServer(t, func(c *Config) { 18 c.NumSchedulers = 0 // Prevent automatic dequeue 19 }) 20 state := s1.fsm.State() 21 defer s1.Shutdown() 22 codec := rpcClient(t, s1) 23 testutil.WaitForLeader(t, s1.RPC) 24 25 // Create and insert a periodic job. 26 job := mock.PeriodicJob() 27 job.Periodic.ProhibitOverlap = true // Shouldn't affect anything. 28 if err := state.UpsertJob(100, job); err != nil { 29 t.Fatalf("err: %v", err) 30 } 31 s1.periodicDispatcher.Add(job) 32 33 // Force launch it. 34 req := &structs.PeriodicForceRequest{ 35 JobID: job.ID, 36 WriteRequest: structs.WriteRequest{ 37 Region: "global", 38 Namespace: job.Namespace, 39 }, 40 } 41 42 // Fetch the response 43 var resp structs.PeriodicForceResponse 44 if err := msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp); err != nil { 45 t.Fatalf("err: %v", err) 46 } 47 if resp.Index == 0 { 48 t.Fatalf("bad index: %d", resp.Index) 49 } 50 51 // Lookup the evaluation 52 ws := memdb.NewWatchSet() 53 eval, err := state.EvalByID(ws, resp.EvalID) 54 if err != nil { 55 t.Fatalf("err: %v", err) 56 } 57 if eval == nil { 58 t.Fatalf("expected eval") 59 } 60 if eval.CreateIndex != resp.EvalCreateIndex { 61 t.Fatalf("index mis-match") 62 } 63 } 64 65 func TestPeriodicEndpoint_Force_ACL(t *testing.T) { 66 t.Parallel() 67 s1, root := TestACLServer(t, func(c *Config) { 68 c.NumSchedulers = 0 // Prevent automatic dequeue 69 }) 70 defer s1.Shutdown() 71 state := s1.fsm.State() 72 assert := assert.New(t) 73 codec := rpcClient(t, s1) 74 testutil.WaitForLeader(t, s1.RPC) 75 76 // Create and insert a periodic job. 77 job := mock.PeriodicJob() 78 job.Periodic.ProhibitOverlap = true // Shouldn't affect anything. 79 assert.Nil(state.UpsertJob(100, job)) 80 err := s1.periodicDispatcher.Add(job) 81 assert.Nil(err) 82 83 // Force launch it. 84 req := &structs.PeriodicForceRequest{ 85 JobID: job.ID, 86 WriteRequest: structs.WriteRequest{ 87 Region: "global", 88 Namespace: job.Namespace, 89 }, 90 } 91 92 // Try with no token and expect permission denied 93 { 94 var resp structs.PeriodicForceResponse 95 err := msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp) 96 assert.NotNil(err) 97 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 98 } 99 100 // Try with an invalid token and expect permission denied 101 { 102 invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "invalid", mock.NodePolicy(acl.PolicyWrite)) 103 req.AuthToken = invalidToken.SecretID 104 var resp structs.PeriodicForceResponse 105 err := msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp) 106 assert.NotNil(err) 107 assert.Contains(err.Error(), structs.ErrPermissionDenied.Error()) 108 } 109 110 // Fetch the response with a valid token 111 { 112 policy := mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilitySubmitJob}) 113 token := mock.CreatePolicyAndToken(t, state, 1005, "valid", policy) 114 req.AuthToken = token.SecretID 115 var resp structs.PeriodicForceResponse 116 assert.Nil(msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp)) 117 assert.NotEqual(0, resp.Index) 118 119 // Lookup the evaluation 120 ws := memdb.NewWatchSet() 121 eval, err := state.EvalByID(ws, resp.EvalID) 122 assert.Nil(err) 123 if assert.NotNil(eval) { 124 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex) 125 } 126 } 127 128 // Fetch the response with management token 129 { 130 req.AuthToken = root.SecretID 131 var resp structs.PeriodicForceResponse 132 assert.Nil(msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp)) 133 assert.NotEqual(0, resp.Index) 134 135 // Lookup the evaluation 136 ws := memdb.NewWatchSet() 137 eval, err := state.EvalByID(ws, resp.EvalID) 138 assert.Nil(err) 139 if assert.NotNil(eval) { 140 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex) 141 } 142 } 143 } 144 145 func TestPeriodicEndpoint_Force_NonPeriodic(t *testing.T) { 146 t.Parallel() 147 s1 := TestServer(t, func(c *Config) { 148 c.NumSchedulers = 0 // Prevent automatic dequeue 149 }) 150 state := s1.fsm.State() 151 defer s1.Shutdown() 152 codec := rpcClient(t, s1) 153 testutil.WaitForLeader(t, s1.RPC) 154 155 // Create and insert a non-periodic job. 156 job := mock.Job() 157 if err := state.UpsertJob(100, job); err != nil { 158 t.Fatalf("err: %v", err) 159 } 160 161 // Force launch it. 162 req := &structs.PeriodicForceRequest{ 163 JobID: job.ID, 164 WriteRequest: structs.WriteRequest{ 165 Region: "global", 166 Namespace: job.Namespace, 167 }, 168 } 169 170 // Fetch the response 171 var resp structs.PeriodicForceResponse 172 if err := msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp); err == nil { 173 t.Fatalf("Force on non-periodic job should err") 174 } 175 }