From 8e0b54109ad2f1a6c4d4972675d523240038bf44 Mon Sep 17 00:00:00 2001 From: shin- Date: Tue, 14 May 2013 11:34:26 -0700 Subject: [PATCH] WIP --- client.py | 795 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 795 insertions(+) create mode 100644 client.py diff --git a/client.py b/client.py new file mode 100644 index 00000000..e68ffbd2 --- /dev/null +++ b/client.py @@ -0,0 +1,795 @@ +import requests + +class Client(requests.Session): + def __init__(self, base_url="http://localhost"): + self.base_url = base_url + + def _url(self, path): + return self.base_url + path + + def _result(self, response): + # FIXME + return response + + def build(self, dockerfile): + url = self._url("/build") + return self._result(self.post(url, dockerfile)) + + def history(self, image): + return self._result(self.get(self._url("/images/{0}/history".format(image)))) + + def info(self): + return self._result(self.get(self._url("/info"))) + + def insert(self, image, url, path): + url = self._url("/images/" + image) + params = { + 'url': url, + 'path': path + } + return self._result(self.post(url, None, params=params)) + + def inspect_container(self, container_id): + return self._result(self.get(self._url("/containers/{0}/json".format(container_id)))) + + def inspect_image(self, image_id): + return self._result(self.get(self._url("/images/{0}/json".format(image_id)))) + + def kill(self, *args): + for name in args: + url = self._url("/containers/{0}/kill".format(name)) + self.post(url, None) + + def login(self, username, password=None, email=None): + url = self._url("/auth") + res = self.get(url) + json = res.json() + if 'username' in json and json['username'] == username: + return json + req_data = { + 'username': username, + 'password': password if password is not None else json['password'], + 'email': email if email is not None else json['email'] + } + return self._result(self.post(url, req_data)) + + def port(self, container, private_port): + res = self.get(self._url("/containers/{0}/json".format(container))) + json = res.json() + return json['NetworkSettings']['PortMapping'][private_port] + + def remove_container(self, *args, **kwargs): + params = { + 'v': kwargs.get('v', False) + } + for container in args: + self.delete(self._url("/containers/" + container), params=params) + + def remove_image(self, *args): + for image in args: + self.delete(self._url("/images/" + image)) + + def restart(self, *args, **kwargs): + params = { + 't': kwargs.get('timeout', 10) + } + for name in args: + url = self._url("/containers/{0}/restart".format(name)) + self.post(url, None, params=params) + + def start(self, *args): + for name in args: + url = self._url("/containers/{0}/start".format(name)) + self.post(url, None) + + def stop(self, *args, **kwargs): + params = { + 't': kwargs.get('timeout', 10) + } + for name in args: + url = self._url("/containers/{0}/stop".format(name)) + self.post(url, None, params=params) + + def version(self): + return self._result(self.get(self._url("/version"))) + + def wait(self, *args): + result = [] + for name in args: + url = self._url("/containers/{0}/wait".format(name)) + res = self.post(url, None, timeout=None) + json = res.json() + if 'StatusCode' in json: + result.append(json['StatusCode']) + return result + + +# func CmdImport(args ...string) error { +# cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball") + +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# if cmd.NArg() < 1 { +# cmd.Usage() +# return nil +# } +# src, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) +# v := url.Values{} +# v.Set("repo", repository) +# v.Set("tag", tag) +# v.Set("fromSrc", src) + +# err := hijack("POST", "/images/create?"+v.Encode(), false) +# if err != nil { +# return err +# } +# return nil +# } + +# func CmdPush(args ...string) error { +# cmd := Subcmd("push", "[OPTION] NAME", "Push an image or a repository to the registry") +# registry := cmd.String("registry", "", "Registry host to push the image to") +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# name := cmd.Arg(0) + +# if name == "" { +# cmd.Usage() +# return nil +# } + +# body, _, err := call("GET", "/auth", nil) +# if err != nil { +# return err +# } + +# var out auth.AuthConfig +# err = json.Unmarshal(body, &out) +# if err != nil { +# return err +# } + +# // If the login failed AND we're using the index, abort +# if *registry == "" && out.Username == "" { +# if err := CmdLogin(args...); err != nil { +# return err +# } + +# body, _, err = call("GET", "/auth", nil) +# if err != nil { +# return err +# } +# err = json.Unmarshal(body, &out) +# if err != nil { +# return err +# } + +# if out.Username == "" { +# return fmt.Errorf("Please login prior to push. ('docker login')") +# } +# } + +# if len(strings.SplitN(name, "/", 2)) == 1 { +# return fmt.Errorf("Impossible to push a \"root\" repository. Please rename your repository in / (ex: %s/%s)", out.Username, name) +# } + +# v := url.Values{} +# v.Set("registry", *registry) +# if err := hijack("POST", "/images/"+name+"/push?"+v.Encode(), false); err != nil { +# return err +# } +# return nil +# } + +# func CmdPull(args ...string) error { +# cmd := Subcmd("pull", "NAME", "Pull an image or a repository from the registry") +# tag := cmd.String("t", "", "Download tagged image in repository") +# registry := cmd.String("registry", "", "Registry to download from. Necessary if image is pulled by ID") +# if err := cmd.Parse(args); err != nil { +# return nil +# } + +# if cmd.NArg() != 1 { +# cmd.Usage() +# return nil +# } + +# remote := cmd.Arg(0) +# if strings.Contains(remote, ":") { +# remoteParts := strings.Split(remote, ":") +# tag = &remoteParts[1] +# remote = remoteParts[0] +# } + +# v := url.Values{} +# v.Set("fromImage", remote) +# v.Set("tag", *tag) +# v.Set("registry", *registry) + +# if err := hijack("POST", "/images/create?"+v.Encode(), false); err != nil { +# return err +# } + +# return nil +# } + +# func CmdImages(args ...string) error { +# cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images") +# quiet := cmd.Bool("q", false, "only show numeric IDs") +# all := cmd.Bool("a", false, "show all images") +# flViz := cmd.Bool("viz", false, "output graph in graphviz format") + +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# if cmd.NArg() > 1 { +# cmd.Usage() +# return nil +# } + +# if *flViz { +# body, _, err := call("GET", "/images/viz", false) +# if err != nil { +# return err +# } +# fmt.Printf("%s", body) +# } else { +# v := url.Values{} +# if cmd.NArg() == 1 { +# v.Set("filter", cmd.Arg(0)) +# } +# if *quiet { +# v.Set("only_ids", "1") +# } +# if *all { +# v.Set("all", "1") +# } + +# body, _, err := call("GET", "/images/json?"+v.Encode(), nil) +# if err != nil { +# return err +# } + +# var outs []ApiImages +# err = json.Unmarshal(body, &outs) +# if err != nil { +# return err +# } + +# w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) +# if !*quiet { +# fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED") +# } + +# for _, out := range outs { +# if !*quiet { +# fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\n", out.Repository, out.Tag, out.Id, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) +# } else { +# fmt.Fprintln(w, out.Id) +# } +# } + +# if !*quiet { +# w.Flush() +# } +# } +# return nil +# } + +# func CmdPs(args ...string) error { +# cmd := Subcmd("ps", "[OPTIONS]", "List containers") +# quiet := cmd.Bool("q", false, "Only display numeric IDs") +# all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.") +# noTrunc := cmd.Bool("notrunc", false, "Don't truncate output") +# nLatest := cmd.Bool("l", false, "Show only the latest created container, include non-running ones.") +# since := cmd.String("sinceId", "", "Show only containers created since Id, include non-running ones.") +# before := cmd.String("beforeId", "", "Show only container created before Id, include non-running ones.") +# last := cmd.Int("n", -1, "Show n last created containers, include non-running ones.") + +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# v := url.Values{} +# if *last == -1 && *nLatest { +# *last = 1 +# } +# if *quiet { +# v.Set("only_ids", "1") +# } +# if *all { +# v.Set("all", "1") +# } +# if *noTrunc { +# v.Set("trunc_cmd", "0") +# } +# if *last != -1 { +# v.Set("limit", strconv.Itoa(*last)) +# } +# if *since != "" { +# v.Set("since", *since) +# } +# if *before != "" { +# v.Set("before", *before) +# } + +# body, _, err := call("GET", "/containers/ps?"+v.Encode(), nil) +# if err != nil { +# return err +# } + +# var outs []ApiContainers +# err = json.Unmarshal(body, &outs) +# if err != nil { +# return err +# } +# w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) +# if !*quiet { +# fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS") +# } + +# for _, out := range outs { +# if !*quiet { +# fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", out.Id, out.Image, out.Command, out.Status, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) +# } else { +# fmt.Fprintln(w, out.Id) +# } +# } + +# if !*quiet { +# w.Flush() +# } +# return nil +# } + +# func CmdCommit(args ...string) error { +# cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes") +# flComment := cmd.String("m", "", "Commit message") +# flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith \"") +# flConfig := cmd.String("run", "", "Config automatically applied when the image is run. "+`(ex: {"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`) +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# name, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) +# if name == "" { +# cmd.Usage() +# return nil +# } + +# v := url.Values{} +# v.Set("container", name) +# v.Set("repo", repository) +# v.Set("tag", tag) +# v.Set("comment", *flComment) +# v.Set("author", *flAuthor) +# var config *Config +# if *flConfig != "" { +# config = &Config{} +# if err := json.Unmarshal([]byte(*flConfig), config); err != nil { +# return err +# } +# } +# body, _, err := call("POST", "/commit?"+v.Encode(), config) +# if err != nil { +# return err +# } + +# apiId := &ApiId{} +# err = json.Unmarshal(body, apiId) +# if err != nil { +# return err +# } + +# fmt.Println(apiId.Id) +# return nil +# } + +# func CmdExport(args ...string) error { +# cmd := Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive") +# if err := cmd.Parse(args); err != nil { +# return nil +# } + +# if cmd.NArg() != 1 { +# cmd.Usage() +# return nil +# } + +# if err := stream("GET", "/containers/"+cmd.Arg(0)+"/export"); err != nil { +# return err +# } +# return nil +# } + +# func CmdDiff(args ...string) error { +# cmd := Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem") +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# if cmd.NArg() != 1 { +# cmd.Usage() +# return nil +# } + +# body, _, err := call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil) +# if err != nil { +# return err +# } + +# changes := []Change{} +# err = json.Unmarshal(body, &changes) +# if err != nil { +# return err +# } +# for _, change := range changes { +# fmt.Println(change.String()) +# } +# 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 +# } + +# v := url.Values{} +# v.Set("logs", "1") +# v.Set("stdout", "1") +# v.Set("stderr", "1") + +# if err := hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), false); err != nil { +# return err +# } +# return nil +# } + +# func CmdAttach(args ...string) error { +# cmd := Subcmd("attach", "CONTAINER", "Attach to a running container") +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# if cmd.NArg() != 1 { +# cmd.Usage() +# return nil +# } + +# body, _, err := call("GET", "/containers/"+cmd.Arg(0)+"/json", nil) +# if err != nil { +# return err +# } + +# container := &Container{} +# err = json.Unmarshal(body, container) +# if err != nil { +# return err +# } + +# v := url.Values{} +# v.Set("stream", "1") +# v.Set("stdout", "1") +# v.Set("stderr", "1") +# v.Set("stdin", "1") + +# if err := hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty); err != nil { +# return err +# } +# return nil +# } + +# func CmdSearch(args ...string) error { +# cmd := Subcmd("search", "NAME", "Search the docker index for images") +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# if cmd.NArg() != 1 { +# cmd.Usage() +# return nil +# } + +# v := url.Values{} +# v.Set("term", cmd.Arg(0)) +# body, _, err := call("GET", "/images/search?"+v.Encode(), nil) +# if err != nil { +# return err +# } + +# outs := []ApiSearch{} +# err = json.Unmarshal(body, &outs) +# if err != nil { +# return err +# } +# fmt.Printf("Found %d results matching your query (\"%s\")\n", len(outs), cmd.Arg(0)) +# w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) +# fmt.Fprintf(w, "NAME\tDESCRIPTION\n") +# for _, out := range outs { +# fmt.Fprintf(w, "%s\t%s\n", out.Name, out.Description) +# } +# w.Flush() +# return nil +# } + +# // Ports type - Used to parse multiple -p flags +# type ports []int + +# // ListOpts type +# type ListOpts []string + +# func (opts *ListOpts) String() string { +# return fmt.Sprint(*opts) +# } + +# func (opts *ListOpts) Set(value string) error { +# *opts = append(*opts, value) +# return nil +# } + +# // AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to +# type AttachOpts map[string]bool + +# func NewAttachOpts() AttachOpts { +# return make(AttachOpts) +# } + +# func (opts AttachOpts) String() string { +# // Cast to underlying map type to avoid infinite recursion +# return fmt.Sprintf("%v", map[string]bool(opts)) +# } + +# func (opts AttachOpts) Set(val string) error { +# if val != "stdin" && val != "stdout" && val != "stderr" { +# return fmt.Errorf("Unsupported stream name: %s", val) +# } +# opts[val] = true +# return nil +# } + +# func (opts AttachOpts) Get(val string) bool { +# if res, exists := opts[val]; exists { +# return res +# } +# return false +# } + +# // PathOpts stores a unique set of absolute paths +# type PathOpts map[string]struct{} + +# func NewPathOpts() PathOpts { +# return make(PathOpts) +# } + +# func (opts PathOpts) String() string { +# return fmt.Sprintf("%v", map[string]struct{}(opts)) +# } + +# func (opts PathOpts) Set(val string) error { +# if !filepath.IsAbs(val) { +# return fmt.Errorf("%s is not an absolute path", val) +# } +# opts[filepath.Clean(val)] = struct{}{} +# return nil +# } + +# func CmdTag(args ...string) error { +# cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository") +# force := cmd.Bool("f", false, "Force") +# if err := cmd.Parse(args); err != nil { +# return nil +# } +# if cmd.NArg() != 2 && cmd.NArg() != 3 { +# cmd.Usage() +# return nil +# } + +# v := url.Values{} +# v.Set("repo", cmd.Arg(1)) +# if cmd.NArg() == 3 { +# v.Set("tag", cmd.Arg(2)) +# } + +# if *force { +# v.Set("force", "1") +# } + +# if _, _, err := call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil); err != nil { +# return err +# } +# return nil +# } + +# func CmdRun(args ...string) error { +# config, cmd, err := ParseRun(args, nil) +# if err != nil { +# return err +# } +# if config.Image == "" { +# cmd.Usage() +# return nil +# } + +# //create the container +# body, statusCode, err := call("POST", "/containers/create", config) +# //if image not found try to pull it +# if statusCode == 404 { +# v := url.Values{} +# v.Set("fromImage", config.Image) +# err = hijack("POST", "/images?"+v.Encode(), false) +# if err != nil { +# return err +# } +# body, _, err = call("POST", "/containers/create", config) +# if err != nil { +# return err +# } +# } +# if err != nil { +# return err +# } + +# out := &ApiRun{} +# err = json.Unmarshal(body, out) +# if err != nil { +# return err +# } + +# for _, warning := range out.Warnings { +# fmt.Fprintln(os.Stderr, "WARNING: ", warning) +# } + +# v := url.Values{} +# v.Set("logs", "1") +# v.Set("stream", "1") + +# if config.AttachStdin { +# v.Set("stdin", "1") +# } +# if config.AttachStdout { +# v.Set("stdout", "1") +# } +# if config.AttachStderr { +# v.Set("stderr", "1") + +# } + +# //start the container +# _, _, err = call("POST", "/containers/"+out.Id+"/start", nil) +# if err != nil { +# return err +# } + +# if config.AttachStdin || config.AttachStdout || config.AttachStderr { +# if err := hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil { +# return err +# } +# } +# if !config.AttachStdout && !config.AttachStderr { +# fmt.Println(out.Id) +# } +# return nil +# } + +# func call(method, path string, data interface{}) ([]byte, int, error) { +# var params io.Reader +# if data != nil { +# buf, err := json.Marshal(data) +# if err != nil { +# return nil, -1, err +# } +# params = bytes.NewBuffer(buf) +# } + +# req, err := http.NewRequest(method, "http://0.0.0.0:4243"+path, params) +# if err != nil { +# return nil, -1, err +# } +# req.Header.Set("User-Agent", "Docker-Client/"+VERSION) +# if data != nil { +# req.Header.Set("Content-Type", "application/json") +# } else if method == "POST" { +# req.Header.Set("Content-Type", "plain/text") +# } +# resp, err := http.DefaultClient.Do(req) +# if err != nil { +# if strings.Contains(err.Error(), "connection refused") { +# return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?") +# } +# return nil, -1, err +# } +# defer resp.Body.Close() +# body, err := ioutil.ReadAll(resp.Body) +# if err != nil { +# return nil, -1, err +# } +# if resp.StatusCode < 200 || resp.StatusCode >= 400 { +# return nil, resp.StatusCode, fmt.Errorf("error: %s", body) +# } +# return body, resp.StatusCode, nil +# } + +# func stream(method, path string) error { +# req, err := http.NewRequest(method, "http://0.0.0.0:4243"+path, nil) +# if err != nil { +# return err +# } +# req.Header.Set("User-Agent", "Docker-Client/"+VERSION) +# if method == "POST" { +# req.Header.Set("Content-Type", "plain/text") +# } +# resp, err := http.DefaultClient.Do(req) +# if err != nil { +# if strings.Contains(err.Error(), "connection refused") { +# return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?") +# } +# return err +# } +# defer resp.Body.Close() +# if _, err := io.Copy(os.Stdout, resp.Body); err != nil { +# return err +# } +# return nil +# } + +# func hijack(method, path string, setRawTerminal bool) error { +# req, err := http.NewRequest(method, path, nil) +# if err != nil { +# return err +# } +# req.Header.Set("Content-Type", "plain/text") +# dial, err := net.Dial("tcp", "0.0.0.0:4243") +# if err != nil { +# return err +# } +# clientconn := httputil.NewClientConn(dial, nil) +# clientconn.Do(req) +# defer clientconn.Close() + +# rwc, br := clientconn.Hijack() +# defer rwc.Close() + +# receiveStdout := Go(func() error { +# _, err := io.Copy(os.Stdout, br) +# return err +# }) + +# if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { +# if oldState, err := SetRawTerminal(); err != nil { +# return err +# } else { +# defer RestoreTerminal(oldState) +# } +# } + +# sendStdin := Go(func() error { +# _, err := io.Copy(rwc, os.Stdin) +# if err := rwc.(*net.TCPConn).CloseWrite(); err != nil { +# fmt.Fprintf(os.Stderr, "Couldn't send EOF: %s\n", err) +# } +# return err +# }) + +# if err := <-receiveStdout; err != nil { +# return err +# } + +# if !term.IsTerminal(int(os.Stdin.Fd())) { +# if err := <-sendStdin; err != nil { +# return err +# } +# } +# return 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 +# }