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 }