github.com/nikkelma/oras-project_oras-go@v1.1.1-0.20220201001104-a75f6a419090/examples/advanced/advanced.go (about) 1 /* 2 Copyright The ORAS Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 package main 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 "path/filepath" 22 "strings" 23 24 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 25 log "github.com/sirupsen/logrus" 26 "github.com/spf13/cobra" 27 28 "oras.land/oras-go/pkg/artifact" 29 "oras.land/oras-go/pkg/content" 30 "oras.land/oras-go/pkg/oras" 31 "oras.land/oras-go/pkg/target" 32 ) 33 34 func main() { 35 var verbose int 36 cmd := &cobra.Command{ 37 Use: fmt.Sprintf("%s [command]", os.Args[0]), 38 SilenceUsage: true, 39 PersistentPreRun: func(cmd *cobra.Command, args []string) { 40 log.SetLevel(log.InfoLevel) 41 if verbose > 1 { 42 log.SetLevel(log.DebugLevel) 43 } 44 }, 45 } 46 cmd.AddCommand(copyCmd()) 47 cmd.PersistentFlags().IntVarP(&verbose, "verbose", "v", 1, "set log level") 48 if err := cmd.Execute(); err != nil { 49 os.Exit(1) 50 } 51 } 52 53 func copyCmd() *cobra.Command { 54 var ( 55 fromStr, toStr string 56 manifestConfig string 57 manifestAnnotations map[string]string 58 configAnnotations map[string]string 59 showRootManifest, showLayers bool 60 opts content.RegistryOptions 61 ) 62 cmd := &cobra.Command{ 63 Use: "copy <name:tag|name@digest>", 64 Short: "Copy artifacts from one location to another", 65 Long: `Copy artifacts from one location to another 66 Example - Copy artifacts from local files to local files: 67 oras copy foo/bar:v1 --from files --to files:path/to/save file1 file2 ... filen 68 Example - Copy artifacts from registry to local files: 69 oras copy foo/bar:v1 --from registry --to files:path/to/save 70 Example - Copy artifacts from registry to oci: 71 oras copy foo/bar:v1 --from registry --to oci:path/to/oci 72 Example - Copy artifacts from local files to registry: 73 oras copy foo/bar:v1 --from files --to registry file1 file2 ... filen 74 75 When the source (--from) is "files", the config by default will be "{}" and of media type 76 application/vnd.unknown.config.v1+json. You can override it by setting the path, for example: 77 78 oras copy foo/bar:v1 --from files --manifest-config path/to/config:application/vnd.oci.image.config.v1+json --to files:path/to/save file1 file2 ... filen 79 80 81 `, 82 Args: cobra.MinimumNArgs(1), 83 RunE: func(cmd *cobra.Command, args []string) error { 84 var ( 85 ref = args[0] 86 err error 87 from, to target.Target 88 configDesc ocispec.Descriptor 89 ) 90 // get the fromStr; it might also have a ':' to add options 91 fromParts := strings.SplitN(fromStr, ":", 2) 92 toParts := strings.SplitN(toStr, ":", 2) 93 switch fromParts[0] { 94 case "files": 95 fromFile := content.NewFile("") 96 descs, err := loadFiles(fromFile, args[1:]...) 97 if err != nil { 98 return fmt.Errorf("unable to load files: %w", err) 99 } 100 // parse the manifest config 101 if manifestConfig != "" { 102 manifestConfigPath, manifestConfigMediaType := parseFileRef(manifestConfig, artifact.UnknownConfigMediaType) 103 configDesc, err = fromFile.Add("", manifestConfigMediaType, manifestConfigPath) 104 if err != nil { 105 return fmt.Errorf("unable to load manifest config: %w", err) 106 } 107 } else { 108 var config []byte 109 config, configDesc, err = content.GenerateConfig(configAnnotations) 110 if err != nil { 111 return fmt.Errorf("unable to create new manifest config: %w", err) 112 } 113 if err := fromFile.Load(configDesc, config); err != nil { 114 return fmt.Errorf("unable to load new manifest config: %w", err) 115 } 116 } 117 manifest, manifestDesc, err := content.GenerateManifest(&configDesc, manifestAnnotations, descs...) 118 if err != nil { 119 return fmt.Errorf("unable to create manifest: %w", err) 120 } 121 if err := fromFile.StoreManifest(ref, manifestDesc, manifest); err != nil { 122 return fmt.Errorf("unable to generate root manifest: %w", err) 123 } 124 rootDesc, rootManifest, err := fromFile.Ref(ref) 125 if err != nil { 126 return err 127 } 128 log.Debugf("root manifest: %s %v %s", ref, rootDesc, rootManifest) 129 from = fromFile 130 case "registry": 131 from, err = content.NewRegistry(opts) 132 if err != nil { 133 return fmt.Errorf("could not create registry target: %w", err) 134 } 135 case "oci": 136 from, err = content.NewOCI(fromParts[1]) 137 if err != nil { 138 return fmt.Errorf("could not read OCI layout at %s: %w", fromParts[1], err) 139 } 140 default: 141 return fmt.Errorf("unknown from argyment: %s", from) 142 } 143 144 switch toParts[0] { 145 case "files": 146 to = content.NewFile(toParts[1]) 147 case "registry": 148 to, err = content.NewRegistry(opts) 149 if err != nil { 150 return fmt.Errorf("could not create registry target: %w", err) 151 } 152 case "oci": 153 to, err = content.NewOCI(toParts[1]) 154 if err != nil { 155 return fmt.Errorf("could not read OCI layout at %s: %v", toParts[1], err) 156 } 157 default: 158 return fmt.Errorf("unknown from argyment: %s", from) 159 } 160 161 if manifestConfig != "" && fromParts[0] != "files" { 162 return fmt.Errorf("only specify --manifest-config when using --from files") 163 } 164 var copyOpts []oras.CopyOpt 165 if showRootManifest { 166 copyOpts = append(copyOpts, oras.WithRootManifest(func(b []byte) { 167 fmt.Printf("root: %s\n", b) 168 })) 169 } 170 if showLayers { 171 copyOpts = append(copyOpts, oras.WithLayerDescriptors(func(layers []ocispec.Descriptor) { 172 fmt.Printf("%#v\n", layers) 173 })) 174 } 175 return runCopy(ref, from, to, copyOpts...) 176 }, 177 } 178 cmd.Flags().StringVar(&fromStr, "from", "", "source type and possible options") 179 cmd.MarkFlagRequired("from") 180 cmd.Flags().StringVar(&toStr, "to", "", "destination type and possible options") 181 cmd.MarkFlagRequired("to") 182 cmd.Flags().StringArrayVarP(&opts.Configs, "config", "c", nil, "auth config path") 183 cmd.Flags().StringVarP(&opts.Username, "username", "u", "", "registry username") 184 cmd.Flags().StringVarP(&opts.Password, "password", "p", "", "registry password") 185 cmd.Flags().BoolVarP(&opts.Insecure, "insecure", "", false, "allow connections to SSL registry without certs") 186 cmd.Flags().BoolVarP(&opts.PlainHTTP, "plain-http", "", false, "use plain http and not https") 187 cmd.Flags().StringVar(&manifestConfig, "manifest-config", "", "path to manifest config and its media type, e.g. path/to/file.json:application/vnd.oci.image.config.v1+json") 188 cmd.Flags().StringToStringVar(&manifestAnnotations, "manifest-annotations", nil, "key-value pairs of annotations to set on the manifest, e.g. 'annotation=foo,other=bar'") 189 cmd.Flags().StringToStringVar(&configAnnotations, "config-annotations", nil, "key-value pairs of annotations to set on the config, only if config is not passed explicitly, e.g. 'annotation=foo,other=bar'") 190 cmd.Flags().BoolVarP(&showRootManifest, "show-manifest", "", false, "when copying, show the root manifest") 191 cmd.Flags().BoolVarP(&showLayers, "show-layers", "", false, "when copying, show the descriptors for the layers") 192 return cmd 193 } 194 195 func runCopy(ref string, from, to target.Target, copyOpts ...oras.CopyOpt) error { 196 desc, err := oras.Copy(context.Background(), from, ref, to, "", copyOpts...) 197 if err != nil { 198 fmt.Fprintf(os.Stderr, "error: %v", err) 199 os.Exit(1) 200 } 201 fmt.Printf("%#v\n", desc) 202 return nil 203 } 204 205 func loadFiles(store *content.File, files ...string) ([]ocispec.Descriptor, error) { 206 var descs []ocispec.Descriptor 207 for _, fileRef := range files { 208 filename, mediaType := parseFileRef(fileRef, "") 209 name := filepath.Clean(filename) 210 if !filepath.IsAbs(name) { 211 // convert to slash-separated path unless it is absolute path 212 name = filepath.ToSlash(name) 213 } 214 desc, err := store.Add(name, mediaType, filename) 215 if err != nil { 216 return nil, err 217 } 218 descs = append(descs, desc) 219 } 220 return descs, nil 221 }