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 }