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

Commit

Permalink
Improve how relocation mappings are applied (#862)
Browse files Browse the repository at this point in the history
Also support a relocation mapping flag on the status command.

Fixes #858
Fixes #859
  • Loading branch information
glyn authored and jeremyrickard committed Sep 17, 2019
1 parent 5b3af64 commit 414c8a6
Show file tree
Hide file tree
Showing 9 changed files with 145 additions and 53 deletions.
9 changes: 7 additions & 2 deletions cmd/duffle/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (i *installCmd) run() error {
return err
}

driverImpl, err := prepareDriver(i.driver, i.relocationMapping)
driverImpl, err := prepareDriver(i.driver)
if err != nil {
return err
}
Expand All @@ -147,11 +147,16 @@ func (i *installCmd) run() error {
return err
}

opRelocator, err := makeOpRelocator(i.relocationMapping)
if err != nil {
return err
}

inst := &action.Install{
Driver: driverImpl,
}
fmt.Fprintf(i.out, "Executing install action...\n")
err = inst.Run(c, creds, setOut(i.out))
err = inst.Run(c, creds, setOut(i.out), opRelocator)

// Even if the action fails, we want to store a claim. This is because
// we cannot know, based on a failure, whether or not any resources were
Expand Down
64 changes: 23 additions & 41 deletions cmd/duffle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"runtime"
"strings"

"github.com/deislabs/cnab-go/action"
"github.com/deislabs/cnab-go/bundle"
"github.com/deislabs/cnab-go/bundle/loader"
"github.com/deislabs/cnab-go/claim"
Expand Down Expand Up @@ -111,7 +112,7 @@ func must(err error) {
}

// prepareDriver prepares a driver per the user's request.
func prepareDriver(driverName string, relMap string) (driver.Driver, error) {
func prepareDriver(driverName string) (driver.Driver, error) {
driverImpl, err := lookup.Lookup(driverName)
if err != nil {
return nil, err
Expand All @@ -126,55 +127,36 @@ func prepareDriver(driverName string, relMap string) (driver.Driver, error) {
configurable.SetConfig(driverCfg)
}

rm, err := loadRelMapping(relMap)
return driverImpl, nil
}

func makeOpRelocator(relMapping string) (action.OperationConfigFunc, error) {
rm, err := loadRelMapping(relMapping)
if err != nil {
return nil, err
}

// wrap the driver so any relocation mapping is mounted
return &driverWithRelocationMapping{
driver: driverImpl,
relMapping: rm,
}, nil
}

type driverWithRelocationMapping struct {
driver driver.Driver
relMapping string
}

func (d *driverWithRelocationMapping) Run(op *driver.Operation) (driver.OperationResult, error) {
// if there is a relocation mapping, ensure it is mounted and relocate the invocation image
if d.relMapping != "" {
op.Files["/cnab/app/relocation-mapping.json"] = d.relMapping

var err error
op.Image.Image, err = d.relocateImage(op.Image.Image)
relMap := make(map[string]string)
if rm != "" {
err := json.Unmarshal([]byte(rm), &relMap)
if err != nil {
return driver.OperationResult{}, err
return nil, fmt.Errorf("failed to unmarshal relocation mapping: %v", err)
}
}

return d.driver.Run(op)
}

func (d *driverWithRelocationMapping) Handles(it string) bool {
return d.driver.Handles(it)
}

func (d *driverWithRelocationMapping) relocateImage(im string) (string, error) {
relMap := make(map[string]string)
err := json.Unmarshal([]byte(d.relMapping), &relMap)
if err != nil {
return "", fmt.Errorf("failed to unmarshal relocation mapping: %v", err)
}

mapped, ok := relMap[im]
if !ok {
return "", fmt.Errorf("invocation image %s not present in relocation mapping %v", im, relMap)
}
return func(op *driver.Operation) error {
// if there is a relocation mapping, ensure it is mounted and relocate the invocation image
if rm != "" {
op.Files["/cnab/app/relocation-mapping.json"] = rm

return mapped, nil
im, ok := relMap[op.Image.Image]
if !ok {
return fmt.Errorf("invocation image %s not present in relocation mapping %v", op.Image.Image, relMap)
}
op.Image.Image = im
}
return nil
}, nil
}

func loadRelMapping(relMap string) (string, error) {
Expand Down
81 changes: 81 additions & 0 deletions cmd/duffle/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"path/filepath"
"testing"

"github.com/deislabs/cnab-go/driver"

"github.com/deislabs/duffle/pkg/duffle/home"

"github.com/deislabs/cnab-go/bundle"
Expand Down Expand Up @@ -162,3 +164,82 @@ func TestFindCreds(t *testing.T) {
os.RemoveAll(filepath.Join(credDir, "*"))
}
}

func TestMakeOpRelocator(t *testing.T) {
is := assert.New(t)

tests := map[string]struct {
relMapFile string
invocationImage string
expectedErrorMessage string
expectedInvocationImage string
expectedOpRelocatorErrorMessage string
}{
"bad relocation mapping file": {
relMapFile: "no-such-file",
invocationImage: "example.com/original",
expectedErrorMessage: "failed to read relocation mapping from no-such-file:",
expectedInvocationImage: "",
expectedOpRelocatorErrorMessage: "",
},
"valid relocation mapping file": {
relMapFile: "testdata/oprelocator/relmap.json",
invocationImage: "example.com/original",
expectedErrorMessage: "",
expectedInvocationImage: "example.com/relocated",
expectedOpRelocatorErrorMessage: "",
},
"omitted relocation mapping file": {
relMapFile: "",
invocationImage: "example.com/original",
expectedErrorMessage: "",
expectedInvocationImage: "example.com/original",
expectedOpRelocatorErrorMessage: "",
},
"relocation mapping file with malformed contents": {
relMapFile: "testdata/oprelocator/badrelmap.json",
invocationImage: "example.com/original",
expectedErrorMessage: "failed to unmarshal relocation mapping:",
expectedInvocationImage: "example.com/original",
expectedOpRelocatorErrorMessage: "",
},
"invocation image not in relocation mapping": {
relMapFile: "testdata/oprelocator/relmap.json",
invocationImage: "example.com/other",
expectedErrorMessage: "",
expectedInvocationImage: "example.com/other",
expectedOpRelocatorErrorMessage: "invocation image example.com/other not present in relocation mapping map[",
},
}

withNextTest:
for name, testCase := range tests {
opRelocator, err := makeOpRelocator(testCase.relMapFile)
if testCase.expectedErrorMessage != "" {
is.Contains(err.Error(), testCase.expectedErrorMessage, "Failed on test: "+name)
continue withNextTest
}
is.Nil(err, "Failed on test: "+name)

op := driver.Operation{
Files: make(map[string]string),
Image: bundle.InvocationImage{
BaseImage: bundle.BaseImage{
Image: testCase.invocationImage,
},
},
}
err = opRelocator(&op)
if testCase.expectedOpRelocatorErrorMessage != "" {
is.Contains(err.Error(), testCase.expectedOpRelocatorErrorMessage, "Failed on test: "+name)
continue withNextTest
}
is.Nil(err, "Failed on test: "+name)

is.Equal(testCase.expectedInvocationImage, op.Image.Image, "Failed on test: "+name)

// In the success case, a relocation mapping should be mounted if and only if a relocation mapping was specified
_, mounted := op.Files["/cnab/app/relocation-mapping.json"]
is.Equal(testCase.relMapFile != "", mounted, "Failed on test: "+name)
}
}
9 changes: 7 additions & 2 deletions cmd/duffle/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Credentials and parameters may be passed to the bundle during a target action.
return err
}

driverImpl, err := prepareDriver(driver, relocationMapping)
driverImpl, err := prepareDriver(driver)
if err != nil {
return err
}
Expand All @@ -74,13 +74,18 @@ Credentials and parameters may be passed to the bundle during a target action.
}
}

opRelocator, err := makeOpRelocator(relocationMapping)
if err != nil {
return err
}

action := &action.RunCustom{
Driver: driverImpl,
Action: target,
}

fmt.Fprintf(w, "Executing custom action %q for release %q", target, claimName)
err = action.Run(&c, creds, setOut(cmd.OutOrStdout()))
err = action.Run(&c, creds, setOut(cmd.OutOrStdout()), opRelocator)
if actionDef := c.Bundle.Actions[target]; !actionDef.Modifies {
// Do not store a claim for non-mutating actions.
return err
Expand Down
15 changes: 11 additions & 4 deletions cmd/duffle/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ action will restart the CNAB image and ask it to query for status. For that
reason, it may need the same credentials used to install.
`
var (
statusDriver string
credentialsFiles []string
statusDriver string
credentialsFiles []string
relocationMapping string
)

cmd := &cobra.Command{
Expand Down Expand Up @@ -62,19 +63,25 @@ reason, it may need the same credentials used to install.
return err
}

driverImpl, err := prepareDriver(statusDriver, "")
driverImpl, err := prepareDriver(statusDriver)
if err != nil {
return err
}

opRelocator, err := makeOpRelocator(relocationMapping)
if err != nil {
return err
}

// TODO: Do we pass new values in here? Or just from Claim?
action := &action.Status{Driver: driverImpl}
fmt.Println("Executing status action in bundle...")
return action.Run(&c, creds, setOut(cmd.OutOrStdout()))
return action.Run(&c, creds, setOut(cmd.OutOrStdout()), opRelocator)
},
}
cmd.Flags().StringVarP(&statusDriver, "driver", "d", "docker", "Specify a driver name")
cmd.Flags().StringArrayVarP(&credentialsFiles, "credentials", "c", []string{}, "Specify credentials to use inside the CNAB bundle. This can be a credentialset name or a path to a file.")
cmd.Flags().StringVarP(&relocationMapping, "relocation-mapping", "m", "", "Path of relocation mapping JSON file")

return cmd
}
Expand Down
1 change: 1 addition & 0 deletions cmd/duffle/testdata/oprelocator/badrelmap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{
1 change: 1 addition & 0 deletions cmd/duffle/testdata/oprelocator/relmap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"example.com/original":"example.com/relocated"}
9 changes: 7 additions & 2 deletions cmd/duffle/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (un *uninstallCmd) run() error {
claim.Parameters = params
}

driverImpl, err := prepareDriver(un.driver, un.relocationMapping)
driverImpl, err := prepareDriver(un.driver)
if err != nil {
return fmt.Errorf("could not prepare driver: %s", err)
}
Expand All @@ -106,12 +106,17 @@ func (un *uninstallCmd) run() error {
return fmt.Errorf("could not load credentials: %s", err)
}

opRelocator, err := makeOpRelocator(un.relocationMapping)
if err != nil {
return err
}

uninst := &action.Uninstall{
Driver: driverImpl,
}

fmt.Fprintln(un.out, "Executing uninstall action...")
if err := uninst.Run(&claim, creds, setOut(un.out)); err != nil {
if err := uninst.Run(&claim, creds, setOut(un.out), opRelocator); err != nil {
return fmt.Errorf("could not uninstall %q: %s", un.name, err)
}
return claimStorage().Delete(un.name)
Expand Down
9 changes: 7 additions & 2 deletions cmd/duffle/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (up *upgradeCmd) run() error {
return err
}

driverImpl, err := prepareDriver(up.driver, up.relocationMapping)
driverImpl, err := prepareDriver(up.driver)
if err != nil {
return err
}
Expand All @@ -123,10 +123,15 @@ func (up *upgradeCmd) run() error {
}
}

opRelocator, err := makeOpRelocator(up.relocationMapping)
if err != nil {
return err
}

upgr := &action.Upgrade{
Driver: driverImpl,
}
err = upgr.Run(&claim, creds, setOut(up.out))
err = upgr.Run(&claim, creds, setOut(up.out), opRelocator)

// persist the claim, regardless of the success of the upgrade action
persistErr := claimStorage().Store(claim)
Expand Down

0 comments on commit 414c8a6

Please sign in to comment.