Skip to content
This repository has been archived by the owner on Jun 13, 2021. It is now read-only.

Docker app run runs only references #702

Merged
merged 1 commit into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions e2e/cnab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestCallCustomStatusAction(t *testing.T) {
icmd.RunCmd(cmd).Assert(t, icmd.Success)

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

// docker app uninstall
Expand Down Expand Up @@ -78,7 +78,7 @@ func TestCnabParameters(t *testing.T) {
}()

// docker app install
cmd.Command = dockerCli.Command("app", "run", path.Join(testDir, "bundle.json"), "--name", "cnab-parameters",
cmd.Command = dockerCli.Command("app", "run", "--cnab-bundle-json", path.Join(testDir, "bundle.json"), "--name", "cnab-parameters",
"--set", "boolParam=true",
"--set", "stringParam=value",
"--set", "intParam=42",
Expand Down
47 changes: 36 additions & 11 deletions e2e/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,23 @@ func TestInspectApp(t *testing.T) {
})
}

func TestRunOnlyOne(t *testing.T) {
cmd, cleanup := dockerCli.createTestCmd()
defer cleanup()

cmd.Command = dockerCli.Command("app", "run")
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 1,
Err: `"docker app run" requires exactly 1 argument.`,
})

cmd.Command = dockerCli.Command("app", "run", "--cnab-bundle-json", "bundle.json", "myapp")
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 1,
Err: `"docker app run" cannot run a bundle and an app image`,
})
}

