github.com/docker/cnab-to-oci@v0.3.0-beta4/remotes/fixup_test.go (about)

     1  package remotes
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"testing"
    11  
    12  	"github.com/cnabio/cnab-go/bundle"
    13  	"github.com/containerd/containerd/images"
    14  	"github.com/containerd/containerd/platforms"
    15  	"github.com/docker/cnab-to-oci/relocation"
    16  	"github.com/docker/distribution/reference"
    17  	"github.com/opencontainers/go-digest"
    18  	ocischemav1 "github.com/opencontainers/image-spec/specs-go/v1"
    19  	"gotest.tools/assert"
    20  )
    21  
    22  func TestFixupBundleWithAutoUpdate(t *testing.T) {
    23  	index := ocischemav1.Manifest{}
    24  	bufManifest, err := json.Marshal(index)
    25  	assert.NilError(t, err)
    26  	fetcher := &mockFetcher{indexBuffers: []*bytes.Buffer{
    27  		// Manifest index
    28  		bytes.NewBuffer(bufManifest),
    29  	}}
    30  	pusher := &mockPusher{}
    31  	resolver := &mockResolver{
    32  		pusher:  pusher,
    33  		fetcher: fetcher,
    34  		resolvedDescriptors: []ocischemav1.Descriptor{
    35  			// Resolving source Invocation image manifest descriptor my.registry/namespace/my-app-invoc
    36  			{
    37  				MediaType: ocischemav1.MediaTypeImageManifest,
    38  				Size:      42,
    39  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
    40  			},
    41  			// Target Invocation image manifest descriptor my.registry/namespace/my-app@sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343 for mounting
    42  			{},
    43  			// Resolving source service image manifest descriptor my.registry/namespace/my-service
    44  			{
    45  				MediaType: ocischemav1.MediaTypeImageManifest,
    46  				Size:      43,
    47  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
    48  			},
    49  			// Target service image manifest descriptor my.registry/namespace/my-app@sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344 for mounting
    50  			{},
    51  		},
    52  	}
    53  	b := &bundle.Bundle{
    54  		SchemaVersion: "v1.0.0",
    55  		InvocationImages: []bundle.InvocationImage{
    56  			{
    57  				BaseImage: bundle.BaseImage{
    58  					Image:     "my.registry/namespace/my-app-invoc",
    59  					ImageType: "docker",
    60  				},
    61  			},
    62  		},
    63  		Images: map[string]bundle.Image{
    64  			"my-service": {
    65  				BaseImage: bundle.BaseImage{
    66  					Image:     "my.registry/namespace/my-service",
    67  					ImageType: "docker",
    68  				},
    69  			},
    70  		},
    71  		Name:    "my-app",
    72  		Version: "0.1.0",
    73  	}
    74  	ref, err := reference.ParseNamed("my.registry/namespace/my-app")
    75  	assert.NilError(t, err)
    76  	_, err = FixupBundle(context.TODO(), b, ref, resolver, WithAutoBundleUpdate())
    77  	assert.NilError(t, err)
    78  	expectedBundle := &bundle.Bundle{
    79  		SchemaVersion: "v1.0.0",
    80  		InvocationImages: []bundle.InvocationImage{
    81  			{
    82  				BaseImage: bundle.BaseImage{
    83  					Image:     "my.registry/namespace/my-app-invoc",
    84  					ImageType: "docker",
    85  					MediaType: ocischemav1.MediaTypeImageManifest,
    86  					Size:      42,
    87  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
    88  				},
    89  			},
    90  		},
    91  		Images: map[string]bundle.Image{
    92  			"my-service": {
    93  				BaseImage: bundle.BaseImage{
    94  					Image:     "my.registry/namespace/my-service",
    95  					ImageType: "docker",
    96  					MediaType: ocischemav1.MediaTypeImageManifest,
    97  					Size:      43,
    98  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
    99  				},
   100  			},
   101  		},
   102  		Name:    "my-app",
   103  		Version: "0.1.0",
   104  	}
   105  	assert.DeepEqual(t, b, expectedBundle)
   106  }
   107  
   108  func TestFixupBundlePushImages(t *testing.T) {
   109  	index := ocischemav1.Manifest{}
   110  	bufManifest, err := json.Marshal(index)
   111  	assert.NilError(t, err)
   112  	fetcher := &mockFetcher{indexBuffers: []*bytes.Buffer{
   113  		// Manifest index
   114  		bytes.NewBuffer(bufManifest),
   115  	}}
   116  	pusher := &mockPusher{}
   117  	resolver := &mockResolver{
   118  		pusher:  pusher,
   119  		fetcher: fetcher,
   120  		resolvedDescriptors: []ocischemav1.Descriptor{
   121  			// Invocation image will not be resolved first, so push will occurs
   122  			{
   123  				// just a code to raise an error in the mock
   124  				Size: -1,
   125  			},
   126  			// Invocation image is resolved after push
   127  			{
   128  				MediaType: ocischemav1.MediaTypeImageManifest,
   129  				Size:      42,
   130  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   131  			},
   132  			// Image will be resolved after push based on Digest
   133  			{
   134  				MediaType: ocischemav1.MediaTypeImageManifest,
   135  				Size:      43,
   136  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   137  			},
   138  		},
   139  	}
   140  	b := &bundle.Bundle{
   141  		SchemaVersion: "v1.0.0",
   142  		InvocationImages: []bundle.InvocationImage{
   143  			{
   144  				BaseImage: bundle.BaseImage{
   145  					Image:     "my.registry/namespace/my-app-invoc",
   146  					ImageType: "docker",
   147  				},
   148  			},
   149  		},
   150  		Images: map[string]bundle.Image{
   151  			"my-service": {
   152  				BaseImage: bundle.BaseImage{
   153  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   154  					Image:     "",
   155  					ImageType: "docker",
   156  				},
   157  			},
   158  		},
   159  		Name:    "my-app",
   160  		Version: "0.1.0",
   161  	}
   162  	imageClient := newMockImageClient()
   163  	ref, err := reference.ParseNamed("my.registry/namespace/my-app")
   164  	assert.NilError(t, err)
   165  	_, err = FixupBundle(context.TODO(), b, ref, resolver, WithAutoBundleUpdate(), WithPushImages(imageClient, os.Stdout))
   166  	assert.NilError(t, err)
   167  	// 2 images has been pushed
   168  	assert.Equal(t, imageClient.pushedImages, 2)
   169  	expectedBundle := &bundle.Bundle{
   170  		SchemaVersion: "v1.0.0",
   171  		InvocationImages: []bundle.InvocationImage{
   172  			{
   173  				BaseImage: bundle.BaseImage{
   174  					Image:     "my.registry/namespace/my-app-invoc",
   175  					ImageType: "docker",
   176  					MediaType: ocischemav1.MediaTypeImageManifest,
   177  					Size:      42,
   178  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   179  				},
   180  			},
   181  		},
   182  		Images: map[string]bundle.Image{
   183  			"my-service": {
   184  				BaseImage: bundle.BaseImage{
   185  					Image:     "",
   186  					ImageType: "docker",
   187  					MediaType: ocischemav1.MediaTypeImageManifest,
   188  					Size:      43,
   189  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   190  				},
   191  			},
   192  		},
   193  		Name:    "my-app",
   194  		Version: "0.1.0",
   195  	}
   196  	assert.DeepEqual(t, b, expectedBundle)
   197  }
   198  
   199  func TestFixupBundleCheckResolveOrder(t *testing.T) {
   200  	index := ocischemav1.Manifest{}
   201  	bufManifest, err := json.Marshal(index)
   202  	assert.NilError(t, err)
   203  	fetcher := &mockFetcher{indexBuffers: []*bytes.Buffer{
   204  		// Manifest index
   205  		bytes.NewBuffer(bufManifest),
   206  	}}
   207  	pusher := &mockPusher{}
   208  
   209  	// Construct a test case
   210  	// - invocation map will be found in relocation map, resolved and copy
   211  	// - first service image will be found in relocation map but not resolved
   212  	//   then the service image in the bundle will be resolved and copy
   213  	// - second service image will be found in relocation map but not resolved
   214  	//   then the service image from the bundle will not be resolved
   215  	//   then the image will be found locally and pushed
   216  	// - third service, image is not found in relocation map
   217  	//   but resolved from bundle
   218  	// - fourth service, image is not found in relocation map, not resolvable
   219  	//   but pushed
   220  
   221  	b := &bundle.Bundle{
   222  		SchemaVersion: "v1.0.0",
   223  		InvocationImages: []bundle.InvocationImage{
   224  			{
   225  				BaseImage: bundle.BaseImage{
   226  					Image:     "resolvable-from-relocation-map",
   227  					ImageType: "docker",
   228  				},
   229  			},
   230  		},
   231  		Images: map[string]bundle.Image{
   232  			"resolved-from-bundle": {
   233  				BaseImage: bundle.BaseImage{
   234  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   235  					Image:     "not-resolved-from-relocation-map",
   236  					ImageType: "docker",
   237  				},
   238  			},
   239  			"local-push": {
   240  				BaseImage: bundle.BaseImage{
   241  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   242  					Image:     "local-image-with-relocation-entry",
   243  					ImageType: "docker",
   244  				},
   245  			},
   246  			"not-in-relocation": {
   247  				BaseImage: bundle.BaseImage{
   248  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   249  					Image:     "not-in-relocation",
   250  					ImageType: "docker",
   251  				},
   252  			},
   253  			"not-in-relocation-but-local": {
   254  				BaseImage: bundle.BaseImage{
   255  					Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   256  					Image:     "local-image-not-in-relocation",
   257  					ImageType: "docker",
   258  				},
   259  			},
   260  		},
   261  		Name:    "my-app",
   262  		Version: "0.1.0",
   263  	}
   264  	relocationMap := relocation.ImageRelocationMap{
   265  		"resolvable-from-relocation-map":    "my.registry/other-namespace/my-app-invoc@sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   266  		"not-resolved-from-relocation-map":  "my.registry/other-namespace/my-app-invoc@sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   267  		"local-image-with-relocation-entry": "my.registry/other-namespace/my-app-invoc@sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0344",
   268  	}
   269  	nbImagePushed := 2 // "local-push" and "not-in-relocation-but-local" services
   270  
   271  	resolver := &mockResolver{
   272  		pusher:  pusher,
   273  		fetcher: fetcher,
   274  		resolvedDescriptors: []ocischemav1.Descriptor{
   275  			// Invocation image first
   276  			// Resolvable
   277  			{
   278  				MediaType: ocischemav1.MediaTypeImageManifest,
   279  				Size:      42,
   280  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   281  			},
   282  			// This one is from the copy task
   283  			{
   284  				MediaType: ocischemav1.MediaTypeImageManifest,
   285  				Size:      42,
   286  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   287  			},
   288  
   289  			// First image "resolved-from-bundle"
   290  			// not resolvable from relocation map
   291  			{
   292  				Size: -1,
   293  			},
   294  			// resolved by second pass, from the bundle
   295  			{
   296  				MediaType: ocischemav1.MediaTypeImageManifest,
   297  				Size:      42,
   298  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   299  			},
   300  			// copy task
   301  			{
   302  				MediaType: ocischemav1.MediaTypeImageManifest,
   303  				Size:      42,
   304  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   305  			},
   306  
   307  			// Second image "local-push"
   308  			// not resolvable from relocation map
   309  			{
   310  				Size: -1,
   311  			},
   312  			// not resolvable from bundle
   313  			{
   314  				Size: -1,
   315  			},
   316  			// image is pushed, resolve is called at the end
   317  			{
   318  				MediaType: ocischemav1.MediaTypeImageManifest,
   319  				Size:      42,
   320  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   321  			},
   322  
   323  			// Third image "not-in-relocation"
   324  			// not in relocation map but resolvable
   325  			{
   326  				MediaType: ocischemav1.MediaTypeImageManifest,
   327  				Size:      42,
   328  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   329  			},
   330  			// copy task
   331  			{
   332  				MediaType: ocischemav1.MediaTypeImageManifest,
   333  				Size:      42,
   334  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   335  			},
   336  
   337  			// Fourth image "not-in-relocation-but-local"
   338  			// not resolvable
   339  			{
   340  				Size: -1,
   341  			},
   342  			// image is pushed, resolve is called at the end
   343  			{
   344  				MediaType: ocischemav1.MediaTypeImageManifest,
   345  				Size:      42,
   346  				Digest:    "sha256:beef1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   347  			},
   348  		},
   349  	}
   350  	imageClient := newMockImageClient()
   351  	ref, err := reference.ParseNamed("my.registry/namespace/my-app")
   352  	assert.NilError(t, err)
   353  	_, err = FixupBundle(context.TODO(), b, ref, resolver, WithAutoBundleUpdate(), WithPushImages(imageClient, os.Stdout), WithRelocationMap(relocationMap))
   354  	assert.NilError(t, err)
   355  	assert.Equal(t, imageClient.pushedImages, nbImagePushed)
   356  }
   357  
   358  func TestFixupBundleFailsWithDifferentDigests(t *testing.T) {
   359  	index := ocischemav1.Manifest{}
   360  	bufManifest, err := json.Marshal(index)
   361  	assert.NilError(t, err)
   362  	fetcher := &mockFetcher{indexBuffers: []*bytes.Buffer{
   363  		// Manifest index
   364  		bytes.NewBuffer(bufManifest),
   365  	}}
   366  	pusher := &mockPusher{}
   367  	resolver := &mockResolver{
   368  		pusher:  pusher,
   369  		fetcher: fetcher,
   370  		resolvedDescriptors: []ocischemav1.Descriptor{
   371  			// Invocation image manifest descriptor
   372  			{
   373  				MediaType: ocischemav1.MediaTypeImageManifest,
   374  				Size:      42,
   375  				Digest:    "sha256:c0ffeea7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   376  			},
   377  			{},
   378  		},
   379  	}
   380  	b := &bundle.Bundle{
   381  		SchemaVersion: "v1.0.0",
   382  		InvocationImages: []bundle.InvocationImage{
   383  			{
   384  				BaseImage: bundle.BaseImage{
   385  					Image:     "my.registry/namespace/my-app-invoc",
   386  					ImageType: "docker",
   387  					Digest:    "beef00a7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   388  					Size:      42,
   389  					MediaType: ocischemav1.MediaTypeImageManifest,
   390  				},
   391  			},
   392  		},
   393  		Name:    "my-app",
   394  		Version: "0.1.0",
   395  	}
   396  	ref, err := reference.ParseNamed("my.registry/namespace/my-app")
   397  	assert.NilError(t, err)
   398  	_, err = FixupBundle(context.TODO(), b, ref, resolver)
   399  	assert.ErrorContains(t, err, "digest differs")
   400  }
   401  
   402  func TestFixupBundleFailsWithDifferentSizes(t *testing.T) {
   403  	index := ocischemav1.Manifest{}
   404  	bufManifest, err := json.Marshal(index)
   405  	assert.NilError(t, err)
   406  	fetcher := &mockFetcher{indexBuffers: []*bytes.Buffer{
   407  		// Manifest index
   408  		bytes.NewBuffer(bufManifest),
   409  	}}
   410  	pusher := &mockPusher{}
   411  	resolver := &mockResolver{
   412  		pusher:  pusher,
   413  		fetcher: fetcher,
   414  		resolvedDescriptors: []ocischemav1.Descriptor{
   415  			// Invocation image manifest descriptor
   416  			{
   417  				MediaType: ocischemav1.MediaTypeImageManifest,
   418  				Size:      43,
   419  				Digest:    "sha256:c0ffeea7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   420  			},
   421  			{},
   422  		},
   423  	}
   424  
   425  	b := &bundle.Bundle{
   426  		SchemaVersion: "v1.0.0",
   427  		InvocationImages: []bundle.InvocationImage{
   428  			{
   429  				BaseImage: bundle.BaseImage{
   430  					Image:     "my.registry/namespace/my-app-invoc",
   431  					ImageType: "docker",
   432  					Digest:    "sha256:c0ffeea7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   433  					Size:      42,
   434  					MediaType: ocischemav1.MediaTypeImageManifest,
   435  				},
   436  			},
   437  		},
   438  		Name:    "my-app",
   439  		Version: "0.1.0",
   440  	}
   441  	ref, err := reference.ParseNamed("my.registry/namespace/my-app")
   442  	assert.NilError(t, err)
   443  	_, err = FixupBundle(context.TODO(), b, ref, resolver)
   444  	assert.ErrorContains(t, err, "size differs")
   445  }
   446  
   447  func TestFixupBundleFailsWithDifferentMediaTypes(t *testing.T) {
   448  	index := ocischemav1.Manifest{}
   449  	bufManifest, err := json.Marshal(index)
   450  	assert.NilError(t, err)
   451  	fetcher := &mockFetcher{indexBuffers: []*bytes.Buffer{
   452  		// Manifest index
   453  		bytes.NewBuffer(bufManifest),
   454  	}}
   455  	pusher := &mockPusher{}
   456  	resolver := &mockResolver{
   457  		pusher:  pusher,
   458  		fetcher: fetcher,
   459  		resolvedDescriptors: []ocischemav1.Descriptor{
   460  			// Invocation image manifest descriptor
   461  			{
   462  				MediaType: ocischemav1.MediaTypeImageIndex,
   463  				Size:      42,
   464  				Digest:    "sha256:c0ffeea7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   465  			},
   466  			{},
   467  		},
   468  	}
   469  
   470  	b := &bundle.Bundle{
   471  		SchemaVersion: "v1.0.0",
   472  		InvocationImages: []bundle.InvocationImage{
   473  			{
   474  				BaseImage: bundle.BaseImage{
   475  					Image:     "my.registry/namespace/my-app-invoc",
   476  					ImageType: "docker",
   477  					Digest:    "sha256:c0ffeea7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0343",
   478  					Size:      42,
   479  					MediaType: ocischemav1.MediaTypeImageManifest,
   480  				},
   481  			},
   482  		},
   483  		Name:    "my-app",
   484  		Version: "0.1.0",
   485  	}
   486  	ref, err := reference.ParseNamed("my.registry/namespace/my-app")
   487  	assert.NilError(t, err)
   488  	_, err = FixupBundle(context.TODO(), b, ref, resolver)
   489  	assert.ErrorContains(t, err, "media type differs")
   490  }
   491  
   492  func TestFixupPlatformShortPaths(t *testing.T) {
   493  	// those cases should not need to fetch any data
   494  	cases := []struct {
   495  		name      string
   496  		platform  string
   497  		mediaType string
   498  	}{
   499  		{
   500  			name:      "no-filter",
   501  			mediaType: ocischemav1.MediaTypeImageIndex,
   502  		},
   503  		{
   504  			name:      "oci-image",
   505  			platform:  "linux/amd64",
   506  			mediaType: ocischemav1.MediaTypeImageManifest,
   507  		},
   508  		{
   509  			name:      "docker-image",
   510  			platform:  "linux/amd64",
   511  			mediaType: images.MediaTypeDockerSchema2Manifest,
   512  		},
   513  		{
   514  			name:      "docker-image-schema1",
   515  			platform:  "linux/amd64",
   516  			mediaType: images.MediaTypeDockerSchema1Manifest,
   517  		},
   518  	}
   519  
   520  	for _, c := range cases {
   521  		t.Run(c.name, func(t *testing.T) {
   522  			var filter platforms.Matcher
   523  			if c.platform != "" {
   524  				filter = platforms.NewMatcher(platforms.MustParse(c.platform))
   525  			}
   526  			assert.NilError(t, fixupPlatforms(context.Background(), &bundle.BaseImage{}, relocation.ImageRelocationMap{}, &imageFixupInfo{
   527  				resolvedDescriptor: ocischemav1.Descriptor{
   528  					MediaType: c.mediaType,
   529  				},
   530  			}, nil, filter))
   531  		})
   532  	}
   533  }
   534  
   535  func TestFixupPlatforms(t *testing.T) {
   536  	cases := []struct {
   537  		name           string
   538  		manifest       *testManifest
   539  		filter         []string
   540  		expectedResult *testManifest
   541  		expectedError  string
   542  	}{
   543  		{
   544  			name:           "single-filter",
   545  			manifest:       newTestManifest("linux/amd64", "windows/amd64"),
   546  			filter:         []string{"linux/amd64"},
   547  			expectedResult: newTestManifest("linux/amd64"),
   548  		},
   549  		{
   550  			name:           "multi-filter",
   551  			manifest:       newTestManifest("linux/amd64", "windows/amd64", "linux/arm64"),
   552  			filter:         []string{"linux/amd64", "linux/arm64"},
   553  			expectedResult: newTestManifest("linux/amd64", "linux/arm64"),
   554  		},
   555  
   556  		{
   557  			name:          "no-match",
   558  			manifest:      newTestManifest("linux/amd64", "windows/amd64"),
   559  			filter:        []string{"linux/arm64"},
   560  			expectedError: `no descriptor matching the platform filter found in "docker.io/docker/test@sha256:4ff4130e3c087b3dd1ce3d7e9d29316e707c0a793783aa76380a14c1dba9b536"`,
   561  		},
   562  	}
   563  	for _, c := range cases {
   564  		t.Run(c.name, func(t *testing.T) {
   565  			// parse filter
   566  			plats, err := toPlatforms(c.filter)
   567  			assert.NilError(t, err)
   568  			filter := platforms.Any(plats...)
   569  
   570  			// setup fixupinfo, baseImage
   571  			sourceBytes, err := json.Marshal(c.manifest)
   572  			assert.NilError(t, err)
   573  			sourceDigest := digest.FromBytes(sourceBytes)
   574  			sourceRepo, err := reference.ParseNormalizedNamed("docker/test")
   575  			assert.NilError(t, err)
   576  			targetRepo, err := reference.ParseNormalizedNamed("docker/target")
   577  			assert.NilError(t, err)
   578  			sourceRef, err := reference.WithDigest(sourceRepo, sourceDigest)
   579  			assert.NilError(t, err)
   580  			bi := bundle.BaseImage{
   581  				Image: sourceRef.String(),
   582  			}
   583  			fixupInfo := &imageFixupInfo{
   584  				resolvedDescriptor: ocischemav1.Descriptor{
   585  					Digest:    sourceDigest,
   586  					Size:      int64(len(sourceBytes)),
   587  					MediaType: ocischemav1.MediaTypeImageIndex,
   588  				},
   589  				targetRepo: targetRepo,
   590  				sourceRef:  sourceRef,
   591  			}
   592  
   593  			// setup source fetcher
   594  			sourceFetcher := newSourceFetcherWithLocalData(bytesFetcher(sourceBytes))
   595  
   596  			// fixup
   597  			err = fixupPlatforms(context.Background(), &bi, relocation.ImageRelocationMap{}, fixupInfo, sourceFetcher, filter)
   598  			if c.expectedError != "" {
   599  				assert.ErrorContains(t, err, c.expectedError)
   600  				return
   601  			}
   602  			assert.NilError(t, err)
   603  
   604  			// baseImage.Image should have changed
   605  			// assert.Check(t, bi.Image != sourceRef.String())
   606  			// resolved digest should have changed
   607  			assert.Check(t, fixupInfo.resolvedDescriptor.Digest != sourceDigest)
   608  
   609  			// parsing back the resolved manifest and making sure extra fields are still there
   610  			resolvedReader, err := sourceFetcher.Fetch(context.Background(), fixupInfo.resolvedDescriptor)
   611  			assert.NilError(t, err)
   612  			defer resolvedReader.Close()
   613  			resolvedBytes, err := ioutil.ReadAll(resolvedReader)
   614  			assert.NilError(t, err)
   615  			var resolvedManifest testManifest
   616  			assert.NilError(t, json.Unmarshal(resolvedBytes, &resolvedManifest))
   617  			assert.DeepEqual(t, &resolvedManifest, c.expectedResult)
   618  		})
   619  	}
   620  }
   621  
   622  type testManifest struct {
   623  	Manifests []testDescriptor `json:"manifests"`
   624  	Foo       string           `json:"foo"`
   625  }
   626  
   627  type testDescriptor struct {
   628  	Platform *ocischemav1.Platform `json:"platform,omitempty"`
   629  	Bar      string                `json:"bar"`
   630  }
   631  
   632  func newTestManifest(plats ...string) *testManifest {
   633  	m := &testManifest{
   634  		Foo: "bar",
   635  	}
   636  	for _, p := range plats {
   637  		plat := platforms.MustParse(p)
   638  		m.Manifests = append(m.Manifests, testDescriptor{
   639  			Bar:      "baz",
   640  			Platform: &plat,
   641  		})
   642  	}
   643  	return m
   644  }
   645  
   646  type bytesFetcher []byte
   647  
   648  func (f bytesFetcher) Fetch(_ context.Context, _ ocischemav1.Descriptor) (io.ReadCloser, error) {
   649  	reader := bytes.NewReader(f)
   650  	return ioutil.NopCloser(reader), nil
   651  }