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 48 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: 22 additions & 1 deletion cmd/oras/internal/display/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ func NewPushHandler(format string, tty *os.File, out io.Writer, verbose bool) (s
default:
metadataHandler = template.NewPushHandler(out, format)
}

return statusHandler, metadataHandler
}

Expand All @@ -70,6 +69,28 @@ func NewAttachHandler(format string, tty *os.File, out io.Writer, verbose bool)
default:
metadataHandler = template.NewAttachHandler(out, format)
}
return statusHandler, metadataHandler
}

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

var metadataHandler metadata.PullHandler
switch format {
case "":
metadataHandler = text.NewPullHandler(out)
case "json":
metadataHandler = json.NewPullHandler(out, path)
default:
metadataHandler = template.NewPullHandler(out, path, format)
}
return statusHandler, metadataHandler
}
10 changes: 10 additions & 0 deletions cmd/oras/internal/display/metadata/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ type PushHandler interface {
type AttachHandler interface {
OnCompleted(opts *option.Target, root, subject ocispec.Descriptor) error
}

// PullHandler handles metadata output for pull events.
type PullHandler interface {
// OnLayerSkipped is called when a layer is skipped.
OnLayerSkipped(ocispec.Descriptor) error
// OnFilePulled is called after a file is pulled.
OnFilePulled(name string, outputDir string, desc ocispec.Descriptor, descPath string) error
// OnCompleted is called when the pull cmd execution is completed.
OnCompleted(opts *option.Target, desc ocispec.Descriptor) error
}
53 changes: 53 additions & 0 deletions cmd/oras/internal/display/metadata/json/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
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 (
"io"

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
pulled model.Pulled
out io.Writer
}

// OnLayerSkipped implements metadata.PullHandler.
func (ph *PullHandler) OnLayerSkipped(ocispec.Descriptor) error { return nil }
qweeah marked this conversation as resolved.
Show resolved Hide resolved

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

// OnFilePulled implements metadata.PullHandler.
func (ph *PullHandler) OnFilePulled(name string, outputDir string, desc ocispec.Descriptor, descPath string) error {
return ph.pulled.Add(name, outputDir, desc, descPath)
}

// OnCompleted implements metadata.PullHandler.
func (ph *PullHandler) OnCompleted(opts *option.Target, desc ocispec.Descriptor) error {
return printJSON(ph.out, model.NewPull(ph.path+"@"+desc.Digest.String(), ph.pulled.Files()))
}
93 changes: 93 additions & 0 deletions cmd/oras/internal/display/metadata/model/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
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"
"slices"
"sync"

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

// File records metadata of a pulled file.
type File struct {
// Path is the absolute path of the pulled file.
Path string
Descriptor
}

// newFile creates a new file metadata.
func newFile(name string, outputDir string, desc ocispec.Descriptor, descPath string) (File, error) {
path := name
if !filepath.IsAbs(name) {
var err error
path, err = filepath.Abs(filepath.Join(outputDir, name))
// not likely to go wrong since the file has already be written to file store
if err != nil {
return File{}, fmt.Errorf("failed to get absolute path of pulled file %s: %w", name, err)
}
}
if desc.Annotations[file.AnnotationUnpack] == "true" {
path += string(filepath.Separator)
qweeah marked this conversation as resolved.
Show resolved Hide resolved
}
return File{
Path: path,
Descriptor: FromDescriptor(descPath, desc),
}, nil
}

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

// NewPull creates a new metadata struct for pull command.
func NewPull(digestReference string, files []File) any {
return pull{
DigestReference: DigestReference{
Ref: digestReference,
},
Files: files,
}
}

// Pulled records all pulled files.
type Pulled struct {
lock sync.Mutex
files []File
}

// Files returns all pulled files.
func (p *Pulled) Files() []File {
p.lock.Lock()
defer p.lock.Unlock()
return slices.Clone(p.files)
}

// Add adds a pulled file.
func (p *Pulled) Add(name string, outputDir string, desc ocispec.Descriptor, descPath string) error {
p.lock.Lock()
defer p.lock.Unlock()
file, err := newFile(name, outputDir, desc, descPath)
if err != nil {
return err
}
p.files = append(p.files, file)
return nil
}
55 changes: 55 additions & 0 deletions cmd/oras/internal/display/metadata/template/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
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 (
"io"

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
out io.Writer
pulled model.Pulled
}

// OnCompleted implements metadata.PullHandler.
func (ph *PullHandler) OnCompleted(opts *option.Target, desc ocispec.Descriptor) error {
return parseAndWrite(ph.out, model.NewPull(ph.path+"@"+desc.Digest.String(), ph.pulled.Files()), ph.template)
}

// OnFilePulled implements metadata.PullHandler.
func (ph *PullHandler) OnFilePulled(name string, outputDir string, desc ocispec.Descriptor, descPath string) error {
return ph.pulled.Add(name, outputDir, desc, descPath)
}

// OnLayerSkipped implements metadata.PullHandler.
func (ph *PullHandler) OnLayerSkipped(ocispec.Descriptor) error { return nil }
qweeah marked this conversation as resolved.
Show resolved Hide resolved

// NewPullHandler returns a new handler for pull events.
func NewPullHandler(out io.Writer, path string, template string) metadata.PullHandler {
return &PullHandler{
path: path,
template: template,
out: out,
}
}
61 changes: 61 additions & 0 deletions cmd/oras/internal/display/metadata/text/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
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"
"io"
"sync/atomic"

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

// PullHandler handles text metadata output for pull events.
type PullHandler struct {
out io.Writer
layerSkipped atomic.Bool
}

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

func (p *PullHandler) OnFilePulled(name string, outputDir string, desc ocispec.Descriptor, descPath string) error {
return nil
}

// OnLayerSkipped implements metadata.PullHandler.
func (ph *PullHandler) OnLayerSkipped(ocispec.Descriptor) error {
ph.layerSkipped.Store(true)
return nil
}

// NewPullHandler returns a new handler for Pull events.
func NewPullHandler(out io.Writer) metadata.PullHandler {
return &PullHandler{
out: out,
}
}
33 changes: 32 additions & 1 deletion cmd/oras/internal/display/status/discard.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.
package status

import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content"
)
Expand All @@ -38,10 +39,40 @@ func (DiscardHandler) OnEmptyArtifact() error {
return nil
}

// TrackTarget returns a target with status tracking
// TrackTarget returns a target with status tracking.
func (DiscardHandler) TrackTarget(gt oras.GraphTarget) (oras.GraphTarget, error) {
return gt, nil
}

// Close implements io.Closer.
func (DiscardHandler) Close() error {
return nil
}

// 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(_ ocispec.Descriptor) error {
return nil
}

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

// OnNodeProcessing implements PullHandler.
func (DiscardHandler) OnNodeSkipped(desc ocispec.Descriptor) error {
return nil
}
21 changes: 21 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 (
"io"

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,21 @@ type PushHandler interface {

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

// PullHandler handles status output for pull command.
type PullHandler interface {
io.Closer
// TrackTarget returns a tracked target.
// If no TTY is available, it returns the original target.
TrackTarget(gt oras.GraphTarget) (oras.GraphTarget, error)
// 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(desc ocispec.Descriptor) error
// OnNodeSkipped is called when a node is skipped.
OnNodeSkipped(desc ocispec.Descriptor) error
}
Loading
Loading