Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support --format for oras pull #1293

Merged
merged 58 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
c89e678
refactor: use handler in oras pull command
qweeah Mar 13, 2024
b7b76de
refactor: remove option update for pull
qweeah Mar 14, 2024
e08a5a5
add meta output
qweeah Mar 15, 2024
1f6c55e
add e2e test
qweeah Mar 18, 2024
29bf5f1
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Mar 18, 2024
ba7120e
add test for tracked target
qweeah Mar 18, 2024
8e28e99
fix race
qweeah Mar 18, 2024
49f9ad9
add json test
qweeah Mar 18, 2024
7f130a4
add coverage
qweeah Mar 18, 2024
d8b87d8
add comment
qweeah Mar 18, 2024
2b8b8bb
resolve comment
qweeah Mar 25, 2024
63d664b
change to io.Closer
qweeah Mar 25, 2024
a83297b
resolve comments
qweeah Mar 25, 2024
4c2c148
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Mar 25, 2024
cd5c4a4
resolve comments
qweeah Mar 25, 2024
8f8f9a4
bug fix
qweeah Mar 25, 2024
07d4a8e
add printer handle output
qweeah Mar 25, 2024
a879b17
refactor printer
qweeah Mar 25, 2024
eeabec7
doc clean
qweeah Mar 25, 2024
61e0241
move outputable to view
qweeah Mar 26, 2024
b82375f
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Mar 28, 2024
6a3d9df
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Mar 29, 2024
5f398fe
use new printer
qweeah Mar 29, 2024
a8289aa
align status output
qweeah Mar 29, 2024
c10a161
doc clean
qweeah Mar 29, 2024
a3f5f02
revert changes
qweeah Mar 29, 2024
d839760
code clean
qweeah Mar 29, 2024
cd93b0e
remove unused fields
qweeah Mar 29, 2024
67d17a7
code clean
qweeah Apr 1, 2024
d62f9b0
code clean
qweeah Apr 1, 2024
6cfd9bb
refactor
qweeah Apr 1, 2024
6b22b08
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Apr 1, 2024
625c61b
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Apr 2, 2024
2b52250
add used descriptor
qweeah Apr 2, 2024
47eea86
resolve comment
qweeah Apr 2, 2024
17df57b
resolve comment
qweeah Apr 2, 2024
bdbf73d
resolve comment
qweeah Apr 3, 2024
7a21bdc
bug fix
qweeah Apr 3, 2024
da5fa03
return error for OnFilePulled
qweeah Apr 3, 2024
a20c600
resolve comment
qweeah Apr 7, 2024
f20d61b
resolve commment
qweeah Apr 7, 2024
a4791db
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Apr 7, 2024
54cfd51
e2e: cover invalid abs path
qweeah Apr 7, 2024
ea80183
add e2e coverage
qweeah Apr 7, 2024
7b59561
increase coverage
qweeah Apr 7, 2024
6bf012f
add e2e test
qweeah Apr 7, 2024
8e32d5e
add e2e
qweeah Apr 7, 2024
98c7faf
revert attach change
qweeah Apr 7, 2024
a1b0026
exclude abs path
qweeah Apr 7, 2024
7079ffa
refactor stop tracked target
qweeah Apr 7, 2024
a38b975
code clean
qweeah Apr 7, 2024
7def480
code clean
qweeah Apr 7, 2024
3a9aa4f
add stop track function
qweeah Apr 7, 2024
9c801bb
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Apr 7, 2024
71fee7a
Merge remote-tracking branch 'upstream/main' into format-pull
qweeah Apr 7, 2024
bfe910f
fix lint
qweeah Apr 7, 2024
1b32187
fix lint
qweeah Apr 7, 2024
1e11eb4
fix lint
qweeah Apr 7, 2024
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
23 changes: 23 additions & 0 deletions cmd/oras/internal/display/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,26 @@

return statusHandler, metadataHandler
}

// NewPullHandler returns status and metadata handlers for pull command.
func NewPullHandler(format string, path string, tty *os.File, verbose bool) (status.PullHandler, metadata.PullHandler) {
var statusHandler status.PullHandler
if tty != nil {
statusHandler = status.NewTTYPullHandler(tty)

Check warning on line 80 in cmd/oras/internal/display/handler.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/internal/display/handler.go#L80

Added line #L80 was not covered by tests
} else if format == "" {
statusHandler = status.NewTextPullHandler(verbose)
} else {
statusHandler = status.NewDiscardHandler()
}

var metadataHandler metadata.PullHandler
switch format {
case "":
metadataHandler = text.NewPullHandler()
case "json":
metadataHandler = json.NewPullHandler(path)
default:
metadataHandler = template.NewPullHandler(path, format)
}
return statusHandler, metadataHandler
}
7 changes: 7 additions & 0 deletions cmd/oras/internal/display/metadata/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package metadata

import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras/cmd/oras/internal/display/metadata/model"
"oras.land/oras/cmd/oras/internal/option"
)

