open-nomad/nomad/deploymentwatcher/testutil_test.go
Drew Bailey 6c788fdccd
Events/msgtype cleanup (#9117)
* use msgtype in upsert node

adds message type to signature for upsert node, update tests, remove placeholder method

* UpsertAllocs msg type test setup

* use upsertallocs with msg type in signature

update test usage of delete node

delete placeholder msgtype method

* add msgtype to upsert evals signature, update test call sites with test setup msg type

handle snapshot upsert eval outside of FSM and ignore eval event

remove placeholder upsertevalsmsgtype

handle job plan rpc and prevent event creation for plan

msgtype cleanup upsertnodeevents

updatenodedrain msgtype

msg type 0 is a node registration event, so set the default  to the ignore type

* fix named import

* fix signature ordering on upsertnode to match
2020-10-19 09:30:15 -04:00

270 lines
7.1 KiB
Go

package deploymentwatcher
import (
"reflect"
"strings"
"sync"
"testing"
"github.com/hashicorp/nomad/nomad/state"
"github.com/hashicorp/nomad/nomad/structs"
mocker "github.com/stretchr/testify/mock"
)
type mockBackend struct {
mocker.Mock
index uint64
state *state.StateStore
l sync.Mutex
}
func newMockBackend(t *testing.T) *mockBackend {
m := &mockBackend{
index: 10000,
state: state.TestStateStore(t),
}
m.Test(t)
return m
}
func (m *mockBackend) nextIndex() uint64 {
m.l.Lock()
defer m.l.Unlock()
i := m.index
m.index++
return i
}
func (m *mockBackend) UpdateAllocDesiredTransition(u *structs.AllocUpdateDesiredTransitionRequest) (uint64, error) {
m.Called(u)
i := m.nextIndex()
return i, m.state.UpdateAllocsDesiredTransitions(structs.MsgTypeTestSetup, i, u.Allocs, u.Evals)
}
// matchUpdateAllocDesiredTransitions is used to match an upsert request
func matchUpdateAllocDesiredTransitions(deploymentIDs []string) func(update *structs.AllocUpdateDesiredTransitionRequest) bool {
return func(update *structs.AllocUpdateDesiredTransitionRequest) bool {
if len(update.Evals) != len(deploymentIDs) {
return false
}
dmap := make(map[string]struct{}, len(deploymentIDs))
for _, d := range deploymentIDs {
dmap[d] = struct{}{}
}
for _, e := range update.Evals {
if _, ok := dmap[e.DeploymentID]; !ok {
return false
}
delete(dmap, e.DeploymentID)
}
return true
}
}
// matchUpdateAllocDesiredTransitionReschedule is used to match allocs that have their DesiredTransition set to Reschedule
func matchUpdateAllocDesiredTransitionReschedule(allocIDs []string) func(update *structs.AllocUpdateDesiredTransitionRequest) bool {
return func(update *structs.AllocUpdateDesiredTransitionRequest) bool {
amap := make(map[string]struct{}, len(allocIDs))
for _, d := range allocIDs {
amap[d] = struct{}{}
}
for allocID, dt := range update.Allocs {
if _, ok := amap[allocID]; !ok {
return false
}
if !*dt.Reschedule {
return false
}
}
return true
}
}
func (m *mockBackend) UpsertJob(job *structs.Job) (uint64, error) {
m.Called(job)
i := m.nextIndex()
return i, m.state.UpsertJob(structs.MsgTypeTestSetup, i, job)
}
func (m *mockBackend) UpdateDeploymentStatus(u *structs.DeploymentStatusUpdateRequest) (uint64, error) {
m.Called(u)
i := m.nextIndex()
return i, m.state.UpdateDeploymentStatus(structs.MsgTypeTestSetup, i, u)
}
// matchDeploymentStatusUpdateConfig is used to configure the matching
// function
type matchDeploymentStatusUpdateConfig struct {
// DeploymentID is the expected ID
DeploymentID string
// Status is the desired status
Status string
// StatusDescription is the desired status description
StatusDescription string
// JobVersion marks whether we expect a roll back job at the given version
JobVersion *uint64
// Eval marks whether we expect an evaluation.
Eval bool
}
// matchDeploymentStatusUpdateRequest is used to match an update request
func matchDeploymentStatusUpdateRequest(c *matchDeploymentStatusUpdateConfig) func(args *structs.DeploymentStatusUpdateRequest) bool {
return func(args *structs.DeploymentStatusUpdateRequest) bool {
if args.DeploymentUpdate.DeploymentID != c.DeploymentID {
return false
}
if args.DeploymentUpdate.Status != c.Status && args.DeploymentUpdate.StatusDescription != c.StatusDescription {
return false
}
if c.Eval && args.Eval == nil || !c.Eval && args.Eval != nil {
return false
}
if c.JobVersion != nil {
if args.Job == nil {
return false
} else if args.Job.Version != *c.JobVersion {
return false
}
} else if c.JobVersion == nil && args.Job != nil {
return false
}
return true
}
}
func (m *mockBackend) UpdateDeploymentPromotion(req *structs.ApplyDeploymentPromoteRequest) (uint64, error) {
m.Called(req)
i := m.nextIndex()
return i, m.state.UpdateDeploymentPromotion(structs.MsgTypeTestSetup, i, req)
}
// matchDeploymentPromoteRequestConfig is used to configure the matching
// function
type matchDeploymentPromoteRequestConfig struct {
// Promotion holds the expected promote request
Promotion *structs.DeploymentPromoteRequest
// Eval marks whether we expect an evaluation.
Eval bool
}
// matchDeploymentPromoteRequest is used to match a promote request
func matchDeploymentPromoteRequest(c *matchDeploymentPromoteRequestConfig) func(args *structs.ApplyDeploymentPromoteRequest) bool {
return func(args *structs.ApplyDeploymentPromoteRequest) bool {
if !reflect.DeepEqual(*c.Promotion, args.DeploymentPromoteRequest) {
return false
}
if c.Eval && args.Eval == nil || !c.Eval && args.Eval != nil {
return false
}
return true
}
}
func (m *mockBackend) UpdateDeploymentAllocHealth(req *structs.ApplyDeploymentAllocHealthRequest) (uint64, error) {
m.Called(req)
i := m.nextIndex()
return i, m.state.UpdateDeploymentAllocHealth(structs.MsgTypeTestSetup, i, req)
}
// matchDeploymentAllocHealthRequestConfig is used to configure the matching
// function
type matchDeploymentAllocHealthRequestConfig struct {
// DeploymentID is the expected ID
DeploymentID string
// Healthy and Unhealthy contain the expected allocation IDs that are having
// their health set
Healthy, Unhealthy []string
// DeploymentUpdate holds the expected values of status and description. We
// don't check for exact match but string contains
DeploymentUpdate *structs.DeploymentStatusUpdate
// JobVersion marks whether we expect a roll back job at the given version
JobVersion *uint64
// Eval marks whether we expect an evaluation.
Eval bool
}
// matchDeploymentAllocHealthRequest is used to match an update request
func matchDeploymentAllocHealthRequest(c *matchDeploymentAllocHealthRequestConfig) func(args *structs.ApplyDeploymentAllocHealthRequest) bool {
return func(args *structs.ApplyDeploymentAllocHealthRequest) bool {
if args.DeploymentID != c.DeploymentID {
return false
}
// Require a timestamp
if args.Timestamp.IsZero() {
return false
}
if len(c.Healthy) != len(args.HealthyAllocationIDs) {
return false
}
if len(c.Unhealthy) != len(args.UnhealthyAllocationIDs) {
return false
}
hmap, umap := make(map[string]struct{}, len(c.Healthy)), make(map[string]struct{}, len(c.Unhealthy))
for _, h := range c.Healthy {
hmap[h] = struct{}{}
}
for _, u := range c.Unhealthy {
umap[u] = struct{}{}
}
for _, h := range args.HealthyAllocationIDs {
if _, ok := hmap[h]; !ok {
return false
}
}
for _, u := range args.UnhealthyAllocationIDs {
if _, ok := umap[u]; !ok {
return false
}
}
if c.DeploymentUpdate != nil {
if args.DeploymentUpdate == nil {
return false
}
if !strings.Contains(args.DeploymentUpdate.Status, c.DeploymentUpdate.Status) {
return false
}
if !strings.Contains(args.DeploymentUpdate.StatusDescription, c.DeploymentUpdate.StatusDescription) {
return false
}
} else if args.DeploymentUpdate != nil {
return false
}
if c.Eval && args.Eval == nil || !c.Eval && args.Eval != nil {
return false
}
if (c.JobVersion != nil && (args.Job == nil || args.Job.Version != *c.JobVersion)) || c.JobVersion == nil && args.Job != nil {
return false
}
return true
}
}