diff --git a/cmd/control/cli.go b/cmd/control/cli.go index d2d17fdb..2a11df8f 100644 --- a/cmd/control/cli.go +++ b/cmd/control/cli.go @@ -5,6 +5,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/rancher/os/cmd/control/service" "github.com/rancher/os/config" ) @@ -71,7 +72,7 @@ func Main() { SkipFlagParsing: true, Action: envAction, }, - serviceCommand(), + service.Commands(), { Name: "os", Usage: "operating system upgrade/downgrade", diff --git a/cmd/control/service/app/app.go b/cmd/control/service/app/app.go new file mode 100644 index 00000000..f8b5ab6c --- /dev/null +++ b/cmd/control/service/app/app.go @@ -0,0 +1,180 @@ +package app + +import ( + "fmt" + "os" + "os/signal" + "strings" + "syscall" + + "golang.org/x/net/context" + + "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/docker/libcompose/project" + "github.com/docker/libcompose/project/options" +) + +func ProjectPs(p project.APIProject, c *cli.Context) error { + qFlag := c.Bool("q") + allInfo, err := p.Ps(context.Background(), qFlag, c.Args()...) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + os.Stdout.WriteString(allInfo.String(!qFlag)) + return nil +} + +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 +} + +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 +} + +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 +} + +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 +} + +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"), + }, + } + 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("foreground") { + 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 +} + +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 +} + +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 +} + +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 +} + +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 +} + +func ProjectDelete(p project.APIProject, c *cli.Context) error { + options := options.Delete{ + RemoveVolume: c.Bool("v"), + } + if !c.Bool("force") { + options.BeforeDeleteCallback = func(stoppedContainers []string) bool { + fmt.Printf("Going to remove %v\nAre you sure? [yN]\n", strings.Join(stoppedContainers, ", ")) + var answer string + _, err := fmt.Scanln(&answer) + if err != nil { + logrus.Error(err) + return false + } + if answer != "y" && answer != "Y" { + return false + } + return true + } + } + err := p.Delete(context.Background(), options, c.Args()...) + if err != nil { + return cli.NewExitError(err.Error(), 1) + } + return nil +} + +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 +} diff --git a/cmd/control/service/command/command.go b/cmd/control/service/command/command.go new file mode 100644 index 00000000..2db8d38d --- /dev/null +++ b/cmd/control/service/command/command.go @@ -0,0 +1,243 @@ +package command + +import ( + "errors" + + "github.com/codegangsta/cli" + composeApp "github.com/docker/libcompose/cli/app" + "github.com/rancher/os/cmd/control/service/app" +) + +func verifyOneOrMoreServices(c *cli.Context) error { + if len(c.Args()) == 0 { + return errors.New("Must specify one or more services") + } + return nil +} + +func CreateCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "create", + Usage: "Create services", + Before: verifyOneOrMoreServices, + Action: composeApp.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.", + }, + }, + } +} + +func BuildCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "build", + Usage: "Build or rebuild services", + Before: verifyOneOrMoreServices, + Action: composeApp.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", + }, + }, + } +} + +func PsCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "ps", + Usage: "List containers", + Action: composeApp.WithProject(factory, app.ProjectPs), + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "q", + Usage: "Only display IDs", + }, + }, + } +} + +func UpCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "up", + Usage: "Create and start containers", + Before: verifyOneOrMoreServices, + Action: composeApp.WithProject(factory, app.ProjectUp), + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "foreground", + Usage: "Run in foreground 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.", + }, + }, + } +} + +func StartCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "start", + Usage: "Start services", + Before: verifyOneOrMoreServices, + Action: composeApp.WithProject(factory, app.ProjectStart), + Flags: []cli.Flag{ + cli.BoolTFlag{ + Name: "foreground", + Usage: "Run in foreground and log", + }, + }, + } +} + +func PullCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "pull", + Usage: "Pulls service images", + Before: verifyOneOrMoreServices, + Action: composeApp.WithProject(factory, app.ProjectPull), + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "ignore-pull-failures", + Usage: "Pull what it can and ignores images with pull failures.", + }, + }, + } +} + +func LogsCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "logs", + Usage: "View output from containers", + Before: verifyOneOrMoreServices, + Action: composeApp.WithProject(factory, app.ProjectLog), + Flags: []cli.Flag{ + cli.IntFlag{ + Name: "lines", + Usage: "number of lines to tail", + Value: 100, + }, + cli.BoolFlag{ + Name: "follow", + Usage: "Follow log output.", + }, + }, + } +} + +func RestartCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "restart", + Usage: "Restart services", + Before: verifyOneOrMoreServices, + Action: composeApp.WithProject(factory, app.ProjectRestart), + Flags: []cli.Flag{ + cli.IntFlag{ + Name: "timeout,t", + Usage: "Specify a shutdown timeout in seconds.", + Value: 10, + }, + }, + } +} + +func StopCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "stop", + Usage: "Stop services", + Before: verifyOneOrMoreServices, + Action: composeApp.WithProject(factory, app.ProjectStop), + Flags: []cli.Flag{ + cli.IntFlag{ + Name: "timeout,t", + Usage: "Specify a shutdown timeout in seconds.", + Value: 10, + }, + }, + } +} + +func DownCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "down", + Usage: "Stop and remove containers, networks, images, and volumes", + Before: verifyOneOrMoreServices, + Action: composeApp.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", + }, + }, + } +} + +func RmCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "rm", + Usage: "Delete services", + Before: verifyOneOrMoreServices, + Action: composeApp.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", + }, + }, + } +} + +func KillCommand(factory composeApp.ProjectFactory) cli.Command { + return cli.Command{ + Name: "kill", + Usage: "Kill containers", + Before: verifyOneOrMoreServices, + Action: composeApp.WithProject(factory, app.ProjectKill), + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "signal,s", + Usage: "SIGNAL to send to the container", + Value: "SIGKILL", + }, + }, + } +} diff --git a/cmd/control/service.go b/cmd/control/service/service.go similarity index 94% rename from cmd/control/service.go rename to cmd/control/service/service.go index b681804c..0f50ffb3 100644 --- a/cmd/control/service.go +++ b/cmd/control/service/service.go @@ -1,4 +1,4 @@ -package control +package service import ( "fmt" @@ -6,9 +6,9 @@ import ( "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/docker/libcompose/cli/command" dockerApp "github.com/docker/libcompose/cli/docker/app" "github.com/docker/libcompose/project" + "github.com/rancher/os/cmd/control/service/command" "github.com/rancher/os/compose" "github.com/rancher/os/config" "github.com/rancher/os/util/network" @@ -29,7 +29,7 @@ func beforeApp(c *cli.Context) error { return nil } -func serviceCommand() cli.Command { +func Commands() cli.Command { factory := &projectFactory{} app := cli.Command{} @@ -37,7 +37,9 @@ func serviceCommand() cli.Command { app.ShortName = "s" app.Usage = "Command line interface for services and compose." app.Before = beforeApp - app.Flags = append(command.CommonFlags(), dockerApp.DockerClientFlags()...) + app.Flags = append(dockerApp.DockerClientFlags(), cli.BoolFlag{ + Name: "verbose,debug", + }) app.Subcommands = append(serviceSubCommands(), command.BuildCommand(factory), command.CreateCommand(factory), @@ -46,11 +48,9 @@ func serviceCommand() cli.Command { command.LogsCommand(factory), command.RestartCommand(factory), command.StopCommand(factory), - command.ScaleCommand(factory), command.RmCommand(factory), command.PullCommand(factory), command.KillCommand(factory), - command.PortCommand(factory), command.PsCommand(factory), ) diff --git a/vendor/github.com/docker/libcompose/cli/command/command.go b/vendor/github.com/docker/libcompose/cli/command/command.go deleted file mode 100644 index a4ccff08..00000000 --- a/vendor/github.com/docker/libcompose/cli/command/command.go +++ /dev/null @@ -1,336 +0,0 @@ -package command - -import ( - "github.com/codegangsta/cli" - "github.com/docker/libcompose/cli/app" -) - -// 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.", - }, - }, - } -} - -// 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.", - }, - }, - } -} - -// 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{}, - } -} - -// 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.IntFlag{ - Name: "lines", - Usage: "number of lines to tail", - Value: 100, - }, - 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.", - Value: 10, - }, - }, - } -} - -// 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.", - Value: 10, - }, - }, - } -} - -// 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.", - Value: 10, - }, - }, - } -} - -// 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), - } -} - -// 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", - }, - } -}