// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package state import ( "testing" "time" "github.com/hashicorp/go-memdb" "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/shoenig/test/must" ) func TestStateStore_RestoreNode(t *testing.T) { ci.Parallel(t) state := testStateStore(t) node := mock.Node() restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.NodeRestore(node)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.NodeByID(ws, node.ID) must.NoError(t, err) must.Eq(t, node, out) } func TestStateStore_RestoreJob(t *testing.T) { ci.Parallel(t) state := testStateStore(t) mockJob1 := mock.Job() restore, err := state.Restore() must.NoError(t, err) err = restore.JobRestore(mockJob1) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.JobByID(ws, mockJob1.Namespace, mockJob1.ID) must.NoError(t, err) must.Eq(t, mockJob1, out) // Test upgrade to 1.6 or greater to simulate restoring a job which does // not have a node pool set. mockJob2 := mock.Job() mockJob2.NodePool = "" restore, err = state.Restore() must.NoError(t, err) err = restore.JobRestore(mockJob2) must.NoError(t, err) must.NoError(t, restore.Commit()) ws = memdb.NewWatchSet() out, err = state.JobByID(ws, mockJob2.Namespace, mockJob2.ID) must.NoError(t, err) must.Eq(t, structs.NodePoolDefault, out.NodePool) } func TestStateStore_RestorePeriodicLaunch(t *testing.T) { ci.Parallel(t) state := testStateStore(t) job := mock.Job() launch := &structs.PeriodicLaunch{ ID: job.ID, Namespace: job.Namespace, Launch: time.Now(), } restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.PeriodicLaunchRestore(launch)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.PeriodicLaunchByID(ws, job.Namespace, job.ID) must.NoError(t, err) must.Eq(t, launch, out) must.False(t, watchFired(ws)) } func TestStateStore_RestoreJobVersion(t *testing.T) { ci.Parallel(t) state := testStateStore(t) job := mock.Job() restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.JobVersionRestore(job)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.JobByIDAndVersion(ws, job.Namespace, job.ID, job.Version) must.NoError(t, err) must.Eq(t, job, out) must.False(t, watchFired(ws)) } func TestStateStore_RestoreDeployment(t *testing.T) { ci.Parallel(t) state := testStateStore(t) d := mock.Deployment() restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.DeploymentRestore(d)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.DeploymentByID(ws, d.ID) must.NoError(t, err) must.Eq(t, d, out) must.False(t, watchFired(ws)) } func TestStateStore_RestoreJobSummary(t *testing.T) { ci.Parallel(t) state := testStateStore(t) job := mock.Job() jobSummary := &structs.JobSummary{ JobID: job.ID, Namespace: job.Namespace, Summary: map[string]structs.TaskGroupSummary{ "web": { Starting: 10, }, }, } restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.JobSummaryRestore(jobSummary)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.JobSummaryByID(ws, job.Namespace, job.ID) must.NoError(t, err) must.Eq(t, jobSummary, out) must.False(t, watchFired(ws)) } func TestStateStore_RestoreCSIPlugin(t *testing.T) { ci.Parallel(t) state := testStateStore(t) plugin := mock.CSIPlugin() restore, err := state.Restore() must.NoError(t, err) err = restore.CSIPluginRestore(plugin) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.CSIPluginByID(ws, plugin.ID) must.NoError(t, err) must.Eq(t, plugin, out) must.False(t, watchFired(ws)) } func TestStateStore_RestoreCSIVolume(t *testing.T) { ci.Parallel(t) state := testStateStore(t) plugin := mock.CSIPlugin() volume := mock.CSIVolume(plugin) restore, err := state.Restore() must.NoError(t, err) err = restore.CSIVolumeRestore(volume) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.CSIVolumeByID(ws, "default", volume.ID) must.NoError(t, err) must.Eq(t, volume, out) } func TestStateStore_RestoreIndex(t *testing.T) { ci.Parallel(t) state := testStateStore(t) restore, err := state.Restore() must.NoError(t, err) index := &IndexEntry{"jobs", 1000} must.NoError(t, restore.IndexRestore(index)) must.NoError(t, restore.Commit()) out, err := state.Index("jobs") must.NoError(t, err) must.Eq(t, 1000, out) } func TestStateStore_RestoreEval(t *testing.T) { ci.Parallel(t) state := testStateStore(t) eval := mock.Eval() restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.EvalRestore(eval)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.EvalByID(ws, eval.ID) must.NoError(t, err) must.Eq(t, eval, out) } func TestStateStore_RestoreAlloc(t *testing.T) { ci.Parallel(t) state := testStateStore(t) alloc := mock.Alloc() restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.AllocRestore(alloc)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.AllocByID(ws, alloc.ID) must.NoError(t, err) must.Eq(t, alloc, out) must.False(t, watchFired(ws)) } func TestStateStore_RestoreVaultAccessor(t *testing.T) { ci.Parallel(t) state := testStateStore(t) a := mock.VaultAccessor() restore, err := state.Restore() must.NoError(t, err) err = restore.VaultAccessorRestore(a) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.VaultAccessor(ws, a.Accessor) must.NoError(t, err) must.Eq(t, a, out) must.False(t, watchFired(ws)) } func TestStateStore_RestoreSITokenAccessor(t *testing.T) { ci.Parallel(t) state := testStateStore(t) a1 := mock.SITokenAccessor() restore, err := state.Restore() must.NoError(t, err) must.NoError(t, restore.SITokenAccessorRestore(a1)) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() result, err := state.SITokenAccessor(ws, a1.AccessorID) must.NoError(t, err) must.Eq(t, a1, result) must.False(t, watchFired(ws)) } func TestStateStore_RestoreACLPolicy(t *testing.T) { ci.Parallel(t) state := testStateStore(t) policy := mock.ACLPolicy() restore, err := state.Restore() must.NoError(t, err) err = restore.ACLPolicyRestore(policy) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.ACLPolicyByName(ws, policy.Name) must.NoError(t, err) must.Eq(t, policy, out) } func TestStateStore_RestoreACLToken(t *testing.T) { ci.Parallel(t) state := testStateStore(t) token := mock.ACLToken() restore, err := state.Restore() must.NoError(t, err) err = restore.ACLTokenRestore(token) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.ACLTokenByAccessorID(ws, token.AccessorID) must.NoError(t, err) must.Eq(t, token, out) } func TestStateStore_ClusterMetadataRestore(t *testing.T) { ci.Parallel(t) state := testStateStore(t) clusterID := "12345678-1234-1234-1234-1234567890" now := time.Now().UnixNano() meta := &structs.ClusterMetadata{ClusterID: clusterID, CreateTime: now} restore, err := state.Restore() must.NoError(t, err) err = restore.ClusterMetadataRestore(meta) must.NoError(t, err) must.NoError(t, restore.Commit()) out, err := state.ClusterMetadata(nil) must.NoError(t, err) must.Eq(t, clusterID, out.ClusterID) must.Eq(t, now, out.CreateTime) } func TestStateStore_RestoreScalingPolicy(t *testing.T) { ci.Parallel(t) state := testStateStore(t) scalingPolicy := mock.ScalingPolicy() restore, err := state.Restore() must.NoError(t, err) err = restore.ScalingPolicyRestore(scalingPolicy) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, err := state.ScalingPolicyByID(ws, scalingPolicy.ID) must.NoError(t, err) must.Eq(t, scalingPolicy, out) } func TestStateStore_RestoreScalingEvents(t *testing.T) { ci.Parallel(t) state := testStateStore(t) jobScalingEvents := &structs.JobScalingEvents{ Namespace: uuid.Generate(), JobID: uuid.Generate(), ScalingEvents: map[string][]*structs.ScalingEvent{ uuid.Generate(): { structs.NewScalingEvent(uuid.Generate()), }, }, } restore, err := state.Restore() must.NoError(t, err) err = restore.ScalingEventsRestore(jobScalingEvents) must.NoError(t, err) must.NoError(t, restore.Commit()) ws := memdb.NewWatchSet() out, _, err := state.ScalingEventsByJob(ws, jobScalingEvents.Namespace, jobScalingEvents.JobID) must.NoError(t, err) must.NotNil(t, out) must.Eq(t, jobScalingEvents.ScalingEvents, out) } func TestStateStore_RestoreSchedulerConfig(t *testing.T) { ci.Parallel(t) state := testStateStore(t) schedConfig := &structs.SchedulerConfiguration{ PreemptionConfig: structs.PreemptionConfig{ SystemSchedulerEnabled: false, }, CreateIndex: 100, ModifyIndex: 200, } restore, err := state.Restore() must.NoError(t, err) err = restore.SchedulerConfigRestore(schedConfig) must.NoError(t, err) must.NoError(t, restore.Commit()) modIndex, out, err := state.SchedulerConfig() must.NoError(t, err) must.Eq(t, schedConfig.ModifyIndex, modIndex) must.Eq(t, schedConfig, out) } func TestStateStore_ServiceRegistrationRestore(t *testing.T) { ci.Parallel(t) testState := testStateStore(t) // Set up our test registrations and index. expectedIndex := uint64(13) serviceRegs := mock.ServiceRegistrations() restore, err := testState.Restore() must.NoError(t, err) // Iterate the service registrations, restore, and commit. Set the indexes // on the objects, so we can check these. for i := range serviceRegs { serviceRegs[i].ModifyIndex = expectedIndex serviceRegs[i].CreateIndex = expectedIndex must.NoError(t, restore.ServiceRegistrationRestore(serviceRegs[i])) } must.NoError(t, restore.Commit()) // Check the state is now populated as we expect and that we can find the // restored registrations. ws := memdb.NewWatchSet() for i := range serviceRegs { out, err := testState.GetServiceRegistrationByID(ws, serviceRegs[i].Namespace, serviceRegs[i].ID) must.NoError(t, err) must.Eq(t, serviceRegs[i], out) } } func TestStateStore_VariablesRestore(t *testing.T) { ci.Parallel(t) testState := testStateStore(t) // Set up our test variables and index. expectedIndex := uint64(13) svs := mock.VariablesEncrypted(5, 5) restore, err := testState.Restore() must.NoError(t, err) // Iterate the variables, restore, and commit. Set the indexes // on the objects, so we can check these. for i := range svs { svs[i].ModifyIndex = expectedIndex svs[i].CreateIndex = expectedIndex must.NoError(t, restore.VariablesRestore(svs[i])) } must.NoError(t, restore.Commit()) // Check the state is now populated as we expect and that we can find the // restored variables. ws := memdb.NewWatchSet() for i := range svs { out, err := testState.GetVariable(ws, svs[i].Namespace, svs[i].Path) must.NoError(t, err) must.Eq(t, svs[i], out) } } func TestStateStore_ACLRoleRestore(t *testing.T) { ci.Parallel(t) testState := testStateStore(t) // Set up our test registrations and index. expectedIndex := uint64(13) aclRole := mock.ACLRole() aclRole.CreateIndex = expectedIndex aclRole.ModifyIndex = expectedIndex restore, err := testState.Restore() must.NoError(t, err) must.NoError(t, restore.ACLRoleRestore(aclRole)) must.NoError(t, restore.Commit()) // Check the state is now populated as we expect and that we can find the // restored registrations. ws := memdb.NewWatchSet() out, err := testState.GetACLRoleByName(ws, aclRole.Name) must.NoError(t, err) must.Eq(t, aclRole, out) } func TestStateStore_ACLAuthMethodRestore(t *testing.T) { ci.Parallel(t) testState := testStateStore(t) // Set up our test registrations and index. expectedIndex := uint64(13) authMethod := mock.ACLOIDCAuthMethod() authMethod.CreateIndex = expectedIndex authMethod.ModifyIndex = expectedIndex restore, err := testState.Restore() must.NoError(t, err) must.NoError(t, restore.ACLAuthMethodRestore(authMethod)) must.NoError(t, restore.Commit()) // Check the state is now populated as we expect and that we can find the // restored registrations. ws := memdb.NewWatchSet() out, err := testState.GetACLAuthMethodByName(ws, authMethod.Name) must.NoError(t, err) must.Eq(t, authMethod, out) } func TestStateStore_ACLBindingRuleRestore(t *testing.T) { ci.Parallel(t) testState := testStateStore(t) // Set up our test ACL binding rule and index. expectedIndex := uint64(13) aclBindingRule := mock.ACLBindingRule() aclBindingRule.CreateIndex = expectedIndex aclBindingRule.ModifyIndex = expectedIndex restore, err := testState.Restore() must.NoError(t, err) must.NoError(t, restore.ACLBindingRuleRestore(aclBindingRule)) must.NoError(t, restore.Commit()) // Check the state is now populated as we expect and that we can find the // restored ACL binding rule. ws := memdb.NewWatchSet() out, err := testState.GetACLBindingRule(ws, aclBindingRule.ID) must.NoError(t, err) must.Eq(t, aclBindingRule, out) }