Skip to content

Commit

Permalink
WIP: Port to use docker CLI plugins framework
Browse files Browse the repository at this point in the history
docker/cli#1564

Since this pushes the actual command down to `docker-app app`, adjust the e2e
tests with:

    $ sed -Eie 's/icmd\.RunCommand\(dockerApp,/icmd.RunCommand(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/exec.Command\(dockerApp,/exec.Command(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/\[\]string\{dockerApp,/\[\]string\{dockerApp, "app",/g' e2e/*.go

(which might be precisely equivalent to `sed -Eie 's/dockerApp,/dockerApp, "app",/g' e2e/*.go`)

Finally, the idiom in docker/cli (for better or worse) is to include an "Error:
" prefix in the error string itself, rather than injecting it when printing.
Since CLI plugins follow the behaviour of the CLI here it is necessary to
prepend "Error: " to some messages. I've only done exactly those necessary to
pass the e2e tests, a fuller audit is very likely required.

Signed-off-by: Ian Campbell <ijc@docker.com>
  • Loading branch information
Ian Campbell committed Dec 13, 2018
1 parent d5ce5f2 commit e57d8cb
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 85 deletions.
6 changes: 4 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/docker-app/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func helmCmd() *cobra.Command {
defer app.Cleanup()
d := cliopts.ConvertKVStringsToMap(helmEnv)
if stackVersion != helm.V1Beta1 && stackVersion != helm.V1Beta2 {
return fmt.Errorf("invalid stack version %q (accepted values: %s, %s)", stackVersion, helm.V1Beta1, helm.V1Beta2)
return fmt.Errorf("Error: invalid stack version %q (accepted values: %s, %s)", stackVersion, helm.V1Beta1, helm.V1Beta2)
}
return helm.Helm(app, d, helmRender, stackVersion)
},
Expand Down
23 changes: 10 additions & 13 deletions cmd/docker-app/main.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package main

import (
"os"

"github.com/docker/app/internal"
cliplugins "github.com/docker/cli/cli-plugins"
"github.com/docker/cli/cli-plugins/plugin"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/pkg/term"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

func main() {
// Set terminal emulation based on platform as required.
stdin, stdout, stderr := term.StdStreams()
logrus.SetOutput(stderr)

dockerCli := command.NewDockerCli(stdin, stdout, stderr, nil)
cmd := newRootCmd(dockerCli)
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
plugin.Run(func(dockerCli command.Cli) *cobra.Command {
return newRootCmd(dockerCli)
}, cliplugins.Metadata{
Vendor: "Docker Inc.",
Version: internal.Version,
})
}
27 changes: 4 additions & 23 deletions cmd/docker-app/root.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,22 @@
package main

import (
"fmt"

"github.com/docker/app/internal"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
cliconfig "github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/debug"
cliflags "github.com/docker/cli/cli/flags"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

// rootCmd represents the base command when called without any subcommands
// FIXME(vdemeester) use command.Cli interface
func newRootCmd(dockerCli *command.DockerCli) *cobra.Command {
opts := cliflags.NewClientOptions()
var flags *pflag.FlagSet

func newRootCmd(dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "docker-app",
Short: "Docker Application Packages",
Long: `Build and deploy Docker Application Packages.`,
SilenceUsage: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
opts.Common.SetDefaultOptions(flags)
dockerPreRun(opts)
return dockerCli.Initialize(opts)
},
Version: fmt.Sprintf("%s, build %s", internal.Version, internal.GitCommit),
Use: "app",
Short: "Docker Application Packages",
Long: `Build and deploy Docker Application Packages.`,
}
cli.SetupRootCommand(cmd)
flags = cmd.Flags()
flags.BoolP("version", "v", false, "Print version information")
opts.Common.InstallFlags(flags)
cmd.SetVersionTemplate("docker-app version {{.Version}}\n")
addCommands(cmd, dockerCli)
return cmd
}
Expand Down
82 changes: 41 additions & 41 deletions e2e/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func testRenderApp(appPath string, env ...string) func(*testing.T) {
data, err := ioutil.ReadFile(filepath.Join(appPath, "env.yml"))
assert.NilError(t, err)
assert.NilError(t, yaml.Unmarshal(data, &envSettings))
args := []string{dockerApp, "render", filepath.Join(appPath, "my.dockerapp"),
args := []string{dockerApp, "app", "render", filepath.Join(appPath, "my.dockerapp"),
"-f", filepath.Join(appPath, "settings-0.yml"),
}
for k, v := range envSettings {
Expand All @@ -79,10 +79,10 @@ func testRenderApp(appPath string, env ...string) func(*testing.T) {

func TestRenderFormatters(t *testing.T) {
appPath := filepath.Join("testdata", "fork", "simple.dockerapp")
result := icmd.RunCommand(dockerApp, "render", "--formatter", "json", appPath).Assert(t, icmd.Success)
result := icmd.RunCommand(dockerApp, "app", "render", "--formatter", "json", appPath).Assert(t, icmd.Success)
assert.Assert(t, golden.String(result.Stdout(), "expected-json-render.golden"))

result = icmd.RunCommand(dockerApp, "render", "--formatter", "yaml", appPath).Assert(t, icmd.Success)
result = icmd.RunCommand(dockerApp, "app", "render", "--formatter", "yaml", appPath).Assert(t, icmd.Success)
assert.Assert(t, golden.String(result.Stdout(), "expected-yaml-render.golden"))
}

Expand Down Expand Up @@ -119,7 +119,7 @@ maintainers:
dirName := internal.DirNameFromAppName(testAppName)
defer os.RemoveAll(dirName)

icmd.RunCommand(dockerApp, "init", testAppName,
icmd.RunCommand(dockerApp, "app", "init", testAppName,
"-c", dir.Join(internal.ComposeFileName),
"-d", "my cool app",
"-m", "bob",
Expand All @@ -135,10 +135,10 @@ maintainers:
assert.Assert(t, fs.Equal(dirName, manifest))

// validate metadata with JSON Schema
icmd.RunCommand(dockerApp, "validate", testAppName).Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "validate", testAppName).Assert(t, icmd.Success)

// test single-file init
icmd.RunCommand(dockerApp, "init", "tac",
icmd.RunCommand(dockerApp, "app", "init", "tac",
"-c", dir.Join(internal.ComposeFileName),
"-d", "my cool app",
"-m", "bob",
Expand All @@ -150,8 +150,8 @@ maintainers:
assert.NilError(t, err)
assert.Assert(t, golden.Bytes(appData, "init-singlefile.dockerapp"))
// Check various commands work on single-file app package
icmd.RunCommand(dockerApp, "inspect", "tac").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "render", "tac").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "inspect", "tac").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "render", "tac").Assert(t, icmd.Success)
}

func TestDetectApp(t *testing.T) {
Expand All @@ -165,19 +165,19 @@ func TestDetectApp(t *testing.T) {
)
defer dir.Remove()
icmd.RunCmd(icmd.Cmd{
Command: []string{dockerApp, "inspect"},
Command: []string{dockerApp, "app", "inspect"},
Dir: dir.Path(),
}).Assert(t, icmd.Success)
icmd.RunCmd(icmd.Cmd{
Command: []string{dockerApp, "inspect"},
Command: []string{dockerApp, "app", "inspect"},
Dir: dir.Join("helm.dockerapp"),
}).Assert(t, icmd.Success)
icmd.RunCmd(icmd.Cmd{
Command: []string{dockerApp, "inspect", "."},
Command: []string{dockerApp, "app", "inspect", "."},
Dir: dir.Join("helm.dockerapp"),
}).Assert(t, icmd.Success)
result := icmd.RunCmd(icmd.Cmd{
Command: []string{dockerApp, "inspect"},
Command: []string{dockerApp, "app", "inspect"},
Dir: dir.Join("render"),
})
result.Assert(t, icmd.Expected{
Expand All @@ -191,23 +191,23 @@ func TestPack(t *testing.T) {
tempDir, err := ioutil.TempDir("", "dockerapp")
assert.NilError(t, err)
defer os.RemoveAll(tempDir)
icmd.RunCommand(dockerApp, "pack", "testdata/helm", "-o", filepath.Join(tempDir, "test.dockerapp")).Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "pack", "testdata/helm", "-o", filepath.Join(tempDir, "test.dockerapp")).Assert(t, icmd.Success)
// check that our commands run on the packed version
icmd.RunCommand(dockerApp, "inspect", filepath.Join(tempDir, "test")).Assert(t, icmd.Expected{
icmd.RunCommand(dockerApp, "app", "inspect", filepath.Join(tempDir, "test")).Assert(t, icmd.Expected{
Out: "myapp",
})
icmd.RunCommand(dockerApp, "render", filepath.Join(tempDir, "test")).Assert(t, icmd.Expected{
icmd.RunCommand(dockerApp, "app", "render", filepath.Join(tempDir, "test")).Assert(t, icmd.Expected{
Out: "nginx",
})
icmd.RunCmd(icmd.Cmd{
Command: []string{dockerApp, "helm", "test"},
Command: []string{dockerApp, "app", "helm", "test"},
Dir: tempDir,
}).Assert(t, icmd.Success)
_, err = os.Stat(filepath.Join(tempDir, "test.chart", "Chart.yaml"))
assert.NilError(t, err)
assert.NilError(t, os.Mkdir(filepath.Join(tempDir, "output"), 0755))
icmd.RunCmd(icmd.Cmd{
Command: []string{dockerApp, "unpack", "test", "-o", "output"},
Command: []string{dockerApp, "app", "unpack", "test", "-o", "output"},
Dir: tempDir,
}).Assert(t, icmd.Success)
_, err = os.Stat(filepath.Join(tempDir, "output", "test.dockerapp", "docker-compose.yml"))
Expand All @@ -224,7 +224,7 @@ func testHelm(version string) func(*testing.T) {
return func(t *testing.T) {
dir := fs.NewDir(t, "testHelmBinary", fs.FromDir("testdata"))
defer dir.Remove()
cmd := []string{dockerApp, "helm", "helm", "-s", "myapp.nginx_version=2"}
cmd := []string{dockerApp, "app", "helm", "helm", "-s", "myapp.nginx_version=2"}
if version != "" {
cmd = append(cmd, "--stack-version", version)
}
Expand All @@ -243,31 +243,31 @@ func testHelm(version string) func(*testing.T) {
}

func TestHelmInvalidStackVersion(t *testing.T) {
icmd.RunCommand(dockerApp, "helm", "testdata/helm", "--stack-version", "foobar").Assert(t, icmd.Expected{
icmd.RunCommand(dockerApp, "app", "helm", "testdata/helm", "--stack-version", "foobar").Assert(t, icmd.Expected{
ExitCode: 1,
Err: `Error: invalid stack version "foobar" (accepted values: v1beta1, v1beta2)`,
})
}

func TestSplitMerge(t *testing.T) {
icmd.RunCommand(dockerApp, "merge", "testdata/render/envvariables/my.dockerapp", "-o", "remerged.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "merge", "testdata/render/envvariables/my.dockerapp", "-o", "remerged.dockerapp").Assert(t, icmd.Success)
defer os.Remove("remerged.dockerapp")
// test that inspect works on single-file
result := icmd.RunCommand(dockerApp, "inspect", "remerged").Assert(t, icmd.Success)
result := icmd.RunCommand(dockerApp, "app", "inspect", "remerged").Assert(t, icmd.Success)
assert.Assert(t, golden.String(result.Combined(), "envvariables-inspect.golden"))
// split it
icmd.RunCommand(dockerApp, "split", "remerged", "-o", "split.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "split", "remerged", "-o", "split.dockerapp").Assert(t, icmd.Success)
defer os.RemoveAll("split.dockerapp")
result = icmd.RunCommand(dockerApp, "inspect", "remerged").Assert(t, icmd.Success)
result = icmd.RunCommand(dockerApp, "app", "inspect", "remerged").Assert(t, icmd.Success)
assert.Assert(t, golden.String(result.Combined(), "envvariables-inspect.golden"))
// test inplace
icmd.RunCommand(dockerApp, "merge", "split").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "split", "split").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "merge", "split").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "split", "split").Assert(t, icmd.Success)
}

func TestURL(t *testing.T) {
url := "https://raw.githubusercontent.com/docker/app/v0.4.1/examples/hello-world/hello-world.dockerapp"
result := icmd.RunCommand(dockerApp, "inspect", url).Assert(t, icmd.Success)
result := icmd.RunCommand(dockerApp, "app", "inspect", url).Assert(t, icmd.Success)
assert.Assert(t, golden.String(result.Combined(), "helloworld-inspect.golden"))
}

Expand All @@ -282,36 +282,36 @@ func TestWithRegistry(t *testing.T) {
func testImage(registry string) func(*testing.T) {
return func(t *testing.T) {
// push to a registry
icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/myuser", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/myuser", "-t", "latest", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my.dockerapp:0.1.0").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my:0.1.0").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "push", "--namespace", registry+"/myuser", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "push", "--namespace", registry+"/myuser", "-t", "latest", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "inspect", registry+"/myuser/my.dockerapp:0.1.0").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "inspect", registry+"/myuser/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "inspect", registry+"/myuser/my").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "inspect", registry+"/myuser/my:0.1.0").Assert(t, icmd.Success)
// push a single-file app to a registry
dir := fs.NewDir(t, "save-prepare-build", fs.WithFile("my.dockerapp", singleFileApp))
defer dir.Remove()
icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/myuser", dir.Join("my.dockerapp")).Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "push", "--namespace", registry+"/myuser", dir.Join("my.dockerapp")).Assert(t, icmd.Success)

// push with custom repo name
icmd.RunCommand(dockerApp, "push", "-t", "marshmallows", "--namespace", registry+"/rainbows", "--repo", "unicorns", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "inspect", registry+"/rainbows/unicorns:marshmallows").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "push", "-t", "marshmallows", "--namespace", registry+"/rainbows", "--repo", "unicorns", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "inspect", registry+"/rainbows/unicorns:marshmallows").Assert(t, icmd.Success)
}
}

func testFork(registry string) func(*testing.T) {
return func(t *testing.T) {
icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/acmecorp", "testdata/fork/simple").Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "push", "--namespace", registry+"/acmecorp", "testdata/fork/simple").Assert(t, icmd.Success)

tempDir := fs.NewDir(t, "dockerapptest")
defer tempDir.Remove()

icmd.RunCommand(dockerApp, "fork", registry+"/acmecorp/simple.dockerapp:1.1.0-beta1", "acmecorp/scarlet.devil",
icmd.RunCommand(dockerApp, "app", "fork", registry+"/acmecorp/simple.dockerapp:1.1.0-beta1", "acmecorp/scarlet.devil",
"-p", tempDir.Path(), "-m", "Remilia Scarlet:remilia@acmecorp.cool").Assert(t, icmd.Success)
metadata := golden.Get(t, tempDir.Join("scarlet.devil.dockerapp", "metadata.yml"))
assert.Assert(t, golden.Bytes(metadata, "expected-fork-metadata.golden"))

icmd.RunCommand(dockerApp, "fork", registry+"/acmecorp/simple.dockerapp:1.1.0-beta1",
icmd.RunCommand(dockerApp, "app", "fork", registry+"/acmecorp/simple.dockerapp:1.1.0-beta1",
"-p", tempDir.Path(), "-m", "Remilia Scarlet:remilia@acmecorp.cool").Assert(t, icmd.Success)
metadata2 := golden.Get(t, tempDir.Join("simple.dockerapp", "metadata.yml"))
assert.Assert(t, golden.Bytes(metadata2, "expected-fork-metadata-no-rename.golden"))
Expand All @@ -328,10 +328,10 @@ func TestAttachmentsWithRegistry(t *testing.T) {
)
defer dir.Remove()

icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/acmecorp", dir.Join("attachments.dockerapp")).Assert(t, icmd.Success)
icmd.RunCommand(dockerApp, "app", "push", "--namespace", registry+"/acmecorp", dir.Join("attachments.dockerapp")).Assert(t, icmd.Success)

// inspect will run the core pull code too
result := icmd.RunCommand(dockerApp, "inspect", registry+"/acmecorp/attachments.dockerapp:0.1.0")
result := icmd.RunCommand(dockerApp, "app", "inspect", registry+"/acmecorp/attachments.dockerapp:0.1.0")

result.Assert(t, icmd.Success)
resultOutput := result.Combined()
Expand All @@ -344,7 +344,7 @@ func TestAttachmentsWithRegistry(t *testing.T) {
tempDir := fs.NewDir(t, "dockerapptest")
defer tempDir.Remove()

icmd.RunCommand(dockerApp, "fork", registry+"/acmecorp/attachments.dockerapp:0.1.0",
icmd.RunCommand(dockerApp, "app", "fork", registry+"/acmecorp/attachments.dockerapp:0.1.0",
"-p", tempDir.Path()).Assert(t, icmd.Success)
externalFile := golden.Get(t, tempDir.Join("attachments.dockerapp", "config.cfg"))
assert.Assert(t, golden.Bytes(externalFile, filepath.Join("attachments.dockerapp", "config.cfg")))
Expand Down
2 changes: 1 addition & 1 deletion e2e/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestExamplesAreValid(t *testing.T) {
case !info.IsDir():
return nil
default:
result := icmd.RunCommand(dockerApp, "validate", filepath.Join(p, filepath.Base(p)+".dockerapp"))
result := icmd.RunCommand(dockerApp, "app", "validate", filepath.Join(p, filepath.Base(p)+".dockerapp"))
result.Assert(t, icmd.Success)
return filepath.SkipDir
}
Expand Down
2 changes: 1 addition & 1 deletion e2e/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestMain(m *testing.M) {
if err != nil {
panic(err)
}
cmd := exec.Command(dockerApp, "version")
cmd := exec.Command(dockerApp, "app", "version")
output, err := cmd.CombinedOutput()
if err != nil {
panic(err)
Expand Down
4 changes: 2 additions & 2 deletions internal/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func makeStack(appname string, targetDir string, data []byte, stackVersion strin
return errors.Wrap(err, "failed to marshal final stack")
}
default:
return fmt.Errorf("invalid stack version %q", stackVersion)
return fmt.Errorf("Error: invalid stack version %q", stackVersion)
}
stackData = unquote(stackData)
return ioutil.WriteFile(filepath.Join(targetDir, "templates", "stack.yaml"), stackData, 0644)
Expand Down Expand Up @@ -215,7 +215,7 @@ func helmRender(app *types.App, targetDir string, env map[string]string, stackVe
},
}
default:
return fmt.Errorf("invalid stack version %q", stackVersion)
return fmt.Errorf("Error: invalid stack version %q", stackVersion)
}
stackData, err := yaml.Marshal(stack)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/packager/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func findApp() (string, error) {
for _, c := range content {
if strings.HasSuffix(c.Name(), internal.AppExtension) {
if hit != "" {
return "", fmt.Errorf("multiple applications found in current directory, specify the application name on the command line")
return "", fmt.Errorf("Error: multiple applications found in current directory, specify the application name on the command line")
}
hit = c.Name()
}
Expand Down
Loading

0 comments on commit e57d8cb

Please sign in to comment.