Skip to content

Commit

Permalink
add options to save layers and root manifest; remove unused opts (#27)
Browse files Browse the repository at this point in the history
Signed-off-by: Avi Deitcher <[email protected]>
  • Loading branch information
deitch authored Sep 15, 2021
1 parent 0b0339c commit 3d423c0
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 56 deletions.
30 changes: 22 additions & 8 deletions examples/advanced/advanced.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ func main() {

func copyCmd() *cobra.Command {
var (
fromStr, toStr string
manifestConfig string
manifestAnnotations map[string]string
configAnnotations map[string]string
opts content.RegistryOptions
fromStr, toStr string
manifestConfig string
manifestAnnotations map[string]string
configAnnotations map[string]string
showRootManifest, showLayers bool
opts content.RegistryOptions
)
cmd := &cobra.Command{
Use: "copy <name:tag|name@digest>",
Expand Down Expand Up @@ -160,7 +161,18 @@ application/vnd.unknown.config.v1+json. You can override it by setting the path,
if manifestConfig != "" && fromParts[0] != "files" {
return fmt.Errorf("only specify --manifest-config when using --from files")
}
return runCopy(ref, from, to)
var copyOpts []oras.CopyOpt
if showRootManifest {
copyOpts = append(copyOpts, oras.WithRootManifest(func(b []byte) {
fmt.Printf("root: %s\n", b)
}))
}
if showLayers {
copyOpts = append(copyOpts, oras.WithLayerDescriptors(func(layers []ocispec.Descriptor) {
fmt.Printf("%#v\n", layers)
}))
}
return runCopy(ref, from, to, copyOpts...)
},
}
cmd.Flags().StringVar(&fromStr, "from", "", "source type and possible options")
Expand All @@ -175,11 +187,13 @@ application/vnd.unknown.config.v1+json. You can override it by setting the path,
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")
cmd.Flags().StringToStringVar(&manifestAnnotations, "manifest-annotations", nil, "key-value pairs of annotations to set on the manifest, e.g. 'annotation=foo,other=bar'")
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'")
cmd.Flags().BoolVarP(&showRootManifest, "show-manifest", "", false, "when copying, show the root manifest")
cmd.Flags().BoolVarP(&showLayers, "show-layers", "", false, "when copying, show the descriptors for the layers")
return cmd
}

func runCopy(ref string, from, to target.Target) error {
desc, err := oras.Copy(context.Background(), from, ref, to, "")
func runCopy(ref string, from, to target.Target, copyOpts ...oras.CopyOpt) error {
desc, err := oras.Copy(context.Background(), from, ref, to, "", copyOpts...)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v", err)
os.Exit(1)
Expand Down
31 changes: 26 additions & 5 deletions pkg/oras/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ limitations under the License.
package oras

import (
"bytes"
"context"
"fmt"
"sync"
Expand Down Expand Up @@ -105,7 +106,7 @@ func transferContent(ctx context.Context, desc ocispec.Descriptor, fetcher remot
}

// fetchHandler pushes to the *store*, which may or may not cache it
baseFetchHandler := func(p remotes.Pusher) images.HandlerFunc {
baseFetchHandler := func(p remotes.Pusher, f remotes.Fetcher) images.HandlerFunc {
return images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
cw, err := p.Push(ctx, desc)
if err != nil {
Expand All @@ -117,7 +118,7 @@ func transferContent(ctx context.Context, desc ocispec.Descriptor, fetcher remot
}
defer cw.Close()

rc, err := fetcher.Fetch(ctx, desc)
rc, err := f.Fetch(ctx, desc)
if err != nil {
return nil, err
}
Expand All @@ -133,7 +134,7 @@ func transferContent(ctx context.Context, desc ocispec.Descriptor, fetcher remot
defer lock.Unlock()
manifests = append(manifests, desc)
}
return baseFetchHandler(store)(ctx, desc)
return baseFetchHandler(store, fetcher)(ctx, desc)
})

handlers := []images.Handler{
Expand All @@ -151,14 +152,34 @@ func transferContent(ctx context.Context, desc ocispec.Descriptor, fetcher remot
return err
}

// finally, we cached all of the manifests, so push those out
// we cached all of the manifests, so push those out
// Iterate in reverse order as seen, parent always uploaded after child
for i := len(manifests) - 1; i >= 0; i-- {
_, err := baseFetchHandler(pusher)(ctx, manifests[i])
_, err := baseFetchHandler(pusher, store)(ctx, manifests[i])
if err != nil {
return err
}
}

// if the option to request the root manifest was passed, accommodate it
if opts.saveManifest != nil && len(manifests) > 0 {
rc, err := store.Fetch(ctx, manifests[0])
if err != nil {
return fmt.Errorf("could not get root manifest to save based on CopyOpt: %v", err)
}
defer rc.Close()
buf := new(bytes.Buffer)
if _, err := buf.ReadFrom(rc); err != nil {
return fmt.Errorf("unable to read data for root manifest to save based on CopyOpt: %v", err)
}
// get the root manifest from the store
opts.saveManifest(buf.Bytes())
}

// if the option to request the layers was passed, accommodate it
if opts.saveLayers != nil && len(descriptors) > 0 {
opts.saveLayers(descriptors)
}
return nil
}

Expand Down
67 changes: 24 additions & 43 deletions pkg/oras/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,9 @@ type copyOpts struct {
filterName func(ocispec.Descriptor) bool
cachedMediaTypes []string

config *ocispec.Descriptor
configMediaType string
configAnnotations map[string]string
manifest *ocispec.Descriptor
manifestAnnotations map[string]string
validateName func(desc ocispec.Descriptor) error
saveManifest func([]byte)
saveLayers func([]ocispec.Descriptor)
validateName func(desc ocispec.Descriptor) error

userAgent string
}
Expand Down Expand Up @@ -215,59 +212,43 @@ func pullStatusTrack(writer io.Writer) images.Handler {
})
}

// WithConfig overrides the config - setting this will ignore WithConfigMediaType and WithConfigAnnotations
func WithConfig(config ocispec.Descriptor) CopyOpt {
return func(o *copyOpts) error {
o.config = &config
return nil
}
}

// WithConfigMediaType overrides the config media type
func WithConfigMediaType(mediaType string) CopyOpt {
return func(o *copyOpts) error {
o.configMediaType = mediaType
return nil
}
}

// WithConfigAnnotations overrides the config annotations
func WithConfigAnnotations(annotations map[string]string) CopyOpt {
return func(o *copyOpts) error {
o.configAnnotations = annotations
return nil
}
}

// WithManifest overrides the manifest - setting this will ignore WithManifestConfigAnnotations
func WithManifest(manifest ocispec.Descriptor) CopyOpt {
// WithNameValidation validates the image title in the descriptor.
// Pass nil to disable name validation.
func WithNameValidation(validate func(desc ocispec.Descriptor) error) CopyOpt {
return func(o *copyOpts) error {
o.manifest = &manifest
o.validateName = validate
return nil
}
}

// WithManifestAnnotations overrides the manifest annotations
func WithManifestAnnotations(annotations map[string]string) CopyOpt {
// WithUserAgent set the user agent string in http communications
func WithUserAgent(agent string) CopyOpt {
return func(o *copyOpts) error {
o.manifestAnnotations = annotations
o.userAgent = agent
return nil
}
}

// WithNameValidation validates the image title in the descriptor.
// Pass nil to disable name validation.
func WithNameValidation(validate func(desc ocispec.Descriptor) error) CopyOpt {
// WithLayerDescriptors passes the slice of Descriptors for layers to the
// provided func. If the passed parameter is nil, returns an error.
func WithLayerDescriptors(save func([]ocispec.Descriptor)) CopyOpt {
return func(o *copyOpts) error {
o.validateName = validate
if save == nil {
return errors.New("layers save func must be non-nil")
}
o.saveLayers = save
return nil
}
}

// WithUserAgent set the user agent string in http communications
func WithUserAgent(agent string) CopyOpt {
// WithRootManifest passes the root manifest for the artifacts to the provided
// func. If the passed parameter is nil, returns an error.
func WithRootManifest(save func(b []byte)) CopyOpt {
return func(o *copyOpts) error {
o.userAgent = agent
if save == nil {
return errors.New("manifest save func must be non-nil")
}
o.saveManifest = save
return nil
}
}

0 comments on commit 3d423c0

Please sign in to comment.