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

WIP: Push pull to image registry #442

Open
wants to merge 5 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
32 changes: 31 additions & 1 deletion Gopkg.lock

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

2 changes: 2 additions & 0 deletions cmd/duffle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ func newBundleCmd(w io.Writer) *cobra.Command {
newBundleSignCmd(w),
newBundleVerifyCmd(w),
newBundleRemoveCmd(w),
newBundlePushCmd(w),
newBundlePullCmd(w),
)
return cmd
}
2 changes: 1 addition & 1 deletion cmd/duffle/bundle_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func newBundleListCmd(w io.Writer) *cobra.Command {
}

for _, ref := range references {
fmt.Println(ref.Name())
fmt.Println(ref)
}

return nil
Expand Down
55 changes: 55 additions & 0 deletions cmd/duffle/bundle_pull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

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

"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/flags"
"github.com/spf13/cobra"

"github.com/deis/duffle/pkg/crypto/digest"
"github.com/deis/duffle/pkg/duffle/home"
"github.com/deis/duffle/pkg/image"
)

func newBundlePullCmd(w io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "pull BUNDLE",
Short: "pull a bundle from an image registry",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cli := command.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, false)
if err := cli.Initialize(flags.NewClientOptions()); err != nil {
return err
}
signedBundle, err := image.PullBundle(context.TODO(), cli, args[0])
if err != nil {
return err
}
sha, err := digest.OfBuffer(signedBundle)
if err != nil {
return fmt.Errorf("cannot compute digest from bundle: %v", err)
}

h := home.Home(homePath())
fpath := filepath.Join(h.Bundles(), sha)
if err := ioutil.WriteFile(fpath, signedBundle, 0644); err != nil {
return err
}

parts := strings.Split(args[0], ":")
if len(parts) != 2 {
return fmt.Errorf("%s is of a wrong format, must be repo:tag", args[0])
}
name, version := parts[0], parts[1]
return recordBundleReference(h, name, version, sha)
},
}
return cmd
}
59 changes: 59 additions & 0 deletions cmd/duffle/bundle_push.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package main

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

"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/flags"
"github.com/spf13/cobra"

"github.com/deis/duffle/pkg/duffle/home"
"github.com/deis/duffle/pkg/image"
"github.com/deis/duffle/pkg/repo"
)

func newBundlePushCmd(w io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "push BUNDLE",
Short: "push a bundle to an image registry",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
parts := strings.Split(args[0], ":")
if len(parts) != 2 {
return fmt.Errorf("%s is of a wrong format, must be repo:tag", args[0])
}
name, version := parts[0], parts[1]
h := home.Home(homePath())
index, err := repo.LoadIndex(h.Repositories())
if err != nil {
return err
}
sha, err := index.Get(name, version)
if err != nil {
return err
}
fpath := filepath.Join(h.Bundles(), sha)
data, err := ioutil.ReadFile(fpath)
if err != nil {
return err
}
cli := command.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, false)
if err := cli.Initialize(flags.NewClientOptions()); err != nil {
return err
}
digest, err := image.PushBundle(context.TODO(), cli, data, args[0])
if err != nil {
return err
}
fmt.Println("Digest of manifest is", digest)
return nil
},
}
return cmd
}
44 changes: 41 additions & 3 deletions cmd/duffle/bundle_remove.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package main

import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/deis/duffle/pkg/duffle/home"
"github.com/deis/duffle/pkg/repo"
Expand Down Expand Up @@ -32,6 +34,13 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
bname := args[0]
var specificVersion string
if parts := strings.Split(bname, ":"); len(parts) == 2 {
if versions != "" {
return errors.New("cannot set --version flag when bundle name has a tag")
}
bname, specificVersion = parts[0], parts[1]
}

h := home.Home(homePath())
index, err := repo.LoadIndex(h.Repositories())
Expand Down Expand Up @@ -79,6 +88,22 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
deleteBundleVersions(deletions, index, h, w)
return nil
}
if specificVersion != "" {
sha, ok := vers[specificVersion]
if !ok {
return fmt.Errorf("version %q not found", specificVersion)
}
index.DeleteVersion(bname, specificVersion)
if err := index.WriteFile(h.Repositories(), 0644); err != nil {
return err
}
if !isShaReferenced(index, sha) {
fpath := filepath.Join(h.Bundles(), sha)
if err := os.Remove(fpath); err != nil {
fmt.Fprintf(w, "WARNING: could not delete stake record %q", fpath)
}
}
}

