Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 33 additions & 19 deletions oci/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,47 +18,61 @@ package oci

import (
"context"
"net/http"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
)

// Client holds the options for accessing remote OCI registries.
type Client struct {
options []crane.Option
options []remote.Option
}

// NewClient returns an OCI client configured with the given crane options.
func NewClient(opts []crane.Option) *Client {
options := []crane.Option{
crane.WithUserAgent(UserAgent),
// NewClient returns an OCI client configured with the given remote options.
func NewClient(opts []remote.Option) *Client {
options := []remote.Option{
remote.WithUserAgent(UserAgent),
}
options = append(options, opts...)

return &Client{options: options}
}

// DefaultOptions returns an empty list of client options.
func DefaultOptions() []crane.Option {
return []crane.Option{}
func DefaultOptions() []remote.Option {
return []remote.Option{}
}

// GetOptions returns the list of crane.Option used by this Client.
func (c *Client) GetOptions() []crane.Option {
// GetOptions returns the list of remote.Option used by this Client.
func (c *Client) GetOptions() []remote.Option {
return c.options
}

// optionsWithContext returns the crane options for the given context.
func (c *Client) optionsWithContext(ctx context.Context) []crane.Option {
options := []crane.Option{
crane.WithContext(ctx),
// optionsWithContext returns the remote options for the given context.
func (c *Client) optionsWithContext(ctx context.Context) []remote.Option {
options := []remote.Option{
remote.WithContext(ctx),
}
return append(options, c.options...)
}

// WithRetryBackOff returns a function for setting the given backoff on crane.Option.
func WithRetryBackOff(backoff remote.Backoff) crane.Option {
return func(options *crane.Options) {
options.Remote = append(options.Remote, remote.WithRetryBackoff(backoff))
}
// WithRetryBackOff returns a function for setting the given backoff on
// remote.Option.
func WithRetryBackOff(backoff remote.Backoff) remote.Option {
return remote.WithRetryBackoff(backoff)
}

// WithTransport returns a remote.Option that sets the HTTP transport.
func WithTransport(t http.RoundTripper) remote.Option {
return remote.WithTransport(t)
}

// defaultRetryTransport wraps an http.RoundTripper with retry logic
// suitable for use with the remote package.
func defaultRetryTransport(inner http.RoundTripper) http.RoundTripper {
return transport.NewRetry(inner,
transport.WithRetryPredicate(defaultRetryPredicate),
transport.WithRetryStatusCodes(retryableStatusCodes...),
)
}
6 changes: 3 additions & 3 deletions oci/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ import (
"context"
"fmt"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
)

// Delete deletes a particular image from an OCI repository
// If the url has no tag, the latest image is deleted
func (c *Client) Delete(ctx context.Context, url string) error {
_, err := name.ParseReference(url)
ref, err := name.ParseReference(url)
if err != nil {
return fmt.Errorf("invalid URL: %w", err)
}

return crane.Delete(url, c.optionsWithContext(ctx)...)
return remote.Delete(ref, c.optionsWithContext(ctx)...)
}
11 changes: 8 additions & 3 deletions oci/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import (
"testing"
"time"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
gcrv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/google/go-containerregistry/pkg/v1/remote"
. "github.com/onsi/gomega"
)

Expand All @@ -49,7 +50,9 @@ func TestDelete(t *testing.T) {
img, err := random.Image(1024, 1)
g.Expect(err).ToNot(HaveOccurred())
img = mutate.Annotations(img, m.ToAnnotations()).(gcrv1.Image)
err = crane.Push(img, dst, c.options...)
ref, err := name.ParseReference(dst)
g.Expect(err).ToNot(HaveOccurred())
err = remote.Write(ref, img, c.options...)
g.Expect(err).ToNot(HaveOccurred())
}

Expand Down Expand Up @@ -83,7 +86,9 @@ func TestDelete(t *testing.T) {
g.Expect(err).To(BeNil())

for _, tag := range tt.checkTags {
_, err = crane.Pull(fmt.Sprintf("%s/%s:%s", dockerReg, repo, tag))
ref, err := name.ParseReference(fmt.Sprintf("%s/%s:%s", dockerReg, repo, tag))
g.Expect(err).ToNot(HaveOccurred())
_, err = remote.Get(ref)
g.Expect(err).ToNot(BeNil())
g.Expect(err.Error()).To(ContainSubstring("manifest unknown"))
}
Expand Down
11 changes: 8 additions & 3 deletions oci/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ import (
"os"
"path/filepath"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
)

// Diff compares the files included in an OCI image with the local files in the given path
// and returns an error if the contents is different
func (c *Client) Diff(ctx context.Context, url, dir string, ignorePaths []string) error {
_, err := name.ParseReference(url)
ref, err := name.ParseReference(url)
if err != nil {
return fmt.Errorf("invalid URL: %w", err)
}
Expand Down Expand Up @@ -65,11 +65,16 @@ func (c *Client) Diff(ctx context.Context, url, dir string, ignorePaths []string
return fmt.Errorf("calculating artifact hash failed: %w", err)
}

img, err := crane.Pull(url, c.optionsWithContext(ctx)...)
desc, err := remote.Get(ref, c.optionsWithContext(ctx)...)
if err != nil {
return err
}

img, err := desc.Image()
if err != nil {
return fmt.Errorf("parsing image failed: %w", err)
}

layers, err := img.Layers()
if err != nil {
return fmt.Errorf("failed to list layers: %w", err)
Expand Down
1 change: 0 additions & 1 deletion oci/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"path/filepath"
"testing"

_ "github.com/google/go-containerregistry/pkg/crane"
. "github.com/onsi/gomega"
)

Expand Down
22 changes: 13 additions & 9 deletions oci/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import (
"strings"

"github.com/Masterminds/semver/v3"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
gcrv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"

"github.com/fluxcd/pkg/version"
)
Expand All @@ -45,7 +46,13 @@ type ListOptions struct {
// List fetches the tags and their manifests for a given OCI repository.
func (c *Client) List(ctx context.Context, url string, opts ListOptions) ([]Metadata, error) {
metas := make([]Metadata, 0)
tags, err := crane.ListTags(url, c.options...)

repo, err := name.NewRepository(url)
if err != nil {
return nil, fmt.Errorf("parsing repository failed: %w", err)
}

tags, err := remote.List(repo, c.optionsWithContext(ctx)...)
if err != nil {
return nil, fmt.Errorf("listing tags failed: %w", err)
}
Expand Down Expand Up @@ -94,12 +101,13 @@ func (c *Client) List(ctx context.Context, url string, opts ListOptions) ([]Meta
URL: fmt.Sprintf("%s:%s", url, tag),
}

manifestJSON, err := crane.Manifest(meta.URL, c.optionsWithContext(ctx)...)
tagRef := repo.Tag(tag)
desc, err := remote.Get(tagRef, c.optionsWithContext(ctx)...)
if err != nil {
return nil, fmt.Errorf("fetching manifest failed: %w", err)
}

manifest, err := gcrv1.ParseManifest(bytes.NewReader(manifestJSON))
manifest, err := gcrv1.ParseManifest(bytes.NewReader(desc.Manifest))
if err != nil {
return nil, fmt.Errorf("parsing manifest failed: %w", err)
}
Expand All @@ -109,11 +117,7 @@ func (c *Client) List(ctx context.Context, url string, opts ListOptions) ([]Meta
meta.Source = manifestMetadata.Source
meta.Created = manifestMetadata.Created

digest, err := crane.Digest(meta.URL, c.optionsWithContext(ctx)...)
if err != nil {
return nil, fmt.Errorf("fetching digest failed: %w", err)
}
meta.Digest = digest
meta.Digest = desc.Digest.String()

metas = append(metas, meta)
}
Expand Down
12 changes: 8 additions & 4 deletions oci/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import (
"testing"
"time"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
gcrv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/google/go-containerregistry/pkg/v1/remote"
. "github.com/onsi/gomega"
)

Expand Down Expand Up @@ -61,7 +61,9 @@ func Test_List(t *testing.T) {
img, err := random.Image(1024, 1)
g.Expect(err).ToNot(HaveOccurred())
img = mutate.Annotations(img, m.ToAnnotations()).(gcrv1.Image)
err = crane.Push(img, dst, c.options...)
ref, err := name.ParseReference(dst)
g.Expect(err).ToNot(HaveOccurred())
err = remote.Write(ref, img, c.options...)
g.Expect(err).ToNot(HaveOccurred())
}

Expand Down Expand Up @@ -135,9 +137,11 @@ func Test_List(t *testing.T) {

g.Expect(meta.ToAnnotations()).To(Equal(m.ToAnnotations()))

digest, err := crane.Digest(meta.URL, c.options...)
digestRef, err := name.ParseReference(fmt.Sprintf("%s/%s:%s", dockerReg, repo, tag.TagStr()))
g.Expect(err).ToNot(HaveOccurred())
desc, err := remote.Get(digestRef, c.options...)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(meta.Digest).To(Equal(digest))
g.Expect(meta.Digest).To(Equal(desc.Digest.String()))
}
})
}
Expand Down
4 changes: 2 additions & 2 deletions oci/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"strings"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/v1/remote"
)

// LoginWithCredentials configures the client with static credentials, accepts a single token
Expand All @@ -32,7 +32,7 @@ func (c *Client) LoginWithCredentials(credentials string) error {
return err
}

c.options = append(c.options, crane.WithAuth(auth))
c.options = append(c.options, remote.WithAuth(auth))
return nil
}

Expand Down
9 changes: 6 additions & 3 deletions oci/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import (
"strings"
"testing"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
. "github.com/onsi/gomega"
)

Expand Down Expand Up @@ -72,10 +73,12 @@ func Test_Login(t *testing.T) {
},
}

c.options = append(c.options, crane.WithTransport(&transportFunc))
c.options = append(c.options, remote.WithTransport(&transportFunc))

err = crane.Delete(fmt.Sprintf("%s/%s:%s", dockerReg, "test", "test"), c.optionsWithContext(ctx)...)
ref, err := name.ParseReference(fmt.Sprintf("%s/%s:%s", dockerReg, "test", "test"))
g.Expect(err).ToNot(HaveOccurred())

_ = remote.Delete(ref, c.optionsWithContext(ctx)...)
g.Expect(transportFunc.request).ToNot(BeNil())
g.Expect(transportFunc.request.Header.Get("Authorization")).To(Equal(tt.expectedAuth))
})
Expand Down
9 changes: 7 additions & 2 deletions oci/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import (
"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/google/go-containerregistry/pkg/v1/remote"

"github.com/fluxcd/pkg/tar"
)
Expand Down Expand Up @@ -75,11 +75,16 @@ func (c *Client) Pull(ctx context.Context, url, outPath string, opts ...PullOpti
return nil, fmt.Errorf("invalid URL: %w", err)
}

img, err := crane.Pull(url, c.optionsWithContext(ctx)...)
desc, err := remote.Get(ref, c.optionsWithContext(ctx)...)
if err != nil {
return nil, err
}

img, err := desc.Image()
if err != nil {
return nil, fmt.Errorf("parsing image failed: %w", err)
}

digest, err := img.Digest()
if err != nil {
return nil, fmt.Errorf("parsing digest failed: %w", err)
Expand Down
7 changes: 5 additions & 2 deletions oci/pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import (
"path/filepath"
"testing"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -53,7 +54,9 @@ func Test_PullAnyTarball(t *testing.T) {
img, err = mutate.Append(img, mutate.Addendum{Layer: layer})
g.Expect(err).ToNot(HaveOccurred())

g.Expect(crane.Push(img, dst, c.optionsWithContext(ctx)...)).ToNot(HaveOccurred())
ref, err := name.ParseReference(dst)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(remote.Write(ref, img, c.optionsWithContext(ctx)...)).ToNot(HaveOccurred())

extractTo := filepath.Join(t.TempDir(), "artifact")
m, err := c.Pull(ctx, dst, extractTo)
Expand Down
4 changes: 2 additions & 2 deletions oci/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import (
"path/filepath"
"time"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/name"
gcrv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/static"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
Expand Down Expand Up @@ -146,7 +146,7 @@ func (c *Client) Push(ctx context.Context, url, sourcePath string, opts ...PushO
return "", fmt.Errorf("appeding content to artifact failed: %w", err)
}

if err := crane.Push(img, url, c.optionsWithContext(ctx)...); err != nil {
if err := remote.Write(ref, img, c.optionsWithContext(ctx)...); err != nil {
return "", fmt.Errorf("pushing artifact failed: %w", err)
}

Expand Down
Loading