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  }