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

Leverage distribution/reference for local index #505

Open
wants to merge 1 commit 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 14 additions & 4 deletions cmd/duffle/build.go
Expand Up @@ -14,6 +14,7 @@ import (
dockerdebug "github.com/docker/cli/cli/debug"
dockerflags "github.com/docker/cli/cli/flags"
"github.com/docker/cli/opts"
"github.com/docker/distribution/reference"
"github.com/docker/go-connections/tlsconfig"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -133,16 +134,25 @@ func (b *buildCmd) run() (err error) {
return err
}

named, err := reference.ParseNormalizedNamed(bf.Name)
if err != nil {
return err
}
versioned, err := reference.WithTag(named, bf.Version)
if err != nil {
return err
}

digest, err := b.writeBundle(bf)
if err != nil {
return err
}

// record the new bundle in repositories.json
if err := recordBundleReference(b.home, bf.Name, bf.Version, digest); err != nil {
if err := recordBundleReference(b.home, versioned, digest); err != nil {
return fmt.Errorf("could not record bundle: %v", err)
}
ohai.Fsuccessf(b.out, "Successfully built bundle %s:%s\n", bf.Name, bf.Version)
ohai.Fsuccessf(b.out, "Successfully built bundle %s\n", versioned)

return nil
}
Expand Down Expand Up @@ -222,14 +232,14 @@ func dockerPreRun(opts *dockerflags.ClientOptions) {
}
}

