From 86875142cf3d286a4a3cedb57a6bf7b38e3f85b6 Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Wed, 15 Nov 2023 00:05:28 +0100 Subject: [PATCH 1/4] implement pull static artifact Signed-off-by: Somtochi Onyekwere --- oci/client/pull.go | 114 ++++++++++++++++++-- oci/client/push_pull_test.go | 204 +++++++++++++++++++++++++++-------- 2 files changed, 269 insertions(+), 49 deletions(-) diff --git a/oci/client/pull.go b/oci/client/pull.go index b23452c2..c5c9391f 100644 --- a/oci/client/pull.go +++ b/oci/client/pull.go @@ -17,17 +17,59 @@ limitations under the License. package client import ( + "bufio" + "bytes" "context" "fmt" + "io" + "os" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" + gcrv1 "github.com/google/go-containerregistry/pkg/v1" "github.com/fluxcd/pkg/tar" ) -// Pull downloads an artifact from an OCI repository and extracts the content to the given directory. -func (c *Client) Pull(ctx context.Context, url, outDir string) (*Metadata, error) { +var ( + // gzipMagicHeader are bytes found at the start of gzip files + // https://github.com/google/go-containerregistry/blob/a54d64203cffcbf94146e04069aae4a97f228ee2/internal/gzip/zip.go#L28 + gzipMagicHeader = []byte{'\x1f', '\x8b'} +) + +// PullOptions contains options for pulling a layer. +type PullOptions struct { + layerIndex int + layerType LayerType +} + +// PullOption is a function for configuring PullOptions. +type PullOption func(o *PullOptions) + +// WithPullLayerType sets the layer type of the layer that is being pulled. +func WithPullLayerType(l LayerType) PullOption { + return func(o *PullOptions) { + o.layerType = l + } +} + +// WithPullLayerIndex set the index of the layer to be pulled. +func WithPullLayerIndex(i int) PullOption { + return func(o *PullOptions) { + o.layerIndex = i + } +} + +// Pull downloads an artifact from an OCI repository and extracts the content. +// It untar or copies the content to the given outPath depending on the layerType. +// If no layer type is given, it tries to determine the right type by checking compressed content of the layer. +func (c *Client) Pull(ctx context.Context, url, outPath string, opts ...PullOption) (*Metadata, error) { + o := &PullOptions{ + layerIndex: 0, + } + for _, opt := range opts { + opt(o) + } ref, err := name.ParseReference(url) if err != nil { return nil, fmt.Errorf("invalid URL: %w", err) @@ -61,14 +103,72 @@ func (c *Client) Pull(ctx context.Context, url, outDir string) (*Metadata, error return nil, fmt.Errorf("no layers found in artifact") } - blob, err := layers[0].Compressed() + if len(layers) < o.layerIndex+1 { + return nil, fmt.Errorf("index '%d' out of bound for '%d' layers in artifact", o.layerIndex, len(layers)) + } + + err = extractLayer(layers[o.layerIndex], outPath, o) if err != nil { - return nil, fmt.Errorf("extracting first layer failed: %w", err) + return nil, err } + return meta, nil +} - if err = tar.Untar(blob, outDir, tar.WithMaxUntarSize(-1), tar.WithSkipSymlinks()); err != nil { - return nil, fmt.Errorf("failed to untar first layer: %w", err) +// extractLayer extracts the Layer to the path +func extractLayer(layer gcrv1.Layer, path string, opts *PullOptions) error { + var blob io.Reader + blob, err := layer.Compressed() + if err != nil { + return fmt.Errorf("extracting layer failed: %w", err) } - return meta, nil + if opts.layerType == "" { + bufReader := bufio.NewReader(blob) + if ok, _ := isGzipBlob(bufReader); ok { + opts.layerType = LayerTypeTarball + } else { + opts.layerType = LayerTypeStatic + } + // the bufio.Reader has read the bytes from the io.Reader + // and should be used instead + blob = bufReader + } + + return extractLayerType(path, blob, opts.layerType) +} + +// extractLayerType extracts the contents of a io.Reader to the given path. +// If the LayerType is LayerTypeTarball, it will untar to a directory, +// If the LayerType is LayerTypeStatic, it will copy to a file. +func extractLayerType(path string, blob io.Reader, layerType LayerType) error { + switch layerType { + case LayerTypeTarball: + return tar.Untar(blob, path, tar.WithMaxUntarSize(-1), tar.WithSkipSymlinks()) + case LayerTypeStatic: + f, err := os.Create(path) + if err != nil { + return err + } + + _, err = io.Copy(f, blob) + if err != nil { + return fmt.Errorf("error copying layer content: %s", err) + } + return nil + default: + return fmt.Errorf("unsupported layer type: '%s'", layerType) + } +} + +// isGzipBlob reads the first two bytes from a bufio.Reader and +// checks that they are equal to the expected gzip file headers. +func isGzipBlob(buf *bufio.Reader) (bool, error) { + b, err := buf.Peek(len(gzipMagicHeader)) + if err != nil { + if err == io.EOF { + return false, nil + } + return false, err + } + return bytes.Equal(b, gzipMagicHeader), nil } diff --git a/oci/client/push_pull_test.go b/oci/client/push_pull_test.go index 3c3411eb..9195d365 100644 --- a/oci/client/push_pull_test.go +++ b/oci/client/push_pull_test.go @@ -19,7 +19,6 @@ package client import ( "context" "fmt" - "io" "io/fs" "os" "path/filepath" @@ -28,6 +27,10 @@ import ( "time" "github.com/google/go-containerregistry/pkg/crane" + "github.com/google/go-containerregistry/pkg/v1/empty" + "github.com/google/go-containerregistry/pkg/v1/mutate" + "github.com/google/go-containerregistry/pkg/v1/static" + "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" . "github.com/onsi/gomega" @@ -48,8 +51,13 @@ func Test_Push_Pull(t *testing.T) { sourcePath string tag string ignorePaths []string - opts []PushOption - expectErr bool + pushOpts []PushOption + pullOpts []PullOption + pushFn func(url string, path string) error + testLayerIndex int + expectedNumLayers int + expectPullErr bool + expectPushErr bool expectedMediaType types.MediaType }{ { @@ -63,37 +71,133 @@ func Test_Push_Pull(t *testing.T) { tag: "v0.0.1", sourcePath: "testdata/artifact", expectedMediaType: oci.CanonicalContentMediaType, - opts: []PushOption{ + pushOpts: []PushOption{ WithPushLayerType(LayerTypeTarball), }, + pullOpts: []PullOption{ + WithPullLayerType(LayerTypeTarball), + }, }, { name: "push static file", tag: "v0.0.2", sourcePath: "testdata/artifact/deployment.yaml", - opts: []PushOption{ + pushOpts: []PushOption{ WithPushLayerType(LayerTypeStatic), WithPushMediaTypeExt("ml"), }, + pullOpts: []PullOption{ + WithPullLayerType(LayerTypeStatic), + }, expectedMediaType: getLayerMediaType("ml"), }, { name: "push directory as static layer (should return error)", sourcePath: "testdata/artifact", - opts: []PushOption{ + pushOpts: []PushOption{ WithPushLayerType(LayerTypeStatic), }, - expectErr: true, + expectPushErr: true, }, { name: "push static file without media type extension", tag: "v0.0.2", sourcePath: "testdata/artifact/deployment.yaml", - opts: []PushOption{ + pushOpts: []PushOption{ WithPushLayerType(LayerTypeStatic), }, expectedMediaType: oci.CanonicalMediaTypePrefix, }, + { + name: "push directory and pull archive (push with LayerTypeTarball and pull with LayerTypeStatic)", + tag: "v0.0.2", + sourcePath: "testdata/artifact", + pullOpts: []PullOption{ + WithPullLayerType(LayerTypeStatic), + }, + expectedMediaType: oci.CanonicalContentMediaType, + }, + { + name: "push static artifact (and with LayerTypeTarball PullOption - should return error)", + tag: "static-flux", + sourcePath: "testdata/artifact/deployment.yaml", + pullOpts: []PullOption{WithPullLayerType(LayerTypeTarball)}, + pushOpts: []PushOption{WithPushLayerType(LayerTypeStatic)}, + expectPullErr: true, + expectedMediaType: oci.CanonicalMediaTypePrefix, + }, + { + name: "two layers in image (specify index)", + tag: "two-layers", + sourcePath: "testdata/artifact", + pullOpts: []PullOption{WithPullLayerType(LayerTypeTarball), WithPullLayerIndex(1)}, + pushFn: func(url string, path string) error { + artifact := filepath.Join(t.TempDir(), "artifact.tgz") + err := build(artifact, path, nil) + if err != nil { + return err + } + + img := mutate.MediaType(empty.Image, types.OCIManifestSchema1) + img = mutate.ConfigMediaType(img, oci.CanonicalConfigMediaType) + + layer1 := static.NewLayer([]byte("test-byte"), oci.CanonicalMediaTypePrefix) + + layer2, err := tarball.LayerFromFile(artifact, tarball.WithMediaType(oci.CanonicalContentMediaType)) + if err != nil { + return err + } + + img, err = mutate.Append(img, mutate.Addendum{Layer: layer1}, mutate.Addendum{Layer: layer2}) + if err != nil { + return err + } + + err = crane.Push(img, url, c.optionsWithContext(ctx)...) + if err != nil { + return err + } + return err + }, + testLayerIndex: 1, + expectedNumLayers: 2, + expectedMediaType: oci.CanonicalContentMediaType, + }, + { + name: "specify wrong layer (should return error)", + tag: "not-flux", + sourcePath: "testdata/artifact", + pullOpts: []PullOption{WithPullLayerType(LayerTypeTarball), WithPullLayerIndex(1)}, + pushFn: func(url string, path string) error { + artifact := filepath.Join(t.TempDir(), "artifact.tgz") + err := build(artifact, path, nil) + if err != nil { + return err + } + + img := mutate.MediaType(empty.Image, types.OCIManifestSchema1) + img = mutate.ConfigMediaType(img, oci.CanonicalConfigMediaType) + + layer1, err := tarball.LayerFromFile(artifact, tarball.WithMediaType(oci.CanonicalContentMediaType)) + if err != nil { + return err + } + + img, err = mutate.Append(img, mutate.Addendum{Layer: layer1}) + if err != nil { + return err + } + + dst := fmt.Sprintf("%s/%s:%s", dockerReg, repo, "not-flux") + err = crane.Push(img, dst, c.optionsWithContext(ctx)...) + if err != nil { + return err + } + return err + }, + expectedMediaType: oci.CanonicalContentMediaType, + expectPullErr: true, + }, } for _, tt := range tests { @@ -109,11 +213,16 @@ func Test_Push_Pull(t *testing.T) { "org.opencontainers.image.licenses": "Apache-2.0", }, } - opts := append(tt.opts, WithPushMetadata(metadata)) + opts := append(tt.pushOpts, WithPushMetadata(metadata)) // Build and push the artifact to registry - _, err := c.Push(ctx, url, tt.sourcePath, opts...) - if tt.expectErr { + var err error + if tt.pushFn == nil { + _, err = c.Push(ctx, url, tt.sourcePath, opts...) + } else { + err = tt.pushFn(url, tt.sourcePath) + } + if tt.expectPushErr { g.Expect(err).To(HaveOccurred()) return } @@ -133,35 +242,54 @@ func Test_Push_Pull(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) // Verify that annotations exist in manifest - g.Expect(manifest.Annotations[oci.CreatedAnnotation]).To(BeEquivalentTo(created)) - g.Expect(manifest.Annotations[oci.SourceAnnotation]).To(BeEquivalentTo(source)) - g.Expect(manifest.Annotations[oci.RevisionAnnotation]).To(BeEquivalentTo(revision)) + if len(manifest.Annotations) != 0 { + g.Expect(manifest.Annotations[oci.CreatedAnnotation]).To(BeEquivalentTo(created)) + g.Expect(manifest.Annotations[oci.SourceAnnotation]).To(BeEquivalentTo(source)) + g.Expect(manifest.Annotations[oci.RevisionAnnotation]).To(BeEquivalentTo(revision)) + } // Verify media types g.Expect(manifest.MediaType).To(Equal(types.OCIManifestSchema1)) g.Expect(manifest.Config.MediaType).To(BeEquivalentTo(oci.CanonicalConfigMediaType)) - g.Expect(len(manifest.Layers)).To(BeEquivalentTo(1)) - g.Expect(manifest.Layers[0].MediaType).To(BeEquivalentTo(tt.expectedMediaType)) + + numLayers := 1 + layerIdx := 0 + if tt.expectedNumLayers > 0 { + numLayers = tt.expectedNumLayers + layerIdx = tt.testLayerIndex + } + g.Expect(len(manifest.Layers)).To(BeEquivalentTo(numLayers)) + g.Expect(manifest.Layers[layerIdx].MediaType).To(BeEquivalentTo(tt.expectedMediaType)) // Verify custom annotations meta := MetadataFromAnnotations(manifest.Annotations) - g.Expect(meta.Annotations["org.opencontainers.image.documentation"]).To(BeEquivalentTo("https://my/readme.md")) - g.Expect(meta.Annotations["org.opencontainers.image.licenses"]).To(BeEquivalentTo("Apache-2.0")) + if len(meta.Annotations) > 0 { + g.Expect(meta.Annotations["org.opencontainers.image.documentation"]).To(BeEquivalentTo("https://my/readme.md")) + g.Expect(meta.Annotations["org.opencontainers.image.licenses"]).To(BeEquivalentTo("Apache-2.0")) + } - po := &PushOptions{} - for _, opt := range opts { + po := &PullOptions{ + layerType: LayerTypeTarball, + } + for _, opt := range tt.pullOpts { opt(po) } + + // Pull the artifact from registry and extract its contents to tmp + tmpPath := filepath.Join(t.TempDir(), "artifact") + _, err = c.Pull(ctx, url, tmpPath, tt.pullOpts...) + if tt.expectPullErr { + g.Expect(err).To(HaveOccurred()) + return + } + g.Expect(err).ToNot(HaveOccurred()) + switch po.layerType { case LayerTypeTarball: - // Pull the artifact from registry and extract its contents to tmp - tmpDir := t.TempDir() - _, err := c.Pull(ctx, url, tmpDir) - g.Expect(err).ToNot(HaveOccurred()) // Walk the test directory and check that all files exist in the pulled artifact fsErr := filepath.Walk(tt.sourcePath, func(path string, info fs.FileInfo, err error) error { if !info.IsDir() { - tmpPath := filepath.Join(tmpDir, strings.TrimPrefix(path, tt.sourcePath)) + tmpPath := filepath.Join(tmpPath, strings.TrimPrefix(path, tt.sourcePath)) if _, err := os.Stat(tmpPath); err != nil && os.IsNotExist(err) { return fmt.Errorf("path '%s' doesn't exist in archive", path) } @@ -171,27 +299,19 @@ func Test_Push_Pull(t *testing.T) { }) g.Expect(fsErr).ToNot(HaveOccurred()) case LayerTypeStatic: - // contents of uncompressed and compressed layer should be the same as file - expectedBytes, err := os.ReadFile(tt.sourcePath) - g.Expect(err).To(Not(HaveOccurred())) - layers, err := image.Layers() - g.Expect(err).ToNot(HaveOccurred()) - - blob, err := layers[0].Uncompressed() - g.Expect(err).ToNot(HaveOccurred()) - - b, err := io.ReadAll(blob) - g.Expect(err).ToNot(HaveOccurred()) - - g.Expect(b).To(BeEquivalentTo(expectedBytes)) - - blob, err = layers[0].Compressed() + fileInfo, err := os.Stat(tt.sourcePath) + // if a directory was pushed, then the created file is a gzipped archive and + // we don't need to check that its content matches the source directory + if fileInfo.IsDir() { + return + } + expected, err := os.ReadFile(tt.sourcePath) g.Expect(err).ToNot(HaveOccurred()) - b, err = io.ReadAll(blob) + got, err := os.ReadFile(tmpPath) g.Expect(err).ToNot(HaveOccurred()) - g.Expect(b).To(BeEquivalentTo(expectedBytes)) + g.Expect(expected).To(Equal(got)) } }) } From f155227712e5a6d18371f7d2518ee56c96ef5a78 Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Thu, 25 Jan 2024 19:34:58 +0100 Subject: [PATCH 2/4] refactor test Signed-off-by: Somtochi Onyekwere --- oci/client/pull.go | 13 ++++++------ oci/client/push_pull_test.go | 40 +++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/oci/client/pull.go b/oci/client/pull.go index c5c9391f..633bf4b3 100644 --- a/oci/client/pull.go +++ b/oci/client/pull.go @@ -107,7 +107,7 @@ func (c *Client) Pull(ctx context.Context, url, outPath string, opts ...PullOpti return nil, fmt.Errorf("index '%d' out of bound for '%d' layers in artifact", o.layerIndex, len(layers)) } - err = extractLayer(layers[o.layerIndex], outPath, o) + err = extractLayer(layers[o.layerIndex], outPath, o.layerType) if err != nil { return nil, err } @@ -115,26 +115,27 @@ func (c *Client) Pull(ctx context.Context, url, outPath string, opts ...PullOpti } // extractLayer extracts the Layer to the path -func extractLayer(layer gcrv1.Layer, path string, opts *PullOptions) error { +func extractLayer(layer gcrv1.Layer, path string, layerType LayerType) error { var blob io.Reader blob, err := layer.Compressed() if err != nil { return fmt.Errorf("extracting layer failed: %w", err) } - if opts.layerType == "" { + actualLayerType := layerType + if actualLayerType == "" { bufReader := bufio.NewReader(blob) if ok, _ := isGzipBlob(bufReader); ok { - opts.layerType = LayerTypeTarball + actualLayerType = LayerTypeTarball } else { - opts.layerType = LayerTypeStatic + actualLayerType = LayerTypeStatic } // the bufio.Reader has read the bytes from the io.Reader // and should be used instead blob = bufReader } - return extractLayerType(path, blob, opts.layerType) + return extractLayerType(path, blob, actualLayerType) } // extractLayerType extracts the contents of a io.Reader to the given path. diff --git a/oci/client/push_pull_test.go b/oci/client/push_pull_test.go index 9195d365..da478300 100644 --- a/oci/client/push_pull_test.go +++ b/oci/client/push_pull_test.go @@ -54,29 +54,32 @@ func Test_Push_Pull(t *testing.T) { pushOpts []PushOption pullOpts []PullOption pushFn func(url string, path string) error + testLayerType LayerType testLayerIndex int expectedNumLayers int - expectPullErr bool - expectPushErr bool + expectedPullErr bool + expectedPushErr bool expectedMediaType types.MediaType }{ { name: "push directory (default layer type)", tag: "v0.0.1", sourcePath: "testdata/artifact", + testLayerType: LayerTypeTarball, expectedMediaType: oci.CanonicalContentMediaType, }, { - name: "push directory (specify layer type)", - tag: "v0.0.1", - sourcePath: "testdata/artifact", - expectedMediaType: oci.CanonicalContentMediaType, + name: "push directory (specify layer type)", + tag: "v0.0.1", + sourcePath: "testdata/artifact", pushOpts: []PushOption{ WithPushLayerType(LayerTypeTarball), }, pullOpts: []PullOption{ WithPullLayerType(LayerTypeTarball), }, + testLayerType: LayerTypeTarball, + expectedMediaType: oci.CanonicalContentMediaType, }, { name: "push static file", @@ -89,6 +92,7 @@ func Test_Push_Pull(t *testing.T) { pullOpts: []PullOption{ WithPullLayerType(LayerTypeStatic), }, + testLayerType: LayerTypeTarball, expectedMediaType: getLayerMediaType("ml"), }, { @@ -97,7 +101,7 @@ func Test_Push_Pull(t *testing.T) { pushOpts: []PushOption{ WithPushLayerType(LayerTypeStatic), }, - expectPushErr: true, + expectedPushErr: true, }, { name: "push static file without media type extension", @@ -106,6 +110,7 @@ func Test_Push_Pull(t *testing.T) { pushOpts: []PushOption{ WithPushLayerType(LayerTypeStatic), }, + testLayerType: LayerTypeStatic, expectedMediaType: oci.CanonicalMediaTypePrefix, }, { @@ -115,6 +120,7 @@ func Test_Push_Pull(t *testing.T) { pullOpts: []PullOption{ WithPullLayerType(LayerTypeStatic), }, + testLayerType: LayerTypeStatic, expectedMediaType: oci.CanonicalContentMediaType, }, { @@ -123,7 +129,7 @@ func Test_Push_Pull(t *testing.T) { sourcePath: "testdata/artifact/deployment.yaml", pullOpts: []PullOption{WithPullLayerType(LayerTypeTarball)}, pushOpts: []PushOption{WithPushLayerType(LayerTypeStatic)}, - expectPullErr: true, + expectedPullErr: true, expectedMediaType: oci.CanonicalMediaTypePrefix, }, { @@ -160,6 +166,7 @@ func Test_Push_Pull(t *testing.T) { return err }, testLayerIndex: 1, + testLayerType: LayerTypeTarball, expectedNumLayers: 2, expectedMediaType: oci.CanonicalContentMediaType, }, @@ -196,7 +203,7 @@ func Test_Push_Pull(t *testing.T) { return err }, expectedMediaType: oci.CanonicalContentMediaType, - expectPullErr: true, + expectedPullErr: true, }, } @@ -222,7 +229,7 @@ func Test_Push_Pull(t *testing.T) { } else { err = tt.pushFn(url, tt.sourcePath) } - if tt.expectPushErr { + if tt.expectedPushErr { g.Expect(err).To(HaveOccurred()) return } @@ -268,23 +275,16 @@ func Test_Push_Pull(t *testing.T) { g.Expect(meta.Annotations["org.opencontainers.image.licenses"]).To(BeEquivalentTo("Apache-2.0")) } - po := &PullOptions{ - layerType: LayerTypeTarball, - } - for _, opt := range tt.pullOpts { - opt(po) - } - // Pull the artifact from registry and extract its contents to tmp tmpPath := filepath.Join(t.TempDir(), "artifact") _, err = c.Pull(ctx, url, tmpPath, tt.pullOpts...) - if tt.expectPullErr { + if tt.expectedPullErr { g.Expect(err).To(HaveOccurred()) return } g.Expect(err).ToNot(HaveOccurred()) - switch po.layerType { + switch tt.testLayerType { case LayerTypeTarball: // Walk the test directory and check that all files exist in the pulled artifact fsErr := filepath.Walk(tt.sourcePath, func(path string, info fs.FileInfo, err error) error { @@ -312,6 +312,8 @@ func Test_Push_Pull(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) g.Expect(expected).To(Equal(got)) + default: + t.Errorf("no layer type specified for test") } }) } From 255f8fc9a20b869685e7916b06556d4a95a725a9 Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Thu, 25 Jan 2024 19:46:25 +0100 Subject: [PATCH 3/4] test for static archive Signed-off-by: Somtochi Onyekwere --- oci/client/push_pull_test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/oci/client/push_pull_test.go b/oci/client/push_pull_test.go index da478300..b8ceebfe 100644 --- a/oci/client/push_pull_test.go +++ b/oci/client/push_pull_test.go @@ -17,6 +17,8 @@ limitations under the License. package client import ( + "bufio" + "bytes" "context" "fmt" "io/fs" @@ -299,16 +301,18 @@ func Test_Push_Pull(t *testing.T) { }) g.Expect(fsErr).ToNot(HaveOccurred()) case LayerTypeStatic: + got, err := os.ReadFile(tmpPath) + g.Expect(err).ToNot(HaveOccurred()) + fileInfo, err := os.Stat(tt.sourcePath) - // if a directory was pushed, then the created file is a gzipped archive and - // we don't need to check that its content matches the source directory + // if a directory was pushed, then the created file should be a gzipped archive if fileInfo.IsDir() { + bufReader := bufio.NewReader(bytes.NewReader(got)) + g.Expect(isGzipBlob(bufReader)).To(BeTrue()) return } - expected, err := os.ReadFile(tt.sourcePath) - g.Expect(err).ToNot(HaveOccurred()) - got, err := os.ReadFile(tmpPath) + expected, err := os.ReadFile(tt.sourcePath) g.Expect(err).ToNot(HaveOccurred()) g.Expect(expected).To(Equal(got)) From a33044510560cd52a3d3c0e9a5caa837d4f12ba7 Mon Sep 17 00:00:00 2001 From: Somtochi Onyekwere Date: Thu, 25 Jan 2024 19:51:14 +0100 Subject: [PATCH 4/4] fix options Signed-off-by: Somtochi Onyekwere --- oci/client/push_pull_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oci/client/push_pull_test.go b/oci/client/push_pull_test.go index b8ceebfe..3c68b253 100644 --- a/oci/client/push_pull_test.go +++ b/oci/client/push_pull_test.go @@ -94,7 +94,7 @@ func Test_Push_Pull(t *testing.T) { pullOpts: []PullOption{ WithPullLayerType(LayerTypeStatic), }, - testLayerType: LayerTypeTarball, + testLayerType: LayerTypeStatic, expectedMediaType: getLayerMediaType("ml"), }, { @@ -106,7 +106,7 @@ func Test_Push_Pull(t *testing.T) { expectedPushErr: true, }, { - name: "push static file without media type extension", + name: "push static file without media type extension (automatic layer detection for pull)", tag: "v0.0.2", sourcePath: "testdata/artifact/deployment.yaml", pushOpts: []PushOption{