-
Notifications
You must be signed in to change notification settings - Fork 187
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: add fetch
command to get manifest of an artifact
#449
Merged
Merged
Changes from all commits
Commits
Show all changes
65 commits
Select commit
Hold shift + click to select a range
04bfc87
Add get-manifest command with pretty option
qweeah c1da0e9
Add error
qweeah 78b1e66
Rename to fetch
qweeah 269bb01
Add media type support and refactor
qweeah e87f4af
Add platform and media type
qweeah b902a7f
Moving multi-arch options into a dedicated type.
qweeah b79f8e4
Enhance platform
qweeah 994f495
Updage long description
qweeah 8c263ba
Refactor platform
qweeah ad97af8
Add get-manifest command with pretty option
qweeah f5877e5
Add error
qweeah 46f032d
Rename to fetch
qweeah 4bac348
Add media type support and refactor
qweeah 3bed958
Add platform and media type
qweeah d0640c7
Moving multi-arch options into a dedicated type.
qweeah 25b5e51
Enhance platform
qweeah 9a5dbe7
Updage long description
qweeah 19f4451
Refactor platform
qweeah 2d11906
Combine platform parsing
qweeah 7908bf6
Merge branch 'get-manifest' of https://github.com/qweeah/oras into ge…
qweeah 6da78a2
Add tests for platform.parse
qweeah 5849406
Add os version example
qweeah 4c7b117
Add manifest and fetch subcommand
qweeah bc2428b
Merge remote-tracking branch 'origin_src' into get-manifest
qweeah cd9ed13
Restructured
qweeah 864bb08
Merge remote-tracking branch 'origin_src/main' into get-manifest
qweeah 275b801
Use reference fetcher
qweeah d905883
Use FetchReference and promoted content.FetchAll
qweeah 3369ac3
Add mocked unit test
qweeah 0af3746
Refactor and add unit tests
qweeah 1553423
More coverage
qweeah bc5ce3e
Resolve comment
qweeah b3ae291
Add fetch descriptor
qweeah 7c6d00b
Add fetching descriptor
qweeah 8fc62b5
Add test for fetch descriptor
qweeah 7b0989c
Use unmerged branch
qweeah f59912e
Add coverage
qweeah 7145418
Add mock package
qweeah 516526e
Code clean
qweeah d58db42
Code clean
qweeah 00ed116
Merge remote-tracking branch 'origin_src/main' into get-manifest
qweeah cd910f3
Use strings.Cut
qweeah bc503e9
Code clean
qweeah 89b2747
Merge remote-tracking branch 'origin_src/main' into get-manifest
qweeah c9157c8
Merge remote-tracking branch 'origin_src/main' into get-manifest
qweeah 96b88d6
Add caching
qweeah 983d7b8
Code clean
qweeah 3eb653f
Merge remote-tracking branch 'origin_src/main' into get-manifest
qweeah 037561a
Remod
qweeah 81bf60f
Remod to rc 2
qweeah 335ee91
Code clean
qweeah b219e00
Refactor: move cas to a dedicated package
qweeah 1722b16
Merge branch 'main' into get-manifest
qweeah 39a71da
Code clean
qweeah b0a5fb2
Add code coverage
qweeah 2168f22
Add descriptor example
qweeah b2223d0
Add platform example
qweeah 7957e58
Code clean
qweeah f05e7fa
Code clean
qweeah 2ac5fce
Code clean
qweeah 349160f
Change media type to array
qweeah 3a09ca7
Refactor fetching
qweeah 2574a80
Add default arch if not provided
qweeah e3f2c90
Code clean
qweeah 851356f
Fix unit test
qweeah File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
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 option | ||
|
||
import ( | ||
"fmt" | ||
"runtime" | ||
"strings" | ||
|
||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
// Platform option struct. | ||
type Platform struct { | ||
Platform string | ||
} | ||
|
||
// ApplyFlags applies flags to a command flag set. | ||
func (opts *Platform) ApplyFlags(fs *pflag.FlagSet) { | ||
fs.StringVarP(&opts.Platform, "platform", "", "", "fetch the manifest of a specific platform if target is multi-platform capable") | ||
} | ||
|
||
// parse parses the input platform flag to an oci platform type. | ||
func (opts *Platform) Parse() (*ocispec.Platform, error) { | ||
if opts.Platform == "" { | ||
return nil, nil | ||
} | ||
|
||
// OS[/Arch[/Variant]][:OSVersion] | ||
// If Arch is not provided, will use GOARCH instead | ||
var platformStr string | ||
var p ocispec.Platform | ||
platformStr, p.OSVersion, _ = strings.Cut(opts.Platform, ":") | ||
parts := strings.Split(platformStr, "/") | ||
switch len(parts) { | ||
case 3: | ||
p.Variant = parts[2] | ||
fallthrough | ||
case 2: | ||
p.Architecture = parts[1] | ||
case 1: | ||
p.Architecture = runtime.GOARCH | ||
default: | ||
return nil, fmt.Errorf("failed to parse platform %q: expected format os[/arch[/variant]]", opts.Platform) | ||
} | ||
p.OS = parts[0] | ||
if p.OS == "" { | ||
return nil, fmt.Errorf("invalid platform: OS cannot be empty") | ||
} | ||
if p.Architecture == "" { | ||
return nil, fmt.Errorf("invalid platform: Architecture cannot be empty") | ||
} | ||
return &p, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/* | ||
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 option | ||
|
||
import ( | ||
"reflect" | ||
"runtime" | ||
"testing" | ||
|
||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/spf13/pflag" | ||
) | ||
|
||
func TestPlatform_ApplyFlags(t *testing.T) { | ||
var test struct{ Platform } | ||
ApplyFlags(&test, pflag.NewFlagSet("oras-test", pflag.ExitOnError)) | ||
if test.Platform.Platform != "" { | ||
t.Fatalf("expecting platform to be empty but got: %v", test.Platform.Platform) | ||
} | ||
} | ||
|
||
func TestPlatform_Parse_err(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
opts *Platform | ||
}{ | ||
{name: "empty arch 1", opts: &Platform{"os/"}}, | ||
{name: "empty arch 2", opts: &Platform{"os//variant"}}, | ||
{name: "empty os", opts: &Platform{"/arch"}}, | ||
{name: "empty os with variant", opts: &Platform{"/arch/variant"}}, | ||
{name: "trailing slash", opts: &Platform{"os/arch/variant/llama"}}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
_, err := tt.opts.Parse() | ||
if err == nil { | ||
t.Errorf("Platform.Parse() error = %v, wantErr %v", err, true) | ||
return | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestPlatform_Parse(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
opts *Platform | ||
want *ocispec.Platform | ||
}{ | ||
{name: "empty", opts: &Platform{""}, want: nil}, | ||
{name: "default arch", opts: &Platform{"os"}, want: &ocispec.Platform{OS: "os", Architecture: runtime.GOARCH}}, | ||
{name: "os&arch", opts: &Platform{"os/aRcH"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH"}}, | ||
{name: "empty variant", opts: &Platform{"os/aRcH/"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: ""}}, | ||
{name: "os&arch&variant", opts: &Platform{"os/aRcH/vAriAnt"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: "vAriAnt"}}, | ||
{name: "os version", opts: &Platform{"os/aRcH/vAriAnt:osversion"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH", Variant: "vAriAnt", OSVersion: "osversion"}}, | ||
{name: "long os version", opts: &Platform{"os/aRcH"}, want: &ocispec.Platform{OS: "os", Architecture: "aRcH"}}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := tt.opts.Parse() | ||
if err != nil { | ||
t.Errorf("Platform.Parse() error = %v", err) | ||
return | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("Platform.Parse() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
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 manifest | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func Cmd() *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "manifest [fetch]", | ||
Short: "[Preview] Manifest operations", | ||
} | ||
|
||
cmd.AddCommand( | ||
fetchCmd(), | ||
) | ||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
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 manifest | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
"oras.land/oras/cmd/oras/internal/errors" | ||
"oras.land/oras/cmd/oras/internal/option" | ||
"oras.land/oras/internal/cas" | ||
) | ||
|
||
type fetchOptions struct { | ||
option.Common | ||
option.Remote | ||
option.Platform | ||
|
||
targetRef string | ||
pretty bool | ||
mediaTypes []string | ||
fetchDescriptor bool | ||
} | ||
|
||
func fetchCmd() *cobra.Command { | ||
var opts fetchOptions | ||
cmd := &cobra.Command{ | ||
Use: "fetch [flags] <name:tag|name@digest>", | ||
Short: "[Preview] Fetch manifest of the target artifact", | ||
Long: `[Preview] Fetch manifest of the target artifact | ||
** This command is in preview and under development. ** | ||
|
||
Example - Fetch raw manifest: | ||
oras manifest fetch localhost:5000/hello:latest | ||
|
||
Example - Fetch the descriptor of a manifest: | ||
oras manifest fetch --descriptor localhost:5000/hello:latest | ||
|
||
Example - Fetch manifest with specified media type: | ||
oras manifest fetch --media-type 'application/vnd.oci.image.manifest.v1+json' localhost:5000/hello:latest | ||
|
||
Example - Fetch manifest with certain platform: | ||
oras manifest fetch --platform 'linux/arm/v5' localhost:5000/hello:latest | ||
|
||
Example - Fetch manifest with prettified json result: | ||
oras manifest fetch --pretty localhost:5000/hello:latest | ||
`, | ||
Args: cobra.ExactArgs(1), | ||
PreRunE: func(cmd *cobra.Command, args []string) error { | ||
return opts.ReadPassword() | ||
}, | ||
Aliases: []string{"get"}, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
opts.targetRef = args[0] | ||
return fetchManifest(opts) | ||
}, | ||
} | ||
|
||
cmd.Flags().BoolVarP(&opts.pretty, "pretty", "", false, "output prettified manifest") | ||
cmd.Flags().BoolVarP(&opts.fetchDescriptor, "descriptor", "", false, "fetch a descriptor of the manifest") | ||
cmd.Flags().StringSliceVarP(&opts.mediaTypes, "media-type", "", nil, "accepted media types") | ||
option.ApplyFlags(&opts, cmd.Flags()) | ||
return cmd | ||
} | ||
|
||
func fetchManifest(opts fetchOptions) error { | ||
ctx, _ := opts.SetLoggerLevel() | ||
tagetPlatform, err := opts.Parse() | ||
if err != nil { | ||
return err | ||
} | ||
repo, err := opts.NewRepository(opts.targetRef, opts.Common) | ||
if err != nil { | ||
return err | ||
} | ||
if repo.Reference.Reference == "" { | ||
return errors.NewErrInvalidReference(repo.Reference) | ||
} | ||
repo.ManifestMediaTypes = opts.mediaTypes | ||
|
||
// Fetch and output | ||
var content []byte | ||
if opts.fetchDescriptor { | ||
content, err = cas.FetchDescriptor(ctx, repo, opts.targetRef, tagetPlatform) | ||
} else { | ||
content, err = cas.FetchManifest(ctx, repo, opts.targetRef, tagetPlatform) | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
if opts.pretty { | ||
buf := bytes.NewBuffer(nil) | ||
if err = json.Indent(buf, content, "", " "); err != nil { | ||
return fmt.Errorf("failed to prettify: %w", err) | ||
} | ||
content = buf.Bytes() | ||
} | ||
_, err = os.Stdout.Write(content) | ||
return err | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we call it
Command()
?