Skip to content

Commit

Permalink
Handle wiring images (#708)
Browse files Browse the repository at this point in the history
* Add support for wiring

* Added tests

* Add support for wiring

* Added tests

* Address vdice comments, BYE BYE OriginalImage

* updated logic for the relocation mapping.

* DEP FOR THE DEP GODS
  • Loading branch information
jeremyrickard authored Oct 10, 2019
1 parent 35ee341 commit 5d8cd2d
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 23 deletions.
1 change: 1 addition & 0 deletions Gopkg.lock

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

13 changes: 6 additions & 7 deletions pkg/cnab/config_adapter/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,12 @@ func TestManifestConverter_generateImages(t *testing.T) {
a := NewManifestConverter(c.Context, m, nil)

mappedImage := manifest.MappedImage{
Description: "un petite server",
Repository: "deislabs/myserver",
ImageType: "docker",
Digest: "abc123",
Size: 12,
MediaType: "download",
OriginalImage: "deis/myserver:1.0.0",
Description: "un petite server",
Repository: "deislabs/myserver",
ImageType: "docker",
Digest: "abc123",
Size: 12,
MediaType: "download",
Labels: map[string]string{
"OS": "linux",
"Architecture": "amd64",
Expand Down
19 changes: 8 additions & 11 deletions pkg/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ type Manifest struct {

// ImageMap is a map of images referenced in the bundle. If an image relocation mapping is later provided, that
// will be mounted at as a file at runtime to /cnab/app/relocation-mapping.json.
// TODO: porter should handle the relocation and overwrite the repository and tag (if present), and
// populate originalImage
ImageMap map[string]MappedImage `yaml:"images,omitempty"`
}

Expand Down Expand Up @@ -258,15 +256,14 @@ func (m MixinDeclaration) MarshalYAML() (interface{}, error) {
}

type MappedImage struct {
Description string `yaml:"description"`
ImageType string `yaml:"imageType"`
Repository string `yaml:"repository"`
OriginalImage string `yaml:"originalImage,omitempty"`
Digest string `yaml:"digest,omitempty"`
Size uint64 `yaml:"size,omitempty"`
MediaType string `yaml:"mediaType,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
Tag string `yaml:"tag,omitempty"`
Description string `yaml:"description"`
ImageType string `yaml:"imageType"`
Repository string `yaml:"repository"`
Digest string `yaml:"digest,omitempty"`
Size uint64 `yaml:"size,omitempty"`
MediaType string `yaml:"mediaType,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
Tag string `yaml:"tag,omitempty"`
}

type Dependency struct {
Expand Down
62 changes: 60 additions & 2 deletions pkg/manifest/runtime-manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
"reflect"
"strings"

"github.com/deislabs/porter/pkg/context"

"github.com/cbroglie/mustache"
"github.com/deislabs/cnab-go/bundle"
"github.com/deislabs/porter/pkg/context"
"github.com/deislabs/porter/pkg/runtime"
"github.com/docker/cnab-to-oci/relocation"
"github.com/docker/distribution/reference"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
Expand Down Expand Up @@ -306,3 +307,60 @@ func (m *RuntimeManifest) Prepare() error {
}
return nil
}

// ResolveImages updates the RuntimeManifest to properly reflect the image map passed to the bundle via the
// mounted bundle.json and relocation mapping
func (m *RuntimeManifest) ResolveImages(bun *bundle.Bundle, reloMap relocation.ImageRelocationMap) error {
reverseLookup := make(map[string]string)
for alias, image := range bun.Images {
manifestImage, ok := m.ImageMap[alias]
if !ok {
return fmt.Errorf("unable to find image in porter manifest: %s", alias)
}
manifestImage.Digest = image.Digest
err := resolveImage(&manifestImage, image.Image)
if err != nil {
return errors.Wrap(err, "unable to update image map from bundle.json")
}
m.ImageMap[alias] = manifestImage
reverseLookup[image.Image] = alias
}

for oldRef, reloRef := range reloMap {
alias := reverseLookup[oldRef]
manifestImage, ok := m.ImageMap[alias]
if !ok {
return fmt.Errorf("unable to find relocated image: %s", oldRef)
}
err := resolveImage(&manifestImage, reloRef)
if err != nil {
return errors.Wrap(err, "unable to update image map from relocation mapping")
}
m.ImageMap[alias] = manifestImage
}
return nil
}

func resolveImage(image *MappedImage, refString string) error {
//figure out what type of Reference it is so we can extract useful things for our image map
ref, err := reference.Parse(refString)
if err != nil {
return errors.Wrapf(err, "unable to parse docker image %s", refString)
}
switch v := ref.(type) {
case reference.Canonical:
if tagged, ok := ref.(reference.NamedTagged); ok {
image.Tag = tagged.Tag()
}
image.Repository = v.Name()
image.Digest = v.Digest().String()

case reference.NamedTagged:
image.Tag = v.Tag()
image.Repository = v.Name()
case reference.Named:
image.Repository = v.Name()
image.Tag = "latest" //Populate this with latest so that the {{ can reference something }}
}
return nil
}
233 changes: 231 additions & 2 deletions pkg/manifest/runtime-manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
"os"
"testing"

"github.com/deislabs/porter/pkg/config"

"github.com/deislabs/cnab-go/bundle"
"github.com/deislabs/cnab-go/bundle/definition"
"github.com/deislabs/porter/pkg/config"
"github.com/deislabs/porter/pkg/context"
"github.com/docker/cnab-to-oci/relocation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
Expand Down Expand Up @@ -863,3 +863,232 @@ func TestManifest_ResolveImageMapMissingImage(t *testing.T) {
err := rm.ResolveStep(s)
assert.Error(t, err)
}

func TestResolveImage(t *testing.T) {
tests := []struct {
name string
reference string
want MappedImage
}{
{
name: "canonical reference",
reference: "deislabs/porter-hello@sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63",
want: MappedImage{
Repository: "deislabs/porter-hello",
Digest: "sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63",
},
},
{
name: "tagged reference",
reference: "deislabs/porter-hello:v0.1.10",
want: MappedImage{
Repository: "deislabs/porter-hello",
Tag: "v0.1.10",
},
},
{
name: "named reference",
reference: "deislabs/porter-hello",
want: MappedImage{
Repository: "deislabs/porter-hello",
Tag: "latest",
},
},
{
name: "the one with a hostname",
reference: "deislabs.io/deislabs/porter-hello",
want: MappedImage{
Repository: "deislabs.io/deislabs/porter-hello",
Tag: "latest",
},
},
{
name: "the one with a hostname and port",
reference: "deislabs.io:9090/deislabs/porter-hello:foo",
want: MappedImage{
Repository: "deislabs.io:9090/deislabs/porter-hello",
Tag: "foo",
},
},
{

name: "tagged and digested",
reference: "deislabs/porter-hello:latest@sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63",
want: MappedImage{
Repository: "deislabs/porter-hello",
Tag: "latest",
Digest: "sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63",
},
},
}
for _, test := range tests {
got := &MappedImage{}
err := resolveImage(got, test.reference)
assert.NoError(t, err)
assert.Equal(t, test.want.Repository, got.Repository)
assert.Equal(t, test.want.Tag, got.Tag)
assert.Equal(t, test.want.Digest, got.Digest)
}
}

func TestResolveImageErrors(t *testing.T) {
tests := []struct {
name string
reference string
want string
}{
{
name: "no algo digest",
reference: "deislabs/porter-hello@8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63",
want: "unable to parse docker image %s: invalid reference format",
},
{
name: "bad digest",
reference: "deislabs/porter-hello@sha256:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f",
want: "unable to parse docker image %s: invalid checksum digest length",
},
{
name: "bad digest algo",
reference: "deislabs/porter-hello@sha356:8b06c3da72dc9fa7002b9bc1f73a7421b4287c9cf0d3b08633287473707f9a63",
want: "unable to parse docker image %s: unsupported digest algorithm",
},
{
name: "malformed tagged ref",
reference: "deislabs/porter-hello@latest",
want: "unable to parse docker image %s: invalid reference format",
},
{
name: "too many ports tagged ref",
reference: "deislabs:8080:8080/porter-hello:latest",
want: "unable to parse docker image %s: invalid reference format",
},
}
for _, test := range tests {
got := &MappedImage{}
err := resolveImage(got, test.reference)
assert.EqualError(t, err, fmt.Sprintf(test.want, test.reference))
}
}

func TestResolveImageWithUpdatedBundle(t *testing.T) {
cxt := context.NewTestContext(t)
m := &Manifest{
ImageMap: map[string]MappedImage{
"machine": MappedImage{
Repository: "deislabs/ghost",
Tag: "latest",
Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041",
},
},
}

img := bundle.Image{}
img.Image = "blah/ghost:latest"
img.Digest = "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041"
bun := &bundle.Bundle{
Images: map[string]bundle.Image{
"machine": img,
},
}

reloMap := relocation.ImageRelocationMap{}

rm := NewRuntimeManifest(cxt.Context, ActionInstall, m)
err := rm.ResolveImages(bun, reloMap)
assert.NoError(t, err)
mi := rm.ImageMap["machine"]
assert.Equal(t, "blah/ghost", mi.Repository)
}

func TestResolveImageWithUpdatedMismatchedBundle(t *testing.T) {
cxt := context.NewTestContext(t)
m := &Manifest{
ImageMap: map[string]MappedImage{
"machine": MappedImage{
Repository: "deislabs/ghost",
Tag: "latest",
Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041",
},
},
}

img := bundle.Image{}
img.Image = "blah/ghost:latest"
img.Digest = "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041"
bun := &bundle.Bundle{
Images: map[string]bundle.Image{
"ghost": img,
},
}

reloMap := relocation.ImageRelocationMap{}

rm := NewRuntimeManifest(cxt.Context, ActionInstall, m)
err := rm.ResolveImages(bun, reloMap)
assert.Error(t, err)
assert.EqualError(t, err, fmt.Sprintf("unable to find image in porter manifest: %s", "ghost"))

}

func TestResolveImageWithRelo(t *testing.T) {
cxt := context.NewTestContext(t)
m := &Manifest{
ImageMap: map[string]MappedImage{
"machine": MappedImage{
Repository: "gabrtv/microservice",
Tag: "latest",
Digest: "sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687",
},
},
}

img := bundle.Image{}
img.Image = "gabrtv/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687"
img.Digest = "sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687"
bun := &bundle.Bundle{
Images: map[string]bundle.Image{
"machine": img,
},
}

reloMap := relocation.ImageRelocationMap{
"gabrtv/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687": "my.registry/microservice@sha256:cca460afa270d4c527981ef9ca4989346c56cf9b20217dcea37df1ece8120687",
}

rm := NewRuntimeManifest(cxt.Context, ActionInstall, m)
err := rm.ResolveImages(bun, reloMap)
assert.NoError(t, err)
mi := rm.ImageMap["machine"]
assert.Equal(t, "my.registry/microservice", mi.Repository)
}

func TestResolveImageBadRelocation(t *testing.T) {
cxt := context.NewTestContext(t)
m := &Manifest{
ImageMap: map[string]MappedImage{
"machine": MappedImage{
Repository: "deislabs/ghost",
Tag: "latest",
Digest: "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041",
},
},
}

img := bundle.Image{}
img.Image = "deislabs/ghost:latest"
img.Digest = "sha256:75c495e5ce9c428d482973d72e3ce9925e1db304a97946c9aa0b540d7537e041"
bun := &bundle.Bundle{
Images: map[string]bundle.Image{
"machine": img,
},
}

reloMap := relocation.ImageRelocationMap{
"deislabs/nogood:latest": "cnabio/ghost:latest",
}

rm := NewRuntimeManifest(cxt.Context, ActionInstall, m)
err := rm.ResolveImages(bun, reloMap)
assert.Error(t, err)
assert.EqualError(t, err, fmt.Sprintf("unable to find relocated image: %s", "deislabs/nogood:latest"))
}
Loading

0 comments on commit 5d8cd2d

Please sign in to comment.