mirror of https://github.com/docker/docs.git
529 lines
11 KiB
Go
529 lines
11 KiB
Go
package docker
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"text/tabwriter"
|
|
)
|
|
|
|
func ParseCommands(args []string) error {
|
|
|
|
cmds := map[string]func(args []string) error{
|
|
"images": cmdImages,
|
|
"info": cmdInfo,
|
|
"history": cmdHistory,
|
|
"kill": cmdKill,
|
|
"logs": cmdLogs,
|
|
"ps": cmdPs,
|
|
"restart": cmdRestart,
|
|
"rm": cmdRm,
|
|
"rmi": cmdRmi,
|
|
"run": cmdRun,
|
|
"start": cmdStart,
|
|
"stop": cmdStop,
|
|
"version": cmdVersion,
|
|
}
|
|
|
|
if len(args) > 0 {
|
|
cmd, exists := cmds[args[0]]
|
|
if !exists {
|
|
//TODO display commend not found
|
|
return cmdHelp(args)
|
|
}
|
|
return cmd(args[1:])
|
|
}
|
|
return cmdHelp(args)
|
|
}
|
|
|
|
func cmdHelp(args []string) error {
|
|
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
|
|
for _, cmd := range [][]string{
|
|
// {"attach", "Attach to a running container"},
|
|
// {"commit", "Create a new image from a container's changes"},
|
|
// {"diff", "Inspect changes on a container's filesystem"},
|
|
// {"export", "Stream the contents of a container as a tar archive"},
|
|
{"history", "Show the history of an image"},
|
|
{"images", "List images"},
|
|
// {"import", "Create a new filesystem image from the contents of a tarball"},
|
|
{"info", "Display system-wide information"},
|
|
// {"inspect", "Return low-level information on a container"},
|
|
{"kill", "Kill a running container"},
|
|
// {"login", "Register or Login to the docker registry server"},
|
|
{"logs", "Fetch the logs of a container"},
|
|
// {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
|
{"ps", "List containers"},
|
|
// {"pull", "Pull an image or a repository from 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"},
|
|
// {"tag", "Tag an image into a repository"},
|
|
{"version", "Show the docker version information"},
|
|
// {"wait", "Block until a container stops, then print its exit code"},
|
|
} {
|
|
help += fmt.Sprintf(" %-10.10s%s\n", cmd[0], cmd[1])
|
|
}
|
|
fmt.Println(help)
|
|
return nil
|
|
}
|
|
|
|
func cmdImages(args []string) error {
|
|
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 {
|
|
return nil
|
|
}
|
|
if cmd.NArg() > 1 {
|
|
cmd.Usage()
|
|
return nil
|
|
}
|
|
if cmd.NArg() == 1 {
|
|
in.NameFilter = cmd.Arg(0)
|
|
}
|
|
|
|
body, err := apiPost("images", in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var outs []ImagesOut
|
|
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, "REPOSITORY\tTAG\tID\tCREATED")
|
|
}
|
|
|
|
for _, out := range outs {
|
|
if !in.Quiet {
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Repository, out.Tag, out.Id, out.Created)
|
|
} else {
|
|
fmt.Fprintln(w, out.Id)
|
|
}
|
|
}
|
|
|
|
if !in.Quiet {
|
|
w.Flush()
|
|
}
|
|
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 {
|
|
cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
|
|
if err := cmd.Parse(args); err != nil {
|
|
return nil
|
|
}
|
|
if cmd.NArg() < 1 {
|
|
cmd.Usage()
|
|
return nil
|
|
}
|
|
|
|
body, err := apiPost("kill", args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var out SimpleMessage
|
|
err = json.Unmarshal(body, &out)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Print(out.Message)
|
|
return nil
|
|
}
|
|
|
|
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")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var out VersionOut
|
|
err = json.Unmarshal(body, &out)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println("Version:", out.Version)
|
|
fmt.Println("Git Commit:", out.GitCommit)
|
|
if out.MemoryLimitDisabled {
|
|
fmt.Println("Memory limit disabled")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func apiGet(path string) ([]byte, error) {
|
|
resp, err := http.Get("http://0.0.0.0:4243/" + path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("error: %s", body)
|
|
}
|
|
return body, nil
|
|
|
|
}
|
|
|
|
func apiPost(path string, data interface{}) ([]byte, 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
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("[error] %s", body)
|
|
}
|
|
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 {
|
|
flags := flag.NewFlagSet(name, flag.ContinueOnError)
|
|
flags.Usage = func() {
|
|
fmt.Printf("\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)
|
|
flags.PrintDefaults()
|
|
}
|
|
return flags
|
|
}
|