main.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2018 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // etcd-dump-metrics automates etcd Prometheus metrics documentation.
  15. package main
  16. import (
  17. "flag"
  18. "fmt"
  19. "io/ioutil"
  20. "net/url"
  21. "os"
  22. "os/exec"
  23. "path/filepath"
  24. "time"
  25. "go.etcd.io/etcd/embed"
  26. "go.uber.org/zap"
  27. )
  28. var lg *zap.Logger
  29. func init() {
  30. var err error
  31. lg, err = zap.NewProduction()
  32. if err != nil {
  33. panic(err)
  34. }
  35. }
  36. func main() {
  37. addr := flag.String("addr", "", "etcd metrics URL to fetch from (empty to use current git branch)")
  38. downloadVer := flag.String("download-ver", "", "etcd binary version to download and fetch metrics from")
  39. debug := flag.Bool("debug", false, "true to enable debug logging")
  40. flag.Parse()
  41. if *addr != "" && *downloadVer != "" {
  42. panic("specify either 'addr' or 'download-ver'")
  43. }
  44. if *debug {
  45. lg = zap.NewExample()
  46. }
  47. ep := *addr
  48. if ep == "" {
  49. if *downloadVer != "" {
  50. ver := *downloadVer
  51. // download release binary to temporary directory
  52. d, err := ioutil.TempDir(os.TempDir(), ver)
  53. if err != nil {
  54. panic(err)
  55. }
  56. defer os.RemoveAll(d)
  57. var bp string
  58. bp, err = install(ver, d)
  59. if err != nil {
  60. panic(err)
  61. }
  62. // set up 2-node cluster locally
  63. ep = "http://localhost:2379/metrics"
  64. cluster := "s1=http://localhost:2380,s2=http://localhost:22380"
  65. d1 := filepath.Join(d, "s1")
  66. d2 := filepath.Join(d, "s2")
  67. os.RemoveAll(d1)
  68. os.RemoveAll(d2)
  69. type run struct {
  70. err error
  71. cmd *exec.Cmd
  72. }
  73. rc := make(chan run)
  74. cs1 := getCommand(bp, "s1", d1, "http://localhost:2379", "http://localhost:2380", cluster)
  75. cmd1 := exec.Command("bash", "-c", cs1)
  76. go func() {
  77. if *debug {
  78. cmd1.Stderr = os.Stderr
  79. }
  80. if cerr := cmd1.Start(); cerr != nil {
  81. lg.Warn("failed to start first process", zap.Error(cerr))
  82. rc <- run{err: cerr}
  83. return
  84. }
  85. lg.Debug("started first process")
  86. rc <- run{cmd: cmd1}
  87. }()
  88. cs2 := getCommand(bp, "s2", d2, "http://localhost:22379", "http://localhost:22380", cluster)
  89. cmd2 := exec.Command("bash", "-c", cs2)
  90. go func() {
  91. if *debug {
  92. cmd2.Stderr = os.Stderr
  93. }
  94. if cerr := cmd2.Start(); cerr != nil {
  95. lg.Warn("failed to start second process", zap.Error(cerr))
  96. rc <- run{err: cerr}
  97. return
  98. }
  99. lg.Debug("started second process")
  100. rc <- run{cmd: cmd2}
  101. }()
  102. rc1 := <-rc
  103. if rc1.err != nil {
  104. panic(rc1.err)
  105. }
  106. rc2 := <-rc
  107. if rc2.err != nil {
  108. panic(rc2.err)
  109. }
  110. defer func() {
  111. lg.Debug("killing processes")
  112. rc1.cmd.Process.Kill()
  113. rc2.cmd.Process.Kill()
  114. rc1.cmd.Wait()
  115. rc2.cmd.Wait()
  116. lg.Debug("killed processes")
  117. }()
  118. // give enough time for peer-to-peer metrics
  119. lg.Debug("waiting")
  120. time.Sleep(7 * time.Second)
  121. lg.Debug("started 2-node etcd cluster")
  122. } else {
  123. // fetch metrics from embedded etcd
  124. uss := newEmbedURLs(4)
  125. ep = uss[0].String() + "/metrics"
  126. cfgs := []*embed.Config{embed.NewConfig(), embed.NewConfig()}
  127. cfgs[0].Name, cfgs[1].Name = "0", "1"
  128. setupEmbedCfg(cfgs[0], []url.URL{uss[0]}, []url.URL{uss[1]}, []url.URL{uss[1], uss[3]})
  129. setupEmbedCfg(cfgs[1], []url.URL{uss[2]}, []url.URL{uss[3]}, []url.URL{uss[1], uss[3]})
  130. type embedAndError struct {
  131. ec *embed.Etcd
  132. err error
  133. }
  134. ech := make(chan embedAndError)
  135. for _, cfg := range cfgs {
  136. go func(c *embed.Config) {
  137. e, err := embed.StartEtcd(c)
  138. if err != nil {
  139. ech <- embedAndError{err: err}
  140. return
  141. }
  142. <-e.Server.ReadyNotify()
  143. ech <- embedAndError{ec: e}
  144. }(cfg)
  145. }
  146. for range cfgs {
  147. ev := <-ech
  148. if ev.err != nil {
  149. lg.Panic("failed to start embedded etcd", zap.Error(ev.err))
  150. }
  151. defer ev.ec.Close()
  152. }
  153. // give enough time for peer-to-peer metrics
  154. lg.Debug("waiting")
  155. time.Sleep(7 * time.Second)
  156. lg.Debug("started 2-node embedded etcd cluster")
  157. }
  158. }
  159. // send client requests to populate gRPC client-side metrics
  160. // TODO: enable default metrics initialization in v3.1 and v3.2
  161. write(ep)
  162. lg.Debug("fetching metrics", zap.String("endpoint", ep))
  163. fmt.Println(getMetrics(ep))
  164. }