Skip to content

Commit

Permalink
cmd/cp: Add oci-layout-path flag
Browse files Browse the repository at this point in the history
The oci-layout can't be used when the image
reference contains a slash ( see issue 1505). This
PR introduces a new oci-layout-path that
explicitly receives the path of the oci layout
fixing the parsing ambiguity.

The usage of this flag is:

oras cp --from-oci-layout-path <layout_path> <src> <dst>

Signed-off-by: Mauricio Vásquez <mauriciov@microsoft.com>
  • Loading branch information
mauriciovasquezbernal committed Oct 16, 2024
1 parent 1d60e22 commit 939d9bd
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 0 deletions.
9 changes: 9 additions & 0 deletions cmd/oras/internal/option/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func (opts *Target) AnnotatedReference() string {
func (opts *Target) applyFlagsWithPrefix(fs *pflag.FlagSet, prefix, description string) {
flagPrefix, notePrefix := applyPrefix(prefix, description)
fs.BoolVarP(&opts.IsOCILayout, flagPrefix+"oci-layout", "", false, "set "+notePrefix+"target as an OCI image layout")
fs.StringVar(&opts.Path, flagPrefix+"oci-layout-path", "", "set the path for the "+notePrefix+"OCI image layout target")
}

// ApplyFlagsWithPrefix applies flags to a command flag set with a prefix string.
Expand All @@ -96,13 +97,21 @@ func (opts *Target) ApplyFlagsWithPrefix(fs *pflag.FlagSet, prefix, description

// Parse gets target options from user input.
func (opts *Target) Parse(cmd *cobra.Command) error {
if opts.IsOCILayout && opts.Path != "" {
return fmt.Errorf("flag %q is not supported with %q", "oci-layout-path", "oci-layout")
}

switch {
case opts.IsOCILayout:
opts.Type = TargetTypeOCILayout
if len(opts.headerFlags) != 0 {
return errors.New("custom header flags cannot be used on an OCI image layout target")
}
return opts.parseOCILayoutReference()
case opts.Path != "":
opts.Type = TargetTypeOCILayout
opts.Reference = opts.RawReference
return nil
default:
opts.Type = TargetTypeRemote
if ref, err := registry.ParseReference(opts.RawReference); err != nil {
Expand Down
30 changes: 30 additions & 0 deletions cmd/oras/internal/option/target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"net/http"
"net/url"
"reflect"
"strings"
"testing"

"github.com/spf13/cobra"
Expand All @@ -28,6 +29,19 @@ import (
oerrors "oras.land/oras/cmd/oras/internal/errors"
)

func TestTarget_Parse_oci_path(t *testing.T) {
opts := Target{
Path: "foo",
RawReference: "mocked/test",
}
if err := opts.Parse(nil); err != nil {
t.Errorf("Target.Parse() error = %v", err)
}
if opts.Type != TargetTypeOCILayout {
t.Errorf("Target.Parse() failed, got %q, want %q", opts.Type, TargetTypeOCILayout)
}
}

func TestTarget_Parse_oci(t *testing.T) {
opts := Target{IsOCILayout: true}
err := opts.Parse(nil)
Expand All @@ -39,6 +53,22 @@ func TestTarget_Parse_oci(t *testing.T) {
}
}

func TestTarget_Parse_oci_and_oci_path(t *testing.T) {
opts := Target{
IsOCILayout: true,
Path: "foo",
}
cmd := &cobra.Command{}
ApplyFlags(&opts, cmd.Flags())
err := opts.Parse(cmd)
if err == nil {
t.Errorf("expect Target.Parse() to fail but not")
}
if !strings.Contains(err.Error(), "supported") {
t.Errorf("expect error message to contain 'supported' but not")
}
}

func TestTarget_Parse_remote(t *testing.T) {
opts := Target{
RawReference: "mocked/test",
Expand Down

0 comments on commit 939d9bd

Please sign in to comment.