Expand All @@ -30,3 +31,9 @@ type PushHandler interface {
type AttachHandler interface {
OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error
}

// PullHandler handles metadata output for attach events.
type PullHandler interface {
// OnCompleted is called when the pull cmd execution is completed.
OnCompleted(opts *option.Target, desc ocispec.Descriptor, layerSkipped bool, _ []model.File) error
qweeah marked this conversation as resolved.
Show resolved Hide resolved
}
qweeah marked this conversation as resolved.
Show resolved Hide resolved
40 changes: 40 additions & 0 deletions cmd/oras/internal/display/metadata/json/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright The ORAS Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package json

import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras/cmd/oras/internal/display/metadata"
"oras.land/oras/cmd/oras/internal/display/metadata/model"
"oras.land/oras/cmd/oras/internal/option"
)

// PullHandler handles JSON metadata output for pull events.
type PullHandler struct {
path string
}

// NewPullHandler returns a new handler for Pull events.
func NewPullHandler(path string) metadata.PullHandler {
return &PullHandler{
path: path,
}
}

// OnCompleted implements metadata.PullHandler.
func (ph *PullHandler) OnCompleted(opts *option.Target, desc ocispec.Descriptor, _ bool, files []model.File) error {
return printJSON(model.NewPull(ph.path+"@"+desc.Digest.String(), files))
}
62 changes: 62 additions & 0 deletions cmd/oras/internal/display/metadata/model/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
Copyright The ORAS Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package model

import (
"fmt"
"path/filepath"

ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras-go/v2/content/file"
)

// File records pulled files.
type File struct {
// Absolute path of the pulled file.
qweeah marked this conversation as resolved.
Show resolved Hide resolved
Path string
Descriptor
}

// NewFile creates a new file metadata.
func NewFile(name string, outputDir string, desc ocispec.Descriptor, descPath string) File {
path := name
if !filepath.IsAbs(name) {
// ignore error since it's successfully written to file store
path, _ = filepath.Abs(filepath.Join(outputDir, name))
}
if desc.Annotations[file.AnnotationUnpack] == "true" {
path = fmt.Sprintf("%s%c", path, filepath.Separator)
qweeah marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 42 in cmd/oras/internal/display/metadata/model/pull.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/internal/display/metadata/model/pull.go#L41-L42

Added lines #L41 - L42 were not covered by tests
return File{
Path: path,
Descriptor: FromDescriptor(descPath, desc),
}
}

type pull struct {
DigestReference
Files []File `json:"Files"`
}

// NewPull creates a new metadata struct for pull command.
func NewPull(digestReference string, files []File) pull {
qweeah marked this conversation as resolved.
Show resolved Hide resolved
return pull{
DigestReference: DigestReference{
Ref: digestReference,
},
Files: files,
}
}
42 changes: 42 additions & 0 deletions cmd/oras/internal/display/metadata/template/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright The ORAS Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package template

import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras/cmd/oras/internal/display/metadata"
"oras.land/oras/cmd/oras/internal/display/metadata/model"
"oras.land/oras/cmd/oras/internal/option"
)

// PullHandler handles text metadata output for pull events.
type PullHandler struct {
template string
path string
}

// OnCompleted implements metadata.PullHandler.
func (ph *PullHandler) OnCompleted(opts *option.Target, desc ocispec.Descriptor, _ bool, files []model.File) error {
return parseAndWrite(model.NewPull(ph.path+"@"+desc.Digest.String(), files), ph.template)
}

// NewPullHandler returns a new handler for Pull events.
func NewPullHandler(path string, template string) metadata.PullHandler {
return &PullHandler{
path: path,
template: template,
}
}
45 changes: 45 additions & 0 deletions cmd/oras/internal/display/metadata/text/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright The ORAS Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package text

import (
"fmt"

ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras/cmd/oras/internal/display/metadata"
"oras.land/oras/cmd/oras/internal/display/metadata/model"
"oras.land/oras/cmd/oras/internal/option"
)

// PullHandler handles text metadata output for pull events.
type PullHandler struct{}

// OnCompleted implements metadata.PullHandler.
func (p *PullHandler) OnCompleted(opts *option.Target, desc ocispec.Descriptor, layerSkipped bool, _ []model.File) error {
if layerSkipped {
_, _ = fmt.Printf("Skipped pulling layers without file name in %q\n", ocispec.AnnotationTitle)
_, _ = fmt.Printf("Use 'oras copy %s --to-oci-layout <layout-dir>' to pull all layers.\n", opts.RawReference)
} else {
_, _ = fmt.Println("Pulled", opts.AnnotatedReference())
_, _ = fmt.Println("Digest:", desc.Digest)
}
return nil
}

// NewPullHandler returns a new handler for Pull events.
func NewPullHandler() metadata.PullHandler {
return &PullHandler{}
}
31 changes: 31 additions & 0 deletions cmd/oras/internal/display/status/discard.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
package status

import (
"sync"

ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content"
)
Expand Down Expand Up @@ -43,5 +46,33 @@
return gt, nil
}

// StopTracking stops tracking the status of the target.
func (DiscardHandler) StopTracking() {}

