From fa7ddfd319b9d9dc92b8de8d8b5b325977208e03 Mon Sep 17 00:00:00 2001 From: Johannes Schicktanz Date: Tue, 1 Mar 2022 10:23:56 +0100 Subject: [PATCH] Fix image index copy (#71) * fix image index copy * refactors oci client * refactor ociclient * adds tests for GetRawManifest and PushRawManifest * update ociclient unit tests to new get/push manifest functions * fix tests * review feedback * fix test and adds deprecation to go doc --- ociclient/client.go | 176 +++++++-- ociclient/client_test.go | 347 ++++++++++++------ ociclient/copy.go | 41 ++- ociclient/types.go | 24 +- ociclient/utils.go | 11 + pkg/testutils/oci.go | 168 +++------ .../downloaders/downloaders_suite_test.go | 69 ++-- .../process/downloaders/oci_artifact_test.go | 10 - .../process/uploaders/oci_artifact_test.go | 27 +- .../utils/oci_artifact_serialization_test.go | 6 +- 10 files changed, 548 insertions(+), 331 deletions(-) diff --git a/ociclient/client.go b/ociclient/client.go index e9f62d0a..376866e9 100644 --- a/ociclient/client.go +++ b/ociclient/client.go @@ -165,7 +165,7 @@ func (c *client) GetOCIArtifact(ctx context.Context, ref string) (*oci.Artifact, return nil, err } - if desc.MediaType == ocispecv1.MediaTypeImageIndex || desc.MediaType == images.MediaTypeDockerSchema2ManifestList { + if IsMultiArchImage(desc.MediaType) { var index ocispecv1.Index if err := json.Unmarshal(data.Bytes(), &index); err != nil { return nil, err @@ -201,7 +201,7 @@ func (c *client) GetOCIArtifact(ctx context.Context, ref string) (*oci.Artifact, } return indexArtifact, nil - } else if desc.MediaType == ocispecv1.MediaTypeImageManifest || desc.MediaType == images.MediaTypeDockerSchema2Manifest { + } else if IsSingleArchImage(desc.MediaType) { var manifest ocispecv1.Manifest if err := json.Unmarshal(data.Bytes(), &manifest); err != nil { return nil, err @@ -291,6 +291,114 @@ func (c *client) PushBlob(ctx context.Context, ref string, desc ocispecv1.Descri return nil } +func (c *client) PushRawManifest(ctx context.Context, ref string, desc ocispecv1.Descriptor, rawManifest []byte, options ...PushOption) error { + if !IsSingleArchImage(desc.MediaType) && !IsMultiArchImage(desc.MediaType) { + return fmt.Errorf("media type is not an image manifest or image index: %s", desc.MediaType) + } + + tempCache := c.cache + if tempCache == nil { + tempCache = cache.NewInMemoryCache() + } + + opts := &PushOptions{} + opts.ApplyOptions(options) + if opts.Store == nil { + opts.ApplyOptions([]PushOption{WithStore(tempCache)}) + } + + resolver, err := c.getResolverForRef(ctx, ref, transport.PushScope) + if err != nil { + return err + } + + pusher, err := resolver.Pusher(ctx, ref) + if err != nil { + return err + } + + if IsSingleArchImage(desc.MediaType) { + manifest := ocispecv1.Manifest{} + if err := json.Unmarshal(rawManifest, &manifest); err != nil { + return fmt.Errorf("unable to unmarshal manifest: %w", err) + } + + // add dummy config if it is not set + if manifest.Config.Size == 0 { + dummyConfig := []byte("{}") + dummyDesc := ocispecv1.Descriptor{ + MediaType: "application/json", + Digest: digest.FromBytes(dummyConfig), + Size: int64(len(dummyConfig)), + } + if err := tempCache.Add(dummyDesc, ioutil.NopCloser(bytes.NewBuffer(dummyConfig))); err != nil { + return fmt.Errorf("unable to add dummy config to cache: %w", err) + } + if err := c.pushContent(ctx, tempCache, pusher, dummyDesc); err != nil { + return fmt.Errorf("unable to push dummy config: %w", err) + } + } else { + if err := c.pushContent(ctx, opts.Store, pusher, manifest.Config); err != nil { + return fmt.Errorf("unable to push config: %w", err) + } + } + + for _, layerDesc := range manifest.Layers { + if err := c.pushContent(ctx, opts.Store, pusher, layerDesc); err != nil { + return fmt.Errorf("unable to push layer: %w", err) + } + } + } + + if err := tempCache.Add(desc, ioutil.NopCloser(bytes.NewBuffer(rawManifest))); err != nil { + return fmt.Errorf("unable to add manifest to cache: %w", err) + } + + if err := c.pushContent(ctx, tempCache, pusher, desc); err != nil { + return fmt.Errorf("unable to push manifest: %w", err) + } + + return nil +} + +func (c *client) GetRawManifest(ctx context.Context, ref string) (ocispecv1.Descriptor, []byte, error) { + refspec, err := oci.ParseRef(ref) + if err != nil { + return ocispecv1.Descriptor{}, nil, fmt.Errorf("unable to parse ref: %w", err) + } + ref = refspec.String() + + resolver, err := c.getResolverForRef(ctx, ref, transport.PullScope) + if err != nil { + return ocispecv1.Descriptor{}, nil, err + } + _, desc, err := resolver.Resolve(ctx, ref) + if err != nil { + return ocispecv1.Descriptor{}, nil, err + } + + if desc.MediaType == MediaTypeDockerV2Schema1Manifest || desc.MediaType == MediaTypeDockerV2Schema1SignedManifest { + c.log.V(7).Info("found v1 manifest -> convert to v2") + convertedManifestDesc, err := ConvertV1ManifestToV2(ctx, c, c.cache, ref, desc) + if err != nil { + return ocispecv1.Descriptor{}, nil, fmt.Errorf("unable to convert v1 manifest to v2: %w", err) + } + desc = convertedManifestDesc + } + + if !IsSingleArchImage(desc.MediaType) && !IsMultiArchImage(desc.MediaType) { + return ocispecv1.Descriptor{}, nil, fmt.Errorf("media type is not an image manifest or image index: %s", desc.MediaType) + } + + data := bytes.NewBuffer([]byte{}) + if err := c.Fetch(ctx, ref, desc, data); err != nil { + return ocispecv1.Descriptor{}, nil, err + } + rawManifest := data.Bytes() + + return desc, rawManifest, nil +} + func (c *client) pushManifest(ctx context.Context, manifest *ocispecv1.Manifest, pusher remotes.Pusher, cache cache.Cache, opts *PushOptions) (ocispecv1.Descriptor, error) { // add dummy config if it is not set if manifest.Config.Size == 0 { @@ -360,12 +468,22 @@ func (c *client) pushImageIndex(ctx context.Context, indexArtifact *oci.Index, p Annotations: indexArtifact.Annotations, } - idesc, err := createDescriptorFromIndex(cache, &index) + indexBytes, err := json.Marshal(index) if err != nil { - return fmt.Errorf("unable to create image index descriptor: %w", err) + return err + } + indexDescriptor := ocispecv1.Descriptor{ + MediaType: ocispecv1.MediaTypeImageIndex, + Digest: digest.FromBytes(indexBytes), + Size: int64(len(indexBytes)), } - if err := c.pushContent(ctx, cache, pusher, idesc); err != nil { + manifestBuf := bytes.NewBuffer(indexBytes) + if err := cache.Add(indexDescriptor, ioutil.NopCloser(manifestBuf)); err != nil { + return err + } + + if err := c.pushContent(ctx, cache, pusher, indexDescriptor); err != nil { return fmt.Errorf("unable to push image index: %w", err) } @@ -373,16 +491,21 @@ func (c *client) pushImageIndex(ctx context.Context, indexArtifact *oci.Index, p } func (c *client) GetManifest(ctx context.Context, ref string) (*ocispecv1.Manifest, error) { - ociArtifact, err := c.GetOCIArtifact(ctx, ref) + desc, rawManifest, err := c.GetRawManifest(ctx, ref) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to get manifest: %w", err) } - if !ociArtifact.IsManifest() { - return nil, fmt.Errorf("oci artifact is not a manifest: %+v", ociArtifact) + if desc.MediaType != ocispecv1.MediaTypeImageManifest && desc.MediaType != images.MediaTypeDockerSchema2Manifest { + return nil, fmt.Errorf("media type is not an image manifest: %s", desc.MediaType) } - return ociArtifact.GetManifest().Data, nil + var manifest ocispecv1.Manifest + if err := json.Unmarshal(rawManifest, &manifest); err != nil { + return nil, fmt.Errorf("unable to unmarshal manifest: %w", err) + } + + return &manifest, nil } func (c *client) Fetch(ctx context.Context, ref string, desc ocispecv1.Descriptor, writer io.Writer) error { @@ -448,16 +571,19 @@ func (c *client) getFetchReader(ctx context.Context, ref string, desc ocispecv1. } func (c *client) PushManifest(ctx context.Context, ref string, manifest *ocispecv1.Manifest, options ...PushOption) error { - m := oci.Manifest{ - Data: manifest, + manifestBytes, err := json.Marshal(manifest) + if err != nil { + return fmt.Errorf("unable to marshal manifest: %w", err) } - a, err := oci.NewManifestArtifact(&m) - if err != nil { - return fmt.Errorf("unable to create oci artifact: %w", err) + desc := ocispecv1.Descriptor{ + MediaType: ocispecv1.MediaTypeImageManifest, + Digest: digest.FromBytes(manifestBytes), + Size: int64(len(manifestBytes)), + Annotations: manifest.Annotations, } - return c.PushOCIArtifact(ctx, ref, a, options...) + return c.PushRawManifest(ctx, ref, desc, manifestBytes, options...) } func (c *client) getHttpClient() *http.Client { @@ -755,24 +881,6 @@ func CreateDescriptorFromManifest(manifest *ocispecv1.Manifest) (ocispecv1.Descr return manifestDescriptor, nil } -func createDescriptorFromIndex(cache cache.Cache, index *ocispecv1.Index) (ocispecv1.Descriptor, error) { - indexBytes, err := json.Marshal(index) - if err != nil { - return ocispecv1.Descriptor{}, err - } - indexDescriptor := ocispecv1.Descriptor{ - MediaType: ocispecv1.MediaTypeImageIndex, - Digest: digest.FromBytes(indexBytes), - Size: int64(len(indexBytes)), - } - - manifestBuf := bytes.NewBuffer(indexBytes) - if err := cache.Add(indexDescriptor, ioutil.NopCloser(manifestBuf)); err != nil { - return ocispecv1.Descriptor{}, err - } - return indexDescriptor, nil -} - func (c *client) pushContent(ctx context.Context, store Store, pusher remotes.Pusher, desc ocispecv1.Descriptor) error { if store == nil { return errors.New("a store is needed to upload content but no store has been defined") diff --git a/ociclient/client_test.go b/ociclient/client_test.go index 8642b37d..00a646af 100644 --- a/ociclient/client_test.go +++ b/ociclient/client_test.go @@ -5,9 +5,10 @@ package ociclient_test import ( - "bytes" "context" + "encoding/json" "fmt" + "io" "net/http" "net/http/httptest" "net/url" @@ -15,185 +16,314 @@ import ( "github.com/go-logr/logr" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go" + ocispecv1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/gardener/component-cli/ociclient" "github.com/gardener/component-cli/ociclient/credentials" - "github.com/gardener/component-cli/ociclient/oci" "github.com/gardener/component-cli/pkg/testutils" - - "github.com/gardener/component-cli/ociclient" ) -var _ = Describe("client", func() { +func RunPushAndPullImageTest(ref, manifestMediaType string) { + ctx := context.Background() + defer ctx.Done() - Context("Client", func() { + configData := []byte("config-data") + layersData := [][]byte{ + []byte("layer-1-data"), + []byte("layer-2-data"), + } - It("should push and pull an oci artifact", func() { - ctx := context.Background() - defer ctx.Done() - - ref := testenv.Addr + "/test/artifact:v0.0.1" - manifest, mdesc, err := testutils.UploadTestManifest(ctx, client, ref) - Expect(err).ToNot(HaveOccurred()) + manifestDesc, manifestBytes := testutils.UploadTestImage(ctx, client, ref, manifestMediaType, configData, layersData) - res, err := client.GetManifest(ctx, ref) - Expect(err).ToNot(HaveOccurred()) - Expect(res.Config).To(Equal(manifest.Config)) - Expect(res.Layers).To(Equal(manifest.Layers)) + testutils.CompareRemoteManifest(ctx, client, ref, manifestDesc, manifestBytes, configData, layersData) +} - // TODO: oci image index test only working because cache is filled in this function with config/layer blobs. should be fixed - expectedManifest := oci.Manifest{ - Descriptor: mdesc, - Data: manifest, - } - testutils.CompareRemoteManifest( - client, - ref, - expectedManifest, - []byte("config-data"), - [][]byte{ - []byte("layer-data"), - }, - ) - }, 20) +func RunPushAndPullImageIndexTest(untaggedRepo, indexMediaType string) { + ctx := context.Background() + defer ctx.Done() + + configData1 := []byte("config-data") + layersData1 := [][]byte{ + []byte("layer-1-data"), + []byte("layer-2-data"), + } + _, manifest1Desc, blobMap := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData1, layersData1) + manifest1Ref := fmt.Sprintf("%s@%s", untaggedRepo, manifest1Desc.Digest) + store := ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(blobMap[desc.Digest]) + return err + }) + manifest1Bytes := blobMap[manifest1Desc.Digest] + Expect(client.PushRawManifest(ctx, manifest1Ref, manifest1Desc, manifest1Bytes, ociclient.WithStore(store))).To(Succeed()) + + configData2 := []byte("config-data2") + layersData2 := [][]byte{ + []byte("layer-1-data2"), + []byte("layer-2-data2"), + } + _, manifest2Desc, blobMap := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData2, layersData2) + manifest2Ref := fmt.Sprintf("%s@%s", untaggedRepo, manifest2Desc.Digest) + store = ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(blobMap[desc.Digest]) + return err + }) + manifest2Bytes := blobMap[manifest2Desc.Digest] + Expect(client.PushRawManifest(ctx, manifest2Ref, manifest2Desc, manifest2Bytes, ociclient.WithStore(store))).To(Succeed()) + + manifest1IndexDesc := manifest1Desc + manifest1IndexDesc.Platform = &ocispecv1.Platform{ + Architecture: "amd64", + OS: "linux", + } + + manifest2IndexDesc := manifest2Desc + manifest2IndexDesc.Platform = &ocispecv1.Platform{ + Architecture: "amd64", + OS: "windows", + } + + index := ocispecv1.Index{ + Versioned: specs.Versioned{SchemaVersion: 2}, + Manifests: []ocispecv1.Descriptor{ + manifest1IndexDesc, + manifest2IndexDesc, + }, + Annotations: map[string]string{ + "test": "test", + }, + } + + multiArchRef := untaggedRepo + ":v0.1.0" + indexDesc, indexBytes := testutils.UploadTestIndex(ctx, client, multiArchRef, indexMediaType, index) + + actualIndexDesc, actualIndexBytes, err := client.GetRawManifest(ctx, multiArchRef) + Expect(err).ToNot(HaveOccurred()) + Expect(actualIndexDesc).To(Equal(indexDesc)) + Expect(actualIndexBytes).To(Equal(indexBytes)) + + testutils.CompareRemoteManifest(ctx, client, manifest1Ref, manifest1Desc, manifest1Bytes, configData1, layersData1) + testutils.CompareRemoteManifest(ctx, client, manifest2Ref, manifest2Desc, manifest2Bytes, configData2, layersData2) +} - It("should push and pull an oci image index", func() { - ctx := context.Background() - defer ctx.Done() +var _ = Describe("client", func() { - indexRef := testenv.Addr + "/image-index/1/img:v0.0.1" - index, err := testutils.UploadTestIndex(ctx, client, indexRef) - Expect(err).ToNot(HaveOccurred()) + Context("Client", func() { - actualArtifact, err := client.GetOCIArtifact(ctx, indexRef) - Expect(err).ToNot(HaveOccurred()) + It("should push and pull a single architecture image without modifications (oci media type)", func() { + ref := fmt.Sprintf("%s/%s", testenv.Addr, "single-arch-tests/0/artifact:v0.0.1") + RunPushAndPullImageTest(ref, ocispecv1.MediaTypeImageManifest) + }, 20) - Expect(actualArtifact.IsManifest()).To(BeFalse()) - Expect(actualArtifact.IsIndex()).To(BeTrue()) - Expect(actualArtifact.GetIndex()).To(Equal(index)) + It("should push and pull a multi architecture image without modifications (oci media type)", func() { + untaggedRef := fmt.Sprintf("%s/%s", testenv.Addr, "multi-arch-tests/0/artifact") + RunPushAndPullImageIndexTest(untaggedRef, ocispecv1.MediaTypeImageIndex) }, 20) + // TODO: investigate why this test isn't working (could be registry not accepting docker media type) + // It("should push and pull a single architecture image without modifications (docker media type)", func() { + // RunPushAndPullTest("single-arch-tests/1/artifact:0.0.1", images.MediaTypeDockerSchema2Manifest) + // }, 20) + + // TODO: investigate why this test isn't working (could be registry not accepting docker media type) + // It("should push and pull a multi architecture image without modifications (docker media type)", func() { + // RunPushAndPullImageIndexTest("multi-arch-tests/1/artifact", images.MediaTypeDockerSchema2ManifestList) + // }, 20) + It("should push and pull an empty oci image index", func() { ctx := context.Background() defer ctx.Done() - ref := testenv.Addr + "/image-index/2/empty-img:v0.0.1" - index := oci.Index{ - Manifests: []*oci.Manifest{}, + ref := testenv.Addr + "/multi-arch-tests/2/empty-img:v0.0.1" + index := ocispecv1.Index{ + Versioned: specs.Versioned{ + SchemaVersion: 2, + }, + Manifests: []ocispecv1.Descriptor{}, Annotations: map[string]string{ "test": "test", }, } - tmp, err := oci.NewIndexArtifact(&index) + indexBytes, err := json.Marshal(index) Expect(err).ToNot(HaveOccurred()) - err = client.PushOCIArtifact(ctx, ref, tmp) - Expect(err).ToNot(HaveOccurred()) + indexDesc := ocispecv1.Descriptor{ + MediaType: ocispecv1.MediaTypeImageIndex, + Digest: digest.FromBytes(indexBytes), + Size: int64(len(indexBytes)), + } + + store := ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(indexBytes) + return err + }) - actualArtifact, err := client.GetOCIArtifact(ctx, ref) + Expect(client.PushRawManifest(ctx, ref, indexDesc, indexBytes, ociclient.WithStore(store))).To(Succeed()) + + actualIndexDesc, actualIndexBytes, err := client.GetRawManifest(ctx, ref) Expect(err).ToNot(HaveOccurred()) - Expect(actualArtifact.IsManifest()).To(BeFalse()) - Expect(actualArtifact.IsIndex()).To(BeTrue()) - Expect(actualArtifact.GetIndex()).To(Equal(&index)) + Expect(actualIndexDesc).To(Equal(indexDesc)) + Expect(actualIndexBytes).To(Equal(indexBytes)) }, 20) It("should push and pull an oci image index with only 1 manifest and no platform information", func() { ctx := context.Background() defer ctx.Done() - ref := testenv.Addr + "/image-index/3/img:v0.0.1" - manifest1Ref := testenv.Addr + "/image-index/1/img-platform-1:v0.0.1" - manifest, mdesc, err := testutils.UploadTestManifest(ctx, client, manifest1Ref) - Expect(err).ToNot(HaveOccurred()) + configData := []byte("config-data") + layersData := [][]byte{ + []byte("layer-1-data"), + []byte("layer-2-data"), + } + untaggedRef := testenv.Addr + "/multi-arch-tests/3/img" + + _, manifest1Desc, blobMap := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData, layersData) + manifest1Ref := fmt.Sprintf("%s@%s", untaggedRef, manifest1Desc.Digest) + store := ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(blobMap[desc.Digest]) + return err + }) + manifest1Bytes := blobMap[manifest1Desc.Digest] + Expect(client.PushRawManifest(ctx, manifest1Ref, manifest1Desc, manifest1Bytes, ociclient.WithStore(store))).To(Succeed()) - index := oci.Index{ - Manifests: []*oci.Manifest{ - { - Descriptor: mdesc, - Data: manifest, - }, + index := ocispecv1.Index{ + Versioned: specs.Versioned{ + SchemaVersion: 2, + }, + Manifests: []ocispecv1.Descriptor{ + manifest1Desc, }, Annotations: map[string]string{ "test": "test", }, } - tmp, err := oci.NewIndexArtifact(&index) + indexBytes, err := json.Marshal(index) Expect(err).ToNot(HaveOccurred()) - err = client.PushOCIArtifact(ctx, ref, tmp) - Expect(err).ToNot(HaveOccurred()) + indexDesc := ocispecv1.Descriptor{ + MediaType: ocispecv1.MediaTypeImageIndex, + Digest: digest.FromBytes(indexBytes), + Size: int64(len(indexBytes)), + } - actualArtifact, err := client.GetOCIArtifact(ctx, ref) + store = ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(indexBytes) + return err + }) + + multiArchRef := untaggedRef + ":v0.1.0" + Expect(client.PushRawManifest(ctx, multiArchRef, indexDesc, indexBytes, ociclient.WithStore(store))).To(Succeed()) + + actualIndexDesc, actualIndexBytes, err := client.GetRawManifest(ctx, multiArchRef) Expect(err).ToNot(HaveOccurred()) + Expect(actualIndexDesc).To(Equal(indexDesc)) + Expect(actualIndexBytes).To(Equal(indexBytes)) - Expect(actualArtifact.IsManifest()).To(BeFalse()) - Expect(actualArtifact.IsIndex()).To(BeTrue()) - Expect(actualArtifact.GetIndex()).To(Equal(&index)) + testutils.CompareRemoteManifest(ctx, client, manifest1Ref, manifest1Desc, manifest1Bytes, configData, layersData) }, 20) It("should copy an oci artifact", func() { ctx := context.Background() defer ctx.Done() - ref := testenv.Addr + "/test/artifact:v0.0.1" - manifest, _, err := testutils.UploadTestManifest(ctx, client, ref) - Expect(err).ToNot(HaveOccurred()) + configData := []byte("config-data") + layersData := [][]byte{ + []byte("layer-1-data"), + []byte("layer-2-data"), + } + ref := testenv.Addr + "/single-arch-tests/2/src/artifact:v0.0.1" + mdesc, mbytes := testutils.UploadTestImage(ctx, client, ref, ocispecv1.MediaTypeImageManifest, configData, layersData) + newRef := testenv.Addr + "/single-arch-tests/2/tgt/artifact:v0.0.1" - newRef := testenv.Addr + "/new/artifact:v0.0.1" Expect(ociclient.Copy(ctx, client, ref, newRef)).To(Succeed()) - res, err := client.GetManifest(ctx, newRef) - Expect(err).ToNot(HaveOccurred()) - Expect(res.Config).To(Equal(manifest.Config)) - Expect(res.Layers).To(Equal(manifest.Layers)) - - var configBlob bytes.Buffer - Expect(client.Fetch(ctx, ref, res.Config, &configBlob)).To(Succeed()) - Expect(configBlob.String()).To(Equal("config-data")) - - var layerBlob bytes.Buffer - Expect(client.Fetch(ctx, ref, res.Layers[0], &layerBlob)).To(Succeed()) - Expect(layerBlob.String()).To(Equal("layer-data")) + testutils.CompareRemoteManifest(ctx, client, newRef, mdesc, mbytes, configData, layersData) }, 20) It("should copy an oci image index", func() { ctx := context.Background() defer ctx.Done() - ref := testenv.Addr + "/copy/image-index/src/img:v0.0.1" - index, err := testutils.UploadTestIndex(ctx, client, ref) - Expect(err).ToNot(HaveOccurred()) + untaggedSrcRef := testenv.Addr + "/multi-arch-tests/4/src/img" + untaggedTgtRef := testenv.Addr + "/multi-arch-tests/4/tgt/img" - newRef := testenv.Addr + "/copy/image-index/tgt/img:v0.0.1" - Expect(ociclient.Copy(ctx, client, ref, newRef)).To(Succeed()) + configData := []byte("config-data") + layersData := [][]byte{ + []byte("layer-1-data"), + []byte("layer-2-data"), + } + _, manifest1Desc, blobMap := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData, layersData) + manifest1Ref := fmt.Sprintf("%s@%s", untaggedSrcRef, manifest1Desc.Digest) + store := ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(blobMap[desc.Digest]) + return err + }) + manifest1Bytes := blobMap[manifest1Desc.Digest] + Expect(client.PushRawManifest(ctx, manifest1Ref, manifest1Desc, manifest1Bytes, ociclient.WithStore(store))).To(Succeed()) - actualArtifact, err := client.GetOCIArtifact(ctx, newRef) - Expect(err).ToNot(HaveOccurred()) + configData2 := []byte("config-data2") + layersData2 := [][]byte{ + []byte("layer-1-data2"), + []byte("layer-2-data2"), + } + _, manifest2Desc, blobMap := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData2, layersData2) + manifest2Ref := fmt.Sprintf("%s@%s", untaggedSrcRef, manifest2Desc.Digest) + store = ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(blobMap[desc.Digest]) + return err + }) + manifest2Bytes := blobMap[manifest2Desc.Digest] + Expect(client.PushRawManifest(ctx, manifest2Ref, manifest2Desc, manifest2Bytes, ociclient.WithStore(store))).To(Succeed()) - Expect(actualArtifact.IsManifest()).To(BeFalse()) - Expect(actualArtifact.IsIndex()).To(BeTrue()) - Expect(actualArtifact.GetIndex()).To(Equal(index)) - - for i := range actualArtifact.GetIndex().Manifests { - testutils.CompareRemoteManifest( - client, - ref, - *index.Manifests[i], - []byte("config-data"), - [][]byte{ - []byte("layer-data"), - }, - ) + manifest1IndexDesc := manifest1Desc + manifest1IndexDesc.Platform = &ocispecv1.Platform{ + Architecture: "amd64", + OS: "linux", + } + + manifest2IndexDesc := manifest2Desc + manifest2IndexDesc.Platform = &ocispecv1.Platform{ + Architecture: "amd64", + OS: "windows", + } + + index := ocispecv1.Index{ + Versioned: specs.Versioned{SchemaVersion: 2}, + Manifests: []ocispecv1.Descriptor{ + manifest1IndexDesc, + manifest2IndexDesc, + }, + Annotations: map[string]string{ + "test": "test", + }, } + + multiArchSrcRef := untaggedSrcRef + ":v0.1.0" + indexDesc, indexBytes := testutils.UploadTestIndex(ctx, client, multiArchSrcRef, ocispecv1.MediaTypeImageIndex, index) + + multiArchTgtRef := untaggedTgtRef + ":v0.0.1" + manifest1TgtRef := fmt.Sprintf("%s@%s", untaggedTgtRef, manifest1Desc.Digest) + manifest2TgtRef := fmt.Sprintf("%s@%s", untaggedTgtRef, manifest2Desc.Digest) + + Expect(ociclient.Copy(ctx, client, multiArchSrcRef, multiArchTgtRef)).To(Succeed()) + + actualIndexDesc, actualIndexBytes, err := client.GetRawManifest(ctx, multiArchTgtRef) + Expect(err).ToNot(HaveOccurred()) + Expect(actualIndexDesc).To(Equal(indexDesc)) + Expect(actualIndexBytes).To(Equal(indexBytes)) + + testutils.CompareRemoteManifest(ctx, client, manifest1TgtRef, manifest1Desc, manifest1Bytes, configData, layersData) + testutils.CompareRemoteManifest(ctx, client, manifest2TgtRef, manifest2Desc, manifest2Bytes, configData2, layersData2) }, 20) }) Context("ExtendedClient", func() { Context("ListTags", func() { - var ( server *httptest.Server host string @@ -250,7 +380,6 @@ var _ = Describe("client", func() { }) Context("ListRepositories", func() { - var ( server *httptest.Server host string diff --git a/ociclient/copy.go b/ociclient/copy.go index a29a71c6..ebd2e9de 100644 --- a/ociclient/copy.go +++ b/ociclient/copy.go @@ -6,6 +6,7 @@ package ociclient import ( "context" + "encoding/json" "fmt" "io" @@ -15,17 +16,47 @@ import ( // Copy copies a oci artifact from one location to a target ref. // The artifact is copied without any modification. // This function does directly stream the blobs from the upstream it does not use any cache. -func Copy(ctx context.Context, client Client, from, to string) error { - artifact, err := client.GetOCIArtifact(ctx, from) +func Copy(ctx context.Context, client Client, srcRef, tgtRef string) error { + desc, rawManifest, err := client.GetRawManifest(ctx, srcRef) if err != nil { - return fmt.Errorf("unable to get source oci artifact %q: %w", from, err) + return fmt.Errorf("unable to get manifest: %w", err) } store := GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { - return client.Fetch(ctx, from, desc, writer) + return client.Fetch(ctx, srcRef, desc, writer) }) - return client.PushOCIArtifact(ctx, to, artifact, WithStore(store)) + if IsMultiArchImage(desc.MediaType) { + index := ocispecv1.Index{} + if err := json.Unmarshal(rawManifest, &index); err != nil { + return fmt.Errorf("unable to unmarshal image index: %w", err) + } + + srcRepo, _, err := ParseImageRef(srcRef) + if err != nil { + return fmt.Errorf("unable to parse src ref: %w", err) + } + + tgtRepo, _, err := ParseImageRef(tgtRef) + if err != nil { + return fmt.Errorf("unable to parse tgt ref: %w", err) + } + + for _, manifestDesc := range index.Manifests { + subManifestSrcRef := fmt.Sprintf("%s@%s", srcRepo, manifestDesc.Digest) + subManifestTgtRef := fmt.Sprintf("%s@%s", tgtRepo, manifestDesc.Digest) + + if err := Copy(ctx, client, subManifestSrcRef, subManifestTgtRef); err != nil { + return fmt.Errorf("unable to copy sub manifest: %w", err) + } + } + } + + if err := client.PushRawManifest(ctx, tgtRef, desc, rawManifest, WithStore(store)); err != nil { + return fmt.Errorf("unable to push manifest: %w", err) + } + + return nil } // GenericStore is a helper struct to implement a custom oci blob store. diff --git a/ociclient/types.go b/ociclient/types.go index 8134ccd3..b0117f38 100644 --- a/ociclient/types.go +++ b/ociclient/types.go @@ -19,23 +19,37 @@ import ( type Client interface { Resolver - // GetManifest returns the ocispec Manifest for a reference - GetManifest(ctx context.Context, ref string) (*ocispecv1.Manifest, error) // Fetch fetches the blob for the given ocispec Descriptor. Fetch(ctx context.Context, ref string, desc ocispecv1.Descriptor, writer io.Writer) error + // PushBlob uploads the blob for the given ocispec Descriptor to the given ref + PushBlob(ctx context.Context, ref string, desc ocispecv1.Descriptor, opts ...PushOption) error + + // GetRawManifest returns the raw manifest for a reference. + // The returned manifest can either be single arch or multi arch (image index/manifest list) + GetRawManifest(ctx context.Context, ref string) (ocispecv1.Descriptor, []byte, error) + + // PushRawManifest uploads the given raw manifest to the given reference. + // If the manifest is multi arch (image index/manifest list), only the multi arch manifest is pushed. + // The referenced single arch manifests must be pushed individiually before. + PushRawManifest(ctx context.Context, ref string, desc ocispecv1.Descriptor, rawManifest []byte, opts ...PushOption) error + + // GetManifest returns the ocispec Manifest for a reference + // Deprecated: Please prefer GetRawManifest instead + GetManifest(ctx context.Context, ref string) (*ocispecv1.Manifest, error) + // PushManifest uploads the given Manifest to the given reference. + // Deprecated: Please prefer PushRawManifest instead PushManifest(ctx context.Context, ref string, manifest *ocispecv1.Manifest, opts ...PushOption) error // GetOCIArtifact returns an OCIArtifact for a reference. + // Deprecated: Please prefer GetRawManifest instead GetOCIArtifact(ctx context.Context, ref string) (*oci.Artifact, error) // PushOCIArtifact uploads the given OCIArtifact to the given ref. + // Deprecated: Please prefer PushRawManifest instead PushOCIArtifact(ctx context.Context, ref string, artifact *oci.Artifact, opts ...PushOption) error - - // PushBlob uploads the blob for the given ocispec Descriptor to the given ref - PushBlob(ctx context.Context, ref string, desc ocispecv1.Descriptor, opts ...PushOption) error } // ExtendedClient defines an oci client with extended functionality that may not work with all registries. diff --git a/ociclient/utils.go b/ociclient/utils.go index 33cfa6ef..3840a5f6 100644 --- a/ociclient/utils.go +++ b/ociclient/utils.go @@ -8,6 +8,7 @@ import ( "fmt" "strings" + "github.com/containerd/containerd/images" "github.com/opencontainers/go-digest" ocispecv1 "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -73,3 +74,13 @@ func TagIsDigest(tag string) bool { _, err := digest.Parse(tag) return err == nil } + +func IsMultiArchImage(mediaType string) bool { + return mediaType == ocispecv1.MediaTypeImageIndex || + mediaType == images.MediaTypeDockerSchema2ManifestList +} + +func IsSingleArchImage(mediaType string) bool { + return mediaType == ocispecv1.MediaTypeImageManifest || + mediaType == images.MediaTypeDockerSchema2Manifest +} diff --git a/pkg/testutils/oci.go b/pkg/testutils/oci.go index b65953aa..657281e1 100644 --- a/pkg/testutils/oci.go +++ b/pkg/testutils/oci.go @@ -7,9 +7,7 @@ import ( "bytes" "context" "encoding/json" - "fmt" "io" - "strings" . "github.com/onsi/gomega" "github.com/opencontainers/go-digest" @@ -17,122 +15,54 @@ import ( ocispecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/gardener/component-cli/ociclient" - "github.com/gardener/component-cli/ociclient/cache" - "github.com/gardener/component-cli/ociclient/oci" ) -// UploadTestManifest uploads an oci image manifest to a registry -func UploadTestManifest(ctx context.Context, client ociclient.Client, ref string) (*ocispecv1.Manifest, ocispecv1.Descriptor, error) { - configData := []byte("config-data") - layerData := []byte("layer-data") - manifest := &ocispecv1.Manifest{ - Config: ocispecv1.Descriptor{ - MediaType: "text/plain", - Digest: digest.FromBytes(configData), - Size: int64(len(configData)), - }, - Layers: []ocispecv1.Descriptor{ - { - MediaType: "text/plain", - Digest: digest.FromBytes(layerData), - Size: int64(len(layerData)), - }, - }, - } +// UploadTestImage uploads an oci image manifest to a registry +func UploadTestImage(ctx context.Context, client ociclient.Client, ref, manifestMediaType string, configData []byte, layersData [][]byte) (ocispecv1.Descriptor, []byte) { + _, desc, blobMap := CreateImage(manifestMediaType, configData, layersData) + store := ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { - switch desc.Digest.String() { - case manifest.Config.Digest.String(): - _, err := writer.Write(configData) - return err - default: - _, err := writer.Write(layerData) - return err - } + _, err := writer.Write(blobMap[desc.Digest]) + return err }) - if err := client.PushManifest(ctx, ref, manifest, ociclient.WithStore(store)); err != nil { - return nil, ocispecv1.Descriptor{}, err - } - - manifestBytes, err := json.Marshal(manifest) - if err != nil { - return nil, ocispecv1.Descriptor{}, err - } - - desc := ocispecv1.Descriptor{ - MediaType: ocispecv1.MediaTypeImageManifest, - Digest: digest.FromBytes(manifestBytes), - Size: int64(len(manifestBytes)), - } + manifestBytes := blobMap[desc.Digest] + Expect(client.PushRawManifest(ctx, ref, desc, manifestBytes, ociclient.WithStore(store))).To(Succeed()) - return manifest, desc, nil + return desc, manifestBytes } // UploadTestIndex uploads an oci image index to a registry -func UploadTestIndex(ctx context.Context, client ociclient.Client, indexRef string) (*oci.Index, error) { - splitted := strings.Split(indexRef, ":") - indexRepo := strings.Join(splitted[0:len(splitted)-1], ":") - tag := splitted[len(splitted)-1] - - manifest1Ref := fmt.Sprintf("%s-platform-1:%s", indexRepo, tag) - manifest2Ref := fmt.Sprintf("%s-platform-2:%s", indexRepo, tag) - - manifest1, mdesc1, err := UploadTestManifest(ctx, client, manifest1Ref) - if err != nil { - return nil, err - } - mdesc1.Platform = &ocispecv1.Platform{ - Architecture: "amd64", - OS: "linux", - } +func UploadTestIndex(ctx context.Context, client ociclient.Client, ref, indexMediaType string, index ocispecv1.Index) (ocispecv1.Descriptor, []byte) { + indexBytes, err := json.Marshal(index) + Expect(err).ToNot(HaveOccurred()) - manifest2, mdesc2, err := UploadTestManifest(ctx, client, manifest2Ref) - if err != nil { - return nil, err - } - mdesc2.Platform = &ocispecv1.Platform{ - Architecture: "amd64", - OS: "windows", + indexDesc := ocispecv1.Descriptor{ + MediaType: indexMediaType, + Digest: digest.FromBytes(indexBytes), + Size: int64(len(indexBytes)), } - index := oci.Index{ - Manifests: []*oci.Manifest{ - { - Descriptor: mdesc1, - Data: manifest1, - }, - { - Descriptor: mdesc2, - Data: manifest2, - }, - }, - Annotations: map[string]string{ - "test": "test", - }, - } - - ociArtifact, err := oci.NewIndexArtifact(&index) - if err != nil { - return nil, err - } + store := ociclient.GenericStore(func(ctx context.Context, desc ocispecv1.Descriptor, writer io.Writer) error { + _, err := writer.Write(indexBytes) + return err + }) - if err := client.PushOCIArtifact(ctx, indexRef, ociArtifact); err != nil { - return nil, err - } + Expect(client.PushRawManifest(ctx, ref, indexDesc, indexBytes, ociclient.WithStore(store))).To(Succeed()) - return &index, nil + return indexDesc, indexBytes } -// CreateManifest creates an oci manifest. if ocicache is set, all blobs are added to cache -func CreateManifest(configData []byte, layersData [][]byte, ocicache cache.Cache) (*ocispecv1.Manifest, ocispecv1.Descriptor) { +// CreateImage creates an oci image manifest. +func CreateImage(manifestMediaType string, configData []byte, layersData [][]byte) (*ocispecv1.Manifest, ocispecv1.Descriptor, map[digest.Digest][]byte) { + blobMap := map[digest.Digest][]byte{} + configDesc := ocispecv1.Descriptor{ MediaType: "text/plain", Digest: digest.FromBytes(configData), Size: int64(len(configData)), } - if ocicache != nil { - Expect(ocicache.Add(configDesc, io.NopCloser(bytes.NewReader(configData)))).To(Succeed()) - } + blobMap[configDesc.Digest] = configData layerDescs := []ocispecv1.Descriptor{} for _, layerData := range layersData { @@ -141,10 +71,8 @@ func CreateManifest(configData []byte, layersData [][]byte, ocicache cache.Cache Digest: digest.FromBytes(layerData), Size: int64(len(layerData)), } + blobMap[layerDesc.Digest] = layerData layerDescs = append(layerDescs, layerDesc) - if ocicache != nil { - Expect(ocicache.Add(layerDesc, io.NopCloser(bytes.NewReader(layerData)))).To(Succeed()) - } } manifest := ocispecv1.Manifest{ @@ -159,31 +87,31 @@ func CreateManifest(configData []byte, layersData [][]byte, ocicache cache.Cache Expect(err).ToNot(HaveOccurred()) manifestDesc := ocispecv1.Descriptor{ - MediaType: ocispecv1.MediaTypeImageManifest, + MediaType: manifestMediaType, Digest: digest.FromBytes(manifestBytes), Size: int64(len(manifestBytes)), } - if ocicache != nil { - Expect(ocicache.Add(manifestDesc, io.NopCloser(bytes.NewReader(manifestBytes)))).To(Succeed()) - } + blobMap[manifestDesc.Digest] = manifestBytes - return &manifest, manifestDesc + return &manifest, manifestDesc, blobMap } -func CompareRemoteManifest(client ociclient.Client, ref string, expectedManifest oci.Manifest, expectedCfgBytes []byte, expectedLayers [][]byte) { - buf := bytes.NewBuffer([]byte{}) - Expect(client.Fetch(context.TODO(), ref, expectedManifest.Descriptor, buf)).To(Succeed()) - manifestFromRemote := ocispecv1.Manifest{} - Expect(json.Unmarshal(buf.Bytes(), &manifestFromRemote)).To(Succeed()) - Expect(manifestFromRemote).To(Equal(*expectedManifest.Data)) - - buf = bytes.NewBuffer([]byte{}) - Expect(client.Fetch(context.TODO(), ref, manifestFromRemote.Config, buf)).To(Succeed()) - Expect(buf.Bytes()).To(Equal(expectedCfgBytes)) - - for i, layerDesc := range manifestFromRemote.Layers { - buf = bytes.NewBuffer([]byte{}) - Expect(client.Fetch(context.TODO(), ref, layerDesc, buf)).To(Succeed()) - Expect(buf.Bytes()).To(Equal(expectedLayers[i])) +func CompareRemoteManifest(ctx context.Context, client ociclient.Client, ref string, expectedManifestDesc ocispecv1.Descriptor, expectedManifestBytes []byte, expectedCfgBytes []byte, expectedLayers [][]byte) { + actualManifestDesc, actualManifestBytes, err := client.GetRawManifest(ctx, ref) + Expect(err).ToNot(HaveOccurred()) + Expect(actualManifestDesc).To(Equal(expectedManifestDesc)) + Expect(actualManifestBytes).To(Equal(expectedManifestBytes)) + + actualManifest := ocispecv1.Manifest{} + Expect(json.Unmarshal(actualManifestBytes, &actualManifest)).To(Succeed()) + + actualConfigBuf := bytes.NewBuffer([]byte{}) + Expect(client.Fetch(ctx, ref, actualManifest.Config, actualConfigBuf)).To(Succeed()) + Expect(actualConfigBuf.Bytes()).To(Equal(expectedCfgBytes)) + + for i, layerDesc := range actualManifest.Layers { + actualLayerBuf := bytes.NewBuffer([]byte{}) + Expect(client.Fetch(ctx, ref, layerDesc, actualLayerBuf)).To(Succeed()) + Expect(actualLayerBuf.Bytes()).To(Equal(expectedLayers[i])) } } diff --git a/pkg/transport/process/downloaders/downloaders_suite_test.go b/pkg/transport/process/downloaders/downloaders_suite_test.go index 74266f32..ee95f1a5 100644 --- a/pkg/transport/process/downloaders/downloaders_suite_test.go +++ b/pkg/transport/process/downloaders/downloaders_suite_test.go @@ -5,6 +5,7 @@ package downloaders_test import ( "context" + "encoding/json" "os" "path/filepath" "testing" @@ -18,6 +19,8 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go" + ocispecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/gardener/component-cli/ociclient" "github.com/gardener/component-cli/ociclient/cache" @@ -156,27 +159,20 @@ func createLocalOciBlobRes(fs vfs.FileSystem) cdv2.Resource { func createImageRes(ctx context.Context) cdv2.Resource { imageRef = testenv.Addr + "/test/downloaders/image:0.1.0" - manifest, desc, err := testutils.UploadTestManifest(ctx, ociClient, imageRef) - Expect(err).ToNot(HaveOccurred()) - - // TODO: currently needed to fill the cache. remove from test, also from ociclient unit test - m := oci.Manifest{ - Descriptor: desc, - Data: manifest, + configData := []byte("config-data") + layersData := [][]byte{ + []byte("layer-data"), } - testutils.CompareRemoteManifest( - ociClient, - imageRef, - m, - []byte("config-data"), - [][]byte{ - []byte("layer-data"), - }, - ) + + mdesc, mbytes := testutils.UploadTestImage(ctx, ociClient, imageRef, ocispecv1.MediaTypeImageManifest, configData, layersData) + testutils.CompareRemoteManifest(ctx, ociClient, imageRef, mdesc, mbytes, configData, layersData) + + manifest := ocispecv1.Manifest{} + Expect(json.Unmarshal(mbytes, &manifest)).To(Succeed()) expectedImageManifest = oci.Manifest{ - Descriptor: desc, - Data: manifest, + Descriptor: mdesc, + Data: &manifest, } acc, err := cdv2.NewUnstructured( @@ -202,10 +198,41 @@ func createImageRes(ctx context.Context) cdv2.Resource { func createImageIndexRes(ctx context.Context) cdv2.Resource { imageIndexRef = testenv.Addr + "/test/downloaders/image-index:0.1.0" - i, err := testutils.UploadTestIndex(ctx, ociClient, imageIndexRef) - Expect(err).ToNot(HaveOccurred()) + configData := []byte("config-data") + layersData := [][]byte{ + []byte("layer-1-data"), + []byte("layer-2-data"), + } + + manifest1Desc, _ := testutils.UploadTestImage(ctx, ociClient, imageIndexRef, ocispecv1.MediaTypeImageManifest, configData, layersData) + manifest1Desc.Platform = &ocispecv1.Platform{ + Architecture: "amd64", + OS: "linux", + } - expectedImageIndex = *i + manifest2Desc, _ := testutils.UploadTestImage(ctx, ociClient, imageIndexRef, ocispecv1.MediaTypeImageManifest, configData, layersData) + manifest2Desc.Platform = &ocispecv1.Platform{ + Architecture: "amd64", + OS: "windows", + } + + index := ocispecv1.Index{ + Versioned: specs.Versioned{SchemaVersion: 2}, + Manifests: []ocispecv1.Descriptor{ + manifest1Desc, + manifest2Desc, + }, + Annotations: map[string]string{ + "test": "test", + }, + } + + testutils.UploadTestIndex(ctx, ociClient, imageIndexRef, ocispecv1.MediaTypeImageIndex, index) + + var err error + ociArtifact, err := ociClient.GetOCIArtifact(ctx, imageIndexRef) + Expect(err).ToNot(HaveOccurred()) + expectedImageIndex = *ociArtifact.GetIndex() acc, err := cdv2.NewUnstructured( cdv2.NewOCIRegistryAccess( diff --git a/pkg/transport/process/downloaders/oci_artifact_test.go b/pkg/transport/process/downloaders/oci_artifact_test.go index 789e3f5d..3c3b783a 100644 --- a/pkg/transport/process/downloaders/oci_artifact_test.go +++ b/pkg/transport/process/downloaders/oci_artifact_test.go @@ -10,7 +10,6 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/gardener/component-cli/pkg/testutils" "github.com/gardener/component-cli/pkg/transport/process/downloaders" "github.com/gardener/component-cli/pkg/transport/process/utils" ) @@ -43,15 +42,6 @@ var _ = Describe("ociArtifact", func() { actualOciArtifact, err := utils.DeserializeOCIArtifact(resBlobReader, ociCache) Expect(err).ToNot(HaveOccurred()) Expect(*actualOciArtifact.GetManifest()).To(Equal(expectedImageManifest)) - testutils.CompareRemoteManifest( - ociClient, - imageRef, - expectedImageManifest, - []byte("config-data"), - [][]byte{ - []byte("layer-data"), - }, - ) }) It("should download and stream oci image index", func() { diff --git a/pkg/transport/process/uploaders/oci_artifact_test.go b/pkg/transport/process/uploaders/oci_artifact_test.go index 7c74f416..41a655e9 100644 --- a/pkg/transport/process/uploaders/oci_artifact_test.go +++ b/pkg/transport/process/uploaders/oci_artifact_test.go @@ -53,7 +53,7 @@ var _ = Describe("ociArtifact", func() { layers := [][]byte{ []byte("layer-data"), } - m, mdesc := testutils.CreateManifest(configData, layers, nil) + m, mdesc, _ := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData, layers) expectedOciArtifact, err := oci.NewManifestArtifact( &oci.Manifest{ @@ -97,13 +97,6 @@ var _ = Describe("ociArtifact", func() { actualOciArtifact, err := utils.DeserializeOCIArtifact(resBlobReader, cache.NewInMemoryCache()) Expect(err).ToNot(HaveOccurred()) Expect(actualOciArtifact).To(Equal(expectedOciArtifact)) - testutils.CompareRemoteManifest( - ociClient, - expectedImageRef, - *expectedOciArtifact.GetManifest(), - configData, - layers, - ) }) It("should upload and stream oci image index", func() { @@ -139,13 +132,13 @@ var _ = Describe("ociArtifact", func() { []byte("layer-data-2"), } - m1, m1Desc := testutils.CreateManifest(configData1, layers1, nil) + m1, m1Desc, _ := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData1, layers1) m1Desc.Platform = &ocispecv1.Platform{ Architecture: "amd64", OS: "linux", } - m2, m2Desc := testutils.CreateManifest(configData2, layers2, nil) + m2, m2Desc, _ := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData2, layers2) m2Desc.Platform = &ocispecv1.Platform{ Architecture: "amd64", OS: "windows", @@ -214,20 +207,6 @@ var _ = Describe("ociArtifact", func() { actualOciArtifact, err := utils.DeserializeOCIArtifact(resBlobReader, cache.NewInMemoryCache()) Expect(err).ToNot(HaveOccurred()) Expect(actualOciArtifact).To(Equal(expectedOciArtifact)) - testutils.CompareRemoteManifest( - ociClient, - expectedImageRef, - *expectedOciArtifact.GetIndex().Manifests[0], - configData1, - layers1, - ) - testutils.CompareRemoteManifest( - ociClient, - expectedImageRef, - *expectedOciArtifact.GetIndex().Manifests[1], - configData2, - layers2, - ) }) It("should return error for invalid access type", func() { diff --git a/pkg/transport/process/utils/oci_artifact_serialization_test.go b/pkg/transport/process/utils/oci_artifact_serialization_test.go index a65be7ae..52129775 100644 --- a/pkg/transport/process/utils/oci_artifact_serialization_test.go +++ b/pkg/transport/process/utils/oci_artifact_serialization_test.go @@ -30,7 +30,7 @@ var _ = Describe("oci artifact serialization", func() { layers := [][]byte{ []byte("layer-data"), } - m, _ := testutils.CreateManifest(configData, layers, nil) + m, _, _ := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData, layers) expectedOciArtifact, err := oci.NewManifestArtifact( &oci.Manifest{ @@ -76,13 +76,13 @@ var _ = Describe("oci artifact serialization", func() { []byte("layer-data-2"), } - m1, m1Desc := testutils.CreateManifest(configData1, layers1, nil) + m1, m1Desc, _ := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData1, layers1) m1Desc.Platform = &ocispecv1.Platform{ Architecture: "amd64", OS: "linux", } - m2, m2Desc := testutils.CreateManifest(configData2, layers2, nil) + m2, m2Desc, _ := testutils.CreateImage(ocispecv1.MediaTypeImageManifest, configData2, layers2) m2Desc.Platform = &ocispecv1.Platform{ Architecture: "amd64", OS: "windows",