diff --git a/cmd/oras/internal/option/remote.go b/cmd/oras/internal/option/remote.go index efd4e054a..64f2f769a 100644 --- a/cmd/oras/internal/option/remote.go +++ b/cmd/oras/internal/option/remote.go @@ -25,6 +25,7 @@ import ( "os" "strconv" "strings" + "sync" credentials "github.com/oras-project/oras-credentials-go" "github.com/spf13/pflag" @@ -248,13 +249,22 @@ func (opts *Remote) Credential() auth.Credential { } // NewRegistry assembles a oras remote registry. -func (opts *Remote) NewRegistry(hostname string, common Common) (reg *remote.Registry, err error) { +func (opts *Remote) NewRegistry(hostname string, log func(...interface{}), common Common) (reg *remote.Registry, err error) { reg, err = remote.NewRegistry(hostname) if err != nil { return nil, err } hostname = reg.Reference.Registry reg.PlainHTTP = opts.isPlainHttp(hostname) + + if opts.distributionSpec.referrersAPI == nil || *opts.distributionSpec.referrersAPI != false { + once := sync.Once{} + reg.HandleWarning = func(warning remote.Warning) { + once.Do(func() { + log(warning.Text) + }) + } + } if reg.Client, err = opts.authClient(hostname, common.Debug); err != nil { return nil, err } @@ -262,7 +272,7 @@ func (opts *Remote) NewRegistry(hostname string, common Common) (reg *remote.Reg } // NewRepository assembles a oras remote repository. -func (opts *Remote) NewRepository(reference string, common Common) (repo *remote.Repository, err error) { +func (opts *Remote) NewRepository(reference string, log func(...interface{}), common Common) (repo *remote.Repository, err error) { repo, err = remote.NewRepository(reference) if err != nil { return nil, err @@ -272,6 +282,16 @@ func (opts *Remote) NewRepository(reference string, common Common) (repo *remote if repo.Client, err = opts.authClient(hostname, common.Debug); err != nil { return nil, err } + + if opts.distributionSpec.referrersAPI == nil || *opts.distributionSpec.referrersAPI != false { + once := sync.Once{} + repo.HandleWarning = func(warning remote.Warning) { + once.Do(func() { + log(warning.Text) + }) + } + } + if opts.distributionSpec.referrersAPI != nil { if err := repo.SetReferrersCapability(*opts.distributionSpec.referrersAPI); err != nil { return nil, err diff --git a/cmd/oras/internal/option/remote_test.go b/cmd/oras/internal/option/remote_test.go index abd1c3029..f7b105091 100644 --- a/cmd/oras/internal/option/remote_test.go +++ b/cmd/oras/internal/option/remote_test.go @@ -180,7 +180,7 @@ func TestRemote_NewRegistry(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - reg, err := opts.NewRegistry(uri.Host, opts.Common) + reg, err := opts.NewRegistry(uri.Host, nil, opts.Common) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -208,7 +208,7 @@ func TestRemote_NewRepository(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - repo, err := opts.NewRepository(uri.Host+"/"+testRepo, opts.Common) + repo, err := opts.NewRepository(uri.Host+"/"+testRepo, nil, opts.Common) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -255,7 +255,7 @@ func TestRemote_NewRepository_Retry(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - repo, err := opts.NewRepository(uri.Host+"/"+testRepo, opts.Common) + repo, err := opts.NewRepository(uri.Host+"/"+testRepo, nil, opts.Common) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/cmd/oras/internal/option/target.go b/cmd/oras/internal/option/target.go index 9494e1789..c94baf37c 100644 --- a/cmd/oras/internal/option/target.go +++ b/cmd/oras/internal/option/target.go @@ -111,7 +111,7 @@ func parseOCILayoutReference(raw string) (path string, ref string, err error) { } // NewTarget generates a new target based on opts. -func (opts *Target) NewTarget(common Common) (oras.GraphTarget, error) { +func (opts *Target) NewTarget(common Common, log func(...interface{})) (oras.GraphTarget, error) { switch opts.Type { case TargetTypeOCILayout: var err error @@ -121,7 +121,7 @@ func (opts *Target) NewTarget(common Common) (oras.GraphTarget, error) { } return oci.New(opts.Path) case TargetTypeRemote: - repo, err := opts.NewRepository(opts.RawReference, common) + repo, err := opts.NewRepository(opts.RawReference, nil, common) if err != nil { return nil, err } @@ -159,7 +159,7 @@ func (opts *Target) NewReadonlyTarget(ctx context.Context, common Common) (ReadO } return oci.NewFromTar(ctx, opts.Path) case TargetTypeRemote: - repo, err := opts.NewRepository(opts.RawReference, common) + repo, err := opts.NewRepository(opts.RawReference, nil, common) if err != nil { return nil, err } diff --git a/cmd/oras/root/attach.go b/cmd/oras/root/attach.go index a18be3e0d..f962c840a 100644 --- a/cmd/oras/root/attach.go +++ b/cmd/oras/root/attach.go @@ -116,7 +116,7 @@ func runAttach(ctx context.Context, opts attachOptions) error { } defer store.Close() - dst, err := opts.NewTarget(opts.Common) + dst, err := opts.NewTarget(opts.Common, logger.Warn) if err != nil { return err } diff --git a/cmd/oras/root/blob/delete.go b/cmd/oras/root/blob/delete.go index 0f55d044b..7e9d46bf8 100644 --- a/cmd/oras/root/blob/delete.go +++ b/cmd/oras/root/blob/delete.go @@ -72,7 +72,7 @@ Example - Delete a blob and print its descriptor: func deleteBlob(ctx context.Context, opts deleteBlobOptions) (err error) { ctx, _ = opts.WithContext(ctx) - repo, err := opts.NewRepository(opts.targetRef, opts.Common) + repo, err := opts.NewRepository(opts.targetRef, nil, opts.Common) if err != nil { return err } diff --git a/cmd/oras/root/blob/push.go b/cmd/oras/root/blob/push.go index 9de6c45fe..f973042a4 100644 --- a/cmd/oras/root/blob/push.go +++ b/cmd/oras/root/blob/push.go @@ -96,9 +96,9 @@ Example - Push blob 'hi.txt' into an OCI image layout folder 'layout-dir': } func pushBlob(ctx context.Context, opts pushBlobOptions) (err error) { - ctx, _ = opts.WithContext(ctx) + ctx, logger := opts.WithContext(ctx) - repo, err := opts.NewTarget(opts.Common) + repo, err := opts.NewTarget(opts.Common, logger.Warn) if err != nil { return err } diff --git a/cmd/oras/root/cp.go b/cmd/oras/root/cp.go index 20cc2a753..e207391b0 100644 --- a/cmd/oras/root/cp.go +++ b/cmd/oras/root/cp.go @@ -111,7 +111,7 @@ func runCopy(ctx context.Context, opts copyOptions) error { } // Prepare destination - dst, err := opts.To.NewTarget(opts.Common) + dst, err := opts.To.NewTarget(opts.Common, logger.Warn) if err != nil { return err } diff --git a/cmd/oras/root/login.go b/cmd/oras/root/login.go index aa862fd4d..7bed32686 100644 --- a/cmd/oras/root/login.go +++ b/cmd/oras/root/login.go @@ -108,7 +108,7 @@ func runLogin(ctx context.Context, opts loginOptions) (err error) { if err != nil { return err } - remote, err := opts.Remote.NewRegistry(opts.Hostname, opts.Common) + remote, err := opts.Remote.NewRegistry(opts.Hostname, nil, opts.Common) if err != nil { return err } diff --git a/cmd/oras/root/manifest/delete.go b/cmd/oras/root/manifest/delete.go index 8ece10400..1aae58a64 100644 --- a/cmd/oras/root/manifest/delete.go +++ b/cmd/oras/root/manifest/delete.go @@ -76,8 +76,8 @@ Example - Delete a manifest by digest 'sha256:99e4703fbf30916f549cd6bfa9cdbab614 } func deleteManifest(ctx context.Context, opts deleteOptions) error { - ctx, _ = opts.WithContext(ctx) - repo, err := opts.NewRepository(opts.targetRef, opts.Common) + ctx, logger := opts.WithContext(ctx) + repo, err := opts.NewRepository(opts.targetRef, logger.Warn, opts.Common) if err != nil { return err } diff --git a/cmd/oras/root/manifest/push.go b/cmd/oras/root/manifest/push.go index f6391c939..223cf4274 100644 --- a/cmd/oras/root/manifest/push.go +++ b/cmd/oras/root/manifest/push.go @@ -109,7 +109,7 @@ func pushManifest(ctx context.Context, opts pushOptions) error { ctx, logger := opts.WithContext(ctx) var target oras.Target var err error - target, err = opts.NewTarget(opts.Common) + target, err = opts.NewTarget(opts.Common, logger.Warn) if err != nil { return err } diff --git a/cmd/oras/root/push.go b/cmd/oras/root/push.go index bd6467b05..e40d4cb31 100644 --- a/cmd/oras/root/push.go +++ b/cmd/oras/root/push.go @@ -126,7 +126,7 @@ Example - Push file "hi.txt" into an OCI image layout folder 'layout-dir' with t } func runPush(ctx context.Context, opts pushOptions) error { - ctx, _ = opts.WithContext(ctx) + ctx, logger := opts.WithContext(ctx) annotations, err := opts.LoadManifestAnnotations() if err != nil { return err @@ -173,7 +173,7 @@ func runPush(ctx context.Context, opts pushOptions) error { } // prepare push - dst, err := opts.NewTarget(opts.Common) + dst, err := opts.NewTarget(opts.Common, logger.Warn) if err != nil { return err } diff --git a/cmd/oras/root/repo/ls.go b/cmd/oras/root/repo/ls.go index f2da5e5f3..e72298401 100644 --- a/cmd/oras/root/repo/ls.go +++ b/cmd/oras/root/repo/ls.go @@ -69,8 +69,8 @@ Example - List the repositories under the registry that include values lexically } func listRepository(ctx context.Context, opts repositoryOptions) error { - ctx, _ = opts.WithContext(ctx) - reg, err := opts.Remote.NewRegistry(opts.hostname, opts.Common) + ctx, logger := opts.WithContext(ctx) + reg, err := opts.Remote.NewRegistry(opts.hostname, logger.Warn, opts.Common) if err != nil { return err } diff --git a/cmd/oras/root/tag.go b/cmd/oras/root/tag.go index 35475fec6..ecb486a8b 100644 --- a/cmd/oras/root/tag.go +++ b/cmd/oras/root/tag.go @@ -72,8 +72,8 @@ Example - Tag the manifest 'v1.0.1' to 'v1.0.2' in an OCI image layout folder 'l } func tagManifest(ctx context.Context, opts tagOptions) error { - ctx, _ = opts.WithContext(ctx) - target, err := opts.NewTarget(opts.Common) + ctx, logger := opts.WithContext(ctx) + target, err := opts.NewTarget(opts.Common, logger.Warn) if err != nil { return err }