network.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. package client
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "net/http"
  7. "text/tabwriter"
  8. flag "github.com/docker/docker/pkg/mflag"
  9. "github.com/docker/docker/pkg/stringid"
  10. )
  11. type command struct {
  12. name string
  13. description string
  14. }
  15. var (
  16. networkCommands = []command{
  17. {"create", "Create a network"},
  18. {"rm", "Remove a network"},
  19. {"ls", "List all networks"},
  20. {"info", "Display information of a network"},
  21. }
  22. )
  23. // CmdNetwork handles the root Network UI
  24. func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error {
  25. cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false)
  26. cmd.Require(flag.Min, 1)
  27. err := cmd.ParseFlags(args, true)
  28. if err == nil {
  29. cmd.Usage()
  30. return fmt.Errorf("invalid command : %v", args)
  31. }
  32. return err
  33. }
  34. // CmdNetworkCreate handles Network Create UI
  35. func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
  36. cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false)
  37. flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network")
  38. cmd.Require(flag.Exact, 1)
  39. err := cmd.ParseFlags(args, true)
  40. if err != nil {
  41. return err
  42. }
  43. // Construct network create request body
  44. var labels []string
  45. nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Labels: labels}
  46. obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
  47. if err != nil {
  48. return err
  49. }
  50. var replyID string
  51. err = json.Unmarshal(obj, &replyID)
  52. if err != nil {
  53. return err
  54. }
  55. fmt.Fprintf(cli.out, "%s\n", replyID)
  56. return nil
  57. }
  58. // CmdNetworkRm handles Network Delete UI
  59. func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
  60. cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false)
  61. cmd.Require(flag.Exact, 1)
  62. err := cmd.ParseFlags(args, true)
  63. if err != nil {
  64. return err
  65. }
  66. id, err := lookupNetworkID(cli, cmd.Arg(0))
  67. if err != nil {
  68. return err
  69. }
  70. _, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil))
  71. if err != nil {
  72. return err
  73. }
  74. return nil
  75. }
  76. // CmdNetworkLs handles Network List UI
  77. func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
  78. cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false)
  79. quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
  80. noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
  81. nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
  82. last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
  83. err := cmd.ParseFlags(args, true)
  84. if err != nil {
  85. return err
  86. }
  87. obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
  88. if err != nil {
  89. return err
  90. }
  91. if *last == -1 && *nLatest {
  92. *last = 1
  93. }
  94. var networkResources []networkResource
  95. err = json.Unmarshal(obj, &networkResources)
  96. if err != nil {
  97. return err
  98. }
  99. wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
  100. // unless quiet (-q) is specified, print field titles
  101. if !*quiet {
  102. fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE")
  103. }
  104. for _, networkResource := range networkResources {
  105. ID := networkResource.ID
  106. netName := networkResource.Name
  107. if !*noTrunc {
  108. ID = stringid.TruncateID(ID)
  109. }
  110. if *quiet {
  111. fmt.Fprintln(wr, ID)
  112. continue
  113. }
  114. netType := networkResource.Type
  115. fmt.Fprintf(wr, "%s\t%s\t%s\t",
  116. ID,
  117. netName,
  118. netType)
  119. fmt.Fprint(wr, "\n")
  120. }
  121. wr.Flush()
  122. return nil
  123. }
  124. // CmdNetworkInfo handles Network Info UI
  125. func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
  126. cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false)
  127. cmd.Require(flag.Exact, 1)
  128. err := cmd.ParseFlags(args, true)
  129. if err != nil {
  130. return err
  131. }
  132. id, err := lookupNetworkID(cli, cmd.Arg(0))
  133. if err != nil {
  134. return err
  135. }
  136. obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil))
  137. if err != nil {
  138. return err
  139. }
  140. networkResource := &networkResource{}
  141. if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
  142. return err
  143. }
  144. fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID)
  145. fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name)
  146. fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type)
  147. if networkResource.Services != nil {
  148. for _, serviceResource := range networkResource.Services {
  149. fmt.Fprintf(cli.out, " Service Id: %s\n", serviceResource.ID)
  150. fmt.Fprintf(cli.out, "\tName: %s\n", serviceResource.Name)
  151. }
  152. }
  153. return nil
  154. }
  155. // Helper function to predict if a string is a name or id or partial-id
  156. // This provides a best-effort mechanism to identify a id with the help of GET Filter APIs
  157. // Being a UI, its most likely that name will be used by the user, which is used to lookup
  158. // the corresponding ID. If ID is not found, this function will assume that the passed string
  159. // is an ID by itself.
  160. func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) {
  161. obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil))
  162. if err != nil {
  163. return "", err
  164. }
  165. if statusCode != http.StatusOK {
  166. return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
  167. }
  168. var list []*networkResource
  169. err = json.Unmarshal(obj, &list)
  170. if err != nil {
  171. return "", err
  172. }
  173. if len(list) > 0 {
  174. // name query filter will always return a single-element collection
  175. return list[0].ID, nil
  176. }
  177. // Check for Partial-id
  178. obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil))
  179. if err != nil {
  180. return "", err
  181. }
  182. if statusCode != http.StatusOK {
  183. return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
  184. }
  185. err = json.Unmarshal(obj, &list)
  186. if err != nil {
  187. return "", err
  188. }
  189. if len(list) == 0 {
  190. return "", fmt.Errorf("resource not found %s", nameID)
  191. }
  192. if len(list) > 1 {
  193. return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID)
  194. }
  195. return list[0].ID, nil
  196. }
  197. func networkUsage(chain string) string {
  198. help := "Commands:\n"
  199. for _, cmd := range networkCommands {
  200. help += fmt.Sprintf(" %-25.25s%s\n", cmd.name, cmd.description)
  201. }
  202. help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain)
  203. return help
  204. }