Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle wiring images #708

Merged
merged 7 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from 6 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
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