diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7bb92c74..4a2883bd9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,9 +43,10 @@ jobs: run: make test - name: Run E2E Tests run: | - sh $GITHUB_WORKSPACE/test/e2e/scripts/e2e.sh $GITHUB_WORKSPACE --clean + bash $GITHUB_WORKSPACE/test/e2e/scripts/e2e.sh $GITHUB_WORKSPACE --clean env: ORAS_PATH: bin/linux/amd64/oras + COVERAGE_DUMP_ROOT: .cover - name: Check Version run: bin/linux/amd64/oras version - name: Upload coverage to codecov.io diff --git a/.gitignore b/.gitignore index 7b8e4c7e0..0c2f8f30d 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,9 @@ debug # Custom coverage.txt +test/e2e/coverage.txt +**/covcounters.* +**/covmeta.* bin/ dist/ *.tar.gz diff --git a/Makefile b/Makefile index 6957c0d93..f93af9b07 100644 --- a/Makefile +++ b/Makefile @@ -124,3 +124,7 @@ sign: for f in $$(ls _dist/*.{gz,txt} 2>/dev/null) ; do \ gpg --armor --detach-sign $${f} ; \ done + +.PHONY: e2e-covdata +e2e-covdata: + $(GO_EXE) tool covdata textfmt -i="test/e2e/${COVERAGE_DUMP_ROOT}" -o test/e2e/coverage.txt diff --git a/cmd/oras/internal/option/common.go b/cmd/oras/internal/option/common.go index 9d058ef81..6c5817967 100644 --- a/cmd/oras/internal/option/common.go +++ b/cmd/oras/internal/option/common.go @@ -16,7 +16,11 @@ limitations under the License. package option import ( + "context" + + "github.com/sirupsen/logrus" "github.com/spf13/pflag" + "oras.land/oras/internal/trace" ) // Common option struct. @@ -30,3 +34,8 @@ func (opts *Common) ApplyFlags(fs *pflag.FlagSet) { fs.BoolVarP(&opts.Debug, "debug", "d", false, "debug mode") fs.BoolVarP(&opts.Verbose, "verbose", "v", false, "verbose output") } + +// WithContext returns a new FieldLogger and an associated Context derived from ctx. +func (opts *Common) WithContext(ctx context.Context) (context.Context, logrus.FieldLogger) { + return trace.NewLogger(ctx, opts.Debug, opts.Verbose) +} diff --git a/cmd/oras/root/attach.go b/cmd/oras/root/attach.go index b6f626090..a1032f67b 100644 --- a/cmd/oras/root/attach.go +++ b/cmd/oras/root/attach.go @@ -28,7 +28,6 @@ import ( "oras.land/oras-go/v2/content/file" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/graph" - "oras.land/oras/internal/trace" ) type attachOptions struct { @@ -86,7 +85,7 @@ Example - Attach file to the manifest tagged 'v1' in an OCI layout folder 'layou return nil }, RunE: func(cmd *cobra.Command, args []string) error { - return runAttach(opts) + return runAttach(cmd.Context(), opts) }, } @@ -98,8 +97,8 @@ Example - Attach file to the manifest tagged 'v1' in an OCI layout folder 'layou return cmd } -func runAttach(opts attachOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func runAttach(ctx context.Context, opts attachOptions) error { + ctx, _ = opts.WithContext(ctx) annotations, err := opts.LoadManifestAnnotations() if err != nil { return err diff --git a/cmd/oras/root/blob/delete.go b/cmd/oras/root/blob/delete.go index 9b99eb80a..0f55d044b 100644 --- a/cmd/oras/root/blob/delete.go +++ b/cmd/oras/root/blob/delete.go @@ -16,6 +16,7 @@ limitations under the License. package blob import ( + "context" "errors" "fmt" "os" @@ -23,7 +24,6 @@ import ( "github.com/spf13/cobra" "oras.land/oras-go/v2/errdef" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/trace" ) type deleteBlobOptions struct { @@ -62,7 +62,7 @@ Example - Delete a blob and print its descriptor: }, RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] - return deleteBlob(opts) + return deleteBlob(cmd.Context(), opts) }, } @@ -70,8 +70,8 @@ Example - Delete a blob and print its descriptor: return cmd } -func deleteBlob(opts deleteBlobOptions) (err error) { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func deleteBlob(ctx context.Context, opts deleteBlobOptions) (err error) { + ctx, _ = opts.WithContext(ctx) repo, err := opts.NewRepository(opts.targetRef, opts.Common) if err != nil { return err diff --git a/cmd/oras/root/blob/fetch.go b/cmd/oras/root/blob/fetch.go index 614a414aa..cbf0d31c9 100644 --- a/cmd/oras/root/blob/fetch.go +++ b/cmd/oras/root/blob/fetch.go @@ -16,6 +16,7 @@ limitations under the License. package blob import ( + "context" "errors" "fmt" "io" @@ -28,7 +29,6 @@ import ( "oras.land/oras-go/v2/content" "oras.land/oras-go/v2/registry/remote" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/trace" ) type fetchBlobOptions struct { @@ -80,7 +80,7 @@ Example - Fetch and print a blob from OCI image layout archive file 'layout.tar' }, Aliases: []string{"get"}, RunE: func(cmd *cobra.Command, args []string) error { - return fetchBlob(opts) + return fetchBlob(cmd.Context(), opts) }, } @@ -89,8 +89,8 @@ Example - Fetch and print a blob from OCI image layout archive file 'layout.tar' return cmd } -func fetchBlob(opts fetchBlobOptions) (fetchErr error) { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func fetchBlob(ctx context.Context, opts fetchBlobOptions) (fetchErr error) { + ctx, _ = opts.WithContext(ctx) var target oras.ReadOnlyTarget target, err := opts.NewReadonlyTarget(ctx, opts.Common) if err != nil { diff --git a/cmd/oras/root/blob/push.go b/cmd/oras/root/blob/push.go index 9a21bbd64..ed5d409bc 100644 --- a/cmd/oras/root/blob/push.go +++ b/cmd/oras/root/blob/push.go @@ -16,6 +16,7 @@ limitations under the License. package blob import ( + "context" "errors" "fmt" "os" @@ -25,7 +26,6 @@ import ( "oras.land/oras/cmd/oras/internal/display" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/file" - "oras.land/oras/internal/trace" ) type pushBlobOptions struct { @@ -85,7 +85,7 @@ Example - Push blob 'hi.txt' into an OCI layout folder 'layout-dir': return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { - return pushBlob(opts) + return pushBlob(cmd.Context(), opts) }, } @@ -95,8 +95,8 @@ Example - Push blob 'hi.txt' into an OCI layout folder 'layout-dir': return cmd } -func pushBlob(opts pushBlobOptions) (err error) { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func pushBlob(ctx context.Context, opts pushBlobOptions) (err error) { + ctx, _ = opts.WithContext(ctx) repo, err := opts.NewTarget(opts.Common) if err != nil { diff --git a/cmd/oras/root/cp.go b/cmd/oras/root/cp.go index c8d270569..26b58d18c 100644 --- a/cmd/oras/root/cp.go +++ b/cmd/oras/root/cp.go @@ -28,7 +28,6 @@ import ( "oras.land/oras/cmd/oras/internal/display" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/graph" - "oras.land/oras/internal/trace" ) type copyOptions struct { @@ -86,7 +85,7 @@ Example - Copy an artifact with multiple tags with concurrency tuned: return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { - return runCopy(opts) + return runCopy(cmd.Context(), opts) }, } cmd.Flags().BoolVarP(&opts.recursive, "recursive", "r", false, "[Preview] recursively copy the artifact and its referrer artifacts") @@ -96,8 +95,8 @@ Example - Copy an artifact with multiple tags with concurrency tuned: return cmd } -func runCopy(opts copyOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func runCopy(ctx context.Context, opts copyOptions) error { + ctx, _ = opts.WithContext(ctx) // Prepare source src, err := opts.From.NewReadonlyTarget(ctx, opts.Common) diff --git a/cmd/oras/root/discover.go b/cmd/oras/root/discover.go index dba27ad38..895534423 100644 --- a/cmd/oras/root/discover.go +++ b/cmd/oras/root/discover.go @@ -31,7 +31,6 @@ import ( "oras.land/oras-go/v2" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/graph" - "oras.land/oras/internal/trace" ) type discoverOptions struct { @@ -80,7 +79,7 @@ Example - Discover referrers of the manifest tagged 'v1' in an OCI layout folder return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { - return runDiscover(opts) + return runDiscover(cmd.Context(), opts) }, } @@ -91,8 +90,8 @@ Example - Discover referrers of the manifest tagged 'v1' in an OCI layout folder return cmd } -func runDiscover(opts discoverOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func runDiscover(ctx context.Context, opts discoverOptions) error { + ctx, _ = opts.WithContext(ctx) repo, err := opts.NewReadonlyTarget(ctx, opts.Common) if err != nil { return err diff --git a/cmd/oras/root/login.go b/cmd/oras/root/login.go index a34cbd703..ae2ebe89f 100644 --- a/cmd/oras/root/login.go +++ b/cmd/oras/root/login.go @@ -17,6 +17,7 @@ package root import ( "bufio" + "context" "errors" "fmt" "os" @@ -26,7 +27,6 @@ import ( "github.com/spf13/cobra" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/credential" - "oras.land/oras/internal/trace" ) type loginOptions struct { @@ -66,15 +66,15 @@ Example - Log in with username and password in an interactive terminal and no TL }, RunE: func(cmd *cobra.Command, args []string) error { opts.Hostname = args[0] - return runLogin(opts) + return runLogin(cmd.Context(), opts) }, } option.ApplyFlags(&opts, cmd.Flags()) return cmd } -func runLogin(opts loginOptions) (err error) { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func runLogin(ctx context.Context, opts loginOptions) (err error) { + ctx, _ = opts.WithContext(ctx) // prompt for credential if opts.Password == "" { diff --git a/cmd/oras/root/logout.go b/cmd/oras/root/logout.go index 142157159..a4ee857f4 100644 --- a/cmd/oras/root/logout.go +++ b/cmd/oras/root/logout.go @@ -16,6 +16,8 @@ limitations under the License. package root import ( + "context" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" "oras.land/oras/internal/credential" @@ -41,7 +43,7 @@ Example - Logout: Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { opts.hostname = args[0] - return runLogout(opts) + return runLogout(cmd.Context(), opts) }, } @@ -50,7 +52,7 @@ Example - Logout: return cmd } -func runLogout(opts logoutOptions) error { +func runLogout(ctx context.Context, opts logoutOptions) error { if opts.debug { logrus.SetLevel(logrus.DebugLevel) } diff --git a/cmd/oras/root/manifest/delete.go b/cmd/oras/root/manifest/delete.go index 5007911a3..8ece10400 100644 --- a/cmd/oras/root/manifest/delete.go +++ b/cmd/oras/root/manifest/delete.go @@ -16,6 +16,7 @@ limitations under the License. package manifest import ( + "context" "errors" "fmt" "os" @@ -24,7 +25,6 @@ import ( "oras.land/oras-go/v2/errdef" oerrors "oras.land/oras/cmd/oras/internal/errors" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/trace" ) type deleteOptions struct { @@ -64,9 +64,9 @@ Example - Delete a manifest by digest 'sha256:99e4703fbf30916f549cd6bfa9cdbab614 } return option.Parse(&opts) }, - RunE: func(_ *cobra.Command, args []string) error { + RunE: func(cmd *cobra.Command, args []string) error { opts.targetRef = args[0] - return deleteManifest(opts) + return deleteManifest(cmd.Context(), opts) }, } @@ -75,8 +75,8 @@ Example - Delete a manifest by digest 'sha256:99e4703fbf30916f549cd6bfa9cdbab614 return cmd } -func deleteManifest(opts deleteOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func deleteManifest(ctx context.Context, opts deleteOptions) error { + ctx, _ = opts.WithContext(ctx) repo, err := opts.NewRepository(opts.targetRef, opts.Common) if err != nil { return err diff --git a/cmd/oras/root/manifest/fetch.go b/cmd/oras/root/manifest/fetch.go index de10e5dcb..67470f2f8 100644 --- a/cmd/oras/root/manifest/fetch.go +++ b/cmd/oras/root/manifest/fetch.go @@ -16,6 +16,7 @@ limitations under the License. package manifest import ( + "context" "encoding/json" "errors" "fmt" @@ -26,7 +27,6 @@ import ( "oras.land/oras-go/v2" "oras.land/oras-go/v2/registry/remote" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/trace" ) type fetchOptions struct { @@ -79,7 +79,7 @@ Example - Fetch raw manifest from an OCI layout archive file 'layout.tar': }, Aliases: []string{"get"}, RunE: func(cmd *cobra.Command, args []string) error { - return fetchManifest(opts) + return fetchManifest(cmd.Context(), opts) }, } @@ -89,8 +89,8 @@ Example - Fetch raw manifest from an OCI layout archive file 'layout.tar': return cmd } -func fetchManifest(opts fetchOptions) (fetchErr error) { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func fetchManifest(ctx context.Context, opts fetchOptions) (fetchErr error) { + ctx, _ = opts.WithContext(ctx) target, err := opts.NewReadonlyTarget(ctx, opts.Common) if err != nil { diff --git a/cmd/oras/root/manifest/fetch_config.go b/cmd/oras/root/manifest/fetch_config.go index 61115ba55..db8fca350 100644 --- a/cmd/oras/root/manifest/fetch_config.go +++ b/cmd/oras/root/manifest/fetch_config.go @@ -28,7 +28,6 @@ import ( "oras.land/oras-go/v2/content" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/descriptor" - "oras.land/oras/internal/trace" ) type fetchConfigOptions struct { @@ -77,7 +76,7 @@ Example - Fetch and print the prettified descriptor of the config: return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { - return fetchConfig(opts) + return fetchConfig(cmd.Context(), opts) }, } @@ -86,8 +85,8 @@ Example - Fetch and print the prettified descriptor of the config: return cmd } -func fetchConfig(opts fetchConfigOptions) (fetchErr error) { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func fetchConfig(ctx context.Context, opts fetchConfigOptions) (fetchErr error) { + ctx, _ = opts.WithContext(ctx) repo, err := opts.NewReadonlyTarget(ctx, opts.Common) if err != nil { diff --git a/cmd/oras/root/manifest/push.go b/cmd/oras/root/manifest/push.go index 74dd2d297..e7a86ef02 100644 --- a/cmd/oras/root/manifest/push.go +++ b/cmd/oras/root/manifest/push.go @@ -31,7 +31,6 @@ import ( "oras.land/oras/cmd/oras/internal/display" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/file" - "oras.land/oras/internal/trace" ) type pushOptions struct { @@ -92,8 +91,8 @@ Example - Push a manifest to an OCI layout folder 'layout-dir' and tag with 'v1' opts.extraRefs = refs[1:] return option.Parse(&opts) }, - RunE: func(_ *cobra.Command, args []string) error { - return pushManifest(opts) + RunE: func(cmd *cobra.Command, args []string) error { + return pushManifest(cmd.Context(), opts) }, } @@ -104,8 +103,8 @@ Example - Push a manifest to an OCI layout folder 'layout-dir' and tag with 'v1' return cmd } -func pushManifest(opts pushOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func pushManifest(ctx context.Context, opts pushOptions) error { + ctx, _ = opts.WithContext(ctx) var target oras.Target var err error target, err = opts.NewTarget(opts.Common) diff --git a/cmd/oras/root/pull.go b/cmd/oras/root/pull.go index ef1d72a7e..ec25da1c0 100644 --- a/cmd/oras/root/pull.go +++ b/cmd/oras/root/pull.go @@ -30,7 +30,6 @@ import ( "oras.land/oras/cmd/oras/internal/fileref" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/graph" - "oras.land/oras/internal/trace" ) type pullOptions struct { @@ -88,7 +87,7 @@ Example - Pull artifact files from an OCI layout archive 'layout.tar': return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { - return runPull(opts) + return runPull(cmd.Context(), opts) }, } @@ -102,7 +101,8 @@ Example - Pull artifact files from an OCI layout archive 'layout.tar': return cmd } -func runPull(opts pullOptions) error { +func runPull(ctx context.Context, opts pullOptions) error { + ctx, _ = opts.WithContext(ctx) // Copy Options var printed sync.Map copyOptions := oras.DefaultCopyOptions @@ -181,7 +181,6 @@ func runPull(opts pullOptions) error { return ret, nil } - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) target, err := opts.NewReadonlyTarget(ctx, opts.Common) if err != nil { return err diff --git a/cmd/oras/root/push.go b/cmd/oras/root/push.go index b44ff9d04..152a2b2e1 100644 --- a/cmd/oras/root/push.go +++ b/cmd/oras/root/push.go @@ -30,7 +30,6 @@ import ( "oras.land/oras/cmd/oras/internal/display" "oras.land/oras/cmd/oras/internal/fileref" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/trace" ) type pushOptions struct { @@ -112,7 +111,7 @@ Example - Push file "hi.txt" into an OCI layout folder 'layout-dir' with tag 'te return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { - return runPush(opts) + return runPush(cmd.Context(), opts) }, } cmd.Flags().StringVarP(&opts.manifestConfigRef, "config", "", "", "`path` of image config file") @@ -123,8 +122,8 @@ Example - Push file "hi.txt" into an OCI layout folder 'layout-dir' with tag 'te return cmd } -func runPush(opts pushOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func runPush(ctx context.Context, opts pushOptions) error { + ctx, _ = opts.WithContext(ctx) annotations, err := opts.LoadManifestAnnotations() if err != nil { return err diff --git a/cmd/oras/root/repo/ls.go b/cmd/oras/root/repo/ls.go index 949bdec71..f2da5e5f3 100644 --- a/cmd/oras/root/repo/ls.go +++ b/cmd/oras/root/repo/ls.go @@ -16,13 +16,13 @@ limitations under the License. package repo import ( + "context" "fmt" "strings" "github.com/spf13/cobra" "oras.land/oras/cmd/oras/internal/option" "oras.land/oras/internal/repository" - "oras.land/oras/internal/trace" ) type repositoryOptions struct { @@ -59,7 +59,7 @@ Example - List the repositories under the registry that include values lexically if opts.hostname, opts.namespace, err = repository.ParseRepoPath(args[0]); err != nil { return fmt.Errorf("could not parse repository path: %w", err) } - return listRepository(opts) + return listRepository(cmd.Context(), opts) }, } @@ -68,8 +68,8 @@ Example - List the repositories under the registry that include values lexically return cmd } -func listRepository(opts repositoryOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func listRepository(ctx context.Context, opts repositoryOptions) error { + ctx, _ = opts.WithContext(ctx) reg, err := opts.Remote.NewRegistry(opts.hostname, opts.Common) if err != nil { return err diff --git a/cmd/oras/root/repo/tags.go b/cmd/oras/root/repo/tags.go index e77388c84..21435eadf 100644 --- a/cmd/oras/root/repo/tags.go +++ b/cmd/oras/root/repo/tags.go @@ -16,13 +16,13 @@ limitations under the License. package repo import ( + "context" "fmt" "strings" "github.com/opencontainers/go-digest" "github.com/spf13/cobra" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/trace" ) type showTagsOptions struct { @@ -68,7 +68,7 @@ Example - Show tags associated with a digest: return option.Parse(&opts) }, RunE: func(cmd *cobra.Command, args []string) error { - return showTags(opts) + return showTags(cmd.Context(), opts) }, } cmd.Flags().StringVar(&opts.last, "last", "", "start after the tag specified by `last`") @@ -77,8 +77,8 @@ Example - Show tags associated with a digest: return cmd } -func showTags(opts showTagsOptions) error { - ctx, logger := trace.NewLogger(opts.Debug, opts.Verbose) +func showTags(ctx context.Context, opts showTagsOptions) error { + ctx, logger := opts.WithContext(ctx) finder, err := opts.NewReadonlyTarget(ctx, opts.Common) if err != nil { return err diff --git a/cmd/oras/root/tag.go b/cmd/oras/root/tag.go index 80bf8fcec..ffa29a567 100644 --- a/cmd/oras/root/tag.go +++ b/cmd/oras/root/tag.go @@ -16,13 +16,13 @@ limitations under the License. package root import ( + "context" "fmt" "github.com/spf13/cobra" "oras.land/oras-go/v2" "oras.land/oras/cmd/oras/internal/display" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/trace" ) type tagOptions struct { @@ -61,8 +61,8 @@ Example - Tag the manifest 'v1.0.1' to 'v1.0.2' in an OCI layout folder 'layout- opts.targetRefs = args[1:] return option.Parse(&opts) }, - RunE: func(_ *cobra.Command, args []string) error { - return tagManifest(opts) + RunE: func(cmd *cobra.Command, args []string) error { + return tagManifest(cmd.Context(), opts) }, } @@ -71,8 +71,8 @@ Example - Tag the manifest 'v1.0.1' to 'v1.0.2' in an OCI layout folder 'layout- return cmd } -func tagManifest(opts tagOptions) error { - ctx, _ := trace.NewLogger(opts.Debug, opts.Verbose) +func tagManifest(ctx context.Context, opts tagOptions) error { + ctx, _ = opts.WithContext(ctx) target, err := opts.NewTarget(opts.Common) if err != nil { return err diff --git a/go.mod b/go.mod index c9ef7409b..51e194b44 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module oras.land/oras go 1.20 require ( - github.com/docker/cli v23.0.1+incompatible + github.com/docker/cli v23.0.2+incompatible github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/need-being/go-tree v0.1.0 github.com/opencontainers/go-digest v1.0.0 diff --git a/go.sum b/go.sum index badcd7706..d69fe109e 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7h github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= -github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v23.0.2+incompatible h1:Yj4wkrNtyCNLCMobKDYzEUIsbtMbfAulkHMH75/ecik= +github.com/docker/cli v23.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= diff --git a/internal/trace/context.go b/internal/trace/context.go index 249fa8a6c..d1299e6f1 100644 --- a/internal/trace/context.go +++ b/internal/trace/context.go @@ -27,7 +27,7 @@ type contextKey int const loggerKey contextKey = iota // NewLogger returns a logger. -func NewLogger(debug bool, verbose bool) (context.Context, logrus.FieldLogger) { +func NewLogger(ctx context.Context, debug bool, verbose bool) (context.Context, logrus.FieldLogger) { var logLevel logrus.Level if debug { logLevel = logrus.DebugLevel @@ -40,7 +40,6 @@ func NewLogger(debug bool, verbose bool) (context.Context, logrus.FieldLogger) { logger := logrus.New() logger.SetFormatter(&logrus.TextFormatter{DisableQuote: true}) logger.SetLevel(logLevel) - ctx := context.Background() entry := logger.WithContext(ctx) return context.WithValue(ctx, loggerKey, entry), entry } diff --git a/test/e2e/internal/utils/exec.go b/test/e2e/internal/utils/exec.go index b827ba019..a998828da 100644 --- a/test/e2e/internal/utils/exec.go +++ b/test/e2e/internal/utils/exec.go @@ -189,6 +189,7 @@ func (opts *ExecOption) Exec() *gexec.Session { opts.binary = ORASPath } cmd = exec.Command(opts.binary, opts.args...) + cmd.Env = append(os.Environ(), fmt.Sprintf("GOCOVERDIR=%s", CovDumpPath)) cmd.Stdin = opts.stdin if opts.workDir != "" { // switch working directory diff --git a/test/e2e/internal/utils/init.go b/test/e2e/internal/utils/init.go index f0922c0d8..1928d83d7 100644 --- a/test/e2e/internal/utils/init.go +++ b/test/e2e/internal/utils/init.go @@ -36,6 +36,9 @@ var Host string // FallbackHost points to the registry service where fallback E2E specs will be run against. var FallbackHost string +// Path to generate the coverage report. +var CovDumpPath string + func init() { Host = os.Getenv(RegHostKey) if Host == "" { @@ -72,6 +75,25 @@ func init() { } BeforeSuite(func() { ORASPath = os.Getenv("ORAS_PATH") + if covDumpRoot := os.Getenv("COVERAGE_DUMP_ROOT"); covDumpRoot != "" { + if ORASPath != "" { + fmt.Printf("Pre-built oras ignored: %s\n", ORASPath) + ORASPath = "" + } + if filepath.IsAbs(covDumpRoot) { + CovDumpPath = covDumpRoot + } else if workspacePath := os.Getenv("GITHUB_WORKSPACE"); workspacePath != "" { + CovDumpPath = filepath.Join(workspacePath, "test/e2e", covDumpRoot) + } else { + // local debugging + CovDumpPath = filepath.Join(pwd, "..", "..", covDumpRoot) + } + + // confirm the existence of dump folder + err := os.MkdirAll(CovDumpPath, 0700) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + fmt.Printf("Coverage file dump path: %q\n", CovDumpPath) + } if filepath.IsAbs(ORASPath) { fmt.Printf("Testing based on pre-built binary locates in %q\n", ORASPath) } else if workspacePath := os.Getenv("GITHUB_WORKSPACE"); ORASPath != "" && workspacePath != "" { @@ -82,7 +104,11 @@ func init() { fmt.Printf("Testing based on pre-built binary locates in %q\n", ORASPath) } else { // fallback to native build to facilitate local debugging - ORASPath, err = gexec.Build("oras.land/oras/cmd/oras") + buildArgs := []string{} + if CovDumpPath != "" { + buildArgs = append(buildArgs, "-coverpkg", "oras.land/oras/cmd/oras/...,oras.land/oras/internal/...") + } + ORASPath, err = gexec.Build("oras.land/oras/cmd/oras", buildArgs...) gomega.Expect(err).NotTo(gomega.HaveOccurred()) DeferCleanup(gexec.CleanupBuildArtifacts) fmt.Printf("Testing based on temp binary locates in %q\n", ORASPath) diff --git a/test/e2e/scripts/e2e.sh b/test/e2e/scripts/e2e.sh index f434cfc92..b0a4f0747 100755 --- a/test/e2e/scripts/e2e.sh +++ b/test/e2e/scripts/e2e.sh @@ -43,22 +43,41 @@ fi oras_container_name="oras-e2e" upstream_container_name="oras-e2e-fallback" +e2e_root="${repo_root}/test/e2e" echo " === preparing oras distribution === " run_registry \ - ${repo_root}/test/e2e/testdata/distribution/mount \ + ${e2e_root}/testdata/distribution/mount \ ghcr.io/oras-project/registry:v1.0.0-rc.4 \ $oras_container_name \ $ORAS_REGISTRY_PORT echo " === preparing upstream distribution === " run_registry \ - ${repo_root}/test/e2e/testdata/distribution/mount_fallback \ + ${e2e_root}/testdata/distribution/mount_fallback \ registry:2.8.1 \ $upstream_container_name \ $ORAS_REGISTRY_FALLBACK_PORT +echo " === setup coverage instrumenting == " +if [[ $GITHUB_REF_NAME == v* && $GITHUB_REF_TYPE == tag ]]; then + echo "coverage instrumentation skipped" + unset COVERAGE_DUMP_ROOT +fi + +if ! [ -z ${COVERAGE_DUMP_ROOT} ]; then + rm ${e2e_root}/${COVERAGE_DUMP_ROOT} -rf +fi + echo " === run tests === " -if ! ginkgo -r -p --succinct suite; then +ginkgo -r -p --succinct suite || fail=true + +if ! [ -z ${COVERAGE_DUMP_ROOT} ]; then + echo " === generating code cov report === " + make -C ${repo_root} e2e-covdata || true +fi + +if [ "${fail}" = 'true' ]; then + echo " === retriving registry error logs === " echo '-------- oras distribution trace -------------' docker logs -t --tail 200 $oras_container_name echo '-------- upstream distribution trace -------------' diff --git a/test/e2e/suite/command/blob.go b/test/e2e/suite/command/blob.go index 15f13f408..12ca81dd2 100644 --- a/test/e2e/suite/command/blob.go +++ b/test/e2e/suite/command/blob.go @@ -41,14 +41,14 @@ var _ = Describe("ORAS beginners:", func() { repo := fmt.Sprintf(repoFmt, "push", "password-stdin") ORAS("blob", "push", RegistryRef(Host, repo, ""), "--password-stdin", "-"). ExpectFailure(). - MatchTrimmedContent("Error: `-` read file from input and `--password-stdin` read password from input cannot be both used").Exec() + MatchErrKeyWords("Error: `-` read file from input and `--password-stdin` read password from input cannot be both used").Exec() }) It("should fail to push a blob from stdin but no blob size provided", func() { repo := fmt.Sprintf(repoFmt, "push", "no-size") ORAS("blob", "push", RegistryRef(Host, repo, pushDigest), "-"). WithInput(strings.NewReader(pushContent)). ExpectFailure(). - MatchTrimmedContent("Error: `--size` must be provided if the blob is read from stdin").Exec() + MatchErrKeyWords("Error: `--size` must be provided if the blob is read from stdin").Exec() }) It("should fail to push a blob from stdin if invalid blob size provided", func() { diff --git a/test/e2e/suite/command/tag.go b/test/e2e/suite/command/tag.go index f35b56f64..0878a7d7f 100644 --- a/test/e2e/suite/command/tag.go +++ b/test/e2e/suite/command/tag.go @@ -61,3 +61,61 @@ var _ = Describe("Common registry users:", func() { }) }) }) + +var _ = Describe("OCI image layout users:", func() { + var tagAndValidate = func(root string, tagOrDigest string, digest string, tags ...string) { + out := ORAS(append([]string{"tag", LayoutRef(root, tagOrDigest), Flags.Layout}, tags...)...).MatchKeyWords(tags...).Exec().Out + hint := regexp.QuoteMeta(fmt.Sprintf("Tagging [oci-layout] %s", LayoutRef(root, digest))) + gomega.Expect(out).To(gbytes.Say(hint)) + gomega.Expect(out).NotTo(gbytes.Say(hint)) // should only say hint once + ORAS("repo", "tags", Flags.Layout, LayoutRef(root, "")).MatchKeyWords(tags...).Exec() + } + When("running `tag`", func() { + prepare := func() string { + root := GinkgoT().TempDir() + ORAS("cp", RegistryRef(Host, ImageRepo, multi_arch.Tag), Flags.ToLayout, LayoutRef(root, multi_arch.Tag)).Exec() + return root + } + It("should add a tag to an existent manifest when providing tag reference", func() { + tagAndValidate(prepare(), multi_arch.Tag, multi_arch.Digest, "tag-via-tag") + }) + It("should add a tag to an existent manifest when providing digest reference", func() { + tagAndValidate(prepare(), multi_arch.Digest, multi_arch.Digest, "tag-via-digest") + }) + It("should add multiple tags to an existent manifest when providing digest reference", func() { + tagAndValidate(prepare(), multi_arch.Digest, multi_arch.Digest, "tag1-via-digest", "tag2-via-digest", "tag3-via-digest") + }) + It("should add multiple tags to an existent manifest when providing tag reference", func() { + tagAndValidate(prepare(), multi_arch.Tag, multi_arch.Digest, "tag1-via-tag", "tag1-via-tag", "tag1-via-tag") + }) + }) +}) + +var _ = Describe("OCI image layout users:", func() { + var tagAndValidate = func(root string, tagOrDigest string, digest string, tags ...string) { + out := ORAS(append([]string{"tag", LayoutRef(root, tagOrDigest), Flags.Layout}, tags...)...).MatchKeyWords(tags...).Exec().Out + hint := regexp.QuoteMeta(fmt.Sprintf("Tagging [oci-layout] %s", LayoutRef(root, digest))) + gomega.Expect(out).To(gbytes.Say(hint)) + gomega.Expect(out).NotTo(gbytes.Say(hint)) // should only say hint once + ORAS("repo", "tags", Flags.Layout, LayoutRef(root, "")).MatchKeyWords(tags...).Exec() + } + When("running `tag`", func() { + prepare := func() string { + root := GinkgoT().TempDir() + ORAS("cp", RegistryRef(Host, ImageRepo, multi_arch.Tag), Flags.ToLayout, LayoutRef(root, multi_arch.Tag)).Exec() + return root + } + It("should add a tag to an existent manifest when providing tag reference", func() { + tagAndValidate(prepare(), multi_arch.Tag, multi_arch.Digest, "tag-via-tag") + }) + It("should add a tag to an existent manifest when providing digest reference", func() { + tagAndValidate(prepare(), multi_arch.Digest, multi_arch.Digest, "tag-via-digest") + }) + It("should add multiple tags to an existent manifest when providing digest reference", func() { + tagAndValidate(prepare(), multi_arch.Digest, multi_arch.Digest, "tag1-via-digest", "tag2-via-digest", "tag3-via-digest") + }) + It("should add multiple tags to an existent manifest when providing tag reference", func() { + tagAndValidate(prepare(), multi_arch.Tag, multi_arch.Digest, "tag1-via-tag", "tag1-via-tag", "tag1-via-tag") + }) + }) +})