open-nomad/nomad/namespace_endpoint_test.go
Tim Gross cf9e5f3327
acl: Fix panic when bogus token is passed (#15863)
If a consumer of the new `Authenticate` method gets passed a bogus token that's
a correctly-shaped UUID, it will correctly get an identity without a ACL
token. But most consumers will then panic when they consume this nil `ACLToken`
for authorization.

Because no API client should ever send a bogus auth token, update the
`Authenticate` method to create the identity with remote IP (for metrics
tracking) but also return an `ErrPermissionDenied`.
2023-01-25 10:03:17 -05:00

792 lines
24 KiB
Go

package nomad
import (
"fmt"
"testing"
"time"
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/helper/uuid"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/stretchr/testify/assert"
)
func TestNamespaceEndpoint_GetNamespace(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns := mock.Namespace()
s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns})
// Lookup the namespace
get := &structs.NamespaceSpecificRequest{
Name: ns.Name,
QueryOptions: structs.QueryOptions{Region: "global"},
}
var resp structs.SingleNamespaceResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Equal(ns, resp.Namespace)
// Lookup non-existing namespace
get.Name = uuid.Generate()
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Nil(resp.Namespace)
}
func TestNamespaceEndpoint_GetNamespace_ACL(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, root, cleanupS1 := TestACLServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
state := s1.fsm.State()
s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})
// Create the policy and tokens
validToken := mock.CreatePolicyAndToken(t, state, 1002, "test-valid",
mock.NamespacePolicy(ns1.Name, "", []string{acl.NamespaceCapabilityReadJob}))
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
mock.NamespacePolicy(ns2.Name, "", []string{acl.NamespaceCapabilityReadJob}))
get := &structs.NamespaceSpecificRequest{
Name: ns1.Name,
QueryOptions: structs.QueryOptions{Region: "global"},
}
// Lookup the namespace without a token and expect failure
{
var resp structs.SingleNamespaceResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with an invalid token
get.AuthToken = invalidToken.SecretID
{
var resp structs.SingleNamespaceResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
}
// Try with a valid token
get.AuthToken = validToken.SecretID
{
var resp structs.SingleNamespaceResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Equal(ns1, resp.Namespace)
}
// Try with a root token
get.AuthToken = root.SecretID
{
var resp structs.SingleNamespaceResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Equal(ns1, resp.Namespace)
}
}
func TestNamespaceEndpoint_GetNamespace_Blocking(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
state := s1.fsm.State()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the namespaces
ns1 := mock.Namespace()
ns2 := mock.Namespace()
// First create an namespace
time.AfterFunc(100*time.Millisecond, func() {
assert.Nil(state.UpsertNamespaces(100, []*structs.Namespace{ns1}))
})
// Upsert the namespace we are watching later
time.AfterFunc(200*time.Millisecond, func() {
assert.Nil(state.UpsertNamespaces(200, []*structs.Namespace{ns2}))
})
// Lookup the namespace
req := &structs.NamespaceSpecificRequest{
Name: ns2.Name,
QueryOptions: structs.QueryOptions{
Region: "global",
MinQueryIndex: 150,
},
}
var resp structs.SingleNamespaceResponse
start := time.Now()
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", req, &resp))
assert.EqualValues(200, resp.Index)
assert.NotNil(resp.Namespace)
assert.Equal(ns2.Name, resp.Namespace.Name)
if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
}
// Namespace delete triggers watches
time.AfterFunc(100*time.Millisecond, func() {
assert.Nil(state.DeleteNamespaces(300, []string{ns2.Name}))
})
req.QueryOptions.MinQueryIndex = 250
var resp2 structs.SingleNamespaceResponse
start = time.Now()
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespace", req, &resp2))
assert.EqualValues(300, resp2.Index)
assert.Nil(resp2.Namespace)
if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
}
}
func TestNamespaceEndpoint_GetNamespaces(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})
// Lookup the namespace
get := &structs.NamespaceSetRequest{
Namespaces: []string{ns1.Name, ns2.Name},
QueryOptions: structs.QueryOptions{Region: "global"},
}
var resp structs.NamespaceSetResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Len(resp.Namespaces, 2)
assert.Contains(resp.Namespaces, ns1.Name)
assert.Contains(resp.Namespaces, ns2.Name)
}
func TestNamespaceEndpoint_GetNamespaces_ACL(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, root, cleanupS1 := TestACLServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
state := s1.fsm.State()
state.UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})
// Create the policy and tokens
validToken := mock.CreatePolicyAndToken(t, state, 1002, "test-valid",
mock.NamespacePolicy(ns1.Name, "", []string{acl.NamespaceCapabilityReadJob}))
// Lookup the namespace
get := &structs.NamespaceSetRequest{
Namespaces: []string{ns1.Name, ns2.Name},
QueryOptions: structs.QueryOptions{Region: "global"},
}
// Lookup the namespaces without a token and expect a failure
{
var resp structs.NamespaceSetResponse
assert.NotNil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
}
// Try with an non-management token
get.AuthToken = validToken.SecretID
{
var resp structs.NamespaceSetResponse
assert.NotNil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
}
// Try with a root token
get.AuthToken = root.SecretID
{
var resp structs.NamespaceSetResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Len(resp.Namespaces, 2)
assert.Contains(resp.Namespaces, ns1.Name)
assert.Contains(resp.Namespaces, ns2.Name)
}
}
func TestNamespaceEndpoint_GetNamespaces_Blocking(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
state := s1.fsm.State()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the namespaces
ns1 := mock.Namespace()
ns2 := mock.Namespace()
// First create an namespace
time.AfterFunc(100*time.Millisecond, func() {
assert.Nil(state.UpsertNamespaces(100, []*structs.Namespace{ns1}))
})
// Upsert the namespace we are watching later
time.AfterFunc(200*time.Millisecond, func() {
assert.Nil(state.UpsertNamespaces(200, []*structs.Namespace{ns2}))
})
// Lookup the namespace
req := &structs.NamespaceSetRequest{
Namespaces: []string{ns2.Name},
QueryOptions: structs.QueryOptions{
Region: "global",
MinQueryIndex: 150,
},
}
var resp structs.NamespaceSetResponse
start := time.Now()
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", req, &resp))
assert.EqualValues(200, resp.Index)
assert.Len(resp.Namespaces, 1)
assert.Contains(resp.Namespaces, ns2.Name)
if elapsed := time.Since(start); elapsed < 200*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
}
// Namespace delete triggers watches
time.AfterFunc(100*time.Millisecond, func() {
assert.Nil(state.DeleteNamespaces(300, []string{ns2.Name}))
})
req.QueryOptions.MinQueryIndex = 250
var resp2 structs.NamespaceSetResponse
start = time.Now()
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.GetNamespaces", req, &resp2))
assert.EqualValues(300, resp2.Index)
assert.Empty(resp2.Namespaces)
if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
}
}
func TestNamespaceEndpoint_List(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
ns1.Name = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
ns2.Name = "aaaabbbb-3350-4b4b-d185-0e1992ed43e9"
assert.Nil(s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2}))
// Lookup the namespaces
get := &structs.NamespaceListRequest{
QueryOptions: structs.QueryOptions{Region: "global"},
}
var resp structs.NamespaceListResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Len(resp.Namespaces, 3)
// Lookup the namespaces by prefix
get = &structs.NamespaceListRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
Prefix: "aaaabb",
},
}
var resp2 structs.NamespaceListResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp2))
assert.EqualValues(1000, resp2.Index)
assert.Len(resp2.Namespaces, 1)
}
func TestNamespaceEndpoint_List_ACL(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, root, cleanupS1 := TestACLServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
state := s1.fsm.State()
ns1.Name = "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"
ns2.Name = "bbbbbbbb-3350-4b4b-d185-0e1992ed43e9"
assert.Nil(s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2}))
validDefToken := mock.CreatePolicyAndToken(t, state, 1001, "test-def-valid",
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadFS}))
validMultiToken := mock.CreatePolicyAndToken(t, state, 1002, "test-multi-valid", fmt.Sprintf("%s\n%s",
mock.NamespacePolicy(ns1.Name, "", []string{acl.NamespaceCapabilityReadJob}),
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob})))
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
mock.NamespacePolicy("invalid-namespace", "", []string{acl.NamespaceCapabilityReadJob}))
get := &structs.NamespaceListRequest{
QueryOptions: structs.QueryOptions{Region: "global"},
}
// Lookup the namespaces without a token and expect a failure
{
var resp structs.NamespaceListResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp)
assert.Nil(err)
assert.Len(resp.Namespaces, 0)
}
// Try with an invalid token
get.AuthToken = invalidToken.SecretID
{
var resp structs.NamespaceListResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp)
assert.Nil(err)
assert.Len(resp.Namespaces, 0)
}
// Try with a valid token for one
get.AuthToken = validDefToken.SecretID
{
var resp structs.NamespaceListResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Len(resp.Namespaces, 1)
}
// Try with a valid token for two
get.AuthToken = validMultiToken.SecretID
{
var resp structs.NamespaceListResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Len(resp.Namespaces, 2)
}
// Try with a root token
get.AuthToken = root.SecretID
{
var resp structs.NamespaceListResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", get, &resp))
assert.EqualValues(1000, resp.Index)
assert.Len(resp.Namespaces, 3)
}
}
func TestNamespaceEndpoint_List_Blocking(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
state := s1.fsm.State()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the namespace
ns := mock.Namespace()
// Upsert namespace triggers watches
time.AfterFunc(100*time.Millisecond, func() {
assert.Nil(state.UpsertNamespaces(200, []*structs.Namespace{ns}))
})
req := &structs.NamespaceListRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
MinQueryIndex: 150,
},
}
start := time.Now()
var resp structs.NamespaceListResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", req, &resp))
if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
}
assert.EqualValues(200, resp.Index)
assert.Len(resp.Namespaces, 2)
// Namespace deletion triggers watches
time.AfterFunc(100*time.Millisecond, func() {
assert.Nil(state.DeleteNamespaces(300, []string{ns.Name}))
})
req.MinQueryIndex = 200
start = time.Now()
var resp2 structs.NamespaceListResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.ListNamespaces", req, &resp2))
if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
}
assert.EqualValues(300, resp2.Index)
assert.Len(resp2.Namespaces, 1)
}
func TestNamespaceEndpoint_DeleteNamespaces(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})
// Lookup the namespaces
req := &structs.NamespaceDeleteRequest{
Namespaces: []string{ns1.Name, ns2.Name},
WriteRequest: structs.WriteRequest{Region: "global"},
}
var resp structs.GenericResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp))
assert.NotEqual(uint64(0), resp.Index)
}
func TestNamespaceEndpoint_DeleteNamespaces_NonTerminal_Local(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})
// Create a job in one
j := mock.Job()
j.Namespace = ns1.Name
assert.Nil(s1.fsm.State().UpsertJob(structs.MsgTypeTestSetup, 1001, j))
// Lookup the namespaces
req := &structs.NamespaceDeleteRequest{
Namespaces: []string{ns1.Name, ns2.Name},
WriteRequest: structs.WriteRequest{Region: "global"},
}
var resp structs.GenericResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
if assert.NotNil(err) {
assert.Contains(err.Error(), "has non-terminal jobs")
}
}
func TestNamespaceEndpoint_DeleteNamespaces_NonTerminal_Federated_ACL(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, root, cleanupS1 := TestACLServer(t, func(c *Config) {
c.Region = "region1"
c.AuthoritativeRegion = "region1"
c.ACLEnabled = true
})
defer cleanupS1()
s2, _, cleanupS2 := TestACLServer(t, func(c *Config) {
c.Region = "region2"
c.AuthoritativeRegion = "region1"
c.ACLEnabled = true
c.ReplicationBackoff = 20 * time.Millisecond
c.ReplicationToken = root.SecretID
})
defer cleanupS2()
TestJoin(t, s1, s2)
testutil.WaitForLeader(t, s1.RPC)
testutil.WaitForLeader(t, s2.RPC)
codec := rpcClient(t, s1)
// Create the register request
ns1 := mock.Namespace()
s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1})
testutil.WaitForResult(func() (bool, error) {
state := s2.State()
out, err := state.NamespaceByName(nil, ns1.Name)
return out != nil, err
}, func(err error) {
t.Fatalf("should replicate namespace")
})
// Create a job in the namespace on the non-authority
j := mock.Job()
j.Namespace = ns1.Name
assert.Nil(s2.fsm.State().UpsertJob(structs.MsgTypeTestSetup, 1001, j))
// Delete the namespaces without the correct permissions
req := &structs.NamespaceDeleteRequest{
Namespaces: []string{ns1.Name},
WriteRequest: structs.WriteRequest{
Region: "global",
},
}
var resp structs.GenericResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
if assert.NotNil(err) {
assert.EqualError(err, structs.ErrPermissionDenied.Error())
}
// Try with a auth token
req.AuthToken = root.SecretID
var resp2 structs.GenericResponse
err = msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp2)
if assert.NotNil(err) {
assert.Contains(err.Error(), "has non-terminal jobs")
}
}
func TestNamespaceEndpoint_DeleteNamespaces_ACL(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, root, cleanupS1 := TestACLServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
ns1 := mock.Namespace()
ns2 := mock.Namespace()
state := s1.fsm.State()
s1.fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})
// Create the policy and tokens
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
req := &structs.NamespaceDeleteRequest{
Namespaces: []string{ns1.Name, ns2.Name},
WriteRequest: structs.WriteRequest{Region: "global"},
}
// Delete namespaces without a token and expect failure
{
var resp structs.GenericResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
// Check we did not delete the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.NotNil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.NotNil(out)
}
// Try with an invalid token
req.AuthToken = invalidToken.SecretID
{
var resp structs.GenericResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
// Check we did not delete the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.NotNil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.NotNil(out)
}
// Try with a root token
req.AuthToken = root.SecretID
{
var resp structs.GenericResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp))
assert.NotEqual(uint64(0), resp.Index)
// Check we deleted the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.Nil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.Nil(out)
}
}
func TestNamespaceEndpoint_DeleteNamespaces_Default(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Delete the default namespace
req := &structs.NamespaceDeleteRequest{
Namespaces: []string{structs.DefaultNamespace},
WriteRequest: structs.WriteRequest{Region: "global"},
}
var resp structs.GenericResponse
assert.NotNil(msgpackrpc.CallWithCodec(codec, "Namespace.DeleteNamespaces", req, &resp))
}
func TestNamespaceEndpoint_UpsertNamespaces(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, cleanupS1 := TestServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the register request
ns1 := mock.Namespace()
ns2 := mock.Namespace()
// Lookup the namespaces
req := &structs.NamespaceUpsertRequest{
Namespaces: []*structs.Namespace{ns1, ns2},
WriteRequest: structs.WriteRequest{Region: "global"},
}
var resp structs.GenericResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp))
assert.NotEqual(uint64(0), resp.Index)
// Check we created the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.NotNil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.NotNil(out)
}
func TestNamespaceEndpoint_UpsertNamespaces_ACL(t *testing.T) {
ci.Parallel(t)
assert := assert.New(t)
s1, root, cleanupS1 := TestACLServer(t, nil)
defer cleanupS1()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
ns1 := mock.Namespace()
ns2 := mock.Namespace()
state := s1.fsm.State()
// Create the policy and tokens
invalidToken := mock.CreatePolicyAndToken(t, state, 1003, "test-invalid",
mock.NamespacePolicy(structs.DefaultNamespace, "", []string{acl.NamespaceCapabilityReadJob}))
// Create the register request
req := &structs.NamespaceUpsertRequest{
Namespaces: []*structs.Namespace{ns1, ns2},
WriteRequest: structs.WriteRequest{Region: "global"},
}
// Upsert the namespace without a token and expect failure
{
var resp structs.GenericResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
// Check we did not create the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.Nil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.Nil(out)
}
// Try with an invalid token
req.AuthToken = invalidToken.SecretID
{
var resp structs.GenericResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
// Check we did not create the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.Nil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.Nil(out)
}
// Try with a bogus token
req.AuthToken = uuid.Generate()
{
var resp structs.GenericResponse
err := msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp)
assert.NotNil(err)
assert.Equal(err.Error(), structs.ErrPermissionDenied.Error())
// Check we did not create the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.Nil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.Nil(out)
}
// Try with a root token
req.AuthToken = root.SecretID
{
var resp structs.GenericResponse
assert.Nil(msgpackrpc.CallWithCodec(codec, "Namespace.UpsertNamespaces", req, &resp))
assert.NotEqual(uint64(0), resp.Index)
// Check we created the namespaces
out, err := s1.fsm.State().NamespaceByName(nil, ns1.Name)
assert.Nil(err)
assert.NotNil(out)
out, err = s1.fsm.State().NamespaceByName(nil, ns2.Name)
assert.Nil(err)
assert.NotNil(out)
}
}