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  }