github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/tests/rkt_fetch_test.go (about) 1 // Copyright 2015 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 // +build host coreos src kvm 16 17 package main 18 19 import ( 20 "fmt" 21 "io" 22 "io/ioutil" 23 "net/http" 24 "net/http/httptest" 25 "os" 26 "path/filepath" 27 "strconv" 28 "strings" 29 "sync" 30 "testing" 31 "time" 32 33 "github.com/rkt/rkt/common" 34 "github.com/rkt/rkt/pkg/aci/acitest" 35 "github.com/rkt/rkt/tests/testutils" 36 taas "github.com/rkt/rkt/tests/testutils/aci-server" 37 38 "github.com/appc/spec/schema" 39 "github.com/appc/spec/schema/types" 40 ) 41 42 // TestFetchFromFile tests that 'rkt fetch/run/prepare' for a file will always 43 // fetch the file regardless of the specified behavior (default, store only, 44 // remote only). 45 func TestFetchFromFile(t *testing.T) { 46 image := "rkt-inspect-implicit-fetch.aci" 47 imagePath := patchTestACI(image, "--exec=/inspect") 48 49 defer os.Remove(imagePath) 50 51 tests := []struct { 52 args string 53 image string 54 }{ 55 {"--insecure-options=image --debug fetch", imagePath}, 56 {"--insecure-options=image --debug fetch --store-only", imagePath}, 57 {"--insecure-options=image --debug fetch --no-store", imagePath}, 58 {"--insecure-options=image --debug run --mds-register=false", imagePath}, 59 {"--insecure-options=image --debug run --mds-register=false --store-only", imagePath}, 60 {"--insecure-options=image --debug run --mds-register=false --no-store", imagePath}, 61 {"--insecure-options=image --debug prepare", imagePath}, 62 {"--insecure-options=image --debug prepare --store-only", imagePath}, 63 {"--insecure-options=image --debug prepare --no-store", imagePath}, 64 } 65 66 for _, tt := range tests { 67 testFetchFromFile(t, tt.args, tt.image) 68 } 69 } 70 71 func testFetchFromFile(t *testing.T, arg string, image string) { 72 fetchFromFileMsg := fmt.Sprintf("using image from file %s", image) 73 74 ctx := testutils.NewRktRunCtx() 75 defer ctx.Cleanup() 76 77 cmd := fmt.Sprintf("%s %s %s", ctx.Cmd(), arg, image) 78 79 // 1. Run cmd, should get $fetchFromFileMsg. 80 child := spawnOrFail(t, cmd) 81 if err := expectWithOutput(child, fetchFromFileMsg); err != nil { 82 t.Fatalf("%q should be found: %v", fetchFromFileMsg, err) 83 } 84 waitOrFail(t, child, 0) 85 86 // 1. Run cmd again, should get $fetchFromFileMsg. 87 runRktAndCheckOutput(t, cmd, fetchFromFileMsg, false) 88 } 89 90 // TestFetchAny tests that 'rkt fetch/run/prepare' for any type (image name string 91 // or URL) except file:// URL will work with the default, store only 92 // (--store-only) and remote only (--no-store) behaviors. 93 func TestFetchAny(t *testing.T) { 94 image := "rkt-inspect-implicit-fetch.aci" 95 imagePath := patchTestACI(image, "--exec=/inspect") 96 97 defer os.Remove(imagePath) 98 99 aci_os, aci_arch := common.GetOSArch() 100 os_arch := aci_os + "-" + aci_arch 101 102 etcdVersion := "v3.2.2" 103 etcdImage := "coreos.com/etcd:" + etcdVersion 104 etcdURL := "https://github.com/coreos/etcd/releases/download/" + etcdVersion + "/etcd-" + etcdVersion + "-" + os_arch + ".aci" 105 106 dockerVersion := "latest" 107 dockerImage := "docker://busybox" 108 if aci_arch != "amd64" { 109 dockerImage = "docker://" + aci_arch + "/busybox" 110 } 111 dockerImageV := dockerImage + ":" + dockerVersion 112 113 tests := []struct { 114 args string 115 image string 116 imageArgs string 117 finalURL string 118 }{ 119 {"--insecure-options=image --debug fetch", etcdImage, "", etcdURL}, 120 {"--insecure-options=image --debug fetch", etcdURL, "", etcdURL}, 121 {"--insecure-options=image --debug fetch", dockerImage, "", dockerImage}, 122 {"--insecure-options=image --debug fetch", dockerImageV, "", dockerImageV}, 123 {"--insecure-options=image --debug run --mds-register=false", etcdImage, "--exec /etcdctl", etcdURL}, 124 {"--insecure-options=image --debug run --mds-register=false", etcdURL, "--exec /etcdctl", etcdURL}, 125 {"--insecure-options=image --debug run --mds-register=false", dockerImage, "", dockerImage}, 126 {"--insecure-options=image --debug run --mds-register=false", dockerImageV, "", dockerImageV}, 127 {"--insecure-options=image --debug prepare", etcdURL, "", etcdURL}, 128 {"--insecure-options=image --debug prepare", etcdImage, "", etcdURL}, 129 // test --insecure-options=tls to make sure 130 // https://github.com/rkt/rkt/issues/1829 is not an issue anymore 131 {"--insecure-options=image,tls --debug prepare", dockerImage, "", dockerImage}, 132 {"--insecure-options=image --debug prepare", dockerImageV, "", dockerImageV}, 133 } 134 135 for _, tt := range tests { 136 testFetchNew(t, tt.args, tt.image, tt.imageArgs, tt.finalURL) 137 testFetchNever(t, tt.args, tt.image, tt.imageArgs, tt.finalURL) 138 testFetchUpdate(t, tt.args, tt.image, tt.imageArgs, tt.finalURL) 139 } 140 } 141 142 func TestFetchFullHash(t *testing.T) { 143 imagePath := getInspectImagePath() 144 145 ctx := testutils.NewRktRunCtx() 146 defer ctx.Cleanup() 147 148 tests := []struct { 149 fetchArgs string 150 expectedHashLength int 151 }{ 152 {"", len("sha512-") + 32}, 153 {"--full", len("sha512-") + 64}, 154 } 155 156 for _, tt := range tests { 157 hash, err := importImageAndFetchHash(t, ctx, tt.fetchArgs, imagePath) 158 if err != nil { 159 t.Fatalf("%v", err) 160 } 161 if len(hash) != tt.expectedHashLength { 162 t.Fatalf("expected hash length of %d, got %d", tt.expectedHashLength, len(hash)) 163 } 164 } 165 } 166 167 func testFetchNew(t *testing.T, arg string, image string, imageArgs string, finalURL string) { 168 remoteFetchMsgTpl := `remote fetching from URL %q` 169 storeMsgTpl := `using image from local store for .* %s` 170 if finalURL == "" { 171 finalURL = image 172 } 173 remoteFetchMsg := fmt.Sprintf(remoteFetchMsgTpl, finalURL) 174 storeMsg := fmt.Sprintf(storeMsgTpl, image) 175 176 ctx := testutils.NewRktRunCtx() 177 defer ctx.Cleanup() 178 179 cmd := fmt.Sprintf("%s --pull-policy=new %s %s %s", ctx.Cmd(), arg, image, imageArgs) 180 181 // 1. Run cmd with the image not available in the store, should get $remoteFetchMsg. 182 err := runRktAndCheckRegexOutput(t, cmd, remoteFetchMsg) 183 status, _ := common.GetExitStatus(err) 184 if status != 0 { 185 t.Logf("%v", err) 186 t.Skip("remote fetching failed, probably a network failure. Skipping...") 187 } 188 189 // 2. Run cmd with the image available in the store, should get $storeMsg. 190 runRktAndCheckRegexOutput(t, cmd, storeMsg) 191 } 192 193 func testFetchNever(t *testing.T, args string, image string, imageArgs string, finalURL string) { 194 cannotFetchMsgTpl := `unable to fetch.* image from .* %q` 195 storeMsgTpl := `using image from local store for .* %s` 196 cannotFetchMsg := fmt.Sprintf(cannotFetchMsgTpl, image) 197 storeMsg := fmt.Sprintf(storeMsgTpl, image) 198 199 ctx := testutils.NewRktRunCtx() 200 defer ctx.Cleanup() 201 202 cmd := fmt.Sprintf("%s --pull-policy=never %s %s %s", ctx.Cmd(), args, image, imageArgs) 203 204 // 1. Run cmd with the image not available in the store should get $cannotFetchMsg. 205 runRktAndCheckRegexOutput(t, cmd, cannotFetchMsg) 206 207 if _, err := importImageAndFetchHash(t, ctx, "", image); err != nil { 208 t.Skip(fmt.Sprintf("%v, probably a network failure. Skipping...", err)) 209 } 210 211 // 2. Run cmd with the image available in the store, should get $storeMsg. 212 runRktAndCheckRegexOutput(t, cmd, storeMsg) 213 } 214 215 func testFetchUpdate(t *testing.T, args string, image string, imageArgs string, finalURL string) { 216 remoteFetchMsgTpl := `remote fetching from URL %q` 217 remoteFetchMsg := fmt.Sprintf(remoteFetchMsgTpl, finalURL) 218 219 ctx := testutils.NewRktRunCtx() 220 defer ctx.Cleanup() 221 222 if _, err := importImageAndFetchHash(t, ctx, "", image); err != nil { 223 t.Skip(fmt.Sprintf("%v, probably a network failure. Skipping...", err)) 224 } 225 226 cmd := fmt.Sprintf("%s --pull-policy=update %s %s %s", ctx.Cmd(), args, image, imageArgs) 227 228 // 1. Run cmd with the image available in the store, should get $remoteFetchMsg. 229 err := runRktAndCheckRegexOutput(t, cmd, remoteFetchMsg) 230 status, _ := common.GetExitStatus(err) 231 if status != 0 { 232 t.Logf("%v", err) 233 t.Skip("remote fetching failed, probably a network failure. Skipping...") 234 } 235 236 if err != nil { 237 t.Fatalf("%q should be found: %v", remoteFetchMsg, err) 238 } 239 } 240 241 func TestFetchNoStoreCacheControl(t *testing.T) { 242 imageName := "rkt-inspect-fetch-nostore-cachecontrol" 243 imageFileName := fmt.Sprintf("%s.aci", imageName) 244 // no spaces between words, because of an actool limitation 245 successMsg := "deferredSignatureDownloadWasSuccessful" 246 247 args := []string{ 248 fmt.Sprintf("--exec=/inspect --print-msg='%s'", successMsg), 249 fmt.Sprintf("--name=%s", imageName), 250 } 251 image := patchTestACI(imageFileName, args...) 252 defer os.Remove(image) 253 254 asc := runSignImage(t, image, 1) 255 defer os.Remove(asc) 256 ascBase := filepath.Base(asc) 257 258 setup := taas.GetDefaultServerSetup() 259 setup.Server = taas.ServerQuay 260 server := runServer(t, setup) 261 defer server.Close() 262 fileSet := make(map[string]string, 2) 263 fileSet[imageFileName] = image 264 fileSet[ascBase] = asc 265 if err := server.UpdateFileSet(fileSet); err != nil { 266 t.Fatalf("Failed to populate a file list in test aci server: %v", err) 267 } 268 269 ctx := testutils.NewRktRunCtx() 270 defer ctx.Cleanup() 271 272 runRktTrust(t, ctx, "", 1) 273 274 tests := []struct { 275 imageArg string 276 imageURL string 277 }{ 278 {"https://127.0.0.1/" + imageFileName, "https://127.0.0.1/" + imageFileName}, 279 {"localhost/" + imageName, "https://127.0.0.1:443/localhost/" + imageFileName}, 280 } 281 282 for _, tt := range tests { 283 cmd := fmt.Sprintf("%s --no-store --debug --insecure-options=tls,image fetch %s", ctx.Cmd(), tt.imageArg) 284 expectedMessage := fmt.Sprintf("fetching image from %s", tt.imageURL) 285 runRktAndCheckRegexOutput(t, cmd, expectedMessage) 286 287 cmd = fmt.Sprintf("%s --no-store --debug --insecure-options=tls,image fetch %s", ctx.Cmd(), tt.imageArg) 288 expectedMessage = fmt.Sprintf("image for %s isn't expired, not fetching.", tt.imageURL) 289 runRktAndCheckRegexOutput(t, cmd, expectedMessage) 290 291 ctx.Reset() 292 } 293 } 294 295 type synchronizedBool struct { 296 value bool 297 lock sync.Mutex 298 } 299 300 func (b *synchronizedBool) Read() bool { 301 b.lock.Lock() 302 value := b.value 303 b.lock.Unlock() 304 return value 305 } 306 307 func (b *synchronizedBool) Write(value bool) { 308 b.lock.Lock() 309 b.value = value 310 b.lock.Unlock() 311 } 312 313 func TestResumedFetch(t *testing.T) { 314 image := "rkt-inspect-implicit-fetch.aci" 315 imagePath := patchTestACI(image, "--exec=/inspect") 316 defer os.Remove(imagePath) 317 318 hash := types.ShortHash("sha512-" + getHashOrPanic(imagePath)) 319 320 kill := make(chan struct{}) 321 reportkill := make(chan struct{}) 322 323 shouldInterrupt := &synchronizedBool{} 324 shouldInterrupt.Write(true) 325 326 server := httptest.NewServer(testServerHandler(t, shouldInterrupt, imagePath, kill, reportkill)) 327 defer server.Close() 328 329 ctx := testutils.NewRktRunCtx() 330 defer ctx.Cleanup() 331 332 cmd := fmt.Sprintf("%s --no-store --insecure-options=image fetch %s", ctx.Cmd(), server.URL+"/image.aci") 333 child := spawnOrFail(t, cmd) 334 <-kill 335 err := child.Close() 336 if err != nil { 337 panic(err) 338 } 339 reportkill <- struct{}{} 340 341 // rkt has fetched the first half of the image 342 // If it fetches the first half again these channels will be written to. 343 // Closing them to make the test panic if they're written to. 344 close(kill) 345 close(reportkill) 346 347 child = spawnOrFail(t, cmd) 348 if _, _, err := expectRegexWithOutput(child, ".*"+hash); err != nil { 349 t.Fatalf("hash didn't match: %v", err) 350 } 351 waitOrFail(t, child, 0) 352 } 353 354 func TestResumedFetchInvalidCache(t *testing.T) { 355 image := "rkt-inspect-implicit-fetch.aci" 356 imagePath := patchTestACI(image, "--exec=/inspect") 357 defer os.Remove(imagePath) 358 359 hash := types.ShortHash("sha512-" + getHashOrPanic(imagePath)) 360 361 kill := make(chan struct{}) 362 reportkill := make(chan struct{}) 363 364 ctx := testutils.NewRktRunCtx() 365 defer ctx.Cleanup() 366 367 shouldInterrupt := &synchronizedBool{} 368 shouldInterrupt.Write(true) 369 370 // Fetch the first half of the image, and kill rkt once it reaches halfway. 371 server := httptest.NewServer(testServerHandler(t, shouldInterrupt, imagePath, kill, reportkill)) 372 defer server.Close() 373 cmd := fmt.Sprintf("%s --no-store --insecure-options=image fetch %s", ctx.Cmd(), server.URL+"/image.aci") 374 child := spawnOrFail(t, cmd) 375 <-kill 376 err := child.Close() 377 if err != nil { 378 panic(err) 379 } 380 reportkill <- struct{}{} 381 382 // Fetch the image again. The server doesn't support Etags or the 383 // Last-Modified header, so the cached version should be invalidated. If 384 // rkt tries to use the cache, the hash won't check out. 385 shouldInterrupt.Write(false) 386 child = spawnOrFail(t, cmd) 387 if _, s, err := expectRegexWithOutput(child, ".*"+hash); err != nil { 388 t.Fatalf("hash didn't match: %v\nin: %s", err, s) 389 } 390 waitOrFail(t, child, 0) 391 } 392 393 func testServerHandler(t *testing.T, shouldInterrupt *synchronizedBool, imagePath string, kill, waitforkill chan struct{}) http.HandlerFunc { 394 interruptingHandler := testInterruptingServerHandler(t, imagePath, kill, waitforkill) 395 simpleHandler := testSimpleServerHandler(t, imagePath) 396 397 return func(w http.ResponseWriter, r *http.Request) { 398 if shouldInterrupt.Read() { 399 interruptingHandler(w, r) 400 } else { 401 simpleHandler(w, r) 402 } 403 } 404 } 405 406 func testInterruptingServerHandler(t *testing.T, imagePath string, kill, waitforkill chan struct{}) http.HandlerFunc { 407 finfo, err := os.Stat(imagePath) 408 if err != nil { 409 panic(err) 410 } 411 cutoff := finfo.Size() / 2 412 return func(w http.ResponseWriter, r *http.Request) { 413 if r.Method == "HEAD" { 414 headers := w.Header() 415 headers["Accept-Ranges"] = []string{"bytes"} 416 headers["Last-Modified"] = []string{"Mon, 02 Jan 2006 15:04:05 MST"} 417 w.WriteHeader(http.StatusOK) 418 return 419 } 420 if r.Method != "GET" { 421 w.WriteHeader(http.StatusNotFound) 422 return 423 } 424 425 file, err := os.Open(imagePath) 426 if err != nil { 427 panic(err) 428 } 429 defer file.Close() 430 431 rangeHeaders, ok := r.Header["Range"] 432 if ok && len(rangeHeaders) == 1 && strings.HasPrefix(rangeHeaders[0], "bytes=") { 433 rangeHeader := rangeHeaders[0][6:] // The first (and only) range header, with len("bytes=") characters chopped off the front 434 tokens := strings.Split(rangeHeader, "-") 435 if len(tokens) != 2 { 436 t.Fatalf("couldn't parse range header: %q", rangeHeader) 437 } 438 439 start, err := strconv.Atoi(tokens[0]) 440 if err != nil { 441 if tokens[0] == "" { 442 start = 0 // If start wasn't specified, start at the beginning 443 } else { 444 t.Fatalf("requested non-int starting location: %s", tokens[0]) 445 } 446 } 447 end, err := strconv.Atoi(tokens[1]) 448 if err != nil { 449 if tokens[1] == "" { 450 end = int(finfo.Size()) - 1 // If end wasn't specified, end at the end 451 } else { 452 t.Fatalf("requested non-int ending location: %s", tokens[0]) 453 } 454 } 455 456 _, err = file.Seek(int64(start), os.SEEK_SET) 457 if err != nil { 458 panic(err) 459 } 460 461 _, err = io.CopyN(w, file, int64(end-start+1)) 462 if err != nil { 463 panic(err) 464 } 465 466 return 467 } 468 469 _, err = io.CopyN(w, file, cutoff) 470 if err != nil { 471 panic(err) 472 } 473 474 // sleep a bit before signaling that rkt should be killed since it 475 // might not have had time to write everything to disk 476 time.Sleep(time.Second) 477 kill <- struct{}{} 478 <-waitforkill 479 } 480 } 481 482 func testSimpleServerHandler(t *testing.T, imagePath string) http.HandlerFunc { 483 return func(w http.ResponseWriter, r *http.Request) { 484 if r.Method == "HEAD" { 485 w.WriteHeader(http.StatusOK) 486 return 487 } 488 if r.Method != "GET" { 489 w.WriteHeader(http.StatusNotFound) 490 return 491 } 492 493 file, err := os.Open(imagePath) 494 if err != nil { 495 panic(err) 496 } 497 defer file.Close() 498 499 _, err = io.Copy(w, file) 500 if err != nil { 501 panic(err) 502 } 503 } 504 } 505 506 func TestDeferredSignatureDownload(t *testing.T) { 507 imageName := "localhost/rkt-inspect-deferred-signature-download" 508 imageFileName := fmt.Sprintf("%s.aci", filepath.Base(imageName)) 509 // no spaces between words, because of an actool limitation 510 successMsg := "deferredSignatureDownloadWasSuccessful" 511 512 args := []string{ 513 fmt.Sprintf("--exec=/inspect --print-msg='%s'", successMsg), 514 fmt.Sprintf("--name=%s", imageName), 515 } 516 image := patchTestACI(imageFileName, args...) 517 defer os.Remove(image) 518 519 asc := runSignImage(t, image, 1) 520 defer os.Remove(asc) 521 ascBase := filepath.Base(asc) 522 523 setup := taas.GetDefaultServerSetup() 524 setup.Server = taas.ServerQuay 525 server := runServer(t, setup) 526 defer server.Close() 527 fileSet := make(map[string]string, 2) 528 fileSet[imageFileName] = image 529 fileSet[ascBase] = asc 530 if err := server.UpdateFileSet(fileSet); err != nil { 531 t.Fatalf("Failed to populate a file list in test aci server: %v", err) 532 } 533 534 ctx := testutils.NewRktRunCtx() 535 defer ctx.Cleanup() 536 537 runRktTrust(t, ctx, "", 1) 538 539 runCmd := fmt.Sprintf("%s --debug --insecure-options=tls run %s", ctx.Cmd(), imageName) 540 child := spawnOrFail(t, runCmd) 541 defer waitOrFail(t, child, 0) 542 543 expectedMessages := []string{ 544 "server requested deferring the signature download", 545 successMsg, 546 } 547 for _, msg := range expectedMessages { 548 if err := expectWithOutput(child, msg); err != nil { 549 t.Fatalf("Could not find expected msg %q, output follows:\n%v", msg, err) 550 } 551 } 552 } 553 554 func TestDifferentDiscoveryLabels(t *testing.T) { 555 const imageName = "localhost/rkt-test-different-discovery-labels-image" 556 557 aci_os, aci_arch := common.GetOSArch() 558 559 manifest, err := acitest.ImageManifestString(&schema.ImageManifest{ 560 Name: imageName, Labels: types.Labels{ 561 {"version", "1.2.0"}, 562 {"arch", aci_arch}, 563 {"os", aci_os}, 564 }, 565 }) 566 567 if err != nil { 568 t.Fatalf("unexpected error %v", err) 569 } 570 571 emptyImage := getEmptyImagePath() 572 tmpDir := mustTempDir("rkt-TestDifferentDiscoveryLabels-") 573 defer os.RemoveAll(tmpDir) 574 575 tmpManifest, err := ioutil.TempFile(tmpDir, "manifest") 576 if err != nil { 577 panic(fmt.Sprintf("Cannot create temp manifest: %v", err)) 578 } 579 if err := ioutil.WriteFile(tmpManifest.Name(), []byte(manifest), 0600); err != nil { 580 panic(fmt.Sprintf("Cannot write to temp manifest: %v", err)) 581 } 582 defer os.Remove(tmpManifest.Name()) 583 584 imageFileName := fmt.Sprintf("%s.aci", filepath.Base(imageName)) 585 image := patchACI(emptyImage, imageFileName, "--manifest", tmpManifest.Name()) 586 defer os.Remove(image) 587 588 asc := runSignImage(t, image, 1) 589 defer os.Remove(asc) 590 ascBase := filepath.Base(asc) 591 592 setup := taas.GetDefaultServerSetup() 593 server := runServer(t, setup) 594 defer server.Close() 595 fileSet := make(map[string]string, 2) 596 fileSet[imageFileName] = image 597 fileSet[ascBase] = asc 598 if err := server.UpdateFileSet(fileSet); err != nil { 599 t.Fatalf("Failed to populate a file list in test aci server: %v", err) 600 } 601 602 other_arch := "armv7b" 603 if aci_arch == "armv7b" { 604 other_arch = "amd64" 605 } 606 607 tests := []struct { 608 imageName string 609 expectedMessage string 610 }{ 611 {imageName + ":2.0", fmt.Sprintf("requested value for label %q: %q differs from fetched aci label value: %q", "version", "2.0", "1.2.0")}, 612 {imageName + ":latest", fmt.Sprintf("requested value for label %q: %q differs from fetched aci label value: %q", "version", "latest", "1.2.0")}, 613 {imageName + ",arch=" + other_arch, fmt.Sprintf("requested value for label %q: %q differs from fetched aci label value: %q", "arch", other_arch, aci_arch)}, 614 {imageName + ",unexistinglabel=bla", fmt.Sprintf("requested label %q not provided by the image manifest", "unexistinglabel")}, 615 } 616 617 for _, tt := range tests { 618 testDifferentDiscoveryNameLabels(t, tt.imageName, tt.expectedMessage) 619 } 620 } 621 622 func testDifferentDiscoveryNameLabels(t *testing.T, imageName string, expectedMessage string) { 623 ctx := testutils.NewRktRunCtx() 624 defer ctx.Cleanup() 625 626 runRktTrust(t, ctx, "", 1) 627 628 // Since aci-server provided meta tag template doesn't contains 629 // {version} {os} or {arch}, we can just ask for any version/os/arch 630 // and always get the same ACI 631 runCmd := fmt.Sprintf("%s --debug --insecure-options=tls fetch %s", ctx.Cmd(), imageName) 632 child := spawnOrFail(t, runCmd) 633 defer waitOrFail(t, child, 254) 634 635 if err := expectWithOutput(child, expectedMessage); err != nil { 636 t.Fatalf("Could not find expected msg %q, output follows:\n%v", expectedMessage, err) 637 } 638 }