package nomad import ( "os" "reflect" "sort" "testing" "github.com/hashicorp/nomad/nomad/structs" ) func testStateStore(t *testing.T) *StateStore { b := testBroker(t, 0) state, err := NewStateStore(b, os.Stderr) if err != nil { t.Fatalf("err: %v", err) } if state == nil { t.Fatalf("missing state") } return state } func mockNode() *structs.Node { node := &structs.Node{ ID: generateUUID(), Datacenter: "dc1", Name: "foobar", Attributes: map[string]string{ "os": "linux", "arch": "x86", "version": "0.1.0", "driver.docker": "1.0.0", }, Resources: &structs.Resources{ CPU: 4.0, MemoryMB: 8192, DiskMB: 100 * 1024, IOPS: 150, Networks: []*structs.NetworkResource{ &structs.NetworkResource{ Public: true, CIDR: "192.168.0.100/32", ReservedPorts: []int{22}, MBits: 1000, }, }, }, Reserved: &structs.Resources{ CPU: 0.1, MemoryMB: 256, DiskMB: 4 * 1024, }, Links: map[string]string{ "consul": "foobar.dc1", }, Meta: map[string]string{ "pci-dss": "true", }, NodeClass: "linux-medium-pci", Status: structs.NodeStatusReady, } return node } func mockJob() *structs.Job { job := &structs.Job{ ID: generateUUID(), Name: "my-job", Type: structs.JobTypeService, Priority: 50, AllAtOnce: false, Constraints: []*structs.Constraint{ &structs.Constraint{ Hard: true, LTarget: "attr.os", RTarget: "linux", Operand: "=", }, }, TaskGroups: []*structs.TaskGroup{ &structs.TaskGroup{ Name: "web", Count: 10, Tasks: []*structs.Task{ &structs.Task{ Name: "web", Driver: "docker", Config: map[string]string{ "image": "hashicorp/web", "version": "v1.2.3", }, Resources: &structs.Resources{ CPU: 0.5, MemoryMB: 256, }, }, }, Meta: map[string]string{ "elb_check_type": "http", "elb_check_interval": "30s", "elb_check_min": "3", }, }, }, Meta: map[string]string{ "owner": "armon", }, Status: structs.JobStatusPending, } return job } func mockEval() *structs.Evaluation { eval := &structs.Evaluation{ ID: generateUUID(), Priority: 50, Type: structs.JobTypeService, Status: structs.EvalStatusPending, } return eval } func mockAlloc() *structs.Allocation { alloc := &structs.Allocation{ ID: generateUUID(), NodeID: "foo", Resources: &structs.Resources{ CPU: 1.0, MemoryMB: 1024, DiskMB: 1024, IOPS: 10, Networks: []*structs.NetworkResource{ &structs.NetworkResource{ Public: true, CIDR: "192.168.0.100/32", ReservedPorts: []int{12345}, MBits: 100, }, }, }, } return alloc } func TestStateStore_RegisterNode_GetNode(t *testing.T) { state := testStateStore(t) node := mockNode() err := state.RegisterNode(1000, node) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetNodeByID(node.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(node, out) { t.Fatalf("bad: %#v %#v", node, out) } index, err := state.GetIndex("nodes") if err != nil { t.Fatalf("err: %v", err) } if index != 1000 { t.Fatalf("bad: %d", index) } } func TestStateStore_DeregisterNode_GetNode(t *testing.T) { state := testStateStore(t) node := mockNode() err := state.RegisterNode(1000, node) if err != nil { t.Fatalf("err: %v", err) } err = state.DeregisterNode(1001, node.ID) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetNodeByID(node.ID) if err != nil { t.Fatalf("err: %v", err) } if out != nil { t.Fatalf("bad: %#v %#v", node, out) } index, err := state.GetIndex("nodes") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_UpdateNode_GetNode(t *testing.T) { state := testStateStore(t) node := mockNode() err := state.RegisterNode(1000, node) if err != nil { t.Fatalf("err: %v", err) } err = state.UpdateNodeStatus(1001, node.ID, structs.NodeStatusReady) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetNodeByID(node.ID) if err != nil { t.Fatalf("err: %v", err) } if out.Status != structs.NodeStatusReady { t.Fatalf("bad: %#v", out) } if out.ModifyIndex != 1001 { t.Fatalf("bad: %#v", out) } index, err := state.GetIndex("nodes") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_Nodes(t *testing.T) { state := testStateStore(t) var nodes []*structs.Node for i := 0; i < 10; i++ { node := mockNode() nodes = append(nodes, node) err := state.RegisterNode(1000+uint64(i), node) if err != nil { t.Fatalf("err: %v", err) } } iter, err := state.Nodes() if err != nil { t.Fatalf("err: %v", err) } var out []*structs.Node for { raw := iter.Next() if raw == nil { break } out = append(out, raw.(*structs.Node)) } sort.Sort(NodeIDSort(nodes)) sort.Sort(NodeIDSort(out)) if !reflect.DeepEqual(nodes, out) { t.Fatalf("bad: %#v %#v", nodes, out) } } func TestStateStore_RestoreNode(t *testing.T) { state := testStateStore(t) restore, err := state.Restore() if err != nil { t.Fatalf("err: %v", err) } node := mockNode() err = restore.NodeRestore(node) if err != nil { t.Fatalf("err: %v", err) } restore.Commit() out, err := state.GetNodeByID(node.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(out, node) { t.Fatalf("Bad: %#v %#v", out, node) } } func TestStateStore_RegisterJob_GetJob(t *testing.T) { state := testStateStore(t) job := mockJob() err := state.RegisterJob(1000, job) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetJobByID(job.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(job, out) { t.Fatalf("bad: %#v %#v", job, out) } index, err := state.GetIndex("jobs") if err != nil { t.Fatalf("err: %v", err) } if index != 1000 { t.Fatalf("bad: %d", index) } } func TestStateStore_UpdateRegisterJob_GetJob(t *testing.T) { state := testStateStore(t) job := mockJob() err := state.RegisterJob(1000, job) if err != nil { t.Fatalf("err: %v", err) } job2 := mockJob() job2.ID = job.ID err = state.RegisterJob(1001, job2) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetJobByID(job.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(job2, out) { t.Fatalf("bad: %#v %#v", job2, out) } if out.CreateIndex != 1000 { t.Fatalf("bad: %#v", out) } if out.ModifyIndex != 1001 { t.Fatalf("bad: %#v", out) } index, err := state.GetIndex("jobs") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_DeregisterJob_GetJob(t *testing.T) { state := testStateStore(t) job := mockJob() err := state.RegisterJob(1000, job) if err != nil { t.Fatalf("err: %v", err) } err = state.DeregisterJob(1001, job.ID) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetJobByID(job.ID) if err != nil { t.Fatalf("err: %v", err) } if out != nil { t.Fatalf("bad: %#v %#v", job, out) } index, err := state.GetIndex("jobs") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_Jobs(t *testing.T) { state := testStateStore(t) var jobs []*structs.Job for i := 0; i < 10; i++ { job := mockJob() jobs = append(jobs, job) err := state.RegisterJob(1000+uint64(i), job) if err != nil { t.Fatalf("err: %v", err) } } iter, err := state.Jobs() if err != nil { t.Fatalf("err: %v", err) } var out []*structs.Job for { raw := iter.Next() if raw == nil { break } out = append(out, raw.(*structs.Job)) } sort.Sort(JobIDSort(jobs)) sort.Sort(JobIDSort(out)) if !reflect.DeepEqual(jobs, out) { t.Fatalf("bad: %#v %#v", jobs, out) } } func TestStateStore_RestoreJob(t *testing.T) { state := testStateStore(t) restore, err := state.Restore() if err != nil { t.Fatalf("err: %v", err) } job := mockJob() err = restore.JobRestore(job) if err != nil { t.Fatalf("err: %v", err) } restore.Commit() out, err := state.GetJobByID(job.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(out, job) { t.Fatalf("Bad: %#v %#v", out, job) } } func TestStateStore_Indexes(t *testing.T) { state := testStateStore(t) node := mockNode() err := state.RegisterNode(1000, node) if err != nil { t.Fatalf("err: %v", err) } iter, err := state.Indexes() if err != nil { t.Fatalf("err: %v", err) } var out []*IndexEntry for { raw := iter.Next() if raw == nil { break } out = append(out, raw.(*IndexEntry)) } expect := []*IndexEntry{ &IndexEntry{"nodes", 1000}, } if !reflect.DeepEqual(expect, out) { t.Fatalf("bad: %#v %#v", expect, out) } } func TestStateStore_RestoreIndex(t *testing.T) { state := testStateStore(t) restore, err := state.Restore() if err != nil { t.Fatalf("err: %v", err) } index := &IndexEntry{"jobs", 1000} err = restore.IndexRestore(index) if err != nil { t.Fatalf("err: %v", err) } restore.Commit() out, err := state.GetIndex("jobs") if err != nil { t.Fatalf("err: %v", err) } if out != 1000 { t.Fatalf("Bad: %#v %#v", out, 1000) } } func TestStateStore_UpsertEval_GetEval(t *testing.T) { state := testStateStore(t) eval := mockEval() err := state.UpsertEval(1000, eval) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetEvalByID(eval.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(eval, out) { t.Fatalf("bad: %#v %#v", eval, out) } index, err := state.GetIndex("evals") if err != nil { t.Fatalf("err: %v", err) } if index != 1000 { t.Fatalf("bad: %d", index) } } func TestStateStore_Update_UpsertEval_GetEval(t *testing.T) { state := testStateStore(t) eval := mockEval() err := state.UpsertEval(1000, eval) if err != nil { t.Fatalf("err: %v", err) } eval2 := mockEval() eval2.ID = eval.ID err = state.UpsertEval(1001, eval2) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetEvalByID(eval.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(eval2, out) { t.Fatalf("bad: %#v %#v", eval2, out) } if out.CreateIndex != 1000 { t.Fatalf("bad: %#v", out) } if out.ModifyIndex != 1001 { t.Fatalf("bad: %#v", out) } index, err := state.GetIndex("evals") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_DeleteEval_GetEval(t *testing.T) { state := testStateStore(t) job := mockEval() err := state.UpsertEval(1000, job) if err != nil { t.Fatalf("err: %v", err) } err = state.DeleteEval(1001, job.ID) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetEvalByID(job.ID) if err != nil { t.Fatalf("err: %v", err) } if out != nil { t.Fatalf("bad: %#v %#v", job, out) } index, err := state.GetIndex("evals") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_Evals(t *testing.T) { state := testStateStore(t) var evals []*structs.Evaluation for i := 0; i < 10; i++ { eval := mockEval() evals = append(evals, eval) err := state.UpsertEval(1000+uint64(i), eval) if err != nil { t.Fatalf("err: %v", err) } } iter, err := state.Evals() if err != nil { t.Fatalf("err: %v", err) } var out []*structs.Evaluation for { raw := iter.Next() if raw == nil { break } out = append(out, raw.(*structs.Evaluation)) } sort.Sort(EvalIDSort(evals)) sort.Sort(EvalIDSort(out)) if !reflect.DeepEqual(evals, out) { t.Fatalf("bad: %#v %#v", evals, out) } } func TestStateStore_RestoreEval(t *testing.T) { state := testStateStore(t) restore, err := state.Restore() if err != nil { t.Fatalf("err: %v", err) } job := mockEval() err = restore.EvalRestore(job) if err != nil { t.Fatalf("err: %v", err) } restore.Commit() out, err := state.GetEvalByID(job.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(out, job) { t.Fatalf("Bad: %#v %#v", out, job) } } func TestStateStore_UpsertAlloc_GetAlloc(t *testing.T) { state := testStateStore(t) alloc := mockAlloc() err := state.UpdateAllocations(1000, nil, []*structs.Allocation{alloc}) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetAllocByID(alloc.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(alloc, out) { t.Fatalf("bad: %#v %#v", alloc, out) } index, err := state.GetIndex("allocs") if err != nil { t.Fatalf("err: %v", err) } if index != 1000 { t.Fatalf("bad: %d", index) } } func TestStateStore_UpdateAlloc_GetAlloc(t *testing.T) { state := testStateStore(t) alloc := mockAlloc() err := state.UpdateAllocations(1000, nil, []*structs.Allocation{alloc}) if err != nil { t.Fatalf("err: %v", err) } alloc2 := mockAlloc() alloc2.ID = alloc.ID alloc2.NodeID = alloc.NodeID + ".new" err = state.UpdateAllocations(1001, nil, []*structs.Allocation{alloc2}) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetAllocByID(alloc.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(alloc2, out) { t.Fatalf("bad: %#v %#v", alloc2, out) } if out.CreateIndex != 1000 { t.Fatalf("bad: %#v", out) } if out.ModifyIndex != 1001 { t.Fatalf("bad: %#v", out) } index, err := state.GetIndex("allocs") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_EvictAlloc_GetAlloc(t *testing.T) { state := testStateStore(t) alloc := mockAlloc() err := state.UpdateAllocations(1001, nil, []*structs.Allocation{alloc}) if err != nil { t.Fatalf("err: %v", err) } err = state.UpdateAllocations(1001, []string{alloc.ID}, nil) if err != nil { t.Fatalf("err: %v", err) } out, err := state.GetAllocByID(alloc.ID) if err != nil { t.Fatalf("err: %v", err) } if out != nil { t.Fatalf("bad: %#v %#v", alloc, out) } index, err := state.GetIndex("allocs") if err != nil { t.Fatalf("err: %v", err) } if index != 1001 { t.Fatalf("bad: %d", index) } } func TestStateStore_AllocsByNode(t *testing.T) { state := testStateStore(t) var allocs []*structs.Allocation for i := 0; i < 10; i++ { alloc := mockAlloc() alloc.NodeID = "foo" allocs = append(allocs, alloc) } err := state.UpdateAllocations(1000, nil, allocs) if err != nil { t.Fatalf("err: %v", err) } out, err := state.AllocsByNode("foo") if err != nil { t.Fatalf("err: %v", err) } sort.Sort(AllocIDSort(allocs)) sort.Sort(AllocIDSort(out)) if !reflect.DeepEqual(allocs, out) { t.Fatalf("bad: %#v %#v", allocs, out) } } func TestStateStore_Allocs(t *testing.T) { state := testStateStore(t) var allocs []*structs.Allocation for i := 0; i < 10; i++ { alloc := mockAlloc() allocs = append(allocs, alloc) } err := state.UpdateAllocations(1000, nil, allocs) if err != nil { t.Fatalf("err: %v", err) } iter, err := state.Allocs() if err != nil { t.Fatalf("err: %v", err) } var out []*structs.Allocation for { raw := iter.Next() if raw == nil { break } out = append(out, raw.(*structs.Allocation)) } sort.Sort(AllocIDSort(allocs)) sort.Sort(AllocIDSort(out)) if !reflect.DeepEqual(allocs, out) { t.Fatalf("bad: %#v %#v", allocs, out) } } func TestStateStore_RestoreAlloc(t *testing.T) { state := testStateStore(t) restore, err := state.Restore() if err != nil { t.Fatalf("err: %v", err) } alloc := mockAlloc() err = restore.AllocRestore(alloc) if err != nil { t.Fatalf("err: %v", err) } restore.Commit() out, err := state.GetAllocByID(alloc.ID) if err != nil { t.Fatalf("err: %v", err) } if !reflect.DeepEqual(out, alloc) { t.Fatalf("Bad: %#v %#v", out, alloc) } } // NodeIDSort is used to sort nodes by ID type NodeIDSort []*structs.Node func (n NodeIDSort) Len() int { return len(n) } func (n NodeIDSort) Less(i, j int) bool { return n[i].ID < n[j].ID } func (n NodeIDSort) Swap(i, j int) { n[i], n[j] = n[j], n[i] } // JobIDis used to sort jobs by id type JobIDSort []*structs.Job func (n JobIDSort) Len() int { return len(n) } func (n JobIDSort) Less(i, j int) bool { return n[i].ID < n[j].ID } func (n JobIDSort) Swap(i, j int) { n[i], n[j] = n[j], n[i] } // EvalIDis used to sort evals by id type EvalIDSort []*structs.Evaluation func (n EvalIDSort) Len() int { return len(n) } func (n EvalIDSort) Less(i, j int) bool { return n[i].ID < n[j].ID } func (n EvalIDSort) Swap(i, j int) { n[i], n[j] = n[j], n[i] } // AllocIDsort used to sort allocations by id type AllocIDSort []*structs.Allocation func (n AllocIDSort) Len() int { return len(n) } func (n AllocIDSort) Less(i, j int) bool { return n[i].ID < n[j].ID } func (n AllocIDSort) Swap(i, j int) { n[i], n[j] = n[j], n[i] }