mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
234 lines
6.5 KiB
Go
234 lines
6.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package taskrunner
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/hashicorp/nomad/client/allocrunner/interfaces"
|
|
"github.com/hashicorp/nomad/helper/testlog"
|
|
"github.com/hashicorp/nomad/helper/users/dynamic"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/shoenig/test/must"
|
|
)
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Prestart_unusable(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
// task driver does not indicate DynamicWorkloadUsers capability
|
|
const capable = false
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// if the driver does not indicate the DynamicWorkloadUsers capability,
|
|
// none of the pool, request, or response are touched - so using nil
|
|
// for each of them shows we are exiting the hook immediatly
|
|
var pool dynamic.Pool = nil
|
|
var request *interfaces.TaskPrestartRequest = nil
|
|
var response *interfaces.TaskPrestartResponse = nil
|
|
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.False(t, h.usable)
|
|
must.NoError(t, h.Prestart(ctx, request, response))
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Prestart_State(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = false
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
var pool dynamic.Pool = nil
|
|
request := &interfaces.TaskPrestartRequest{
|
|
Task: &structs.Task{},
|
|
PreviousState: map[string]string{
|
|
dynamicUsersStateKey: "1",
|
|
},
|
|
}
|
|
|
|
response := &interfaces.TaskPrestartResponse{}
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
|
|
// mark as usable
|
|
h.usable = true
|
|
|
|
must.NoError(t, h.Prestart(ctx, request, response))
|
|
|
|
// make sure the user exists in the state
|
|
// by the dynamic user key
|
|
user, ok := response.State[dynamicUsersStateKey]
|
|
must.True(t, ok)
|
|
must.Eq(t, "1", user)
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Prestart_unnecessary(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = true
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// if the task configures a user, no dynamic workload user will be allocated
|
|
// and we prove this by setting a nil pool
|
|
var pool dynamic.Pool = nil
|
|
var response = new(interfaces.TaskPrestartResponse)
|
|
var request = &interfaces.TaskPrestartRequest{
|
|
Task: &structs.Task{User: "billy"},
|
|
}
|
|
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.True(t, h.usable)
|
|
must.NoError(t, h.Prestart(ctx, request, response))
|
|
must.MapEmpty(t, response.State) // no user set
|
|
must.Eq(t, "billy", request.Task.User) // not modified
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Prestart_used(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = true
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// create a pool allowing UIDs in range [100, 199]
|
|
var pool dynamic.Pool = dynamic.New(&dynamic.PoolConfig{
|
|
MinUGID: 100,
|
|
MaxUGID: 199,
|
|
})
|
|
var response = new(interfaces.TaskPrestartResponse)
|
|
var request = &interfaces.TaskPrestartRequest{
|
|
Task: &structs.Task{User: ""}, // user is not set
|
|
}
|
|
|
|
// once the hook runs, check we got an expected ugid and the
|
|
// task user is set to our pseudo dynamic username
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.True(t, h.usable)
|
|
must.NoError(t, h.Prestart(ctx, request, response))
|
|
username, exists := response.State[dynamicUsersStateKey]
|
|
must.True(t, exists)
|
|
ugid, err := dynamic.Parse(username)
|
|
must.NoError(t, err)
|
|
must.Between(t, 100, ugid, 199)
|
|
must.Eq(t, username, request.Task.User)
|
|
must.StrHasPrefix(t, "nomad-", username)
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Prestart_exhausted(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = true
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// create a pool allowing UIDs in range [100, 199]
|
|
var pool dynamic.Pool = dynamic.New(&dynamic.PoolConfig{
|
|
MinUGID: 100,
|
|
MaxUGID: 101,
|
|
})
|
|
pool.Restore(100)
|
|
pool.Restore(101)
|
|
var response = new(interfaces.TaskPrestartResponse)
|
|
var request = &interfaces.TaskPrestartRequest{
|
|
Task: &structs.Task{User: ""}, // user is not set
|
|
}
|
|
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.True(t, h.usable)
|
|
must.ErrorContains(t, h.Prestart(ctx, request, response), "uid/gid pool exhausted")
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Stop_unusable(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = false
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// prove we use none of these by setting them all to nil
|
|
var pool dynamic.Pool = nil
|
|
var request *interfaces.TaskStopRequest = nil
|
|
var response *interfaces.TaskStopResponse = nil
|
|
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.False(t, h.usable)
|
|
must.NoError(t, h.Stop(ctx, request, response))
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Stop_release(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = true
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// prove we use none of these by setting them all to nil
|
|
var pool dynamic.Pool = dynamic.New(&dynamic.PoolConfig{
|
|
MinUGID: 100,
|
|
MaxUGID: 199,
|
|
})
|
|
pool.Restore(150) // allocate ugid 150
|
|
var request = &interfaces.TaskStopRequest{
|
|
ExistingState: map[string]string{
|
|
dynamicUsersStateKey: "nomad-150",
|
|
},
|
|
}
|
|
var response = new(interfaces.TaskStopResponse)
|
|
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.True(t, h.usable)
|
|
must.NoError(t, h.Stop(ctx, request, response))
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Stop_malformed(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = true
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// prove we use none of these by setting them all to nil
|
|
var pool dynamic.Pool = dynamic.New(&dynamic.PoolConfig{
|
|
MinUGID: 100,
|
|
MaxUGID: 199,
|
|
})
|
|
var request = &interfaces.TaskStopRequest{
|
|
ExistingState: map[string]string{
|
|
dynamicUsersStateKey: "not-valid",
|
|
},
|
|
}
|
|
var response = new(interfaces.TaskStopResponse)
|
|
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.True(t, h.usable)
|
|
must.ErrorContains(t, h.Stop(ctx, request, response), "unable to parse uid/gid from username")
|
|
}
|
|
|
|
func TestTaskRunner_DynamicUsersHook_Stop_not_in_use(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
const capable = true
|
|
ctx := context.Background()
|
|
logger := testlog.HCLogger(t)
|
|
|
|
// prove we use none of these by setting them all to nil
|
|
var pool dynamic.Pool = dynamic.New(&dynamic.PoolConfig{
|
|
MinUGID: 100,
|
|
MaxUGID: 199,
|
|
})
|
|
var request = &interfaces.TaskStopRequest{
|
|
ExistingState: map[string]string{
|
|
dynamicUsersStateKey: "nomad-101",
|
|
},
|
|
}
|
|
var response = new(interfaces.TaskStopResponse)
|
|
|
|
h := newDynamicUsersHook(ctx, capable, logger, pool)
|
|
must.True(t, h.usable)
|
|
must.ErrorContains(t, h.Stop(ctx, request, response), "release of unused uid/gid")
|
|
}
|