func recordBundleReference(home home.Home, name, version, digest string) error {
func recordBundleReference(home home.Home, ref reference.NamedTagged, digest string) error {
// record the new bundle in repositories.json
index, err := repo.LoadIndex(home.Repositories())
if err != nil {
return fmt.Errorf("cannot create or open %s: %v", home.Repositories(), err)
}

index.Add(name, version, digest)
index.Add(ref, digest)

if err := index.WriteFile(home.Repositories(), 0644); err != nil {
return fmt.Errorf("could not write to %s: %v", home.Repositories(), err)
Expand Down
11 changes: 10 additions & 1 deletion cmd/duffle/build_test.go
Expand Up @@ -9,13 +9,22 @@ import (
"path/filepath"
"testing"

"github.com/docker/distribution/reference"
"github.com/stretchr/testify/assert"

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

func namedOrDie(v string) reference.Named {
res, err := reference.ParseNormalizedNamed(v)
if err != nil {
panic(err)
}
return res
}

func TestBuild(t *testing.T) {
testHome := CreateTestHome(t)
defer os.RemoveAll(testHome.String())
Expand Down Expand Up @@ -70,7 +79,7 @@ func TestBuild(t *testing.T) {
}

// since we've only built one bundle, let's just fetch the latest version
digest, err := index.Get("testbundle", "")
digest, err := index.Get(namedOrDie("testbundle"), "")
if err != nil {
t.Fatalf("could not find bundle: %v", err)
}
Expand Down
55 changes: 31 additions & 24 deletions cmd/duffle/bundle_list.go
Expand Up @@ -6,52 +6,52 @@ import (
"sort"
"strings"

"github.com/docker/distribution/reference"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"

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

// NamedRepositoryList is a list of bundle references.
// ReferenceToDigestList is a list of bundle references.
// Implements a sorter on Name.
type NamedRepositoryList []*NamedRepository
type ReferenceToDigestList []*ReferenceToDigest

// Len returns the length.
func (bl NamedRepositoryList) Len() int { return len(bl) }
func (bl ReferenceToDigestList) Len() int { return len(bl) }

// Swap swaps the position of two items in the versions slice.
func (bl NamedRepositoryList) Swap(i, j int) { bl[i], bl[j] = bl[j], bl[i] }
func (bl ReferenceToDigestList) Swap(i, j int) { bl[i], bl[j] = bl[j], bl[i] }

// Less returns true if the version of entry a is less than the version of entry b.
func (bl NamedRepositoryList) Less(a, b int) bool {
return strings.Compare(bl[a].Name(), bl[b].Name()) < 1
func (bl ReferenceToDigestList) Less(a, b int) bool {
return strings.Compare(reference.FamiliarString(bl[a].ref), reference.FamiliarString(bl[a].ref)) < 1
}

// NamedRepository is a reference to a repository with a name, tag and digest.
type NamedRepository struct {
name string
tag string
// ReferenceToDigest is a reference to a repository with a name, tag and digest.
type ReferenceToDigest struct {
ref reference.NamedTagged
digest string
}

// Name returns the full name.
func (n *NamedRepository) String() string {
return n.name + ":" + n.tag
func (n *ReferenceToDigest) String() string {
return reference.FamiliarString(n.ref)
}

// Name returns the name.
func (n *NamedRepository) Name() string {
return n.name
func (n *ReferenceToDigest) Name() string {
return reference.FamiliarName(n.ref)
}

// Tag returns the tag.
func (n *NamedRepository) Tag() string {
return n.tag
func (n *ReferenceToDigest) Tag() string {
return n.ref.Tag()
}

// Digest returns the digest.
func (n *NamedRepository) Digest() string {
func (n *ReferenceToDigest) Digest() string {
return n.digest
}

Expand Down 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 All @@ -90,20 +90,27 @@ func newBundleListCmd(w io.Writer) *cobra.Command {
return cmd
}

func searchLocal(home home.Home) (NamedRepositoryList, error) {
references := NamedRepositoryList{}
func searchLocal(home home.Home) (ReferenceToDigestList, error) {
references := ReferenceToDigestList{}

index, err := repo.LoadIndex(home.Repositories())
if err != nil {
return nil, fmt.Errorf("cannot open %s: %v", home.Repositories(), err)
}

for repo, tagList := range index {
named, err := reference.ParseNormalizedNamed(repo)
if err != nil {
return nil, err
}
for tag, digest := range tagList {
references = append(references, &NamedRepository{
repo,
tag,
digest,
tagged, err := reference.WithTag(named, tag)
if err != nil {
return nil, err
}
references = append(references, &ReferenceToDigest{
ref: tagged,
digest: digest,
})
}
}
Expand Down
67 changes: 56 additions & 11 deletions cmd/duffle/bundle_remove.go
@@ -1,6 +1,7 @@
package main

import (
"errors"
"fmt"
"io"
"os"
Expand All @@ -10,6 +11,7 @@ import (
"github.com/deis/duffle/pkg/repo"

"github.com/Masterminds/semver"
"github.com/docker/distribution/reference"
"github.com/spf13/cobra"
)

Expand All @@ -31,17 +33,26 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
Long: bundleRemoveDesc,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
bname := args[0]
named, err := reference.ParseNormalizedNamed(args[0])
if err != nil {
return err
}
tagged, _ := named.(reference.NamedTagged)
if tagged != nil {
if versions != "" {
return errors.New("cannot set --version flag when bundle name has a tag")
}
}

h := home.Home(homePath())
index, err := repo.LoadIndex(h.Repositories())
if err != nil {
return err
}

vers, ok := index.GetVersions(bname)
vers, ok := index.GetVersions(named)
if !ok {
fmt.Fprintf(w, "Bundle %q not found. Nothing deleted.", bname)
fmt.Fprintf(w, "Bundle %q not found. Nothing deleted.", args[0])
return nil
}

Expand All @@ -61,10 +72,14 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
if ok, _ := matcher.Validate(sv); ok {
fmt.Fprintf(w, "Version %s matches constraint %q\n", ver, versions)
deletions[ver] = sha
index.DeleteVersion(bname, ver)
versioned, err := reference.WithTag(named, ver)
if err != nil {
return err
}
index.DeleteVersion(versioned)
// If there are no more versions, remove the entire entry.
if vers, ok := index.GetVersions(bname); ok && len(vers) == 0 {
index.Delete(bname)
if vers, ok := index.GetVersions(named); ok && len(vers) == 0 {
index.DeleteAll(named)
}

}
Expand All @@ -79,10 +94,27 @@ func newBundleRemoveCmd(w io.Writer) *cobra.Command {
deleteBundleVersions(deletions, index, h, w)
return nil
}
if tagged != nil {
sha, ok := vers[tagged.Tag()]
if !ok {
return fmt.Errorf("version %q not found", tagged.Tag())
}
index.DeleteVersion(tagged)
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)
}
}
return nil
}

// If no version was specified, delete entire record
if !index.Delete(bname) {
fmt.Fprintf(w, "Bundle %q not found. Nothing deleted.", bname)
if !index.DeleteAll(named) {
fmt.Fprintf(w, "Bundle %q not found. Nothing deleted.", named)
return nil
}
if err := index.WriteFile(h.Repositories(), 0644); err != nil {
Expand All @@ -98,14 +130,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)
}
}
}
}
13 changes: 12 additions & 1 deletion cmd/duffle/bundle_sign.go
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"path/filepath"

"github.com/docker/distribution/reference"
"github.com/spf13/cobra"

"github.com/deis/duffle/pkg/bundle"
Expand Down Expand Up @@ -135,12 +136,22 @@ func (bs *bundleSignCmd) signBundle(bundleFile, keyring string) error {
}
}

named, err := reference.ParseNormalizedNamed(b.Name)
if err != nil {
return err
}

versioned, err := reference.WithTag(named, b.Version)
if err != nil {
return err
}

if err := ioutil.WriteFile(filepath.Join(bs.home.Bundles(), digest), data, 0644); err != nil {
return err
}

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

Expand Down
4 changes: 2 additions & 2 deletions cmd/duffle/install.go
Expand Up @@ -201,9 +201,9 @@ func getBundleFilepath(bun string, insecure bool) (string, error) {
return "", fmt.Errorf("cannot open %s: %v", home.Repositories(), err)
}

digest, err := index.Get(ref.Name(), ref.Tag())
digest, err := index.GetExactly(ref)
if err != nil {
return "", fmt.Errorf("could not find %s:%s in %s: %v", ref.Name(), ref.Tag(), home.Repositories(), err)
return "", fmt.Errorf("could not find %s in %s: %v", ref.Name(), home.Repositories(), err)
}
return filepath.Join(home.Bundles(), digest), nil
}
Expand Down
9 changes: 4 additions & 5 deletions cmd/duffle/pull.go
Expand Up @@ -10,14 +10,13 @@ import (
"path/filepath"
"strings"

"github.com/deis/duffle/pkg/bundle"
"github.com/deis/duffle/pkg/loader"
"github.com/deis/duffle/pkg/reference"

"github.com/docker/distribution/reference"
"github.com/spf13/cobra"

"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/loader"
)

func newPullCmd(w io.Writer) *cobra.Command {
Expand Down Expand Up @@ -94,7 +93,7 @@ func pullBundle(bundleName string, insecure bool) (string, error) {
return "", fmt.Errorf("failed to write bundle: %v", err)
}

return bundleFilepath, recordBundleReference(home, ref.Name(), ref.Tag(), digest)
return bundleFilepath, recordBundleReference(home, ref, digest)
}

func getLoader(insecure bool) (loader.Loader, error) {
Expand Down