func TestDockerAppLifecycle(t *testing.T) {
t.Run("withBindMounts", func(t *testing.T) {
testDockerAppLifecycle(t, true)
Expand All @@ -189,18 +206,21 @@ func TestDockerAppLifecycle(t *testing.T) {
func testDockerAppLifecycle(t *testing.T, useBindMount bool) {
cmd, cleanup := dockerCli.createTestCmd()
defer cleanup()
appName := strings.Replace(t.Name(), "/", "_", 1)
appName := strings.ToLower(strings.Replace(t.Name(), "/", "_", 1))
tmpDir := fs.NewDir(t, appName)
defer tmpDir.Remove()
// Running a swarm using docker in docker to install the application
// and run the invocation image
swarm := NewContainer("docker:18.09-dind", 2375)
swarm.Start(t)
swarm := NewContainer("docker:19.03.3-dind", 2375)
swarm.Start(t, "-e", "DOCKER_TLS_CERTDIR=")
defer swarm.Stop(t)
initializeDockerAppEnvironment(t, &cmd, tmpDir, swarm, useBindMount)

cmd.Command = dockerCli.Command("app", "build", "--tag", appName, "testdata/simple")
icmd.RunCmd(cmd).Assert(t, icmd.Success)

// Install an illformed Docker Application Package
cmd.Command = dockerCli.Command("app", "run", "testdata/simple/simple.dockerapp", "--set", "web_port=-1", "--name", appName)
cmd.Command = dockerCli.Command("app", "run", appName, "--set", "web_port=-1", "--name", appName)
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 1,
Err: "error decoding 'Ports': Invalid hostPort: -1",
Expand All @@ -222,7 +242,7 @@ func testDockerAppLifecycle(t *testing.T, useBindMount bool) {
})

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

// Installing again the same application is forbidden
cmd.Command = dockerCli.Command("app", "run", "testdata/simple/simple.dockerapp", "--name", appName)
cmd.Command = dockerCli.Command("app", "run", appName, "--name", appName)
icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 1,
Err: fmt.Sprintf("Installation %q already exists, use 'docker app update' instead", appName),
Expand Down Expand Up @@ -315,7 +335,8 @@ func TestCredentials(t *testing.T) {
"--credential", "secret1=foo",
// secret2 deliberately omitted.
"--credential", "secret3=baz",
"--name", "missing", bundle,
"--name", "missing",
"--cnab-bundle-json", bundle,
)
result := icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 1,
Expand All @@ -330,7 +351,8 @@ func TestCredentials(t *testing.T) {
"--credential", "secret1=foo",
"--credential", "secret2=bar",
"--credential", "secret3=baz",
"--name", "full", bundle,
"--name", "full",
"--cnab-bundle-json", bundle,
)
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
golden.Assert(t, result.Stdout(), "credential-install-full.golden")
Expand All @@ -341,7 +363,8 @@ func TestCredentials(t *testing.T) {
"app", "run",
"--credential-set", "test-creds",
"--credential", "secret3=xyzzy",
"--name", "mixed-credstore", bundle,
"--name", "mixed-credstore",
"--cnab-bundle-json", bundle,
)
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
golden.Assert(t, result.Stdout(), "credential-install-mixed-credstore.golden")
Expand All @@ -352,7 +375,8 @@ func TestCredentials(t *testing.T) {
"app", "run",
"--credential-set", tmpDir.Join("local", "test-creds.yaml"),
"--credential", "secret3=xyzzy",
"--name", "mixed-local-cred", bundle,
"--name", "mixed-local-cred",
"--cnab-bundle-json", bundle,
)
result := icmd.RunCmd(cmd).Assert(t, icmd.Success)
golden.Assert(t, result.Stdout(), "credential-install-mixed-local-cred.golden")
Expand All @@ -364,7 +388,8 @@ func TestCredentials(t *testing.T) {
"--credential-set", "test-creds",
"--credential", "secret1=overload",
"--credential", "secret3=xyzzy",
"--name", "overload", bundle,
"--name", "overload",
"--cnab-bundle-json", bundle,
)
result := icmd.RunCmd(cmd).Assert(t, icmd.Expected{
ExitCode: 1,
Expand Down
2 changes: 1 addition & 1 deletion e2e/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func runWithDindSwarmAndRegistry(t *testing.T, todo func(dindSwarmAndRegistryInf
// Solution found is: fix the port of the registry to be the same internally and externally
// 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

swarm := NewContainer("docker:19.03.2-dind", 2375, "--insecure-registry", fmt.Sprintf("127.0.0.1:%d", registryPort))
swarm := NewContainer("docker:19.03.3-dind", 2375, "--insecure-registry", fmt.Sprintf("127.0.0.1:%d", registryPort))
swarm.Start(t, "--expose", strconv.FormatInt(int64(registryPort), 10),
"-p", fmt.Sprintf("%d:%d", registryPort, registryPort),
"-p", "2375",
Expand Down
24 changes: 7 additions & 17 deletions internal/cnab/cnab.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"io/ioutil"
"os"
"strings"

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

const (
_ nameKind = iota
nameKindEmpty
nameKindFile
nameKindDir
nameKindReference
)

func getAppNameKind(name string) (string, nameKind) {
if name == "" {
return name, nameKindEmpty
return name, nameKindDir
}
// name can be a bundle.json or bundle.cnab file, or a dockerapp directory
// name can be a dockerapp directory
st, err := os.Stat(name)
if os.IsNotExist(err) {
// try with .dockerapp extension
Expand All @@ -47,7 +44,7 @@ func getAppNameKind(name string) (string, nameKind) {
if st.IsDir() {
return name, nameKindDir
}
return name, nameKindFile
return name, nameKindReference
}

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

func loadBundleFromFile(filename string) (*bundle.Bundle, error) {
// LoadBundleFromFile loads a bundle from a file
func LoadBundleFromFile(filename string) (*bundle.Bundle, error) {
b := &bundle.Bundle{}
data, err := ioutil.ReadFile(filename)
if err != nil {
Expand All @@ -74,19 +72,11 @@ func loadBundleFromFile(filename string) (*bundle.Bundle, error) {
// a reference to the bundle if it is found in the bundlestore, and an error.
func ResolveBundle(dockerCli command.Cli, bundleStore appstore.BundleStore, name string) (*bundle.Bundle, string, error) {
// resolution logic:
// - if there is a docker-app package in working directory, or an http:// / https:// prefix, use packager.Extract result
// - the name has a .json or .cnab extension and refers to an existing file or web resource: load the bundle
// - name matches a bundle name:version stored in the bundle store: use it
// - if there is a docker-app package in working directory or if a directory is given use packager.Extract
// - pull the bundle from the registry and add it to the bundle store
name, kind := getAppNameKind(name)
switch kind {
case nameKindFile:
if strings.HasSuffix(name, internal.AppExtension) {
return extractAndLoadAppBasedBundle(dockerCli, name)
}
bndl, err := loadBundleFromFile(name)
return bndl, "", err
case nameKindDir, nameKindEmpty:
case nameKindDir:
return extractAndLoadAppBasedBundle(dockerCli, name)
case nameKindReference:
bndl, tagRef, err := GetBundle(dockerCli, bundleStore, name)
Expand Down
51 changes: 41 additions & 10 deletions internal/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import (
"fmt"
"os"

"github.com/docker/cli/cli"

"github.com/deislabs/cnab-go/action"
"github.com/deislabs/cnab-go/bundle"
"github.com/deislabs/cnab-go/credentials"
"github.com/docker/app/internal/cnab"
"github.com/docker/app/internal/store"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/pkg/errors"
Expand All @@ -22,6 +24,7 @@ type runOptions struct {
orchestrator string
kubeNamespace string
stackName string
cnabBundle string
}

const longDescription = `Run an application based on a docker app image.`
Expand All @@ -37,36 +40,64 @@ func runCmd(dockerCli command.Cli) *cobra.Command {
Short: "Run an application",
Long: longDescription,
Example: example,
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runRun(dockerCli, args[0], opts)
if opts.cnabBundle != "" && len(args) != 0 {
return errors.Errorf(
"%q cannot run a bundle and an app image",
cmd.CommandPath(),
)
}
if opts.cnabBundle == "" {
silvin-lubecki marked this conversation as resolved.
Show resolved Hide resolved
if err := cli.ExactArgs(1)(cmd, args); err != nil {
return err
}
return runDockerApp(dockerCli, args[0], opts)
}
return runCnab(dockerCli, opts)
},
}
opts.parametersOptions.addFlags(cmd.Flags())
opts.credentialOptions.addFlags(cmd.Flags())
cmd.Flags().StringVar(&opts.orchestrator, "orchestrator", "", "Orchestrator to install on (swarm, kubernetes)")
cmd.Flags().StringVar(&opts.kubeNamespace, "namespace", "default", "Kubernetes namespace to install into")
cmd.Flags().StringVar(&opts.stackName, "name", "", "Assign a name to the installation")
cmd.Flags().StringVar(&opts.cnabBundle, "cnab-bundle-json", "", "Run a CNAB bundle instead of a Docker App")

return cmd
}

func runRun(dockerCli command.Cli, appname string, opts runOptions) error {
opts.SetDefaultTargetContext(dockerCli)

bind, err := cnab.RequiredBindMount(opts.targetContext, opts.orchestrator, dockerCli.ContextStore())
func runCnab(dockerCli command.Cli, opts runOptions) error {
bndl, err := cnab.LoadBundleFromFile(opts.cnabBundle)
if err != nil {
return err
return errors.Wrapf(err, "failed to read bundle %q", opts.cnabBundle)
}
bundleStore, installationStore, credentialStore, err := prepareStores(opts.targetContext)
return runBundle(dockerCli, bndl, opts, "")
}

func runDockerApp(dockerCli command.Cli, appname string, opts runOptions) error {
bundleStore, err := prepareBundleStore()
if err != nil {
return err
}

bndl, ref, err := cnab.ResolveBundle(dockerCli, bundleStore, appname)
bndl, ref, err := cnab.GetBundle(dockerCli, bundleStore, appname)
if err != nil {
return errors.Wrapf(err, "Unable to find application %q", appname)
}
return runBundle(dockerCli, bndl, opts, ref.String())
}

func runBundle(dockerCli command.Cli, bndl *bundle.Bundle, opts runOptions, ref string) error {
opts.SetDefaultTargetContext(dockerCli)

bind, err := cnab.RequiredBindMount(opts.targetContext, opts.orchestrator, dockerCli.ContextStore())
if err != nil {
return err
}
_, installationStore, credentialStore, err := prepareStores(opts.targetContext)
if err != nil {
return err
}
if err := bndl.Validate(); err != nil {
return err
}
Expand Down