Commit ffa79f47 authored by Michael Schurter's avatar Michael Schurter Committed by GitHub
Browse files

Merge pull request #3315 from hashicorp/f-acl-deployment-pause

Deployment.Pause ACL enforcement
Showing with 78 additions and 3 deletions
+78 -3
......@@ -116,6 +116,13 @@ func (d *Deployment) Pause(args *structs.DeploymentPauseRequest, reply *structs.
}
defer metrics.MeasureSince([]string{"nomad", "deployment", "pause"}, time.Now())
// Check namespace submit-job permissions
if aclObj, err := d.srv.resolveToken(args.SecretID); err != nil {
return err
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
return structs.ErrPermissionDenied
}
// Validate the arguments
if args.DeploymentID == "" {
return fmt.Errorf("missing deployment ID")
......
......@@ -388,6 +388,74 @@ func TestDeploymentEndpoint_Pause(t *testing.T) {
assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index")
}
func TestDeploymentEndpoint_Pause_ACL(t *testing.T) {
t.Parallel()
s1, _ := testACLServer(t, func(c *Config) {
c.NumSchedulers = 0 // Prevent automatic dequeue
})
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
assert := assert.New(t)
// Create the deployment
j := mock.Job()
d := mock.Deployment()
d.JobID = j.ID
state := s1.fsm.State()
assert.Nil(state.UpsertJob(999, j), "UpsertJob")
assert.Nil(state.UpsertDeployment(1000, d), "UpsertDeployment")
// Create the namespace policy and tokens
validToken := CreatePolicyAndToken(t, state, 1001, "test-valid",
NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilitySubmitJob}))
invalidToken := CreatePolicyAndToken(t, state, 1003, "test-invalid",
NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
// Mark the deployment as failed
req := &structs.DeploymentPauseRequest{
DeploymentID: d.ID,
Pause: true,
WriteRequest: structs.WriteRequest{Region: "global"},
}
// Try with no token and expect permission denied
{
var resp structs.DeploymentUpdateResponse
err := msgpackrpc.CallWithCodec(codec, "Deployment.Pause", req, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with an invalid token
{
req.SecretID = invalidToken.SecretID
var resp structs.DeploymentUpdateResponse
err := msgpackrpc.CallWithCodec(codec, "Deployment.Pause", req, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Fetch the response with a valid token
{
req.SecretID = validToken.SecretID
var resp structs.DeploymentUpdateResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.Pause", req, &resp), "RPC")
assert.NotEqual(resp.Index, uint64(0), "bad response index")
assert.Zero(resp.EvalCreateIndex, "Shouldn't create eval")
assert.Zero(resp.EvalID, "Shouldn't create eval")
// Lookup the deployment
ws := memdb.NewWatchSet()
dout, err := state.DeploymentByID(ws, d.ID)
assert.Nil(err, "DeploymentByID failed")
assert.Equal(dout.Status, structs.DeploymentStatusPaused, "wrong status")
assert.Equal(dout.StatusDescription, structs.DeploymentStatusDescriptionPaused, "wrong status description")
assert.Equal(dout.ModifyIndex, resp.DeploymentModifyIndex, "wrong modify index")
}
}
func TestDeploymentEndpoint_Promote(t *testing.T) {
t.Parallel()
s1 := testServer(t, func(c *Config) {
......
......@@ -315,9 +315,9 @@ The table below shows this endpoint's support for
[blocking queries](/api/index.html#blocking-queries) and
[required ACLs](/api/index.html#acls).
| Blocking Queries | ACL Required |
| ---------------- | ------------ |
| `NO` | `none` |
| Blocking Queries | ACL Required |
| ---------------- | ---------------------- |
| `NO` | `namespace:submit-job` |
### Parameters
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment