Skip to content
This repository has been archived by the owner on May 3, 2022. It is now read-only.

Commit

Permalink
Add initial push-pull implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Radu M <[email protected]>
  • Loading branch information
Radu M committed Aug 7, 2019
1 parent 367ce6b commit 61bb2d7
Show file tree
Hide file tree
Showing 15 changed files with 2,227 additions and 0 deletions.
13 changes: 13 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,8 @@
# version imported by k8s.io/client-go @ kubernetes-1.11.2
name = "github.com/Azure/go-autorest"
revision = "1ff28809256a84bb6966640ff3d0371af82ccba4"

[[constraint]]
name = "github.com/docker/cnab-to-oci"
source = "github.com/radu-matei/cnab-to-oci"
branch = "sync-bundle"
93 changes: 93 additions & 0 deletions cmd/duffle/pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package main

import (
"context"
"fmt"
"io"
"io/ioutil"
"path/filepath"

"github.com/deislabs/duffle/pkg/duffle/home"
"github.com/deislabs/duffle/pkg/reference"

"github.com/deislabs/cnab-go/bundle"
"github.com/docker/cnab-to-oci/remotes"
"github.com/spf13/cobra"
)

type pullCmd struct {
output string
targetRef string
insecureRegistries []string
home home.Home
}

func newPullCmd(w io.Writer) *cobra.Command {
const usage = `Pulls a bundle from an OCI repository`
const pullDesc = `
Pulls a CNAB bundle from an OCI repository.
The only argument for this command is the repository where
the bundle can be found, and by default, this command pulls the
bundle and stores it in the local bundle store.
If the --output flag is passed, the bundle file will be saved in
that file, and its reference will not be recorded in the local store.
Insecure registries can be passed through the --insecure-registries flags.
Examples:
$ duffle pull registry/username/bundle:tag
$ duffle pull --output path-for-bundle.json registry/username/bundle:tag
`
var pull pullCmd
cmd := &cobra.Command{
Use: "pull [TARGET REPOSITORY] [options]",
Short: usage,
Long: pullDesc,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
pull.targetRef = args[0]
pull.home = home.Home(homePath())
return pull.run()
},
}

cmd.Flags().StringVarP(&pull.output, "output", "o", "", "Output file")
cmd.Flags().StringSliceVar(&pull.insecureRegistries, "insecure-registries", nil, "Use plain HTTP for those registries")
return cmd
}

func (p *pullCmd) run() error {
ref, err := reference.ParseNormalizedNamed(p.targetRef)
if err != nil {
return err
}
b, err := remotes.Pull(context.Background(), ref, createResolver(p.insecureRegistries))
if err != nil {
return err
}

return p.writeBundle(b)
}

func (p *pullCmd) writeBundle(bf *bundle.Bundle) error {
data, digest, err := marshalBundle(bf)
if err != nil {
return fmt.Errorf("cannot marshal bundle: %v", err)
}

if p.output != "" {
if err := ioutil.WriteFile(p.output, data, 0644); err != nil {
return fmt.Errorf("cannot write bundle to %s: %v", p.output, err)
}
return nil
}

if err := ioutil.WriteFile(filepath.Join(p.home.Bundles(), digest), data, 0644); err != nil {
return fmt.Errorf("cannot store bundle : %v", err)

}

return recordBundleReference(p.home, bf.Name, bf.Version, digest)

}
113 changes: 113 additions & 0 deletions cmd/duffle/push.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package main

import (
"context"
"fmt"
"io"
"os"

"github.com/deislabs/duffle/pkg/duffle/home"
"github.com/deislabs/duffle/pkg/reference"

containerdRemotes "github.com/containerd/containerd/remotes"
"github.com/docker/cli/cli/config"
"github.com/docker/cnab-to-oci/remotes"
"github.com/spf13/cobra"
)

type pushCmd struct {
inputBundle string
home home.Home
bundleIsFile bool
targetRef string
insecureRegistries []string
allowFallbacks bool
}

func newPushCmd(out io.Writer) *cobra.Command {
const usage = `Pushes a CNAB bundle to an OCI repository.`
const pushDesc = `
Pushes a CNAB bundle to an OCI registry by pushing all container
images referenced in the bundle to the target repository (all images are
pushed to the same repository, and are referenceable through their digest).
The first argument is the bundle to push (or the path to the bundle file, if the
--bundle-is-file flag is passed), and the second argument is the target repository
where the bundle and all referenced images will be pushed.
Insecure registries can be passed through the --insecure-registries flags,
and --allow-fallbacks enables automatic compatibility fallbacks for registries
without support for custom media type, or OCI manifests.
Examples:
$ duffle push bundle-reference registry/usernamne/bundle:tag
$ duffle push path-to-bundle.json --bundle-is-file registtry/username/bundle:tag
`
var push pushCmd

cmd := &cobra.Command{
Use: "push [BUNDLE] [TARGET REPOSITORY] [options]",
Short: usage,
Long: pushDesc,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
push.home = home.Home(homePath())
push.inputBundle = args[0]
push.targetRef = args[1]
return push.run()
},
}

cmd.Flags().BoolVarP(&push.bundleIsFile, "bundle-is-file", "f", false, "Indicates that the bundle source is a file path")
cmd.Flags().StringSliceVar(&push.insecureRegistries, "insecure-registries", nil, "Use plain HTTP for those registries")
cmd.Flags().BoolVar(&push.allowFallbacks, "allow-fallbacks", true, "Enable automatic compatibility fallbacks for registries without support for custom media type, or OCI manifests")

return cmd
}

func (p *pushCmd) run() error {

bundleFile, err := resolveBundleFilePath(p.inputBundle, p.home.String(), p.bundleIsFile)
if err != nil {
return err
}

b, err := loadBundle(bundleFile)
if err != nil {
return err
}

resolver := createResolver(p.insecureRegistries)
ref, err := reference.ParseNormalizedNamed(p.targetRef)
if err != nil {
return err
}

err = remotes.FixupBundle(context.Background(), b, ref, resolver, remotes.WithEventCallback(displayEvent))
if err != nil {
return err
}
d, err := remotes.Push(context.Background(), b, ref, resolver, p.allowFallbacks)
if err != nil {
return err
}
fmt.Printf("Pushed successfully, with digest %q\n", d.Digest)
return nil
}

func createResolver(insecureRegistries []string) containerdRemotes.Resolver {
return remotes.CreateResolver(config.LoadDefaultConfigFile(os.Stderr), insecureRegistries...)
}

func displayEvent(ev remotes.FixupEvent) {
switch ev.EventType {
case remotes.FixupEventTypeCopyImageStart:
fmt.Fprintf(os.Stderr, "Starting to copy image %s...\n", ev.SourceImage)
case remotes.FixupEventTypeCopyImageEnd:
if ev.Error != nil {
fmt.Fprintf(os.Stderr, "Failed to copy image %s: %s\n", ev.SourceImage, ev.Error)
} else {
fmt.Fprintf(os.Stderr, "Completed image %s copy\n", ev.SourceImage)
}
}
}
2 changes: 2 additions & 0 deletions cmd/duffle/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func newRootCmd(outputRedirect io.Writer) *cobra.Command {
newExportCmd(outLog),
newImportCmd(outLog),
newCreateCmd(outLog),
newPullCmd(outLog),
newPushCmd(outLog),
)

return cmd
Expand Down
Loading

0 comments on commit 61bb2d7

Please sign in to comment.