// If no version was specified, delete entire record
if !index.Delete(bname) {
Expand All @@ -98,14 +123,27 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
return cmd
}

func isShaReferenced(index repo.Index, sha string) bool {
for _, vs := range index {
for _, otherSha := range vs {
if otherSha == sha {
return true
}
}
}
return false
}

// deleteBundleVersions removes the given SHAs from bundle storage
//
// It warns, but does not fail, if a given SHA is not found.
func deleteBundleVersions(vers map[string]string, index repo.Index, h home.Home, w io.Writer) {
for _, sha := range vers {
fpath := filepath.Join(h.Bundles(), sha)
if err := os.Remove(fpath); err != nil {
fmt.Fprintf(w, "WARNING: could not delete stake record %q", fpath)
if !isShaReferenced(index, sha) {
fpath := filepath.Join(h.Bundles(), sha)
if err := os.Remove(fpath); err != nil {
fmt.Fprintf(w, "WARNING: could not delete stake record %q", fpath)
}
}
}
}
33 changes: 23 additions & 10 deletions cmd/duffle/bundle_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/deis/duffle/pkg/bundle"
"github.com/deis/duffle/pkg/crypto/digest"
"github.com/deis/duffle/pkg/duffle/home"
"github.com/deis/duffle/pkg/image"
"github.com/deis/duffle/pkg/signature"
)

Expand All @@ -25,12 +26,13 @@ If no key name is supplied, this uses the first signing key in the secret keyrin
`

type bundleSignCmd struct {
out io.Writer
home home.Home
identity string
bundleFile string
outfile string
skipValidation bool
out io.Writer
home home.Home
identity string
bundleFile string
outfile string
skipValidation bool
pushLocalImages bool
}

func newBundleSignCmd(w io.Writer) *cobra.Command {
Expand All @@ -48,13 +50,18 @@ func newBundleSignCmd(w io.Writer) *cobra.Command {
if err != nil {
return err
}
return sign.signBundle(bundle, secring)
resolver, err := image.NewResolver(sign.pushLocalImages)
if err != nil {
return err
}
return sign.signBundle(bundle, secring, resolver)
},
}
cmd.Flags().StringVarP(&sign.identity, "user", "u", "", "the user ID of the key to use. Format is either email address or 'NAME (COMMENT) <EMAIL>'")
cmd.Flags().StringVarP(&sign.bundleFile, "file", "f", "", "path to bundle file to sign")
cmd.Flags().StringVarP(&sign.outfile, "output-file", "o", "", "the name of the output file")
cmd.Flags().BoolVar(&sign.skipValidation, "skip-validate", false, "do not validate the JSON before marshaling it.")
cmd.Flags().BoolVar(&sign.pushLocalImages, "push-local-images", false, "push docker local-only images to the registry.")

return cmd
}
Expand All @@ -71,8 +78,7 @@ func bundleFileOrArg1(args []string, bundle string) (string, error) {
}
return bundle, nil
}
func (bs *bundleSignCmd) signBundle(bundleFile, keyring string) error {
def := home.DefaultRepository()
func (bs *bundleSignCmd) signBundle(bundleFile, keyring string, containerImageResolver bundle.ContainerImageResolver) error {
// Verify that file exists
if fi, err := os.Stat(bundleFile); err != nil {
return fmt.Errorf("cannot find bundle file to sign: %v", err)
Expand All @@ -89,6 +95,13 @@ func (bs *bundleSignCmd) signBundle(bundleFile, keyring string) error {
return err
}

if err := b.FixupContainerImages(containerImageResolver); err != nil {
if ok, image := image.IsErrImageLocalOnly(err); ok {
fmt.Fprintf(os.Stderr, "Image %q is only available locally. Please push it to the registry\n", image)
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

}
return err
}

if !bs.skipValidation {
if err := b.Validate(); err != nil {
return err
Expand Down Expand Up @@ -140,7 +153,7 @@ func (bs *bundleSignCmd) signBundle(bundleFile, keyring string) error {
}

// TODO - write pkg method in bundle that writes file and records the reference
if err := recordBundleReference(bs.home, fmt.Sprintf("%s/%s", def, b.Name), b.Version, digest); err != nil {
if err := recordBundleReference(bs.home, b.Name, b.Version, digest); err != nil {
return err
}

Expand Down
Loading