And move it back to the top-level command.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -33,6 +33,7 @@ func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
|
| 33 | 33 |
system.NewEventsCommand(dockerCli), |
| 34 | 34 |
registry.NewLoginCommand(dockerCli), |
| 35 | 35 |
registry.NewLogoutCommand(dockerCli), |
| 36 |
+ registry.NewSearchCommand(dockerCli), |
|
| 36 | 37 |
system.NewVersionCommand(dockerCli), |
| 37 | 38 |
volume.NewVolumeCommand(dockerCli), |
| 38 | 39 |
system.NewInfoCommand(dockerCli), |
| ... | ... |
@@ -66,7 +67,6 @@ func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
|
| 66 | 66 |
hide(image.NewPushCommand(dockerCli)), |
| 67 | 67 |
hide(image.NewRemoveCommand(dockerCli)), |
| 68 | 68 |
hide(image.NewSaveCommand(dockerCli)), |
| 69 |
- hide(image.NewSearchCommand(dockerCli)), |
|
| 70 | 69 |
hide(image.NewTagCommand(dockerCli)), |
| 71 | 70 |
hide(system.NewInspectCommand(dockerCli)), |
| 72 | 71 |
) |
| ... | ... |
@@ -27,7 +27,6 @@ func NewImageCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 27 | 27 |
NewPullCommand(dockerCli), |
| 28 | 28 |
NewPushCommand(dockerCli), |
| 29 | 29 |
NewSaveCommand(dockerCli), |
| 30 |
- NewSearchCommand(dockerCli), |
|
| 31 | 30 |
NewTagCommand(dockerCli), |
| 32 | 31 |
newListCommand(dockerCli), |
| 33 | 32 |
newRemoveCommand(dockerCli), |
| 34 | 33 |
deleted file mode 100644 |
| ... | ... |
@@ -1,126 +0,0 @@ |
| 1 |
-package image |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "fmt" |
|
| 5 |
- "sort" |
|
| 6 |
- "strings" |
|
| 7 |
- "text/tabwriter" |
|
| 8 |
- |
|
| 9 |
- "golang.org/x/net/context" |
|
| 10 |
- |
|
| 11 |
- "github.com/docker/docker/api/types" |
|
| 12 |
- registrytypes "github.com/docker/docker/api/types/registry" |
|
| 13 |
- "github.com/docker/docker/cli" |
|
| 14 |
- "github.com/docker/docker/cli/command" |
|
| 15 |
- "github.com/docker/docker/opts" |
|
| 16 |
- "github.com/docker/docker/pkg/stringutils" |
|
| 17 |
- "github.com/docker/docker/registry" |
|
| 18 |
- "github.com/spf13/cobra" |
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-type searchOptions struct {
|
|
| 22 |
- term string |
|
| 23 |
- noTrunc bool |
|
| 24 |
- limit int |
|
| 25 |
- filter opts.FilterOpt |
|
| 26 |
- |
|
| 27 |
- // Deprecated |
|
| 28 |
- stars uint |
|
| 29 |
- automated bool |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-// NewSearchCommand creates a new `docker search` command |
|
| 33 |
-func NewSearchCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|
| 34 |
- opts := searchOptions{filter: opts.NewFilterOpt()}
|
|
| 35 |
- |
|
| 36 |
- cmd := &cobra.Command{
|
|
| 37 |
- Use: "search [OPTIONS] TERM", |
|
| 38 |
- Short: "Search the Docker Hub for images", |
|
| 39 |
- Args: cli.ExactArgs(1), |
|
| 40 |
- RunE: func(cmd *cobra.Command, args []string) error {
|
|
| 41 |
- opts.term = args[0] |
|
| 42 |
- return runSearch(dockerCli, opts) |
|
| 43 |
- }, |
|
| 44 |
- } |
|
| 45 |
- |
|
| 46 |
- flags := cmd.Flags() |
|
| 47 |
- |
|
| 48 |
- flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output") |
|
| 49 |
- flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided") |
|
| 50 |
- flags.IntVar(&opts.limit, "limit", registry.DefaultSearchLimit, "Max number of search results") |
|
| 51 |
- |
|
| 52 |
- flags.BoolVar(&opts.automated, "automated", false, "Only show automated builds") |
|
| 53 |
- flags.UintVarP(&opts.stars, "stars", "s", 0, "Only displays with at least x stars") |
|
| 54 |
- |
|
| 55 |
- flags.MarkDeprecated("automated", "use --filter=automated=true instead")
|
|
| 56 |
- flags.MarkDeprecated("stars", "use --filter=stars=3 instead")
|
|
| 57 |
- |
|
| 58 |
- return cmd |
|
| 59 |
-} |
|
| 60 |
- |
|
| 61 |
-func runSearch(dockerCli *command.DockerCli, opts searchOptions) error {
|
|
| 62 |
- indexInfo, err := registry.ParseSearchIndexInfo(opts.term) |
|
| 63 |
- if err != nil {
|
|
| 64 |
- return err |
|
| 65 |
- } |
|
| 66 |
- |
|
| 67 |
- ctx := context.Background() |
|
| 68 |
- |
|
| 69 |
- authConfig := command.ResolveAuthConfig(ctx, dockerCli, indexInfo) |
|
| 70 |
- requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search") |
|
| 71 |
- |
|
| 72 |
- encodedAuth, err := command.EncodeAuthToBase64(authConfig) |
|
| 73 |
- if err != nil {
|
|
| 74 |
- return err |
|
| 75 |
- } |
|
| 76 |
- |
|
| 77 |
- options := types.ImageSearchOptions{
|
|
| 78 |
- RegistryAuth: encodedAuth, |
|
| 79 |
- PrivilegeFunc: requestPrivilege, |
|
| 80 |
- Filters: opts.filter.Value(), |
|
| 81 |
- Limit: opts.limit, |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- clnt := dockerCli.Client() |
|
| 85 |
- |
|
| 86 |
- unorderedResults, err := clnt.ImageSearch(ctx, opts.term, options) |
|
| 87 |
- if err != nil {
|
|
| 88 |
- return err |
|
| 89 |
- } |
|
| 90 |
- |
|
| 91 |
- results := searchResultsByStars(unorderedResults) |
|
| 92 |
- sort.Sort(results) |
|
| 93 |
- |
|
| 94 |
- w := tabwriter.NewWriter(dockerCli.Out(), 10, 1, 3, ' ', 0) |
|
| 95 |
- fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n") |
|
| 96 |
- for _, res := range results {
|
|
| 97 |
- // --automated and -s, --stars are deprecated since Docker 1.12 |
|
| 98 |
- if (opts.automated && !res.IsAutomated) || (int(opts.stars) > res.StarCount) {
|
|
| 99 |
- continue |
|
| 100 |
- } |
|
| 101 |
- desc := strings.Replace(res.Description, "\n", " ", -1) |
|
| 102 |
- desc = strings.Replace(desc, "\r", " ", -1) |
|
| 103 |
- if !opts.noTrunc {
|
|
| 104 |
- desc = stringutils.Ellipsis(desc, 45) |
|
| 105 |
- } |
|
| 106 |
- fmt.Fprintf(w, "%s\t%s\t%d\t", res.Name, desc, res.StarCount) |
|
| 107 |
- if res.IsOfficial {
|
|
| 108 |
- fmt.Fprint(w, "[OK]") |
|
| 109 |
- |
|
| 110 |
- } |
|
| 111 |
- fmt.Fprint(w, "\t") |
|
| 112 |
- if res.IsAutomated {
|
|
| 113 |
- fmt.Fprint(w, "[OK]") |
|
| 114 |
- } |
|
| 115 |
- fmt.Fprint(w, "\n") |
|
| 116 |
- } |
|
| 117 |
- w.Flush() |
|
| 118 |
- return nil |
|
| 119 |
-} |
|
| 120 |
- |
|
| 121 |
-// SearchResultsByStars sorts search results in descending order by number of stars. |
|
| 122 |
-type searchResultsByStars []registrytypes.SearchResult |
|
| 123 |
- |
|
| 124 |
-func (r searchResultsByStars) Len() int { return len(r) }
|
|
| 125 |
-func (r searchResultsByStars) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
|
| 126 |
-func (r searchResultsByStars) Less(i, j int) bool { return r[j].StarCount < r[i].StarCount }
|
| 127 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,126 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "sort" |
|
| 5 |
+ "strings" |
|
| 6 |
+ "text/tabwriter" |
|
| 7 |
+ |
|
| 8 |
+ "golang.org/x/net/context" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/docker/docker/api/types" |
|
| 11 |
+ registrytypes "github.com/docker/docker/api/types/registry" |
|
| 12 |
+ "github.com/docker/docker/cli" |
|
| 13 |
+ "github.com/docker/docker/cli/command" |
|
| 14 |
+ "github.com/docker/docker/opts" |
|
| 15 |
+ "github.com/docker/docker/pkg/stringutils" |
|
| 16 |
+ "github.com/docker/docker/registry" |
|
| 17 |
+ "github.com/spf13/cobra" |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+type searchOptions struct {
|
|
| 21 |
+ term string |
|
| 22 |
+ noTrunc bool |
|
| 23 |
+ limit int |
|
| 24 |
+ filter opts.FilterOpt |
|
| 25 |
+ |
|
| 26 |
+ // Deprecated |
|
| 27 |
+ stars uint |
|
| 28 |
+ automated bool |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// NewSearchCommand creates a new `docker search` command |
|
| 32 |
+func NewSearchCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|
| 33 |
+ opts := searchOptions{filter: opts.NewFilterOpt()}
|
|
| 34 |
+ |
|
| 35 |
+ cmd := &cobra.Command{
|
|
| 36 |
+ Use: "search [OPTIONS] TERM", |
|
| 37 |
+ Short: "Search the Docker Hub for images", |
|
| 38 |
+ Args: cli.ExactArgs(1), |
|
| 39 |
+ RunE: func(cmd *cobra.Command, args []string) error {
|
|
| 40 |
+ opts.term = args[0] |
|
| 41 |
+ return runSearch(dockerCli, opts) |
|
| 42 |
+ }, |
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ flags := cmd.Flags() |
|
| 46 |
+ |
|
| 47 |
+ flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output") |
|
| 48 |
+ flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided") |
|
| 49 |
+ flags.IntVar(&opts.limit, "limit", registry.DefaultSearchLimit, "Max number of search results") |
|
| 50 |
+ |
|
| 51 |
+ flags.BoolVar(&opts.automated, "automated", false, "Only show automated builds") |
|
| 52 |
+ flags.UintVarP(&opts.stars, "stars", "s", 0, "Only displays with at least x stars") |
|
| 53 |
+ |
|
| 54 |
+ flags.MarkDeprecated("automated", "use --filter=automated=true instead")
|
|
| 55 |
+ flags.MarkDeprecated("stars", "use --filter=stars=3 instead")
|
|
| 56 |
+ |
|
| 57 |
+ return cmd |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func runSearch(dockerCli *command.DockerCli, opts searchOptions) error {
|
|
| 61 |
+ indexInfo, err := registry.ParseSearchIndexInfo(opts.term) |
|
| 62 |
+ if err != nil {
|
|
| 63 |
+ return err |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ ctx := context.Background() |
|
| 67 |
+ |
|
| 68 |
+ authConfig := command.ResolveAuthConfig(ctx, dockerCli, indexInfo) |
|
| 69 |
+ requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, indexInfo, "search") |
|
| 70 |
+ |
|
| 71 |
+ encodedAuth, err := command.EncodeAuthToBase64(authConfig) |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ return err |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ options := types.ImageSearchOptions{
|
|
| 77 |
+ RegistryAuth: encodedAuth, |
|
| 78 |
+ PrivilegeFunc: requestPrivilege, |
|
| 79 |
+ Filters: opts.filter.Value(), |
|
| 80 |
+ Limit: opts.limit, |
|
| 81 |
+ } |
|
| 82 |
+ |
|
| 83 |
+ clnt := dockerCli.Client() |
|
| 84 |
+ |
|
| 85 |
+ unorderedResults, err := clnt.ImageSearch(ctx, opts.term, options) |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return err |
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ results := searchResultsByStars(unorderedResults) |
|
| 91 |
+ sort.Sort(results) |
|
| 92 |
+ |
|
| 93 |
+ w := tabwriter.NewWriter(dockerCli.Out(), 10, 1, 3, ' ', 0) |
|
| 94 |
+ fmt.Fprintf(w, "NAME\tDESCRIPTION\tSTARS\tOFFICIAL\tAUTOMATED\n") |
|
| 95 |
+ for _, res := range results {
|
|
| 96 |
+ // --automated and -s, --stars are deprecated since Docker 1.12 |
|
| 97 |
+ if (opts.automated && !res.IsAutomated) || (int(opts.stars) > res.StarCount) {
|
|
| 98 |
+ continue |
|
| 99 |
+ } |
|
| 100 |
+ desc := strings.Replace(res.Description, "\n", " ", -1) |
|
| 101 |
+ desc = strings.Replace(desc, "\r", " ", -1) |
|
| 102 |
+ if !opts.noTrunc {
|
|
| 103 |
+ desc = stringutils.Ellipsis(desc, 45) |
|
| 104 |
+ } |
|
| 105 |
+ fmt.Fprintf(w, "%s\t%s\t%d\t", res.Name, desc, res.StarCount) |
|
| 106 |
+ if res.IsOfficial {
|
|
| 107 |
+ fmt.Fprint(w, "[OK]") |
|
| 108 |
+ |
|
| 109 |
+ } |
|
| 110 |
+ fmt.Fprint(w, "\t") |
|
| 111 |
+ if res.IsAutomated {
|
|
| 112 |
+ fmt.Fprint(w, "[OK]") |
|
| 113 |
+ } |
|
| 114 |
+ fmt.Fprint(w, "\n") |
|
| 115 |
+ } |
|
| 116 |
+ w.Flush() |
|
| 117 |
+ return nil |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+// SearchResultsByStars sorts search results in descending order by number of stars. |
|
| 121 |
+type searchResultsByStars []registrytypes.SearchResult |
|
| 122 |
+ |
|
| 123 |
+func (r searchResultsByStars) Len() int { return len(r) }
|
|
| 124 |
+func (r searchResultsByStars) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
|
| 125 |
+func (r searchResultsByStars) Less(i, j int) bool { return r[j].StarCount < r[i].StarCount }
|