gopkg.in/dotcloud/docker.v1@v1.13.1/daemon/graphdriver/aufs/aufs_test.go (about)

     1  // +build linux
     2  
     3  package aufs
     4  
     5  import (
     6  	"crypto/sha256"
     7  	"encoding/hex"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"sync"
    13  	"testing"
    14  
    15  	"github.com/docker/docker/daemon/graphdriver"
    16  	"github.com/docker/docker/pkg/archive"
    17  	"github.com/docker/docker/pkg/reexec"
    18  	"github.com/docker/docker/pkg/stringid"
    19  )
    20  
    21  var (
    22  	tmpOuter = path.Join(os.TempDir(), "aufs-tests")
    23  	tmp      = path.Join(tmpOuter, "aufs")
    24  )
    25  
    26  func init() {
    27  	reexec.Init()
    28  }
    29  
    30  func testInit(dir string, t testing.TB) graphdriver.Driver {
    31  	d, err := Init(dir, nil, nil, nil)
    32  	if err != nil {
    33  		if err == graphdriver.ErrNotSupported {
    34  			t.Skip(err)
    35  		} else {
    36  			t.Fatal(err)
    37  		}
    38  	}
    39  	return d
    40  }
    41  
    42  func newDriver(t testing.TB) *Driver {
    43  	if err := os.MkdirAll(tmp, 0755); err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	d := testInit(tmp, t)
    48  	return d.(*Driver)
    49  }
    50  
    51  func TestNewDriver(t *testing.T) {
    52  	if err := os.MkdirAll(tmp, 0755); err != nil {
    53  		t.Fatal(err)
    54  	}
    55  
    56  	d := testInit(tmp, t)
    57  	defer os.RemoveAll(tmp)
    58  	if d == nil {
    59  		t.Fatalf("Driver should not be nil")
    60  	}
    61  }
    62  
    63  func TestAufsString(t *testing.T) {
    64  	d := newDriver(t)
    65  	defer os.RemoveAll(tmp)
    66  
    67  	if d.String() != "aufs" {
    68  		t.Fatalf("Expected aufs got %s", d.String())
    69  	}
    70  }
    71  
    72  func TestCreateDirStructure(t *testing.T) {
    73  	newDriver(t)
    74  	defer os.RemoveAll(tmp)
    75  
    76  	paths := []string{
    77  		"mnt",
    78  		"layers",
    79  		"diff",
    80  	}
    81  
    82  	for _, p := range paths {
    83  		if _, err := os.Stat(path.Join(tmp, p)); err != nil {
    84  			t.Fatal(err)
    85  		}
    86  	}
    87  }
    88  
    89  // We should be able to create two drivers with the same dir structure
    90  func TestNewDriverFromExistingDir(t *testing.T) {
    91  	if err := os.MkdirAll(tmp, 0755); err != nil {
    92  		t.Fatal(err)
    93  	}
    94  
    95  	testInit(tmp, t)
    96  	testInit(tmp, t)
    97  	os.RemoveAll(tmp)
    98  }
    99  
   100  func TestCreateNewDir(t *testing.T) {
   101  	d := newDriver(t)
   102  	defer os.RemoveAll(tmp)
   103  
   104  	if err := d.Create("1", "", nil); err != nil {
   105  		t.Fatal(err)
   106  	}
   107  }
   108  
   109  func TestCreateNewDirStructure(t *testing.T) {
   110  	d := newDriver(t)
   111  	defer os.RemoveAll(tmp)
   112  
   113  	if err := d.Create("1", "", nil); err != nil {
   114  		t.Fatal(err)
   115  	}
   116  
   117  	paths := []string{
   118  		"mnt",
   119  		"diff",
   120  		"layers",
   121  	}
   122  
   123  	for _, p := range paths {
   124  		if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
   125  			t.Fatal(err)
   126  		}
   127  	}
   128  }
   129  
   130  func TestRemoveImage(t *testing.T) {
   131  	d := newDriver(t)
   132  	defer os.RemoveAll(tmp)
   133  
   134  	if err := d.Create("1", "", nil); err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	if err := d.Remove("1"); err != nil {
   139  		t.Fatal(err)
   140  	}
   141  
   142  	paths := []string{
   143  		"mnt",
   144  		"diff",
   145  		"layers",
   146  	}
   147  
   148  	for _, p := range paths {
   149  		if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
   150  			t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
   151  		}
   152  	}
   153  }
   154  
   155  func TestGetWithoutParent(t *testing.T) {
   156  	d := newDriver(t)
   157  	defer os.RemoveAll(tmp)
   158  
   159  	if err := d.Create("1", "", nil); err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	diffPath, err := d.Get("1", "")
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  	expected := path.Join(tmp, "diff", "1")
   168  	if diffPath != expected {
   169  		t.Fatalf("Expected path %s got %s", expected, diffPath)
   170  	}
   171  }
   172  
   173  func TestCleanupWithNoDirs(t *testing.T) {
   174  	d := newDriver(t)
   175  	defer os.RemoveAll(tmp)
   176  
   177  	if err := d.Cleanup(); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  }
   181  
   182  func TestCleanupWithDir(t *testing.T) {
   183  	d := newDriver(t)
   184  	defer os.RemoveAll(tmp)
   185  
   186  	if err := d.Create("1", "", nil); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	if err := d.Cleanup(); err != nil {
   191  		t.Fatal(err)
   192  	}
   193  }
   194  
   195  func TestMountedFalseResponse(t *testing.T) {
   196  	d := newDriver(t)
   197  	defer os.RemoveAll(tmp)
   198  
   199  	if err := d.Create("1", "", nil); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	response, err := d.mounted(d.getDiffPath("1"))
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	if response != false {
   209  		t.Fatalf("Response if dir id 1 is mounted should be false")
   210  	}
   211  }
   212  
   213  func TestMountedTrueReponse(t *testing.T) {
   214  	d := newDriver(t)
   215  	defer os.RemoveAll(tmp)
   216  	defer d.Cleanup()
   217  
   218  	if err := d.Create("1", "", nil); err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	if err := d.Create("2", "1", nil); err != nil {
   222  		t.Fatal(err)
   223  	}
   224  
   225  	_, err := d.Get("2", "")
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  
   230  	response, err := d.mounted(d.pathCache["2"])
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  
   235  	if response != true {
   236  		t.Fatalf("Response if dir id 2 is mounted should be true")
   237  	}
   238  }
   239  
   240  func TestMountWithParent(t *testing.T) {
   241  	d := newDriver(t)
   242  	defer os.RemoveAll(tmp)
   243  
   244  	if err := d.Create("1", "", nil); err != nil {
   245  		t.Fatal(err)
   246  	}
   247  	if err := d.Create("2", "1", nil); err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	defer func() {
   252  		if err := d.Cleanup(); err != nil {
   253  			t.Fatal(err)
   254  		}
   255  	}()
   256  
   257  	mntPath, err := d.Get("2", "")
   258  	if err != nil {
   259  		t.Fatal(err)
   260  	}
   261  	if mntPath == "" {
   262  		t.Fatal("mntPath should not be empty string")
   263  	}
   264  
   265  	expected := path.Join(tmp, "mnt", "2")
   266  	if mntPath != expected {
   267  		t.Fatalf("Expected %s got %s", expected, mntPath)
   268  	}
   269  }
   270  
   271  func TestRemoveMountedDir(t *testing.T) {
   272  	d := newDriver(t)
   273  	defer os.RemoveAll(tmp)
   274  
   275  	if err := d.Create("1", "", nil); err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	if err := d.Create("2", "1", nil); err != nil {
   279  		t.Fatal(err)
   280  	}
   281  
   282  	defer func() {
   283  		if err := d.Cleanup(); err != nil {
   284  			t.Fatal(err)
   285  		}
   286  	}()
   287  
   288  	mntPath, err := d.Get("2", "")
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	if mntPath == "" {
   293  		t.Fatal("mntPath should not be empty string")
   294  	}
   295  
   296  	mounted, err := d.mounted(d.pathCache["2"])
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  
   301  	if !mounted {
   302  		t.Fatalf("Dir id 2 should be mounted")
   303  	}
   304  
   305  	if err := d.Remove("2"); err != nil {
   306  		t.Fatal(err)
   307  	}
   308  }
   309  
   310  func TestCreateWithInvalidParent(t *testing.T) {
   311  	d := newDriver(t)
   312  	defer os.RemoveAll(tmp)
   313  
   314  	if err := d.Create("1", "docker", nil); err == nil {
   315  		t.Fatalf("Error should not be nil with parent does not exist")
   316  	}
   317  }
   318  
   319  func TestGetDiff(t *testing.T) {
   320  	d := newDriver(t)
   321  	defer os.RemoveAll(tmp)
   322  
   323  	if err := d.CreateReadWrite("1", "", nil); err != nil {
   324  		t.Fatal(err)
   325  	}
   326  
   327  	diffPath, err := d.Get("1", "")
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  
   332  	// Add a file to the diff path with a fixed size
   333  	size := int64(1024)
   334  
   335  	f, err := os.Create(path.Join(diffPath, "test_file"))
   336  	if err != nil {
   337  		t.Fatal(err)
   338  	}
   339  	if err := f.Truncate(size); err != nil {
   340  		t.Fatal(err)
   341  	}
   342  	f.Close()
   343  
   344  	a, err := d.Diff("1", "")
   345  	if err != nil {
   346  		t.Fatal(err)
   347  	}
   348  	if a == nil {
   349  		t.Fatalf("Archive should not be nil")
   350  	}
   351  }
   352  
   353  func TestChanges(t *testing.T) {
   354  	d := newDriver(t)
   355  	defer os.RemoveAll(tmp)
   356  
   357  	if err := d.Create("1", "", nil); err != nil {
   358  		t.Fatal(err)
   359  	}
   360  
   361  	if err := d.CreateReadWrite("2", "1", nil); err != nil {
   362  		t.Fatal(err)
   363  	}
   364  
   365  	defer func() {
   366  		if err := d.Cleanup(); err != nil {
   367  			t.Fatal(err)
   368  		}
   369  	}()
   370  
   371  	mntPoint, err := d.Get("2", "")
   372  	if err != nil {
   373  		t.Fatal(err)
   374  	}
   375  
   376  	// Create a file to save in the mountpoint
   377  	f, err := os.Create(path.Join(mntPoint, "test.txt"))
   378  	if err != nil {
   379  		t.Fatal(err)
   380  	}
   381  
   382  	if _, err := f.WriteString("testline"); err != nil {
   383  		t.Fatal(err)
   384  	}
   385  	if err := f.Close(); err != nil {
   386  		t.Fatal(err)
   387  	}
   388  
   389  	changes, err := d.Changes("2", "")
   390  	if err != nil {
   391  		t.Fatal(err)
   392  	}
   393  	if len(changes) != 1 {
   394  		t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
   395  	}
   396  	change := changes[0]
   397  
   398  	expectedPath := "/test.txt"
   399  	if change.Path != expectedPath {
   400  		t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
   401  	}
   402  
   403  	if change.Kind != archive.ChangeAdd {
   404  		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
   405  	}
   406  
   407  	if err := d.CreateReadWrite("3", "2", nil); err != nil {
   408  		t.Fatal(err)
   409  	}
   410  	mntPoint, err = d.Get("3", "")
   411  	if err != nil {
   412  		t.Fatal(err)
   413  	}
   414  
   415  	// Create a file to save in the mountpoint
   416  	f, err = os.Create(path.Join(mntPoint, "test2.txt"))
   417  	if err != nil {
   418  		t.Fatal(err)
   419  	}
   420  
   421  	if _, err := f.WriteString("testline"); err != nil {
   422  		t.Fatal(err)
   423  	}
   424  	if err := f.Close(); err != nil {
   425  		t.Fatal(err)
   426  	}
   427  
   428  	changes, err = d.Changes("3", "2")
   429  	if err != nil {
   430  		t.Fatal(err)
   431  	}
   432  
   433  	if len(changes) != 1 {
   434  		t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
   435  	}
   436  	change = changes[0]
   437  
   438  	expectedPath = "/test2.txt"
   439  	if change.Path != expectedPath {
   440  		t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
   441  	}
   442  
   443  	if change.Kind != archive.ChangeAdd {
   444  		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
   445  	}
   446  }
   447  
   448  func TestDiffSize(t *testing.T) {
   449  	d := newDriver(t)
   450  	defer os.RemoveAll(tmp)
   451  
   452  	if err := d.CreateReadWrite("1", "", nil); err != nil {
   453  		t.Fatal(err)
   454  	}
   455  
   456  	diffPath, err := d.Get("1", "")
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  
   461  	// Add a file to the diff path with a fixed size
   462  	size := int64(1024)
   463  
   464  	f, err := os.Create(path.Join(diffPath, "test_file"))
   465  	if err != nil {
   466  		t.Fatal(err)
   467  	}
   468  	if err := f.Truncate(size); err != nil {
   469  		t.Fatal(err)
   470  	}
   471  	s, err := f.Stat()
   472  	if err != nil {
   473  		t.Fatal(err)
   474  	}
   475  	size = s.Size()
   476  	if err := f.Close(); err != nil {
   477  		t.Fatal(err)
   478  	}
   479  
   480  	diffSize, err := d.DiffSize("1", "")
   481  	if err != nil {
   482  		t.Fatal(err)
   483  	}
   484  	if diffSize != size {
   485  		t.Fatalf("Expected size to be %d got %d", size, diffSize)
   486  	}
   487  }
   488  
   489  func TestChildDiffSize(t *testing.T) {
   490  	d := newDriver(t)
   491  	defer os.RemoveAll(tmp)
   492  	defer d.Cleanup()
   493  
   494  	if err := d.CreateReadWrite("1", "", nil); err != nil {
   495  		t.Fatal(err)
   496  	}
   497  
   498  	diffPath, err := d.Get("1", "")
   499  	if err != nil {
   500  		t.Fatal(err)
   501  	}
   502  
   503  	// Add a file to the diff path with a fixed size
   504  	size := int64(1024)
   505  
   506  	f, err := os.Create(path.Join(diffPath, "test_file"))
   507  	if err != nil {
   508  		t.Fatal(err)
   509  	}
   510  	if err := f.Truncate(size); err != nil {
   511  		t.Fatal(err)
   512  	}
   513  	s, err := f.Stat()
   514  	if err != nil {
   515  		t.Fatal(err)
   516  	}
   517  	size = s.Size()
   518  	if err := f.Close(); err != nil {
   519  		t.Fatal(err)
   520  	}
   521  
   522  	diffSize, err := d.DiffSize("1", "")
   523  	if err != nil {
   524  		t.Fatal(err)
   525  	}
   526  	if diffSize != size {
   527  		t.Fatalf("Expected size to be %d got %d", size, diffSize)
   528  	}
   529  
   530  	if err := d.Create("2", "1", nil); err != nil {
   531  		t.Fatal(err)
   532  	}
   533  
   534  	diffSize, err = d.DiffSize("2", "1")
   535  	if err != nil {
   536  		t.Fatal(err)
   537  	}
   538  	// The diff size for the child should be zero
   539  	if diffSize != 0 {
   540  		t.Fatalf("Expected size to be %d got %d", 0, diffSize)
   541  	}
   542  }
   543  
   544  func TestExists(t *testing.T) {
   545  	d := newDriver(t)
   546  	defer os.RemoveAll(tmp)
   547  	defer d.Cleanup()
   548  
   549  	if err := d.Create("1", "", nil); err != nil {
   550  		t.Fatal(err)
   551  	}
   552  
   553  	if d.Exists("none") {
   554  		t.Fatal("id none should not exist in the driver")
   555  	}
   556  
   557  	if !d.Exists("1") {
   558  		t.Fatal("id 1 should exist in the driver")
   559  	}
   560  }
   561  
   562  func TestStatus(t *testing.T) {
   563  	d := newDriver(t)
   564  	defer os.RemoveAll(tmp)
   565  	defer d.Cleanup()
   566  
   567  	if err := d.Create("1", "", nil); err != nil {
   568  		t.Fatal(err)
   569  	}
   570  
   571  	status := d.Status()
   572  	if status == nil || len(status) == 0 {
   573  		t.Fatal("Status should not be nil or empty")
   574  	}
   575  	rootDir := status[0]
   576  	dirs := status[2]
   577  	if rootDir[0] != "Root Dir" {
   578  		t.Fatalf("Expected Root Dir got %s", rootDir[0])
   579  	}
   580  	if rootDir[1] != d.rootPath() {
   581  		t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
   582  	}
   583  	if dirs[0] != "Dirs" {
   584  		t.Fatalf("Expected Dirs got %s", dirs[0])
   585  	}
   586  	if dirs[1] != "1" {
   587  		t.Fatalf("Expected 1 got %s", dirs[1])
   588  	}
   589  }
   590  
   591  func TestApplyDiff(t *testing.T) {
   592  	d := newDriver(t)
   593  	defer os.RemoveAll(tmp)
   594  	defer d.Cleanup()
   595  
   596  	if err := d.CreateReadWrite("1", "", nil); err != nil {
   597  		t.Fatal(err)
   598  	}
   599  
   600  	diffPath, err := d.Get("1", "")
   601  	if err != nil {
   602  		t.Fatal(err)
   603  	}
   604  
   605  	// Add a file to the diff path with a fixed size
   606  	size := int64(1024)
   607  
   608  	f, err := os.Create(path.Join(diffPath, "test_file"))
   609  	if err != nil {
   610  		t.Fatal(err)
   611  	}
   612  	if err := f.Truncate(size); err != nil {
   613  		t.Fatal(err)
   614  	}
   615  	f.Close()
   616  
   617  	diff, err := d.Diff("1", "")
   618  	if err != nil {
   619  		t.Fatal(err)
   620  	}
   621  
   622  	if err := d.Create("2", "", nil); err != nil {
   623  		t.Fatal(err)
   624  	}
   625  	if err := d.Create("3", "2", nil); err != nil {
   626  		t.Fatal(err)
   627  	}
   628  
   629  	if err := d.applyDiff("3", diff); err != nil {
   630  		t.Fatal(err)
   631  	}
   632  
   633  	// Ensure that the file is in the mount point for id 3
   634  
   635  	mountPoint, err := d.Get("3", "")
   636  	if err != nil {
   637  		t.Fatal(err)
   638  	}
   639  	if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
   640  		t.Fatal(err)
   641  	}
   642  }
   643  
   644  func hash(c string) string {
   645  	h := sha256.New()
   646  	fmt.Fprint(h, c)
   647  	return hex.EncodeToString(h.Sum(nil))
   648  }
   649  
   650  func testMountMoreThan42Layers(t *testing.T, mountPath string) {
   651  	if err := os.MkdirAll(mountPath, 0755); err != nil {
   652  		t.Fatal(err)
   653  	}
   654  
   655  	defer os.RemoveAll(mountPath)
   656  	d := testInit(mountPath, t).(*Driver)
   657  	defer d.Cleanup()
   658  	var last string
   659  	var expected int
   660  
   661  	for i := 1; i < 127; i++ {
   662  		expected++
   663  		var (
   664  			parent  = fmt.Sprintf("%d", i-1)
   665  			current = fmt.Sprintf("%d", i)
   666  		)
   667  
   668  		if parent == "0" {
   669  			parent = ""
   670  		} else {
   671  			parent = hash(parent)
   672  		}
   673  		current = hash(current)
   674  
   675  		if err := d.CreateReadWrite(current, parent, nil); err != nil {
   676  			t.Logf("Current layer %d", i)
   677  			t.Error(err)
   678  		}
   679  		point, err := d.Get(current, "")
   680  		if err != nil {
   681  			t.Logf("Current layer %d", i)
   682  			t.Error(err)
   683  		}
   684  		f, err := os.Create(path.Join(point, current))
   685  		if err != nil {
   686  			t.Logf("Current layer %d", i)
   687  			t.Error(err)
   688  		}
   689  		f.Close()
   690  
   691  		if i%10 == 0 {
   692  			if err := os.Remove(path.Join(point, parent)); err != nil {
   693  				t.Logf("Current layer %d", i)
   694  				t.Error(err)
   695  			}
   696  			expected--
   697  		}
   698  		last = current
   699  	}
   700  
   701  	// Perform the actual mount for the top most image
   702  	point, err := d.Get(last, "")
   703  	if err != nil {
   704  		t.Error(err)
   705  	}
   706  	files, err := ioutil.ReadDir(point)
   707  	if err != nil {
   708  		t.Error(err)
   709  	}
   710  	if len(files) != expected {
   711  		t.Errorf("Expected %d got %d", expected, len(files))
   712  	}
   713  }
   714  
   715  func TestMountMoreThan42Layers(t *testing.T) {
   716  	os.RemoveAll(tmpOuter)
   717  	testMountMoreThan42Layers(t, tmp)
   718  }
   719  
   720  func TestMountMoreThan42LayersMatchingPathLength(t *testing.T) {
   721  	defer os.RemoveAll(tmpOuter)
   722  	zeroes := "0"
   723  	for {
   724  		// This finds a mount path so that when combined into aufs mount options
   725  		// 4096 byte boundary would be in between the paths or in permission
   726  		// section. For '/tmp' it will use '/tmp/aufs-tests/00000000/aufs'
   727  		mountPath := path.Join(tmpOuter, zeroes, "aufs")
   728  		pathLength := 77 + len(mountPath)
   729  
   730  		if mod := 4095 % pathLength; mod == 0 || mod > pathLength-2 {
   731  			t.Logf("Using path: %s", mountPath)
   732  			testMountMoreThan42Layers(t, mountPath)
   733  			return
   734  		}
   735  		zeroes += "0"
   736  	}
   737  }
   738  
   739  func BenchmarkConcurrentAccess(b *testing.B) {
   740  	b.StopTimer()
   741  	b.ResetTimer()
   742  
   743  	d := newDriver(b)
   744  	defer os.RemoveAll(tmp)
   745  	defer d.Cleanup()
   746  
   747  	numConcurent := 256
   748  	// create a bunch of ids
   749  	var ids []string
   750  	for i := 0; i < numConcurent; i++ {
   751  		ids = append(ids, stringid.GenerateNonCryptoID())
   752  	}
   753  
   754  	if err := d.Create(ids[0], "", nil); err != nil {
   755  		b.Fatal(err)
   756  	}
   757  
   758  	if err := d.Create(ids[1], ids[0], nil); err != nil {
   759  		b.Fatal(err)
   760  	}
   761  
   762  	parent := ids[1]
   763  	ids = append(ids[2:])
   764  
   765  	chErr := make(chan error, numConcurent)
   766  	var outerGroup sync.WaitGroup
   767  	outerGroup.Add(len(ids))
   768  	b.StartTimer()
   769  
   770  	// here's the actual bench
   771  	for _, id := range ids {
   772  		go func(id string) {
   773  			defer outerGroup.Done()
   774  			if err := d.Create(id, parent, nil); err != nil {
   775  				b.Logf("Create %s failed", id)
   776  				chErr <- err
   777  				return
   778  			}
   779  			var innerGroup sync.WaitGroup
   780  			for i := 0; i < b.N; i++ {
   781  				innerGroup.Add(1)
   782  				go func() {
   783  					d.Get(id, "")
   784  					d.Put(id)
   785  					innerGroup.Done()
   786  				}()
   787  			}
   788  			innerGroup.Wait()
   789  			d.Remove(id)
   790  		}(id)
   791  	}
   792  
   793  	outerGroup.Wait()
   794  	b.StopTimer()
   795  	close(chErr)
   796  	for err := range chErr {
   797  		if err != nil {
   798  			b.Log(err)
   799  			b.Fail()
   800  		}
   801  	}
   802  }