github.com/rawahars/moby@v24.0.4+incompatible/libnetwork/bitseq/sequence_test.go (about)

     1  package bitseq
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/docker/docker/libnetwork/datastore"
    12  	"github.com/docker/libkv/store"
    13  	"github.com/docker/libkv/store/boltdb"
    14  )
    15  
    16  var (
    17  	defaultPrefix = filepath.Join(os.TempDir(), "libnetwork", "test", "bitseq")
    18  )
    19  
    20  func init() {
    21  	boltdb.Register()
    22  }
    23  
    24  func randomLocalStore() (datastore.DataStore, error) {
    25  	tmp, err := os.CreateTemp("", "libnetwork-")
    26  	if err != nil {
    27  		return nil, fmt.Errorf("Error creating temp file: %v", err)
    28  	}
    29  	if err := tmp.Close(); err != nil {
    30  		return nil, fmt.Errorf("Error closing temp file: %v", err)
    31  	}
    32  	return datastore.NewDataStore(datastore.ScopeCfg{
    33  		Client: datastore.ScopeClientCfg{
    34  			Provider: "boltdb",
    35  			Address:  filepath.Join(defaultPrefix, filepath.Base(tmp.Name())),
    36  			Config: &store.Config{
    37  				Bucket:            "libnetwork",
    38  				ConnectionTimeout: 3 * time.Second,
    39  			},
    40  		},
    41  	})
    42  }
    43  
    44  const blockLen = 32
    45  
    46  // This one tests an allocation pattern which unveiled an issue in pushReservation
    47  // Specifically a failure in detecting when we are in the (B) case (the bit to set
    48  // belongs to the last block of the current sequence). Because of a bug, code
    49  // was assuming the bit belonged to a block in the middle of the current sequence.
    50  // Which in turn caused an incorrect allocation when requesting a bit which is not
    51  // in the first or last sequence block.
    52  func TestSetAnyInRange(t *testing.T) {
    53  	numBits := uint64(8 * blockLen)
    54  	hnd, err := NewHandle("", nil, "", numBits)
    55  	if err != nil {
    56  		t.Fatal(err)
    57  	}
    58  
    59  	if err := hnd.Set(0); err != nil {
    60  		t.Fatal(err)
    61  	}
    62  
    63  	if err := hnd.Set(255); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	o, err := hnd.SetAnyInRange(128, 255, false)
    68  	if err != nil {
    69  		t.Fatal(err)
    70  	}
    71  	if o != 128 {
    72  		t.Fatalf("Unexpected ordinal: %d", o)
    73  	}
    74  
    75  	o, err = hnd.SetAnyInRange(128, 255, false)
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  
    80  	if o != 129 {
    81  		t.Fatalf("Unexpected ordinal: %d", o)
    82  	}
    83  
    84  	o, err = hnd.SetAnyInRange(246, 255, false)
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	if o != 246 {
    89  		t.Fatalf("Unexpected ordinal: %d", o)
    90  	}
    91  
    92  	o, err = hnd.SetAnyInRange(246, 255, false)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  	if o != 247 {
    97  		t.Fatalf("Unexpected ordinal: %d", o)
    98  	}
    99  }
   100  
   101  func TestRandomAllocateDeallocate(t *testing.T) {
   102  	ds, err := randomLocalStore()
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  
   107  	numBits := int(16 * blockLen)
   108  	hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  	defer func() {
   113  		if err := hnd.Destroy(); err != nil {
   114  			t.Fatal(err)
   115  		}
   116  	}()
   117  
   118  	seed := time.Now().Unix()
   119  	rng := rand.New(rand.NewSource(seed))
   120  
   121  	// Allocate all bits using a random pattern
   122  	pattern := rng.Perm(numBits)
   123  	for _, bit := range pattern {
   124  		err := hnd.Set(uint64(bit))
   125  		if err != nil {
   126  			t.Errorf("Unexpected failure on allocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
   127  		}
   128  	}
   129  	if unselected := hnd.Unselected(); unselected != 0 {
   130  		t.Errorf("Expected full sequence. Instead found %d free bits. Seed: %d.\n%s", unselected, seed, hnd)
   131  	}
   132  
   133  	// Deallocate all bits using a random pattern
   134  	pattern = rng.Perm(numBits)
   135  	for _, bit := range pattern {
   136  		err := hnd.Unset(uint64(bit))
   137  		if err != nil {
   138  			t.Errorf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
   139  		}
   140  	}
   141  	if unselected := hnd.Unselected(); unselected != uint64(numBits) {
   142  		t.Errorf("Expected full sequence. Instead found %d free bits. Seed: %d.\n%s", unselected, seed, hnd)
   143  	}
   144  }
   145  
   146  func TestRetrieveFromStore(t *testing.T) {
   147  	ds, err := randomLocalStore()
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  
   152  	numBits := int(8 * blockLen)
   153  	hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  
   158  	// Allocate first half of the bits
   159  	for i := 0; i < numBits/2; i++ {
   160  		_, err := hnd.SetAny(false)
   161  		if err != nil {
   162  			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
   163  		}
   164  	}
   165  	hnd0 := hnd.String()
   166  
   167  	// Retrieve same handle
   168  	hnd, err = NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  	hnd1 := hnd.String()
   173  
   174  	if hnd1 != hnd0 {
   175  		t.Fatalf("%v\n%v", hnd0, hnd1)
   176  	}
   177  
   178  	err = hnd.Destroy()
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	}
   182  }
   183  
   184  func testSetRollover(t *testing.T, serial bool) {
   185  	ds, err := randomLocalStore()
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	numBlocks := uint32(8)
   191  	numBits := int(numBlocks * blockLen)
   192  	hnd, err := NewHandle("bitseq-test/data/", ds, "test1", uint64(numBits))
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  
   197  	// Allocate first half of the bits
   198  	for i := 0; i < numBits/2; i++ {
   199  		_, err := hnd.SetAny(serial)
   200  		if err != nil {
   201  			t.Fatalf("Unexpected failure on allocation %d: %v\n%s", i, err, hnd)
   202  		}
   203  	}
   204  
   205  	if unselected := hnd.Unselected(); unselected != uint64(numBits/2) {
   206  		t.Fatalf("Expected full sequence. Instead found %d free bits. %s", unselected, hnd)
   207  	}
   208  
   209  	seed := time.Now().Unix()
   210  	rng := rand.New(rand.NewSource(seed))
   211  
   212  	// Deallocate half of the allocated bits following a random pattern
   213  	pattern := rng.Perm(numBits / 2)
   214  	for i := 0; i < numBits/4; i++ {
   215  		bit := pattern[i]
   216  		err := hnd.Unset(uint64(bit))
   217  		if err != nil {
   218  			t.Fatalf("Unexpected failure on deallocation of %d: %v.\nSeed: %d.\n%s", bit, err, seed, hnd)
   219  		}
   220  	}
   221  	if unselected := hnd.Unselected(); unselected != uint64(3*numBits/4) {
   222  		t.Fatalf("Unexpected free bits: found %d free bits.\nSeed: %d.\n%s", unselected, seed, hnd)
   223  	}
   224  
   225  	//request to allocate for remaining half of the bits
   226  	for i := 0; i < numBits/2; i++ {
   227  		_, err := hnd.SetAny(serial)
   228  		if err != nil {
   229  			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
   230  		}
   231  	}
   232  
   233  	//At this point all the bits must be allocated except the randomly unallocated bits
   234  	//which were unallocated in the first half of the bit sequence
   235  	if unselected := hnd.Unselected(); unselected != uint64(numBits/4) {
   236  		t.Fatalf("Unexpected number of unselected bits %d, Expected %d", unselected, numBits/4)
   237  	}
   238  
   239  	for i := 0; i < numBits/4; i++ {
   240  		_, err := hnd.SetAny(serial)
   241  		if err != nil {
   242  			t.Fatalf("Unexpected failure on allocation %d: %v\nSeed: %d\n%s", i, err, seed, hnd)
   243  		}
   244  	}
   245  	//Now requesting to allocate the unallocated random bits (qurter of the number of bits) should
   246  	//leave no more bits that can be allocated.
   247  	if hnd.Unselected() != 0 {
   248  		t.Fatalf("Unexpected number of unselected bits %d, Expected %d", hnd.Unselected(), 0)
   249  	}
   250  
   251  	err = hnd.Destroy()
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  }
   256  
   257  func TestSetRollover(t *testing.T) {
   258  	testSetRollover(t, false)
   259  }
   260  
   261  func TestSetRolloverSerial(t *testing.T) {
   262  	testSetRollover(t, true)
   263  }
   264  
   265  func TestMarshalJSON(t *testing.T) {
   266  	const expectedID = "my-bitseq"
   267  	expected := []byte("hello libnetwork")
   268  	hnd, err := NewHandle("", nil, expectedID, uint64(len(expected)*8))
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  
   273  	for i, c := range expected {
   274  		for j := 0; j < 8; j++ {
   275  			if c&(1<<j) == 0 {
   276  				continue
   277  			}
   278  			if err := hnd.Set(uint64(i*8 + j)); err != nil {
   279  				t.Fatal(err)
   280  			}
   281  		}
   282  	}
   283  
   284  	hstr := hnd.String()
   285  	t.Log(hstr)
   286  	marshaled, err := hnd.MarshalJSON()
   287  	if err != nil {
   288  		t.Fatalf("MarshalJSON() err = %v", err)
   289  	}
   290  	t.Logf("%s", marshaled)
   291  
   292  	// Serializations of hnd as would be marshaled by versions of the code
   293  	// found in the wild. We need to support unmarshaling old versions to
   294  	// maintain backwards compatibility with sequences persisted on disk.
   295  	const (
   296  		goldenV0 = `{"id":"my-bitseq","sequence":"AAAAAAAAAIAAAAAAAAAAPRamNjYAAAAAAAAAAfYENpYAAAAAAAAAAUZ2pi4AAAAAAAAAAe72TtYAAAAAAAAAAQ=="}`
   297  	)
   298  
   299  	if string(marshaled) != goldenV0 {
   300  		t.Errorf("MarshalJSON() output differs from golden. Please add a new golden case to this test.")
   301  	}
   302  
   303  	for _, tt := range []struct {
   304  		name string
   305  		data []byte
   306  	}{
   307  		{name: "Live", data: marshaled},
   308  		{name: "Golden-v0", data: []byte(goldenV0)},
   309  	} {
   310  		tt := tt
   311  		t.Run("UnmarshalJSON="+tt.name, func(t *testing.T) {
   312  			hnd2, err := NewHandle("", nil, "", 0)
   313  			if err != nil {
   314  				t.Fatal(err)
   315  			}
   316  			if err := hnd2.UnmarshalJSON(tt.data); err != nil {
   317  				t.Errorf("UnmarshalJSON() err = %v", err)
   318  			}
   319  
   320  			h2str := hnd2.String()
   321  			t.Log(h2str)
   322  			if hstr != h2str {
   323  				t.Errorf("Unmarshaled a different bitseq: want %q, got %q", hstr, h2str)
   324  			}
   325  		})
   326  	}
   327  }