Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit d0cc6fb

Browse files
committed
apply relocation map when creating the installation
Allow to replace local images by the one from the registry using stored `relocation-map.json` when creating the installation. The `bundle.json` is not modified. Signed-off-by: Yves Brissaud <[email protected]>
1 parent d48d5cc commit d0cc6fb

File tree

8 files changed

+225
-25
lines changed

8 files changed

+225
-25
lines changed

e2e/relocation_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"testing"
88

99
"github.com/docker/app/internal/relocated"
10+
"gotest.tools/assert/cmp"
1011

1112
"gotest.tools/assert"
1213
"gotest.tools/icmd"
@@ -39,3 +40,83 @@ func TestRelocationMapCreatedOnPull(t *testing.T) {
3940
assert.NilError(t, err)
4041
})
4142
}
43+
44+
func TestRelocationMapRun(t *testing.T) {
45+
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
46+
cmd := info.configuredCmd
47+
cfg := getDockerConfigDir(t, cmd)
48+
49+
path := filepath.Join("testdata", "local")
50+
ref := info.registryAddress + "/test/local:a-tag"
51+
bundlePath := filepath.Join(cfg, "app", "bundles", strings.Replace(info.registryAddress, ":", "_", 1), "test", "local", "_tags", "a-tag")
52+
53+
// Given a pushed application
54+
build(t, cmd, dockerCli, ref, path)
55+
cmd.Command = dockerCli.Command("app", "push", ref)
56+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
57+
// And given application files are remove
58+
assert.NilError(t, os.RemoveAll(bundlePath))
59+
_, err := os.Stat(filepath.Join(bundlePath, relocated.BundleFilename))
60+
assert.Assert(t, os.IsNotExist(err))
61+
// And given local images are removed
62+
cmd.Command = dockerCli.Command("rmi", "web", "local:1.1.0-beta1-invoc", "worker")
63+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
64+
// And given application is pulled from the registry
65+
cmd.Command = dockerCli.Command("app", "pull", ref)
66+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
67+
68+
t.Run("with-relocation-map", func(t *testing.T) {
69+
name := "test-relocation-map-run-with-relocation-map"
70+
// When application is run
71+
cmd.Command = dockerCli.Command("app", "run", "--name", name, ref)
72+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
73+
74+
// Then the application is running
75+
cmd.Command = dockerCli.Command("app", "ls")
76+
assert.Check(t, cmp.Contains(icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(), name))
77+
78+
cmd.Command = dockerCli.Command("app", "rm", name)
79+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
80+
})
81+
82+
t.Run("without-relocation-map", func(t *testing.T) {
83+
name := "test-relocation-map-run-without-relocation-map"
84+
// And given the relocation map is removed after the pull
85+
assert.NilError(t, os.RemoveAll(filepath.Join(bundlePath, relocated.RelocationMapFilename)))
86+
87+
// Then the application cannot be run
88+
cmd.Command = dockerCli.Command("app", "run", "--name", name, ref)
89+
icmd.RunCmd(cmd).Assert(t, icmd.Expected{ExitCode: 1})
90+
})
91+
})
92+
}
93+
94+
func TestRelocationMapOnInspect(t *testing.T) {
95+
runWithDindSwarmAndRegistry(t, func(info dindSwarmAndRegistryInfo) {
96+
cmd := info.configuredCmd
97+
cfg := getDockerConfigDir(t, cmd)
98+
99+
path := filepath.Join("testdata", "local")
100+
ref := info.registryAddress + "/test/local:a-tag"
101+
bundlePath := filepath.Join(cfg, "app", "bundles", strings.Replace(info.registryAddress, ":", "_", 1), "test", "local", "_tags", "a-tag")
102+
103+
// Given a pushed application
104+
build(t, cmd, dockerCli, ref, path)
105+
cmd.Command = dockerCli.Command("app", "push", ref)
106+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
107+
// And given application files are remove
108+
assert.NilError(t, os.RemoveAll(bundlePath))
109+
_, err := os.Stat(filepath.Join(bundlePath, relocated.BundleFilename))
110+
assert.Assert(t, os.IsNotExist(err))
111+
// And given local images are removed
112+
cmd.Command = dockerCli.Command("rmi", "web", "local:1.1.0-beta1-invoc", "worker")
113+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
114+
// And given application is pulled from the registry
115+
cmd.Command = dockerCli.Command("app", "pull", ref)
116+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
117+
118+
// When inspect the image
119+
cmd.Command = dockerCli.Command("app", "image", "inspect", ref)
120+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
121+
})
122+
}

internal/bundle/parameters.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ func MergeBundleParameters(installation *store.Installation, ops ...MergeBundleO
114114
params: userParams,
115115
stderr: os.Stderr,
116116
}
117+
117118
for _, op := range ops {
118119
if err := op(cfg); err != nil {
119120
return err

internal/bundle/parameters_test.go

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"strings"
88
"testing"
99

10+
"github.com/docker/app/internal/relocated"
11+
1012
"github.com/deislabs/cnab-go/bundle"
1113
"github.com/deislabs/cnab-go/bundle/definition"
1214
"github.com/deislabs/cnab-go/claim"
@@ -113,18 +115,18 @@ func withParameterAndValues(name, typ string, allowedValues []interface{}) bundl
113115
}
114116
}
115117

