github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/state/upgrade_int_test.go (about) 1 package state_test 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "fmt" 7 "io" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/hashicorp/nomad/ci" 14 "github.com/hashicorp/nomad/client/allocrunner" 15 "github.com/hashicorp/nomad/client/allocwatcher" 16 clientconfig "github.com/hashicorp/nomad/client/config" 17 "github.com/hashicorp/nomad/client/devicemanager" 18 dmstate "github.com/hashicorp/nomad/client/devicemanager/state" 19 "github.com/hashicorp/nomad/client/pluginmanager/drivermanager" 20 regMock "github.com/hashicorp/nomad/client/serviceregistration/mock" 21 . "github.com/hashicorp/nomad/client/state" 22 "github.com/hashicorp/nomad/client/vaultclient" 23 "github.com/hashicorp/nomad/helper/boltdd" 24 "github.com/hashicorp/nomad/helper/testlog" 25 "github.com/hashicorp/nomad/nomad/structs" 26 pstructs "github.com/hashicorp/nomad/plugins/shared/structs" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 "go.etcd.io/bbolt" 30 ) 31 32 // TestBoltStateDB_Upgrade_Ok asserts upgading an old state db does not error 33 // during upgrade and restore. 34 func TestBoltStateDB_UpgradeOld_Ok(t *testing.T) { 35 ci.Parallel(t) 36 37 dbFromTestFile := func(t *testing.T, dir, fn string) *BoltStateDB { 38 39 var src io.ReadCloser 40 src, err := os.Open(fn) 41 require.NoError(t, err) 42 defer src.Close() 43 44 // testdata may be gzip'd; decode on copy 45 if strings.HasSuffix(fn, ".gz") { 46 src, err = gzip.NewReader(src) 47 require.NoError(t, err) 48 } 49 50 dst, err := os.Create(filepath.Join(dir, "state.db")) 51 require.NoError(t, err) 52 53 // Copy test files before testing them for safety 54 _, err = io.Copy(dst, src) 55 require.NoError(t, err) 56 57 require.NoError(t, src.Close()) 58 59 dbI, err := NewBoltStateDB(testlog.HCLogger(t), dir) 60 require.NoError(t, err) 61 62 db := dbI.(*BoltStateDB) 63 return db 64 } 65 66 pre09files := []string{ 67 "testdata/state-0.7.1.db.gz", 68 "testdata/state-0.8.6-empty.db.gz", 69 "testdata/state-0.8.6-no-deploy.db.gz"} 70 71 for _, fn := range pre09files { 72 t.Run(fn, func(t *testing.T) { 73 74 dir := t.TempDir() 75 76 db := dbFromTestFile(t, dir, fn) 77 defer db.Close() 78 79 // Simply opening old files should *not* alter them 80 require.NoError(t, db.DB().View(func(tx *boltdd.Tx) error { 81 b := tx.Bucket([]byte("meta")) 82 if b != nil { 83 return fmt.Errorf("meta bucket found but should not exist yet!") 84 } 85 return nil 86 })) 87 88 to09, to12, err := NeedsUpgrade(db.DB().BoltDB()) 89 require.NoError(t, err) 90 require.True(t, to09) 91 require.True(t, to12) 92 93 // Attempt the upgrade 94 require.NoError(t, db.Upgrade()) 95 96 to09, to12, err = NeedsUpgrade(db.DB().BoltDB()) 97 require.NoError(t, err) 98 require.False(t, to09) 99 require.False(t, to12) 100 101 // Ensure Allocations can be restored and 102 // NewAR/AR.Restore do not error. 103 allocs, errs, err := db.GetAllAllocations() 104 require.NoError(t, err) 105 assert.Len(t, errs, 0) 106 107 for _, alloc := range allocs { 108 checkUpgradedAlloc(t, dir, db, alloc) 109 } 110 111 // Should be nil for all upgrades 112 ps, err := db.GetDevicePluginState() 113 require.NoError(t, err) 114 require.Nil(t, ps) 115 116 ps = &dmstate.PluginState{ 117 ReattachConfigs: map[string]*pstructs.ReattachConfig{ 118 "test": {Pid: 1}, 119 }, 120 } 121 require.NoError(t, db.PutDevicePluginState(ps)) 122 123 registry, err := db.GetDynamicPluginRegistryState() 124 require.Nil(t, registry) 125 126 require.NoError(t, err) 127 require.NoError(t, db.Close()) 128 }) 129 } 130 131 t.Run("testdata/state-1.2.6.db.gz", func(t *testing.T) { 132 fn := "testdata/state-1.2.6.db.gz" 133 dir := t.TempDir() 134 135 db := dbFromTestFile(t, dir, fn) 136 defer db.Close() 137 138 // Simply opening old files should *not* alter them 139 db.DB().BoltDB().View(func(tx *bbolt.Tx) error { 140 b := tx.Bucket([]byte("meta")) 141 if b == nil { 142 return fmt.Errorf("meta bucket should exist") 143 } 144 v := b.Get([]byte("version")) 145 if len(v) == 0 { 146 return fmt.Errorf("meta version is missing") 147 } 148 if !bytes.Equal(v, []byte{'2'}) { 149 return fmt.Errorf("meta version should not have changed") 150 } 151 return nil 152 }) 153 154 to09, to12, err := NeedsUpgrade(db.DB().BoltDB()) 155 require.NoError(t, err) 156 require.False(t, to09) 157 require.True(t, to12) 158 159 // Attempt the upgrade 160 require.NoError(t, db.Upgrade()) 161 162 to09, to12, err = NeedsUpgrade(db.DB().BoltDB()) 163 require.NoError(t, err) 164 require.False(t, to09) 165 require.False(t, to12) 166 167 registry, err := db.GetDynamicPluginRegistryState() 168 require.NoError(t, err) 169 require.NotNil(t, registry) 170 require.Len(t, registry.Plugins["csi-node"], 2) 171 172 require.NoError(t, db.Close()) 173 }) 174 } 175 176 // checkUpgradedAlloc creates and restores an AllocRunner from an upgraded 177 // database. 178 // 179 // It does not call AR.Run as its intended to be used against a wide test 180 // corpus in testdata that may be expensive to run and require unavailable 181 // dependencies. 182 func checkUpgradedAlloc(t *testing.T, path string, db StateDB, alloc *structs.Allocation) { 183 _, err := db.GetDeploymentStatus(alloc.ID) 184 assert.NoError(t, err) 185 186 tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup) 187 for _, task := range tg.Tasks { 188 _, _, err := db.GetTaskRunnerState(alloc.ID, task.Name) 189 require.NoError(t, err) 190 } 191 192 clientConf, cleanup := clientconfig.TestClientConfig(t) 193 194 // Does *not* cleanup overridden StateDir below. That's left alone for 195 // the caller to cleanup. 196 defer cleanup() 197 198 clientConf.StateDir = path 199 200 conf := &allocrunner.Config{ 201 Alloc: alloc, 202 Logger: clientConf.Logger, 203 ClientConfig: clientConf, 204 StateDB: db, 205 Consul: regMock.NewServiceRegistrationHandler(clientConf.Logger), 206 Vault: vaultclient.NewMockVaultClient(), 207 StateUpdater: &allocrunner.MockStateUpdater{}, 208 PrevAllocWatcher: allocwatcher.NoopPrevAlloc{}, 209 PrevAllocMigrator: allocwatcher.NoopPrevAlloc{}, 210 DeviceManager: devicemanager.NoopMockManager(), 211 DriverManager: drivermanager.TestDriverManager(t), 212 } 213 ar, err := allocrunner.NewAllocRunner(conf) 214 require.NoError(t, err) 215 216 // AllocRunner.Restore should not error 217 require.NoError(t, ar.Restore()) 218 }