open-nomad/nomad/namespace_endpoint_test.go
Seth Hoenig ba728f8f97
api: enable support for setting original job source (#16763)
* api: enable support for setting original source alongside job

This PR adds support for setting job source material along with
the registration of a job.

This includes a new HTTP endpoint and a new RPC endpoint for
making queries for the original source of a job. The
HTTP endpoint is /v1/job/<id>/submission?version=<version> and
the RPC method is Job.GetJobSubmission.

The job source (if submitted, and doing so is always optional), is
stored in the job_submission memdb table, separately from the
actual job. This way we do not incur overhead of reading the large
string field throughout normal job operations.

The server config now includes job_max_source_size for configuring
the maximum size the job source may be, before the server simply
drops the source material. This should help prevent Bad Things from
happening when huge jobs are submitted. If the value is set to 0,
all job source material will be dropped.

* api: avoid writing var content to disk for parsing

* api: move submission validation into RPC layer

* api: return an error if updating a job submission without namespace or job id

* api: be exact about the job index we associate a submission with (modify)

* api: reword api docs scheduling

* api: prune all but the last 6 job submissions

* api: protect against nil job submission in job validation

* api: set max job source size in test server

* api: fixups from pr
2023-04-11 08:45:08 -05:00

795 lines
24 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
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, nil, 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, nil, 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)
}
}