116-
func prepareBundle(ops ...bundleOperator) *bundle.Bundle {
117-
b := &bundle.Bundle{}
118+
func prepareBundle(ops ...bundleOperator) *relocated.Bundle {
119+
b := relocated.FromBundle(&bundle.Bundle{})
118120
for _, op := range ops {
119-
op(b)
121+
op(b.Bundle)
120122
}
121123
return b
122124
}
123125

124126
func TestWithOrchestratorParameters(t *testing.T) {
125127
testCases := []struct {
126128
name string
127-
bundle *bundle.Bundle
129+
bundle *relocated.Bundle
128130
expected map[string]string
129131
}{
130132
{
@@ -146,7 +148,7 @@ func TestWithOrchestratorParameters(t *testing.T) {
146148
t.Run(testCase.name, func(t *testing.T) {
147149
actual := map[string]string{}
148150
err := WithOrchestratorParameters("kubernetes", "my-namespace")(&MergeBundleConfig{
149-
bundle: testCase.bundle,
151+
bundle: testCase.bundle.Bundle,
150152
params: actual,
151153
})
152154
assert.NilError(t, err)
@@ -166,7 +168,10 @@ func TestMergeBundleParameters(t *testing.T) {
166168
return nil
167169
}
168170
bundle := prepareBundle(withParameterAndDefault("param", "string", "default"))
169-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
171+
i := &store.Installation{
172+
Claim: claim.Claim{Bundle: bundle.Bundle},
173+
RelocationMap: bundle.RelocationMap,
174+
}
170175
err := MergeBundleParameters(i,
171176
first,
172177
second,
@@ -180,7 +185,10 @@ func TestMergeBundleParameters(t *testing.T) {
180185

181186
t.Run("Default values", func(t *testing.T) {
182187
bundle := prepareBundle(withParameterAndDefault("param", "string", "default"))
183-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
188+
i := &store.Installation{
189+
Claim: claim.Claim{Bundle: bundle.Bundle},
190+
RelocationMap: bundle.RelocationMap,
191+
}
184192
err := MergeBundleParameters(i)
185193
assert.NilError(t, err)
186194
expected := map[string]interface{}{
@@ -195,7 +203,10 @@ func TestMergeBundleParameters(t *testing.T) {
195203
return nil
196204
}
197205
bundle := prepareBundle(withParameter("param", "integer"))
198-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
206+
i := &store.Installation{
207+
Claim: claim.Claim{Bundle: bundle.Bundle},
208+
RelocationMap: bundle.RelocationMap,
209+
}
199210
err := MergeBundleParameters(i, withIntValue)
200211
assert.NilError(t, err)
201212
expected := map[string]interface{}{
@@ -206,7 +217,10 @@ func TestMergeBundleParameters(t *testing.T) {
206217

207218
t.Run("Default values", func(t *testing.T) {
208219
bundle := prepareBundle(withParameterAndDefault("param", "string", "default"))
209-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
220+
i := &store.Installation{
221+
Claim: claim.Claim{Bundle: bundle.Bundle},
222+
RelocationMap: bundle.RelocationMap,
223+
}
210224
err := MergeBundleParameters(i)
211225
assert.NilError(t, err)
212226
expected := map[string]interface{}{
@@ -221,7 +235,10 @@ func TestMergeBundleParameters(t *testing.T) {
221235
return nil
222236
}
223237
bundle := prepareBundle()
224-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
238+
i := &store.Installation{
239+
Claim: claim.Claim{Bundle: bundle.Bundle},
240+
RelocationMap: bundle.RelocationMap,
241+
}
225242
buf := new(bytes.Buffer)
226243
err := MergeBundleParameters(i, withUndefined, WithErrorWriter(buf))
227244
assert.NilError(t, err)
@@ -239,7 +256,10 @@ func TestMergeBundleParameters(t *testing.T) {
239256
return nil
240257
}
241258
bundle := prepareBundle()
242-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
259+
i := &store.Installation{
260+
Claim: claim.Claim{Bundle: bundle.Bundle},
261+
RelocationMap: bundle.RelocationMap,
262+
}
243263
err := MergeBundleParameters(i, withUndefined, withStdErr)
244264
assert.NilError(t, err)
245265
assert.Equal(t, w.String(), "Warning: parameter \"param\" is not defined in the bundle\n")
@@ -251,7 +271,10 @@ func TestMergeBundleParameters(t *testing.T) {
251271
return nil
252272
}
253273
bundle := prepareBundle(withParameter("param", "integer"))
254-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
274+
i := &store.Installation{
275+
Claim: claim.Claim{Bundle: bundle.Bundle},
276+
RelocationMap: bundle.RelocationMap,
277+
}
255278
err := MergeBundleParameters(i, withIntValue)
256279
assert.ErrorContains(t, err, "invalid value for parameter")
257280
})
@@ -262,7 +285,10 @@ func TestMergeBundleParameters(t *testing.T) {
262285
return nil
263286
}
264287
bundle := prepareBundle(withParameterAndValues("param", "string", []interface{}{"valid"}))
265-
i := &store.Installation{Claim: claim.Claim{Bundle: bundle}}
288+
i := &store.Installation{
289+
Claim: claim.Claim{Bundle: bundle.Bundle},
290+
RelocationMap: bundle.RelocationMap,
291+
}
266292
err := MergeBundleParameters(i, withInvalidValue)
267293
assert.ErrorContains(t, err, "invalid value for parameter")
268294
})

internal/commands/image/inspect.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,10 @@ func runInspect(dockerCli command.Cli, appname string, opts inspectOptions) erro
6666
if err != nil {
6767
return err
6868
}
69-
installation, err := appstore.NewInstallation("custom-action", ref.String())
69+
installation, err := appstore.NewInstallation("custom-action", ref.String(), bndl)
7070
if err != nil {
7171
return err
7272
}
73-
installation.Bundle = bndl.Bundle
7473
driverImpl, errBuf, err := cnab.SetupDriver(installation, dockerCli, opts.InstallerContextOptions, os.Stdout)
7574
if err != nil {
7675
return err

internal/commands/image/render.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,10 @@ func prepareCustomAction(actionName string, dockerCli command.Cli, appname strin
9191
if err != nil {
9292
return nil, nil, nil, errors.Wrapf(err, "could not render %q: no such App image", appname)
9393
}
94-
installation, err := appstore.NewInstallation("custom-action", ref.String())
94+
installation, err := appstore.NewInstallation("custom-action", ref.String(), bundle)
9595
if err != nil {
9696
return nil, nil, nil, err
9797
}
98-
installation.Bundle = bundle.Bundle
9998

10099
if err := bdl.MergeBundleParameters(installation,
101100
bdl.WithFileParameters(opts.ParametersFiles),

internal/commands/run.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func runBundle(dockerCli command.Cli, bndl *relocated.Bundle, opts runOptions, r
121121
} else {
122122
logrus.Debug(err)
123123
}
124-
installation, err := store.NewInstallation(installationName, ref)
124+
installation, err := store.NewInstallation(installationName, ref, bndl)
125125
if err != nil {
126126
return err
127127
}
@@ -130,7 +130,6 @@ func runBundle(dockerCli command.Cli, bndl *relocated.Bundle, opts runOptions, r
130130
if err != nil {
131131
return err
132132
}
133-
installation.Bundle = bndl.Bundle
134133

135134
if err := bdl.MergeBundleParameters(installation,
136135
bdl.WithFileParameters(opts.ParametersFiles),

internal/store/installation.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import (
44
"encoding/json"
55
"fmt"
66

7+
"github.com/docker/app/internal/relocated"
8+
9+
"github.com/docker/cnab-to-oci/relocation"
10+
711
"github.com/deislabs/cnab-go/claim"
812
"github.com/deislabs/cnab-go/utils/crud"
913
)
@@ -20,18 +24,24 @@ type InstallationStore interface {
2024
// It persists the result of an installation and its parameters and context.
2125
type Installation struct {
2226
claim.Claim
23-
Reference string `json:"reference,omitempty"`
27+
RelocationMap relocation.ImageRelocationMap
28+
Reference string `json:"reference,omitempty"`
2429
}
2530

26-
func NewInstallation(name string, reference string) (*Installation, error) {
31+
func NewInstallation(name string, reference string, bndl *relocated.Bundle) (*Installation, error) {
2732
c, err := claim.New(name)
2833
if err != nil {
2934
return nil, err
3035
}
31-
return &Installation{
32-
Claim: *c,
33-
Reference: reference,
34-
}, nil
36+
c.Bundle = bndl.Bundle
37+
i := &Installation{
38+
Claim: *c,
39+
Reference: reference,
40+
RelocationMap: bndl.RelocationMap,
41+
}
42+
i.applyRelocationMap()
43+
44+
return i, nil
3545
}
3646

3747
// SetParameter sets the parameter value if the installation bundle has
@@ -42,6 +52,21 @@ func (i Installation) SetParameter(name string, value string) {
4252
}
4353
}
4454

55+
func (i *Installation) applyRelocationMap() {
56+
for idx, def := range i.Bundle.InvocationImages {
57+
if img, ok := i.RelocationMap[def.Image]; ok {
58+
def.Image = img
59+
i.Bundle.InvocationImages[idx] = def
60+
}
61+
}
62+
for name, def := range i.Bundle.Images {
63+
if img, ok := i.RelocationMap[def.Image]; ok {
64+
def.Image = img
65+
i.Bundle.Images[name] = def
66+
}
67+
}
68+
}
69+
4570
var _ InstallationStore = &installationStore{}
4671

4772
type installationStore struct {

0 commit comments

Comments
 (0)