open-nomad/nomad/state/state_store_service_registr...

626 lines
22 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package state
import (
"strconv"
"testing"
"github.com/hashicorp/go-memdb"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/stretchr/testify/require"
)
func TestStateStore_UpsertServiceRegistrations(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// SubTest Marker: This ensures new service registrations are inserted as
// expected with their correct indexes, along with an update to the index
// table.
services := mock.ServiceRegistrations()
insertIndex := uint64(20)
// Perform the initial upsert of service registrations.
err := testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, insertIndex, services)
require.NoError(t, err)
// Check that the index for the table was modified as expected.
initialIndex, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, insertIndex, initialIndex)
// List all the service registrations in the table, so we can perform a
// number of tests on the return array.
ws := memdb.NewWatchSet()
iter, err := testState.GetServiceRegistrations(ws)
require.NoError(t, err)
// Count how many table entries we have, to ensure it is the expected
// number.
var count int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count++
// Ensure the create and modify indexes are populated correctly.
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, insertIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, insertIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
}
require.Equal(t, 2, count, "incorrect number of service registrations found")
// SubTest Marker: This section attempts to upsert the exact same service
// registrations without any modification. In this case, the index table
// should not be updated, indicating no write actually happened due to
// equality checking.
reInsertIndex := uint64(30)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, reInsertIndex, services))
reInsertActualIndex, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, insertIndex, reInsertActualIndex, "index should not have changed")
// SubTest Marker: This section modifies a single one of the previously
// inserted service registrations and performs an upsert. This ensures the
// index table is modified correctly and that each service registration is
// updated, or not, as expected.
service1Update := services[0].Copy()
service1Update.Tags = []string{"modified"}
services1Update := []*structs.ServiceRegistration{service1Update}
update1Index := uint64(40)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, update1Index, services1Update))
// Check that the index for the table was modified as expected.
updateActualIndex, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, update1Index, updateActualIndex, "index should have changed")
// Get the service registrations from the table.
iter, err = testState.GetServiceRegistrations(ws)
require.NoError(t, err)
// Iterate all the stored registrations and assert they are as expected.
for raw := iter.Next(); raw != nil; raw = iter.Next() {
serviceReg := raw.(*structs.ServiceRegistration)
var expectedModifyIndex uint64
switch serviceReg.ID {
case service1Update.ID:
expectedModifyIndex = update1Index
case services[1].ID:
expectedModifyIndex = insertIndex
default:
t.Errorf("unknown service registration found: %s", serviceReg.ID)
continue
}
require.Equal(t, insertIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, expectedModifyIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
}
// SubTest Marker: Here we modify the second registration but send an
// upsert request that includes this and the already modified registration.
service2Update := services[1].Copy()
service2Update.Tags = []string{"modified"}
services2Update := []*structs.ServiceRegistration{service1Update, service2Update}
update2Index := uint64(50)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, update2Index, services2Update))
// Check that the index for the table was modified as expected.
update2ActualIndex, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, update2Index, update2ActualIndex, "index should have changed")
// Get the service registrations from the table.
iter, err = testState.GetServiceRegistrations(ws)
require.NoError(t, err)
// Iterate all the stored registrations and assert they are as expected.
for raw := iter.Next(); raw != nil; raw = iter.Next() {
serviceReg := raw.(*structs.ServiceRegistration)
var (
expectedModifyIndex uint64
expectedServiceReg *structs.ServiceRegistration
)
switch serviceReg.ID {
case service2Update.ID:
expectedModifyIndex = update2Index
expectedServiceReg = service2Update
case service1Update.ID:
expectedModifyIndex = update1Index
expectedServiceReg = service1Update
default:
t.Errorf("unknown service registration found: %s", serviceReg.ID)
continue
}
require.Equal(t, insertIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, expectedModifyIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
require.True(t, expectedServiceReg.Equal(serviceReg))
}
}
func TestStateStore_DeleteServiceRegistrationByID(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services that we will use and modify throughout.
services := mock.ServiceRegistrations()
// SubTest Marker: This section attempts to delete a service registration
// by an ID that does not exist. This is easy to perform here as the state
// is empty.
initialIndex := uint64(10)
err := testState.DeleteServiceRegistrationByID(
structs.MsgTypeTestSetup, initialIndex, services[0].Namespace, services[0].ID)
require.EqualError(t, err, "service registration not found")
actualInitialIndex, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, uint64(0), actualInitialIndex, "index should not have changed")
// SubTest Marker: This section upserts two registrations, deletes one,
// then ensure the remaining is left as expected.
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
// Perform the delete.
delete1Index := uint64(20)
require.NoError(t, testState.DeleteServiceRegistrationByID(
structs.MsgTypeTestSetup, delete1Index, services[0].Namespace, services[0].ID))
// Check that the index for the table was modified as expected.
actualDelete1Index, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, delete1Index, actualDelete1Index, "index should have changed")
ws := memdb.NewWatchSet()
// Get the service registrations from the table.
iter, err := testState.GetServiceRegistrations(ws)
require.NoError(t, err)
var delete1Count int
// Iterate all the stored registrations and assert we have the expected
// number.
for raw := iter.Next(); raw != nil; raw = iter.Next() {
delete1Count++
}
require.Equal(t, 1, delete1Count, "unexpected number of registrations in table")
// SubTest Marker: Delete the remaining registration and ensure all indexes
// are updated as expected and the table is empty.
delete2Index := uint64(30)
require.NoError(t, testState.DeleteServiceRegistrationByID(
structs.MsgTypeTestSetup, delete2Index, services[1].Namespace, services[1].ID))
// Check that the index for the table was modified as expected.
actualDelete2Index, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, delete2Index, actualDelete2Index, "index should have changed")
// Get the service registrations from the table.
iter, err = testState.GetServiceRegistrations(ws)
require.NoError(t, err)
var delete2Count int
// Iterate all the stored registrations and assert we have the expected
// number.
for raw := iter.Next(); raw != nil; raw = iter.Next() {
delete2Count++
}
require.Equal(t, 0, delete2Count, "unexpected number of registrations in table")
}
func TestStateStore_DeleteServiceRegistrationByNodeID(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services that we will use and modify throughout.
services := mock.ServiceRegistrations()
// SubTest Marker: This section attempts to delete a service registration
// by a nodeID that does not exist. This is easy to perform here as the
// state is empty.
initialIndex := uint64(10)
require.NoError(t,
testState.DeleteServiceRegistrationByNodeID(structs.MsgTypeTestSetup, initialIndex, services[0].NodeID))
actualInitialIndex, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, uint64(0), actualInitialIndex, "index should not have changed")
// SubTest Marker: This section upserts two registrations then deletes one
// by using the nodeID.
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
// Perform the delete.
delete1Index := uint64(20)
require.NoError(t, testState.DeleteServiceRegistrationByNodeID(
structs.MsgTypeTestSetup, delete1Index, services[0].NodeID))
// Check that the index for the table was modified as expected.
actualDelete1Index, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, delete1Index, actualDelete1Index, "index should have changed")
ws := memdb.NewWatchSet()
// Get the service registrations from the table.
iter, err := testState.GetServiceRegistrations(ws)
require.NoError(t, err)
var delete1Count int
// Iterate all the stored registrations and assert we have the expected
// number.
for raw := iter.Next(); raw != nil; raw = iter.Next() {
delete1Count++
}
require.Equal(t, 1, delete1Count, "unexpected number of registrations in table")
// SubTest Marker: Add multiple service registrations for a single nodeID
// then delete these via the nodeID.
delete2NodeID := services[1].NodeID
var delete2NodeServices []*structs.ServiceRegistration
for i := 0; i < 4; i++ {
iString := strconv.Itoa(i)
delete2NodeServices = append(delete2NodeServices, &structs.ServiceRegistration{
ID: "_nomad-task-ca60e901-675a-0ab2-2e57-2f3b05fdc540-group-api-countdash-api-http-" + iString,
ServiceName: "countdash-api-" + iString,
Namespace: "platform",
NodeID: delete2NodeID,
Datacenter: "dc2",
JobID: "countdash-api-" + iString,
AllocID: "ca60e901-675a-0ab2-2e57-2f3b05fdc54" + iString,
Tags: []string{"bar"},
Address: "192.168.200.200",
Port: 27500 + i,
})
}
// Upsert the new service registrations.
delete2UpsertIndex := uint64(30)
require.NoError(t,
testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, delete2UpsertIndex, delete2NodeServices))
delete2Index := uint64(40)
require.NoError(t, testState.DeleteServiceRegistrationByNodeID(
structs.MsgTypeTestSetup, delete2Index, delete2NodeID))
// Check that the index for the table was modified as expected.
actualDelete2Index, err := testState.Index(TableServiceRegistrations)
require.NoError(t, err)
require.Equal(t, delete2Index, actualDelete2Index, "index should have changed")
// Get the service registrations from the table.
iter, err = testState.GetServiceRegistrations(ws)
require.NoError(t, err)
var delete2Count int
// Iterate all the stored registrations and assert we have the expected
// number.
for raw := iter.Next(); raw != nil; raw = iter.Next() {
delete2Count++
}
require.Equal(t, 0, delete2Count, "unexpected number of registrations in table")
}
func TestStateStore_GetServiceRegistrations(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services and upsert them.
services := mock.ServiceRegistrations()
initialIndex := uint64(10)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
// Read the service registrations and check the objects.
ws := memdb.NewWatchSet()
iter, err := testState.GetServiceRegistrations(ws)
require.NoError(t, err)
var count int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count++
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, initialIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, initialIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
switch serviceReg.ID {
case services[0].ID:
require.Equal(t, services[0], serviceReg)
case services[1].ID:
require.Equal(t, services[1], serviceReg)
default:
t.Errorf("unknown service registration found: %s", serviceReg.ID)
}
}
require.Equal(t, 2, count)
}
func TestStateStore_GetServiceRegistrationsByNamespace(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services and upsert them.
services := mock.ServiceRegistrations()
initialIndex := uint64(10)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
// Look up services using the namespace of the first service.
ws := memdb.NewWatchSet()
iter, err := testState.GetServiceRegistrationsByNamespace(ws, services[0].Namespace)
require.NoError(t, err)
var count1 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count1++
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, initialIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, initialIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
require.Equal(t, services[0].Namespace, serviceReg.Namespace)
}
require.Equal(t, 1, count1)
// Look up services using the namespace of the second service.
iter, err = testState.GetServiceRegistrationsByNamespace(ws, services[1].Namespace)
require.NoError(t, err)
var count2 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count2++
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, initialIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, initialIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
require.Equal(t, services[1].Namespace, serviceReg.Namespace)
}
require.Equal(t, 1, count2)
// Look up services using a namespace that shouldn't contain any
// registrations.
iter, err = testState.GetServiceRegistrationsByNamespace(ws, "pony-club")
require.NoError(t, err)
var count3 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count3++
}
require.Equal(t, 0, count3)
}
func TestStateStore_GetServiceRegistrationByName(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services and upsert them.
services := mock.ServiceRegistrations()
initialIndex := uint64(10)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
// Try reading a service by a name that shouldn't exist.
ws := memdb.NewWatchSet()
iter, err := testState.GetServiceRegistrationByName(ws, "default", "pony-glitter-api")
require.NoError(t, err)
var count1 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count1++
}
require.Equal(t, 0, count1)
// Read one of the known service registrations.
expectedReg := services[1].Copy()
iter, err = testState.GetServiceRegistrationByName(ws, expectedReg.Namespace, expectedReg.ServiceName)
require.NoError(t, err)
var count2 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count2++
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, expectedReg.ServiceName, serviceReg.ServiceName)
require.Equal(t, expectedReg.Namespace, serviceReg.Namespace)
}
require.Equal(t, 1, count2)
// Create a bunch of additional services whose name and namespace match
// that of expectedReg.
var newServices []*structs.ServiceRegistration
for i := 0; i < 4; i++ {
iString := strconv.Itoa(i)
newServices = append(newServices, &structs.ServiceRegistration{
ID: "_nomad-task-ca60e901-675a-0ab2-2e57-2f3b05fdc540-group-api-countdash-api-http-" + iString,
ServiceName: expectedReg.ServiceName,
Namespace: expectedReg.Namespace,
NodeID: "2873cf75-42e5-7c45-ca1c-415f3e18be3d",
Datacenter: "dc1",
JobID: expectedReg.JobID,
AllocID: "ca60e901-675a-0ab2-2e57-2f3b05fdc54" + iString,
Tags: []string{"bar"},
Address: "192.168.200.200",
Port: 27500 + i,
})
}
updateIndex := uint64(20)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, updateIndex, newServices))
iter, err = testState.GetServiceRegistrationByName(ws, expectedReg.Namespace, expectedReg.ServiceName)
require.NoError(t, err)
var count3 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count3++
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, expectedReg.ServiceName, serviceReg.ServiceName)
require.Equal(t, expectedReg.Namespace, serviceReg.Namespace)
}
require.Equal(t, 5, count3)
}
func TestStateStore_GetServiceRegistrationByID(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services and upsert them.
services := mock.ServiceRegistrations()
initialIndex := uint64(10)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
ws := memdb.NewWatchSet()
// Try reading a service by an ID that shouldn't exist.
serviceReg, err := testState.GetServiceRegistrationByID(ws, "default", "pony-glitter-sparkles")
require.NoError(t, err)
require.Nil(t, serviceReg)
// Read the two services that we should find.
serviceReg, err = testState.GetServiceRegistrationByID(ws, services[0].Namespace, services[0].ID)
require.NoError(t, err)
require.Equal(t, services[0], serviceReg)
serviceReg, err = testState.GetServiceRegistrationByID(ws, services[1].Namespace, services[1].ID)
require.NoError(t, err)
require.Equal(t, services[1], serviceReg)
}
func TestStateStore_GetServiceRegistrationsByAllocID(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services and upsert them.
services := mock.ServiceRegistrations()
initialIndex := uint64(10)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
ws := memdb.NewWatchSet()
// Try reading services by an allocation that doesn't have any
// registrations.
iter, err := testState.GetServiceRegistrationsByAllocID(ws, "4eed3c6d-6bf1-60d6-040a-e347accae6c4")
require.NoError(t, err)
var count1 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count1++
}
require.Equal(t, 0, count1)
// Read the two allocations that we should find.
iter, err = testState.GetServiceRegistrationsByAllocID(ws, services[0].AllocID)
require.NoError(t, err)
var count2 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count2++
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, services[0].AllocID, serviceReg.AllocID)
}
require.Equal(t, 1, count2)
iter, err = testState.GetServiceRegistrationsByAllocID(ws, services[1].AllocID)
require.NoError(t, err)
var count3 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count3++
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, services[1].AllocID, serviceReg.AllocID)
}
require.Equal(t, 1, count3)
}
func TestStateStore_GetServiceRegistrationsByJobID(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services and upsert them.
services := mock.ServiceRegistrations()
initialIndex := uint64(10)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
ws := memdb.NewWatchSet()
// Perform a query against a job that shouldn't have any registrations.
iter, err := testState.GetServiceRegistrationsByJobID(ws, "default", "tamagotchi")
require.NoError(t, err)
var count1 int
for raw := iter.Next(); raw != nil; raw = iter.Next() {
count1++
}
require.Equal(t, 0, count1)
// Look up services using the namespace and jobID of the first service.
iter, err = testState.GetServiceRegistrationsByJobID(ws, services[0].Namespace, services[0].JobID)
require.NoError(t, err)
var outputList1 []*structs.ServiceRegistration
for raw := iter.Next(); raw != nil; raw = iter.Next() {
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, initialIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, initialIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
outputList1 = append(outputList1, serviceReg)
}
require.ElementsMatch(t, outputList1, []*structs.ServiceRegistration{services[0]})
// Look up services using the namespace and jobID of the second service.
iter, err = testState.GetServiceRegistrationsByJobID(ws, services[1].Namespace, services[1].JobID)
require.NoError(t, err)
var outputList2 []*structs.ServiceRegistration
for raw := iter.Next(); raw != nil; raw = iter.Next() {
serviceReg := raw.(*structs.ServiceRegistration)
require.Equal(t, initialIndex, serviceReg.CreateIndex, "incorrect create index", serviceReg.ID)
require.Equal(t, initialIndex, serviceReg.ModifyIndex, "incorrect modify index", serviceReg.ID)
outputList2 = append(outputList2, serviceReg)
}
require.ElementsMatch(t, outputList2, []*structs.ServiceRegistration{services[1]})
}
func TestStateStore_GetServiceRegistrationsByNodeID(t *testing.T) {
ci.Parallel(t)
testState := testStateStore(t)
// Generate some test services and upsert them.
services := mock.ServiceRegistrations()
initialIndex := uint64(10)
require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, initialIndex, services))
ws := memdb.NewWatchSet()
// Perform a query against a node that shouldn't have any registrations.
serviceRegs, err := testState.GetServiceRegistrationsByNodeID(ws, "4eed3c6d-6bf1-60d6-040a-e347accae6c4")
require.NoError(t, err)
require.Len(t, serviceRegs, 0)
// Read the two nodes that we should find entries for.
serviceRegs, err = testState.GetServiceRegistrationsByNodeID(ws, services[0].NodeID)
require.NoError(t, err)
require.Len(t, serviceRegs, 1)
serviceRegs, err = testState.GetServiceRegistrationsByNodeID(ws, services[1].NodeID)
require.NoError(t, err)
require.Len(t, serviceRegs, 1)
}