diff --git a/cmd/common.go b/cmd/common.go index f590960eb..3ed758fbe 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -11,7 +11,6 @@ import ( "syscall" "text/template" - "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/namesgenerator" "github.com/rancher/go-rancher/client" "github.com/urfave/cli" @@ -27,13 +26,12 @@ func GetRawClient(ctx *cli.Context) (*client.RancherClient, error) { if err != nil { return nil, err } - idx := strings.LastIndex(config.URL, "/v1") - if idx == -1 { - return nil, fmt.Errorf("Invalid URL %s, must contain /v1", config.URL) + url, err := baseURL(config.URL) + if err != nil { + return nil, err } - return client.NewRancherClient(&client.ClientOpts{ - Url: config.URL[:idx] + "/v1", + Url: url + "/v1", AccessKey: config.AccessKey, SecretKey: config.SecretKey, }) @@ -155,6 +153,25 @@ func GetOrCreateDefaultStack(c *client.RancherClient, name string) (*client.Envi }) } +func getProjectByName(c *client.RancherClient, name string) (client.ResourceCollection, error) { + var result client.ResourceCollection + err := c.List("account", &client.ListOpts{ + Filters: map[string]interface{}{ + "kind": "project", + "limit": "-2", + "name": name, + }, + }, &result) + for i, resource := range result.Data { + err := c.ById("project", resource.Id, &resource) + if err != nil { + return result, err + } + result.Data[i] = resource + } + return result, err +} + func getHostByHostname(c *client.RancherClient, name string) (client.ResourceCollection, error) { var result client.ResourceCollection allHosts, err := c.Host.List(nil) @@ -208,13 +225,16 @@ func Lookup(c *client.RancherClient, name string, types ...string) (*client.Reso } var collection client.ResourceCollection - if err := c.List(schemaType, &client.ListOpts{ - Filters: map[string]interface{}{ - "name": name, - "removed_null": 1, - }, - }, &collection); err != nil { - return nil, err + // search by name for project doesn't work + if schemaType != "project" { + if err := c.List(schemaType, &client.ListOpts{ + Filters: map[string]interface{}{ + "name": name, + "removed_null": 1, + }, + }, &collection); err != nil { + return nil, err + } } if len(collection.Data) > 1 { @@ -229,6 +249,8 @@ func Lookup(c *client.RancherClient, name string, types ...string) (*client.Reso var err error // Per type specific logic switch schemaType { + case "project": + collection, err = getProjectByName(c, name) case "host": collection, err = getHostByHostname(c, name) case "service": @@ -285,15 +307,6 @@ func SimpleFormat(values [][]string) (string, string) { return headerBuffer.String(), valueBuffer.String() } -func errorWrapper(f func(*cli.Context) error) func(*cli.Context) error { - return func(ctx *cli.Context) error { - if err := f(ctx); err != nil { - logrus.Fatal(err) - } - return nil - } -} - func defaultAction(fn func(ctx *cli.Context) error) func(ctx *cli.Context) error { return func(ctx *cli.Context) error { if ctx.Bool("help") { diff --git a/cmd/config.go b/cmd/config.go index 8f115c1cf..8e841d8f1 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net/url" "os" "path" "strings" @@ -22,6 +23,22 @@ type Config struct { Path string `json:"path,omitempty"` } +func baseURL(fullURL string) (string, error) { + idx := strings.LastIndex(fullURL, "/v1") + if idx == -1 { + u, err := url.Parse(fullURL) + if err != nil { + return "", err + } + newURL := url.URL{ + Scheme: u.Scheme, + Host: u.Host, + } + return newURL.String(), nil + } + return fullURL[:idx], nil +} + func (c Config) EnvironmentURL() (string, error) { projectID := c.Environment if projectID == "" || !strings.HasPrefix(projectID, "1a") { @@ -40,12 +57,11 @@ func (c Config) EnvironmentURL() (string, error) { projectID = project.Id } - idx := strings.LastIndex(c.URL, "/v1") - if idx == -1 { - return "", fmt.Errorf("Invalid URL %s, must contain /v1", c.URL) + url, err := baseURL(c.URL) + if err != nil { + return "", err } - - url := c.URL[:idx] + "/v1/projects/" + projectID + "/schemas" + url = url + "/v1/projects/" + projectID + "/schemas" return url, nil } @@ -89,7 +105,7 @@ func ConfigCommand() cli.Command { return cli.Command{ Name: "config", Usage: "Setup client configuration", - Action: errorWrapper(configSetup), + Action: configSetup, ArgsUsage: "None", Flags: []cli.Flag{ cli.BoolFlag{ diff --git a/cmd/container.go b/cmd/container.go deleted file mode 100644 index 96da75879..000000000 --- a/cmd/container.go +++ /dev/null @@ -1,52 +0,0 @@ -package cmd - -import "github.com/urfave/cli" - -func ContainerCommand() cli.Command { - return cli.Command{ - Name: "container", - Usage: "Interact with containers", - Action: defaultAction(errorWrapper(containerLs)), - Subcommands: []cli.Command{ - cli.Command{ - Name: "ls", - Usage: "List containers", - Action: errorWrapper(containerLs), - Flags: []cli.Flag{ - cli.BoolFlag{ - Name: "quiet,q", - Usage: "Only display IDs", - }, - }, - }, - }, - } -} - -func containerLs(ctx *cli.Context) error { - client, err := GetClient(ctx) - if err != nil { - return err - } - - writer := NewTableWriter([][]string{ - {"ID", "Id"}, - {"NAME", "Name"}, - {"STATE", "State"}, - {"CREATED", "Created"}, - {"START COUNT", "StartCount"}, - {"CREATE INDEX", "CreateIndex"}, - }, ctx) - defer writer.Close() - - collection, err := client.Container.List(nil) - if err != nil { - return err - } - - for _, item := range collection.Data { - writer.Write(item) - } - - return writer.Err() -} diff --git a/cmd/env.go b/cmd/env.go index be3343d7b..481f91191 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/Sirupsen/logrus" "github.com/rancher/go-rancher/client" "github.com/urfave/cli" ) @@ -21,8 +20,9 @@ func EnvCommand() cli.Command { Usage: "List environments", Description: "\nWith an account API key, all environments in Rancher will be listed. If you are using an environment API key, it will only list the environment of the API key. \n\nExample:\n\t$ rancher env ls\n", ArgsUsage: "None", - Action: errorWrapper(envLs), + Action: envLs, Flags: []cli.Flag{ + listAllFlag(), cli.BoolFlag{ Name: "quiet,q", Usage: "Only display IDs", @@ -38,7 +38,7 @@ func EnvCommand() cli.Command { Usage: "Create an environment", Description: "\nBy default, an environment with cattle orchestration framework will be created. This command only works for Account API keys.\n\nExample:\n\t$ rancher env create newEnv\n\t$ rancher env create -o kubernetes newK8sEnv\n\t$ rancher env create -o mesos newMesosEnv\n\t$ rancher env create -o swarm newSwarmEnv\n", ArgsUsage: "[NEWENVNAME...]", - Action: errorWrapper(envCreate), + Action: envCreate, Flags: []cli.Flag{ cli.StringFlag{ Name: "orchestration,o", @@ -51,7 +51,7 @@ func EnvCommand() cli.Command { Usage: "Remove environment(s)", Description: "\nExample:\n\t$ rancher env rm 1a5\n\t$ rancher env rm newEnv\n", ArgsUsage: "[ENVID ENVNAME...]", - Action: errorWrapper(envRm), + Action: envRm, Flags: []cli.Flag{}, }, cli.Command{ @@ -59,7 +59,7 @@ func EnvCommand() cli.Command { Usage: "Update environment", Description: "\nChange the orchestration framework of the environment. This command only works for Account API keys.\n\nExample:\n\t$ rancher env update -o kubernetes 1a5\n\t$ rancher env update -o cattle Default\n\t$ rancher env update -o swarm 1a5\n\t$ rancher env update -o mesos 1a5\n", ArgsUsage: "[ENVID ENVNAME...]", - Action: errorWrapper(envUpdate), + Action: envUpdate, Flags: []cli.Flag{ cli.StringFlag{ Name: "orchestration,o", @@ -67,6 +67,34 @@ func EnvCommand() cli.Command { }, }, }, + cli.Command{ + Name: "deactivate", + Usage: "Deactivate environment(s)", + Description: ` +Deactivate an environment by ID or name + +Example: + $ rancher env deactivate 1a5 + $ rancher env deactivate Default +`, + ArgsUsage: "[ID NAME...]", + Action: envDeactivate, + Flags: []cli.Flag{}, + }, + cli.Command{ + Name: "activate", + Usage: "Activate environment(s)", + Description: ` +Activate an environment by ID or name + +Example: + $ rancher env activate 1a5 + $ rancher env activate Default +`, + ArgsUsage: "[ID NAME...]", + Action: envActivate, + Flags: []cli.Flag{}, + }, }, } } @@ -102,23 +130,9 @@ func envRm(ctx *cli.Context) error { return err } - var lastErr error - for _, id := range ctx.Args() { - env, err := Lookup(c, id, "account") - if err != nil { - logrus.Errorf("Failed to delete %s: %v", id, err) - lastErr = err - continue - } - if err := c.Delete(env); err != nil { - logrus.Errorf("Failed to delete %s: %v", id, err) - lastErr = err - continue - } - fmt.Println(env.Id) - } - - return lastErr + return forEachResourceWithClient(c, ctx, []string{"project"}, func(c *client.RancherClient, resource *client.Resource) (string, error) { + return resource.Id, c.Delete(resource) + }) } func envUpdate(ctx *cli.Context) error { @@ -151,7 +165,7 @@ func envUpdate(ctx *cli.Context) error { return err } - fmt.Println(env.Name + " (" + env.Id + ")") + fmt.Println(env.Id) return nil } @@ -177,7 +191,7 @@ func envCreate(ctx *cli.Context) error { return err } - fmt.Println(newEnv.Name + " (" + newEnv.Id + ")") + fmt.Println(newEnv.Id) return nil } @@ -211,12 +225,9 @@ func envLs(ctx *cli.Context) error { defer writer.Close() collection := client.ProjectCollection{} - err = c.List("account", &client.ListOpts{ - Filters: map[string]interface{}{ - "kind": "project", - }, - }, &collection) - if err != nil { + listOpts := defaultListOpts(ctx) + listOpts.Filters["kind"] = "project" + if err = c.List("account", listOpts, &collection); err != nil { return err } @@ -226,3 +237,33 @@ func envLs(ctx *cli.Context) error { return writer.Err() } + +func envDeactivate(ctx *cli.Context) error { + c, err := GetRawClient(ctx) + if err != nil { + return err + } + + return forEachResourceWithClient(c, ctx, []string{"project"}, func(c *client.RancherClient, resource *client.Resource) (string, error) { + action, err := pickAction(resource, "deactivate") + if err != nil { + return "", err + } + return resource.Id, c.Action(resource.Type, action, resource, nil, resource) + }) +} + +func envActivate(ctx *cli.Context) error { + c, err := GetRawClient(ctx) + if err != nil { + return err + } + + return forEachResourceWithClient(c, ctx, []string{"project"}, func(c *client.RancherClient, resource *client.Resource) (string, error) { + action, err := pickAction(resource, "activate") + if err != nil { + return "", err + } + return resource.Id, c.Action(resource.Type, action, resource, nil, resource) + }) +} diff --git a/cmd/host.go b/cmd/host.go index 06cb38881..17491b512 100644 --- a/cmd/host.go +++ b/cmd/host.go @@ -17,7 +17,7 @@ func HostCommand() cli.Command { Usage: "List hosts", Description: "\nLists all hosts in the current $RANCHER_ENVIRONMENT. Use `--env ` or `--env ` to select a different environment.\n\nExample:\n\t$ rancher hosts ls\n\t$ rancher --env 1a5 hosts ls\n", ArgsUsage: "None", - Action: errorWrapper(hostLs), + Action: hostLs, Flags: []cli.Flag{ cli.BoolFlag{ Name: "quiet,q", @@ -35,7 +35,7 @@ func HostCommand() cli.Command { Description: "\nCreates a host in the $RANCHER_ENVIRONMENT. Use `--env ` or `--env ` to select a different environment.\n\nExample:\n\t$ rancher --env k8slab host create newHostName\n", ArgsUsage: "[NEWHOSTNAME...]", SkipFlagParsing: true, - Action: errorWrapper(hostCreate), + Action: hostCreate, }, }, } diff --git a/cmd/inspect.go b/cmd/inspect.go new file mode 100644 index 000000000..b566cf751 --- /dev/null +++ b/cmd/inspect.go @@ -0,0 +1,57 @@ +package cmd + +import ( + "strings" + + "github.com/rancher/go-rancher/client" + "github.com/urfave/cli" +) + +var ( + inspectTypes = []string{"service", "container", "host", "project", "environment"} +) + +func InspectCommand() cli.Command { + return cli.Command{ + Name: "inspect", + Usage: "View details for " + replaceTypeNames(strings.Join(inspectTypes, ", ")), + Description: ` +Inspect resources by ID or name in the current $RANCHER_ENVIRONMENT. Use '--env ' or '--env ' to select a different environment. + +Example: + $ rancher inspect 1s70 +`, + ArgsUsage: "[ID NAME...]", + Action: inspectResources, + Flags: []cli.Flag{ + typesStringFlag(stopTypes), + cli.BoolFlag{ + Name: "links", + Usage: "Include URLs to actions and links in resource output", + }, + cli.StringFlag{ + Name: "format", + Usage: "'json' or Custom format: {{.id]} {{.name}}", + Value: "json", + }, + }, + } +} + +func inspectResources(ctx *cli.Context) error { + writer := NewTableWriter(nil, ctx) + forEachResource(ctx, inspectTypes, func(c *client.RancherClient, resource *client.Resource) (string, error) { + mapResource := map[string]interface{}{} + err := c.ById(resource.Type, resource.Id, &mapResource) + if err != nil { + return "", err + } + if !ctx.Bool("links") { + delete(mapResource, "links") + delete(mapResource, "actions") + } + writer.Write(mapResource) + return "", nil + }) + return writer.Err() +} diff --git a/cmd/ps.go b/cmd/ps.go index d83bba301..01024fd4b 100644 --- a/cmd/ps.go +++ b/cmd/ps.go @@ -16,6 +16,7 @@ func PsCommand() cli.Command { ArgsUsage: "None", Action: servicePs, Flags: []cli.Flag{ + listAllFlag(), cli.BoolFlag{ Name: "containers,c", Usage: "Display containers", @@ -34,13 +35,7 @@ func PsCommand() cli.Command { func GetStackMap(c *client.RancherClient) map[string]client.Environment { result := map[string]client.Environment{} - - stacks, err := c.Environment.List(&client.ListOpts{ - Filters: map[string]interface{}{ - "limit": -1, - }, - }) - + stacks, err := c.Environment.List(defaultListOpts(nil)) if err != nil { return result } @@ -82,7 +77,7 @@ func servicePs(ctx *cli.Context) error { stackMap := GetStackMap(c) - collection, err := c.Service.List(nil) + collection, err := c.Service.List(defaultListOpts(ctx)) if err != nil { return errors.Wrap(err, "service list failed") } @@ -145,7 +140,7 @@ func serviceContainersPs(ctx *cli.Context, c *client.RancherClient, names []stri func hostContainerPs(ctx *cli.Context, c *client.RancherClient) error { if len(ctx.Args()) == 0 { - containerList, err := c.Container.List(nil) + containerList, err := c.Container.List(defaultListOpts(ctx)) if err != nil { return err } diff --git a/cmd/restart.go b/cmd/restart.go index 7caf42238..cd1741b54 100644 --- a/cmd/restart.go +++ b/cmd/restart.go @@ -1,7 +1,6 @@ package cmd import ( - "fmt" "strings" "github.com/rancher/go-rancher/client" @@ -9,7 +8,7 @@ import ( ) var ( - restartTypes = cli.StringSlice([]string{"service", "container"}) + restartTypes = []string{"service", "container"} ) func RestartCommand() cli.Command { @@ -20,11 +19,7 @@ func RestartCommand() cli.Command { ArgsUsage: "[ID NAME...]", Action: restartResources, Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "type", - Usage: "Restrict restart to specific types", - Value: &restartTypes, - }, + typesStringFlag(restartTypes), cli.IntFlag{ Name: "batch-size", Usage: "Number of containers to restart at a time", @@ -40,49 +35,17 @@ func RestartCommand() cli.Command { } func restartResources(ctx *cli.Context) error { - c, err := GetClient(ctx) - if err != nil { - return err - } - - w, err := NewWaiter(ctx) - if err != nil { - return err - } - - types := ctx.StringSlice("type") - - var lastErr error - var envErr error - for _, id := range ctx.Args() { - resource, err := Lookup(c, id, types...) + return forEachResource(ctx, restartTypes, func(c *client.RancherClient, resource *client.Resource) (string, error) { + action, err := pickAction(resource, "restart") if err != nil { - lastErr = err - if _, envErr = LookupEnvironment(c, id); envErr != nil { - fmt.Println("Incorrect usage: Environments cannot be restarted.") - } else { - fmt.Println(lastErr) - } - continue + return "", err } - - if err := c.Action(resource.Type, "restart", resource, &client.ServiceRestart{ + err = c.Action(resource.Type, action, resource, &client.ServiceRestart{ RollingRestartStrategy: client.RollingRestartStrategy{ BatchSize: int64(ctx.Int("batch-size")), IntervalMillis: int64(ctx.Int("interval")), }, - }, resource); err != nil { - lastErr = err - fmt.Println(lastErr) - } else { - w.Add(resource.Id) - //fmt.Println(resource.Id) - } - - if lastErr != nil && envErr == nil { - return lastErr - } - } - - return w.Wait() + }, resource) + return resource.Id, err + }) } diff --git a/cmd/rm.go b/cmd/rm.go index 827d879eb..91177370c 100644 --- a/cmd/rm.go +++ b/cmd/rm.go @@ -1,8 +1,9 @@ package cmd import ( - "fmt" + "strings" + "github.com/rancher/go-rancher/client" "github.com/urfave/cli" ) @@ -13,7 +14,7 @@ var ( func RmCommand() cli.Command { return cli.Command{ Name: "rm", - Usage: "Delete resources", + Usage: "Delete " + strings.Join(rmTypes, ", "), Description: "\nDeletes resources by ID or name in the current $RANCHER_ENVIRONMENT. Use `--env ` or `--env ` to select a different environment.\n\nExample:\n\t$ rancher rm 1s70\n\t$ rancher --env 1a5 rm stackName/serviceName \n", ArgsUsage: "[ID NAME...]", Action: deleteResources, @@ -23,51 +24,31 @@ func RmCommand() cli.Command { Usage: "Restrict delete to specific types", Value: &cli.StringSlice{}, }, + cli.BoolFlag{ + Name: "stop,s", + Usage: "Stop or deactivate resource first if needed before deleting", + }, }, } } func deleteResources(ctx *cli.Context) error { - c, err := GetClient(ctx) - if err != nil { - return err - } - - w, err := NewWaiter(ctx) - if err != nil { - return err - } - - types := ctx.StringSlice("type") - if len(types) == 0 { - types = rmTypes - } - - var lastErr error - var envErr error - for _, id := range ctx.Args() { - resource, err := Lookup(c, id, types...) - if err != nil { - lastErr = err - if _, envErr = LookupEnvironment(c, id); envErr != nil { - fmt.Println("Incorrect usage: Use `rancher env rm`.") - } else { - fmt.Println(lastErr) + return forEachResource(ctx, rmTypes, func(c *client.RancherClient, resource *client.Resource) (string, error) { + if ctx.Bool("stop") { + action, err := pickAction(resource, "stop", "deactivate") + if err == nil { + w, err := NewWaiter(ctx) + if err != nil { + return "", err + } + if err := c.Action(resource.Type, action, resource, nil, resource); err != nil { + return "", err + } + if err := w.Add(resource.Id).Wait(); err != nil { + return "", err + } } - continue } - - if err := c.Delete(resource); err != nil { - lastErr = err - fmt.Println(lastErr) - } else { - w.Add(resource.Id) - } - } - - if lastErr != nil && envErr == nil { - return lastErr - } - - return w.Wait() + return resource.Id, c.Delete(resource) + }) } diff --git a/cmd/run.go b/cmd/run.go index 973ac101e..7ff8cedd5 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -12,9 +12,6 @@ import ( --add-host=[] Add a custom host-to-IP mapping (host:ip) --blkio-weight Block IO (relative weight), between 10 and 1000 --blkio-weight-device=[] Block IO weight (relative device weight) - x--cpu-shares CPU shares (relative weight) - x--cap-add=[] Add Linux capabilities - x--cap-drop=[] Drop Linux capabilities --cgroup-parent Optional parent cgroup for the container --cidfile Write the container ID to the file --cpu-period Limit CPU CFS (Completely Fair Scheduler) period @@ -23,23 +20,16 @@ import ( --cpuset-mems MEMs in which to allow execution (0-3, 0,1) -d, --detach Run container in background and print container ID --detach-keys Override the key sequence for detaching a container - x--device=[] Add a host device to the container --device-read-bps=[] Limit read rate (bytes per second) from a device --device-read-iops=[] Limit read rate (IO per second) from a device --device-write-bps=[] Limit write rate (bytes per second) to a device --device-write-iops=[] Limit write rate (IO per second) to a device --disable-content-trust=true Skip image verification - x--dns=[] Set custom DNS servers --dns-opt=[] Set DNS options - x--dns-search=[] Set custom DNS search domains -e, --env=[] Set environment variables - x--entrypoint Overwrite the default ENTRYPOINT of the image --env-file=[] Read in a file of environment variables - x--expose=[] Expose a port or a range of ports --group-add=[] Add additional groups to join - x-h, --hostname Container host name --help Print usage - x-i, --interactive Keep STDIN open even if not attached --ip Container IPv4 address (e.g. 172.30.100.104) --ip6 Container IPv6 address (e.g. 2001:db8::33) --ipc IPC namespace to use @@ -50,35 +40,23 @@ import ( --link=[] Add link to another container --log-driver Logging driver for container --log-opt=[] Log driver options - x-m, --memory Memory limit --mac-address Container MAC address (e.g. 92:d0:c6:0a:29:33) --memory-reservation Memory soft limit - x--memory-swap Swap limit equal to memory plus swap: '-1' to enable unlimited swap --memory-swappiness=-1 Tune container memory swappiness (0 to 100) - x--name Assign a name to the container --net=default Connect a container to a network --net-alias=[] Add network-scoped alias for the container --oom-kill-disable Disable OOM Killer --oom-score-adj Tune host's OOM preferences (-1000 to 1000) - x-P, --publish-all Publish all exposed ports to random ports - x-p, --publish=[] Publish a container's port(s) to the host - x--pid PID namespace to use - x--privileged Give extended privileges to this container - x--read-only Mount the container's root filesystem as read only --restart=no Restart policy to apply when a container exits --rm Automatically remove the container when it exits - x--security-opt=[] Security Options --shm-size Size of /dev/shm, default value is 64MB --sig-proxy=true Proxy received signals to the process --stop-signal=SIGTERM Signal to stop a container, SIGTERM by default --tmpfs=[] Mount a tmpfs directory - x-u, --user Username or UID (format: [:]) --ulimit=[] Ulimit options --uts UTS namespace to use -v, --volume=[] Bind mount a volume - x--volume-driver Optional volume driver for the container --volumes-from=[] Mount volumes from the specified container(s) - x-w, --workdir Working directory inside the container */ func RunCommand() cli.Command { @@ -179,6 +157,19 @@ func RunCommand() cli.Command { Name: "workdir, w", Usage: "Working directory inside the container", }, + cli.StringFlag{ + Name: "log-driver", + Usage: "Logging driver for container", + }, + cli.StringSliceFlag{ + Name: "log-opt", + Usage: "Log driver options", + }, + cli.StringFlag{ + Name: "net", + Usage: "Connect a container to a network: host, none, bridge, managed", + Value: "managed", + }, }, } } @@ -216,7 +207,6 @@ func serviceRun(ctx *cli.Context) error { } launchConfig := &client.LaunchConfig{ - //BlkioDeviceOptions: CapAdd: ctx.StringSlice("cap-add"), CapDrop: ctx.StringSlice("cap-drop"), @@ -235,7 +225,7 @@ func serviceRun(ctx *cli.Context) error { Memory: ctx.Int64("memory"), MemorySwap: ctx.Int64("memory-swap"), //NetworkIds: ctx.StringSlice("networkids"), - //NetworkMode: ctx.String("network"), + NetworkMode: ctx.String("net"), PidMode: ctx.String("pid"), Ports: ctx.StringSlice("publish"), Privileged: ctx.Bool("privileged"), @@ -249,6 +239,21 @@ func serviceRun(ctx *cli.Context) error { WorkingDir: ctx.String("workdir"), } + if ctx.String("log-driver") != "" || len(ctx.StringSlice("log-opt")) > 0 { + launchConfig.LogConfig = &client.LogConfig{ + Driver: ctx.String("log-driver"), + Config: map[string]interface{}{}, + } + for _, opt := range ctx.StringSlice("log-opt") { + parts := strings.SplitN(opt, "=", 2) + if len(parts) > 1 { + launchConfig.LogConfig.Config[parts[0]] = parts[1] + } else { + launchConfig.LogConfig.Config[parts[0]] = "" + } + } + } + args := ctx.Args()[1:] if len(args) > 0 { diff --git a/cmd/start.go b/cmd/start.go index d2f62c37e..b0bf71801 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -1,14 +1,14 @@ package cmd import ( - "fmt" "strings" + "github.com/rancher/go-rancher/client" "github.com/urfave/cli" ) var ( - startTypes = cli.StringSlice([]string{"service", "container", "host"}) + startTypes = []string{"service", "container", "host"} ) func StartCommand() cli.Command { @@ -20,58 +20,18 @@ func StartCommand() cli.Command { ArgsUsage: "[ID NAME...]", Action: startResources, Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "type", - Usage: "Restrict start to specific types", - Value: &startTypes, - }, + typesStringFlag(startTypes), }, } } func startResources(ctx *cli.Context) error { - c, err := GetClient(ctx) - if err != nil { - return err - } - - types := ctx.StringSlice("type") - - w, err := NewWaiter(ctx) - if err != nil { - return err - } - - var lastErr error - var envErr error - for _, id := range ctx.Args() { - resource, err := Lookup(c, id, types...) + return forEachResource(ctx, startTypes, func(c *client.RancherClient, resource *client.Resource) (string, error) { + action, err := pickAction(resource, "start", "activate") if err != nil { - lastErr = err - if _, envErr = LookupEnvironment(c, id); envErr != nil { - fmt.Println("Incorrect usage: Use `rancher env start`.") - } else { - fmt.Println(lastErr) - } - continue - } - - action := "activate" - if _, ok := resource.Actions["start"]; ok { - action = "start" + return "", err } - - if err := c.Action(resource.Type, action, resource, nil, resource); err != nil { - lastErr = err - fmt.Println(lastErr) - } else { - w.Add(resource.Id) - } - } - - if lastErr != nil && envErr == nil { - return lastErr - } - - return w.Wait() + err = c.Action(resource.Type, action, resource, nil, resource) + return resource.Id, err + }) } diff --git a/cmd/stop.go b/cmd/stop.go index 4de3f433b..cf39aca0a 100644 --- a/cmd/stop.go +++ b/cmd/stop.go @@ -1,14 +1,14 @@ package cmd import ( - "fmt" "strings" + "github.com/rancher/go-rancher/client" "github.com/urfave/cli" ) var ( - stopTypes = cli.StringSlice([]string{"service", "container", "host"}) + stopTypes = []string{"service", "container", "host"} ) func StopCommand() cli.Command { @@ -20,63 +20,17 @@ func StopCommand() cli.Command { ArgsUsage: "[ID NAME...]", Action: stopResources, Flags: []cli.Flag{ - cli.StringSliceFlag{ - Name: "type", - Usage: "Restrict stop to specific types", - Value: &stopTypes, - }, + typesStringFlag(stopTypes), }, } } func stopResources(ctx *cli.Context) error { - c, err := GetClient(ctx) - if err != nil { - return err - } - - types := ctx.StringSlice("type") - - w, err := NewWaiter(ctx) - if err != nil { - return err - } - - var lastErr error - var envErr error - for _, id := range ctx.Args() { - resource, err := Lookup(c, id, types...) + return forEachResource(ctx, stopTypes, func(c *client.RancherClient, resource *client.Resource) (string, error) { + action, err := pickAction(resource, "stop", "deactivate") if err != nil { - lastErr = err - if _, envErr = LookupEnvironment(c, id); envErr != nil { - fmt.Println("Incorrect usage: Use `rancher env stop`.") - } else { - fmt.Println(lastErr) - } - continue - } - - action := "" - if _, ok := resource.Actions["stop"]; ok { - action = "stop" - } else if _, ok := resource.Actions["deactivate"]; ok { - action = "deactivate" + return "", err } - - if action == "" { - lastErr = fmt.Errorf("stop or deactivate not available on %s", id) - fmt.Println(lastErr) - } else if err := c.Action(resource.Type, action, resource, nil, resource); err != nil { - lastErr = err - fmt.Println(lastErr) - } else { - w.Add(resource.Id) - } - } - - if lastErr != nil && envErr == nil { - return lastErr - } - - return w.Wait() + return resource.Id, c.Action(resource.Type, action, resource, nil, resource) + }) } diff --git a/cmd/type.go b/cmd/type.go new file mode 100644 index 000000000..4629d85db --- /dev/null +++ b/cmd/type.go @@ -0,0 +1,27 @@ +package cmd + +import ( + "fmt" + "strings" + + "github.com/urfave/cli" +) + +func typesStringFlag(def []string) cli.StringSliceFlag { + usage := "Restrict restart to specific types" + if len(def) > 0 { + usage = fmt.Sprintf("%s (%s)", usage, strings.Join(def, ", ")) + } + return cli.StringSliceFlag{ + Name: "type", + Usage: usage, + } +} + +func getTypesStringFlag(ctx *cli.Context, def []string) []string { + val := ctx.StringSlice("type") + if len(val) > 0 { + return val + } + return def +} diff --git a/cmd/up.go b/cmd/up.go index b39b02370..b24032b34 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -8,15 +8,34 @@ import ( func UpCommand() cli.Command { factory := &projectFactory{} - return rancherApp.UpCommand(factory) + cmd := rancherApp.UpCommand(factory) + cmd.Flags = append(cmd.Flags, []cli.Flag{ + cli.StringFlag{ + Name: "rancher-file", + Usage: "Specify an alternate Rancher compose file (default: rancher-compose.yml)", + }, + cli.StringFlag{ + Name: "env-file,e", + Usage: "Specify a file from which to read environment variables", + }, + cli.StringSliceFlag{ + Name: "file,f", + Usage: "Specify one or more alternate compose files (default: docker-compose.yml)", + Value: &cli.StringSlice{}, + EnvVar: "COMPOSE_FILE", + }, + cli.StringFlag{ + Name: "stack,s", + Usage: "Specify an alternate project name (default: directory name)", + }, + }...) + return cmd } type projectFactory struct { } func (p *projectFactory) Create(c *cli.Context) (project.APIProject, error) { - factory := &rancherApp.ProjectFactory{} - config, err := lookupConfig(c) if err != nil { return nil, err @@ -27,10 +46,19 @@ func (p *projectFactory) Create(c *cli.Context) (project.APIProject, error) { return nil, err } + // from config c.GlobalSet("url", url) c.GlobalSet("access-key", config.AccessKey) c.GlobalSet("secret-key", config.SecretKey) - c.GlobalSet("project-name", c.GlobalString("stack")) + // copy from flags + c.GlobalSet("rancher-file", c.String("rancher-file")) + c.GlobalSet("env-file", c.String("env-file")) + c.GlobalSet("project-name", c.String("stack")) + for _, f := range c.StringSlice("file") { + c.GlobalSet("file", f) + } + + factory := &rancherApp.ProjectFactory{} return factory.Create(c) } diff --git a/cmd/util_actions.go b/cmd/util_actions.go new file mode 100644 index 000000000..5f4debe55 --- /dev/null +++ b/cmd/util_actions.go @@ -0,0 +1,24 @@ +package cmd + +import ( + "errors" + "fmt" + "strings" + + "github.com/rancher/go-rancher/client" +) + +func pickAction(resource *client.Resource, actions ...string) (string, error) { + for _, action := range actions { + if _, ok := resource.Actions[action]; ok { + return action, nil + } + } + msg := fmt.Sprintf("%s not currently available on %s %s", strings.Join(actions, "/"), resource.Type, resource.Id) + return "", errors.New(replaceTypeNames(msg)) +} + +func replaceTypeNames(msg string) string { + msg = strings.Replace(msg, "environment", "stack", -1) + return strings.Replace(msg, "project", "enviroment", -1) +} diff --git a/cmd/util_foreach.go b/cmd/util_foreach.go new file mode 100644 index 000000000..ad38b66df --- /dev/null +++ b/cmd/util_foreach.go @@ -0,0 +1,57 @@ +package cmd + +import ( + "fmt" + + "github.com/rancher/go-rancher/client" + "github.com/urfave/cli" +) + +func printErr(id string, oldErr, newErr error) error { + if newErr != nil { + fmt.Printf("error %s: %s\n", id, newErr.Error()) + return newErr + } + return oldErr +} + +func forEachResourceWithClient(c *client.RancherClient, ctx *cli.Context, types []string, fn func(c *client.RancherClient, resource *client.Resource) (string, error)) error { + types = getTypesStringFlag(ctx, types) + w, err := NewWaiter(ctx) + if err != nil { + return err + } + + var lastErr error + for _, id := range ctx.Args() { + resource, err := Lookup(c, id, types...) + if err != nil { + lastErr = printErr(id, lastErr, err) + continue + } + + resourceID, err := fn(c, resource) + if resourceID == "" { + resourceID = resource.Id + } + lastErr = printErr(resource.Id, lastErr, err) + if resourceID != "" { + w.Add(resourceID) + } + } + + if lastErr != nil { + return cli.NewExitError("", 1) + } + + return w.Wait() +} + +func forEachResource(ctx *cli.Context, types []string, fn func(c *client.RancherClient, resource *client.Resource) (string, error)) error { + c, err := GetClient(ctx) + if err != nil { + return err + } + + return forEachResourceWithClient(c, ctx, types, fn) +} diff --git a/cmd/util_ls.go b/cmd/util_ls.go new file mode 100644 index 000000000..ead05bbec --- /dev/null +++ b/cmd/util_ls.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "github.com/rancher/go-rancher/client" + "github.com/urfave/cli" +) + +func listAllFlag() cli.BoolFlag { + return cli.BoolFlag{ + Name: "all,a", + Usage: "Show stop/inactive and recently removed resources", + } +} + +func defaultListOpts(ctx *cli.Context) *client.ListOpts { + listOpts := &client.ListOpts{ + Filters: map[string]interface{}{ + "limit": -2, + }, + } + if ctx != nil && !ctx.Bool("all") { + listOpts.Filters["removed_null"] = "1" + listOpts.Filters["state_ne"] = []string{ + "inactive", + "stopped", + "removing", + } + } + return listOpts +} diff --git a/cmd/wait.go b/cmd/wait.go index 1b794ab82..217cd50de 100644 --- a/cmd/wait.go +++ b/cmd/wait.go @@ -88,9 +88,12 @@ func (r ResourceID) Type() string { return str[:strings.Index(str, ":")] } -func (w *Waiter) Add(resource string) { - fmt.Println(resource) - w.resources = append(w.resources, resource) +func (w *Waiter) Add(resources ...string) *Waiter { + for _, resource := range resources { + fmt.Println(resource) + w.resources = append(w.resources, resource) + } + return w } func (w *Waiter) done(resourceType, id string) (bool, error) { diff --git a/main.go b/main.go index d765bd049..d4597c7ea 100644 --- a/main.go +++ b/main.go @@ -17,8 +17,8 @@ Usage: {{.Name}} {{if .Flags}}[OPTIONS] {{end}}COMMAND [arg...] Version: {{.Version}} {{if .Flags}} Options: - {{range .Flags}}{{.}} - {{end}}{{end}} + {{range .Flags}}{{if .Hidden}}{{else}}{{.}} + {{end}}{{end}}{{end}} Commands: {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} {{end}} @@ -90,24 +90,6 @@ func mainErr() error { Usage: "Host used for docker command", EnvVar: "RANCHER_DOCKER_HOST", }, - cli.StringFlag{ - Name: "rancher-file,r", - Usage: "Specify an alternate Rancher compose file (default: rancher-compose.yml)", - }, - cli.StringFlag{ - Name: "env-file,e", - Usage: "Specify a file from which to read environment variables", - }, - cli.StringSliceFlag{ - Name: "file,f", - Usage: "Specify one or more alternate compose files (default: docker-compose.yml)", - Value: &cli.StringSlice{}, - EnvVar: "COMPOSE_FILE", - }, - cli.StringFlag{ - Name: "stack,s", - Usage: "Specify an alternate project name (default: directory name)", - }, cli.BoolFlag{ Name: "wait,w", Usage: "Wait for resource to reach resting state", @@ -121,6 +103,23 @@ func mainErr() error { Name: "wait-state", Usage: "State to wait for (active, healthy, etc)", }, + // Below four flags are for rancher-compose code capability. The users doesn't use them directly + cli.StringFlag{ + Name: "rancher-file", + Hidden: true, + }, + cli.StringFlag{ + Name: "env-file", + Hidden: true, + }, + cli.StringSliceFlag{ + Name: "file,f", + Hidden: true, + }, + cli.StringFlag{ + Name: "project-name", + Hidden: true, + }, } app.Commands = []cli.Command{ cmd.CatalogCommand(), @@ -143,6 +142,7 @@ func mainErr() error { cmd.StopCommand(), cmd.UpCommand(), //cmd.VolumeCommand(), + cmd.InspectCommand(), cmd.WaitCommand(), } diff --git a/trash.conf b/trash.conf index 4f25bebad..a3ce7e0b9 100644 --- a/trash.conf +++ b/trash.conf @@ -16,7 +16,7 @@ github.com/Microsoft/go-winio v0.3.0 github.com/opencontainers/runc cc29e3dded8e27ba8f65738f40d251c885030a28 github.com/patrickmn/go-cache 1881a9bccb818787f68c52bfba648c6cf34c34fa github.com/pkg/errors 1d2e60385a13aaa66134984235061c2f9302520e -github.com/rancher/go-rancher bdb84801f2cf82be73e210ff586c531b07c83c42 +github.com/rancher/go-rancher 96dc0b0438660397a2119fb2fd8ead1d6d56608f github.com/rancher/rancher-catalog-service a3a8b500adceb82b3a0387b2c7b06a60e7eeee9a github.com/rancher/rancher-compose ea72f323fb6b7f2b457b919b10e530fc823f9679 github.com/rancher/rancher-docker-api-proxy 152712a566dbdb8b809ee42b20d9d18f15d7ece0 diff --git a/vendor/github.com/rancher/go-rancher/client/common.go b/vendor/github.com/rancher/go-rancher/client/common.go index a9f5a40d2..910285996 100644 --- a/vendor/github.com/rancher/go-rancher/client/common.go +++ b/vendor/github.com/rancher/go-rancher/client/common.go @@ -115,7 +115,13 @@ func appendFilters(urlString string, filters map[string]interface{}) (string, er q := u.Query() for k, v := range filters { - q.Add(k, fmt.Sprintf("%v", v)) + if l, ok := v.([]string); ok { + for _, v := range l { + q.Add(k, v) + } + } else { + q.Add(k, fmt.Sprintf("%v", v)) + } } u.RawQuery = q.Encode() diff --git a/vendor/github.com/rancher/go-rancher/client/generated_agent.go b/vendor/github.com/rancher/go-rancher/client/generated_agent.go index 557ca6188..d3bfb0729 100644 --- a/vendor/github.com/rancher/go-rancher/client/generated_agent.go +++ b/vendor/github.com/rancher/go-rancher/client/generated_agent.go @@ -60,6 +60,8 @@ type AgentOperations interface { ActionDeactivate(*Agent) (*Agent, error) + ActionDisconnect(*Agent) (*Agent, error) + ActionPurge(*Agent) (*Agent, error) ActionReconnect(*Agent) (*Agent, error) @@ -137,6 +139,15 @@ func (c *AgentClient) ActionDeactivate(resource *Agent) (*Agent, error) { return resp, err } +func (c *AgentClient) ActionDisconnect(resource *Agent) (*Agent, error) { + + resp := &Agent{} + + err := c.rancherClient.doAction(AGENT_TYPE, "disconnect", &resource.Resource, nil, resp) + + return resp, err +} + func (c *AgentClient) ActionPurge(resource *Agent) (*Agent, error) { resp := &Agent{} diff --git a/vendor/github.com/rancher/go-rancher/client/generated_amazonec2config.go b/vendor/github.com/rancher/go-rancher/client/generated_amazonec2config.go index 8306f1ffc..0c7c35b40 100644 --- a/vendor/github.com/rancher/go-rancher/client/generated_amazonec2config.go +++ b/vendor/github.com/rancher/go-rancher/client/generated_amazonec2config.go @@ -11,6 +11,8 @@ type Amazonec2Config struct { Ami string `json:"ami,omitempty" yaml:"ami,omitempty"` + DeviceName string `json:"deviceName,omitempty" yaml:"device_name,omitempty"` + IamInstanceProfile string `json:"iamInstanceProfile,omitempty" yaml:"iam_instance_profile,omitempty"` InstanceType string `json:"instanceType,omitempty" yaml:"instance_type,omitempty"` @@ -33,12 +35,20 @@ type Amazonec2Config struct { SpotPrice string `json:"spotPrice,omitempty" yaml:"spot_price,omitempty"` + SshKeypath string `json:"sshKeypath,omitempty" yaml:"ssh_keypath,omitempty"` + SshUser string `json:"sshUser,omitempty" yaml:"ssh_user,omitempty"` SubnetId string `json:"subnetId,omitempty" yaml:"subnet_id,omitempty"` + Tags string `json:"tags,omitempty" yaml:"tags,omitempty"` + + UseEbsOptimizedInstance bool `json:"useEbsOptimizedInstance,omitempty" yaml:"use_ebs_optimized_instance,omitempty"` + UsePrivateAddress bool `json:"usePrivateAddress,omitempty" yaml:"use_private_address,omitempty"` + VolumeType string `json:"volumeType,omitempty" yaml:"volume_type,omitempty"` + VpcId string `json:"vpcId,omitempty" yaml:"vpc_id,omitempty"` Zone string `json:"zone,omitempty" yaml:"zone,omitempty"` diff --git a/vendor/github.com/rancher/go-rancher/client/generated_client.go b/vendor/github.com/rancher/go-rancher/client/generated_client.go index 152f1177c..cc3d43ac4 100644 --- a/vendor/github.com/rancher/go-rancher/client/generated_client.go +++ b/vendor/github.com/rancher/go-rancher/client/generated_client.go @@ -9,8 +9,10 @@ type RancherClient struct { AddRemoveLoadBalancerServiceLinkInput AddRemoveLoadBalancerServiceLinkInputOperations AddRemoveServiceLinkInput AddRemoveServiceLinkInputOperations Agent AgentOperations + Amazonec2Config Amazonec2ConfigOperations ApiKey ApiKeyOperations AuditLog AuditLogOperations + AzureConfig AzureConfigOperations Azureadconfig AzureadconfigOperations Backup BackupOperations BackupTarget BackupTargetOperations @@ -32,6 +34,7 @@ type RancherClient struct { Credential CredentialOperations Databasechangelog DatabasechangelogOperations Databasechangeloglock DatabasechangeloglockOperations + DigitaloceanConfig DigitaloceanConfigOperations DnsService DnsServiceOperations DockerBuild DockerBuildOperations DynamicSchema DynamicSchemaOperations @@ -89,6 +92,7 @@ type RancherClient struct { Network NetworkOperations NfsConfig NfsConfigOperations Openldapconfig OpenldapconfigOperations + PacketConfig PacketConfigOperations Password PasswordOperations PhysicalHost PhysicalHostOperations Port PortOperations @@ -156,8 +160,10 @@ func constructClient() *RancherClient { client.AddRemoveLoadBalancerServiceLinkInput = newAddRemoveLoadBalancerServiceLinkInputClient(client) client.AddRemoveServiceLinkInput = newAddRemoveServiceLinkInputClient(client) client.Agent = newAgentClient(client) + client.Amazonec2Config = newAmazonec2ConfigClient(client) client.ApiKey = newApiKeyClient(client) client.AuditLog = newAuditLogClient(client) + client.AzureConfig = newAzureConfigClient(client) client.Azureadconfig = newAzureadconfigClient(client) client.Backup = newBackupClient(client) client.BackupTarget = newBackupTargetClient(client) @@ -179,6 +185,7 @@ func constructClient() *RancherClient { client.Credential = newCredentialClient(client) client.Databasechangelog = newDatabasechangelogClient(client) client.Databasechangeloglock = newDatabasechangeloglockClient(client) + client.DigitaloceanConfig = newDigitaloceanConfigClient(client) client.DnsService = newDnsServiceClient(client) client.DockerBuild = newDockerBuildClient(client) client.DynamicSchema = newDynamicSchemaClient(client) @@ -236,6 +243,7 @@ func constructClient() *RancherClient { client.Network = newNetworkClient(client) client.NfsConfig = newNfsConfigClient(client) client.Openldapconfig = newOpenldapconfigClient(client) + client.PacketConfig = newPacketConfigClient(client) client.Password = newPasswordClient(client) client.PhysicalHost = newPhysicalHostClient(client) client.Port = newPortClient(client) diff --git a/vendor/github.com/rancher/go-rancher/client/generated_digitalocean_config.go b/vendor/github.com/rancher/go-rancher/client/generated_digitalocean_config.go index c30234b9b..9976dd543 100644 --- a/vendor/github.com/rancher/go-rancher/client/generated_digitalocean_config.go +++ b/vendor/github.com/rancher/go-rancher/client/generated_digitalocean_config.go @@ -21,7 +21,11 @@ type DigitaloceanConfig struct { Size string `json:"size,omitempty" yaml:"size,omitempty"` + SshPort string `json:"sshPort,omitempty" yaml:"ssh_port,omitempty"` + SshUser string `json:"sshUser,omitempty" yaml:"ssh_user,omitempty"` + + Userdata string `json:"userdata,omitempty" yaml:"userdata,omitempty"` } type DigitaloceanConfigCollection struct { diff --git a/vendor/github.com/rancher/go-rancher/client/generated_machine.go b/vendor/github.com/rancher/go-rancher/client/generated_machine.go index a62d0779d..fb7768d97 100644 --- a/vendor/github.com/rancher/go-rancher/client/generated_machine.go +++ b/vendor/github.com/rancher/go-rancher/client/generated_machine.go @@ -9,16 +9,22 @@ type Machine struct { AccountId string `json:"accountId,omitempty" yaml:"account_id,omitempty"` + Amazonec2Config *Amazonec2Config `json:"amazonec2Config,omitempty" yaml:"amazonec2config,omitempty"` + AuthCertificateAuthority string `json:"authCertificateAuthority,omitempty" yaml:"auth_certificate_authority,omitempty"` AuthKey string `json:"authKey,omitempty" yaml:"auth_key,omitempty"` + AzureConfig *AzureConfig `json:"azureConfig,omitempty" yaml:"azure_config,omitempty"` + Created string `json:"created,omitempty" yaml:"created,omitempty"` Data map[string]interface{} `json:"data,omitempty" yaml:"data,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` + DigitaloceanConfig *DigitaloceanConfig `json:"digitaloceanConfig,omitempty" yaml:"digitalocean_config,omitempty"` + DockerVersion string `json:"dockerVersion,omitempty" yaml:"docker_version,omitempty"` Driver string `json:"driver,omitempty" yaml:"driver,omitempty"` @@ -47,6 +53,8 @@ type Machine struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` + PacketConfig *PacketConfig `json:"packetConfig,omitempty" yaml:"packet_config,omitempty"` + RemoveTime string `json:"removeTime,omitempty" yaml:"remove_time,omitempty"` Removed string `json:"removed,omitempty" yaml:"removed,omitempty"`