// UpdateCopyOptions updates the copy options for the artifact push.
func (DiscardHandler) UpdateCopyOptions(opts *oras.CopyGraphOptions, fetcher content.Fetcher) {}

// OnNodeDownloading implements PullHandler.
func (DiscardHandler) OnNodeDownloading(desc ocispec.Descriptor) error {
return nil
}

// OnNodeDownloaded implements PullHandler.
func (DiscardHandler) OnNodeDownloaded(desc ocispec.Descriptor) error {
return nil
}

// OnNodeRestored implements PullHandler.
func (DiscardHandler) OnNodeRestored(_ *sync.Map, _ ocispec.Descriptor) error {
return nil
}

// OnNodeProcessing implements PullHandler.
func (DiscardHandler) OnNodeProcessing(desc ocispec.Descriptor) error {
return nil
}

// OnNodeProcessing implements PullHandler.
func (DiscardHandler) OnNodeSkipped(printed *sync.Map, desc ocispec.Descriptor) error {
return nil

Check warning on line 77 in cmd/oras/internal/display/status/discard.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/internal/display/status/discard.go#L76-L77

Added lines #L76 - L77 were not covered by tests
}
22 changes: 22 additions & 0 deletions cmd/oras/internal/display/status/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ limitations under the License.
package status

import (
"sync"

ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content"
)
Expand All @@ -30,3 +33,22 @@ type PushHandler interface {

// AttachHandler handles text status output for attach command.
type AttachHandler PushHandler

// PullHandler handles status output for pull command.
type PullHandler interface {
// TrackTarget returns a tracked target.
// If no TTY is available, it returns the original target.
TrackTarget(gt oras.GraphTarget) (oras.GraphTarget, error)
// StopTracking stops tracking the target if available.
StopTracking()
qweeah marked this conversation as resolved.
Show resolved Hide resolved
// OnNodeProcessing is called when processing a manifest.
OnNodeProcessing(desc ocispec.Descriptor) error
// OnNodeDownloading is called before downloading a node.
OnNodeDownloading(desc ocispec.Descriptor) error
// OnNodeDownloaded is called after a node is downloaded.
OnNodeDownloaded(desc ocispec.Descriptor) error
// OnNodeRestored is called after a deduplicated node is restored.
OnNodeRestored(printed *sync.Map, desc ocispec.Descriptor) error
// OnNodeSkipped is called when a node is skipped.
OnNodeSkipped(printed *sync.Map, desc ocispec.Descriptor) error
qweeah marked this conversation as resolved.
Show resolved Hide resolved
}
55 changes: 55 additions & 0 deletions cmd/oras/internal/display/status/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content"
utils "oras.land/oras/cmd/oras/internal/display/utils"
)

// TextPushHandler handles text status output for push events.
Expand Down Expand Up @@ -86,3 +87,57 @@ func (ph *TextPushHandler) UpdateCopyOptions(opts *oras.CopyGraphOptions, fetche
func NewTextAttachHandler(verbose bool) AttachHandler {
return NewTextPushHandler(verbose)
}

// TextPullHandler handles text status output for pull events.
type TextPullHandler struct {
fetcher content.Fetcher
verbose bool
}

func (ph *TextPullHandler) printOnce(printed *sync.Map, s ocispec.Descriptor, msg string) error {
if _, loaded := printed.LoadOrStore(utils.GenerateContentKey(s), true); loaded {
return nil
}
return PrintStatus(s, msg, ph.verbose)
}

// TrackTarget returns a tracked target.
func (ph *TextPullHandler) TrackTarget(gt oras.GraphTarget) (oras.GraphTarget, error) {
ph.fetcher = gt
return gt, nil
}

// StopTracking stop tracked target.
func (ph *TextPullHandler) StopTracking() {}

// OnNodeDownloading implements PullHandler.
func (ph *TextPullHandler) OnNodeDownloading(desc ocispec.Descriptor) error {
return PrintStatus(desc, utils.PullPromptDownloading, ph.verbose)
}

// OnNodeDownloaded implements PullHandler.
func (ph *TextPullHandler) OnNodeDownloaded(desc ocispec.Descriptor) error {
return PrintStatus(desc, utils.PullPromptDownloaded, ph.verbose)
}

// OnNodeRestored implements PullHandler.
func (ph *TextPullHandler) OnNodeRestored(printed *sync.Map, desc ocispec.Descriptor) error {
return ph.printOnce(printed, desc, utils.PullPromptRestored)
}

// OnNodeProcessing implements PullHandler.
func (ph *TextPullHandler) OnNodeProcessing(desc ocispec.Descriptor) error {
return PrintStatus(desc, utils.PullPromptProcessing, ph.verbose)
}

// OnNodeProcessing implements PullHandler.
func (ph *TextPullHandler) OnNodeSkipped(printed *sync.Map, desc ocispec.Descriptor) error {
return ph.printOnce(printed, desc, utils.PullPromptSkipped)
}

// NewTextPullHandler returns a new handler for pull command.
func NewTextPullHandler(verbose bool) PullHandler {
return &TextPullHandler{
verbose: verbose,
}
}
Loading
Loading