network.go 6.4 KB

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