From 64c73597e2eac4ffa38e768263d546b3f790c709 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn Date: Tue, 9 Dec 2014 17:45:10 +0000 Subject: [PATCH] adds codegangsta/cli Signed-off-by: Simon Thulbourn --- Godeps/Godeps.json | 9 +- .../github.com/codegangsta/cli/.travis.yml | 6 + .../src/github.com/codegangsta/cli/LICENSE | 21 + .../src/github.com/codegangsta/cli/README.md | 298 +++++ .../src/github.com/codegangsta/cli/app.go | 251 ++++ .../github.com/codegangsta/cli/app_test.go | 423 +++++++ .../cli/autocomplete/bash_autocomplete | 13 + .../cli/autocomplete/zsh_autocomplete | 5 + .../src/github.com/codegangsta/cli/cli.go | 19 + .../github.com/codegangsta/cli/cli_test.go | 100 ++ .../src/github.com/codegangsta/cli/command.go | 144 +++ .../codegangsta/cli/command_test.go | 49 + .../src/github.com/codegangsta/cli/context.go | 339 ++++++ .../codegangsta/cli/context_test.go | 99 ++ .../src/github.com/codegangsta/cli/flag.go | 447 ++++++++ .../github.com/codegangsta/cli/flag_test.go | 743 ++++++++++++ .../src/github.com/codegangsta/cli/help.go | 224 ++++ .../codegangsta/cli/helpers_test.go | 19 + .../docker/docker/pkg/mflag/LICENSE | 27 - .../docker/docker/pkg/mflag/MAINTAINERS | 1 - .../docker/docker/pkg/mflag/README.md | 40 - .../docker/pkg/mflag/example/example.go | 36 - .../docker/docker/pkg/mflag/flag.go | 1013 ----------------- .../docker/docker/pkg/mflag/flag_test.go | 506 -------- commands.go | 912 +++++++-------- drivers/azure/azure.go | 195 ++-- drivers/digitalocean/digitalocean.go | 75 +- drivers/drivers.go | 28 +- drivers/none/none.go | 36 +- drivers/virtualbox/virtualbox.go | 40 +- main.go | 66 +- store.go | 6 +- store_test.go | 75 +- version.go | 3 + 34 files changed, 3901 insertions(+), 2367 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/README.md create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/app.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/cli.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/command.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/context.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/flag.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/help.go create mode 100644 Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/MAINTAINERS delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/README.md delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/example/example.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag.go delete mode 100644 Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag_test.go create mode 100644 version.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 7ed76c0ada..275c30f450 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -52,11 +52,6 @@ "Comment": "v1.2.0-1619-g831d09f", "Rev": "831d09f8b535a1e2939bdacb02032d238e8dd249" }, - { - "ImportPath": "github.com/docker/docker/pkg/mflag", - "Comment": "v1.2.0-1619-g831d09f", - "Rev": "831d09f8b535a1e2939bdacb02032d238e8dd249" - }, { "ImportPath": "github.com/docker/docker/pkg/parsers", "Comment": "v1.2.0-1619-g831d09f", @@ -118,6 +113,10 @@ { "ImportPath": "github.com/tent/http-link-go", "Rev": "ac974c61c2f990f4115b119354b5e0b47550e888" + }, + { + "ImportPath": "github.com/codegangsta/cli", + "Rev": "fbda1ce02d5dabcee952040e5f4025753b6c9ce0" } ] } diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml b/Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml new file mode 100644 index 0000000000..baf46abc6f --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml @@ -0,0 +1,6 @@ +language: go +go: 1.1 + +script: +- go vet ./... +- go test -v ./... diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE b/Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE new file mode 100644 index 0000000000..5515ccfb71 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE @@ -0,0 +1,21 @@ +Copyright (C) 2013 Jeremy Saenz +All Rights Reserved. + +MIT LICENSE + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/README.md b/Godeps/_workspace/src/github.com/codegangsta/cli/README.md new file mode 100644 index 0000000000..0e8327b8b3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/README.md @@ -0,0 +1,298 @@ +[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli) + +# cli.go +cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. + +You can view the API docs here: +http://godoc.org/github.com/codegangsta/cli + +## Overview +Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. + +**This is where cli.go comes into play.** cli.go makes command line programming fun, organized, and expressive! + +## Installation +Make sure you have a working Go environment (go 1.1 is *required*). [See the install instructions](http://golang.org/doc/install.html). + +To install `cli.go`, simply run: +``` +$ go get github.com/codegangsta/cli +``` + +Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used: +``` +export PATH=$PATH:$GOPATH/bin +``` + +## Getting Started +One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`. + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + cli.NewApp().Run(os.Args) +} +``` + +This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "boom" + app.Usage = "make an explosive entrance" + app.Action = func(c *cli.Context) { + println("boom! I say!") + } + + app.Run(os.Args) +} +``` + +Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. + +## Example + +Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! + +Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: + +``` go +package main + +import ( + "os" + "github.com/codegangsta/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "greet" + app.Usage = "fight the loneliness!" + app.Action = func(c *cli.Context) { + println("Hello friend!") + } + + app.Run(os.Args) +} +``` + +Install our command to the `$GOPATH/bin` directory: + +``` +$ go install +``` + +Finally run our new command: + +``` +$ greet +Hello friend! +``` + +cli.go also generates some bitchass help text: +``` +$ greet help +NAME: + greet - fight the loneliness! + +USAGE: + greet [global options] command [command options] [arguments...] + +VERSION: + 0.0.0 + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS + --version Shows version information +``` + +### Arguments +You can lookup arguments by calling the `Args` function on `cli.Context`. + +``` go +... +app.Action = func(c *cli.Context) { + println("Hello", c.Args()[0]) +} +... +``` + +### Flags +Setting and querying flags is simple. +``` go +... +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + }, +} +app.Action = func(c *cli.Context) { + name := "someone" + if len(c.Args()) > 0 { + name = c.Args()[0] + } + if c.String("lang") == "spanish" { + println("Hola", name) + } else { + println("Hello", name) + } +} +... +``` + +#### Alternate Names + +You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. + +``` go +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "language for the greeting", + }, +} +``` + +That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. + +#### Values from the Environment + +You can also have the default value set from the environment via `EnvVar`. e.g. + +``` go +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "language for the greeting", + EnvVar: "APP_LANG", + }, +} +``` + +The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. + +``` go +app.Flags = []cli.Flag { + cli.StringFlag{ + Name: "lang, l", + Value: "english", + Usage: "language for the greeting", + EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", + }, +} +``` + +### Subcommands + +Subcommands can be defined for a more git-like command line app. +```go +... +app.Commands = []cli.Command{ + { + Name: "add", + ShortName: "a", + Usage: "add a task to the list", + Action: func(c *cli.Context) { + println("added task: ", c.Args().First()) + }, + }, + { + Name: "complete", + ShortName: "c", + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + }, + { + Name: "template", + ShortName: "r", + Usage: "options for task templates", + Subcommands: []cli.Command{ + { + Name: "add", + Usage: "add a new template", + Action: func(c *cli.Context) { + println("new task template: ", c.Args().First()) + }, + }, + { + Name: "remove", + Usage: "remove an existing template", + Action: func(c *cli.Context) { + println("removed task template: ", c.Args().First()) + }, + }, + }, + }, +} +... +``` + +### Bash Completion + +You can enable completion commands by setting the `EnableBashCompletion` +flag on the `App` object. By default, this setting will only auto-complete to +show an app's subcommands, but you can write your own completion methods for +the App or its subcommands. +```go +... +var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} +app := cli.NewApp() +app.EnableBashCompletion = true +app.Commands = []cli.Command{ + { + Name: "complete", + ShortName: "c", + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + BashComplete: func(c *cli.Context) { + // This will complete if no args are passed + if len(c.Args()) > 0 { + return + } + for _, t := range tasks { + fmt.Println(t) + } + }, + } +} +... +``` + +#### To Enable + +Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while +setting the `PROG` variable to the name of your program: + +`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` + + +## Contribution Guidelines +Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. + +If you are have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together. + +If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out. diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app.go new file mode 100644 index 0000000000..f4c4af84c2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/app.go @@ -0,0 +1,251 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "os" + "time" +) + +// App is the main structure of a cli application. It is recomended that +// and app be created with the cli.NewApp() function +type App struct { + // The name of the program. Defaults to os.Args[0] + Name string + // Description of the program. + Usage string + // Version of the program + Version string + // List of commands to execute + Commands []Command + // List of flags to parse + Flags []Flag + // Boolean to enable bash completion commands + EnableBashCompletion bool + // Boolean to hide built-in help command + HideHelp bool + // Boolean to hide built-in version flag + HideVersion bool + // An action to execute when the bash-completion flag is set + BashComplete func(context *Context) + // An action to execute before any subcommands are run, but after the context is ready + // If a non-nil error is returned, no subcommands are run + Before func(context *Context) error + // The action to execute when no subcommands are specified + Action func(context *Context) + // Execute this function if the proper command cannot be found + CommandNotFound func(context *Context, command string) + // Compilation date + Compiled time.Time + // Author + Author string + // Author e-mail + Email string +} + +// Tries to find out when this binary was compiled. +// Returns the current time if it fails to find it. +func compileTime() time.Time { + info, err := os.Stat(os.Args[0]) + if err != nil { + return time.Now() + } + return info.ModTime() +} + +// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. +func NewApp() *App { + return &App{ + Name: os.Args[0], + Usage: "A new cli application", + Version: "0.0.0", + BashComplete: DefaultAppComplete, + Action: helpCommand.Action, + Compiled: compileTime(), + } +} + +// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination +func (a *App) Run(arguments []string) error { + // append help to commands + if a.Command(helpCommand.Name) == nil && !a.HideHelp { + a.Commands = append(a.Commands, helpCommand) + a.appendFlag(HelpFlag) + } + + //append version/help flags + if a.EnableBashCompletion { + a.appendFlag(BashCompletionFlag) + } + + if !a.HideVersion { + a.appendFlag(VersionFlag) + } + + // parse flags + set := flagSet(a.Name, a.Flags) + set.SetOutput(ioutil.Discard) + err := set.Parse(arguments[1:]) + nerr := normalizeFlags(a.Flags, set) + if nerr != nil { + fmt.Println(nerr) + context := NewContext(a, set, set) + ShowAppHelp(context) + fmt.Println("") + return nerr + } + context := NewContext(a, set, set) + + if err != nil { + fmt.Printf("Incorrect Usage.\n\n") + ShowAppHelp(context) + fmt.Println("") + return err + } + + if checkCompletions(context) { + return nil + } + + if checkHelp(context) { + return nil + } + + if checkVersion(context) { + return nil + } + + if a.Before != nil { + err := a.Before(context) + if err != nil { + return err + } + } + + args := context.Args() + if args.Present() { + name := args.First() + c := a.Command(name) + if c != nil { + return c.Run(context) + } + } + + // Run default Action + a.Action(context) + return nil +} + +// Another entry point to the cli app, takes care of passing arguments and error handling +func (a *App) RunAndExitOnError() { + if err := a.Run(os.Args); err != nil { + os.Stderr.WriteString(fmt.Sprintln(err)) + os.Exit(1) + } +} + +// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags +func (a *App) RunAsSubcommand(ctx *Context) error { + // append help to commands + if len(a.Commands) > 0 { + if a.Command(helpCommand.Name) == nil && !a.HideHelp { + a.Commands = append(a.Commands, helpCommand) + a.appendFlag(HelpFlag) + } + } + + // append flags + if a.EnableBashCompletion { + a.appendFlag(BashCompletionFlag) + } + + // parse flags + set := flagSet(a.Name, a.Flags) + set.SetOutput(ioutil.Discard) + err := set.Parse(ctx.Args().Tail()) + nerr := normalizeFlags(a.Flags, set) + context := NewContext(a, set, ctx.globalSet) + + if nerr != nil { + fmt.Println(nerr) + if len(a.Commands) > 0 { + ShowSubcommandHelp(context) + } else { + ShowCommandHelp(ctx, context.Args().First()) + } + fmt.Println("") + return nerr + } + + if err != nil { + fmt.Printf("Incorrect Usage.\n\n") + ShowSubcommandHelp(context) + return err + } + + if checkCompletions(context) { + return nil + } + + if len(a.Commands) > 0 { + if checkSubcommandHelp(context) { + return nil + } + } else { + if checkCommandHelp(ctx, context.Args().First()) { + return nil + } + } + + if a.Before != nil { + err := a.Before(context) + if err != nil { + return err + } + } + + args := context.Args() + if args.Present() { + name := args.First() + c := a.Command(name) + if c != nil { + return c.Run(context) + } + } + + // Run default Action + if len(a.Commands) > 0 { + a.Action(context) + } else { + a.Action(ctx) + } + + return nil +} + +// Returns the named command on App. Returns nil if the command does not exist +func (a *App) Command(name string) *Command { + for _, c := range a.Commands { + if c.HasName(name) { + return &c + } + } + + return nil +} + +func (a *App) hasFlag(flag Flag) bool { + for _, f := range a.Flags { + if flag == f { + return true + } + } + + return false +} + +func (a *App) appendFlag(flag Flag) { + if !a.hasFlag(flag) { + a.Flags = append(a.Flags, flag) + } +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go new file mode 100644 index 0000000000..81d11743e3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go @@ -0,0 +1,423 @@ +package cli_test + +import ( + "fmt" + "os" + "testing" + + "github.com/codegangsta/cli" +) + +func ExampleApp() { + // set args for examples sake + os.Args = []string{"greet", "--name", "Jeremy"} + + app := cli.NewApp() + app.Name = "greet" + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Action = func(c *cli.Context) { + fmt.Printf("Hello %v\n", c.String("name")) + } + app.Run(os.Args) + // Output: + // Hello Jeremy +} + +func ExampleAppSubcommand() { + // set args for examples sake + os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} + app := cli.NewApp() + app.Name = "say" + app.Commands = []cli.Command{ + { + Name: "hello", + ShortName: "hi", + Usage: "use it to see a description", + Description: "This is how we describe hello the function", + Subcommands: []cli.Command{ + { + Name: "english", + ShortName: "en", + Usage: "sends a greeting in english", + Description: "greets someone in english", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "name", + Value: "Bob", + Usage: "Name of the person to greet", + }, + }, + Action: func(c *cli.Context) { + fmt.Println("Hello,", c.String("name")) + }, + }, + }, + }, + } + + app.Run(os.Args) + // Output: + // Hello, Jeremy +} + +func ExampleAppHelp() { + // set args for examples sake + os.Args = []string{"greet", "h", "describeit"} + + app := cli.NewApp() + app.Name = "greet" + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, + } + app.Commands = []cli.Command{ + { + Name: "describeit", + ShortName: "d", + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *cli.Context) { + fmt.Printf("i like to describe things") + }, + }, + } + app.Run(os.Args) + // Output: + // NAME: + // describeit - use it to see a description + // + // USAGE: + // command describeit [arguments...] + // + // DESCRIPTION: + // This is how we describe describeit the function +} + +func ExampleAppBashComplete() { + // set args for examples sake + os.Args = []string{"greet", "--generate-bash-completion"} + + app := cli.NewApp() + app.Name = "greet" + app.EnableBashCompletion = true + app.Commands = []cli.Command{ + { + Name: "describeit", + ShortName: "d", + Usage: "use it to see a description", + Description: "This is how we describe describeit the function", + Action: func(c *cli.Context) { + fmt.Printf("i like to describe things") + }, + }, { + Name: "next", + Usage: "next example", + Description: "more stuff to see when generating bash completion", + Action: func(c *cli.Context) { + fmt.Printf("the next example") + }, + }, + } + + app.Run(os.Args) + // Output: + // describeit + // d + // next + // help + // h +} + +func TestApp_Run(t *testing.T) { + s := "" + + app := cli.NewApp() + app.Action = func(c *cli.Context) { + s = s + c.Args().First() + } + + err := app.Run([]string{"command", "foo"}) + expect(t, err, nil) + err = app.Run([]string{"command", "bar"}) + expect(t, err, nil) + expect(t, s, "foobar") +} + +var commandAppTests = []struct { + name string + expected bool +}{ + {"foobar", true}, + {"batbaz", true}, + {"b", true}, + {"f", true}, + {"bat", false}, + {"nothing", false}, +} + +func TestApp_Command(t *testing.T) { + app := cli.NewApp() + fooCommand := cli.Command{Name: "foobar", ShortName: "f"} + batCommand := cli.Command{Name: "batbaz", ShortName: "b"} + app.Commands = []cli.Command{ + fooCommand, + batCommand, + } + + for _, test := range commandAppTests { + expect(t, app.Command(test.name) != nil, test.expected) + } +} + +func TestApp_CommandWithArgBeforeFlags(t *testing.T) { + var parsedOption, firstArg string + + app := cli.NewApp() + command := cli.Command{ + Name: "cmd", + Flags: []cli.Flag{ + cli.StringFlag{Name: "option", Value: "", Usage: "some option"}, + }, + Action: func(c *cli.Context) { + parsedOption = c.String("option") + firstArg = c.Args().First() + }, + } + app.Commands = []cli.Command{command} + + app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) + + expect(t, parsedOption, "my-option") + expect(t, firstArg, "my-arg") +} + +func TestApp_Float64Flag(t *testing.T) { + var meters float64 + + app := cli.NewApp() + app.Flags = []cli.Flag{ + cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, + } + app.Action = func(c *cli.Context) { + meters = c.Float64("height") + } + + app.Run([]string{"", "--height", "1.93"}) + expect(t, meters, 1.93) +} + +func TestApp_ParseSliceFlags(t *testing.T) { + var parsedOption, firstArg string + var parsedIntSlice []int + var parsedStringSlice []string + + app := cli.NewApp() + command := cli.Command{ + Name: "cmd", + Flags: []cli.Flag{ + cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"}, + cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"}, + }, + Action: func(c *cli.Context) { + parsedIntSlice = c.IntSlice("p") + parsedStringSlice = c.StringSlice("ip") + parsedOption = c.String("option") + firstArg = c.Args().First() + }, + } + app.Commands = []cli.Command{command} + + app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) + + IntsEquals := func(a, b []int) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + + StrsEquals := func(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + var expectedIntSlice = []int{22, 80} + var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} + + if !IntsEquals(parsedIntSlice, expectedIntSlice) { + t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice) + } + + if !StrsEquals(parsedStringSlice, expectedStringSlice) { + t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) + } +} + +func TestApp_BeforeFunc(t *testing.T) { + beforeRun, subcommandRun := false, false + beforeError := fmt.Errorf("fail") + var err error + + app := cli.NewApp() + + app.Before = func(c *cli.Context) error { + beforeRun = true + s := c.String("opt") + if s == "fail" { + return beforeError + } + + return nil + } + + app.Commands = []cli.Command{ + cli.Command{ + Name: "sub", + Action: func(c *cli.Context) { + subcommandRun = true + }, + }, + } + + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "opt"}, + } + + // run with the Before() func succeeding + err = app.Run([]string{"command", "--opt", "succeed", "sub"}) + + if err != nil { + t.Fatalf("Run error: %s", err) + } + + if beforeRun == false { + t.Errorf("Before() not executed when expected") + } + + if subcommandRun == false { + t.Errorf("Subcommand not executed when expected") + } + + // reset + beforeRun, subcommandRun = false, false + + // run with the Before() func failing + err = app.Run([]string{"command", "--opt", "fail", "sub"}) + + // should be the same error produced by the Before func + if err != beforeError { + t.Errorf("Run error expected, but not received") + } + + if beforeRun == false { + t.Errorf("Before() not executed when expected") + } + + if subcommandRun == true { + t.Errorf("Subcommand executed when NOT expected") + } + +} + +func TestAppHelpPrinter(t *testing.T) { + oldPrinter := cli.HelpPrinter + defer func() { + cli.HelpPrinter = oldPrinter + }() + + var wasCalled = false + cli.HelpPrinter = func(template string, data interface{}) { + wasCalled = true + } + + app := cli.NewApp() + app.Run([]string{"-h"}) + + if wasCalled == false { + t.Errorf("Help printer expected to be called, but was not") + } +} + +func TestAppVersionPrinter(t *testing.T) { + oldPrinter := cli.VersionPrinter + defer func() { + cli.VersionPrinter = oldPrinter + }() + + var wasCalled = false + cli.VersionPrinter = func(c *cli.Context) { + wasCalled = true + } + + app := cli.NewApp() + ctx := cli.NewContext(app, nil, nil) + cli.ShowVersion(ctx) + + if wasCalled == false { + t.Errorf("Version printer expected to be called, but was not") + } +} + +func TestAppCommandNotFound(t *testing.T) { + beforeRun, subcommandRun := false, false + app := cli.NewApp() + + app.CommandNotFound = func(c *cli.Context, command string) { + beforeRun = true + } + + app.Commands = []cli.Command{ + cli.Command{ + Name: "bar", + Action: func(c *cli.Context) { + subcommandRun = true + }, + }, + } + + app.Run([]string{"command", "foo"}) + + expect(t, beforeRun, true) + expect(t, subcommandRun, false) +} + +func TestGlobalFlagsInSubcommands(t *testing.T) { + subcommandRun := false + app := cli.NewApp() + + app.Flags = []cli.Flag{ + cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, + } + + app.Commands = []cli.Command{ + cli.Command{ + Name: "foo", + Subcommands: []cli.Command{ + { + Name: "bar", + Action: func(c *cli.Context) { + if c.GlobalBool("debug") { + subcommandRun = true + } + }, + }, + }, + }, + } + + app.Run([]string{"command", "-d", "foo", "bar"}) + + expect(t, subcommandRun, true) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete b/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete new file mode 100644 index 0000000000..9b55dd990c --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete @@ -0,0 +1,13 @@ +#! /bin/bash + +_cli_bash_autocomplete() { + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + } + + complete -F _cli_bash_autocomplete $PROG \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete b/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete new file mode 100644 index 0000000000..5430a18f95 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete @@ -0,0 +1,5 @@ +autoload -U compinit && compinit +autoload -U bashcompinit && bashcompinit + +script_dir=$(dirname $0) +source ${script_dir}/bash_autocomplete diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/cli.go b/Godeps/_workspace/src/github.com/codegangsta/cli/cli.go new file mode 100644 index 0000000000..b742545812 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/cli.go @@ -0,0 +1,19 @@ +// Package cli provides a minimal framework for creating and organizing command line +// Go applications. cli is designed to be easy to understand and write, the most simple +// cli application can be written as follows: +// func main() { +// cli.NewApp().Run(os.Args) +// } +// +// Of course this application does not do much, so let's make this an actual application: +// func main() { +// app := cli.NewApp() +// app.Name = "greet" +// app.Usage = "say a greeting" +// app.Action = func(c *cli.Context) { +// println("Greetings") +// } +// +// app.Run(os.Args) +// } +package cli diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go new file mode 100644 index 0000000000..879a793dc2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go @@ -0,0 +1,100 @@ +package cli_test + +import ( + "os" + + "github.com/codegangsta/cli" +) + +func Example() { + app := cli.NewApp() + app.Name = "todo" + app.Usage = "task list on the command line" + app.Commands = []cli.Command{ + { + Name: "add", + ShortName: "a", + Usage: "add a task to the list", + Action: func(c *cli.Context) { + println("added task: ", c.Args().First()) + }, + }, + { + Name: "complete", + ShortName: "c", + Usage: "complete a task on the list", + Action: func(c *cli.Context) { + println("completed task: ", c.Args().First()) + }, + }, + } + + app.Run(os.Args) +} + +func ExampleSubcommand() { + app := cli.NewApp() + app.Name = "say" + app.Commands = []cli.Command{ + { + Name: "hello", + ShortName: "hi", + Usage: "use it to see a description", + Description: "This is how we describe hello the function", + Subcommands: []cli.Command{ + { + Name: "english", + ShortName: "en", + Usage: "sends a greeting in english", + Description: "greets someone in english", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "name", + Value: "Bob", + Usage: "Name of the person to greet", + }, + }, + Action: func(c *cli.Context) { + println("Hello, ", c.String("name")) + }, + }, { + Name: "spanish", + ShortName: "sp", + Usage: "sends a greeting in spanish", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "surname", + Value: "Jones", + Usage: "Surname of the person to greet", + }, + }, + Action: func(c *cli.Context) { + println("Hola, ", c.String("surname")) + }, + }, { + Name: "french", + ShortName: "fr", + Usage: "sends a greeting in french", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "nickname", + Value: "Stevie", + Usage: "Nickname of the person to greet", + }, + }, + Action: func(c *cli.Context) { + println("Bonjour, ", c.String("nickname")) + }, + }, + }, + }, { + Name: "bye", + Usage: "says goodbye", + Action: func(c *cli.Context) { + println("bye") + }, + }, + } + + app.Run(os.Args) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/command.go b/Godeps/_workspace/src/github.com/codegangsta/cli/command.go new file mode 100644 index 0000000000..5622b38f75 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/command.go @@ -0,0 +1,144 @@ +package cli + +import ( + "fmt" + "io/ioutil" + "strings" +) + +// Command is a subcommand for a cli.App. +type Command struct { + // The name of the command + Name string + // short name of the command. Typically one character + ShortName string + // A short description of the usage of this command + Usage string + // A longer explanation of how the command works + Description string + // The function to call when checking for bash command completions + BashComplete func(context *Context) + // An action to execute before any sub-subcommands are run, but after the context is ready + // If a non-nil error is returned, no sub-subcommands are run + Before func(context *Context) error + // The function to call when this command is invoked + Action func(context *Context) + // List of child commands + Subcommands []Command + // List of flags to parse + Flags []Flag + // Treat all flags as normal arguments if true + SkipFlagParsing bool + // Boolean to hide built-in help command + HideHelp bool +} + +// Invokes the command given the context, parses ctx.Args() to generate command-specific flags +func (c Command) Run(ctx *Context) error { + + if len(c.Subcommands) > 0 || c.Before != nil { + return c.startApp(ctx) + } + + if !c.HideHelp { + // append help to flags + c.Flags = append( + c.Flags, + HelpFlag, + ) + } + + if ctx.App.EnableBashCompletion { + c.Flags = append(c.Flags, BashCompletionFlag) + } + + set := flagSet(c.Name, c.Flags) + set.SetOutput(ioutil.Discard) + + firstFlagIndex := -1 + for index, arg := range ctx.Args() { + if strings.HasPrefix(arg, "-") { + firstFlagIndex = index + break + } + } + + var err error + if firstFlagIndex > -1 && !c.SkipFlagParsing { + args := ctx.Args() + regularArgs := args[1:firstFlagIndex] + flagArgs := args[firstFlagIndex:] + err = set.Parse(append(flagArgs, regularArgs...)) + } else { + err = set.Parse(ctx.Args().Tail()) + } + + if err != nil { + fmt.Printf("Incorrect Usage.\n\n") + ShowCommandHelp(ctx, c.Name) + fmt.Println("") + return err + } + + nerr := normalizeFlags(c.Flags, set) + if nerr != nil { + fmt.Println(nerr) + fmt.Println("") + ShowCommandHelp(ctx, c.Name) + fmt.Println("") + return nerr + } + context := NewContext(ctx.App, set, ctx.globalSet) + + if checkCommandCompletions(context, c.Name) { + return nil + } + + if checkCommandHelp(context, c.Name) { + return nil + } + context.Command = c + c.Action(context) + return nil +} + +// Returns true if Command.Name or Command.ShortName matches given name +func (c Command) HasName(name string) bool { + return c.Name == name || c.ShortName == name +} + +func (c Command) startApp(ctx *Context) error { + app := NewApp() + + // set the name and usage + app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) + if c.Description != "" { + app.Usage = c.Description + } else { + app.Usage = c.Usage + } + + // set CommandNotFound + app.CommandNotFound = ctx.App.CommandNotFound + + // set the flags and commands + app.Commands = c.Subcommands + app.Flags = c.Flags + app.HideHelp = c.HideHelp + + // bash completion + app.EnableBashCompletion = ctx.App.EnableBashCompletion + if c.BashComplete != nil { + app.BashComplete = c.BashComplete + } + + // set the actions + app.Before = c.Before + if c.Action != nil { + app.Action = c.Action + } else { + app.Action = helpSubcommand.Action + } + + return app.RunAsSubcommand(ctx) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go new file mode 100644 index 0000000000..c0f556ad24 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go @@ -0,0 +1,49 @@ +package cli_test + +import ( + "flag" + "testing" + + "github.com/codegangsta/cli" +) + +func TestCommandDoNotIgnoreFlags(t *testing.T) { + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"blah", "blah", "-break"} + set.Parse(test) + + c := cli.NewContext(app, set, set) + + command := cli.Command{ + Name: "test-cmd", + ShortName: "tc", + Usage: "this is for testing", + Description: "testing", + Action: func(_ *cli.Context) {}, + } + err := command.Run(c) + + expect(t, err.Error(), "flag provided but not defined: -break") +} + +func TestCommandIgnoreFlags(t *testing.T) { + app := cli.NewApp() + set := flag.NewFlagSet("test", 0) + test := []string{"blah", "blah"} + set.Parse(test) + + c := cli.NewContext(app, set, set) + + command := cli.Command{ + Name: "test-cmd", + ShortName: "tc", + Usage: "this is for testing", + Description: "testing", + Action: func(_ *cli.Context) {}, + SkipFlagParsing: true, + } + err := command.Run(c) + + expect(t, err, nil) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/context.go b/Godeps/_workspace/src/github.com/codegangsta/cli/context.go new file mode 100644 index 0000000000..c9f645b189 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/context.go @@ -0,0 +1,339 @@ +package cli + +import ( + "errors" + "flag" + "strconv" + "strings" + "time" +) + +// Context is a type that is passed through to +// each Handler action in a cli application. Context +// can be used to retrieve context-specific Args and +// parsed command-line options. +type Context struct { + App *App + Command Command + flagSet *flag.FlagSet + globalSet *flag.FlagSet + setFlags map[string]bool + globalSetFlags map[string]bool +} + +// Creates a new context. For use in when invoking an App or Command action. +func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { + return &Context{App: app, flagSet: set, globalSet: globalSet} +} + +// Looks up the value of a local int flag, returns 0 if no int flag exists +func (c *Context) Int(name string) int { + return lookupInt(name, c.flagSet) +} + +// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists +func (c *Context) Duration(name string) time.Duration { + return lookupDuration(name, c.flagSet) +} + +// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists +func (c *Context) Float64(name string) float64 { + return lookupFloat64(name, c.flagSet) +} + +// Looks up the value of a local bool flag, returns false if no bool flag exists +func (c *Context) Bool(name string) bool { + return lookupBool(name, c.flagSet) +} + +// Looks up the value of a local boolT flag, returns false if no bool flag exists +func (c *Context) BoolT(name string) bool { + return lookupBoolT(name, c.flagSet) +} + +// Looks up the value of a local string flag, returns "" if no string flag exists +func (c *Context) String(name string) string { + return lookupString(name, c.flagSet) +} + +// Looks up the value of a local string slice flag, returns nil if no string slice flag exists +func (c *Context) StringSlice(name string) []string { + return lookupStringSlice(name, c.flagSet) +} + +// Looks up the value of a local int slice flag, returns nil if no int slice flag exists +func (c *Context) IntSlice(name string) []int { + return lookupIntSlice(name, c.flagSet) +} + +// Looks up the value of a local generic flag, returns nil if no generic flag exists +func (c *Context) Generic(name string) interface{} { + return lookupGeneric(name, c.flagSet) +} + +// Looks up the value of a global int flag, returns 0 if no int flag exists +func (c *Context) GlobalInt(name string) int { + return lookupInt(name, c.globalSet) +} + +// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists +func (c *Context) GlobalDuration(name string) time.Duration { + return lookupDuration(name, c.globalSet) +} + +// Looks up the value of a global bool flag, returns false if no bool flag exists +func (c *Context) GlobalBool(name string) bool { + return lookupBool(name, c.globalSet) +} + +// Looks up the value of a global string flag, returns "" if no string flag exists +func (c *Context) GlobalString(name string) string { + return lookupString(name, c.globalSet) +} + +// Looks up the value of a global string slice flag, returns nil if no string slice flag exists +func (c *Context) GlobalStringSlice(name string) []string { + return lookupStringSlice(name, c.globalSet) +} + +// Looks up the value of a global int slice flag, returns nil if no int slice flag exists +func (c *Context) GlobalIntSlice(name string) []int { + return lookupIntSlice(name, c.globalSet) +} + +// Looks up the value of a global generic flag, returns nil if no generic flag exists +func (c *Context) GlobalGeneric(name string) interface{} { + return lookupGeneric(name, c.globalSet) +} + +// Determines if the flag was actually set +func (c *Context) IsSet(name string) bool { + if c.setFlags == nil { + c.setFlags = make(map[string]bool) + c.flagSet.Visit(func(f *flag.Flag) { + c.setFlags[f.Name] = true + }) + } + return c.setFlags[name] == true +} + +// Determines if the global flag was actually set +func (c *Context) GlobalIsSet(name string) bool { + if c.globalSetFlags == nil { + c.globalSetFlags = make(map[string]bool) + c.globalSet.Visit(func(f *flag.Flag) { + c.globalSetFlags[f.Name] = true + }) + } + return c.globalSetFlags[name] == true +} + +// Returns a slice of flag names used in this context. +func (c *Context) FlagNames() (names []string) { + for _, flag := range c.Command.Flags { + name := strings.Split(flag.getName(), ",")[0] + if name == "help" { + continue + } + names = append(names, name) + } + return +} + +// Returns a slice of global flag names used by the app. +func (c *Context) GlobalFlagNames() (names []string) { + for _, flag := range c.App.Flags { + name := strings.Split(flag.getName(), ",")[0] + if name == "help" || name == "version" { + continue + } + names = append(names, name) + } + return +} + +type Args []string + +// Returns the command line arguments associated with the context. +func (c *Context) Args() Args { + args := Args(c.flagSet.Args()) + return args +} + +// Returns the nth argument, or else a blank string +func (a Args) Get(n int) string { + if len(a) > n { + return a[n] + } + return "" +} + +// Returns the first argument, or else a blank string +func (a Args) First() string { + return a.Get(0) +} + +// Return the rest of the arguments (not the first one) +// or else an empty string slice +func (a Args) Tail() []string { + if len(a) >= 2 { + return []string(a)[1:] + } + return []string{} +} + +// Checks if there are any arguments present +func (a Args) Present() bool { + return len(a) != 0 +} + +// Swaps arguments at the given indexes +func (a Args) Swap(from, to int) error { + if from >= len(a) || to >= len(a) { + return errors.New("index out of range") + } + a[from], a[to] = a[to], a[from] + return nil +} + +func lookupInt(name string, set *flag.FlagSet) int { + f := set.Lookup(name) + if f != nil { + val, err := strconv.Atoi(f.Value.String()) + if err != nil { + return 0 + } + return val + } + + return 0 +} + +func lookupDuration(name string, set *flag.FlagSet) time.Duration { + f := set.Lookup(name) + if f != nil { + val, err := time.ParseDuration(f.Value.String()) + if err == nil { + return val + } + } + + return 0 +} + +func lookupFloat64(name string, set *flag.FlagSet) float64 { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseFloat(f.Value.String(), 64) + if err != nil { + return 0 + } + return val + } + + return 0 +} + +func lookupString(name string, set *flag.FlagSet) string { + f := set.Lookup(name) + if f != nil { + return f.Value.String() + } + + return "" +} + +func lookupStringSlice(name string, set *flag.FlagSet) []string { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*StringSlice)).Value() + + } + + return nil +} + +func lookupIntSlice(name string, set *flag.FlagSet) []int { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*IntSlice)).Value() + + } + + return nil +} + +func lookupGeneric(name string, set *flag.FlagSet) interface{} { + f := set.Lookup(name) + if f != nil { + return f.Value + } + return nil +} + +func lookupBool(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return false + } + return val + } + + return false +} + +func lookupBoolT(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseBool(f.Value.String()) + if err != nil { + return true + } + return val + } + + return false +} + +func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { + switch ff.Value.(type) { + case *StringSlice: + default: + set.Set(name, ff.Value.String()) + } +} + +func normalizeFlags(flags []Flag, set *flag.FlagSet) error { + visited := make(map[string]bool) + set.Visit(func(f *flag.Flag) { + visited[f.Name] = true + }) + for _, f := range flags { + parts := strings.Split(f.getName(), ",") + if len(parts) == 1 { + continue + } + var ff *flag.Flag + for _, name := range parts { + name = strings.Trim(name, " ") + if visited[name] { + if ff != nil { + return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) + } + ff = set.Lookup(name) + } + } + if ff == nil { + continue + } + for _, name := range parts { + name = strings.Trim(name, " ") + if !visited[name] { + copyFlag(name, ff, set) + } + } + } + return nil +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go new file mode 100644 index 0000000000..7c9a4436fc --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go @@ -0,0 +1,99 @@ +package cli_test + +import ( + "flag" + "testing" + "time" + + "github.com/codegangsta/cli" +) + +func TestNewContext(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Int("myflag", 42, "doc") + command := cli.Command{Name: "mycommand"} + c := cli.NewContext(nil, set, globalSet) + c.Command = command + expect(t, c.Int("myflag"), 12) + expect(t, c.GlobalInt("myflag"), 42) + expect(t, c.Command.Name, "mycommand") +} + +func TestContext_Int(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.Int("myflag"), 12) +} + +func TestContext_Duration(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Duration("myflag", time.Duration(12*time.Second), "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) +} + +func TestContext_String(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.String("myflag", "hello world", "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.String("myflag"), "hello world") +} + +func TestContext_Bool(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.Bool("myflag"), false) +} + +func TestContext_BoolT(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", true, "doc") + c := cli.NewContext(nil, set, set) + expect(t, c.BoolT("myflag"), true) +} + +func TestContext_Args(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := cli.NewContext(nil, set, set) + set.Parse([]string{"--myflag", "bat", "baz"}) + expect(t, len(c.Args()), 2) + expect(t, c.Bool("myflag"), true) +} + +func TestContext_IsSet(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + set.String("otherflag", "hello world", "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Bool("myflagGlobal", true, "doc") + c := cli.NewContext(nil, set, globalSet) + set.Parse([]string{"--myflag", "bat", "baz"}) + globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) + expect(t, c.IsSet("myflag"), true) + expect(t, c.IsSet("otherflag"), false) + expect(t, c.IsSet("bogusflag"), false) + expect(t, c.IsSet("myflagGlobal"), false) +} + +func TestContext_GlobalIsSet(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + set.String("otherflag", "hello world", "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Bool("myflagGlobal", true, "doc") + globalSet.Bool("myflagGlobalUnset", true, "doc") + c := cli.NewContext(nil, set, globalSet) + set.Parse([]string{"--myflag", "bat", "baz"}) + globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) + expect(t, c.GlobalIsSet("myflag"), false) + expect(t, c.GlobalIsSet("otherflag"), false) + expect(t, c.GlobalIsSet("bogusflag"), false) + expect(t, c.GlobalIsSet("myflagGlobal"), true) + expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) + expect(t, c.GlobalIsSet("bogusGlobal"), false) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/flag.go b/Godeps/_workspace/src/github.com/codegangsta/cli/flag.go new file mode 100644 index 0000000000..ddd6ef8a71 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/flag.go @@ -0,0 +1,447 @@ +package cli + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" + "time" +) + +// This flag enables bash-completion for all commands and subcommands +var BashCompletionFlag = BoolFlag{ + Name: "generate-bash-completion", +} + +// This flag prints the version for the application +var VersionFlag = BoolFlag{ + Name: "version, v", + Usage: "print the version", +} + +// This flag prints the help for all commands and subcommands +var HelpFlag = BoolFlag{ + Name: "help, h", + Usage: "show help", +} + +// Flag is a common interface related to parsing flags in cli. +// For more advanced flag parsing techniques, it is recomended that +// this interface be implemented. +type Flag interface { + fmt.Stringer + // Apply Flag settings to the given flag set + Apply(*flag.FlagSet) + getName() string +} + +func flagSet(name string, flags []Flag) *flag.FlagSet { + set := flag.NewFlagSet(name, flag.ContinueOnError) + + for _, f := range flags { + f.Apply(set) + } + return set +} + +func eachName(longName string, fn func(string)) { + parts := strings.Split(longName, ",") + for _, name := range parts { + name = strings.Trim(name, " ") + fn(name) + } +} + +// Generic is a generic parseable type identified by a specific flag +type Generic interface { + Set(value string) error + String() string +} + +// GenericFlag is the flag type for types implementing Generic +type GenericFlag struct { + Name string + Value Generic + Usage string + EnvVar string +} + +func (f GenericFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage)) +} + +func (f GenericFlag) Apply(set *flag.FlagSet) { + val := f.Value + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + val.Set(envVal) + break + } + } + } + + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) +} + +func (f GenericFlag) getName() string { + return f.Name +} + +type StringSlice []string + +func (f *StringSlice) Set(value string) error { + *f = append(*f, value) + return nil +} + +func (f *StringSlice) String() string { + return fmt.Sprintf("%s", *f) +} + +func (f *StringSlice) Value() []string { + return *f +} + +type StringSliceFlag struct { + Name string + Value *StringSlice + Usage string + EnvVar string +} + +func (f StringSliceFlag) String() string { + firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") + pref := prefixFor(firstName) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) +} + +func (f StringSliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + newVal := &StringSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + newVal.Set(s) + } + f.Value = newVal + break + } + } + } + + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) +} + +func (f StringSliceFlag) getName() string { + return f.Name +} + +type IntSlice []int + +func (f *IntSlice) Set(value string) error { + + tmp, err := strconv.Atoi(value) + if err != nil { + return err + } else { + *f = append(*f, tmp) + } + return nil +} + +func (f *IntSlice) String() string { + return fmt.Sprintf("%d", *f) +} + +func (f *IntSlice) Value() []int { + return *f +} + +type IntSliceFlag struct { + Name string + Value *IntSlice + Usage string + EnvVar string +} + +func (f IntSliceFlag) String() string { + firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") + pref := prefixFor(firstName) + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) +} + +func (f IntSliceFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + newVal := &IntSlice{} + for _, s := range strings.Split(envVal, ",") { + s = strings.TrimSpace(s) + err := newVal.Set(s) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + } + } + f.Value = newVal + break + } + } + } + + eachName(f.Name, func(name string) { + set.Var(f.Value, name, f.Usage) + }) +} + +func (f IntSliceFlag) getName() string { + return f.Name +} + +type BoolFlag struct { + Name string + Usage string + EnvVar string +} + +func (f BoolFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) +} + +func (f BoolFlag) Apply(set *flag.FlagSet) { + val := false + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValBool, err := strconv.ParseBool(envVal) + if err == nil { + val = envValBool + } + break + } + } + } + + eachName(f.Name, func(name string) { + set.Bool(name, val, f.Usage) + }) +} + +func (f BoolFlag) getName() string { + return f.Name +} + +type BoolTFlag struct { + Name string + Usage string + EnvVar string +} + +func (f BoolTFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) +} + +func (f BoolTFlag) Apply(set *flag.FlagSet) { + val := true + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValBool, err := strconv.ParseBool(envVal) + if err == nil { + val = envValBool + break + } + } + } + } + + eachName(f.Name, func(name string) { + set.Bool(name, val, f.Usage) + }) +} + +func (f BoolTFlag) getName() string { + return f.Name +} + +type StringFlag struct { + Name string + Value string + Usage string + EnvVar string +} + +func (f StringFlag) String() string { + var fmtString string + fmtString = "%s %v\t%v" + + if len(f.Value) > 0 { + fmtString = "%s '%v'\t%v" + } else { + fmtString = "%s %v\t%v" + } + + return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) +} + +func (f StringFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + f.Value = envVal + break + } + } + } + + eachName(f.Name, func(name string) { + set.String(name, f.Value, f.Usage) + }) +} + +func (f StringFlag) getName() string { + return f.Name +} + +type IntFlag struct { + Name string + Value int + Usage string + EnvVar string +} + +func (f IntFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) +} + +func (f IntFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValInt, err := strconv.ParseUint(envVal, 10, 64) + if err == nil { + f.Value = int(envValInt) + break + } + } + } + } + + eachName(f.Name, func(name string) { + set.Int(name, f.Value, f.Usage) + }) +} + +func (f IntFlag) getName() string { + return f.Name +} + +type DurationFlag struct { + Name string + Value time.Duration + Usage string + EnvVar string +} + +func (f DurationFlag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) +} + +func (f DurationFlag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValDuration, err := time.ParseDuration(envVal) + if err == nil { + f.Value = envValDuration + break + } + } + } + } + + eachName(f.Name, func(name string) { + set.Duration(name, f.Value, f.Usage) + }) +} + +func (f DurationFlag) getName() string { + return f.Name +} + +type Float64Flag struct { + Name string + Value float64 + Usage string + EnvVar string +} + +func (f Float64Flag) String() string { + return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) +} + +func (f Float64Flag) Apply(set *flag.FlagSet) { + if f.EnvVar != "" { + for _, envVar := range strings.Split(f.EnvVar, ",") { + envVar = strings.TrimSpace(envVar) + if envVal := os.Getenv(envVar); envVal != "" { + envValFloat, err := strconv.ParseFloat(envVal, 10) + if err == nil { + f.Value = float64(envValFloat) + } + } + } + } + + eachName(f.Name, func(name string) { + set.Float64(name, f.Value, f.Usage) + }) +} + +func (f Float64Flag) getName() string { + return f.Name +} + +func prefixFor(name string) (prefix string) { + if len(name) == 1 { + prefix = "-" + } else { + prefix = "--" + } + + return +} + +func prefixedNames(fullName string) (prefixed string) { + parts := strings.Split(fullName, ",") + for i, name := range parts { + name = strings.Trim(name, " ") + prefixed += prefixFor(name) + name + if i < len(parts)-1 { + prefixed += ", " + } + } + return +} + +func withEnvHint(envVar, str string) string { + envText := "" + if envVar != "" { + envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $")) + } + return str + envText +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go new file mode 100644 index 0000000000..4f0ba555b1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go @@ -0,0 +1,743 @@ +package cli_test + +import ( + "fmt" + "os" + "reflect" + "strings" + "testing" + + "github.com/codegangsta/cli" +) + +var boolFlagTests = []struct { + name string + expected string +}{ + {"help", "--help\t"}, + {"h", "-h\t"}, +} + +func TestBoolFlagHelpOutput(t *testing.T) { + + for _, test := range boolFlagTests { + flag := cli.BoolFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +var stringFlagTests = []struct { + name string + value string + expected string +}{ + {"help", "", "--help \t"}, + {"h", "", "-h \t"}, + {"h", "", "-h \t"}, + {"test", "Something", "--test 'Something'\t"}, +} + +func TestStringFlagHelpOutput(t *testing.T) { + + for _, test := range stringFlagTests { + flag := cli.StringFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_FOO", "derp") + for _, test := range stringFlagTests { + flag := cli.StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_FOO]") { + t.Errorf("%s does not end with [$APP_FOO]", output) + } + } +} + +var stringSliceFlagTests = []struct { + name string + value *cli.StringSlice + expected string +}{ + {"help", func() *cli.StringSlice { + s := &cli.StringSlice{} + s.Set("") + return s + }(), "--help '--help option --help option'\t"}, + {"h", func() *cli.StringSlice { + s := &cli.StringSlice{} + s.Set("") + return s + }(), "-h '-h option -h option'\t"}, + {"h", func() *cli.StringSlice { + s := &cli.StringSlice{} + s.Set("") + return s + }(), "-h '-h option -h option'\t"}, + {"test", func() *cli.StringSlice { + s := &cli.StringSlice{} + s.Set("Something") + return s + }(), "--test '--test option --test option'\t"}, +} + +func TestStringSliceFlagHelpOutput(t *testing.T) { + + for _, test := range stringSliceFlagTests { + flag := cli.StringSliceFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_QWWX", "11,4") + for _, test := range stringSliceFlagTests { + flag := cli.StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_QWWX]") { + t.Errorf("%q does not end with [$APP_QWWX]", output) + } + } +} + +var intFlagTests = []struct { + name string + expected string +}{ + {"help", "--help '0'\t"}, + {"h", "-h '0'\t"}, +} + +func TestIntFlagHelpOutput(t *testing.T) { + + for _, test := range intFlagTests { + flag := cli.IntFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAR", "2") + for _, test := range intFlagTests { + flag := cli.IntFlag{Name: test.name, EnvVar: "APP_BAR"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_BAR]") { + t.Errorf("%s does not end with [$APP_BAR]", output) + } + } +} + +var durationFlagTests = []struct { + name string + expected string +}{ + {"help", "--help '0'\t"}, + {"h", "-h '0'\t"}, +} + +func TestDurationFlagHelpOutput(t *testing.T) { + + for _, test := range durationFlagTests { + flag := cli.DurationFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAR", "2h3m6s") + for _, test := range durationFlagTests { + flag := cli.DurationFlag{Name: test.name, EnvVar: "APP_BAR"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_BAR]") { + t.Errorf("%s does not end with [$APP_BAR]", output) + } + } +} + +var intSliceFlagTests = []struct { + name string + value *cli.IntSlice + expected string +}{ + {"help", &cli.IntSlice{}, "--help '--help option --help option'\t"}, + {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, + {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, + {"test", func() *cli.IntSlice { + i := &cli.IntSlice{} + i.Set("9") + return i + }(), "--test '--test option --test option'\t"}, +} + +func TestIntSliceFlagHelpOutput(t *testing.T) { + + for _, test := range intSliceFlagTests { + flag := cli.IntSliceFlag{Name: test.name, Value: test.value} + output := flag.String() + + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_SMURF", "42,3") + for _, test := range intSliceFlagTests { + flag := cli.IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_SMURF]") { + t.Errorf("%q does not end with [$APP_SMURF]", output) + } + } +} + +var float64FlagTests = []struct { + name string + expected string +}{ + {"help", "--help '0'\t"}, + {"h", "-h '0'\t"}, +} + +func TestFloat64FlagHelpOutput(t *testing.T) { + + for _, test := range float64FlagTests { + flag := cli.Float64Flag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%s does not match %s", output, test.expected) + } + } +} + +func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_BAZ", "99.4") + for _, test := range float64FlagTests { + flag := cli.Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_BAZ]") { + t.Errorf("%s does not end with [$APP_BAZ]", output) + } + } +} + +var genericFlagTests = []struct { + name string + value cli.Generic + expected string +}{ + {"help", &Parser{}, "--help \t`-help option -help option` "}, + {"h", &Parser{}, "-h \t`-h option -h option` "}, + {"test", &Parser{}, "--test \t`-test option -test option` "}, +} + +func TestGenericFlagHelpOutput(t *testing.T) { + + for _, test := range genericFlagTests { + flag := cli.GenericFlag{Name: test.name} + output := flag.String() + + if output != test.expected { + t.Errorf("%q does not match %q", output, test.expected) + } + } +} + +func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { + os.Clearenv() + os.Setenv("APP_ZAP", "3") + for _, test := range genericFlagTests { + flag := cli.GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} + output := flag.String() + + if !strings.HasSuffix(output, " [$APP_ZAP]") { + t.Errorf("%s does not end with [$APP_ZAP]", output) + } + } +} + +func TestParseMultiString(t *testing.T) { + (&cli.App{ + Flags: []cli.Flag{ + cli.StringFlag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.String("serve") != "10" { + t.Errorf("main name not set") + } + if ctx.String("s") != "10" { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10"}) +} + +func TestParseMultiStringFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_COUNT", "20") + (&cli.App{ + Flags: []cli.Flag{ + cli.StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, + }, + Action: func(ctx *cli.Context) { + if ctx.String("count") != "20" { + t.Errorf("main name not set") + } + if ctx.String("c") != "20" { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiStringFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_COUNT", "20") + (&cli.App{ + Flags: []cli.Flag{ + cli.StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, + }, + Action: func(ctx *cli.Context) { + if ctx.String("count") != "20" { + t.Errorf("main name not set") + } + if ctx.String("c") != "20" { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiStringSlice(t *testing.T) { + (&cli.App{ + Flags: []cli.Flag{ + cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10", "-s", "20"}) +} + +func TestParseMultiStringSliceFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&cli.App{ + Flags: []cli.Flag{ + cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "APP_INTERVALS"}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&cli.App{ + Flags: []cli.Flag{ + cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiInt(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.IntFlag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Int("serve") != 10 { + t.Errorf("main name not set") + } + if ctx.Int("s") != 10 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10"}) +} + +func TestParseMultiIntFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "10") + a := cli.App{ + Flags: []cli.Flag{ + cli.IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Int("timeout") != 10 { + t.Errorf("main name not set") + } + if ctx.Int("t") != 10 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiIntFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "10") + a := cli.App{ + Flags: []cli.Flag{ + cli.IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Int("timeout") != 10 { + t.Errorf("main name not set") + } + if ctx.Int("t") != 10 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiIntSlice(t *testing.T) { + (&cli.App{ + Flags: []cli.Flag{ + cli.IntSliceFlag{Name: "serve, s", Value: &cli.IntSlice{}}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { + t.Errorf("short name not set") + } + }, + }).Run([]string{"run", "-s", "10", "-s", "20"}) +} + +func TestParseMultiIntSliceFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&cli.App{ + Flags: []cli.Flag{ + cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "APP_INTERVALS"}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_INTERVALS", "20,30,40") + + (&cli.App{ + Flags: []cli.Flag{ + cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { + t.Errorf("short name not set from env") + } + }, + }).Run([]string{"run"}) +} + +func TestParseMultiFloat64(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.Float64Flag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Float64("serve") != 10.2 { + t.Errorf("main name not set") + } + if ctx.Float64("s") != 10.2 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10.2"}) +} + +func TestParseMultiFloat64FromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + a := cli.App{ + Flags: []cli.Flag{ + cli.Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Float64("timeout") != 15.5 { + t.Errorf("main name not set") + } + if ctx.Float64("t") != 15.5 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiFloat64FromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_TIMEOUT_SECONDS", "15.5") + a := cli.App{ + Flags: []cli.Flag{ + cli.Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Float64("timeout") != 15.5 { + t.Errorf("main name not set") + } + if ctx.Float64("t") != 15.5 { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBool(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.BoolFlag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Bool("serve") != true { + t.Errorf("main name not set") + } + if ctx.Bool("s") != true { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "--serve"}) +} + +func TestParseMultiBoolFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "1") + a := cli.App{ + Flags: []cli.Flag{ + cli.BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Bool("debug") != true { + t.Errorf("main name not set from env") + } + if ctx.Bool("d") != true { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBoolFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "1") + a := cli.App{ + Flags: []cli.Flag{ + cli.BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, + }, + Action: func(ctx *cli.Context) { + if ctx.Bool("debug") != true { + t.Errorf("main name not set from env") + } + if ctx.Bool("d") != true { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBoolT(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.BoolTFlag{Name: "serve, s"}, + }, + Action: func(ctx *cli.Context) { + if ctx.BoolT("serve") != true { + t.Errorf("main name not set") + } + if ctx.BoolT("s") != true { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "--serve"}) +} + +func TestParseMultiBoolTFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "0") + a := cli.App{ + Flags: []cli.Flag{ + cli.BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, + }, + Action: func(ctx *cli.Context) { + if ctx.BoolT("debug") != false { + t.Errorf("main name not set from env") + } + if ctx.BoolT("d") != false { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseMultiBoolTFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_DEBUG", "0") + a := cli.App{ + Flags: []cli.Flag{ + cli.BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, + }, + Action: func(ctx *cli.Context) { + if ctx.BoolT("debug") != false { + t.Errorf("main name not set from env") + } + if ctx.BoolT("d") != false { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +type Parser [2]string + +func (p *Parser) Set(value string) error { + parts := strings.Split(value, ",") + if len(parts) != 2 { + return fmt.Errorf("invalid format") + } + + (*p)[0] = parts[0] + (*p)[1] = parts[1] + + return nil +} + +func (p *Parser) String() string { + return fmt.Sprintf("%s,%s", p[0], p[1]) +} + +func TestParseGeneric(t *testing.T) { + a := cli.App{ + Flags: []cli.Flag{ + cli.GenericFlag{Name: "serve, s", Value: &Parser{}}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { + t.Errorf("main name not set") + } + if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { + t.Errorf("short name not set") + } + }, + } + a.Run([]string{"run", "-s", "10,20"}) +} + +func TestParseGenericFromEnv(t *testing.T) { + os.Clearenv() + os.Setenv("APP_SERVE", "20,30") + a := cli.App{ + Flags: []cli.Flag{ + cli.GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { + t.Errorf("main name not set from env") + } + if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { + t.Errorf("short name not set from env") + } + }, + } + a.Run([]string{"run"}) +} + +func TestParseGenericFromEnvCascade(t *testing.T) { + os.Clearenv() + os.Setenv("APP_FOO", "99,2000") + a := cli.App{ + Flags: []cli.Flag{ + cli.GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, + }, + Action: func(ctx *cli.Context) { + if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { + t.Errorf("value not set from env") + } + }, + } + a.Run([]string{"run"}) +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/help.go b/Godeps/_workspace/src/github.com/codegangsta/cli/help.go new file mode 100644 index 0000000000..5667d3a8c5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/help.go @@ -0,0 +1,224 @@ +package cli + +import ( + "fmt" + "os" + "text/tabwriter" + "text/template" +) + +// The text template for the Default help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var AppHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] + +VERSION: + {{.Version}}{{if or .Author .Email}} + +AUTHOR:{{if .Author}} + {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}} + {{.Email}}{{end}}{{end}} + +COMMANDS: + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{if .Flags}} +GLOBAL OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}} +` + +// The text template for the command help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var CommandHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + command {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} + +DESCRIPTION: + {{.Description}}{{end}}{{if .Flags}} + +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{ end }} +` + +// The text template for the subcommand help topic. +// cli.go uses text/template to render templates. You can +// render custom help text by setting this variable. +var SubcommandHelpTemplate = `NAME: + {{.Name}} - {{.Usage}} + +USAGE: + {{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...] + +COMMANDS: + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}}{{if .Flags}} +OPTIONS: + {{range .Flags}}{{.}} + {{end}}{{end}} +` + +var helpCommand = Command{ + Name: "help", + ShortName: "h", + Usage: "Shows a list of commands or help for one command", + Action: func(c *Context) { + args := c.Args() + if args.Present() { + ShowCommandHelp(c, args.First()) + } else { + ShowAppHelp(c) + } + }, +} + +var helpSubcommand = Command{ + Name: "help", + ShortName: "h", + Usage: "Shows a list of commands or help for one command", + Action: func(c *Context) { + args := c.Args() + if args.Present() { + ShowCommandHelp(c, args.First()) + } else { + ShowSubcommandHelp(c) + } + }, +} + +// Prints help for the App +var HelpPrinter = printHelp + +// Prints version for the App +var VersionPrinter = printVersion + +func ShowAppHelp(c *Context) { + HelpPrinter(AppHelpTemplate, c.App) +} + +// Prints the list of subcommands as the default app completion method +func DefaultAppComplete(c *Context) { + for _, command := range c.App.Commands { + fmt.Println(command.Name) + if command.ShortName != "" { + fmt.Println(command.ShortName) + } + } +} + +// Prints help for the given command +func ShowCommandHelp(c *Context, command string) { + for _, c := range c.App.Commands { + if c.HasName(command) { + HelpPrinter(CommandHelpTemplate, c) + return + } + } + + if c.App.CommandNotFound != nil { + c.App.CommandNotFound(c, command) + } else { + fmt.Printf("No help topic for '%v'\n", command) + } +} + +// Prints help for the given subcommand +func ShowSubcommandHelp(c *Context) { + HelpPrinter(SubcommandHelpTemplate, c.App) +} + +// Prints the version number of the App +func ShowVersion(c *Context) { + VersionPrinter(c) +} + +func printVersion(c *Context) { + fmt.Printf("%v version %v\n", c.App.Name, c.App.Version) +} + +// Prints the lists of commands within a given context +func ShowCompletions(c *Context) { + a := c.App + if a != nil && a.BashComplete != nil { + a.BashComplete(c) + } +} + +// Prints the custom completions for a given command +func ShowCommandCompletions(ctx *Context, command string) { + c := ctx.App.Command(command) + if c != nil && c.BashComplete != nil { + c.BashComplete(ctx) + } +} + +func printHelp(templ string, data interface{}) { + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + t := template.Must(template.New("help").Parse(templ)) + err := t.Execute(w, data) + if err != nil { + panic(err) + } + w.Flush() +} + +func checkVersion(c *Context) bool { + if c.GlobalBool("version") { + ShowVersion(c) + return true + } + + return false +} + +func checkHelp(c *Context) bool { + if c.GlobalBool("h") || c.GlobalBool("help") { + ShowAppHelp(c) + return true + } + + return false +} + +func checkCommandHelp(c *Context, name string) bool { + if c.Bool("h") || c.Bool("help") { + ShowCommandHelp(c, name) + return true + } + + return false +} + +func checkSubcommandHelp(c *Context) bool { + if c.GlobalBool("h") || c.GlobalBool("help") { + ShowSubcommandHelp(c) + return true + } + + return false +} + +func checkCompletions(c *Context) bool { + if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { + ShowCompletions(c) + return true + } + + return false +} + +func checkCommandCompletions(c *Context, name string) bool { + if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { + ShowCommandCompletions(c, name) + return true + } + + return false +} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go new file mode 100644 index 0000000000..cdc4feb2fc --- /dev/null +++ b/Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go @@ -0,0 +1,19 @@ +package cli_test + +import ( + "reflect" + "testing" +) + +/* Test Helpers */ +func expect(t *testing.T, a interface{}, b interface{}) { + if a != b { + t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func refute(t *testing.T, a interface{}, b interface{}) { + if a == b { + t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/LICENSE b/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/LICENSE deleted file mode 100644 index ebcfbcc779..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014 The Docker & Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/MAINTAINERS b/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/MAINTAINERS deleted file mode 100644 index e0f18f14f1..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/MAINTAINERS +++ /dev/null @@ -1 +0,0 @@ -Victor Vieux (@vieux) diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/README.md b/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/README.md deleted file mode 100644 index da00efa336..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/README.md +++ /dev/null @@ -1,40 +0,0 @@ -Package mflag (aka multiple-flag) implements command-line flag parsing. -It's an **hacky** fork of the [official golang package](http://golang.org/pkg/flag/) - -It adds: - -* both short and long flag version -`./example -s red` `./example --string blue` - -* multiple names for the same option -``` -$>./example -h -Usage of example: - -s, --string="": a simple string -``` - -___ -It is very flexible on purpose, so you can do things like: -``` -$>./example -h -Usage of example: - -s, -string, --string="": a simple string -``` - -Or: -``` -$>./example -h -Usage of example: - -oldflag, --newflag="": a simple string -``` - -You can also hide some flags from the usage, so if we want only `--newflag`: -``` -$>./example -h -Usage of example: - --newflag="": a simple string -$>./example -oldflag str -str -``` - -See [example.go](example/example.go) for more details. diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/example/example.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/example/example.go deleted file mode 100644 index 2e766dd1e5..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/example/example.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - - flag "github.com/docker/docker/pkg/mflag" -) - -var ( - i int - str string - b, b2, h bool -) - -func init() { - flag.Bool([]string{"#hp", "#-halp"}, false, "display the halp") - flag.BoolVar(&b, []string{"b", "#bal", "#bol", "-bal"}, false, "a simple bool") - flag.BoolVar(&b, []string{"g", "#gil"}, false, "a simple bool") - flag.BoolVar(&b2, []string{"#-bool"}, false, "a simple bool") - flag.IntVar(&i, []string{"-integer", "-number"}, -1, "a simple integer") - flag.StringVar(&str, []string{"s", "#hidden", "-string"}, "", "a simple string") //-s -hidden and --string will work, but -hidden won't be in the usage - flag.BoolVar(&h, []string{"h", "#help", "-help"}, false, "display the help") - flag.StringVar(&str, []string{"mode"}, "mode1", "set the mode\nmode1: use the mode1\nmode2: use the mode2\nmode3: use the mode3") - flag.Parse() -} -func main() { - if h { - flag.PrintDefaults() - } else { - fmt.Printf("s/#hidden/-string: %s\n", str) - fmt.Printf("b: %t\n", b) - fmt.Printf("-bool: %t\n", b2) - fmt.Printf("s/#hidden/-string(via lookup): %s\n", flag.Lookup("s").Value.String()) - fmt.Printf("ARGS: %v\n", flag.Args()) - } -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag.go deleted file mode 100644 index c9061c2d73..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag.go +++ /dev/null @@ -1,1013 +0,0 @@ -// Copyright 2014 The Docker & Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - Package flag implements command-line flag parsing. - - Usage: - - Define flags using flag.String(), Bool(), Int(), etc. - - This declares an integer flag, -f or --flagname, stored in the pointer ip, with type *int. - import "flag /github.com/docker/docker/pkg/mflag" - var ip = flag.Int([]string{"f", "-flagname"}, 1234, "help message for flagname") - If you like, you can bind the flag to a variable using the Var() functions. - var flagvar int - func init() { - // -flaghidden will work, but will be hidden from the usage - flag.IntVar(&flagvar, []string{"f", "#flaghidden", "-flagname"}, 1234, "help message for flagname") - } - Or you can create custom flags that satisfy the Value interface (with - pointer receivers) and couple them to flag parsing by - flag.Var(&flagVal, []string{"name"}, "help message for flagname") - For such flags, the default value is just the initial value of the variable. - - You can also add "deprecated" flags, they are still usable, bur are not shown - in the usage and will display a warning when you try to use them: - var ip = flag.Int([]string{"f", "#flagname", "-flagname"}, 1234, "help message for flagname") - this will display: `Warning: '-flagname' is deprecated, it will be replaced by '--flagname' soon. See usage.` and - var ip = flag.Int([]string{"f", "#flagname"}, 1234, "help message for flagname") - will display: `Warning: '-t' is deprecated, it will be removed soon. See usage.` - - You can also group one letter flags, bif you declare - var v = flag.Bool([]string{"v", "-verbose"}, false, "help message for verbose") - var s = flag.Bool([]string{"s", "-slow"}, false, "help message for slow") - you will be able to use the -vs or -sv - - After all flags are defined, call - flag.Parse() - to parse the command line into the defined flags. - - Flags may then be used directly. If you're using the flags themselves, - they are all pointers; if you bind to variables, they're values. - fmt.Println("ip has value ", *ip) - fmt.Println("flagvar has value ", flagvar) - - After parsing, the arguments after the flag are available as the - slice flag.Args() or individually as flag.Arg(i). - The arguments are indexed from 0 through flag.NArg()-1. - - Command line flag syntax: - -flag - -flag=x - -flag="x" - -flag='x' - -flag x // non-boolean flags only - One or two minus signs may be used; they are equivalent. - The last form is not permitted for boolean flags because the - meaning of the command - cmd -x * - will change if there is a file called 0, false, etc. You must - use the -flag=false form to turn off a boolean flag. - - Flag parsing stops just before the first non-flag argument - ("-" is a non-flag argument) or after the terminator "--". - - Integer flags accept 1234, 0664, 0x1234 and may be negative. - Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. - Duration flags accept any input valid for time.ParseDuration. - - The default set of command-line flags is controlled by - top-level functions. The FlagSet type allows one to define - independent sets of flags, such as to implement subcommands - in a command-line interface. The methods of FlagSet are - analogous to the top-level functions for the command-line - flag set. -*/ -package mflag - -import ( - "errors" - "fmt" - "io" - "os" - "sort" - "strconv" - "strings" - "text/tabwriter" - "time" -) - -// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. -var ErrHelp = errors.New("flag: help requested") - -// ErrRetry is the error returned if you need to try letter by letter -var ErrRetry = errors.New("flag: retry") - -// -- bool Value -type boolValue bool - -func newBoolValue(val bool, p *bool) *boolValue { - *p = val - return (*boolValue)(p) -} - -func (b *boolValue) Set(s string) error { - v, err := strconv.ParseBool(s) - *b = boolValue(v) - return err -} - -func (b *boolValue) Get() interface{} { return bool(*b) } - -func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } - -func (b *boolValue) IsBoolFlag() bool { return true } - -// optional interface to indicate boolean flags that can be -// supplied without "=value" text -type boolFlag interface { - Value - IsBoolFlag() bool -} - -// -- int Value -type intValue int - -func newIntValue(val int, p *int) *intValue { - *p = val - return (*intValue)(p) -} - -func (i *intValue) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) - *i = intValue(v) - return err -} - -func (i *intValue) Get() interface{} { return int(*i) } - -func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } - -// -- int64 Value -type int64Value int64 - -func newInt64Value(val int64, p *int64) *int64Value { - *p = val - return (*int64Value)(p) -} - -func (i *int64Value) Set(s string) error { - v, err := strconv.ParseInt(s, 0, 64) - *i = int64Value(v) - return err -} - -func (i *int64Value) Get() interface{} { return int64(*i) } - -func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } - -// -- uint Value -type uintValue uint - -func newUintValue(val uint, p *uint) *uintValue { - *p = val - return (*uintValue)(p) -} - -func (i *uintValue) Set(s string) error { - v, err := strconv.ParseUint(s, 0, 64) - *i = uintValue(v) - return err -} - -func (i *uintValue) Get() interface{} { return uint(*i) } - -func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } - -// -- uint64 Value -type uint64Value uint64 - -func newUint64Value(val uint64, p *uint64) *uint64Value { - *p = val - return (*uint64Value)(p) -} - -func (i *uint64Value) Set(s string) error { - v, err := strconv.ParseUint(s, 0, 64) - *i = uint64Value(v) - return err -} - -func (i *uint64Value) Get() interface{} { return uint64(*i) } - -func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } - -// -- string Value -type stringValue string - -func newStringValue(val string, p *string) *stringValue { - *p = val - return (*stringValue)(p) -} - -func (s *stringValue) Set(val string) error { - *s = stringValue(val) - return nil -} - -func (s *stringValue) Get() interface{} { return string(*s) } - -func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } - -// -- float64 Value -type float64Value float64 - -func newFloat64Value(val float64, p *float64) *float64Value { - *p = val - return (*float64Value)(p) -} - -func (f *float64Value) Set(s string) error { - v, err := strconv.ParseFloat(s, 64) - *f = float64Value(v) - return err -} - -func (f *float64Value) Get() interface{} { return float64(*f) } - -func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } - -// -- time.Duration Value -type durationValue time.Duration - -func newDurationValue(val time.Duration, p *time.Duration) *durationValue { - *p = val - return (*durationValue)(p) -} - -func (d *durationValue) Set(s string) error { - v, err := time.ParseDuration(s) - *d = durationValue(v) - return err -} - -func (d *durationValue) Get() interface{} { return time.Duration(*d) } - -func (d *durationValue) String() string { return (*time.Duration)(d).String() } - -// Value is the interface to the dynamic value stored in a flag. -// (The default value is represented as a string.) -// -// If a Value has an IsBoolFlag() bool method returning true, -// the command-line parser makes -name equivalent to -name=true -// rather than using the next command-line argument. -type Value interface { - String() string - Set(string) error -} - -// Getter is an interface that allows the contents of a Value to be retrieved. -// It wraps the Value interface, rather than being part of it, because it -// appeared after Go 1 and its compatibility rules. All Value types provided -// by this package satisfy the Getter interface. -type Getter interface { - Value - Get() interface{} -} - -// ErrorHandling defines how to handle flag parsing errors. -type ErrorHandling int - -const ( - ContinueOnError ErrorHandling = iota - ExitOnError - PanicOnError -) - -// A FlagSet represents a set of defined flags. The zero value of a FlagSet -// has no name and has ContinueOnError error handling. -type FlagSet struct { - // Usage is the function called when an error occurs while parsing flags. - // The field is a function (not a method) that may be changed to point to - // a custom error handler. - Usage func() - - name string - parsed bool - actual map[string]*Flag - formal map[string]*Flag - args []string // arguments after flags - errorHandling ErrorHandling - output io.Writer // nil means stderr; use out() accessor -} - -// A Flag represents the state of a flag. -type Flag struct { - Names []string // name as it appears on command line - Usage string // help message - Value Value // value as set - DefValue string // default value (as text); for usage message -} - -type flagSlice []string - -func (p flagSlice) Len() int { return len(p) } -func (p flagSlice) Less(i, j int) bool { - pi, pj := strings.TrimPrefix(p[i], "-"), strings.TrimPrefix(p[j], "-") - lpi, lpj := strings.ToLower(pi), strings.ToLower(pj) - if lpi != lpj { - return lpi < lpj - } - return pi < pj -} -func (p flagSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// sortFlags returns the flags as a slice in lexicographical sorted order. -func sortFlags(flags map[string]*Flag) []*Flag { - var list flagSlice - - // The sorted list is based on the first name, when flag map might use the other names. - nameMap := make(map[string]string) - - for n, f := range flags { - fName := strings.TrimPrefix(f.Names[0], "#") - nameMap[fName] = n - if len(f.Names) == 1 { - list = append(list, fName) - continue - } - - found := false - for _, name := range list { - if name == fName { - found = true - break - } - } - if !found { - list = append(list, fName) - } - } - sort.Sort(list) - result := make([]*Flag, len(list)) - for i, name := range list { - result[i] = flags[nameMap[name]] - } - return result -} - -func (f *FlagSet) out() io.Writer { - if f.output == nil { - return os.Stderr - } - return f.output -} - -// SetOutput sets the destination for usage and error messages. -// If output is nil, os.Stderr is used. -func (f *FlagSet) SetOutput(output io.Writer) { - f.output = output -} - -// VisitAll visits the flags in lexicographical order, calling fn for each. -// It visits all flags, even those not set. -func (f *FlagSet) VisitAll(fn func(*Flag)) { - for _, flag := range sortFlags(f.formal) { - fn(flag) - } -} - -// VisitAll visits the command-line flags in lexicographical order, calling -// fn for each. It visits all flags, even those not set. -func VisitAll(fn func(*Flag)) { - CommandLine.VisitAll(fn) -} - -// Visit visits the flags in lexicographical order, calling fn for each. -// It visits only those flags that have been set. -func (f *FlagSet) Visit(fn func(*Flag)) { - for _, flag := range sortFlags(f.actual) { - fn(flag) - } -} - -// Visit visits the command-line flags in lexicographical order, calling fn -// for each. It visits only those flags that have been set. -func Visit(fn func(*Flag)) { - CommandLine.Visit(fn) -} - -// Lookup returns the Flag structure of the named flag, returning nil if none exists. -func (f *FlagSet) Lookup(name string) *Flag { - return f.formal[name] -} - -// Indicates whether the specified flag was specified at all on the cmd line -func (f *FlagSet) IsSet(name string) bool { - return f.actual[name] != nil -} - -// Lookup returns the Flag structure of the named command-line flag, -// returning nil if none exists. -func Lookup(name string) *Flag { - return CommandLine.formal[name] -} - -// Indicates whether the specified flag was specified at all on the cmd line -func IsSet(name string) bool { - return CommandLine.IsSet(name) -} - -// Set sets the value of the named flag. -func (f *FlagSet) Set(name, value string) error { - flag, ok := f.formal[name] - if !ok { - return fmt.Errorf("no such flag -%v", name) - } - err := flag.Value.Set(value) - if err != nil { - return err - } - if f.actual == nil { - f.actual = make(map[string]*Flag) - } - f.actual[name] = flag - return nil -} - -// Set sets the value of the named command-line flag. -func Set(name, value string) error { - return CommandLine.Set(name, value) -} - -// PrintDefaults prints, to standard error unless configured -// otherwise, the default values of all defined flags in the set. -func (f *FlagSet) PrintDefaults() { - writer := tabwriter.NewWriter(f.out(), 20, 1, 3, ' ', 0) - f.VisitAll(func(flag *Flag) { - format := " -%s=%s" - if _, ok := flag.Value.(*stringValue); ok { - // put quotes on the value - format = " -%s=%q" - } - names := []string{} - for _, name := range flag.Names { - if name[0] != '#' { - names = append(names, name) - } - } - if len(names) > 0 { - fmt.Fprintf(writer, format, strings.Join(names, ", -"), flag.DefValue) - for i, line := range strings.Split(flag.Usage, "\n") { - if i != 0 { - line = " " + line - } - fmt.Fprintln(writer, "\t", line) - } - } - }) - writer.Flush() -} - -// PrintDefaults prints to standard error the default values of all defined command-line flags. -func PrintDefaults() { - CommandLine.PrintDefaults() -} - -// defaultUsage is the default function to print a usage message. -func defaultUsage(f *FlagSet) { - if f.name == "" { - fmt.Fprintf(f.out(), "Usage:\n") - } else { - fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) - } - f.PrintDefaults() -} - -// NOTE: Usage is not just defaultUsage(CommandLine) -// because it serves (via godoc flag Usage) as the example -// for how to write your own usage function. - -// Usage prints to standard error a usage message documenting all defined command-line flags. -// The function is a variable that may be changed to point to a custom function. -var Usage = func() { - fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) - PrintDefaults() -} - -// FlagCount returns the number of flags that have been defined. -func (f *FlagSet) FlagCount() int { return len(sortFlags(f.formal)) } - -// FlagCountUndeprecated returns the number of undeprecated flags that have been defined. -func (f *FlagSet) FlagCountUndeprecated() int { - count := 0 - for _, flag := range sortFlags(f.formal) { - for _, name := range flag.Names { - if name[0] != '#' { - count++ - break - } - } - } - return count -} - -// NFlag returns the number of flags that have been set. -func (f *FlagSet) NFlag() int { return len(f.actual) } - -// NFlag returns the number of command-line flags that have been set. -func NFlag() int { return len(CommandLine.actual) } - -// Arg returns the i'th argument. Arg(0) is the first remaining argument -// after flags have been processed. -func (f *FlagSet) Arg(i int) string { - if i < 0 || i >= len(f.args) { - return "" - } - return f.args[i] -} - -// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument -// after flags have been processed. -func Arg(i int) string { - return CommandLine.Arg(i) -} - -// NArg is the number of arguments remaining after flags have been processed. -func (f *FlagSet) NArg() int { return len(f.args) } - -// NArg is the number of arguments remaining after flags have been processed. -func NArg() int { return len(CommandLine.args) } - -// Args returns the non-flag arguments. -func (f *FlagSet) Args() []string { return f.args } - -// Args returns the non-flag command-line arguments. -func Args() []string { return CommandLine.args } - -// BoolVar defines a bool flag with specified name, default value, and usage string. -// The argument p points to a bool variable in which to store the value of the flag. -func (f *FlagSet) BoolVar(p *bool, names []string, value bool, usage string) { - f.Var(newBoolValue(value, p), names, usage) -} - -// BoolVar defines a bool flag with specified name, default value, and usage string. -// The argument p points to a bool variable in which to store the value of the flag. -func BoolVar(p *bool, names []string, value bool, usage string) { - CommandLine.Var(newBoolValue(value, p), names, usage) -} - -// Bool defines a bool flag with specified name, default value, and usage string. -// The return value is the address of a bool variable that stores the value of the flag. -func (f *FlagSet) Bool(names []string, value bool, usage string) *bool { - p := new(bool) - f.BoolVar(p, names, value, usage) - return p -} - -// Bool defines a bool flag with specified name, default value, and usage string. -// The return value is the address of a bool variable that stores the value of the flag. -func Bool(names []string, value bool, usage string) *bool { - return CommandLine.Bool(names, value, usage) -} - -// IntVar defines an int flag with specified name, default value, and usage string. -// The argument p points to an int variable in which to store the value of the flag. -func (f *FlagSet) IntVar(p *int, names []string, value int, usage string) { - f.Var(newIntValue(value, p), names, usage) -} - -// IntVar defines an int flag with specified name, default value, and usage string. -// The argument p points to an int variable in which to store the value of the flag. -func IntVar(p *int, names []string, value int, usage string) { - CommandLine.Var(newIntValue(value, p), names, usage) -} - -// Int defines an int flag with specified name, default value, and usage string. -// The return value is the address of an int variable that stores the value of the flag. -func (f *FlagSet) Int(names []string, value int, usage string) *int { - p := new(int) - f.IntVar(p, names, value, usage) - return p -} - -// Int defines an int flag with specified name, default value, and usage string. -// The return value is the address of an int variable that stores the value of the flag. -func Int(names []string, value int, usage string) *int { - return CommandLine.Int(names, value, usage) -} - -// Int64Var defines an int64 flag with specified name, default value, and usage string. -// The argument p points to an int64 variable in which to store the value of the flag. -func (f *FlagSet) Int64Var(p *int64, names []string, value int64, usage string) { - f.Var(newInt64Value(value, p), names, usage) -} - -// Int64Var defines an int64 flag with specified name, default value, and usage string. -// The argument p points to an int64 variable in which to store the value of the flag. -func Int64Var(p *int64, names []string, value int64, usage string) { - CommandLine.Var(newInt64Value(value, p), names, usage) -} - -// Int64 defines an int64 flag with specified name, default value, and usage string. -// The return value is the address of an int64 variable that stores the value of the flag. -func (f *FlagSet) Int64(names []string, value int64, usage string) *int64 { - p := new(int64) - f.Int64Var(p, names, value, usage) - return p -} - -// Int64 defines an int64 flag with specified name, default value, and usage string. -// The return value is the address of an int64 variable that stores the value of the flag. -func Int64(names []string, value int64, usage string) *int64 { - return CommandLine.Int64(names, value, usage) -} - -// UintVar defines a uint flag with specified name, default value, and usage string. -// The argument p points to a uint variable in which to store the value of the flag. -func (f *FlagSet) UintVar(p *uint, names []string, value uint, usage string) { - f.Var(newUintValue(value, p), names, usage) -} - -// UintVar defines a uint flag with specified name, default value, and usage string. -// The argument p points to a uint variable in which to store the value of the flag. -func UintVar(p *uint, names []string, value uint, usage string) { - CommandLine.Var(newUintValue(value, p), names, usage) -} - -// Uint defines a uint flag with specified name, default value, and usage string. -// The return value is the address of a uint variable that stores the value of the flag. -func (f *FlagSet) Uint(names []string, value uint, usage string) *uint { - p := new(uint) - f.UintVar(p, names, value, usage) - return p -} - -// Uint defines a uint flag with specified name, default value, and usage string. -// The return value is the address of a uint variable that stores the value of the flag. -func Uint(names []string, value uint, usage string) *uint { - return CommandLine.Uint(names, value, usage) -} - -// Uint64Var defines a uint64 flag with specified name, default value, and usage string. -// The argument p points to a uint64 variable in which to store the value of the flag. -func (f *FlagSet) Uint64Var(p *uint64, names []string, value uint64, usage string) { - f.Var(newUint64Value(value, p), names, usage) -} - -// Uint64Var defines a uint64 flag with specified name, default value, and usage string. -// The argument p points to a uint64 variable in which to store the value of the flag. -func Uint64Var(p *uint64, names []string, value uint64, usage string) { - CommandLine.Var(newUint64Value(value, p), names, usage) -} - -// Uint64 defines a uint64 flag with specified name, default value, and usage string. -// The return value is the address of a uint64 variable that stores the value of the flag. -func (f *FlagSet) Uint64(names []string, value uint64, usage string) *uint64 { - p := new(uint64) - f.Uint64Var(p, names, value, usage) - return p -} - -// Uint64 defines a uint64 flag with specified name, default value, and usage string. -// The return value is the address of a uint64 variable that stores the value of the flag. -func Uint64(names []string, value uint64, usage string) *uint64 { - return CommandLine.Uint64(names, value, usage) -} - -// StringVar defines a string flag with specified name, default value, and usage string. -// The argument p points to a string variable in which to store the value of the flag. -func (f *FlagSet) StringVar(p *string, names []string, value string, usage string) { - f.Var(newStringValue(value, p), names, usage) -} - -// StringVar defines a string flag with specified name, default value, and usage string. -// The argument p points to a string variable in which to store the value of the flag. -func StringVar(p *string, names []string, value string, usage string) { - CommandLine.Var(newStringValue(value, p), names, usage) -} - -// String defines a string flag with specified name, default value, and usage string. -// The return value is the address of a string variable that stores the value of the flag. -func (f *FlagSet) String(names []string, value string, usage string) *string { - p := new(string) - f.StringVar(p, names, value, usage) - return p -} - -// String defines a string flag with specified name, default value, and usage string. -// The return value is the address of a string variable that stores the value of the flag. -func String(names []string, value string, usage string) *string { - return CommandLine.String(names, value, usage) -} - -// Float64Var defines a float64 flag with specified name, default value, and usage string. -// The argument p points to a float64 variable in which to store the value of the flag. -func (f *FlagSet) Float64Var(p *float64, names []string, value float64, usage string) { - f.Var(newFloat64Value(value, p), names, usage) -} - -// Float64Var defines a float64 flag with specified name, default value, and usage string. -// The argument p points to a float64 variable in which to store the value of the flag. -func Float64Var(p *float64, names []string, value float64, usage string) { - CommandLine.Var(newFloat64Value(value, p), names, usage) -} - -// Float64 defines a float64 flag with specified name, default value, and usage string. -// The return value is the address of a float64 variable that stores the value of the flag. -func (f *FlagSet) Float64(names []string, value float64, usage string) *float64 { - p := new(float64) - f.Float64Var(p, names, value, usage) - return p -} - -// Float64 defines a float64 flag with specified name, default value, and usage string. -// The return value is the address of a float64 variable that stores the value of the flag. -func Float64(names []string, value float64, usage string) *float64 { - return CommandLine.Float64(names, value, usage) -} - -// DurationVar defines a time.Duration flag with specified name, default value, and usage string. -// The argument p points to a time.Duration variable in which to store the value of the flag. -func (f *FlagSet) DurationVar(p *time.Duration, names []string, value time.Duration, usage string) { - f.Var(newDurationValue(value, p), names, usage) -} - -// DurationVar defines a time.Duration flag with specified name, default value, and usage string. -// The argument p points to a time.Duration variable in which to store the value of the flag. -func DurationVar(p *time.Duration, names []string, value time.Duration, usage string) { - CommandLine.Var(newDurationValue(value, p), names, usage) -} - -// Duration defines a time.Duration flag with specified name, default value, and usage string. -// The return value is the address of a time.Duration variable that stores the value of the flag. -func (f *FlagSet) Duration(names []string, value time.Duration, usage string) *time.Duration { - p := new(time.Duration) - f.DurationVar(p, names, value, usage) - return p -} - -// Duration defines a time.Duration flag with specified name, default value, and usage string. -// The return value is the address of a time.Duration variable that stores the value of the flag. -func Duration(names []string, value time.Duration, usage string) *time.Duration { - return CommandLine.Duration(names, value, usage) -} - -// Var defines a flag with the specified name and usage string. The type and -// value of the flag are represented by the first argument, of type Value, which -// typically holds a user-defined implementation of Value. For instance, the -// caller could create a flag that turns a comma-separated string into a slice -// of strings by giving the slice the methods of Value; in particular, Set would -// decompose the comma-separated string into the slice. -func (f *FlagSet) Var(value Value, names []string, usage string) { - // Remember the default value as a string; it won't change. - flag := &Flag{names, usage, value, value.String()} - for _, name := range names { - name = strings.TrimPrefix(name, "#") - _, alreadythere := f.formal[name] - if alreadythere { - var msg string - if f.name == "" { - msg = fmt.Sprintf("flag redefined: %s", name) - } else { - msg = fmt.Sprintf("%s flag redefined: %s", f.name, name) - } - fmt.Fprintln(f.out(), msg) - panic(msg) // Happens only if flags are declared with identical names - } - if f.formal == nil { - f.formal = make(map[string]*Flag) - } - f.formal[name] = flag - } -} - -// Var defines a flag with the specified name and usage string. The type and -// value of the flag are represented by the first argument, of type Value, which -// typically holds a user-defined implementation of Value. For instance, the -// caller could create a flag that turns a comma-separated string into a slice -// of strings by giving the slice the methods of Value; in particular, Set would -// decompose the comma-separated string into the slice. -func Var(value Value, names []string, usage string) { - CommandLine.Var(value, names, usage) -} - -// failf prints to standard error a formatted error and usage message and -// returns the error. -func (f *FlagSet) failf(format string, a ...interface{}) error { - err := fmt.Errorf(format, a...) - fmt.Fprintln(f.out(), err) - f.usage() - return err -} - -// usage calls the Usage method for the flag set, or the usage function if -// the flag set is CommandLine. -func (f *FlagSet) usage() { - if f == CommandLine { - Usage() - } else if f.Usage == nil { - defaultUsage(f) - } else { - f.Usage() - } -} - -func trimQuotes(str string) string { - if len(str) == 0 { - return str - } - type quote struct { - start, end byte - } - - // All valid quote types. - quotes := []quote{ - // Double quotes - { - start: '"', - end: '"', - }, - - // Single quotes - { - start: '\'', - end: '\'', - }, - } - - for _, quote := range quotes { - // Only strip if outermost match. - if str[0] == quote.start && str[len(str)-1] == quote.end { - str = str[1 : len(str)-1] - break - } - } - - return str -} - -// parseOne parses one flag. It reports whether a flag was seen. -func (f *FlagSet) parseOne() (bool, string, error) { - if len(f.args) == 0 { - return false, "", nil - } - s := f.args[0] - if len(s) == 0 || s[0] != '-' || len(s) == 1 { - return false, "", nil - } - if s[1] == '-' && len(s) == 2 { // "--" terminates the flags - f.args = f.args[1:] - return false, "", nil - } - name := s[1:] - if len(name) == 0 || name[0] == '=' { - return false, "", f.failf("bad flag syntax: %s", s) - } - - // it's a flag. does it have an argument? - f.args = f.args[1:] - has_value := false - value := "" - if i := strings.Index(name, "="); i != -1 { - value = trimQuotes(name[i+1:]) - has_value = true - name = name[:i] - } - - m := f.formal - flag, alreadythere := m[name] // BUG - if !alreadythere { - if name == "-help" || name == "help" || name == "h" { // special case for nice help message. - f.usage() - return false, "", ErrHelp - } - if len(name) > 0 && name[0] == '-' { - return false, "", f.failf("flag provided but not defined: -%s", name) - } - return false, name, ErrRetry - } - if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg - if has_value { - if err := fv.Set(value); err != nil { - return false, "", f.failf("invalid boolean value %q for -%s: %v", value, name, err) - } - } else { - fv.Set("true") - } - } else { - // It must have a value, which might be the next argument. - if !has_value && len(f.args) > 0 { - // value is the next arg - has_value = true - value, f.args = f.args[0], f.args[1:] - } - if !has_value { - return false, "", f.failf("flag needs an argument: -%s", name) - } - if err := flag.Value.Set(value); err != nil { - return false, "", f.failf("invalid value %q for flag -%s: %v", value, name, err) - } - } - if f.actual == nil { - f.actual = make(map[string]*Flag) - } - f.actual[name] = flag - for i, n := range flag.Names { - if n == fmt.Sprintf("#%s", name) { - replacement := "" - for j := i; j < len(flag.Names); j++ { - if flag.Names[j][0] != '#' { - replacement = flag.Names[j] - break - } - } - if replacement != "" { - fmt.Fprintf(f.out(), "Warning: '-%s' is deprecated, it will be replaced by '-%s' soon. See usage.\n", name, replacement) - } else { - fmt.Fprintf(f.out(), "Warning: '-%s' is deprecated, it will be removed soon. See usage.\n", name) - } - } - } - return true, "", nil -} - -// Parse parses flag definitions from the argument list, which should not -// include the command name. Must be called after all flags in the FlagSet -// are defined and before flags are accessed by the program. -// The return value will be ErrHelp if -help was set but not defined. -func (f *FlagSet) Parse(arguments []string) error { - f.parsed = true - f.args = arguments - for { - seen, name, err := f.parseOne() - if seen { - continue - } - if err == nil { - break - } - if err == ErrRetry { - if len(name) > 1 { - err = nil - for _, letter := range strings.Split(name, "") { - f.args = append([]string{"-" + letter}, f.args...) - seen2, _, err2 := f.parseOne() - if seen2 { - continue - } - if err2 != nil { - err = f.failf("flag provided but not defined: -%s", name) - break - } - } - if err == nil { - continue - } - } else { - err = f.failf("flag provided but not defined: -%s", name) - } - } - switch f.errorHandling { - case ContinueOnError: - return err - case ExitOnError: - os.Exit(2) - case PanicOnError: - panic(err) - } - } - return nil -} - -// Parsed reports whether f.Parse has been called. -func (f *FlagSet) Parsed() bool { - return f.parsed -} - -// Parse parses the command-line flags from os.Args[1:]. Must be called -// after all flags are defined and before flags are accessed by the program. -func Parse() { - // Ignore errors; CommandLine is set for ExitOnError. - CommandLine.Parse(os.Args[1:]) -} - -// Parsed returns true if the command-line flags have been parsed. -func Parsed() bool { - return CommandLine.Parsed() -} - -// CommandLine is the default set of command-line flags, parsed from os.Args. -// The top-level functions such as BoolVar, Arg, and on are wrappers for the -// methods of CommandLine. -var CommandLine = NewFlagSet(os.Args[0], ExitOnError) - -// NewFlagSet returns a new, empty flag set with the specified name and -// error handling property. -func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { - f := &FlagSet{ - name: name, - errorHandling: errorHandling, - } - return f -} - -// Init sets the name and error handling property for a flag set. -// By default, the zero FlagSet uses an empty name and the -// ContinueOnError error handling policy. -func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { - f.name = name - f.errorHandling = errorHandling -} diff --git a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag_test.go b/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag_test.go deleted file mode 100644 index 340a1cb175..0000000000 --- a/Godeps/_workspace/src/github.com/docker/docker/pkg/mflag/flag_test.go +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2014 The Docker & Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mflag_test - -import ( - "bytes" - "fmt" - . "github.com/docker/docker/pkg/mflag" - "os" - "sort" - "strings" - "testing" - "time" -) - -// ResetForTesting clears all flag state and sets the usage function as directed. -// After calling ResetForTesting, parse errors in flag handling will not -// exit the program. -func ResetForTesting(usage func()) { - CommandLine = NewFlagSet(os.Args[0], ContinueOnError) - Usage = usage -} -func boolString(s string) string { - if s == "0" { - return "false" - } - return "true" -} - -func TestEverything(t *testing.T) { - ResetForTesting(nil) - Bool([]string{"test_bool"}, false, "bool value") - Int([]string{"test_int"}, 0, "int value") - Int64([]string{"test_int64"}, 0, "int64 value") - Uint([]string{"test_uint"}, 0, "uint value") - Uint64([]string{"test_uint64"}, 0, "uint64 value") - String([]string{"test_string"}, "0", "string value") - Float64([]string{"test_float64"}, 0, "float64 value") - Duration([]string{"test_duration"}, 0, "time.Duration value") - - m := make(map[string]*Flag) - desired := "0" - visitor := func(f *Flag) { - for _, name := range f.Names { - if len(name) > 5 && name[0:5] == "test_" { - m[name] = f - ok := false - switch { - case f.Value.String() == desired: - ok = true - case name == "test_bool" && f.Value.String() == boolString(desired): - ok = true - case name == "test_duration" && f.Value.String() == desired+"s": - ok = true - } - if !ok { - t.Error("Visit: bad value", f.Value.String(), "for", name) - } - } - } - } - VisitAll(visitor) - if len(m) != 8 { - t.Error("VisitAll misses some flags") - for k, v := range m { - t.Log(k, *v) - } - } - m = make(map[string]*Flag) - Visit(visitor) - if len(m) != 0 { - t.Errorf("Visit sees unset flags") - for k, v := range m { - t.Log(k, *v) - } - } - // Now set all flags - Set("test_bool", "true") - Set("test_int", "1") - Set("test_int64", "1") - Set("test_uint", "1") - Set("test_uint64", "1") - Set("test_string", "1") - Set("test_float64", "1") - Set("test_duration", "1s") - desired = "1" - Visit(visitor) - if len(m) != 8 { - t.Error("Visit fails after set") - for k, v := range m { - t.Log(k, *v) - } - } - // Now test they're visited in sort order. - var flagNames []string - Visit(func(f *Flag) { - for _, name := range f.Names { - flagNames = append(flagNames, name) - } - }) - if !sort.StringsAreSorted(flagNames) { - t.Errorf("flag names not sorted: %v", flagNames) - } -} - -func TestGet(t *testing.T) { - ResetForTesting(nil) - Bool([]string{"test_bool"}, true, "bool value") - Int([]string{"test_int"}, 1, "int value") - Int64([]string{"test_int64"}, 2, "int64 value") - Uint([]string{"test_uint"}, 3, "uint value") - Uint64([]string{"test_uint64"}, 4, "uint64 value") - String([]string{"test_string"}, "5", "string value") - Float64([]string{"test_float64"}, 6, "float64 value") - Duration([]string{"test_duration"}, 7, "time.Duration value") - - visitor := func(f *Flag) { - for _, name := range f.Names { - if len(name) > 5 && name[0:5] == "test_" { - g, ok := f.Value.(Getter) - if !ok { - t.Errorf("Visit: value does not satisfy Getter: %T", f.Value) - return - } - switch name { - case "test_bool": - ok = g.Get() == true - case "test_int": - ok = g.Get() == int(1) - case "test_int64": - ok = g.Get() == int64(2) - case "test_uint": - ok = g.Get() == uint(3) - case "test_uint64": - ok = g.Get() == uint64(4) - case "test_string": - ok = g.Get() == "5" - case "test_float64": - ok = g.Get() == float64(6) - case "test_duration": - ok = g.Get() == time.Duration(7) - } - if !ok { - t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), name) - } - } - } - } - VisitAll(visitor) -} - -func TestUsage(t *testing.T) { - called := false - ResetForTesting(func() { called = true }) - if CommandLine.Parse([]string{"-x"}) == nil { - t.Error("parse did not fail for unknown flag") - } - if !called { - t.Error("did not call Usage for unknown flag") - } -} - -func testParse(f *FlagSet, t *testing.T) { - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - boolFlag := f.Bool([]string{"bool"}, false, "bool value") - bool2Flag := f.Bool([]string{"bool2"}, false, "bool2 value") - intFlag := f.Int([]string{"-int"}, 0, "int value") - int64Flag := f.Int64([]string{"-int64"}, 0, "int64 value") - uintFlag := f.Uint([]string{"uint"}, 0, "uint value") - uint64Flag := f.Uint64([]string{"-uint64"}, 0, "uint64 value") - stringFlag := f.String([]string{"string"}, "0", "string value") - singleQuoteFlag := f.String([]string{"squote"}, "", "single quoted value") - doubleQuoteFlag := f.String([]string{"dquote"}, "", "double quoted value") - mixedQuoteFlag := f.String([]string{"mquote"}, "", "mixed quoted value") - mixed2QuoteFlag := f.String([]string{"mquote2"}, "", "mixed2 quoted value") - nestedQuoteFlag := f.String([]string{"nquote"}, "", "nested quoted value") - nested2QuoteFlag := f.String([]string{"nquote2"}, "", "nested2 quoted value") - float64Flag := f.Float64([]string{"float64"}, 0, "float64 value") - durationFlag := f.Duration([]string{"duration"}, 5*time.Second, "time.Duration value") - extra := "one-extra-argument" - args := []string{ - "-bool", - "-bool2=true", - "--int", "22", - "--int64", "0x23", - "-uint", "24", - "--uint64", "25", - "-string", "hello", - "-squote='single'", - `-dquote="double"`, - `-mquote='mixed"`, - `-mquote2="mixed2'`, - `-nquote="'single nested'"`, - `-nquote2='"double nested"'`, - "-float64", "2718e28", - "-duration", "2m", - extra, - } - if err := f.Parse(args); err != nil { - t.Fatal(err) - } - if !f.Parsed() { - t.Error("f.Parse() = false after Parse") - } - if *boolFlag != true { - t.Error("bool flag should be true, is ", *boolFlag) - } - if *bool2Flag != true { - t.Error("bool2 flag should be true, is ", *bool2Flag) - } - if *intFlag != 22 { - t.Error("int flag should be 22, is ", *intFlag) - } - if *int64Flag != 0x23 { - t.Error("int64 flag should be 0x23, is ", *int64Flag) - } - if *uintFlag != 24 { - t.Error("uint flag should be 24, is ", *uintFlag) - } - if *uint64Flag != 25 { - t.Error("uint64 flag should be 25, is ", *uint64Flag) - } - if *stringFlag != "hello" { - t.Error("string flag should be `hello`, is ", *stringFlag) - } - if *singleQuoteFlag != "single" { - t.Error("single quote string flag should be `single`, is ", *singleQuoteFlag) - } - if *doubleQuoteFlag != "double" { - t.Error("double quote string flag should be `double`, is ", *doubleQuoteFlag) - } - if *mixedQuoteFlag != `'mixed"` { - t.Error("mixed quote string flag should be `'mixed\"`, is ", *mixedQuoteFlag) - } - if *mixed2QuoteFlag != `"mixed2'` { - t.Error("mixed2 quote string flag should be `\"mixed2'`, is ", *mixed2QuoteFlag) - } - if *nestedQuoteFlag != "'single nested'" { - t.Error("nested quote string flag should be `'single nested'`, is ", *nestedQuoteFlag) - } - if *nested2QuoteFlag != `"double nested"` { - t.Error("double quote string flag should be `\"double nested\"`, is ", *nested2QuoteFlag) - } - if *float64Flag != 2718e28 { - t.Error("float64 flag should be 2718e28, is ", *float64Flag) - } - if *durationFlag != 2*time.Minute { - t.Error("duration flag should be 2m, is ", *durationFlag) - } - if len(f.Args()) != 1 { - t.Error("expected one argument, got", len(f.Args())) - } else if f.Args()[0] != extra { - t.Errorf("expected argument %q got %q", extra, f.Args()[0]) - } -} - -func testPanic(f *FlagSet, t *testing.T) { - f.Int([]string{"-int"}, 0, "int value") - if f.Parsed() { - t.Error("f.Parse() = true before Parse") - } - args := []string{ - "-int", "21", - } - f.Parse(args) -} - -func TestParsePanic(t *testing.T) { - ResetForTesting(func() {}) - testPanic(CommandLine, t) -} - -func TestParse(t *testing.T) { - ResetForTesting(func() { t.Error("bad parse") }) - testParse(CommandLine, t) -} - -func TestFlagSetParse(t *testing.T) { - testParse(NewFlagSet("test", ContinueOnError), t) -} - -// Declare a user-defined flag type. -type flagVar []string - -func (f *flagVar) String() string { - return fmt.Sprint([]string(*f)) -} - -func (f *flagVar) Set(value string) error { - *f = append(*f, value) - return nil -} - -func TestUserDefined(t *testing.T) { - var flags FlagSet - flags.Init("test", ContinueOnError) - var v flagVar - flags.Var(&v, []string{"v"}, "usage") - if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { - t.Error(err) - } - if len(v) != 3 { - t.Fatal("expected 3 args; got ", len(v)) - } - expect := "[1 2 3]" - if v.String() != expect { - t.Errorf("expected value %q got %q", expect, v.String()) - } -} - -// Declare a user-defined boolean flag type. -type boolFlagVar struct { - count int -} - -func (b *boolFlagVar) String() string { - return fmt.Sprintf("%d", b.count) -} - -func (b *boolFlagVar) Set(value string) error { - if value == "true" { - b.count++ - } - return nil -} - -func (b *boolFlagVar) IsBoolFlag() bool { - return b.count < 4 -} - -func TestUserDefinedBool(t *testing.T) { - var flags FlagSet - flags.Init("test", ContinueOnError) - var b boolFlagVar - var err error - flags.Var(&b, []string{"b"}, "usage") - if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil { - if b.count < 4 { - t.Error(err) - } - } - - if b.count != 4 { - t.Errorf("want: %d; got: %d", 4, b.count) - } - - if err == nil { - t.Error("expected error; got none") - } -} - -func TestSetOutput(t *testing.T) { - var flags FlagSet - var buf bytes.Buffer - flags.SetOutput(&buf) - flags.Init("test", ContinueOnError) - flags.Parse([]string{"-unknown"}) - if out := buf.String(); !strings.Contains(out, "-unknown") { - t.Logf("expected output mentioning unknown; got %q", out) - } -} - -// This tests that one can reset the flags. This still works but not well, and is -// superseded by FlagSet. -func TestChangingArgs(t *testing.T) { - ResetForTesting(func() { t.Fatal("bad parse") }) - oldArgs := os.Args - defer func() { os.Args = oldArgs }() - os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} - before := Bool([]string{"before"}, false, "") - if err := CommandLine.Parse(os.Args[1:]); err != nil { - t.Fatal(err) - } - cmd := Arg(0) - os.Args = Args() - after := Bool([]string{"after"}, false, "") - Parse() - args := Args() - - if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { - t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) - } -} - -// Test that -help invokes the usage message and returns ErrHelp. -func TestHelp(t *testing.T) { - var helpCalled = false - fs := NewFlagSet("help test", ContinueOnError) - fs.Usage = func() { helpCalled = true } - var flag bool - fs.BoolVar(&flag, []string{"flag"}, false, "regular flag") - // Regular flag invocation should work - err := fs.Parse([]string{"-flag=true"}) - if err != nil { - t.Fatal("expected no error; got ", err) - } - if !flag { - t.Error("flag was not set by -flag") - } - if helpCalled { - t.Error("help called for regular flag") - helpCalled = false // reset for next test - } - // Help flag should work as expected. - err = fs.Parse([]string{"-help"}) - if err == nil { - t.Fatal("error expected") - } - if err != ErrHelp { - t.Fatal("expected ErrHelp; got ", err) - } - if !helpCalled { - t.Fatal("help was not called") - } - // If we define a help flag, that should override. - var help bool - fs.BoolVar(&help, []string{"help"}, false, "help flag") - helpCalled = false - err = fs.Parse([]string{"-help"}) - if err != nil { - t.Fatal("expected no error for defined -help; got ", err) - } - if helpCalled { - t.Fatal("help was called; should not have been for defined help flag") - } -} - -// Test the flag count functions. -func TestFlagCounts(t *testing.T) { - fs := NewFlagSet("help test", ContinueOnError) - var flag bool - fs.BoolVar(&flag, []string{"flag1"}, false, "regular flag") - fs.BoolVar(&flag, []string{"#deprecated1"}, false, "regular flag") - fs.BoolVar(&flag, []string{"f", "flag2"}, false, "regular flag") - fs.BoolVar(&flag, []string{"#d", "#deprecated2"}, false, "regular flag") - fs.BoolVar(&flag, []string{"flag3"}, false, "regular flag") - fs.BoolVar(&flag, []string{"g", "#flag4", "-flag4"}, false, "regular flag") - - if fs.FlagCount() != 6 { - t.Fatal("FlagCount wrong. ", fs.FlagCount()) - } - if fs.FlagCountUndeprecated() != 4 { - t.Fatal("FlagCountUndeprecated wrong. ", fs.FlagCountUndeprecated()) - } - if fs.NFlag() != 0 { - t.Fatal("NFlag wrong. ", fs.NFlag()) - } - err := fs.Parse([]string{"-fd", "-g", "-flag4"}) - if err != nil { - t.Fatal("expected no error for defined -help; got ", err) - } - if fs.NFlag() != 4 { - t.Fatal("NFlag wrong. ", fs.NFlag()) - } -} - -// Show up bug in sortFlags -func TestSortFlags(t *testing.T) { - fs := NewFlagSet("help TestSortFlags", ContinueOnError) - - var err error - - var b bool - fs.BoolVar(&b, []string{"b", "-banana"}, false, "usage") - - err = fs.Parse([]string{"--banana=true"}) - if err != nil { - t.Fatal("expected no error; got ", err) - } - - count := 0 - - fs.VisitAll(func(flag *Flag) { - count++ - if flag == nil { - t.Fatal("VisitAll should not return a nil flag") - } - }) - flagcount := fs.FlagCount() - if flagcount != count { - t.Fatalf("FlagCount (%d) != number (%d) of elements visited", flagcount, count) - } - // Make sure its idempotent - if flagcount != fs.FlagCount() { - t.Fatalf("FlagCount (%d) != fs.FlagCount() (%d) of elements visited", flagcount, fs.FlagCount()) - } - - count = 0 - fs.Visit(func(flag *Flag) { - count++ - if flag == nil { - t.Fatal("Visit should not return a nil flag") - } - }) - nflag := fs.NFlag() - if nflag != count { - t.Fatalf("NFlag (%d) != number (%d) of elements visited", nflag, count) - } - if nflag != fs.NFlag() { - t.Fatalf("NFlag (%d) != fs.NFlag() (%d) of elements visited", nflag, fs.NFlag()) - } -} diff --git a/commands.go b/commands.go index 0b62e080c1..eed7345310 100644 --- a/commands.go +++ b/commands.go @@ -4,14 +4,12 @@ import ( "encoding/json" "fmt" "os" - "reflect" - "sort" "strings" "sync" "text/tabwriter" log "github.com/Sirupsen/logrus" - flag "github.com/docker/docker/pkg/mflag" + "github.com/codegangsta/cli" "github.com/docker/machine/drivers" _ "github.com/docker/machine/drivers/azure" @@ -21,79 +19,6 @@ import ( "github.com/docker/machine/state" ) -type DockerCli struct{} - -func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) { - camelArgs := make([]string, len(args)) - for i, s := range args { - if len(s) == 0 { - return nil, false - } - camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) - } - methodName := "Cmd" + strings.Join(camelArgs, "") - method := reflect.ValueOf(cli).MethodByName(methodName) - if !method.IsValid() { - return nil, false - } - return method.Interface().(func(...string) error), true -} - -func (cli *DockerCli) Cmd(args ...string) error { - if len(args) > 1 { - method, exists := cli.getMethod(args[:2]...) - if exists { - return method(args[2:]...) - } - } - if len(args) > 0 { - method, exists := cli.getMethod(args[0]) - if !exists { - fmt.Println("Error: Command not found:", args[0]) - return cli.CmdHelp() - } - return method(args[1:]...) - } - return cli.CmdHelp() -} - -func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet { - flags := flag.NewFlagSet(name, flag.ContinueOnError) - flags.Usage = func() { - options := "" - if flags.FlagCountUndeprecated() > 0 { - options = "[OPTIONS] " - } - fmt.Fprintf(os.Stderr, "\nUsage: machine %s %s%s\n\n%s\n\n", name, options, signature, description) - flags.PrintDefaults() - os.Exit(2) - } - return flags -} - -func (cli *DockerCli) CmdHelp(args ...string) error { - if len(args) > 1 { - method, exists := cli.getMethod(args[:2]...) - if exists { - method("--help") - return nil - } - } - if len(args) > 0 { - method, exists := cli.getMethod(args[0]) - if !exists { - fmt.Fprintf(os.Stderr, "Error: Command not found: %s\n", args[0]) - } else { - method("--help") - return nil - } - } - - flag.Usage() - - return nil -} - type HostListItem struct { Name string Active bool @@ -116,430 +41,449 @@ func (h HostListItemByName) Less(i, j int) bool { return strings.ToLower(h[i].Name) < strings.ToLower(h[j].Name) } -func (cli *DockerCli) CmdLs(args ...string) error { - cmd := cli.Subcmd("ls", "", "List machines") - quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display names") - - if err := cmd.Parse(args); err != nil { - return err +func BeforeHandler(c *cli.Context) error { + if c.Bool("debug") { + os.Setenv("DEBUG", "1") + initLogging(log.DebugLevel) } - store := NewStore() + return nil +} - hostList, err := store.List() - if err != nil { - return err - } +var Commands = []cli.Command{ + { + Name: "active", + Usage: "Get or set the active machine", + Action: func(c *cli.Context) { + name := c.Args().First() + store := NewStore() - w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) - - if !*quiet { - fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL") - } - - wg := sync.WaitGroup{} - - hostListItem := []HostListItem{} - - for _, host := range hostList { - host := host - if *quiet { - fmt.Fprintf(w, "%s\n", host.Name) - } else { - wg.Add(1) - go func() { - currentState, err := host.Driver.GetState() + if name == "" { + host, err := store.GetActive() if err != nil { - log.Errorf("error getting state for host %s: %s", host.Name, err) + log.Errorf("error finding active host") + } + if host != nil { + fmt.Println(host.Name) + } + } else if name != "" { + host, err := store.Load(name) + if err != nil { + log.Errorln(err) + log.Errorf("error loading new active host") + os.Exit(1) } - url, err := host.GetURL() - if err != nil { - if err == drivers.ErrHostIsNotRunning { - url = "" - } else { - log.Errorf("error getting URL for host %s: %s", host.Name, err) - } + if err := store.SetActive(host); err != nil { + log.Errorf("error setting new active host") + os.Exit(1) } + } else { + cli.ShowCommandHelp(c, "active") + } + }, + }, + { + Flags: append( + drivers.GetCreateFlags(), + cli.StringFlag{ + Name: "driver, d", + Usage: fmt.Sprintf( + "Driver to create machine with. Available drivers: %s", + strings.Join(drivers.GetDriverNames(), ", "), + ), + Value: "none", + }, + ), + Name: "create", + Usage: "Create a machine", + Action: func(c *cli.Context) { + driver := c.String("driver") + name := c.Args().First() - isActive, err := store.IsActive(&host) + if name == "" { + cli.ShowCommandHelp(c, "create") + os.Exit(1) + } + + keyExists, err := drivers.PublicKeyExists() + if err != nil { + log.Errorf("error") + os.Exit(1) + } + + if !keyExists { + log.Errorf("error key doesn't exist") + os.Exit(1) + } + + store := NewStore() + + host, err := store.Create(name, driver, c) + if err != nil { + log.Errorf("%s", err) + os.Exit(1) + } + if err := store.SetActive(host); err != nil { + log.Errorf("%s", err) + os.Exit(1) + } + + log.Infof("%q has been created and is now the active machine. To point Docker at this machine, run: export DOCKER_HOST=$(machine url) DOCKER_AUTH=identity", name) + }, + }, + { + Name: "inspect", + Usage: "Inspect information about a machine", + Action: func(c *cli.Context) { + name := c.Args().First() + + if name == "" { + cli.ShowCommandHelp(c, "inspect") + os.Exit(1) + } + + store := NewStore() + host, err := store.Load(name) + if err != nil { + log.Errorf("error loading data") + os.Exit(1) + } + + prettyJson, err := json.MarshalIndent(host, "", " ") + if err != nil { + log.Error("error with json") + os.Exit(1) + } + + fmt.Println(string(prettyJson)) + }, + }, + { + Name: "ip", + Usage: "Get the IP address of a machine", + Action: func(c *cli.Context) { + name := c.Args().First() + + if name == "" { + cli.ShowCommandHelp(c, "ip") + os.Exit(1) + } + + var ( + err error + host *Host + store = NewStore() + ) + + if name != "" { + host, err = store.Load(name) if err != nil { - log.Errorf("error determining whether host %q is active: %s", - host.Name, err) + log.Errorf("error unable to load data") + os.Exit(1) } + } else { + host, err = store.GetActive() + if err != nil { + log.Errorf("error") + os.Exit(1) + } + if host == nil { + os.Exit(1) + } + } - hostListItem = append(hostListItem, HostListItem{ - Name: host.Name, - Active: isActive, - DriverName: host.Driver.DriverName(), - State: currentState, - URL: url, - }) + ip, err := host.Driver.GetIP() + if err != nil { + log.Errorf("error unable to get IP") + os.Exit(1) + } - wg.Done() - }() - } - } + fmt.Println(ip) + }, + }, + { + Name: "kill", + Usage: "Kill a machine", + Action: func(c *cli.Context) { + name := c.Args().First() - wg.Wait() + if name == "" { + cli.ShowCommandHelp(c, "kill") + os.Exit(1) + } - sort.Sort(HostListItemByName(hostListItem)) + store := NewStore() - for _, hostState := range hostListItem { - activeString := "" - if hostState.Active { - activeString = "*" - } - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", - hostState.Name, activeString, hostState.DriverName, hostState.State, hostState.URL) - } + host, err := store.Load(name) + if err != nil { + log.Errorf("error unable to load data") + os.Exit(1) + } - w.Flush() + host.Driver.Kill() + }, + }, + { + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "quiet, q", + Usage: "Enable quiet mode", + }, + }, + Name: "ls", + Usage: "List machines", + Action: func(c *cli.Context) { + quiet := c.Bool("quiet") + store := NewStore() - return nil -} - -func (cli *DockerCli) CmdCreate(args ...string) error { - cmd := cli.Subcmd("create", "NAME", "Create machines") - - driverDesc := fmt.Sprintf( - "Driver to create machine with. Available drivers: %s", - strings.Join(drivers.GetDriverNames(), ", "), - ) - - driver := cmd.String([]string{"d", "-driver"}, "none", driverDesc) - - createFlags := drivers.RegisterCreateFlags(cmd) - - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() != 1 { - cmd.Usage() - return nil - } - - keyExists, err := drivers.PublicKeyExists() - if err != nil { - return err - } - if !keyExists { - log.Fatalf("Identity auth public key does not exist at %s. Please run the docker client without any options to create it.", drivers.PublicKeyPath()) - } - - name := cmd.Arg(0) - - store := NewStore() - - driverCreateFlags, _ := createFlags[*driver] - host, err := store.Create(name, *driver, driverCreateFlags) - if err != nil { - return err - } - if err := store.SetActive(host); err != nil { - return err - } - log.Infof("%q has been created and is now the active machine. To point Docker at this machine, run: export DOCKER_HOST=$(machine url) DOCKER_AUTH=identity", name) - return nil -} - -func (cli *DockerCli) CmdStart(args ...string) error { - cmd := cli.Subcmd("start", "NAME", "Start a machine") - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() < 1 { - cmd.Usage() - return nil - } - - store := NewStore() - - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - return host.Start() -} - -func (cli *DockerCli) CmdStop(args ...string) error { - cmd := cli.Subcmd("stop", "NAME", "Stop a machine") - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() < 1 { - cmd.Usage() - return nil - } - - store := NewStore() - - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - return host.Stop() -} - -func (cli *DockerCli) CmdRm(args ...string) error { - cmd := cli.Subcmd("rm", "NAME", "Remove a machine") - force := cmd.Bool([]string{"f", "-force"}, false, "Remove local configuration even if machine cannot be removed") - - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() < 1 { - cmd.Usage() - return nil - } - - isError := false - - store := NewStore() - for _, host := range cmd.Args() { - host := host - if err := store.Remove(host, *force); err != nil { - log.Errorf("Error removing machine %s: %s", host, err) - isError = true - } - } - if isError { - return fmt.Errorf("There was an error removing a machine. To force remove it, pass the -f option. Warning: this might leave it running on the provider.") - } - return nil -} - -func (cli *DockerCli) CmdIp(args ...string) error { - cmd := cli.Subcmd("ip", "NAME", "Get the IP address of a machine") - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() > 1 { - cmd.Usage() - return nil - } - - var ( - err error - host *Host - store = NewStore() - ) - - if cmd.NArg() == 1 { - host, err = store.Load(cmd.Arg(0)) - if err != nil { - return err - } - } else { - host, err = store.GetActive() - if err != nil { - return err - } - if host == nil { - os.Exit(1) - } - } - - ip, err := host.Driver.GetIP() - if err != nil { - return err - } - - fmt.Println(ip) - - return nil -} - -func (cli *DockerCli) CmdUrl(args ...string) error { - cmd := cli.Subcmd("url", "NAME", "Get the URL of a machine") - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() > 1 { - cmd.Usage() - return nil - } - - var ( - err error - host *Host - store = NewStore() - ) - - if cmd.NArg() == 1 { - host, err = store.Load(cmd.Arg(0)) - if err != nil { - return err - } - } else { - host, err = store.GetActive() - if err != nil { - return err - } - if host == nil { - os.Exit(1) - } - } - - url, err := host.GetURL() - if err != nil { - return err - } - - fmt.Println(url) - - return nil -} - -func (cli *DockerCli) CmdRestart(args ...string) error { - cmd := cli.Subcmd("restart", "NAME", "Restart a machine") - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() < 1 { - cmd.Usage() - return nil - } - - store := NewStore() - - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - return host.Driver.Restart() -} - -func (cli *DockerCli) CmdKill(args ...string) error { - cmd := cli.Subcmd("kill", "NAME", "Kill a machine") - if err := cmd.Parse(args); err != nil { - return err - } - if cmd.NArg() < 1 { - cmd.Usage() - return nil - } - - store := NewStore() - - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - return host.Driver.Kill() -} - -func (cli *DockerCli) CmdSsh(args ...string) error { - cmd := cli.Subcmd("ssh", "NAME [COMMAND ...]", "Log into or run a command on a machine with SSH") - if err := cmd.Parse(args); err != nil { - return err - } - - if cmd.NArg() < 1 { - cmd.Usage() - return nil - } - - i := 1 - for i < len(os.Args) && os.Args[i-1] != cmd.Arg(0) { - i++ - } - - store := NewStore() - - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - - sshCmd, err := host.Driver.GetSSHCommand(os.Args[i:]...) - if err != nil { - return err - } - sshCmd.Stdin = os.Stdin - sshCmd.Stdout = os.Stdout - sshCmd.Stderr = os.Stderr - if err := sshCmd.Run(); err != nil { - return fmt.Errorf("%s", err) - } - return nil -} - -func (cli *DockerCli) CmdActive(args ...string) error { - cmd := cli.Subcmd("active", "[NAME]", "Get or set the active machine") - if err := cmd.Parse(args); err != nil { - return err - } - - store := NewStore() - - if cmd.NArg() == 0 { - host, err := store.GetActive() - if err != nil { - return err - } - if host != nil { - fmt.Println(host.Name) - } - } else if cmd.NArg() == 1 { - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - if err := store.SetActive(host); err != nil { - return err - } - } else { - cmd.Usage() - } - - return nil - -} - -func (cli *DockerCli) CmdInspect(args ...string) error { - cmd := cli.Subcmd("inspect", "[NAME]", "Get detailed information about a machine") - if err := cmd.Parse(args); err != nil { - return err - } - - if cmd.NArg() == 0 { - cmd.Usage() - return nil - } - - store := NewStore() - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - - prettyJson, err := json.MarshalIndent(host, "", " ") - if err != nil { - return err - } - - fmt.Println(string(prettyJson)) - - return nil -} - -func (cli *DockerCli) CmdUpgrade(args ...string) error { - cmd := cli.Subcmd("upgrade", "[NAME]", "Upgrade a machine to the latest version of Docker") - if err := cmd.Parse(args); err != nil { - return err - } - - if cmd.NArg() == 0 { - cmd.Usage() - return nil - } - - store := NewStore() - host, err := store.Load(cmd.Arg(0)) - if err != nil { - return err - } - - return host.Driver.Upgrade() + hostList, err := store.List() + if err != nil { + log.Errorf("error unable to list hosts") + os.Exit(1) + } + + w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) + + if !quiet { + fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL") + } + + wg := sync.WaitGroup{} + + for _, host := range hostList { + host := host + if quiet { + fmt.Fprintf(w, "%s\n", host.Name) + } else { + wg.Add(1) + go func() { + currentState, err := host.Driver.GetState() + if err != nil { + log.Errorf("error getting state for host %s: %s", host.Name, err) + } + + url, err := host.GetURL() + if err != nil { + if err == drivers.ErrHostIsNotRunning { + url = "" + } else { + log.Errorf("error getting URL for host %s: %s", host.Name, err) + } + } + + isActive, err := store.IsActive(&host) + if err != nil { + log.Errorf("error determining whether host %q is active: %s", + host.Name, err) + } + + activeString := "" + if isActive { + activeString = "*" + } + + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", + host.Name, activeString, host.Driver.DriverName(), currentState, url) + wg.Done() + }() + } + } + + wg.Wait() + w.Flush() + }, + }, + { + Name: "restart", + Usage: "Restart a machine", + Action: func(c *cli.Context) { + name := c.Args().First() + if name == "" { + cli.ShowCommandHelp(c, "restart") + os.Exit(1) + } + + store := NewStore() + + host, err := store.Load(name) + if err != nil { + log.Errorf("error unable to load data") + os.Exit(1) + } + + host.Driver.Restart() + }, + }, + { + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "force, f", + Usage: "Remove local configuration even if machine cannot be removed", + }, + }, + Name: "rm", + Usage: "Remove a machine", + Action: func(c *cli.Context) { + if len(c.Args()) == 0 { + cli.ShowCommandHelp(c, "rm") + os.Exit(1) + } + + force := c.Bool("force") + + isError := false + + store := NewStore() + for _, host := range c.Args() { + if err := store.Remove(host, force); err != nil { + log.Errorf("Error removing machine %s: %s", host, err) + isError = true + } + } + if isError { + log.Errorf("There was an error removing a machine. To force remove it, pass the -f option. Warning: this might leave it running on the provider.") + } + }, + }, + { + Name: "ssh", + Usage: "Log into or run a command on a machine with SSH", + Action: func(c *cli.Context) { + name := c.Args().First() + + if name == "" { + cli.ShowCommandHelp(c, "ssh") + os.Exit(1) + } + + i := 1 + for i < len(os.Args) && os.Args[i-1] != name { + i++ + } + + store := NewStore() + + host, err := store.Load(name) + if err != nil { + log.Errorf("%s", err) + os.Exit(1) + } + + sshCmd, err := host.Driver.GetSSHCommand(os.Args[i:]...) + if err != nil { + log.Errorf("%s", err) + os.Exit(1) + } + + sshCmd.Stdin = os.Stdin + sshCmd.Stdout = os.Stdout + sshCmd.Stderr = os.Stderr + if err := sshCmd.Run(); err != nil { + log.Errorf("%s", err) + os.Exit(1) + } + }, + }, + { + Name: "start", + Usage: "Start a machine", + Action: func(c *cli.Context) { + name := c.Args().First() + + store := NewStore() + + host, err := store.Load(name) + if err != nil { + log.Errorf("error unable to load data") + os.Exit(1) + } + + host.Start() + }, + }, + { + Name: "stop", + Usage: "Stop a machine", + Action: func(c *cli.Context) { + name := c.Args().First() + if name == "" { + cli.ShowCommandHelp(c, "stop") + os.Exit(1) + } + + store := NewStore() + + host, err := store.Load(name) + if err != nil { + log.Errorf("error unable to load data") + os.Exit(1) + } + + host.Stop() + }, + }, + { + Name: "upgrade", + Usage: "Upgrade a machine to the latest version of Docker", + Action: func(c *cli.Context) { + name := c.Args().First() + if name == "" { + cli.ShowCommandHelp(c, "upgrade") + os.Exit(1) + } + + store := NewStore() + host, err := store.Load(name) + if err != nil { + log.Errorf("error unable to load host") + os.Exit(1) + } + + host.Driver.Upgrade() + }, + }, + { + Name: "url", + Usage: "Get the URL of a machine", + Action: func(c *cli.Context) { + name := c.Args().First() + + if name == "" { + cli.ShowCommandHelp(c, "url") + os.Exit(1) + } + + var ( + err error + host *Host + store = NewStore() + ) + + if name != "" { + host, err = store.Load(name) + if err != nil { + log.Errorf("error unable to load data") + os.Exit(1) + } + } else { + host, err = store.GetActive() + if err != nil { + log.Errorf("error unable to get active host") + os.Exit(1) + } + if host == nil { + os.Exit(1) + } + } + + url, err := host.GetURL() + if err != nil { + log.Errorf("error unable to get url for host") + os.Exit(1) + } + + fmt.Println(url) + }, + }, } diff --git a/drivers/azure/azure.go b/drivers/azure/azure.go index 1e1175098d..30e5d216fd 100644 --- a/drivers/azure/azure.go +++ b/drivers/azure/azure.go @@ -6,7 +6,6 @@ import ( "os" "os/exec" "path" - "strconv" "strings" "time" @@ -14,7 +13,7 @@ import ( "github.com/MSOpenTech/azure-sdk-for-go/clients/vmClient" log "github.com/Sirupsen/logrus" - flag "github.com/docker/docker/pkg/mflag" + "github.com/codegangsta/cli" "github.com/docker/docker/utils" "github.com/docker/machine/drivers" "github.com/docker/machine/ssh" @@ -36,87 +35,74 @@ type Driver struct { storePath string } -type CreateFlags struct { - SubscriptionID *string - SubscriptionCert *string - PublishSettingsFilePath *string - Name *string - Location *string - Size *string - UserName *string - UserPassword *string - Image *string - SSHPort *string - DockerPort *string -} - func init() { drivers.Register("azure", &drivers.RegisteredDriver{ - New: NewDriver, - RegisterCreateFlags: RegisterCreateFlags, + New: NewDriver, + GetCreateFlags: GetCreateFlags, }) } -// RegisterCreateFlags registers the flags this driver adds to +// GetCreateFlags registers the flags this driver adds to // "docker hosts create" -func RegisterCreateFlags(cmd *flag.FlagSet) interface{} { - createFlags := new(CreateFlags) - createFlags.SubscriptionID = cmd.String( - []string{"-azure-subscription-id"}, - "", - "Azure subscription ID", - ) - createFlags.SubscriptionCert = cmd.String( - []string{"-azure-subscription-cert"}, - "", - "Azure subscription cert", - ) - createFlags.PublishSettingsFilePath = cmd.String( - []string{"-azure-publish-settings-file"}, - "", - "Azure publish settings file", - ) - createFlags.Location = cmd.String( - []string{"-azure-location"}, - "West US", - "Azure location", - ) - createFlags.Size = cmd.String( - []string{"-azure-size"}, - "Small", - "Azure size", - ) - createFlags.Name = cmd.String( - []string{"-azure-name"}, - "", - "Azure cloud service name", - ) - createFlags.UserName = cmd.String( - []string{"-azure-username"}, - "tcuser", - "Azure username", - ) - createFlags.UserPassword = cmd.String( - []string{"-azure-password"}, - "", - "Azure user password", - ) - createFlags.Image = cmd.String( - []string{"-azure-image"}, - "", - "Azure image name. Default is Ubuntu 14.04 LTS x64", - ) - createFlags.SSHPort = cmd.String( - []string{"-azure-ssh"}, - "22", - "Azure ssh port", - ) - createFlags.DockerPort = cmd.String( - []string{"-azure-docker-port"}, - "4243", - "Azure docker port", - ) - return createFlags +func GetCreateFlags() []cli.Flag { + return []cli.Flag{ + cli.IntFlag{ + Name: "azure-docker-port", + Usage: "Azure Docker port", + Value: 4243, + }, + cli.StringFlag{ + EnvVar: "AZURE_IMAGE", + Name: "azure-image", + Usage: "Azure image name. Default is Ubuntu 14.04 LTS x64", + }, + cli.StringFlag{ + EnvVar: "AZURE_LOCATION", + Name: "azure-location", + Usage: "Azure location", + Value: "West US", + }, + cli.StringFlag{ + Name: "azure-name", + Usage: "Azure cloud service name", + }, + cli.StringFlag{ + Name: "azure-password", + Usage: "Azure user password", + }, + cli.StringFlag{ + EnvVar: "AZURE_PUBLISH_SETTINGS_FILE", + Name: "azure-publish-settings-file", + Usage: "Azure publish settings file", + }, + cli.StringFlag{ + EnvVar: "AZURE_SIZE", + Name: "azure-size", + Usage: "Azure size", + Value: "Small", + }, + cli.IntFlag{ + Name: "azure-ssh", + Usage: "Azure SSH port", + Value: 22, + }, + + cli.StringFlag{ + EnvVar: "AZURE_SUBSCRIPTION_CERT", + Name: "azure-subscription-cert", + Usage: "Azure subscription cert", + }, + cli.StringFlag{ + EnvVar: "AZURE_SUBSCRIPTION_ID", + Name: "azure-subscription-id", + Usage: "Azure subscription ID", + }, + cli.StringFlag{ + Name: "azure-username", + Usage: "Azure username", + Value: "tcuser", + }, + } } func NewDriver(storePath string) (drivers.Driver, error) { @@ -128,61 +114,56 @@ func (driver *Driver) DriverName() string { return "azure" } -func (driver *Driver) SetConfigFromFlags(flagsInterface interface{}) error { - flags := flagsInterface.(*CreateFlags) - driver.SubscriptionID = *flags.SubscriptionID +func (driver *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { + driver.SubscriptionID = flags.String("azure-subscription-id") - if *flags.SubscriptionCert != "" { - if _, err := os.Stat(*flags.SubscriptionCert); os.IsNotExist(err) { + cert := flags.String("azure-subscription-cert") + publishSettings := flags.String("azure-publish-settings-file") + name := flags.String("azure-name") + image := flags.String("azure-image") + username := flags.String("azure-username") + + if cert != "" { + if _, err := os.Stat(cert); os.IsNotExist(err) { return err } - driver.SubscriptionCert = *flags.SubscriptionCert + driver.SubscriptionCert = cert } - if *flags.PublishSettingsFilePath != "" { - if _, err := os.Stat(*flags.PublishSettingsFilePath); os.IsNotExist(err) { + if publishSettings != "" { + if _, err := os.Stat(publishSettings); os.IsNotExist(err) { return err } - driver.PublishSettingsFilePath = *flags.PublishSettingsFilePath + driver.PublishSettingsFilePath = publishSettings } if (driver.SubscriptionID == "" || driver.SubscriptionCert == "") && driver.PublishSettingsFilePath == "" { return fmt.Errorf("Please specify azure subscription params using options: --azure-subscription-id and --azure-subscription-cert or --azure-publish-settings-file") } - if *flags.Name == "" { + if name == "" { driver.Name = generateVMName() } else { - driver.Name = *flags.Name + driver.Name = name } - if *flags.Image == "" { + if image == "" { driver.Image = "b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_1-LTS-amd64-server-20140927-en-us-30GB" } else { - driver.Image = *flags.Image + driver.Image = image } - driver.Location = *flags.Location - driver.Size = *flags.Size + driver.Location = flags.String("azure-location") + driver.Size = flags.String("azure-size") - if strings.ToLower(*flags.UserName) == "docker" { + if strings.ToLower(username) == "docker" { return fmt.Errorf("'docker' is not valid user name for docker host. Please specify another user name") } - driver.UserName = *flags.UserName - driver.UserPassword = *flags.UserPassword - - dockerPort, err := strconv.Atoi(*flags.DockerPort) - if err != nil { - return err - } - driver.DockerPort = dockerPort - - sshPort, err := strconv.Atoi(*flags.SSHPort) - if err != nil { - return err - } - driver.SSHPort = sshPort + driver.UserName = username + driver.UserPassword = flags.String("azure-password") + driver.DockerPort = flags.Int("azure-docker-port") + driver.SSHPort = flags.Int("azure-docker-port") return nil } diff --git a/drivers/digitalocean/digitalocean.go b/drivers/digitalocean/digitalocean.go index 2ef263270f..60d9ea9a4b 100644 --- a/drivers/digitalocean/digitalocean.go +++ b/drivers/digitalocean/digitalocean.go @@ -10,8 +10,8 @@ import ( "code.google.com/p/goauth2/oauth" log "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" "github.com/digitalocean/godo" - flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/utils" "github.com/docker/machine/drivers" "github.com/docker/machine/ssh" @@ -30,45 +30,41 @@ type Driver struct { storePath string } -type CreateFlags struct { - AccessToken *string - Image *string - Region *string - Size *string -} - func init() { drivers.Register("digitalocean", &drivers.RegisteredDriver{ - New: NewDriver, - RegisterCreateFlags: RegisterCreateFlags, + New: NewDriver, + GetCreateFlags: GetCreateFlags, }) } -// RegisterCreateFlags registers the flags this driver adds to +// GetCreateFlags registers the flags this driver adds to // "docker hosts create" -func RegisterCreateFlags(cmd *flag.FlagSet) interface{} { - createFlags := new(CreateFlags) - createFlags.AccessToken = cmd.String( - []string{"-digitalocean-access-token"}, - "", - "Digital Ocean access token", - ) - createFlags.Image = cmd.String( - []string{"-digitalocean-image"}, - "docker", - "Digital Ocean image", - ) - createFlags.Region = cmd.String( - []string{"-digitalocean-region"}, - "nyc3", - "Digital Ocean region", - ) - createFlags.Size = cmd.String( - []string{"-digitalocean-size"}, - "512mb", - "Digital Ocean size", - ) - return createFlags +func GetCreateFlags() []cli.Flag { + return []cli.Flag{ + cli.StringFlag{ + EnvVar: "DIGITALOCEAN_ACCESS_TOKEN", + Name: "digitalocean-access-token", + Usage: "Digital Ocean access token", + }, + cli.StringFlag{ + EnvVar: "DIGITALOCEAN_IMAGE", + Name: "digitalocean-image", + Usage: "Digital Ocean Image", + Value: "docker", + }, + cli.StringFlag{ + EnvVar: "DIGITALOCEAN_REGION", + Name: "digitalocean-region", + Usage: "Digital Ocean region", + Value: "nyc3", + }, + cli.StringFlag{ + EnvVar: "DIGITALOCEAN_SIZE", + Name: "digitalocean-size", + Usage: "Digital Ocean size", + Value: "512mb", + }, + } } func NewDriver(storePath string) (drivers.Driver, error) { @@ -79,12 +75,11 @@ func (d *Driver) DriverName() string { return "digitalocean" } -func (d *Driver) SetConfigFromFlags(flagsInterface interface{}) error { - flags := flagsInterface.(*CreateFlags) - d.AccessToken = *flags.AccessToken - d.Image = *flags.Image - d.Region = *flags.Region - d.Size = *flags.Size +func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { + d.AccessToken = flags.String("digitalocean-access-token") + d.Image = flags.String("digitalocean-image") + d.Region = flags.String("digitalocean-region") + d.Size = flags.String("digitalocean-size") if d.AccessToken == "" { return fmt.Errorf("digitalocean driver requires the --digitalocean-access-token option") diff --git a/drivers/drivers.go b/drivers/drivers.go index 0718579b7f..39c32f0cbd 100644 --- a/drivers/drivers.go +++ b/drivers/drivers.go @@ -6,7 +6,7 @@ import ( "os/exec" "sort" - flag "github.com/docker/docker/pkg/mflag" + "github.com/codegangsta/cli" "github.com/docker/machine/state" ) @@ -19,7 +19,7 @@ type Driver interface { // SetConfigFromFlags configures the driver with the object that was returned // by RegisterCreateFlags - SetConfigFromFlags(flags interface{}) error + SetConfigFromFlags(flags DriverOptions) error // GetURL returns a Docker compatible host URL for connecting to this host // e.g. tcp://1.2.3.4:2376 @@ -67,8 +67,8 @@ type Driver interface { // - RegisterCreateFlags: a function that takes the FlagSet for // "docker hosts create" and returns an object to pass to SetConfigFromFlags type RegisteredDriver struct { - New func(storePath string) (Driver, error) - RegisterCreateFlags func(cmd *flag.FlagSet) interface{} + New func(storePath string) (Driver, error) + GetCreateFlags func() []cli.Flag } var ErrHostIsNotRunning = errors.New("host is not running") @@ -86,8 +86,8 @@ func Register(name string, registeredDriver *RegisteredDriver) error { if _, exists := drivers[name]; exists { return fmt.Errorf("Name already registered %s", name) } - drivers[name] = registeredDriver + drivers[name] = registeredDriver return nil } @@ -100,14 +100,18 @@ func NewDriver(name string, storePath string) (Driver, error) { return driver.New(storePath) } -// RegisterCreateFlags runs RegisterCreateFlags for all of the drivers and +// GetCreateFlags runs GetCreateFlags for all of the drivers and // returns their return values indexed by the driver name -func RegisterCreateFlags(cmd *flag.FlagSet) map[string]interface{} { - flags := make(map[string]interface{}) +func GetCreateFlags() []cli.Flag { + flags := []cli.Flag{} + for driverName := range drivers { driver := drivers[driverName] - flags[driverName] = driver.RegisterCreateFlags(cmd) + for _, f := range driver.GetCreateFlags() { + flags = append(flags, f) + } } + return flags } @@ -120,3 +124,9 @@ func GetDriverNames() []string { sort.Strings(names) return names } + +type DriverOptions interface { + String(key string) string + Int(key string) int + Bool(key string) bool +} diff --git a/drivers/none/none.go b/drivers/none/none.go index 498fd08b47..1122e852d1 100644 --- a/drivers/none/none.go +++ b/drivers/none/none.go @@ -4,8 +4,8 @@ import ( "fmt" "os/exec" + "github.com/codegangsta/cli" "github.com/docker/docker/api" - flag "github.com/docker/docker/pkg/mflag" "github.com/docker/machine/drivers" "github.com/docker/machine/state" ) @@ -17,23 +17,21 @@ type Driver struct { URL string } -type CreateFlags struct { - URL *string -} - func init() { drivers.Register("none", &drivers.RegisteredDriver{ - New: NewDriver, - RegisterCreateFlags: RegisterCreateFlags, + New: NewDriver, + GetCreateFlags: GetCreateFlags, }) } -// RegisterCreateFlags registers the flags this driver adds to -// "docker hosts create" -func RegisterCreateFlags(cmd *flag.FlagSet) interface{} { - createFlags := new(CreateFlags) - createFlags.URL = cmd.String([]string{"-url"}, "", "URL of host when no driver is selected") - return createFlags +func GetCreateFlags() []cli.Flag { + return []cli.Flag{ + cli.StringFlag{ + Name: "url", + Usage: "URL of host when no driver is selected", + Value: "", + }, + } } func NewDriver(storePath string) (drivers.Driver, error) { @@ -44,16 +42,18 @@ func (d *Driver) DriverName() string { return "none" } -func (d *Driver) SetConfigFromFlags(flagsInterface interface{}) error { - flags := flagsInterface.(*CreateFlags) - if *flags.URL == "" { +func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { + url := flags.String("url") + + if url == "" { return fmt.Errorf("--url option is required when no driver is selected") } - url, err := api.ValidateHost(*flags.URL) + validatedUrl, err := api.ValidateHost(url) if err != nil { return err } - d.URL = url + + d.URL = validatedUrl return nil } diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index d913638d8c..70a88018cf 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -19,7 +19,7 @@ import ( "time" log "github.com/Sirupsen/logrus" - flag "github.com/docker/docker/pkg/mflag" + "github.com/codegangsta/cli" "github.com/docker/docker/utils" "github.com/docker/machine/drivers" "github.com/docker/machine/ssh" @@ -43,19 +43,32 @@ type CreateFlags struct { func init() { drivers.Register("virtualbox", &drivers.RegisteredDriver{ - New: NewDriver, - RegisterCreateFlags: RegisterCreateFlags, + New: NewDriver, + GetCreateFlags: GetCreateFlags, }) } // RegisterCreateFlags registers the flags this driver adds to // "docker hosts create" -func RegisterCreateFlags(cmd *flag.FlagSet) interface{} { - createFlags := new(CreateFlags) - createFlags.Memory = cmd.Int([]string{"-virtualbox-memory"}, 1024, "Size of memory for host in MB") - createFlags.DiskSize = cmd.Int([]string{"-virtualbox-disk-size"}, 20000, "Size of disk for host in MB") - createFlags.Boot2DockerURL = cmd.String([]string{"-virtualbox-boot2docker-url"}, "", "The URL of the boot2docker image. Defaults to the latest available version") - return createFlags +func GetCreateFlags() []cli.Flag { + return []cli.Flag{ + cli.IntFlag{ + Name: "virtualbox-memory", + Usage: "Size of memory for host in MB", + Value: 1024, + }, + cli.IntFlag{ + Name: "virtualbox-disk-size", + Usage: "Size of disk for host in MB", + Value: 20000, + }, + cli.StringFlag{ + EnvVar: "VIRTUALBOX_BOOT2DOCKER_URL", + Name: "virtualbox-boot2docker-url", + Usage: "The URL of the boot2docker image. Defaults to the latest available version", + Value: "", + }, + } } func NewDriver(storePath string) (drivers.Driver, error) { @@ -77,11 +90,10 @@ func (d *Driver) GetURL() (string, error) { return fmt.Sprintf("tcp://%s:2376", ip), nil } -func (d *Driver) SetConfigFromFlags(flagsInterface interface{}) error { - flags := flagsInterface.(*CreateFlags) - d.Memory = *flags.Memory - d.DiskSize = *flags.DiskSize - d.Boot2DockerURL = *flags.Boot2DockerURL +func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { + d.Memory = flags.Int("virtualbox-memory") + d.DiskSize = flags.Int("virtualbox-disk-size") + d.Boot2DockerURL = flags.String("virtualbox-boot2docker-url") return nil } diff --git a/main.go b/main.go index df3a37f700..1479911779 100644 --- a/main.go +++ b/main.go @@ -1,66 +1,32 @@ package main import ( - "fmt" "os" log "github.com/Sirupsen/logrus" - flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/docker/utils" -) - -var ( - flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") - flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") + "github.com/codegangsta/cli" ) func main() { - flag.Usage = func() { - fmt.Fprint(os.Stderr, "Usage: machine [OPTIONS] COMMAND [arg...]\n\nCreate and manage machines running Docker.\n\nOptions:\n") - - flag.PrintDefaults() - - help := "\nCommands:\n" - - for _, command := range [][]string{ - {"active", "Get or set the active machine"}, - {"create", "Create a machine"}, - {"inspect", "Inspect information about a machine"}, - {"ip", "Get the IP address of a machine"}, - {"kill", "Kill a machine"}, - {"ls", "List machines"}, - {"restart", "Restart a machine"}, - {"rm", "Remove a machine"}, - {"ssh", "Log into or run a command on a machine with SSH"}, - {"start", "Start a machine"}, - {"stop", "Stop a machine"}, - {"upgrade", "Upgrade a machine to the latest version of Docker"}, - {"url", "Get the URL of a machine"}, - } { - help += fmt.Sprintf(" %-10.10s%s\n", command[0], command[1]) + for _, f := range os.Args { + if f == "-D" || f == "--debug" || f == "-debug" { + os.Setenv("DEBUG", "1") + initLogging(log.DebugLevel) } - help += "\nRun 'machine COMMAND --help' for more information on a command." - fmt.Fprintf(os.Stderr, "%s\n", help) } - flag.Parse() + app := cli.NewApp() + app.Name = "machine" + app.Commands = Commands + app.Usage = "Create and manage machines running Docker." + app.Version = VERSION - // -D, --debug, -l/--log-level=debug processing - // When/if -D is removed this block can be deleted - if *flDebug { - os.Setenv("DEBUG", "1") - initLogging(log.DebugLevel) + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug, D", + Usage: "Enable debug mode", + }, } - cli := &DockerCli{} - - if err := cli.Cmd(flag.Args()...); err != nil { - if sterr, ok := err.(*utils.StatusError); ok { - if sterr.Status != "" { - log.Println(sterr.Status) - } - os.Exit(sterr.StatusCode) - } - log.Fatal(err) - } + app.Run(os.Args) } diff --git a/store.go b/store.go index 6994cb899a..10977866a5 100644 --- a/store.go +++ b/store.go @@ -20,7 +20,7 @@ func NewStore() *Store { return &Store{Path: rootPath} } -func (s *Store) Create(name string, driverName string, createFlags interface{}) (*Host, error) { +func (s *Store) Create(name string, driverName string, flags drivers.DriverOptions) (*Host, error) { exists, err := s.Exists(name) if err != nil { return nil, err @@ -35,8 +35,8 @@ func (s *Store) Create(name string, driverName string, createFlags interface{}) if err != nil { return host, err } - if createFlags != nil { - if err := host.Driver.SetConfigFromFlags(createFlags); err != nil { + if flags != nil { + if err := host.Driver.SetConfigFromFlags(flags); err != nil { return host, err } } diff --git a/store_test.go b/store_test.go index 79af244219..9149c37fcf 100644 --- a/store_test.go +++ b/store_test.go @@ -6,9 +6,25 @@ import ( "testing" "github.com/docker/machine/drivers" - "github.com/docker/machine/drivers/none" + _ "github.com/docker/machine/drivers/none" ) +type DriverOptionsMock struct { + Data map[string]interface{} +} + +func (d DriverOptionsMock) String(key string) string { + return d.Data[key].(string) +} + +func (d DriverOptionsMock) Int(key string) int { + return d.Data[key].(int) +} + +func (d DriverOptionsMock) Bool(key string) bool { + return d.Data[key].(bool) +} + func clearHosts() error { return os.RemoveAll(path.Join(drivers.GetHomeDir(), ".docker/hosts")) } @@ -18,9 +34,15 @@ func TestStoreCreate(t *testing.T) { t.Fatal(err) } + flags := &DriverOptionsMock{ + Data: map[string]interface{}{ + "url": "unix:///var/run/docker.sock", + }, + } + store := NewStore() - url := "unix:///var/run/docker.sock" - host, err := store.Create("test", "none", &none.CreateFlags{URL: &url}) + + host, err := store.Create("test", "none", flags) if err != nil { t.Fatal(err) } @@ -38,9 +60,14 @@ func TestStoreRemove(t *testing.T) { t.Fatal(err) } + flags := &DriverOptionsMock{ + Data: map[string]interface{}{ + "url": "unix:///var/run/docker.sock", + }, + } + store := NewStore() - url := "unix:///var/run/docker.sock" - _, err := store.Create("test", "none", &none.CreateFlags{URL: &url}) + _, err := store.Create("test", "none", flags) if err != nil { t.Fatal(err) } @@ -62,9 +89,14 @@ func TestStoreList(t *testing.T) { t.Fatal(err) } + flags := &DriverOptionsMock{ + Data: map[string]interface{}{ + "url": "unix:///var/run/docker.sock", + }, + } + store := NewStore() - url := "unix:///var/run/docker.sock" - _, err := store.Create("test", "none", &none.CreateFlags{URL: &url}) + _, err := store.Create("test", "none", flags) if err != nil { t.Fatal(err) } @@ -82,13 +114,18 @@ func TestStoreExists(t *testing.T) { t.Fatal(err) } + flags := &DriverOptionsMock{ + Data: map[string]interface{}{ + "url": "unix:///var/run/docker.sock", + }, + } + store := NewStore() exists, err := store.Exists("test") if exists { t.Fatal("Exists returned true when it should have been false") } - url := "unix:///var/run/docker.sock" - _, err = store.Create("test", "none", &none.CreateFlags{URL: &url}) + _, err = store.Create("test", "none", flags) if err != nil { t.Fatal(err) } @@ -106,9 +143,15 @@ func TestStoreLoad(t *testing.T) { t.Fatal(err) } - store := NewStore() expectedURL := "unix:///foo/baz" - _, err := store.Create("test", "none", &none.CreateFlags{URL: &expectedURL}) + flags := &DriverOptionsMock{ + Data: map[string]interface{}{ + "url": expectedURL, + }, + } + + store := NewStore() + _, err := store.Create("test", "none", flags) if err != nil { t.Fatal(err) } @@ -132,6 +175,12 @@ func TestStoreGetSetActive(t *testing.T) { t.Fatal(err) } + flags := &DriverOptionsMock{ + Data: map[string]interface{}{ + "url": "unix:///var/run/docker.sock", + }, + } + store := NewStore() // No hosts set @@ -144,8 +193,7 @@ func TestStoreGetSetActive(t *testing.T) { } // Set normal host - url := "unix:///var/run/docker.sock" - originalHost, err := store.Create("test", "none", &none.CreateFlags{URL: &url}) + originalHost, err := store.Create("test", "none", flags) if err != nil { t.Fatal(err) } @@ -181,5 +229,4 @@ func TestStoreGetSetActive(t *testing.T) { if host != nil { t.Fatalf("Active host is not nil", host.Name) } - } diff --git a/version.go b/version.go new file mode 100644 index 0000000000..365e6cb32b --- /dev/null +++ b/version.go @@ -0,0 +1,3 @@ +package main + +const VERSION = "0.0.1"