-
Notifications
You must be signed in to change notification settings - Fork 177
/
fetch.go
156 lines (137 loc) · 5.36 KB
/
fetch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
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 (
"fmt"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras/cmd/oras/internal/argument"
"oras.land/oras/cmd/oras/internal/command"
"oras.land/oras/cmd/oras/internal/display"
oerrors "oras.land/oras/cmd/oras/internal/errors"
"oras.land/oras/cmd/oras/internal/option"
)
type fetchOptions struct {
option.Cache
option.Common
option.Descriptor
option.Platform
option.Pretty
option.Target
option.Format
mediaTypes []string
outputPath string
}
func fetchCmd() *cobra.Command {
var opts fetchOptions
cmd := &cobra.Command{
Use: "fetch [flags] <name>{:<tag>|@<digest>}",
Short: "Fetch manifest of the target artifact",
Long: `Fetch manifest of the target artifact
Example - Fetch raw manifest from a registry:
oras manifest fetch localhost:5000/hello:v1
Example - Fetch the descriptor of a manifest from a registry:
oras manifest fetch --descriptor localhost:5000/hello:v1
Example - Fetch manifest from a registry with specified media type:
oras manifest fetch --media-type 'application/vnd.oci.image.manifest.v1+json' localhost:5000/hello:v1
Example - Fetch manifest from a registry with certain platform:
oras manifest fetch --platform 'linux/arm/v5' localhost:5000/hello:v1
Example - Fetch manifest from a registry with prettified json result:
oras manifest fetch --pretty localhost:5000/hello:v1
Example - Fetch raw manifest from an OCI image layout folder 'layout-dir':
oras manifest fetch --oci-layout layout-dir:v1
Example - Fetch raw manifest from an OCI layout archive file 'layout.tar':
oras manifest fetch --oci-layout layout.tar:v1
`,
Args: oerrors.CheckArgs(argument.Exactly(1), "the manifest to fetch"),
PreRunE: func(cmd *cobra.Command, args []string) error {
switch {
case opts.outputPath == "-" && opts.FormatFlag != "":
return fmt.Errorf("`--output -` cannot be used with `--format %s` at the same time", opts.Template)
case opts.outputPath == "-" && opts.OutputDescriptor:
return fmt.Errorf("`--descriptor` cannot be used with `--output -` at the same time")
// ignore --pretty when output to a file
case opts.outputPath != "" && opts.outputPath != "-":
opts.Pretty.Pretty = false
}
if err := oerrors.CheckMutuallyExclusiveFlags(cmd.Flags(), "format", "pretty"); err != nil {
return err
}
if err := oerrors.CheckMutuallyExclusiveFlags(cmd.Flags(), "format", "descriptor"); err != nil {
return err
}
opts.RawReference = args[0]
return option.Parse(cmd, &opts)
},
Aliases: []string{"get"},
RunE: func(cmd *cobra.Command, args []string) error {
return fetchManifest(cmd, &opts)
},
}
cmd.Flags().StringSliceVarP(&opts.mediaTypes, "media-type", "", nil, "accepted media types")
cmd.Flags().StringVarP(&opts.outputPath, "output", "o", "", "file `path` to write the fetched manifest to, use - for stdout")
opts.AllowedTypes = []*option.FormatType{
option.FormatTypeJSON.WithUsage("Print in prettified JSON format"),
option.FormatTypeGoTemplate.WithUsage("Print using the given Go template"),
}
option.ApplyFlags(&opts, cmd.Flags())
return oerrors.Command(cmd, &opts.Target)
}
func fetchManifest(cmd *cobra.Command, opts *fetchOptions) (fetchErr error) {
ctx, logger := command.GetLogger(cmd, &opts.Common)
metadataHandler, contentHandler, err := display.NewManifestFetchHandler(cmd.OutOrStdout(), opts.Format, opts.OutputDescriptor, opts.Pretty.Pretty, opts.outputPath)
if err != nil {
return err
}
target, err := opts.NewReadonlyTarget(ctx, opts.Common, logger)
if err != nil {
return err
}
if err := opts.EnsureReferenceNotEmpty(cmd, true); err != nil {
return err
}
if repo, ok := target.(*remote.Repository); ok {
repo.ManifestMediaTypes = opts.mediaTypes
} else if opts.mediaTypes != nil {
return fmt.Errorf("`--media-type` cannot be used with `--oci-layout` at the same time")
}
src, err := opts.CachedTarget(target)
if err != nil {
return err
}
var desc ocispec.Descriptor
var content []byte
if opts.OutputDescriptor && opts.outputPath == "" {
// fetch manifest descriptor only
fetchOpts := oras.DefaultResolveOptions
fetchOpts.TargetPlatform = opts.Platform.Platform
desc, err = oras.Resolve(ctx, src, opts.Reference, fetchOpts)
if err != nil {
return fmt.Errorf("failed to find %q: %w", opts.RawReference, err)
}
} else {
// fetch manifest descriptor and content
fetchOpts := oras.DefaultFetchBytesOptions
fetchOpts.TargetPlatform = opts.Platform.Platform
desc, content, err = oras.FetchBytes(ctx, src, opts.Reference, fetchOpts)
if err != nil {
return fmt.Errorf("failed to fetch the content of %q: %w", opts.RawReference, err)
}
if err = contentHandler.OnContentFetched(desc, content); err != nil {
return err
}
}
return metadataHandler.OnFetched(opts.Path, desc, content)
}