client.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package weave
  2. import (
  3. "bytes"
  4. "fmt"
  5. "net"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. realexec "os/exec"
  10. "regexp"
  11. "strconv"
  12. "github.com/ugorji/go/codec"
  13. "github.com/weaveworks/common/exec"
  14. )
  15. const dockerAPIVersion = "1.22" // Support Docker Engine >= 1.10
  16. // Client for Weave Net API
  17. type Client interface {
  18. Status() (Status, error)
  19. AddDNSEntry(fqdn, containerid string, ip net.IP) error
  20. Expose() error // on the interface for mocking
  21. }
  22. // Status describes whats happen in the Weave Net router.
  23. type Status struct {
  24. Version string
  25. Router Router
  26. DNS *DNS
  27. IPAM *IPAM
  28. Proxy *Proxy
  29. Plugin *Plugin
  30. }
  31. // Router describes the status of the Weave Router
  32. type Router struct {
  33. Name string
  34. Encryption bool
  35. ProtocolMinVersion int
  36. ProtocolMaxVersion int
  37. PeerDiscovery bool
  38. Peers []Peer
  39. Connections []struct {
  40. Address string
  41. Outbound bool
  42. State string
  43. Info string
  44. }
  45. Targets []string
  46. TrustedSubnets []string
  47. }
  48. // Peer describes a peer in the weave network
  49. type Peer struct {
  50. Name string
  51. NickName string
  52. Connections []struct {
  53. Name string
  54. NickName string
  55. Address string
  56. Outbound bool
  57. Established bool
  58. }
  59. }
  60. // DNS describes the status of Weave DNS
  61. type DNS struct {
  62. Domain string
  63. Upstream []string
  64. TTL uint32
  65. Entries []struct {
  66. Hostname string
  67. ContainerID string
  68. Tombstone int64
  69. }
  70. }
  71. // IPAM describes the status of Weave IPAM
  72. type IPAM struct {
  73. Paxos *struct {
  74. Elector bool
  75. KnownNodes int
  76. Quorum uint
  77. }
  78. Range string
  79. DefaultSubnet string
  80. Entries []struct {
  81. Size uint32
  82. IsKnownPeer bool
  83. }
  84. PendingAllocates []string
  85. }
  86. // Proxy describes the status of Weave Proxy
  87. type Proxy struct {
  88. Addresses []string
  89. }
  90. // Plugin describes the status of the Weave Plugin
  91. type Plugin struct {
  92. DriverName string
  93. }
  94. var ipMatch = regexp.MustCompile(`([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(/[0-9]+)`)
  95. type client struct {
  96. url string
  97. }
  98. // NewClient makes a new Client
  99. func NewClient(url string) Client {
  100. return &client{
  101. url: url,
  102. }
  103. }
  104. func (c *client) Status() (Status, error) {
  105. req, err := http.NewRequest("GET", c.url+"/report", nil)
  106. if err != nil {
  107. return Status{}, err
  108. }
  109. req.Header.Add("Accept", "application/json")
  110. resp, err := http.DefaultClient.Do(req)
  111. if err != nil {
  112. return Status{}, errorf("%v", err)
  113. }
  114. defer resp.Body.Close()
  115. if resp.StatusCode != http.StatusOK {
  116. return Status{}, fmt.Errorf("Got %d", resp.StatusCode)
  117. }
  118. var status Status
  119. decoder := codec.NewDecoder(resp.Body, &codec.JsonHandle{})
  120. if err := decoder.Decode(&status); err != nil {
  121. return Status{}, err
  122. }
  123. return status, nil
  124. }
  125. func (c *client) AddDNSEntry(fqdn, containerID string, ip net.IP) error {
  126. data := url.Values{
  127. "fqdn": []string{fqdn},
  128. }
  129. url := fmt.Sprintf("%s/name/%s/%s", c.url, containerID, ip.String())
  130. req, err := http.NewRequest("PUT", url, bytes.NewBufferString(data.Encode()))
  131. if err != nil {
  132. return err
  133. }
  134. req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
  135. req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
  136. resp, err := http.DefaultClient.Do(req)
  137. if err != nil {
  138. return errorf("%v", err)
  139. }
  140. if err := resp.Body.Close(); err != nil {
  141. return err
  142. }
  143. if resp.StatusCode != http.StatusNoContent {
  144. return fmt.Errorf("Got %d", resp.StatusCode)
  145. }
  146. return nil
  147. }
  148. func (c *client) Expose() error {
  149. cmd := weaveCommand("--local", "ps", "weave:expose")
  150. output, err := cmd.Output()
  151. if err != nil {
  152. stdErr := []byte{}
  153. if exitErr, ok := err.(*realexec.ExitError); ok {
  154. stdErr = exitErr.Stderr
  155. }
  156. return errorf("Error running weave ps: %s: %q", err, stdErr)
  157. }
  158. ips := ipMatch.FindAllSubmatch(output, -1)
  159. if ips != nil {
  160. // Alread exposed!
  161. return nil
  162. }
  163. cmd = weaveCommand("--local", "expose")
  164. if _, err := cmd.Output(); err != nil {
  165. stdErr := []byte{}
  166. if exitErr, ok := err.(*realexec.ExitError); ok {
  167. stdErr = exitErr.Stderr
  168. }
  169. return errorf("Error running weave expose: %s: %q", err, stdErr)
  170. }
  171. return nil
  172. }
  173. func weaveCommand(arg ...string) exec.Cmd {
  174. cmd := exec.Command("weave", arg...)
  175. cmd.SetEnv(append(os.Environ(), "DOCKER_API_VERSION="+dockerAPIVersion))
  176. return cmd
  177. }
  178. func errorf(format string, a ...interface{}) error {
  179. return fmt.Errorf(format+". If you are not running Weave Net, you may wish to suppress this warning by launching scope with the `--weave=false` option.", a...)
  180. }