|
@@ -5,6 +5,7 @@ import (
|
|
"encoding/json"
|
|
"encoding/json"
|
|
"flag"
|
|
"flag"
|
|
"fmt"
|
|
"fmt"
|
|
|
|
+ "io"
|
|
"io/ioutil"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http"
|
|
"os"
|
|
"os"
|
|
@@ -15,7 +16,17 @@ func ParseCommands(args []string) error {
|
|
|
|
|
|
cmds := map[string]func(args []string) error{
|
|
cmds := map[string]func(args []string) error{
|
|
"images": cmdImages,
|
|
"images": cmdImages,
|
|
|
|
+ "info": cmdInfo,
|
|
|
|
+ "history": cmdHistory,
|
|
"kill": cmdKill,
|
|
"kill": cmdKill,
|
|
|
|
+ "logs": cmdLogs,
|
|
|
|
+ "ps": cmdPs,
|
|
|
|
+ "restart": cmdRestart,
|
|
|
|
+ "rm": cmdRm,
|
|
|
|
+ "rmi": cmdRmi,
|
|
|
|
+ "run": cmdRun,
|
|
|
|
+ "start": cmdStart,
|
|
|
|
+ "stop": cmdStop,
|
|
"version": cmdVersion,
|
|
"version": cmdVersion,
|
|
}
|
|
}
|
|
|
|
|
|
@@ -37,24 +48,24 @@ func cmdHelp(args []string) error {
|
|
// {"commit", "Create a new image from a container's changes"},
|
|
// {"commit", "Create a new image from a container's changes"},
|
|
// {"diff", "Inspect changes on a container's filesystem"},
|
|
// {"diff", "Inspect changes on a container's filesystem"},
|
|
// {"export", "Stream the contents of a container as a tar archive"},
|
|
// {"export", "Stream the contents of a container as a tar archive"},
|
|
- // {"history", "Show the history of an image"},
|
|
|
|
|
|
+ {"history", "Show the history of an image"},
|
|
{"images", "List images"},
|
|
{"images", "List images"},
|
|
// {"import", "Create a new filesystem image from the contents of a tarball"},
|
|
// {"import", "Create a new filesystem image from the contents of a tarball"},
|
|
- // {"info", "Display system-wide information"},
|
|
|
|
|
|
+ {"info", "Display system-wide information"},
|
|
// {"inspect", "Return low-level information on a container"},
|
|
// {"inspect", "Return low-level information on a container"},
|
|
{"kill", "Kill a running container"},
|
|
{"kill", "Kill a running container"},
|
|
// {"login", "Register or Login to the docker registry server"},
|
|
// {"login", "Register or Login to the docker registry server"},
|
|
- // {"logs", "Fetch the logs of a container"},
|
|
|
|
|
|
+ {"logs", "Fetch the logs of a container"},
|
|
// {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
|
// {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
|
- // {"ps", "List containers"},
|
|
|
|
|
|
+ {"ps", "List containers"},
|
|
// {"pull", "Pull an image or a repository from the docker registry server"},
|
|
// {"pull", "Pull an image or a repository from the docker registry server"},
|
|
// {"push", "Push an image or a repository to the docker registry server"},
|
|
// {"push", "Push an image or a repository to the docker registry server"},
|
|
- // {"restart", "Restart a running container"},
|
|
|
|
- // {"rm", "Remove a container"},
|
|
|
|
- // {"rmi", "Remove an image"},
|
|
|
|
- // {"run", "Run a command in a new container"},
|
|
|
|
- // {"start", "Start a stopped container"},
|
|
|
|
- // {"stop", "Stop a running container"},
|
|
|
|
|
|
+ {"restart", "Restart a running container"},
|
|
|
|
+ {"rm", "Remove a container"},
|
|
|
|
+ {"rmi", "Remove an image"},
|
|
|
|
+ {"run", "Run a command in a new container"},
|
|
|
|
+ {"start", "Start a stopped container"},
|
|
|
|
+ {"stop", "Stop a running container"},
|
|
// {"tag", "Tag an image into a repository"},
|
|
// {"tag", "Tag an image into a repository"},
|
|
{"version", "Show the docker version information"},
|
|
{"version", "Show the docker version information"},
|
|
// {"wait", "Block until a container stops, then print its exit code"},
|
|
// {"wait", "Block until a container stops, then print its exit code"},
|
|
@@ -66,9 +77,10 @@ func cmdHelp(args []string) error {
|
|
}
|
|
}
|
|
|
|
|
|
func cmdImages(args []string) error {
|
|
func cmdImages(args []string) error {
|
|
- cmd := subcmd("images", "[OPTIONS] [NAME]", "List images")
|
|
|
|
- quiet := cmd.Bool("q", false, "only show numeric IDs")
|
|
|
|
- flAll := cmd.Bool("a", false, "show all images")
|
|
|
|
|
|
+ cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images")
|
|
|
|
+ var in ImagesIn
|
|
|
|
+ cmd.BoolVar(&in.Quiet, "q", false, "only show numeric IDs")
|
|
|
|
+ cmd.BoolVar(&in.All, "a", false, "show all images")
|
|
if err := cmd.Parse(args); err != nil {
|
|
if err := cmd.Parse(args); err != nil {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
@@ -76,13 +88,10 @@ func cmdImages(args []string) error {
|
|
cmd.Usage()
|
|
cmd.Usage()
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
- var nameFilter string
|
|
|
|
if cmd.NArg() == 1 {
|
|
if cmd.NArg() == 1 {
|
|
- nameFilter = cmd.Arg(0)
|
|
|
|
|
|
+ in.NameFilter = cmd.Arg(0)
|
|
}
|
|
}
|
|
|
|
|
|
- in := ImagesIn{nameFilter, *quiet, *flAll}
|
|
|
|
-
|
|
|
|
body, err := apiPost("images", in)
|
|
body, err := apiPost("images", in)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
@@ -94,27 +103,86 @@ func cmdImages(args []string) error {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
|
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
|
- if !*quiet {
|
|
|
|
|
|
+ if !in.Quiet {
|
|
fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED")
|
|
fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED")
|
|
}
|
|
}
|
|
|
|
|
|
for _, out := range outs {
|
|
for _, out := range outs {
|
|
- if !*quiet {
|
|
|
|
|
|
+ if !in.Quiet {
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Repository, out.Tag, out.Id, out.Created)
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Repository, out.Tag, out.Id, out.Created)
|
|
} else {
|
|
} else {
|
|
fmt.Fprintln(w, out.Id)
|
|
fmt.Fprintln(w, out.Id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- if !*quiet {
|
|
|
|
|
|
+ if !in.Quiet {
|
|
w.Flush()
|
|
w.Flush()
|
|
}
|
|
}
|
|
return nil
|
|
return nil
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func cmdInfo(args []string) error {
|
|
|
|
+ cmd := Subcmd("info", "", "Display system-wide information")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() > 0 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiGet("info")
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var out InfoOut
|
|
|
|
+ err = json.Unmarshal(body, &out)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ fmt.Printf("containers: %d\nversion: %s\nimages: %d\n", out.Containers, out.Version, out.Images)
|
|
|
|
+ if out.Debug {
|
|
|
|
+ fmt.Println("debug mode enabled")
|
|
|
|
+ fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdHistory(args []string) error {
|
|
|
|
+ cmd := Subcmd("history", "IMAGE", "Show the history of an image")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() != 1 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPost("history", HistoryIn{cmd.Arg(0)})
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var outs []HistoryOut
|
|
|
|
+ err = json.Unmarshal(body, &outs)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
|
|
|
+ fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
|
|
|
|
+
|
|
|
|
+ for _, out := range outs {
|
|
|
|
+ fmt.Fprintf(w, "%s\t%s\t%s\n", out.Id, out.Created, out.CreatedBy)
|
|
|
|
+ }
|
|
|
|
+ w.Flush()
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
func cmdKill(args []string) error {
|
|
func cmdKill(args []string) error {
|
|
- cmd := subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
|
|
|
|
|
|
+ cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
|
|
if err := cmd.Parse(args); err != nil {
|
|
if err := cmd.Parse(args); err != nil {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
@@ -137,7 +205,247 @@ func cmdKill(args []string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func cmdVersion(_ []string) error {
|
|
|
|
|
|
+func cmdLogs(args []string) error {
|
|
|
|
+ cmd := Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() != 1 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ body, err := apiPost("logs", LogsIn{cmd.Arg(0)})
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var out LogsOut
|
|
|
|
+ err = json.Unmarshal(body, &out)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ fmt.Fprintln(os.Stdout, out.Stdout)
|
|
|
|
+ fmt.Fprintln(os.Stderr, out.Stderr)
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdPs(args []string) error {
|
|
|
|
+ cmd := Subcmd("ps", "[OPTIONS]", "List containers")
|
|
|
|
+ var in PsIn
|
|
|
|
+ cmd.BoolVar(&in.Quiet, "q", false, "Only display numeric IDs")
|
|
|
|
+ cmd.BoolVar(&in.All, "a", false, "Show all containers. Only running containers are shown by default.")
|
|
|
|
+ cmd.BoolVar(&in.Full, "notrunc", false, "Don't truncate output")
|
|
|
|
+ nLatest := cmd.Bool("l", false, "Show only the latest created container, include non-running ones.")
|
|
|
|
+ cmd.IntVar(&in.Last, "n", -1, "Show n last created containers, include non-running ones.")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if in.Last == -1 && *nLatest {
|
|
|
|
+ in.Last = 1
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPost("ps", in)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var outs []PsOut
|
|
|
|
+ err = json.Unmarshal(body, &outs)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
|
|
|
|
+ if !in.Quiet {
|
|
|
|
+ fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for _, out := range outs {
|
|
|
|
+ if !in.Quiet {
|
|
|
|
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Id, out.Image, out.Command, out.Status, out.Created)
|
|
|
|
+ } else {
|
|
|
|
+ fmt.Fprintln(w, out.Id)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if !in.Quiet {
|
|
|
|
+ w.Flush()
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdRestart(args []string) error {
|
|
|
|
+ cmd := Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPost("restart", cmd.Args())
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var out []string
|
|
|
|
+ err = json.Unmarshal(body, &out)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ for _, name := range out {
|
|
|
|
+ fmt.Println(name)
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdRm(args []string) error {
|
|
|
|
+ cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPost("rm", cmd.Args())
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var out []string
|
|
|
|
+ err = json.Unmarshal(body, &out)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ for _, name := range out {
|
|
|
|
+ fmt.Println(name)
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdRmi(args []string) error {
|
|
|
|
+ cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPost("rmi", cmd.Args())
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var out []string
|
|
|
|
+ err = json.Unmarshal(body, &out)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ for _, name := range out {
|
|
|
|
+ fmt.Println(name)
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdRun(args []string) error {
|
|
|
|
+ config, err := ParseRun(args)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ if config.Image == "" {
|
|
|
|
+ fmt.Println("Error: Image not specified")
|
|
|
|
+ return fmt.Errorf("Image not specified")
|
|
|
|
+ }
|
|
|
|
+ if len(config.Cmd) == 0 {
|
|
|
|
+ fmt.Println("Error: Command not specified")
|
|
|
|
+ return fmt.Errorf("Command not specified")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPostHijack("run", config)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ defer body.Close()
|
|
|
|
+
|
|
|
|
+ /*str, err2 := ioutil.ReadAll(body)
|
|
|
|
+ if err2 != nil {
|
|
|
|
+ return err2
|
|
|
|
+ }
|
|
|
|
+ fmt.Println(str)*/
|
|
|
|
+ return nil
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdStart(args []string) error {
|
|
|
|
+ cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPost("start", cmd.Args())
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var out []string
|
|
|
|
+ err = json.Unmarshal(body, &out)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ for _, name := range out {
|
|
|
|
+ fmt.Println(name)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdStop(args []string) error {
|
|
|
|
+ cmd := Subcmd("stop", "CONTAINER [CONTAINER...]", "Restart a running container")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ body, err := apiPost("stop", cmd.Args())
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var out []string
|
|
|
|
+ err = json.Unmarshal(body, &out)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ for _, name := range out {
|
|
|
|
+ fmt.Println(name)
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func cmdVersion(args []string) error {
|
|
|
|
+ cmd := Subcmd("version", "", "Show the docker version information.")
|
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ if cmd.NArg() > 0 {
|
|
|
|
+ cmd.Usage()
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
body, err := apiGet("version")
|
|
body, err := apiGet("version")
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
@@ -162,12 +470,14 @@ func apiGet(path string) ([]byte, error) {
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
- //TODO check status code
|
|
|
|
defer resp.Body.Close()
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
+ if resp.StatusCode != 200 {
|
|
|
|
+ return nil, fmt.Errorf("error: %s", body)
|
|
|
|
+ }
|
|
return body, nil
|
|
return body, nil
|
|
|
|
|
|
}
|
|
}
|
|
@@ -182,17 +492,33 @@ func apiPost(path string, data interface{}) ([]byte, error) {
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
- //TODO check status code
|
|
|
|
defer resp.Body.Close()
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
+ if resp.StatusCode != 200 {
|
|
|
|
+ return nil, fmt.Errorf("[error] %s", body)
|
|
|
|
+ }
|
|
return body, nil
|
|
return body, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func apiPostHijack(path string, data interface{}) (io.ReadCloser, error) {
|
|
|
|
+ buf, err := json.Marshal(data)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ dataBuf := bytes.NewBuffer(buf)
|
|
|
|
+ resp, err := http.Post("http://0.0.0.0:4243/"+path, "application/json", dataBuf)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ //TODO check status code
|
|
|
|
|
|
|
|
+ return resp.Body, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func subcmd(name, signature, description string) *flag.FlagSet {
|
|
|
|
|
|
+func Subcmd(name, signature, description string) *flag.FlagSet {
|
|
flags := flag.NewFlagSet(name, flag.ContinueOnError)
|
|
flags := flag.NewFlagSet(name, flag.ContinueOnError)
|
|
flags.Usage = func() {
|
|
flags.Usage = func() {
|
|
fmt.Printf("\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
|
|
fmt.Printf("\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
|