mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
The dynamic plugin registry assumes that plugins are singletons, which matches the behavior of other Nomad plugins. But because dynamic plugins like CSI are implemented by allocations, we need to handle the possibility of multiple allocations for a given plugin type + ID, as well as behaviors around interleaved allocation starts and stops. Update the data structure for the dynamic registry so that more recent allocations take over as the instance manager singleton, but we still preserve the previous running allocations so that restores work without racing. Multiple allocations can run on a client for the same plugin, even if only during updates. Provide each plugin task a unique path for the control socket so that the tasks don't interfere with each other.
194 lines
4.8 KiB
Go
194 lines
4.8 KiB
Go
package state
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/boltdb/bolt"
|
|
"github.com/hashicorp/nomad/helper/boltdd"
|
|
"github.com/hashicorp/nomad/helper/testlog"
|
|
"github.com/hashicorp/nomad/helper/uuid"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func setupBoltDB(t *testing.T) (*bolt.DB, func()) {
|
|
dir, err := ioutil.TempDir("", "nomadtest")
|
|
require.NoError(t, err)
|
|
|
|
db, err := bolt.Open(filepath.Join(dir, "state.db"), 0666, nil)
|
|
if err != nil {
|
|
os.RemoveAll(dir)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
return db, func() {
|
|
require.NoError(t, db.Close())
|
|
require.NoError(t, os.RemoveAll(dir))
|
|
}
|
|
}
|
|
|
|
// TestUpgrade_NeedsUpgrade_New asserts new state dbs do not need upgrading.
|
|
func TestUpgrade_NeedsUpgrade_New(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// Setting up a new StateDB should initialize it at the latest version.
|
|
db, cleanup := setupBoltStateDB(t)
|
|
defer cleanup()
|
|
|
|
to09, to12, err := NeedsUpgrade(db.DB().BoltDB())
|
|
require.NoError(t, err)
|
|
require.False(t, to09)
|
|
require.False(t, to12)
|
|
}
|
|
|
|
// TestUpgrade_NeedsUpgrade_Old asserts state dbs with just the alloctions
|
|
// bucket *do* need upgrading.
|
|
func TestUpgrade_NeedsUpgrade_Old(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupBoltDB(t)
|
|
defer cleanup()
|
|
|
|
// Create the allocations bucket which exists in both the old and 0.9
|
|
// schemas
|
|
require.NoError(t, db.Update(func(tx *bolt.Tx) error {
|
|
_, err := tx.CreateBucket(allocationsBucketName)
|
|
return err
|
|
}))
|
|
|
|
to09, to12, err := NeedsUpgrade(db)
|
|
require.NoError(t, err)
|
|
require.True(t, to09)
|
|
require.True(t, to12)
|
|
|
|
// Adding meta should mark it as upgraded
|
|
require.NoError(t, db.Update(addMeta))
|
|
|
|
to09, to12, err = NeedsUpgrade(db)
|
|
require.NoError(t, err)
|
|
require.False(t, to09)
|
|
require.False(t, to12)
|
|
}
|
|
|
|
// TestUpgrade_NeedsUpgrade_Error asserts that an error is returned from
|
|
// NeedsUpgrade if an invalid db version is found. This is a safety measure to
|
|
// prevent invalid and unintentional upgrades when downgrading Nomad.
|
|
func TestUpgrade_NeedsUpgrade_Error(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := [][]byte{
|
|
{'"', '2', '"'}, // wrong type
|
|
{'1'}, // wrong version (never existed)
|
|
{'4'}, // wrong version (future)
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
tc := tc
|
|
t.Run(fmt.Sprintf("%v", tc), func(t *testing.T) {
|
|
db, cleanup := setupBoltDB(t)
|
|
defer cleanup()
|
|
|
|
require.NoError(t, db.Update(func(tx *bolt.Tx) error {
|
|
bkt, err := tx.CreateBucketIfNotExists(metaBucketName)
|
|
require.NoError(t, err)
|
|
|
|
return bkt.Put(metaVersionKey, tc)
|
|
}))
|
|
|
|
_, _, err := NeedsUpgrade(db)
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestUpgrade_DeleteInvalidAllocs asserts invalid allocations are deleted
|
|
// during state upgades instead of failing the entire agent.
|
|
func TestUpgrade_DeleteInvalidAllocs_NoAlloc(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
bdb, cleanup := setupBoltDB(t)
|
|
defer cleanup()
|
|
|
|
db := boltdd.New(bdb)
|
|
|
|
allocID := []byte(uuid.Generate())
|
|
|
|
// Create an allocation bucket with no `alloc` key. This is an observed
|
|
// pre-0.9 state corruption that should result in the allocation being
|
|
// dropped while allowing the upgrade to continue.
|
|
require.NoError(t, db.Update(func(tx *boltdd.Tx) error {
|
|
parentBkt, err := tx.CreateBucket(allocationsBucketName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = parentBkt.CreateBucket(allocID)
|
|
return err
|
|
}))
|
|
|
|
// Perform the Upgrade
|
|
require.NoError(t, db.Update(func(tx *boltdd.Tx) error {
|
|
return UpgradeAllocs(testlog.HCLogger(t), tx)
|
|
}))
|
|
|
|
// Assert invalid allocation bucket was removed
|
|
require.NoError(t, db.View(func(tx *boltdd.Tx) error {
|
|
parentBkt := tx.Bucket(allocationsBucketName)
|
|
if parentBkt == nil {
|
|
return fmt.Errorf("parent allocations bucket should not have been removed")
|
|
}
|
|
|
|
if parentBkt.Bucket(allocID) != nil {
|
|
return fmt.Errorf("invalid alloc bucket should have been deleted")
|
|
}
|
|
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
// TestUpgrade_DeleteInvalidTaskEntries asserts invalid entries under a task
|
|
// bucket are deleted.
|
|
func TestUpgrade_upgradeTaskBucket_InvalidEntries(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupBoltDB(t)
|
|
defer cleanup()
|
|
|
|
taskName := []byte("fake-task")
|
|
|
|
// Insert unexpected bucket, unexpected key, and missing simple-all
|
|
require.NoError(t, db.Update(func(tx *bolt.Tx) error {
|
|
bkt, err := tx.CreateBucket(taskName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = bkt.CreateBucket([]byte("unexpectedBucket"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return bkt.Put([]byte("unexepectedKey"), []byte{'x'})
|
|
}))
|
|
|
|
require.NoError(t, db.Update(func(tx *bolt.Tx) error {
|
|
bkt := tx.Bucket(taskName)
|
|
|
|
// upgradeTaskBucket should fail
|
|
state, err := upgradeTaskBucket(testlog.HCLogger(t), bkt)
|
|
require.Nil(t, state)
|
|
require.Error(t, err)
|
|
|
|
// Invalid entries should have been deleted
|
|
cur := bkt.Cursor()
|
|
for k, v := cur.First(); k != nil; k, v = cur.Next() {
|
|
t.Errorf("unexpected entry found: key=%q value=%q", k, v)
|
|
}
|
|
|
|
return nil
|
|
}))
|
|
}
|