Skip to content

Commit

Permalink
save relocation map from cnab-to-oci
Browse files Browse the repository at this point in the history
When a relocation map is generated from `cnab-to-oci` save it
within the same directory as the `bundle.json` file.

More information about reloaction map can be found in corresponding
`cnab-to-oci` issue: cnabio/cnab-to-oci#58

The `bundle.Bundle` struct is now wrapped in a `relocated.Bundle`
that will also contain the relocation map.

Methods to fetch `bundle.json` and `relocation-map.json` as well
as en entire bundle with the relocation map at once are moved to
a `fetch` package to avoid dependency cycle.

Signed-off-by: Yves Brissaud <[email protected]>
  • Loading branch information
eunomie committed Nov 6, 2019
1 parent 21491f4 commit 8b98603
Show file tree
Hide file tree
Showing 20 changed files with 286 additions and 109 deletions.
2 changes: 2 additions & 0 deletions Gopkg.lock

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

9 changes: 4 additions & 5 deletions e2e/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strings"
"testing"

"github.com/docker/app/internal/fetch"

"gotest.tools/fs"

"github.com/docker/app/internal/store"
Expand All @@ -31,10 +33,7 @@ func TestBuild(t *testing.T) {
cfg := getDockerConfigDir(t, cmd)

f := path.Join(cfg, "app", "bundles", "docker.io", "library", "single", "_tags", "1.0.0", "bundle.json")
data, err := ioutil.ReadFile(f)
assert.NilError(t, err)
var bndl bundle.Bundle
err = json.Unmarshal(data, &bndl)
bndl, err := fetch.RelocatedBundleFromFile(f)
assert.NilError(t, err)

built := []string{bndl.InvocationImages[0].Digest, bndl.Images["web"].Digest, bndl.Images["worker"].Digest}
Expand All @@ -53,7 +52,7 @@ func TestBuild(t *testing.T) {
bytes, err := ioutil.ReadFile(iidfile)
assert.NilError(t, err)
iid := string(bytes)
actualID, err := store.FromBundle(&bndl)
actualID, err := store.FromBundle(bndl)
assert.NilError(t, err)
assert.Equal(t, iid, fmt.Sprintf("sha256:%s", actualID.String()))
})
Expand Down
61 changes: 61 additions & 0 deletions e2e/relocation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package e2e

import (
"os"
"path/filepath"
"strings"
"testing"

"gotest.tools/assert"
"gotest.tools/icmd"
)

func TestRelocationMapCreatedOnPush(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd
cfg := getDockerConfigDir(t, cmd)

path := filepath.Join("testdata", "local")
ref := info.registryAddress + "/test/local:a-tag"
bundlePath := filepath.Join(cfg, "app", "bundles", strings.Replace(info.registryAddress, ":", "_", 1), "test", "local", "_tags", "a-tag")

// Given a locally built application
build(t, cmd, dockerCli, ref, path)

// When pushing to a registry
cmd.Command = dockerCli.Command("app", "push", ref)
icmd.RunCmd(cmd).Assert(t, icmd.Success)

// Then the relocation map should exist
_, err := os.Stat(filepath.Join(bundlePath, "relocation-map.json"))
assert.NilError(t, err)
})
}

