diff --git a/commands.go b/commands.go index 6f3240448c..1bbbc983fc 100644 --- a/commands.go +++ b/commands.go @@ -56,6 +56,7 @@ func (srv *Server) Help() string { {"rm", "Remove a container"}, {"rmi", "Remove an image"}, {"run", "Run a command in a new container"}, + {"search", "Search for an image in the docker index"} {"start", "Start a stopped container"}, {"stop", "Stop a running container"}, {"tag", "Tag an image into a repository"}, @@ -1001,6 +1002,34 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout rcli.DockerConn, args . return <-container.Attach(stdin, nil, stdout, stdout) } +func (srv *Server) CmdSearch(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error { + cmd := rcli.Subcmd(stdout, "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 + } + term := cmd.Arg(0) + results, err := srv.runtime.graph.SearchRepositories(stdout, term) + if err != nil { + return err + } + fmt.Fprintf(stdout, "Found %d results matching your query (\"%s\")\n", results.NumResults, results.Query) + w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0) + fmt.Fprintf(w, "NAME\tDESCRIPTION\n") + for _, repo := range results.Results { + description := repo["description"] + if len(description) > 45 { + description = Trunc(description, 42) + "..." + } + fmt.Fprintf(w, "%s\t%s\n", repo["name"], description) + } + w.Flush() + return nil +} + // Ports type - Used to parse multiple -p flags type ports []int diff --git a/registry.go b/registry.go index f9bc756643..cc76485c09 100644 --- a/registry.go +++ b/registry.go @@ -9,6 +9,7 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "path" "strings" ) @@ -638,3 +639,33 @@ func (graph *Graph) Checksums(output io.Writer, repo Repository) ([]map[string]s } return result, nil } + +type SearchResults struct { + Query string `json:"query"` + NumResults int `json:"num_results"` + Results []map[string]string `json:"results"` +} + +func (graph *Graph) SearchRepositories(stdout io.Writer, term string) (*SearchResults, error) { + client := graph.getHttpClient() + u := INDEX_ENDPOINT + "/search?q=" + url.QueryEscape(term) + req, err := http.NewRequest("GET", u, nil) + if err != nil { + return nil, err + } + res, err := client.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + if res.StatusCode != 200 { + return nil, fmt.Errorf("Unexepected status code %d", res.StatusCode) + } + rawData, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + result := new(SearchResults) + err = json.Unmarshal(rawData, result) + return result, err +} diff --git a/utils.go b/utils.go index 095be2f4bf..2d54ef4c6a 100644 --- a/utils.go +++ b/utils.go @@ -404,7 +404,6 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) return written, err } - func HashData(src io.Reader) (string, error) { h := sha256.New() if _, err := io.Copy(h, src); err != nil {