github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/nomad/periodic_endpoint_test.go (about) 1 package nomad 2 3 import ( 4 "testing" 5 6 memdb "github.com/hashicorp/go-memdb" 7 "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 assert.NotNil(eval) 124 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex) 125 } 126 127 // Fetch the response with management token 128 { 129 req.AuthToken = root.SecretID 130 var resp structs.PeriodicForceResponse 131 assert.Nil(msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp)) 132 assert.NotEqual(0, resp.Index) 133 134 // Lookup the evaluation 135 ws := memdb.NewWatchSet() 136 eval, err := state.EvalByID(ws, resp.EvalID) 137 assert.Nil(err) 138 assert.NotNil(eval) 139 assert.Equal(eval.CreateIndex, resp.EvalCreateIndex) 140 } 141 } 142 143 func TestPeriodicEndpoint_Force_NonPeriodic(t *testing.T) { 144 t.Parallel() 145 s1 := TestServer(t, func(c *Config) { 146 c.NumSchedulers = 0 // Prevent automatic dequeue 147 }) 148 state := s1.fsm.State() 149 defer s1.Shutdown() 150 codec := rpcClient(t, s1) 151 testutil.WaitForLeader(t, s1.RPC) 152 153 // Create and insert a non-periodic job. 154 job := mock.Job() 155 if err := state.UpsertJob(100, job); err != nil { 156 t.Fatalf("err: %v", err) 157 } 158 159 // Force launch it. 160 req := &structs.PeriodicForceRequest{ 161 JobID: job.ID, 162 WriteRequest: structs.WriteRequest{ 163 Region: "global", 164 Namespace: job.Namespace, 165 }, 166 } 167 168 // Fetch the response 169 var resp structs.PeriodicForceResponse 170 if err := msgpackrpc.CallWithCodec(codec, "Periodic.Force", req, &resp); err == nil { 171 t.Fatalf("Force on non-periodic job should err") 172 } 173 }