diff --git a/Makefile b/Makefile index 9f035c969..af5f2622e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all build binary clean cross-binary help test test-unit test-integration test-acceptance validate +.PHONY: all build clean help test test-unit test-integration test-acceptance validate LIBCOMPOSE_ENVS := \ -e OS_PLATFORM_ARG \ @@ -21,28 +21,22 @@ DAEMON_VERSION := $(if $(DAEMON_VERSION),$(DAEMON_VERSION),"default") TTY := $(shell [ -t 0 ] && echo "-t") DOCKER_RUN_LIBCOMPOSE := docker run --rm -i $(TTY) --privileged -e DAEMON_VERSION="$(DAEMON_VERSION)" $(LIBCOMPOSE_ENVS) $(LIBCOMPOSE_MOUNT) "$(LIBCOMPOSE_IMAGE)" -default: binary +default: test -all: build ## validate all checks, build linux binary, run all tests\ncross build non-linux binaries +all: build ## validate all checks, run all tests $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh -binary: build ## build the linux binary - $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh binary - -cross-binary: build ## cross build the non linux binaries (windows, darwin) - $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh cross-binary - test: build ## run the unit, integration and acceptance tests - $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh binary test-unit test-integration test-acceptance + $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh test-unit test-integration test-acceptance test-unit: build ## run the unit tests $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh test-unit test-integration: build ## run the integration tests - $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh binary test-integration + $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh test-integration test-acceptance: build ## run the acceptance tests - $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh binary test-acceptance + $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh test-acceptance validate: build ## validate DCO, git conflicts marks, gofmt, golint and go vet $(DOCKER_RUN_LIBCOMPOSE) ./hack/make.sh validate-dco validate-git-marks validate-gofmt validate-lint validate-vet diff --git a/README.md b/README.md index b36b33208..18388050a 100644 --- a/README.md +++ b/README.md @@ -68,69 +68,6 @@ func main() { } ``` - -## Building - -You need either [Docker](http://github.com/docker/docker) and `make`, -or `go` in order to build libcompose. - -### Building with `docker` - -You need Docker and ``make`` and then run the ``binary`` target. This -will create binary for all platform in the `bundles` folder. - -```bash -$ make binary -docker build -t "libcompose-dev:refactor-makefile" . -# […] ----> Making bundle: binary (in .) -Number of parallel builds: 4 - ---> darwin/386: github.com/docker/libcompose/cli/main ---> darwin/amd64: github.com/docker/libcompose/cli/main ---> linux/386: github.com/docker/libcompose/cli/main ---> linux/amd64: github.com/docker/libcompose/cli/main ---> linux/arm: github.com/docker/libcompose/cli/main ---> windows/386: github.com/docker/libcompose/cli/main ---> windows/amd64: github.com/docker/libcompose/cli/main - -$ ls bundles -libcompose-cli_darwin-386* libcompose-cli_linux-amd64* libcompose-cli_windows-amd64.exe* -libcompose-cli_darwin-amd64* libcompose-cli_linux-arm* -libcompose-cli_linux-386* libcompose-cli_windows-386.exe* -``` - - -### Building with `go` - -- You need `go` v1.11 or greater -- you need to set export `GO111MODULE=on` environment variable -- If your working copy is not in your `GOPATH`, you need to set it -accordingly. - -```bash -$ go generate -# Generate some stuff -$ go build -o libcompose ./cli/main -``` - - -## Running - -A partial implementation of the libcompose-cli CLI is also implemented in Go. The primary purpose of this code is so one can easily test the behavior of libcompose. - -Run one of these: - -``` -libcompose-cli_darwin-386 -libcompose-cli_linux-amd64 -libcompose-cli_windows-amd64.exe -libcompose-cli_darwin-amd64 -libcompose-cli_linux-arm -libcompose-cli_linux-386 -libcompose-cli_windows-386.exe -``` - ## Tests (unit & integration) diff --git a/cli/app/app.go b/cli/app/app.go deleted file mode 100644 index 42f796f1f..000000000 --- a/cli/app/app.go +++ /dev/null @@ -1,333 +0,0 @@ -package app - -import ( - "fmt" - "os" - "os/signal" - "strconv" - "strings" - "syscall" - - "golang.org/x/net/context" - - "github.com/docker/libcompose/project" - "github.com/docker/libcompose/project/options" - "github.com/docker/libcompose/version" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -// ProjectAction is an adapter to allow the use of ordinary functions as libcompose actions. -// Any function that has the appropriate signature can be register as an action on a codegansta/cli command. -// -// cli.Command{ -// Name: "ps", -// Usage: "List containers", -// Action: app.WithProject(factory, app.ProjectPs), -// } -type ProjectAction func(project project.APIProject, c *cli.Context) error - -// BeforeApp is an action that is executed before any cli command. -func BeforeApp(c *cli.Context) error { - if c.GlobalBool("verbose") { - logrus.SetLevel(logrus.DebugLevel) - } - - if version.ShowWarning() { - logrus.Warning("Note: This is an experimental alternate implementation of the Compose CLI (https://github.com/docker/compose)") - } - return nil -} - -// WithProject is a helper function to create a cli.Command action with a ProjectFactory. -func WithProject(factory ProjectFactory, action ProjectAction) func(context *cli.Context) error { - return func(context *cli.Context) error { - p, err := factory.Create(context) - if err != nil { - logrus.Fatalf("Failed to read project: %v", err) - } - return action(p, context) - } -} - -// ProjectPs lists the containers. -func ProjectPs(p project.APIProject, c *cli.Context) error { - qFlag := c.Bool("q") - allInfo, err := p.Ps(context.Background(), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - columns := []string{"Name", "Command", "State", "Ports"} - if qFlag { - columns = []string{"Id"} - } - os.Stdout.WriteString(allInfo.String(columns, !qFlag)) - return nil -} - -// ProjectPort prints the public port for a port binding. -func ProjectPort(p project.APIProject, c *cli.Context) error { - if len(c.Args()) != 2 { - return cli.NewExitError("Please pass arguments in the form: SERVICE PORT", 1) - } - - index := c.Int("index") - protocol := c.String("protocol") - serviceName := c.Args()[0] - privatePort := c.Args()[1] - - port, err := p.Port(context.Background(), index, protocol, serviceName, privatePort) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - fmt.Println(port) - return nil -} - -// ProjectStop stops all services. -func ProjectStop(p project.APIProject, c *cli.Context) error { - err := p.Stop(context.Background(), c.Int("timeout"), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectDown brings all services down (stops and clean containers). -func ProjectDown(p project.APIProject, c *cli.Context) error { - options := options.Down{ - RemoveVolume: c.Bool("volumes"), - RemoveImages: options.ImageType(c.String("rmi")), - RemoveOrphans: c.Bool("remove-orphans"), - } - err := p.Down(context.Background(), options, c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectBuild builds or rebuilds services. -func ProjectBuild(p project.APIProject, c *cli.Context) error { - config := options.Build{ - NoCache: c.Bool("no-cache"), - ForceRemove: c.Bool("force-rm"), - Pull: c.Bool("pull"), - } - err := p.Build(context.Background(), config, c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectCreate creates all services but do not start them. -func ProjectCreate(p project.APIProject, c *cli.Context) error { - options := options.Create{ - NoRecreate: c.Bool("no-recreate"), - ForceRecreate: c.Bool("force-recreate"), - NoBuild: c.Bool("no-build"), - } - err := p.Create(context.Background(), options, c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectUp brings all services up. -func ProjectUp(p project.APIProject, c *cli.Context) error { - options := options.Up{ - Create: options.Create{ - NoRecreate: c.Bool("no-recreate"), - ForceRecreate: c.Bool("force-recreate"), - NoBuild: c.Bool("no-build"), - ForceBuild: c.Bool("build"), - }, - } - ctx, cancelFun := context.WithCancel(context.Background()) - err := p.Up(ctx, options, c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - if !c.Bool("d") { - signalChan := make(chan os.Signal, 1) - cleanupDone := make(chan bool) - signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) - errChan := make(chan error) - go func() { - errChan <- p.Log(ctx, true, c.Args()...) - }() - go func() { - select { - case <-signalChan: - fmt.Printf("\nGracefully stopping...\n") - cancelFun() - ProjectStop(p, c) - cleanupDone <- true - case err := <-errChan: - if err != nil { - logrus.Fatal(err) - } - cleanupDone <- true - } - }() - <-cleanupDone - return nil - } - return nil -} - -// ProjectRun runs a given command within a service's container. -func ProjectRun(p project.APIProject, c *cli.Context) error { - if len(c.Args()) == 0 { - logrus.Fatal("No service specified") - } - - serviceName := c.Args()[0] - commandParts := c.Args()[1:] - - options := options.Run{ - Detached: c.Bool("d"), - } - - exitCode, err := p.Run(context.Background(), serviceName, commandParts, options) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return cli.NewExitError("", exitCode) -} - -// ProjectStart starts services. -func ProjectStart(p project.APIProject, c *cli.Context) error { - err := p.Start(context.Background(), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectRestart restarts services. -func ProjectRestart(p project.APIProject, c *cli.Context) error { - err := p.Restart(context.Background(), c.Int("timeout"), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectLog gets services logs. -func ProjectLog(p project.APIProject, c *cli.Context) error { - err := p.Log(context.Background(), c.Bool("follow"), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectPull pulls images for services. -func ProjectPull(p project.APIProject, c *cli.Context) error { - err := p.Pull(context.Background(), c.Args()...) - if err != nil && !c.Bool("ignore-pull-failures") { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectDelete deletes services. -func ProjectDelete(p project.APIProject, c *cli.Context) error { - options := options.Delete{ - RemoveVolume: c.Bool("v"), - } - if !c.Bool("force") { - stoppedContainers, err := p.Containers(context.Background(), project.Filter{ - State: project.Stopped, - }, c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - if len(stoppedContainers) == 0 { - fmt.Println("No stopped containers") - return nil - } - fmt.Printf("Going to remove %v\nAre you sure? [yN]\n", strings.Join(stoppedContainers, ", ")) - var answer string - _, err = fmt.Scanln(&answer) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - if answer != "y" && answer != "Y" { - return nil - } - } - err := p.Delete(context.Background(), options, c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectKill forces stop service containers. -func ProjectKill(p project.APIProject, c *cli.Context) error { - err := p.Kill(context.Background(), c.String("signal"), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectConfig validates and print the compose file. -func ProjectConfig(p project.APIProject, c *cli.Context) error { - yaml, err := p.Config() - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - if !c.Bool("quiet") { - fmt.Println(yaml) - } - return nil -} - -// ProjectPause pauses service containers. -func ProjectPause(p project.APIProject, c *cli.Context) error { - err := p.Pause(context.Background(), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectUnpause unpauses service containers. -func ProjectUnpause(p project.APIProject, c *cli.Context) error { - err := p.Unpause(context.Background(), c.Args()...) - if err != nil { - return cli.NewExitError(err.Error(), 1) - } - return nil -} - -// ProjectScale scales services. -func ProjectScale(p project.APIProject, c *cli.Context) error { - servicesScale := map[string]int{} - for _, arg := range c.Args() { - kv := strings.SplitN(arg, "=", 2) - if len(kv) != 2 { - return cli.NewExitError(fmt.Sprintf("Invalid scale parameter: %s", arg), 2) - } - - name := kv[0] - - count, err := strconv.Atoi(kv[1]) - if err != nil { - return cli.NewExitError(fmt.Sprintf("Invalid scale parameter: %v", err), 2) - } - - servicesScale[name] = count - } - - err := p.Scale(context.Background(), c.Int("timeout"), servicesScale) - if err != nil { - return cli.NewExitError(err.Error(), 0) - } - return nil -} diff --git a/cli/app/events.go b/cli/app/events.go deleted file mode 100644 index 5f6068108..000000000 --- a/cli/app/events.go +++ /dev/null @@ -1,56 +0,0 @@ -package app - -import ( - "encoding/json" - "fmt" - "os" - "strings" - - "golang.org/x/net/context" - - "github.com/docker/libcompose/project" - "github.com/docker/libcompose/project/events" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -// ProjectEvents listen for real-time events of containers. -func ProjectEvents(p project.APIProject, c *cli.Context) error { - evts, err := p.Events(context.Background(), c.Args()...) - if err != nil { - return err - } - var printfn func(events.ContainerEvent) - - if c.Bool("json") { - printfn = printJSON - } else { - printfn = printStd - } - for event := range evts { - printfn(event) - } - return nil -} - -func printStd(event events.ContainerEvent) { - output := os.Stdout - fmt.Fprintf(output, "%s ", event.Time.Format("2006-01-02 15:04:05.999999999")) - fmt.Fprintf(output, "%s %s %s", event.Type, event.Event, event.ID) - attrs := []string{} - for attr, value := range event.Attributes { - attrs = append(attrs, fmt.Sprintf("%s=%s", attr, value)) - } - - fmt.Fprintf(output, " (%s)", strings.Join(attrs, ", ")) - fmt.Fprint(output, "\n") -} - -func printJSON(event events.ContainerEvent) { - json, err := json.Marshal(event) - if err != nil { - logrus.Warn(err) - } - output := os.Stdout - fmt.Fprintf(output, "%s", json) -} diff --git a/cli/app/types.go b/cli/app/types.go deleted file mode 100644 index 1c61be45a..000000000 --- a/cli/app/types.go +++ /dev/null @@ -1,12 +0,0 @@ -package app - -import ( - "github.com/docker/libcompose/project" - "github.com/urfave/cli" -) - -// ProjectFactory is an interface that helps creating libcompose project. -type ProjectFactory interface { - // Create creates a libcompose project from the command line options (urfave cli context). - Create(c *cli.Context) (project.APIProject, error) -} diff --git a/cli/app/version.go b/cli/app/version.go deleted file mode 100644 index 32ecbdf57..000000000 --- a/cli/app/version.go +++ /dev/null @@ -1,52 +0,0 @@ -package app - -import ( - "fmt" - "os" - "runtime" - "text/template" - - "github.com/docker/libcompose/version" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -var versionTemplate = `Version: {{.Version}} ({{.GitCommit}}) -Go version: {{.GoVersion}} -Built: {{.BuildTime}} -OS/Arch: {{.Os}}/{{.Arch}}` - -// Version prints the libcompose version number and additionnal informations. -func Version(c *cli.Context) error { - if c.Bool("short") { - fmt.Println(version.VERSION) - return nil - } - - tmpl, err := template.New("").Parse(versionTemplate) - if err != nil { - logrus.Fatal(err) - } - - v := struct { - Version string - GitCommit string - GoVersion string - BuildTime string - Os string - Arch string - }{ - Version: version.VERSION, - GitCommit: version.GITCOMMIT, - GoVersion: runtime.Version(), - BuildTime: version.BUILDTIME, - Os: runtime.GOOS, - Arch: runtime.GOARCH, - } - - if err := tmpl.Execute(os.Stdout, v); err != nil { - logrus.Fatal(err) - } - fmt.Printf("\n") - return nil -} diff --git a/cli/command/command.go b/cli/command/command.go deleted file mode 100644 index 7f1be6db2..000000000 --- a/cli/command/command.go +++ /dev/null @@ -1,392 +0,0 @@ -package command - -import ( - "os" - "strings" - - "github.com/docker/libcompose/cli/app" - "github.com/docker/libcompose/project" - "github.com/urfave/cli" -) - -// Populate updates the specified project context based on command line arguments and subcommands. -func Populate(context *project.Context, c *cli.Context) { - // urfave/cli does not distinguish whether the first string in the slice comes from the envvar - // or is from a flag. Worse off, it appends the flag values to the envvar value instead of - // overriding it. To ensure the multifile envvar case is always handled, the first string - // must always be split. It gives a more consistent behavior, then, to split each string in - // the slice. - for _, v := range c.GlobalStringSlice("file") { - context.ComposeFiles = append(context.ComposeFiles, strings.Split(v, string(os.PathListSeparator))...) - } - - if len(context.ComposeFiles) == 0 { - context.ComposeFiles = []string{"docker-compose.yml"} - if _, err := os.Stat("docker-compose.override.yml"); err == nil { - context.ComposeFiles = append(context.ComposeFiles, "docker-compose.override.yml") - } - } - - context.ProjectName = c.GlobalString("project-name") -} - -// CreateCommand defines the libcompose create subcommand. -func CreateCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "create", - Usage: "Create all services but do not start", - Action: app.WithProject(factory, app.ProjectCreate), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "no-recreate", - Usage: "If containers already exist, don't recreate them. Incompatible with --force-recreate.", - }, - cli.BoolFlag{ - Name: "force-recreate", - Usage: "Recreate containers even if their configuration and image haven't changed. Incompatible with --no-recreate.", - }, - cli.BoolFlag{ - Name: "no-build", - Usage: "Don't build an image, even if it's missing.", - }, - }, - } -} - -// ConfigCommand defines the libcompose config subcommand -func ConfigCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "config", - Usage: "Validate and view the compose file.", - Action: app.WithProject(factory, app.ProjectConfig), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "quiet,q", - Usage: "Only validate the configuration, don't print anything.", - }, - }, - } -} - -// BuildCommand defines the libcompose build subcommand. -func BuildCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "build", - Usage: "Build or rebuild services.", - Action: app.WithProject(factory, app.ProjectBuild), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "no-cache", - Usage: "Do not use cache when building the image", - }, - cli.BoolFlag{ - Name: "force-rm", - Usage: "Always remove intermediate containers", - }, - cli.BoolFlag{ - Name: "pull", - Usage: "Always attempt to pull a newer version of the image", - }, - }, - } -} - -// PsCommand defines the libcompose ps subcommand. -func PsCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "ps", - Usage: "List containers", - Action: app.WithProject(factory, app.ProjectPs), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "q", - Usage: "Only display IDs", - }, - }, - } -} - -// PortCommand defines the libcompose port subcommand. -func PortCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "port", - Usage: "Print the public port for a port binding", - Action: app.WithProject(factory, app.ProjectPort), - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "protocol", - Usage: "tcp or udp ", - Value: "tcp", - }, - cli.IntFlag{ - Name: "index", - Usage: "index of the container if there are multiple instances of a service", - Value: 1, - }, - }, - } -} - -// UpCommand defines the libcompose up subcommand. -func UpCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "up", - Usage: "Bring all services up", - Action: app.WithProject(factory, app.ProjectUp), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "d", - Usage: "Do not block and log", - }, - cli.BoolFlag{ - Name: "no-build", - Usage: "Don't build an image, even if it's missing.", - }, - cli.BoolFlag{ - Name: "no-recreate", - Usage: "If containers already exist, don't recreate them. Incompatible with --force-recreate.", - }, - cli.BoolFlag{ - Name: "force-recreate", - Usage: "Recreate containers even if their configuration and image haven't changed. Incompatible with --no-recreate.", - }, - cli.BoolFlag{ - Name: "build", - Usage: "Build images before starting containers.", - }, - }, - } -} - -// StartCommand defines the libcompose start subcommand. -func StartCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "start", - Usage: "Start services", - Action: app.WithProject(factory, app.ProjectStart), - Flags: []cli.Flag{ - cli.BoolTFlag{ - Name: "d", - Usage: "Do not block and log", - }, - }, - } -} - -// RunCommand defines the libcompose run subcommand. -func RunCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "run", - Usage: "Run a one-off command", - Action: app.WithProject(factory, app.ProjectRun), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "d", - Usage: "Detached mode: Run container in the background, print new container name.", - }, - }, - } -} - -// PullCommand defines the libcompose pull subcommand. -func PullCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "pull", - Usage: "Pulls images for services", - Action: app.WithProject(factory, app.ProjectPull), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "ignore-pull-failures", - Usage: "Pull what it can and ignores images with pull failures.", - }, - }, - } -} - -// LogsCommand defines the libcompose logs subcommand. -func LogsCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "logs", - Usage: "Get service logs", - Action: app.WithProject(factory, app.ProjectLog), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "follow", - Usage: "Follow log output.", - }, - }, - } -} - -// RestartCommand defines the libcompose restart subcommand. -func RestartCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "restart", - Usage: "Restart services", - Action: app.WithProject(factory, app.ProjectRestart), - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "timeout,t", - Usage: "Specify a shutdown timeout in seconds.", - }, - }, - } -} - -// StopCommand defines the libcompose stop subcommand. -func StopCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "stop", - Usage: "Stop services", - Action: app.WithProject(factory, app.ProjectStop), - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "timeout,t", - Usage: "Specify a shutdown timeout in seconds.", - }, - }, - } -} - -// DownCommand defines the libcompose stop subcommand. -func DownCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "down", - Usage: "Stop and remove containers, networks, images, and volumes", - Action: app.WithProject(factory, app.ProjectDown), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "volumes,v", - Usage: "Remove data volumes", - }, - cli.StringFlag{ - Name: "rmi", - Usage: "Remove images, type may be one of: 'all' to remove all images, or 'local' to remove only images that don't have an custom name set by the `image` field", - }, - cli.BoolFlag{ - Name: "remove-orphans", - Usage: "Remove containers for services not defined in the Compose file", - }, - }, - } -} - -// ScaleCommand defines the libcompose scale subcommand. -func ScaleCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "scale", - Usage: "Scale services", - Action: app.WithProject(factory, app.ProjectScale), - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "timeout,t", - Usage: "Specify a shutdown timeout in seconds.", - }, - }, - } -} - -// RmCommand defines the libcompose rm subcommand. -func RmCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "rm", - Usage: "Delete services", - Action: app.WithProject(factory, app.ProjectDelete), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "force,f", - Usage: "Allow deletion of all services", - }, - cli.BoolFlag{ - Name: "v", - Usage: "Remove volumes associated with containers", - }, - }, - } -} - -// KillCommand defines the libcompose kill subcommand. -func KillCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "kill", - Usage: "Force stop service containers", - Action: app.WithProject(factory, app.ProjectKill), - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "signal,s", - Usage: "SIGNAL to send to the container", - Value: "SIGKILL", - }, - }, - } -} - -// PauseCommand defines the libcompose pause subcommand. -func PauseCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "pause", - Usage: "Pause services.", - // ArgsUsage: "[SERVICE...]", - Action: app.WithProject(factory, app.ProjectPause), - } -} - -// UnpauseCommand defines the libcompose unpause subcommand. -func UnpauseCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "unpause", - Usage: "Unpause services.", - // ArgsUsage: "[SERVICE...]", - Action: app.WithProject(factory, app.ProjectUnpause), - } -} - -// EventsCommand defines the libcompose events subcommand -func EventsCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "events", - Usage: "Receive real time events from containers.", - Action: app.WithProject(factory, app.ProjectEvents), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "json", - Usage: "Output events as a stream of json objects", - }, - }, - } -} - -// VersionCommand defines the libcompose version subcommand. -func VersionCommand(factory app.ProjectFactory) cli.Command { - return cli.Command{ - Name: "version", - Usage: "Show version informations", - Action: app.Version, - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "short", - Usage: "Shows only Compose's version number.", - }, - }, - } -} - -// CommonFlags defines the flags that are in common for all subcommands. -func CommonFlags() []cli.Flag { - return []cli.Flag{ - cli.BoolFlag{ - Name: "verbose,debug", - }, - cli.StringSliceFlag{ - Name: "file,f", - Usage: "Specify one or more alternate compose files (default: docker-compose.yml)", - Value: &cli.StringSlice{}, - EnvVar: "COMPOSE_FILE", - }, - cli.StringFlag{ - Name: "project-name,p", - Usage: "Specify an alternate project name (default: directory name)", - EnvVar: "COMPOSE_PROJECT_NAME", - }, - } -} diff --git a/cli/docker/app/commands.go b/cli/docker/app/commands.go deleted file mode 100644 index 1ef346a89..000000000 --- a/cli/docker/app/commands.go +++ /dev/null @@ -1,62 +0,0 @@ -package app - -import ( - "github.com/docker/libcompose/cli/command" - "github.com/docker/libcompose/docker/client" - "github.com/docker/libcompose/docker/ctx" - "github.com/sirupsen/logrus" - "github.com/urfave/cli" -) - -// DockerClientFlags defines the flags that are specific to the docker client, -// like configdir or tls related flags. -func DockerClientFlags() []cli.Flag { - return []cli.Flag{ - cli.BoolFlag{ - Name: "tls", - Usage: "Use TLS; implied by --tlsverify", - }, - cli.BoolFlag{ - Name: "tlsverify", - Usage: "Use TLS and verify the remote", - EnvVar: "DOCKER_TLS_VERIFY", - }, - cli.StringFlag{ - Name: "tlscacert", - Usage: "Trust certs signed only by this CA", - }, - cli.StringFlag{ - Name: "tlscert", - Usage: "Path to TLS certificate file", - }, - cli.StringFlag{ - Name: "tlskey", - Usage: "Path to TLS key file", - }, - cli.StringFlag{ - Name: "configdir", - Usage: "Path to docker config dir, default ${HOME}/.docker", - }, - } -} - -// Populate updates the specified docker context based on command line arguments and subcommands. -func Populate(context *ctx.Context, c *cli.Context) { - command.Populate(&context.Context, c) - - context.ConfigDir = c.String("configdir") - - opts := client.Options{} - opts.TLS = c.GlobalBool("tls") - opts.TLSVerify = c.GlobalBool("tlsverify") - opts.TLSOptions.CAFile = c.GlobalString("tlscacert") - opts.TLSOptions.CertFile = c.GlobalString("tlscert") - opts.TLSOptions.KeyFile = c.GlobalString("tlskey") - - clientFactory, err := client.NewDefaultFactory(opts) - if err != nil { - logrus.Fatalf("Failed to construct Docker client: %v", err) - } - - context.ClientFactory = clientFactory -} diff --git a/cli/docker/app/factory.go b/cli/docker/app/factory.go deleted file mode 100644 index 9455d2934..000000000 --- a/cli/docker/app/factory.go +++ /dev/null @@ -1,21 +0,0 @@ -package app - -import ( - "github.com/docker/libcompose/cli/logger" - "github.com/docker/libcompose/docker" - "github.com/docker/libcompose/docker/ctx" - "github.com/docker/libcompose/project" - "github.com/urfave/cli" -) - -// ProjectFactory is a struct that holds the app.ProjectFactory implementation. -type ProjectFactory struct { -} - -// Create implements ProjectFactory.Create using docker client. -func (p *ProjectFactory) Create(c *cli.Context) (project.APIProject, error) { - context := &ctx.Context{} - context.LoggerFactory = logger.NewColorLoggerFactory() - Populate(context, c) - return docker.NewProject(context, nil) -} diff --git a/cli/docker/app/factory_test.go b/cli/docker/app/factory_test.go deleted file mode 100644 index e41540c5b..000000000 --- a/cli/docker/app/factory_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package app - -import ( - "flag" - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/docker/libcompose/project" - "github.com/urfave/cli" -) - -func TestProjectFactoryProjectNameIsNormalized(t *testing.T) { - projects := []struct { - name string - expected string - }{ - { - name: "example", - expected: "example", - }, - { - name: "example-test", - expected: "exampletest", - }, - { - name: "aW3Ird_Project_with_$$", - expected: "aw3irdprojectwith", - }, - } - - tmpDir, err := ioutil.TempDir("", "project-factory-test") - if err != nil { - t.Fatal(err) - } - composeFile := filepath.Join(tmpDir, "docker-compose.yml") - ioutil.WriteFile(composeFile, []byte(`hello: - image: busybox`), 0700) - - for _, projectCase := range projects { - globalSet := flag.NewFlagSet("test", 0) - // Set the project-name flag - globalSet.String("project-name", projectCase.name, "doc") - // Set the compose file flag - globalSet.Var(&cli.StringSlice{composeFile}, "file", "doc") - c := cli.NewContext(nil, globalSet, nil) - factory := &ProjectFactory{} - p, err := factory.Create(c) - if err != nil { - t.Fatal(err) - } - - if p.(*project.Project).Name != projectCase.expected { - t.Fatalf("expected %s, got %s", projectCase.expected, p.(*project.Project).Name) - } - } -} - -func TestProjectFactoryFileArgMayContainMultipleFiles(t *testing.T) { - sep := string(os.PathListSeparator) - fileCases := []struct { - requested []string - available []string - expected []string - }{ - { - requested: []string{}, - available: []string{"docker-compose.yml"}, - expected: []string{"docker-compose.yml"}, - }, - { - requested: []string{}, - available: []string{"docker-compose.yml", "docker-compose.override.yml"}, - expected: []string{"docker-compose.yml", "docker-compose.override.yml"}, - }, - { - requested: []string{"one.yml"}, - available: []string{"one.yml"}, - expected: []string{"one.yml"}, - }, - { - requested: []string{"one.yml"}, - available: []string{"docker-compose.yml", "one.yml"}, - expected: []string{"one.yml"}, - }, - { - requested: []string{"one.yml", "two.yml", "three.yml"}, - available: []string{"one.yml", "two.yml", "three.yml"}, - expected: []string{"one.yml", "two.yml", "three.yml"}, - }, - { - requested: []string{"one.yml" + sep + "two.yml" + sep + "three.yml"}, - available: []string{"one.yml", "two.yml", "three.yml"}, - expected: []string{"one.yml", "two.yml", "three.yml"}, - }, - { - requested: []string{"one.yml" + sep + "two.yml", "three.yml" + sep + "four.yml"}, - available: []string{"one.yml", "two.yml", "three.yml", "four.yml"}, - expected: []string{"one.yml", "two.yml", "three.yml", "four.yml"}, - }, - { - requested: []string{"one.yml", "two.yml" + sep + "three.yml"}, - available: []string{"one.yml", "two.yml", "three.yml"}, - expected: []string{"one.yml", "two.yml", "three.yml"}, - }, - } - - for _, fileCase := range fileCases { - tmpDir, err := ioutil.TempDir("", "project-factory-test") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) - if err = os.Chdir(tmpDir); err != nil { - t.Fatal(err) - } - - for _, file := range fileCase.available { - ioutil.WriteFile(file, []byte(`hello: - image: busybox`), 0700) - } - globalSet := flag.NewFlagSet("test", 0) - // Set the project-name flag - globalSet.String("project-name", "example", "doc") - // Set the compose file flag - fcr := cli.StringSlice(fileCase.requested) - globalSet.Var(&fcr, "file", "doc") - c := cli.NewContext(nil, globalSet, nil) - factory := &ProjectFactory{} - p, err := factory.Create(c) - if err != nil { - t.Fatal(err) - } - - for i, v := range p.(*project.Project).Files { - if v != fileCase.expected[i] { - t.Fatalf("requested %s, available %s, expected %s, got %s", - fileCase.requested, fileCase.available, fileCase.expected, p.(*project.Project).Files) - } - } - } -} diff --git a/cli/logger/color_logger.go b/cli/logger/color_logger.go deleted file mode 100644 index f48230773..000000000 --- a/cli/logger/color_logger.go +++ /dev/null @@ -1,102 +0,0 @@ -package logger - -import ( - "fmt" - "io" - "os" - "strconv" - - "github.com/docker/libcompose/logger" - "golang.org/x/crypto/ssh/terminal" -) - -// ColorLoggerFactory implements logger.Factory interface using ColorLogger. -type ColorLoggerFactory struct { - maxLength int - tty bool -} - -// ColorLogger implements logger.Logger interface with color support. -type ColorLogger struct { - name string - colorPrefix string - factory *ColorLoggerFactory -} - -// NewColorLoggerFactory creates a new ColorLoggerFactory. -func NewColorLoggerFactory() *ColorLoggerFactory { - return &ColorLoggerFactory{ - tty: terminal.IsTerminal(int(os.Stdout.Fd())), - } -} - -// CreateContainerLogger implements logger.Factory.CreateContainerLogger. -func (c *ColorLoggerFactory) CreateContainerLogger(name string) logger.Logger { - return c.create(name) -} - -// CreateBuildLogger implements logger.Factory.CreateBuildLogger. -func (c *ColorLoggerFactory) CreateBuildLogger(name string) logger.Logger { - return &logger.RawLogger{} -} - -// CreatePullLogger implements logger.Factory.CreatePullLogger. -func (c *ColorLoggerFactory) CreatePullLogger(name string) logger.Logger { - return &logger.NullLogger{} -} - -// CreateBuildLogger implements logger.Factory.CreateContainerLogger. -func (c *ColorLoggerFactory) create(name string) logger.Logger { - if c.maxLength < len(name) { - c.maxLength = len(name) - } - - return &ColorLogger{ - name: name, - factory: c, - colorPrefix: <-colorPrefix, - } -} - -// Out implements logger.Logger.Out. -func (c *ColorLogger) Out(bytes []byte) { - if len(bytes) == 0 { - return - } - logFmt, name := c.getLogFmt() - message := fmt.Sprintf(logFmt, name, string(bytes)) - fmt.Print(message) -} - -// Err implements logger.Logger.Err. -func (c *ColorLogger) Err(bytes []byte) { - if len(bytes) == 0 { - return - } - logFmt, name := c.getLogFmt() - message := fmt.Sprintf(logFmt, name, string(bytes)) - fmt.Fprint(os.Stderr, message) -} - -// OutWriter returns the base writer -func (c *ColorLogger) OutWriter() io.Writer { - return os.Stdout -} - -// ErrWriter returns the base writer -func (c *ColorLogger) ErrWriter() io.Writer { - return os.Stderr -} - -func (c *ColorLogger) getLogFmt() (string, string) { - pad := c.factory.maxLength - - logFmt := "%s | %s" - if c.factory.tty { - logFmt = c.colorPrefix + " %s" - } - - name := fmt.Sprintf("%-"+strconv.Itoa(pad)+"s", c.name) - - return logFmt, name -} diff --git a/cli/logger/colors.go b/cli/logger/colors.go deleted file mode 100644 index 4f9e62ade..000000000 --- a/cli/logger/colors.go +++ /dev/null @@ -1,34 +0,0 @@ -package logger - -import "fmt" - -var ( - colorPrefix = make(chan string) -) - -func generateColors() { - i := 0 - colorOrder := []string{ - "36", // cyan - "33", // yellow - "32", // green - "35", // magenta - "31", // red - "34", // blue - "36;1", // intense cyan - "33;1", // intense yellow - "32;1", // intense green - "35;1", // intense magenta - "31;1", // intense red - "34;1", // intense blue - } - - for { - colorPrefix <- fmt.Sprintf("\033[%sm%%s |\033[0m", colorOrder[i]) - i = (i + 1) % len(colorOrder) - } -} - -func init() { - go generateColors() -} diff --git a/cli/main/main.go b/cli/main/main.go deleted file mode 100644 index 38ff07db4..000000000 --- a/cli/main/main.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "os" - "path" - - cliApp "github.com/docker/libcompose/cli/app" - "github.com/docker/libcompose/cli/command" - dockerApp "github.com/docker/libcompose/cli/docker/app" - "github.com/docker/libcompose/version" - "github.com/urfave/cli" -) - -func main() { - factory := &dockerApp.ProjectFactory{} - - cli.AppHelpTemplate = `Usage: {{.Name}} {{if .Flags}}[OPTIONS] {{end}}COMMAND [arg...] - -{{.Usage}} - -Version: {{.Version}}{{if or .Author .Email}} - -Author:{{if .Author}} - {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}} - {{.Email}}{{end}}{{end}} -{{if .Flags}} -Options: - {{range .Flags}}{{.}} - {{end}}{{end}} -Commands: - {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} - {{end}} -Run '{{.Name}} COMMAND --help' for more information on a command. -` - cli.CommandHelpTemplate = `Usage: ` + path.Base(os.Args[0]) + ` {{.Name}}{{if .Flags}} [OPTIONS] - -{{.Usage}} - -Options: - {{range .Flags}}{{.}} - {{end}}{{end}} -` - - app := cli.NewApp() - app.Name = "libcompose-cli" - app.Usage = "Command line interface for libcompose." - app.Version = version.VERSION + " (" + version.GITCOMMIT + ")" - app.Author = "Docker Compose Contributors" - app.Email = "https://github.com/docker/libcompose" - app.Before = cliApp.BeforeApp - app.Flags = append(command.CommonFlags(), dockerApp.DockerClientFlags()...) - app.Commands = []cli.Command{ - command.BuildCommand(factory), - command.ConfigCommand(factory), - command.CreateCommand(factory), - command.EventsCommand(factory), - command.DownCommand(factory), - command.KillCommand(factory), - command.LogsCommand(factory), - command.PauseCommand(factory), - command.PortCommand(factory), - command.PsCommand(factory), - command.PullCommand(factory), - command.RestartCommand(factory), - command.RmCommand(factory), - command.RunCommand(factory), - command.ScaleCommand(factory), - command.StartCommand(factory), - command.StopCommand(factory), - command.UnpauseCommand(factory), - command.UpCommand(factory), - command.VersionCommand(factory), - } - - app.Run(os.Args) - -} diff --git a/hack/make.sh b/hack/make.sh index b88a1c9a9..8daf1fa1e 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -11,13 +11,10 @@ DEFAULT_BUNDLES=( validate-git-marks validate-lint validate-vet - binary test-unit test-integration test-acceptance - - cross-binary ) bundle() { local bundle="$1"; shift