github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/client/state/db_test.go (about) 1 package state 2 3 import ( 4 "io/ioutil" 5 "os" 6 "reflect" 7 "testing" 8 9 trstate "github.com/hashicorp/nomad/client/allocrunner/taskrunner/state" 10 dmstate "github.com/hashicorp/nomad/client/devicemanager/state" 11 "github.com/hashicorp/nomad/client/dynamicplugins" 12 driverstate "github.com/hashicorp/nomad/client/pluginmanager/drivermanager/state" 13 "github.com/hashicorp/nomad/helper/testlog" 14 "github.com/hashicorp/nomad/nomad/mock" 15 "github.com/hashicorp/nomad/nomad/structs" 16 "github.com/kr/pretty" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func setupBoltStateDB(t *testing.T) (*BoltStateDB, func()) { 21 dir, err := ioutil.TempDir("", "nomadtest") 22 require.NoError(t, err) 23 24 db, err := NewBoltStateDB(testlog.HCLogger(t), dir) 25 if err != nil { 26 if err := os.RemoveAll(dir); err != nil { 27 t.Logf("error removing boltdb dir: %v", err) 28 } 29 t.Fatalf("error creating boltdb: %v", err) 30 } 31 32 cleanup := func() { 33 if err := db.Close(); err != nil { 34 t.Errorf("error closing boltdb: %v", err) 35 } 36 if err := os.RemoveAll(dir); err != nil { 37 t.Logf("error removing boltdb dir: %v", err) 38 } 39 } 40 41 return db.(*BoltStateDB), cleanup 42 } 43 44 func testDB(t *testing.T, f func(*testing.T, StateDB)) { 45 boltdb, cleanup := setupBoltStateDB(t) 46 defer cleanup() 47 48 memdb := NewMemDB(testlog.HCLogger(t)) 49 50 impls := []StateDB{boltdb, memdb} 51 52 for _, db := range impls { 53 db := db 54 t.Run(db.Name(), func(t *testing.T) { 55 f(t, db) 56 }) 57 } 58 } 59 60 // TestStateDB asserts the behavior of GetAllAllocations, PutAllocation, and 61 // DeleteAllocationBucket for all operational StateDB implementations. 62 func TestStateDB_Allocations(t *testing.T) { 63 t.Parallel() 64 65 testDB(t, func(t *testing.T, db StateDB) { 66 require := require.New(t) 67 68 // Empty database should return empty non-nil results 69 allocs, errs, err := db.GetAllAllocations() 70 require.NoError(err) 71 require.NotNil(allocs) 72 require.Empty(allocs) 73 require.NotNil(errs) 74 require.Empty(errs) 75 76 // Put allocations 77 alloc1 := mock.Alloc() 78 alloc2 := mock.BatchAlloc() 79 80 require.NoError(db.PutAllocation(alloc1)) 81 require.NoError(db.PutAllocation(alloc2)) 82 83 // Retrieve them 84 allocs, errs, err = db.GetAllAllocations() 85 require.NoError(err) 86 require.NotNil(allocs) 87 require.Len(allocs, 2) 88 for _, a := range allocs { 89 switch a.ID { 90 case alloc1.ID: 91 if !reflect.DeepEqual(a, alloc1) { 92 pretty.Ldiff(t, a, alloc1) 93 t.Fatalf("alloc %q unequal", a.ID) 94 } 95 case alloc2.ID: 96 if !reflect.DeepEqual(a, alloc2) { 97 pretty.Ldiff(t, a, alloc2) 98 t.Fatalf("alloc %q unequal", a.ID) 99 } 100 default: 101 t.Fatalf("unexpected alloc id %q", a.ID) 102 } 103 } 104 require.NotNil(errs) 105 require.Empty(errs) 106 107 // Add another 108 alloc3 := mock.SystemAlloc() 109 require.NoError(db.PutAllocation(alloc3)) 110 allocs, errs, err = db.GetAllAllocations() 111 require.NoError(err) 112 require.NotNil(allocs) 113 require.Len(allocs, 3) 114 require.Contains(allocs, alloc1) 115 require.Contains(allocs, alloc2) 116 require.Contains(allocs, alloc3) 117 require.NotNil(errs) 118 require.Empty(errs) 119 120 // Deleting a nonexistent alloc is a noop 121 require.NoError(db.DeleteAllocationBucket("asdf")) 122 allocs, _, err = db.GetAllAllocations() 123 require.NoError(err) 124 require.NotNil(allocs) 125 require.Len(allocs, 3) 126 127 // Delete alloc1 128 require.NoError(db.DeleteAllocationBucket(alloc1.ID)) 129 allocs, errs, err = db.GetAllAllocations() 130 require.NoError(err) 131 require.NotNil(allocs) 132 require.Len(allocs, 2) 133 require.Contains(allocs, alloc2) 134 require.Contains(allocs, alloc3) 135 require.NotNil(errs) 136 require.Empty(errs) 137 }) 138 } 139 140 // TestStateDB_TaskState asserts the behavior of task state related StateDB 141 // methods. 142 func TestStateDB_TaskState(t *testing.T) { 143 t.Parallel() 144 145 testDB(t, func(t *testing.T, db StateDB) { 146 require := require.New(t) 147 148 // Getting nonexistent state should return nils 149 ls, ts, err := db.GetTaskRunnerState("allocid", "taskname") 150 require.NoError(err) 151 require.Nil(ls) 152 require.Nil(ts) 153 154 // Putting TaskState without first putting the allocation should work 155 state := structs.NewTaskState() 156 state.Failed = true // set a non-default value 157 require.NoError(db.PutTaskState("allocid", "taskname", state)) 158 159 // Getting should return the available state 160 ls, ts, err = db.GetTaskRunnerState("allocid", "taskname") 161 require.NoError(err) 162 require.Nil(ls) 163 require.Equal(state, ts) 164 165 // Deleting a nonexistent task should not error 166 require.NoError(db.DeleteTaskBucket("adsf", "asdf")) 167 require.NoError(db.DeleteTaskBucket("asllocid", "asdf")) 168 169 // Data should be untouched 170 ls, ts, err = db.GetTaskRunnerState("allocid", "taskname") 171 require.NoError(err) 172 require.Nil(ls) 173 require.Equal(state, ts) 174 175 // Deleting the task should remove the state 176 require.NoError(db.DeleteTaskBucket("allocid", "taskname")) 177 ls, ts, err = db.GetTaskRunnerState("allocid", "taskname") 178 require.NoError(err) 179 require.Nil(ls) 180 require.Nil(ts) 181 182 // Putting LocalState should work just like TaskState 183 origLocalState := trstate.NewLocalState() 184 require.NoError(db.PutTaskRunnerLocalState("allocid", "taskname", origLocalState)) 185 ls, ts, err = db.GetTaskRunnerState("allocid", "taskname") 186 require.NoError(err) 187 require.Equal(origLocalState, ls) 188 require.Nil(ts) 189 }) 190 } 191 192 // TestStateDB_DeviceManager asserts the behavior of device manager state related StateDB 193 // methods. 194 func TestStateDB_DeviceManager(t *testing.T) { 195 t.Parallel() 196 197 testDB(t, func(t *testing.T, db StateDB) { 198 require := require.New(t) 199 200 // Getting nonexistent state should return nils 201 ps, err := db.GetDevicePluginState() 202 require.NoError(err) 203 require.Nil(ps) 204 205 // Putting PluginState should work 206 state := &dmstate.PluginState{} 207 require.NoError(db.PutDevicePluginState(state)) 208 209 // Getting should return the available state 210 ps, err = db.GetDevicePluginState() 211 require.NoError(err) 212 require.NotNil(ps) 213 require.Equal(state, ps) 214 }) 215 } 216 217 // TestStateDB_DriverManager asserts the behavior of device manager state related StateDB 218 // methods. 219 func TestStateDB_DriverManager(t *testing.T) { 220 t.Parallel() 221 222 testDB(t, func(t *testing.T, db StateDB) { 223 require := require.New(t) 224 225 // Getting nonexistent state should return nils 226 ps, err := db.GetDriverPluginState() 227 require.NoError(err) 228 require.Nil(ps) 229 230 // Putting PluginState should work 231 state := &driverstate.PluginState{} 232 require.NoError(db.PutDriverPluginState(state)) 233 234 // Getting should return the available state 235 ps, err = db.GetDriverPluginState() 236 require.NoError(err) 237 require.NotNil(ps) 238 require.Equal(state, ps) 239 }) 240 } 241 242 // TestStateDB_DynamicRegistry asserts the behavior of dynamic registry state related StateDB 243 // methods. 244 func TestStateDB_DynamicRegistry(t *testing.T) { 245 t.Parallel() 246 247 testDB(t, func(t *testing.T, db StateDB) { 248 require := require.New(t) 249 250 // Getting nonexistent state should return nils 251 ps, err := db.GetDynamicPluginRegistryState() 252 require.NoError(err) 253 require.Nil(ps) 254 255 // Putting PluginState should work 256 state := &dynamicplugins.RegistryState{} 257 require.NoError(db.PutDynamicPluginRegistryState(state)) 258 259 // Getting should return the available state 260 ps, err = db.GetDynamicPluginRegistryState() 261 require.NoError(err) 262 require.NotNil(ps) 263 require.Equal(state, ps) 264 }) 265 } 266 267 // TestStateDB_Upgrade asserts calling Upgrade on new databases always 268 // succeeds. 269 func TestStateDB_Upgrade(t *testing.T) { 270 t.Parallel() 271 272 testDB(t, func(t *testing.T, db StateDB) { 273 require.NoError(t, db.Upgrade()) 274 }) 275 }