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

Commit 4158c4b

Browse files
committed
Docker app run runs only references
A new flag `--cnab-bundle-json` is added for running a CNAB bundle directly. Otherwise, a built app is ran. Signed-off-by: Djordje Lukic <[email protected]>
1 parent ab69992 commit 4158c4b

File tree

5 files changed

+87
-41
lines changed

5 files changed

+87
-41
lines changed

e2e/cnab_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestCallCustomStatusAction(t *testing.T) {
5050
icmd.RunCmd(cmd).Assert(t, icmd.Success)
5151

5252
// docker app install
53-
cmd.Command = dockerCli.Command("app", "run", path.Join(testDir, "bundle.json"), "--name", testCase.name)
53+
cmd.Command = dockerCli.Command("app", "run", "--cnab-bundle-json", path.Join(testDir, "bundle.json"), "--name", testCase.name)
5454
icmd.RunCmd(cmd).Assert(t, icmd.Success)
5555

5656
// docker app uninstall
@@ -78,7 +78,7 @@ func TestCnabParameters(t *testing.T) {
7878
}()
7979

8080
// docker app install
81-
cmd.Command = dockerCli.Command("app", "run", path.Join(testDir, "bundle.json"), "--name", "cnab-parameters",
81+
cmd.Command = dockerCli.Command("app", "run", "--cnab-bundle-json", path.Join(testDir, "bundle.json"), "--name", "cnab-parameters",
8282
"--set", "boolParam=true",
8383
"--set", "stringParam=value",
8484
"--set", "intParam=42",

e2e/commands_test.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,23 @@ func TestInspectApp(t *testing.T) {
177177
})
178178
}
179179

180+
func TestRunOnlyOne(t *testing.T) {
181+
cmd, cleanup := dockerCli.createTestCmd()
182+
defer cleanup()
183+
184+
cmd.Command = dockerCli.Command("app", "run")
185+
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
186+
ExitCode: 1,
187+
Err: `"docker app run" requires exactly 1 argument.`,
188+
})
189+
190+
cmd.Command = dockerCli.Command("app", "run", "--cnab-bundle-json", "bundle.json", "myapp")
191+
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
192+
ExitCode: 1,
193+
Err: `"docker app run" cannot run a bundle and an app image`,
194+
})
195+
}
196+
180197
func TestDockerAppLifecycle(t *testing.T) {
181198
t.Run("withBindMounts", func(t *testing.T) {
182199
testDockerAppLifecycle(t, true)
@@ -189,18 +206,21 @@ func TestDockerAppLifecycle(t *testing.T) {
189206
func testDockerAppLifecycle(t *testing.T, useBindMount bool) {
190207
cmd, cleanup := dockerCli.createTestCmd()
191208
defer cleanup()
192-
appName := strings.Replace(t.Name(), "/", "_", 1)
209+
appName := strings.ToLower(strings.Replace(t.Name(), "/", "_", 1))
193210
tmpDir := fs.NewDir(t, appName)
194211
defer tmpDir.Remove()
195212
// Running a swarm using docker in docker to install the application
196213
// and run the invocation image
197-
swarm := NewContainer("docker:18.09-dind", 2375)
198-
swarm.Start(t)
214+
swarm := NewContainer("docker:19.03.3-dind", 2375)
215+
swarm.Start(t, "-e", "DOCKER_TLS_CERTDIR=")
199216
defer swarm.Stop(t)
200217
initializeDockerAppEnvironment(t, &cmd, tmpDir, swarm, useBindMount)
201218

219+
cmd.Command = dockerCli.Command("app", "build", "--tag", appName, "testdata/simple")
220+
icmd.RunCmd(cmd).Assert(t, icmd.Success)
221+
202222
// Install an illformed Docker Application Package
203-
cmd.Command = dockerCli.Command("app", "run", "testdata/simple/simple.dockerapp", "--set", "web_port=-1", "--name", appName)
223+
cmd.Command = dockerCli.Command("app", "run", appName, "--set", "web_port=-1", "--name", appName)
204224
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
205225
ExitCode: 1,
206226
Err: "error decoding 'Ports': Invalid hostPort: -1",
@@ -222,7 +242,7 @@ func testDockerAppLifecycle(t *testing.T, useBindMount bool) {
222242
})
223243

224244
// Install a Docker Application Package with an existing failed installation is fine
225-
cmd.Command = dockerCli.Command("app", "run", "testdata/simple/simple.dockerapp", "--name", appName)
245+
cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName)
226246
checkContains(t, icmd.RunCmd(cmd).Assert(t, icmd.Success).Combined(),
227247
[]string{
228248
fmt.Sprintf("WARNING: installing over previously failed installation %q", appName),
@@ -243,7 +263,7 @@ func testDockerAppLifecycle(t *testing.T, useBindMount bool) {
243263
})
244264

245265
// Installing again the same application is forbidden
246-
cmd.Command = dockerCli.Command("app", "run", "testdata/simple/simple.dockerapp", "--name", appName)
266+
cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName)
247267
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
248268
ExitCode: 1,
249269
Err: fmt.Sprintf("Installation %q already exists, use 'docker app update' instead", appName),
@@ -315,7 +335,8 @@ func TestCredentials(t *testing.T) {
315335
"--credential", "secret1=foo",
316336
// secret2 deliberately omitted.
317337
"--credential", "secret3=baz",
318-
"--name", "missing", bundle,
338+
"--name", "missing",
339+
"--cnab-bundle-json", bundle,
319340
)
320341
result := icmd.RunCmd(cmd).Assert(t, icmd.Expected{
321342
ExitCode: 1,
@@ -330,7 +351,8 @@ func TestCredentials(t *testing.T) {
330351
"--credential", "secret1=foo",
331352
"--credential", "secret2=bar",
332353
"--credential", "secret3=baz",
333-
"--name", "full", bundle,
354+
"--name", "full",
355+
"--cnab-bundle-json", bundle,
334356
)
335357
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
336358
golden.Assert(t, result.Stdout(), "credential-install-full.golden")
@@ -341,7 +363,8 @@ func TestCredentials(t *testing.T) {
341363
"app", "run",
342364
"--credential-set", "test-creds",
343365
"--credential", "secret3=xyzzy",
344-
"--name", "mixed-credstore", bundle,
366+
"--name", "mixed-credstore",
367+
"--cnab-bundle-json", bundle,
345368
)
346369
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
347370
golden.Assert(t, result.Stdout(), "credential-install-mixed-credstore.golden")
@@ -352,7 +375,8 @@ func TestCredentials(t *testing.T) {
352375
"app", "run",
353376
"--credential-set", tmpDir.Join("local", "test-creds.yaml"),
354377
"--credential", "secret3=xyzzy",
355-
"--name", "mixed-local-cred", bundle,
378+
"--name", "mixed-local-cred",
379+
"--cnab-bundle-json", bundle,
356380
)
357381
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
358382
golden.Assert(t, result.Stdout(), "credential-install-mixed-local-cred.golden")
@@ -364,7 +388,8 @@ func TestCredentials(t *testing.T) {
364388
"--credential-set", "test-creds",
365389
"--credential", "secret1=overload",
366390
"--credential", "secret3=xyzzy",
367-
"--name", "overload", bundle,
391+
"--name", "overload",
392+
"--cnab-bundle-json", bundle,
368393
)
369394
result := icmd.RunCmd(cmd).Assert(t, icmd.Expected{
370395
ExitCode: 1,

e2e/helper_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func runWithDindSwarmAndRegistry(t *testing.T, todo func(dindSwarmAndRegistryInf
4343
// Solution found is: fix the port of the registry to be the same internally and externally
4444
// and run the dind container in the same network namespace: this way 127.0.0.1:<registry-port> both resolves to the registry from the client and from dind
4545

46-
swarm := NewContainer("docker:19.03.2-dind", 2375, "--insecure-registry", fmt.Sprintf("127.0.0.1:%d", registryPort))
46+
swarm := NewContainer("docker:19.03.3-dind", 2375, "--insecure-registry", fmt.Sprintf("127.0.0.1:%d", registryPort))
4747
swarm.Start(t, "--expose", strconv.FormatInt(int64(registryPort), 10),
4848
"-p", fmt.Sprintf("%d:%d", registryPort, registryPort),
4949
"-p", "2375",

internal/cnab/cnab.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"io/ioutil"
77
"os"
8-
"strings"
98

109
"github.com/deislabs/cnab-go/bundle"
1110
"github.com/docker/app/internal"
@@ -22,17 +21,15 @@ type nameKind uint
2221

2322
const (
2423
_ nameKind = iota
25-
nameKindEmpty
26-
nameKindFile
2724
nameKindDir
2825
nameKindReference
2926
)
3027

3128
func getAppNameKind(name string) (string, nameKind) {
3229
if name == "" {
33-
return name, nameKindEmpty
30+
return name, nameKindDir
3431
}
35-
// name can be a bundle.json or bundle.cnab file, or a dockerapp directory
32+
// name can be a dockerapp directory
3633
st, err := os.Stat(name)
3734
if os.IsNotExist(err) {
3835
// try with .dockerapp extension
@@ -47,7 +44,7 @@ func getAppNameKind(name string) (string, nameKind) {
4744
if st.IsDir() {
4845
return name, nameKindDir
4946
}
50-
return name, nameKindFile
47+
return name, nameKindReference
5148
}
5249

5350
func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*bundle.Bundle, string, error) {
@@ -60,7 +57,8 @@ func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*bundle.B
6057
return bndl, "", err
6158
}
6259

63-
func loadBundleFromFile(filename string) (*bundle.Bundle, error) {
60+
// LoadBundleFromFile loads a bundle from a file
61+
func LoadBundleFromFile(filename string) (*bundle.Bundle, error) {
6462
b := &bundle.Bundle{}
6563
data, err := ioutil.ReadFile(filename)
6664
if err != nil {
@@ -74,19 +72,11 @@ func loadBundleFromFile(filename string) (*bundle.Bundle, error) {
7472
// a reference to the bundle if it is found in the bundlestore, and an error.
7573
func ResolveBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, string, error) {
7674
// resolution logic:
77-
// - if there is a docker-app package in working directory, or an http:// / https:// prefix, use packager.Extract result
78-
// - the name has a .json or .cnab extension and refers to an existing file or web resource: load the bundle
79-
// - name matches a bundle name:version stored in the bundle store: use it
75+
// - if there is a docker-app package in working directory or if a directory is given use packager.Extract
8076
// - pull the bundle from the registry and add it to the bundle store
8177
name, kind := getAppNameKind(name)
8278
switch kind {
83-
case nameKindFile:
84-
if strings.HasSuffix(name, internal.AppExtension) {
85-
return extractAndLoadAppBasedBundle(dockerCli, name)
86-
}
87-
bndl, err := loadBundleFromFile(name)
88-
return bndl, "", err
89-
case nameKindDir, nameKindEmpty:
79+
case nameKindDir:
9080
return extractAndLoadAppBasedBundle(dockerCli, name)
9181
case nameKindReference:
9282
bndl, tagRef, err := GetBundle(dockerCli, bundleStore, name)

internal/commands/run.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import (
44
"fmt"
55
"os"
66

7+
"github.com/docker/cli/cli"
8+
79
"github.com/deislabs/cnab-go/action"
10+
"github.com/deislabs/cnab-go/bundle"
811
"github.com/deislabs/cnab-go/credentials"
912
"github.com/docker/app/internal/cnab"
1013
"github.com/docker/app/internal/store"
11-
"github.com/docker/cli/cli"
1214
"github.com/docker/cli/cli/command"
1315
"github.com/docker/docker/pkg/namesgenerator"
1416
"github.com/pkg/errors"
@@ -22,6 +24,7 @@ type runOptions struct {
2224
orchestrator string
2325
kubeNamespace string
2426
stackName string
27+
cnabBundle string
2528
}
2629

2730
const longDescription = `Run an application based on a docker app image.`
@@ -37,36 +40,64 @@ func runCmd(dockerCli command.Cli) *cobra.Command {
3740
Short: "Run an application",
3841
Long: longDescription,
3942
Example: example,
40-
Args: cli.ExactArgs(1),
4143
RunE: func(cmd *cobra.Command, args []string) error {
42-
return runRun(dockerCli, args[0], opts)
44+
if opts.cnabBundle != "" && len(args) != 0 {
45+
return errors.Errorf(
46+
"%q cannot run a bundle and an app image",
47+
cmd.CommandPath(),
48+
)
49+
}
50+
if opts.cnabBundle == "" {
51+
if err := cli.ExactArgs(1)(cmd, args); err != nil {
52+
return err
53+
}
54+
return runDockerApp(dockerCli, args[0], opts)
55+
}
56+
return runCnab(dockerCli, opts)
4357
},
4458
}
4559
opts.parametersOptions.addFlags(cmd.Flags())
4660
opts.credentialOptions.addFlags(cmd.Flags())
4761
cmd.Flags().StringVar(&opts.orchestrator, "orchestrator", "", "Orchestrator to install on (swarm, kubernetes)")
4862
cmd.Flags().StringVar(&opts.kubeNamespace, "namespace", "default", "Kubernetes namespace to install into")
4963
cmd.Flags().StringVar(&opts.stackName, "name", "", "Assign a name to the installation")
64+
cmd.Flags().StringVar(&opts.cnabBundle, "cnab-bundle-json", "", "Run a CNAB bundle instead of a Docker App")
5065

5166
return cmd
5267
}
5368

54-
func runRun(dockerCli command.Cli, appname string, opts runOptions) error {
55-
opts.SetDefaultTargetContext(dockerCli)
56-
57-
bind, err := cnab.RequiredBindMount(opts.targetContext, opts.orchestrator, dockerCli.ContextStore())
69+
func runCnab(dockerCli command.Cli, opts runOptions) error {
70+
bndl, err := cnab.LoadBundleFromFile(opts.cnabBundle)
5871
if err != nil {
59-
return err
72+
return errors.Wrapf(err, "failed to read bundle %q", opts.cnabBundle)
6073
}
61-
bundleStore, installationStore, credentialStore, err := prepareStores(opts.targetContext)
74+
return runBundle(dockerCli, bndl, opts, "")
75+
}
76+
77+
func runDockerApp(dockerCli command.Cli, appname string, opts runOptions) error {
78+
bundleStore, err := prepareBundleStore()
6279
if err != nil {
6380
return err
6481
}
6582

66-
bndl, ref, err := cnab.ResolveBundle(dockerCli, bundleStore, appname)
83+
bndl, ref, err := cnab.GetBundle(dockerCli, bundleStore, appname)
6784
if err != nil {
6885
return errors.Wrapf(err, "Unable to find application %q", appname)
6986
}
87+
return runBundle(dockerCli, bndl, opts, ref.String())
88+
}
89+
90+
func runBundle(dockerCli command.Cli, bndl *bundle.Bundle, opts runOptions, ref string) error {
91+
opts.SetDefaultTargetContext(dockerCli)
92+
93+
bind, err := cnab.RequiredBindMount(opts.targetContext, opts.orchestrator, dockerCli.ContextStore())
94+
if err != nil {
95+
return err
96+
}
97+
_, installationStore, credentialStore, err := prepareStores(opts.targetContext)
98+
if err != nil {
99+
return err
100+
}
70101
if err := bndl.Validate(); err != nil {
71102
return err
72103
}

0 commit comments

Comments
 (0)