This commit is contained in:
Alex Dadgar 2016-08-19 13:13:51 -07:00
parent 10cd844ca8
commit 909f552d9e
4 changed files with 267 additions and 2 deletions

View File

@ -770,6 +770,54 @@ func TestFSM_UpdateAllocFromClient(t *testing.T) {
}
}
func TestFSM_UpsertVaultAccessor(t *testing.T) {
fsm := testFSM(t)
fsm.blockedEvals.SetEnabled(true)
va := mock.VaultAccessor()
va2 := mock.VaultAccessor()
req := structs.VaultAccessorRegisterRequest{
Accessors: []*structs.VaultAccessor{va, va2},
}
buf, err := structs.Encode(structs.VaultAccessorRegisterRequestType, req)
if err != nil {
t.Fatalf("err: %v", err)
}
resp := fsm.Apply(makeLog(buf))
if resp != nil {
t.Fatalf("resp: %v", resp)
}
// Verify we are registered
out1, err := fsm.State().VaultAccessor(va.Accessor)
if err != nil {
t.Fatalf("err: %v", err)
}
if out1 == nil {
t.Fatalf("not found!")
}
if out1.CreateIndex != 1 {
t.Fatalf("bad index: %d", out1.CreateIndex)
}
out2, err := fsm.State().VaultAccessor(va2.Accessor)
if err != nil {
t.Fatalf("err: %v", err)
}
if out2 == nil {
t.Fatalf("not found!")
}
if out1.CreateIndex != 1 {
t.Fatalf("bad index: %d", out2.CreateIndex)
}
tt := fsm.TimeTable()
index := tt.NearestIndex(time.Now().UTC())
if index != 1 {
t.Fatalf("bad: %d", index)
}
}
func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM {
// Snapshot
snap, err := fsm.Snapshot()

View File

@ -936,6 +936,9 @@ func (n *Node) DeriveVaultToken(args *structs.DeriveVaultTokenRequest,
if alloc.NodeID != args.NodeID {
return fmt.Errorf("Allocation %q not running on Node %q", args.AllocID, args.NodeID)
}
if alloc.TerminalStatus() {
return fmt.Errorf("Can't request Vault token for terminal allocation")
}
// Check the policies
policies := alloc.Job.VaultPolicies()
@ -950,7 +953,7 @@ func (n *Node) DeriveVaultToken(args *structs.DeriveVaultTokenRequest,
var unneeded []string
for _, task := range args.Tasks {
taskVault := tg[task]
if len(taskVault.Policies) == 0 {
if taskVault == nil || len(taskVault.Policies) == 0 {
unneeded = append(unneeded, task)
}
}

View File

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
vapi "github.com/hashicorp/vault/api"
)
func TestClientEndpoint_Register(t *testing.T) {
@ -1597,3 +1598,160 @@ func TestBatchFuture(t *testing.T) {
t.Fatalf("bad: %d", bf.Index())
}
}
func TestClientEndpoint_DeriveVaultToken_Bad(t *testing.T) {
s1 := testServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Create the node
node := mock.Node()
if err := state.UpsertNode(2, node); err != nil {
t.Fatalf("err: %v", err)
}
// Create an alloc
alloc := mock.Alloc()
task := alloc.Job.TaskGroups[0].Tasks[0]
tasks := []string{task.Name}
if err := state.UpsertAllocs(3, []*structs.Allocation{alloc}); err != nil {
t.Fatalf("err: %v", err)
}
req := &structs.DeriveVaultTokenRequest{
NodeID: node.ID,
SecretID: structs.GenerateUUID(),
AllocID: alloc.ID,
Tasks: tasks,
QueryOptions: structs.QueryOptions{
Region: "global",
},
}
var resp structs.DeriveVaultTokenResponse
err := msgpackrpc.CallWithCodec(codec, "Node.DeriveVaultToken", req, &resp)
if err == nil || !strings.Contains(err.Error(), "SecretID mismatch") {
t.Fatalf("Expected SecretID mismatch: %v", err)
}
// Put the correct SecretID
req.SecretID = node.SecretID
// Now we should get an error about the allocation not running on the node
err = msgpackrpc.CallWithCodec(codec, "Node.DeriveVaultToken", req, &resp)
if err == nil || !strings.Contains(err.Error(), "not running on Node") {
t.Fatalf("Expected not running on node error: %v", err)
}
// Update to be running on the node
alloc.NodeID = node.ID
if err := state.UpsertAllocs(4, []*structs.Allocation{alloc}); err != nil {
t.Fatalf("err: %v", err)
}
// Now we should get an error about the job not needing any Vault secrets
err = msgpackrpc.CallWithCodec(codec, "Node.DeriveVaultToken", req, &resp)
if err == nil || !strings.Contains(err.Error(), "without defined Vault") {
t.Fatalf("Expected no policies error: %v", err)
}
// Update to be terminal
alloc.DesiredStatus = structs.AllocDesiredStatusStop
if err := state.UpsertAllocs(5, []*structs.Allocation{alloc}); err != nil {
t.Fatalf("err: %v", err)
}
// Now we should get an error about the job not needing any Vault secrets
err = msgpackrpc.CallWithCodec(codec, "Node.DeriveVaultToken", req, &resp)
if err == nil || !strings.Contains(err.Error(), "terminal") {
t.Fatalf("Expected terminal allocation error: %v", err)
}
}
func TestClientEndpoint_DeriveVaultToken(t *testing.T) {
s1 := testServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
// Enable vault and allow authenticated
s1.config.VaultConfig.Enabled = true
s1.config.VaultConfig.AllowUnauthenticated = true
// Replace the Vault Client on the server
tvc := &TestVaultClient{}
s1.vault = tvc
// Create the node
node := mock.Node()
if err := state.UpsertNode(2, node); err != nil {
t.Fatalf("err: %v", err)
}
// Create an alloc an allocation that has vault policies required
alloc := mock.Alloc()
alloc.NodeID = node.ID
task := alloc.Job.TaskGroups[0].Tasks[0]
tasks := []string{task.Name}
task.Vault = &structs.Vault{Policies: []string{"a", "b"}}
if err := state.UpsertAllocs(3, []*structs.Allocation{alloc}); err != nil {
t.Fatalf("err: %v", err)
}
// Return a secret for the task
token := structs.GenerateUUID()
accessor := structs.GenerateUUID()
ttl := 10
secret := &vapi.Secret{
WrapInfo: &vapi.SecretWrapInfo{
Token: token,
WrappedAccessor: accessor,
TTL: ttl,
},
}
tvc.SetCreateTokenSecret(alloc.ID, task.Name, secret)
req := &structs.DeriveVaultTokenRequest{
NodeID: node.ID,
SecretID: node.SecretID,
AllocID: alloc.ID,
Tasks: tasks,
QueryOptions: structs.QueryOptions{
Region: "global",
},
}
var resp structs.DeriveVaultTokenResponse
if err := msgpackrpc.CallWithCodec(codec, "Node.DeriveVaultToken", req, &resp); err != nil {
t.Fatalf("bad: %v", err)
}
// Check the state store and ensure that we created a VaultAccessor
va, err := state.VaultAccessor(accessor)
if err != nil {
t.Fatalf("bad: %v", err)
}
if va == nil {
t.Fatalf("bad: %v", va)
}
if va.CreateIndex == 0 {
t.Fatalf("bad: %v", va)
}
va.CreateIndex = 0
expected := &structs.VaultAccessor{
AllocID: alloc.ID,
Task: task.Name,
NodeID: alloc.NodeID,
Accessor: accessor,
CreationTTL: ttl,
}
if !reflect.DeepEqual(expected, va) {
t.Fatalf("Got %#v; want %#v", va, expected)
}
}

View File

@ -18,6 +18,14 @@ type TestVaultClient struct {
// LookupTokenSecret maps a token to the Vault secret that will be returned
// by the LookupToken call
LookupTokenSecret map[string]*vapi.Secret
// CreateTokenErrors maps a token to an error that will be returned by the
// CreateToken call
CreateTokenErrors map[string]map[string]error
// CreateTokenSecret maps a token to the Vault secret that will be returned
// by the CreateToken call
CreateTokenSecret map[string]map[string]*vapi.Secret
}
func (v *TestVaultClient) LookupToken(ctx context.Context, token string) (*vapi.Secret, error) {
@ -67,7 +75,55 @@ func (v *TestVaultClient) SetLookupTokenAllowedPolicies(token string, policies [
}
func (v *TestVaultClient) CreateToken(ctx context.Context, a *structs.Allocation, task string) (*vapi.Secret, error) {
return nil, nil
var secret *vapi.Secret
var err error
if v.CreateTokenSecret != nil {
tasks := v.CreateTokenSecret[a.ID]
if tasks != nil {
secret = tasks[task]
}
}
if v.CreateTokenErrors != nil {
tasks := v.CreateTokenErrors[a.ID]
if tasks != nil {
err = tasks[task]
}
}
return secret, err
}
// SetCreateTokenError sets the error that will be returned by the token
// creation
func (v *TestVaultClient) SetCreateTokenError(allocID, task string, err error) {
if v.CreateTokenErrors == nil {
v.CreateTokenErrors = make(map[string]map[string]error)
}
tasks := v.CreateTokenErrors[allocID]
if tasks == nil {
tasks = make(map[string]error)
v.CreateTokenErrors[allocID] = tasks
}
v.CreateTokenErrors[allocID][task] = err
}
// SetCreateTokenSecret sets the secret that will be returned by the token
// creation
func (v *TestVaultClient) SetCreateTokenSecret(allocID, task string, secret *vapi.Secret) {
if v.CreateTokenSecret == nil {
v.CreateTokenSecret = make(map[string]map[string]*vapi.Secret)
}
tasks := v.CreateTokenSecret[allocID]
if tasks == nil {
tasks = make(map[string]*vapi.Secret)
v.CreateTokenSecret[allocID] = tasks
}
v.CreateTokenSecret[allocID][task] = secret
}
func (v *TestVaultClient) Stop() {}