github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/store/store_test.go (about)

     1  // Copyright 2014 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package store
    16  
    17  import (
    18  	"archive/tar"
    19  	"bytes"
    20  	"database/sql"
    21  	"encoding/hex"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/coreos/rkt/pkg/aci"
    29  	"github.com/coreos/rkt/pkg/multicall"
    30  	"github.com/coreos/rkt/pkg/sys"
    31  
    32  	"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
    33  )
    34  
    35  const tstprefix = "store-test"
    36  
    37  func init() {
    38  	multicall.MaybeExec()
    39  }
    40  
    41  func TestBlobStore(t *testing.T) {
    42  	dir, err := ioutil.TempDir("", tstprefix)
    43  	if err != nil {
    44  		t.Fatalf("error creating tempdir: %v", err)
    45  	}
    46  	defer os.RemoveAll(dir)
    47  	s, err := NewStore(dir)
    48  	if err != nil {
    49  		t.Fatalf("Unexpected error: %v", err)
    50  	}
    51  	defer s.Close()
    52  	for _, valueStr := range []string{
    53  		"I am a manually placed object",
    54  	} {
    55  		s.stores[blobType].Write(types.NewHashSHA512([]byte(valueStr)).String(), []byte(valueStr))
    56  	}
    57  
    58  	s.Dump(false)
    59  }
    60  
    61  func TestResolveKey(t *testing.T) {
    62  	dir, err := ioutil.TempDir("", tstprefix)
    63  	if err != nil {
    64  		t.Fatalf("error creating tempdir: %v", err)
    65  	}
    66  	defer os.RemoveAll(dir)
    67  	s, err := NewStore(dir)
    68  	if err != nil {
    69  		t.Fatalf("Unexpected error: %v", err)
    70  	}
    71  	defer s.Close()
    72  
    73  	// Return a hash key buffer from a hex string
    74  	str2key := func(s string) *bytes.Buffer {
    75  		k, _ := hex.DecodeString(s)
    76  		return bytes.NewBufferString(keyToString(k))
    77  	}
    78  
    79  	// Set up store (use key == data for simplicity)
    80  	data := []*bytes.Buffer{
    81  		str2key("12345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    82  		str2key("abcdefabc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    83  		str2key("abcabcabc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    84  		str2key("abc01234500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    85  		str2key("67147019a5b56f5e2ee01e989a8aa4787f56b8445960be2d8678391cf111009bc0780f31001fd181a2b61507547aee4caa44cda4b8bdb238d0e4ba830069ed2c"),
    86  	}
    87  	for _, d := range data {
    88  		// Save aciinfo
    89  		err := s.db.Do(func(tx *sql.Tx) error {
    90  			aciinfo := &ACIInfo{
    91  				BlobKey:    d.String(),
    92  				Name:       "example.com/app",
    93  				ImportTime: time.Now(),
    94  			}
    95  			return WriteACIInfo(tx, aciinfo)
    96  		})
    97  		if err != nil {
    98  			t.Fatalf("error writing to store: %v", err)
    99  		}
   100  	}
   101  
   102  	// Full key already - should return short version of the full key
   103  	fkl := "sha512-67147019a5b56f5e2ee01e989a8aa4787f56b8445960be2d8678391cf111009bc0780f31001fd181a2b61507547aee4caa44cda4b8bdb238d0e4ba830069ed2c"
   104  	fks := "sha512-67147019a5b56f5e2ee01e989a8aa4787f56b8445960be2d8678391cf111009b"
   105  	for _, k := range []string{fkl, fks} {
   106  		key, err := s.ResolveKey(k)
   107  		if key != fks {
   108  			t.Errorf("expected ResolveKey to return unaltered short key, but got %q", key)
   109  		}
   110  		if err != nil {
   111  			t.Errorf("expected err=nil, got %v", err)
   112  		}
   113  	}
   114  
   115  	// Unambiguous prefix match
   116  	k, err := s.ResolveKey("sha512-123")
   117  	if k != "sha512-1234567890000000000000000000000000000000000000000000000000000000" {
   118  		t.Errorf("expected %q, got %q", "sha512-1234567890000000000000000000000000000000000000000000000000000000", k)
   119  	}
   120  	if err != nil {
   121  		t.Errorf("expected err=nil, got %v", err)
   122  	}
   123  
   124  	// Ambiguous prefix match
   125  	k, err = s.ResolveKey("sha512-abc")
   126  	if k != "" {
   127  		t.Errorf("expected %q, got %q", "", k)
   128  	}
   129  	if err == nil {
   130  		t.Errorf("expected non-nil error!")
   131  	}
   132  
   133  	// wrong key prefix
   134  	k, err = s.ResolveKey("badprefix-1")
   135  	expectedErr := "wrong key prefix"
   136  	if err == nil {
   137  		t.Errorf("expected non-nil error!")
   138  	}
   139  	if err.Error() != expectedErr {
   140  		t.Errorf("expected err=%q, got %q", expectedErr, err)
   141  	}
   142  
   143  	// image ID too short
   144  	k, err = s.ResolveKey("sha512-1")
   145  	expectedErr = "image ID too short"
   146  	if err == nil {
   147  		t.Errorf("expected non-nil error!")
   148  	}
   149  	if err.Error() != expectedErr {
   150  		t.Errorf("expected err=%q, got %q", expectedErr, err)
   151  	}
   152  }
   153  
   154  func TestGetImageManifest(t *testing.T) {
   155  	dir, err := ioutil.TempDir("", tstprefix)
   156  	if err != nil {
   157  		t.Fatalf("error creating tempdir: %v", err)
   158  	}
   159  	defer os.RemoveAll(dir)
   160  	s, err := NewStore(dir)
   161  	if err != nil {
   162  		t.Fatalf("Unexpected error: %v", err)
   163  	}
   164  	defer s.Close()
   165  
   166  	imj := `{
   167  			"acKind": "ImageManifest",
   168  			"acVersion": "0.7.1",
   169  			"name": "example.com/test01"
   170  		}`
   171  
   172  	aci, err := aci.NewACI(dir, imj, nil)
   173  	if err != nil {
   174  		t.Fatalf("error creating test tar: %v", err)
   175  	}
   176  	// Rewind the ACI
   177  	if _, err := aci.Seek(0, 0); err != nil {
   178  		t.Fatalf("unexpected error %v", err)
   179  	}
   180  	key, err := s.WriteACI(aci, false)
   181  	if err != nil {
   182  		t.Fatalf("unexpected error: %v", err)
   183  	}
   184  
   185  	wanted := "example.com/test01"
   186  	im, err := s.GetImageManifest(key)
   187  	if err != nil {
   188  		t.Fatalf("unexpected error: %v", err)
   189  	}
   190  	if im.Name.String() != wanted {
   191  		t.Errorf("expected im with name: %s, got: %s", wanted, im.Name.String())
   192  	}
   193  
   194  	// test unexistent key
   195  	im, err = s.GetImageManifest("sha512-aaaaaaaaaaaaaaaaa")
   196  	if err == nil {
   197  		t.Fatalf("expected non-nil error!")
   198  	}
   199  }
   200  
   201  func TestGetAci(t *testing.T) {
   202  	type test struct {
   203  		name     types.ACIdentifier
   204  		labels   types.Labels
   205  		expected int // the aci index to expect or -1 if not result expected,
   206  	}
   207  
   208  	type acidef struct {
   209  		imj    string
   210  		latest bool
   211  	}
   212  
   213  	dir, err := ioutil.TempDir("", tstprefix)
   214  	if err != nil {
   215  		t.Fatalf("error creating tempdir: %v", err)
   216  	}
   217  	defer os.RemoveAll(dir)
   218  	s, err := NewStore(dir)
   219  	if err != nil {
   220  		t.Fatalf("unexpected error %v", err)
   221  	}
   222  	defer s.Close()
   223  
   224  	tests := []struct {
   225  		acidefs []acidef
   226  		tests   []test
   227  	}{
   228  		{
   229  			[]acidef{
   230  				{
   231  					`{
   232  						"acKind": "ImageManifest",
   233  						"acVersion": "0.7.1",
   234  						"name": "example.com/test01"
   235  					}`,
   236  					false,
   237  				},
   238  				{
   239  					`{
   240  						"acKind": "ImageManifest",
   241  						"acVersion": "0.7.1",
   242  						"name": "example.com/test02",
   243  						"labels": [
   244  							{
   245  								"name": "version",
   246  								"value": "1.0.0"
   247  							}
   248  						]
   249  					}`,
   250  					true,
   251  				},
   252  				{
   253  					`{
   254  						"acKind": "ImageManifest",
   255  						"acVersion": "0.7.1",
   256  						"name": "example.com/test02",
   257  						"labels": [
   258  							{
   259  								"name": "version",
   260  								"value": "2.0.0"
   261  							}
   262  						]
   263  					}`,
   264  					false,
   265  				},
   266  			},
   267  			[]test{
   268  				{
   269  					"example.com/unexistentaci",
   270  					types.Labels{},
   271  					-1,
   272  				},
   273  				{
   274  					"example.com/test01",
   275  					types.Labels{},
   276  					0,
   277  				},
   278  				{
   279  					"example.com/test02",
   280  					// Workaround for https://github.com/golang/go/issues/6820 :
   281  					// `go vet` does not correctly detect types.Labels as a container
   282  					[]types.Label{
   283  						{
   284  							Name:  "version",
   285  							Value: "1.0.0",
   286  						},
   287  					},
   288  					1,
   289  				},
   290  				{
   291  					"example.com/test02",
   292  					[]types.Label{
   293  						{
   294  							Name:  "version",
   295  							Value: "2.0.0",
   296  						},
   297  					},
   298  					2,
   299  				},
   300  				{
   301  					"example.com/test02",
   302  					types.Labels{},
   303  					1,
   304  				},
   305  			},
   306  		},
   307  	}
   308  
   309  	for _, tt := range tests {
   310  		var keys []string
   311  		// Create ACIs
   312  		for _, ad := range tt.acidefs {
   313  			aci, err := aci.NewACI(dir, ad.imj, nil)
   314  			if err != nil {
   315  				t.Fatalf("error creating test tar: %v", err)
   316  			}
   317  
   318  			// Rewind the ACI
   319  			if _, err := aci.Seek(0, 0); err != nil {
   320  				t.Fatalf("unexpected error %v", err)
   321  			}
   322  
   323  			key, err := s.WriteACI(aci, ad.latest)
   324  			if err != nil {
   325  				t.Fatalf("unexpected error: %v", err)
   326  			}
   327  			keys = append(keys, key)
   328  		}
   329  
   330  		for _, test := range tt.tests {
   331  			key, err := s.GetACI(test.name, test.labels)
   332  			if test.expected == -1 {
   333  				if err == nil {
   334  					t.Fatalf("Expected no key for name %s, got %s", test.name, key)
   335  				}
   336  
   337  			} else {
   338  				if err != nil {
   339  					t.Fatalf("unexpected error on GetACI for name %s, labels: %v: %v", test.name, test.labels, err)
   340  				}
   341  				if keys[test.expected] != key {
   342  					t.Errorf("expected key: %s, got %s. GetACI with name: %s, labels: %v", key, keys[test.expected], test.name, test.labels)
   343  				}
   344  			}
   345  		}
   346  	}
   347  }
   348  
   349  func TestTreeStore(t *testing.T) {
   350  	if !sys.HasChrootCapability() {
   351  		t.Skipf("chroot capability not available. Disabling test.")
   352  	}
   353  
   354  	dir, err := ioutil.TempDir("", tstprefix)
   355  	if err != nil {
   356  		t.Fatalf("error creating tempdir: %v", err)
   357  	}
   358  	defer os.RemoveAll(dir)
   359  	s, err := NewStore(dir)
   360  	if err != nil {
   361  		t.Fatalf("unexpected error: %v", err)
   362  	}
   363  	defer s.Close()
   364  
   365  	imj := `
   366  		{
   367  		    "acKind": "ImageManifest",
   368  		    "acVersion": "0.7.1",
   369  		    "name": "example.com/test01"
   370  		}
   371  	`
   372  
   373  	entries := []*aci.ACIEntry{
   374  		// An empty dir
   375  		{
   376  			Header: &tar.Header{
   377  				Name:     "rootfs/a",
   378  				Typeflag: tar.TypeDir,
   379  			},
   380  		},
   381  		{
   382  			Contents: "hello",
   383  			Header: &tar.Header{
   384  				Name: "hello.txt",
   385  				Size: 5,
   386  			},
   387  		},
   388  		{
   389  			Header: &tar.Header{
   390  				Name:     "rootfs/link.txt",
   391  				Linkname: "rootfs/hello.txt",
   392  				Typeflag: tar.TypeSymlink,
   393  			},
   394  		},
   395  		// dangling symlink
   396  		{
   397  			Header: &tar.Header{
   398  				Name:     "rootfs/link2.txt",
   399  				Linkname: "rootfs/missingfile.txt",
   400  				Typeflag: tar.TypeSymlink,
   401  			},
   402  		},
   403  		{
   404  			Header: &tar.Header{
   405  				Name:     "rootfs/fifo",
   406  				Typeflag: tar.TypeFifo,
   407  			},
   408  		},
   409  	}
   410  	aci, err := aci.NewACI(dir, imj, entries)
   411  	if err != nil {
   412  		t.Fatalf("error creating test tar: %v", err)
   413  	}
   414  	defer aci.Close()
   415  
   416  	// Rewind the ACI
   417  	if _, err := aci.Seek(0, 0); err != nil {
   418  		t.Fatalf("unexpected error %v", err)
   419  	}
   420  
   421  	// Import the new ACI
   422  	key, err := s.WriteACI(aci, false)
   423  	if err != nil {
   424  		t.Fatalf("unexpected error: %v", err)
   425  	}
   426  
   427  	// Ask the store to render the treestore
   428  	id, err := s.RenderTreeStore(key, false)
   429  	if err != nil {
   430  		t.Fatalf("unexpected error: %v", err)
   431  	}
   432  
   433  	// Verify image Hash. Should be the same.
   434  	err = s.CheckTreeStore(id)
   435  	if err != nil {
   436  		t.Fatalf("unexpected error: %v", err)
   437  	}
   438  
   439  	// Change a file permission
   440  	rootfs := s.GetTreeStoreRootFS(id)
   441  	err = os.Chmod(filepath.Join(rootfs, "a"), 0600)
   442  	if err != nil {
   443  		t.Fatalf("unexpected error: %v", err)
   444  	}
   445  
   446  	// Verify image Hash. Should be different
   447  	err = s.CheckTreeStore(id)
   448  	if err == nil {
   449  		t.Errorf("expected non-nil error!")
   450  	}
   451  
   452  	// rebuild the tree
   453  	prevID := id
   454  	id, err = s.RenderTreeStore(key, true)
   455  	if err != nil {
   456  		t.Fatalf("unexpected error: %v", err)
   457  	}
   458  
   459  	if id != prevID {
   460  		t.Fatalf("unexpected different IDs. prevID: %s, id: %s", prevID, id)
   461  	}
   462  
   463  	// Add a file
   464  	rootfs = s.GetTreeStoreRootFS(id)
   465  	err = ioutil.WriteFile(filepath.Join(rootfs, "newfile"), []byte("newfile"), 0644)
   466  	if err != nil {
   467  		t.Fatalf("unexpected error: %v", err)
   468  	}
   469  
   470  	// Verify image Hash. Should be different
   471  	err = s.CheckTreeStore(id)
   472  	if err == nil {
   473  		t.Errorf("expected non-nil error!")
   474  	}
   475  }
   476  
   477  func TestRemoveACI(t *testing.T) {
   478  	dir, err := ioutil.TempDir("", tstprefix)
   479  	if err != nil {
   480  		t.Fatalf("error creating tempdir: %v", err)
   481  	}
   482  	defer os.RemoveAll(dir)
   483  	s, err := NewStore(dir)
   484  	if err != nil {
   485  		t.Fatalf("Unexpected error: %v", err)
   486  	}
   487  	defer s.Close()
   488  
   489  	imj := `{
   490                      "acKind": "ImageManifest",
   491                      "acVersion": "0.7.1",
   492                      "name": "example.com/test01"
   493                  }`
   494  
   495  	aciFile, err := aci.NewACI(dir, imj, nil)
   496  	if err != nil {
   497  		t.Fatalf("error creating test tar: %v", err)
   498  	}
   499  	// Rewind the ACI
   500  	if _, err := aciFile.Seek(0, 0); err != nil {
   501  		t.Fatalf("unexpected error %v", err)
   502  	}
   503  	key, err := s.WriteACI(aciFile, false)
   504  	if err != nil {
   505  		t.Fatalf("unexpected error: %v", err)
   506  	}
   507  	aciURL := "http://example.com/test01.aci"
   508  	// Create our first Remote, and simulate Store() to create row in the table
   509  	na := NewRemote(aciURL, "")
   510  	na.BlobKey = key
   511  	s.WriteRemote(na)
   512  
   513  	err = s.RemoveACI(key)
   514  	if err != nil {
   515  		t.Fatalf("unexpected error: %v", err)
   516  	}
   517  
   518  	// Verify that no remote for the specified key exists
   519  	_, found, err := s.GetRemote(aciURL)
   520  	if err != nil {
   521  		t.Fatalf("unexpected error: %v", err)
   522  	}
   523  	if found {
   524  		t.Fatalf("expected to find no remote, but a remote was found")
   525  	}
   526  
   527  	// Try to remove a non-existent key
   528  	err = s.RemoveACI("sha512-aaaaaaaaaaaaaaaaa")
   529  	if err == nil {
   530  		t.Fatalf("expected error")
   531  	}
   532  
   533  	// Simulate error removing from the
   534  	imj = `{
   535                     "acKind": "ImageManifest",
   536                     "acVersion": "0.7.1",
   537                     "name": "example.com/test01"
   538                 }`
   539  
   540  	aciFile, err = aci.NewACI(dir, imj, nil)
   541  	if err != nil {
   542  		t.Fatalf("error creating test tar: %v", err)
   543  	}
   544  	// Rewind the ACI
   545  	if _, err := aciFile.Seek(0, 0); err != nil {
   546  		t.Fatalf("unexpected error %v", err)
   547  	}
   548  	key, err = s.WriteACI(aciFile, false)
   549  	if err != nil {
   550  		t.Fatalf("unexpected error: %v", err)
   551  	}
   552  	aciURL = "http://example.com/test02.aci"
   553  	// Create our first Remote, and simulate Store() to create row in the table
   554  	na = NewRemote(aciURL, "")
   555  	na.BlobKey = key
   556  	s.WriteRemote(na)
   557  
   558  	err = os.Remove(filepath.Join(dir, "cas", "blob", blockTransform(key)[0], blockTransform(key)[1], key))
   559  	if err != nil {
   560  		t.Fatalf("unexpected error: %v", err)
   561  	}
   562  
   563  	err = s.RemoveACI(key)
   564  	if err == nil {
   565  		t.Fatalf("expected error: %v", err)
   566  	}
   567  	if _, ok := err.(*StoreRemovalError); !ok {
   568  		t.Fatalf("expected StoreRemovalError got: %v", err)
   569  	}
   570  
   571  }