func TestRelocationMapCreatedOnPull(t *testing.T) {
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
cmd := info.configuredCmd
cfg := getDockerConfigDir(t, cmd)

path := filepath.Join("testdata", "local")
ref := info.registryAddress + "/test/local:a-tag"
bundlePath := filepath.Join(cfg, "app", "bundles", strings.Replace(info.registryAddress, ":", "_", 1), "test", "local", "_tags", "a-tag")

// Given a pushed application
build(t, cmd, dockerCli, ref, path)
cmd.Command = dockerCli.Command("app", "push", ref)
icmd.RunCmd(cmd).Assert(t, icmd.Success)
// And given application files are remove
assert.NilError(t, os.RemoveAll(bundlePath))
_, err := os.Stat(filepath.Join(bundlePath, "bundle.json"))
assert.Assert(t, os.IsNotExist(err))

// When application is pulled
cmd.Command = dockerCli.Command("app", "pull", ref)
icmd.RunCmd(cmd).Assert(t, icmd.Success)

// Then the relocation map should exist
_, err = os.Stat(filepath.Join(bundlePath, "relocation-map.json"))
assert.NilError(t, err)
})
}
32 changes: 11 additions & 21 deletions internal/cnab/cnab.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package cnab
import (
"context"
"fmt"
"io/ioutil"
"os"

"github.com/deislabs/cnab-go/bundle"
"github.com/docker/app/internal"
"github.com/docker/app/internal/log"
"github.com/docker/app/internal/packager"
"github.com/docker/app/internal/relocated"
"github.com/docker/app/internal/store"
appstore "github.com/docker/app/internal/store"
"github.com/docker/cli/cli/command"
Expand Down Expand Up @@ -48,30 +47,20 @@ func getAppNameKind(name string) (string, nameKind) {
return name, nameKindReference
}

func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*bundle.Bundle, string, error) {
func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*relocated.Bundle, string, error) {
app, err := packager.Extract(name)
if err != nil {
return nil, "", err
}
defer app.Cleanup()
bndl, err := packager.MakeBundleFromApp(dockerCli, app, nil)
return bndl, "", err
}

// LoadBundleFromFile loads a bundle from a file
func LoadBundleFromFile(filename string) (*bundle.Bundle, error) {
b := &bundle.Bundle{}
data, err := ioutil.ReadFile(filename)
if err != nil {
return b, err
}
return bundle.Unmarshal(data)
return relocated.FromBundle(bndl), "", err
}

// ResolveBundle looks for a CNAB bundle which can be in a Docker App Package format or
// a bundle stored locally or in the bundle store. It returns a built or found bundle,
// a reference to the bundle if it is found in the bundlestore, and an error.
func ResolveBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, string, error) {
func ResolveBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*relocated.Bundle, string, error) {
// resolution logic:
// - if there is a docker-app package in working directory or if a directory is given use packager.Extract
// - pull the bundle from the registry and add it to the bundle store
Expand All @@ -90,7 +79,7 @@ func ResolveBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name
}

