github.com/portworx/kvdb@v0.0.0-20241107215734-a185a966f535/zookeeper/kv_zookeeper_test.go (about)

     1  package zookeeper
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/portworx/kvdb"
    12  	"github.com/portworx/kvdb/test"
    13  	"github.com/stretchr/testify/assert"
    14  )
    15  
    16  const (
    17  	domain  = "pwx/test"
    18  	dataDir = "/data/zookeeper"
    19  )
    20  
    21  func TestAll(t *testing.T) {
    22  	test.RunBasic(New, t, Start, Stop)
    23  }
    24  
    25  func TestVersion(t *testing.T) {
    26  	fmt.Println("verify version")
    27  	version, err := Version("", nil)
    28  	assert.NoError(t, err, "Unexpected error on Version")
    29  	assert.Equal(t, kvdb.ZookeeperVersion1, version)
    30  }
    31  
    32  func TestZookeeperOps(t *testing.T) {
    33  	err := Start(true)
    34  	assert.NoError(t, err, "Unable to start kvdb")
    35  	// Wait for kvdb to start
    36  	time.Sleep(5 * time.Second)
    37  
    38  	kv, err := New(domain, nil, nil, nil)
    39  	if err != nil {
    40  		t.Fatalf(err.Error())
    41  	}
    42  
    43  	testName(t, kv)
    44  	testEmptyValue(t, kv)
    45  	testCreateWithTTL(t, kv)
    46  	testPutWithTTL(t, kv)
    47  	testUpdateWithTTL(t, kv)
    48  	testCreateEphemeral(t)
    49  	testLockBetweenClientRestarts(t)
    50  	testLockWithIDBetweenClientRestarts(t)
    51  	testLockWithTimeoutBetweenClientRestarts(t)
    52  
    53  	err = Stop()
    54  	assert.NoError(t, err, "Unable to stop kvdb")
    55  }
    56  
    57  func testName(t *testing.T, kv kvdb.Kvdb) {
    58  	fmt.Println("verify name")
    59  	assert.Equal(t, Name, kv.String())
    60  }
    61  
    62  func testEmptyValue(t *testing.T, kv kvdb.Kvdb) {
    63  	fmt.Println("create with empty value")
    64  	key := "empty/value"
    65  	kv.Delete(key)
    66  
    67  	_, err := kv.Create(key, []byte(""), 0)
    68  	assert.Equal(t, kvdb.ErrEmptyValue, err)
    69  
    70  	_, err = kv.Put(key, []byte(""), 0)
    71  	assert.Equal(t, kvdb.ErrEmptyValue, err)
    72  
    73  	_, err = kv.Create(key, []byte("test"), 0)
    74  	assert.NoError(t, err, "Unexpected error on create")
    75  
    76  	_, err = kv.Update(key, []byte(""), 0)
    77  	assert.Equal(t, kvdb.ErrEmptyValue, err)
    78  
    79  	// The parent for the key will internally have empty
    80  	// value and hence should return ErrNotFound error
    81  	_, err = kv.Get("/empty")
    82  	assert.Equal(t, kvdb.ErrNotFound, err)
    83  
    84  	var str string
    85  	_, err = kv.GetVal("/empty", &str)
    86  	assert.Equal(t, kvdb.ErrNotFound, err)
    87  }
    88  
    89  func testCreateWithTTL(t *testing.T, kv kvdb.Kvdb) {
    90  	fmt.Println("create with ttl")
    91  	key := "create/foottl"
    92  	kv.Delete(key)
    93  
    94  	_, err := kv.Create(key, []byte("barttl"), 10)
    95  	assert.Equal(t, kvdb.ErrTTLNotSupported, err)
    96  }
    97  
    98  func testPutWithTTL(t *testing.T, kv kvdb.Kvdb) {
    99  	fmt.Println("put with ttl")
   100  	key := "put/foottl"
   101  	kv.Delete(key)
   102  
   103  	_, err := kv.Put(key, []byte("barttl"), 10)
   104  	assert.Equal(t, kvdb.ErrTTLNotSupported, err)
   105  }
   106  
   107  func testUpdateWithTTL(t *testing.T, kv kvdb.Kvdb) {
   108  	fmt.Println("update with ttl")
   109  	key := "update/foottl"
   110  	kv.Delete(key)
   111  
   112  	_, err := kv.Create(key, []byte("bar"), 0)
   113  	assert.NoError(t, err, "Unexpected error on create")
   114  
   115  	_, err = kv.Update(key, []byte("barttl"), 10)
   116  	assert.Equal(t, kvdb.ErrTTLNotSupported, err)
   117  }
   118  
   119  func testCreateEphemeral(t *testing.T) {
   120  	fmt.Println("create ephemeral node")
   121  	zk, err := newClient(domain, nil, nil, nil)
   122  	assert.NoError(t, err)
   123  	assert.NotNil(t, zk)
   124  
   125  	key := "create/ephemeral"
   126  	value := []byte("val")
   127  	zk.Delete(key)
   128  
   129  	// Should fail for empty value
   130  	kvp, err := zk.createEphemeral(key, []byte(""))
   131  	assert.Equal(t, kvdb.ErrEmptyValue, err)
   132  
   133  	kvp, err = zk.createEphemeral(key, value)
   134  	assert.NoError(t, err, "Unexpected error on ephemeral create")
   135  
   136  	defer func() {
   137  		zk.Delete(key)
   138  	}()
   139  	assert.Equal(t, kvp.Action, kvdb.KVCreate,
   140  		"Expected action KVCreate, action %v", kvp.Action)
   141  	assert.Equal(t, value, kvp.Value)
   142  	assert.Equal(t, uint64(1), kvp.ModifiedIndex)
   143  
   144  	_, err = zk.createEphemeral(key, []byte("val"))
   145  	assert.Equal(t, kvdb.ErrExist, err)
   146  }
   147  
   148  func testLockBetweenClientRestarts(t *testing.T) {
   149  	fmt.Println("Lock between client restarts")
   150  	zk, err := newClient(domain, nil, nil, nil)
   151  	assert.NoError(t, err, "Unable to create a client")
   152  	assert.NotNil(t, zk)
   153  
   154  	zk.SetLockHoldDuration(time.Minute)
   155  
   156  	// Lock before restarting client
   157  	kvPair, err := zk.Lock("lock_key")
   158  	assert.NoError(t, err, "Unable to take a lock")
   159  
   160  	// Stopping client connection
   161  	zk.closeClient()
   162  
   163  	// We don't need to wait for session timeout, as we are doing
   164  	// a proper session close. In this case, the server will delete
   165  	// all ephemeral nodes created in that session immediately.
   166  
   167  	// Reconnecting the client
   168  	zk, err = newClient(domain, nil, nil, nil)
   169  	assert.NoError(t, err, "Unable to reconnect client")
   170  
   171  	// Locking again should succeed as the previous client died
   172  	lockChan := make(chan struct{})
   173  	go func() {
   174  		kvPair, err = zk.Lock("lock_key")
   175  		lockChan <- struct{}{}
   176  	}()
   177  	select {
   178  	case <-time.After(5 * time.Second):
   179  		assert.Fail(t, "Unable to take a lock even when previous session was expired")
   180  	case <-lockChan:
   181  	}
   182  	err = zk.Unlock(kvPair)
   183  	assert.NoError(t, err, "Unable to unlock")
   184  }
   185  
   186  func testLockWithIDBetweenClientRestarts(t *testing.T) {
   187  	fmt.Println("LockWithID between client restarts")
   188  	zk, err := newClient(domain, nil, nil, nil)
   189  	assert.NoError(t, err)
   190  	assert.NotNil(t, zk)
   191  
   192  	zk.SetLockHoldDuration(time.Minute)
   193  
   194  	// Lock before restarting client
   195  	kvPair, err := zk.LockWithID("lock_key", "lock_with_id")
   196  	assert.NoError(t, err, "Unable to take a lock")
   197  
   198  	// Stopping client connection
   199  	zk.closeClient()
   200  
   201  	// We don't need to wait for session timeout, as we are doing
   202  	// a proper session close. In this case, the server will delete
   203  	// all ephemeral nodes created in that session immediately.
   204  
   205  	// Reconnecting the client
   206  	zk, err = newClient(domain, nil, nil, nil)
   207  	assert.NoError(t, err, "Unable to reconnect client")
   208  
   209  	// Locking again should succeed as the previous client died
   210  	lockChan := make(chan struct{})
   211  	go func() {
   212  		kvPair, err = zk.LockWithID("lock_key", "lock_with_id")
   213  		lockChan <- struct{}{}
   214  	}()
   215  	select {
   216  	case <-time.After(5 * time.Second):
   217  		assert.Fail(t, "Unable to take a lock even when previous session was expired")
   218  	case <-lockChan:
   219  	}
   220  	err = zk.Unlock(kvPair)
   221  	assert.NoError(t, err, "Unable to unlock")
   222  }
   223  
   224  func testLockWithTimeoutBetweenClientRestarts(t *testing.T) {
   225  	fmt.Println("LockWithTimeout between client restarts")
   226  	zk, err := newClient(domain, nil, nil, nil)
   227  	assert.NoError(t, err)
   228  	assert.NotNil(t, zk)
   229  
   230  	zk.SetLockHoldDuration(time.Minute)
   231  
   232  	// Lock before restarting client
   233  	kvPair, err := zk.LockWithTimeout("lock_key", "lock_with_id",
   234  		10*time.Second, time.Minute)
   235  	assert.NoError(t, err, "Unable to take a lock")
   236  
   237  	// Stopping client connection
   238  	zk.closeClient()
   239  
   240  	// We don't need to wait for session timeout, as we are doing
   241  	// a proper session close. In this case, the server will delete
   242  	// all ephemeral nodes created in that session immediately.
   243  
   244  	// Reconnecting the client
   245  	zk, err = newClient(domain, nil, nil, nil)
   246  	assert.NoError(t, err, "Unable to reconnect client")
   247  
   248  	// Locking again should succeed as the previous client died
   249  	lockChan := make(chan struct{})
   250  	go func() {
   251  		kvPair, err = zk.LockWithTimeout("lock_key", "lock_with_id",
   252  			10*time.Second, time.Minute)
   253  		lockChan <- struct{}{}
   254  	}()
   255  	select {
   256  	case <-time.After(5 * time.Second):
   257  		assert.Fail(t, "Unable to take a lock even when previous session was expired")
   258  	case <-lockChan:
   259  	}
   260  	err = zk.Unlock(kvPair)
   261  	assert.NoError(t, err, "Unable to unlock")
   262  }
   263  
   264  func Start(removeData bool) error {
   265  	if removeData {
   266  		err := os.RemoveAll(dataDir)
   267  		if err != nil {
   268  			return err
   269  		}
   270  	}
   271  	err := os.MkdirAll(dataDir, 0644)
   272  	if err != nil {
   273  		return err
   274  	}
   275  	err = ioutil.WriteFile(dataDir+"/myid", []byte("1"), 0644)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	cmd := exec.Command("/tmp/test-zookeeper/bin/zkServer.sh", "start")
   280  	err = cmd.Start()
   281  	time.Sleep(5 * time.Second)
   282  	return err
   283  }
   284  
   285  func Stop() error {
   286  	cmd := exec.Command("/tmp/test-zookeeper/bin/zkServer.sh", "stop")
   287  	err := cmd.Start()
   288  	time.Sleep(5 * time.Second)
   289  	return err
   290  }