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

Add push and pull support using cnab-to-oci #681

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions Gopkg.lock

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

9 changes: 8 additions & 1 deletion Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@

[[constraint]]
name = "github.com/deislabs/cnab-go"
version = "v0.2.1-beta1"
# version = "v0.2.1-beta1"
# FIX ME
revision = "b58e5b38096273e9fbbea9691111dda27c59d836"

[[override]]
name = "github.com/google/go-containerregistry"
Expand Down Expand Up @@ -99,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"
11 changes: 4 additions & 7 deletions cmd/duffle/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (i *installCmd) run() error {
c.Bundle = bun
// calculateParamValues determines if values can be changed in later actions, but we don't have
// previous values so install passes nil.
c.Parameters, err = calculateParamValues(bun, i.valuesFile, i.setParams, i.setFiles, nil)
c.Parameters, err = calculateParamValues(bun, i.valuesFile, i.setParams, i.setFiles)
if err != nil {
return err
}
Expand Down Expand Up @@ -229,10 +229,7 @@ func parseValues(file string) (map[string]interface{}, error) {
return vals, nil
}

func calculateParamValues(bun *bundle.Bundle, valuesFile string, setParams, setFilePaths []string, prevVals map[string]interface{}) (map[string]interface{}, error) {
if prevVals == nil {
prevVals = map[string]interface{}{}
}
func calculateParamValues(bun *bundle.Bundle, valuesFile string, setParams, setFilePaths []string) (map[string]interface{}, error) {
vals := map[string]interface{}{}
if valuesFile != "" {
var err error
Expand All @@ -258,7 +255,7 @@ func calculateParamValues(bun *bundle.Bundle, valuesFile string, setParams, setF
}

// Check that this is a known param
if _, ok := bun.Parameters.Fields[parts[0]]; !ok {
if _, ok := bun.Parameters[parts[0]]; !ok {
return vals, fmt.Errorf("bundle does not have a parameter named %q", parts[0])
}

Expand All @@ -272,5 +269,5 @@ func calculateParamValues(bun *bundle.Bundle, valuesFile string, setParams, setF
vals[parts[0]] = string(content)
}

return bundle.ValuesOrDefaults(vals, prevVals, bun)
return bundle.ValuesOrDefaults(vals, bun)
}
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`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use const block

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this command do anything with images referenced by the bundle?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, by default it does not pull the referenced images.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m revisiting this PR after the whole OCI discussion - do you think it pulling the bundle should pull the referenced images?
Most likely they will not be used on the same machine that pulls the bundle, so not sure about this - would an optional flag for pulling the images be helpful?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense for a local development use case to pre-populate the local docker image store (just pull the bundle and everything in it and it "works"), so I agree with the optional flag.

By the way I don't think it should be a cnab-to-oci feature, as it has no knowledge of the docker engine, only duffle has. I guess it will then parse the bundle.json and call docker pull on every docker image using the docker client ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree - I wasn't thinking this should be in cnab-to-oci.


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).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where are images "pushed" from? Are they copied direct from their remote repositories or do they have to be present in a docker daemon?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they are directly copied from the remote (and actually, as #734 tracks, if the images are only present in the local daemon, this will fail)..


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
2 changes: 1 addition & 1 deletion cmd/duffle/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Credentials and parameters may be passed to the bundle during a target action.

// Override parameters only if some are set.
if valuesFile != "" || len(setParams) > 0 {
c.Parameters, err = calculateParamValues(c.Bundle, valuesFile, setParams, setFiles, c.Parameters)
c.Parameters, err = calculateParamValues(c.Bundle, valuesFile, setParams, setFiles)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/duffle/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (un *uninstallCmd) run() error {
if claim.Bundle == nil {
return errors.New("parameters can only be set if a bundle is provided")
}
params, err := calculateParamValues(claim.Bundle, un.valuesFile, un.setParams, []string{}, claim.Parameters)
params, err := calculateParamValues(claim.Bundle, un.valuesFile, un.setParams, []string{})
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/duffle/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func (up *upgradeCmd) run() error {

// Override parameters only if some are set.
if up.valuesFile != "" || len(up.setParams) > 0 {
claim.Parameters, err = calculateParamValues(claim.Bundle, up.valuesFile, up.setParams, up.setFiles, claim.Parameters)
claim.Parameters, err = calculateParamValues(claim.Bundle, up.valuesFile, up.setParams, up.setFiles)
if err != nil {
return err
}
Expand Down
28 changes: 15 additions & 13 deletions pkg/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,21 @@ func (b *Builder) PrepareBuild(bldr *Builder, mfst *manifest.Manifest, appDir st
}

bf := &bundle.Bundle{
Actions: ctx.Manifest.Actions,
Credentials: ctx.Manifest.Credentials,
Custom: ctx.Manifest.Custom,
Definitions: ctx.Manifest.Definitions,
Description: ctx.Manifest.Description,
Images: ctx.Manifest.Images,
Keywords: ctx.Manifest.Keywords,
Maintainers: ctx.Manifest.Maintainers,
Name: ctx.Manifest.Name,
Outputs: ctx.Manifest.Outputs,
Parameters: ctx.Manifest.Parameters,
SchemaVersion: ctx.Manifest.SchemaVersion,
Version: ctx.Manifest.Version,
Actions: ctx.Manifest.Actions,
Credentials: ctx.Manifest.Credentials,
Custom: ctx.Manifest.Custom,
Definitions: ctx.Manifest.Definitions,
Description: ctx.Manifest.Description,
Images: ctx.Manifest.Images,
Keywords: ctx.Manifest.Keywords,
License: ctx.Manifest.License,
Maintainers: ctx.Manifest.Maintainers,
Name: ctx.Manifest.Name,
Outputs: ctx.Manifest.Outputs,
Parameters: ctx.Manifest.Parameters,
RequiredExtensions: ctx.Manifest.RequiredExtensions,
SchemaVersion: ctx.Manifest.SchemaVersion,
Version: ctx.Manifest.Version,
}

for _, imb := range imageBuilders {
Expand Down
Loading