// GetBundle searches for the bundle locally and tries to pull it if not found
func GetBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, reference.Reference, error) {
func GetBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*relocated.Bundle, reference.Reference, error) {
bndl, ref, err := getBundleFromStore(bundleStore, name)
if err != nil {
named, err := store.StringToNamedRef(name)
Expand All @@ -108,7 +97,7 @@ func GetBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name str
return bndl, ref, nil
}

func getBundleFromStore(bundleStore appstore.BundleStore, name string) (*bundle.Bundle, reference.Reference, error) {
func getBundleFromStore(bundleStore appstore.BundleStore, name string) (*relocated.Bundle, reference.Reference, error) {
ref, err := bundleStore.LookUp(name)
if err != nil {
logrus.Debugf("Unable to find reference %q in the bundle store", name)
Expand All @@ -123,18 +112,19 @@ func getBundleFromStore(bundleStore appstore.BundleStore, name string) (*bundle.
}

// PullBundle pulls the bundle and stores it into the bundle store
func PullBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, tagRef reference.Named) (*bundle.Bundle, error) {
func PullBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, tagRef reference.Named) (*relocated.Bundle, error) {
insecureRegistries, err := internal.InsecureRegistriesFromEngine(dockerCli)
if err != nil {
return nil, fmt.Errorf("could not retrieve insecure registries: %v", err)
}

bndl, _, err := remotes.Pull(log.WithLogContext(context.Background()), reference.TagNameOnly(tagRef), remotes.CreateResolver(dockerCli.ConfigFile(), insecureRegistries...))
bndl, relocationMap, err := remotes.Pull(log.WithLogContext(context.Background()), reference.TagNameOnly(tagRef), remotes.CreateResolver(dockerCli.ConfigFile(), insecureRegistries...))
if err != nil {
return nil, err
}
if _, err := bundleStore.Store(tagRef, bndl); err != nil {
relocatedBundle := &relocated.Bundle{Bundle: bndl, RelocationMap: relocationMap}
if _, err := bundleStore.Store(tagRef, relocatedBundle); err != nil {
return nil, err
}
return bndl, nil
return relocatedBundle, nil
}
2 changes: 1 addition & 1 deletion internal/commands/image/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func runInspect(dockerCli command.Cli, appname string, opts inspectOptions) erro
if err != nil {
return err
}
installation.Bundle = bndl
installation.Bundle = bndl.Bundle

driverImpl, errBuf := cnab.PrepareDriver(dockerCli, cnab.BindMount{}, nil)
a := &action.RunCustom{
Expand Down
5 changes: 3 additions & 2 deletions internal/commands/image/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
"strings"
"text/tabwriter"

"github.com/deislabs/cnab-go/bundle"
"github.com/docker/app/internal/relocated"

"github.com/docker/app/internal/store"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config"
Expand Down Expand Up @@ -143,5 +144,5 @@ var (

type pkg struct {
ref reference.Reference
bundle *bundle.Bundle
bundle *relocated.Bundle
}
20 changes: 11 additions & 9 deletions internal/commands/image/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"testing"

"github.com/docker/app/internal/relocated"

"gotest.tools/assert"

"github.com/deislabs/cnab-go/bundle"
Expand All @@ -15,18 +17,18 @@ import (
)

type bundleStoreStubForListCmd struct {
refMap map[reference.Reference]*bundle.Bundle
refMap map[reference.Reference]*relocated.Bundle
// in order to keep the reference in the same order between tests
refList []reference.Reference
}

func (b *bundleStoreStubForListCmd) Store(ref reference.Reference, bndle *bundle.Bundle) (reference.Digested, error) {
b.refMap[ref] = bndle
func (b *bundleStoreStubForListCmd) Store(ref reference.Reference, bndl *relocated.Bundle) (reference.Digested, error) {
b.refMap[ref] = bndl
b.refList = append(b.refList, ref)
return store.FromBundle(bndle)
return store.FromBundle(bndl)
}

func (b *bundleStoreStubForListCmd) Read(ref reference.Reference) (*bundle.Bundle, error) {
func (b *bundleStoreStubForListCmd) Read(ref reference.Reference) (*relocated.Bundle, error) {
bndl, ok := b.refMap[ref]
if ok {
return bndl, nil
Expand All @@ -52,20 +54,20 @@ func TestListWithQuietFlag(t *testing.T) {
dockerCli, err := command.NewDockerCli(command.WithOutputStream(w))
assert.NilError(t, err)
bundleStore := &bundleStoreStubForListCmd{
refMap: make(map[reference.Reference]*bundle.Bundle),
refMap: make(map[reference.Reference]*relocated.Bundle),
refList: []reference.Reference{},
}
ref1, err := store.FromString("a855ac937f2ed375ba4396bbc49c4093e124da933acd2713fb9bc17d7562a087")
assert.NilError(t, err)
ref2, err := reference.Parse("foo/bar:1.0")
assert.NilError(t, err)
_, err = bundleStore.Store(ref1, &bundle.Bundle{})
_, err = bundleStore.Store(ref1, relocated.FromBundle(&bundle.Bundle{}))
assert.NilError(t, err)
_, err = bundleStore.Store(ref2, &bundle.Bundle{
_, err = bundleStore.Store(ref2, relocated.FromBundle(&bundle.Bundle{
Version: "1.0.0",
SchemaVersion: "1.0.0",
Name: "Foo App",
})
}))
assert.NilError(t, err)
err = runList(dockerCli, imageListOption{quiet: true}, bundleStore)
assert.NilError(t, err)
Expand Down
7 changes: 4 additions & 3 deletions internal/commands/image/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package image
import (
"fmt"

"github.com/deislabs/cnab-go/bundle"
"github.com/docker/app/internal/relocated"

"github.com/docker/app/internal/store"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/config"
Expand Down Expand Up @@ -46,7 +47,7 @@ func runTag(bundleStore store.BundleStore, srcAppImage, destAppImage string) err
return storeBundle(srcRef, destAppImage, bundleStore)
}

func readBundle(name string, bundleStore store.BundleStore) (*bundle.Bundle, error) {
func readBundle(name string, bundleStore store.BundleStore) (*relocated.Bundle, error) {
cnabRef, err := bundleStore.LookUp(name)
if err != nil {
switch err.(type) {
Expand All @@ -65,7 +66,7 @@ func readBundle(name string, bundleStore store.BundleStore) (*bundle.Bundle, err
return bundle, nil
}

func storeBundle(bundle *bundle.Bundle, name string, bundleStore store.BundleStore) error {
func storeBundle(bundle *relocated.Bundle, name string, bundleStore store.BundleStore) error {
cnabRef, err := store.StringToNamedRef(name)
if err != nil {
return err
Expand Down
15 changes: 8 additions & 7 deletions internal/commands/image/tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"fmt"
"testing"

"github.com/docker/app/internal/relocated"

"gotest.tools/assert"

"github.com/deislabs/cnab-go/bundle"
"github.com/docker/distribution/reference"
)

Expand All @@ -18,7 +19,7 @@ func parseRefOrDie(t *testing.T, ref string) reference.Named {
}

type bundleStoreStub struct {
ReadBundle *bundle.Bundle
ReadBundle *relocated.Bundle
ReadError error
StoredBundle string
StoredError error
Expand All @@ -27,7 +28,7 @@ type bundleStoreStub struct {
LookUpError error
}

func (b *bundleStoreStub) Store(ref reference.Reference, bndle *bundle.Bundle) (reference.Digested, error) {
func (b *bundleStoreStub) Store(ref reference.Reference, bndle *relocated.Bundle) (reference.Digested, error) {
defer func() {
b.StoredError = nil
}()
Expand All @@ -37,7 +38,7 @@ func (b *bundleStoreStub) Store(ref reference.Reference, bndle *bundle.Bundle) (
return b.StoredID, b.StoredError
}

func (b *bundleStoreStub) Read(ref reference.Reference) (*bundle.Bundle, error) {
func (b *bundleStoreStub) Read(ref reference.Reference) (*relocated.Bundle, error) {
defer func() {
b.ReadBundle = nil
b.ReadError = nil
Expand Down Expand Up @@ -88,7 +89,7 @@ func TestUnexistingSource(t *testing.T) {
func TestInvalidDestinationReference(t *testing.T) {
// given a reference and a bundle is returned by bundleStore.LookUp and bundleStore.Read
mockedBundleStore.LookUpRef = parseRefOrDie(t, "ref")
mockedBundleStore.ReadBundle = &bundle.Bundle{}
mockedBundleStore.ReadBundle = &relocated.Bundle{}
// and given a bad destination reference
const badRef = "b@d reference"

Expand All @@ -100,7 +101,7 @@ func TestInvalidDestinationReference(t *testing.T) {
func TestBundleNotStored(t *testing.T) {
// given a reference and a bundle is returned by bundleStore.LookUp and bundleStore.Read
mockedBundleStore.LookUpRef = parseRefOrDie(t, "src-app")
mockedBundleStore.ReadBundle = &bundle.Bundle{}
mockedBundleStore.ReadBundle = &relocated.Bundle{}
// and given bundleStore.Store will return an error
mockedBundleStore.StoredError = fmt.Errorf("error from bundleStore.Store")

Expand All @@ -112,7 +113,7 @@ func TestBundleNotStored(t *testing.T) {
func TestSuccessfulyTag(t *testing.T) {
// given a reference and a bundle is returned by bundleStore.LookUp and bundleStore.Read
mockedBundleStore.LookUpRef = parseRefOrDie(t, "src-app")
mockedBundleStore.ReadBundle = &bundle.Bundle{}
mockedBundleStore.ReadBundle = &relocated.Bundle{}
// and given valid source and output references
const (
srcRef = "src-app"
Expand Down
Loading

0 comments on commit 8b98603

Please sign in to comment.