github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/state/upgrade_test.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/boltdb/bolt"
    11  	"github.com/hashicorp/nomad/helper/boltdd"
    12  	"github.com/hashicorp/nomad/helper/testlog"
    13  	"github.com/hashicorp/nomad/helper/uuid"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func setupBoltDB(t *testing.T) (*bolt.DB, func()) {
    18  	dir, err := ioutil.TempDir("", "nomadtest")
    19  	require.NoError(t, err)
    20  
    21  	db, err := bolt.Open(filepath.Join(dir, "state.db"), 0666, nil)
    22  	if err != nil {
    23  		os.RemoveAll(dir)
    24  		require.NoError(t, err)
    25  	}
    26  
    27  	return db, func() {
    28  		require.NoError(t, db.Close())
    29  		require.NoError(t, os.RemoveAll(dir))
    30  	}
    31  }
    32  
    33  // TestUpgrade_NeedsUpgrade_New asserts new state dbs do not need upgrading.
    34  func TestUpgrade_NeedsUpgrade_New(t *testing.T) {
    35  	t.Parallel()
    36  
    37  	// Setting up a new StateDB should initialize it at the latest version.
    38  	db, cleanup := setupBoltStateDB(t)
    39  	defer cleanup()
    40  
    41  	up, err := NeedsUpgrade(db.DB().BoltDB())
    42  	require.NoError(t, err)
    43  	require.False(t, up)
    44  }
    45  
    46  // TestUpgrade_NeedsUpgrade_Old asserts state dbs with just the alloctions
    47  // bucket *do* need upgrading.
    48  func TestUpgrade_NeedsUpgrade_Old(t *testing.T) {
    49  	t.Parallel()
    50  
    51  	db, cleanup := setupBoltDB(t)
    52  	defer cleanup()
    53  
    54  	// Create the allocations bucket which exists in both the old and 0.9
    55  	// schemas
    56  	require.NoError(t, db.Update(func(tx *bolt.Tx) error {
    57  		_, err := tx.CreateBucket(allocationsBucketName)
    58  		return err
    59  	}))
    60  
    61  	up, err := NeedsUpgrade(db)
    62  	require.NoError(t, err)
    63  	require.True(t, up)
    64  
    65  	// Adding meta should mark it as upgraded
    66  	require.NoError(t, db.Update(addMeta))
    67  
    68  	up, err = NeedsUpgrade(db)
    69  	require.NoError(t, err)
    70  	require.False(t, up)
    71  }
    72  
    73  // TestUpgrade_NeedsUpgrade_Error asserts that an error is returned from
    74  // NeedsUpgrade if an invalid db version is found. This is a safety measure to
    75  // prevent invalid and unintentional upgrades when downgrading Nomad.
    76  func TestUpgrade_NeedsUpgrade_Error(t *testing.T) {
    77  	t.Parallel()
    78  
    79  	cases := [][]byte{
    80  		{'"', '2', '"'}, // wrong type
    81  		{'1'},           // wrong version (never existed)
    82  		{'3'},           // wrong version (future)
    83  	}
    84  
    85  	for _, tc := range cases {
    86  		tc := tc
    87  		t.Run(fmt.Sprintf("%v", tc), func(t *testing.T) {
    88  			db, cleanup := setupBoltDB(t)
    89  			defer cleanup()
    90  
    91  			require.NoError(t, db.Update(func(tx *bolt.Tx) error {
    92  				bkt, err := tx.CreateBucketIfNotExists(metaBucketName)
    93  				require.NoError(t, err)
    94  
    95  				return bkt.Put(metaVersionKey, tc)
    96  			}))
    97  
    98  			_, err := NeedsUpgrade(db)
    99  			require.Error(t, err)
   100  		})
   101  	}
   102  }
   103  
   104  // TestUpgrade_DeleteInvalidAllocs asserts invalid allocations are deleted
   105  // during state upgades instead of failing the entire agent.
   106  func TestUpgrade_DeleteInvalidAllocs_NoAlloc(t *testing.T) {
   107  	t.Parallel()
   108  
   109  	bdb, cleanup := setupBoltDB(t)
   110  	defer cleanup()
   111  
   112  	db := boltdd.New(bdb)
   113  
   114  	allocID := []byte(uuid.Generate())
   115  
   116  	// Create an allocation bucket with no `alloc` key. This is an observed
   117  	// pre-0.9 state corruption that should result in the allocation being
   118  	// dropped while allowing the upgrade to continue.
   119  	require.NoError(t, db.Update(func(tx *boltdd.Tx) error {
   120  		parentBkt, err := tx.CreateBucket(allocationsBucketName)
   121  		if err != nil {
   122  			return err
   123  		}
   124  
   125  		_, err = parentBkt.CreateBucket(allocID)
   126  		return err
   127  	}))
   128  
   129  	// Perform the Upgrade
   130  	require.NoError(t, db.Update(func(tx *boltdd.Tx) error {
   131  		return UpgradeAllocs(testlog.HCLogger(t), tx)
   132  	}))
   133  
   134  	// Assert invalid allocation bucket was removed
   135  	require.NoError(t, db.View(func(tx *boltdd.Tx) error {
   136  		parentBkt := tx.Bucket(allocationsBucketName)
   137  		if parentBkt == nil {
   138  			return fmt.Errorf("parent allocations bucket should not have been removed")
   139  		}
   140  
   141  		if parentBkt.Bucket(allocID) != nil {
   142  			return fmt.Errorf("invalid alloc bucket should have been deleted")
   143  		}
   144  
   145  		return nil
   146  	}))
   147  }
   148  
   149  // TestUpgrade_DeleteInvalidTaskEntries asserts invalid entries under a task
   150  // bucket are deleted.
   151  func TestUpgrade_upgradeTaskBucket_InvalidEntries(t *testing.T) {
   152  	t.Parallel()
   153  
   154  	db, cleanup := setupBoltDB(t)
   155  	defer cleanup()
   156  
   157  	taskName := []byte("fake-task")
   158  
   159  	// Insert unexpected bucket, unexpected key, and missing simple-all
   160  	require.NoError(t, db.Update(func(tx *bolt.Tx) error {
   161  		bkt, err := tx.CreateBucket(taskName)
   162  		if err != nil {
   163  			return err
   164  		}
   165  
   166  		_, err = bkt.CreateBucket([]byte("unexpectedBucket"))
   167  		if err != nil {
   168  			return err
   169  		}
   170  
   171  		return bkt.Put([]byte("unexepectedKey"), []byte{'x'})
   172  	}))
   173  
   174  	require.NoError(t, db.Update(func(tx *bolt.Tx) error {
   175  		bkt := tx.Bucket(taskName)
   176  
   177  		// upgradeTaskBucket should fail
   178  		state, err := upgradeTaskBucket(testlog.HCLogger(t), bkt)
   179  		require.Nil(t, state)
   180  		require.Error(t, err)
   181  
   182  		// Invalid entries should have been deleted
   183  		cur := bkt.Cursor()
   184  		for k, v := cur.First(); k != nil; k, v = cur.Next() {
   185  			t.Errorf("unexpected entry found: key=%q value=%q", k, v)
   186  		}
   187  
   188  		return nil
   189  	}))
   190  }