cluster_read_config.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. package tester
  15. import (
  16. "errors"
  17. "fmt"
  18. "io/ioutil"
  19. "net/url"
  20. "path/filepath"
  21. "strings"
  22. "github.com/coreos/etcd/functional/rpcpb"
  23. "go.uber.org/zap"
  24. yaml "gopkg.in/yaml.v2"
  25. )
  26. func read(lg *zap.Logger, fpath string) (*Cluster, error) {
  27. bts, err := ioutil.ReadFile(fpath)
  28. if err != nil {
  29. return nil, err
  30. }
  31. lg.Info("opened configuration file", zap.String("path", fpath))
  32. clus := &Cluster{lg: lg}
  33. if err = yaml.Unmarshal(bts, clus); err != nil {
  34. return nil, err
  35. }
  36. if len(clus.Members) < 3 {
  37. return nil, fmt.Errorf("len(clus.Members) expects at least 3, got %d", len(clus.Members))
  38. }
  39. for i, mem := range clus.Members {
  40. if mem.BaseDir == "" {
  41. return nil, fmt.Errorf("BaseDir cannot be empty (got %q)", mem.BaseDir)
  42. }
  43. if mem.EtcdLogPath == "" {
  44. return nil, fmt.Errorf("EtcdLogPath cannot be empty (got %q)", mem.EtcdLogPath)
  45. }
  46. if mem.Etcd.Name == "" {
  47. return nil, fmt.Errorf("'--name' cannot be empty (got %+v)", mem)
  48. }
  49. if mem.Etcd.DataDir == "" {
  50. return nil, fmt.Errorf("'--data-dir' cannot be empty (got %+v)", mem)
  51. }
  52. if mem.Etcd.SnapshotCount == 0 {
  53. return nil, fmt.Errorf("'--snapshot-count' cannot be 0 (got %+v)", mem.Etcd.SnapshotCount)
  54. }
  55. if mem.Etcd.DataDir == "" {
  56. return nil, fmt.Errorf("'--data-dir' cannot be empty (got %q)", mem.Etcd.DataDir)
  57. }
  58. if mem.Etcd.WALDir == "" {
  59. clus.Members[i].Etcd.WALDir = filepath.Join(mem.Etcd.DataDir, "member", "wal")
  60. }
  61. if mem.Etcd.HeartbeatIntervalMs == 0 {
  62. return nil, fmt.Errorf("'--heartbeat-interval' cannot be 0 (got %+v)", mem.Etcd)
  63. }
  64. if mem.Etcd.ElectionTimeoutMs == 0 {
  65. return nil, fmt.Errorf("'--election-timeout' cannot be 0 (got %+v)", mem.Etcd)
  66. }
  67. if int64(clus.Tester.DelayLatencyMs) <= mem.Etcd.ElectionTimeoutMs {
  68. return nil, fmt.Errorf("delay latency %d ms must be greater than election timeout %d ms", clus.Tester.DelayLatencyMs, mem.Etcd.ElectionTimeoutMs)
  69. }
  70. port := ""
  71. listenClientPorts := make([]string, len(clus.Members))
  72. for i, u := range mem.Etcd.ListenClientURLs {
  73. if !isValidURL(u) {
  74. return nil, fmt.Errorf("'--listen-client-urls' has valid URL %q", u)
  75. }
  76. listenClientPorts[i], err = getPort(u)
  77. if err != nil {
  78. return nil, fmt.Errorf("'--listen-client-urls' has no port %q", u)
  79. }
  80. }
  81. for i, u := range mem.Etcd.AdvertiseClientURLs {
  82. if !isValidURL(u) {
  83. return nil, fmt.Errorf("'--advertise-client-urls' has valid URL %q", u)
  84. }
  85. port, err = getPort(u)
  86. if err != nil {
  87. return nil, fmt.Errorf("'--advertise-client-urls' has no port %q", u)
  88. }
  89. if mem.EtcdClientProxy && listenClientPorts[i] == port {
  90. return nil, fmt.Errorf("clus.Members[%d] requires client port proxy, but advertise port %q conflicts with listener port %q", i, port, listenClientPorts[i])
  91. }
  92. }
  93. listenPeerPorts := make([]string, len(clus.Members))
  94. for i, u := range mem.Etcd.ListenPeerURLs {
  95. if !isValidURL(u) {
  96. return nil, fmt.Errorf("'--listen-peer-urls' has valid URL %q", u)
  97. }
  98. listenPeerPorts[i], err = getPort(u)
  99. if err != nil {
  100. return nil, fmt.Errorf("'--listen-peer-urls' has no port %q", u)
  101. }
  102. }
  103. for j, u := range mem.Etcd.AdvertisePeerURLs {
  104. if !isValidURL(u) {
  105. return nil, fmt.Errorf("'--initial-advertise-peer-urls' has valid URL %q", u)
  106. }
  107. port, err = getPort(u)
  108. if err != nil {
  109. return nil, fmt.Errorf("'--initial-advertise-peer-urls' has no port %q", u)
  110. }
  111. if mem.EtcdPeerProxy && listenPeerPorts[j] == port {
  112. return nil, fmt.Errorf("clus.Members[%d] requires peer port proxy, but advertise port %q conflicts with listener port %q", i, port, listenPeerPorts[j])
  113. }
  114. }
  115. if !strings.HasPrefix(mem.EtcdLogPath, mem.BaseDir) {
  116. return nil, fmt.Errorf("EtcdLogPath must be prefixed with BaseDir (got %q)", mem.EtcdLogPath)
  117. }
  118. if !strings.HasPrefix(mem.Etcd.DataDir, mem.BaseDir) {
  119. return nil, fmt.Errorf("Etcd.DataDir must be prefixed with BaseDir (got %q)", mem.Etcd.DataDir)
  120. }
  121. // TODO: support separate WALDir that can be handled via failure-archive
  122. if !strings.HasPrefix(mem.Etcd.WALDir, mem.BaseDir) {
  123. return nil, fmt.Errorf("Etcd.WALDir must be prefixed with BaseDir (got %q)", mem.Etcd.WALDir)
  124. }
  125. // TODO: only support generated certs with TLS generator
  126. // deprecate auto TLS
  127. if mem.Etcd.PeerAutoTLS && mem.Etcd.PeerCertFile != "" {
  128. return nil, fmt.Errorf("Etcd.PeerAutoTLS 'true', but Etcd.PeerCertFile is %q", mem.Etcd.PeerCertFile)
  129. }
  130. if mem.Etcd.PeerAutoTLS && mem.Etcd.PeerKeyFile != "" {
  131. return nil, fmt.Errorf("Etcd.PeerAutoTLS 'true', but Etcd.PeerKeyFile is %q", mem.Etcd.PeerKeyFile)
  132. }
  133. if mem.Etcd.PeerAutoTLS && mem.Etcd.PeerTrustedCAFile != "" {
  134. return nil, fmt.Errorf("Etcd.PeerAutoTLS 'true', but Etcd.PeerTrustedCAFile is %q", mem.Etcd.PeerTrustedCAFile)
  135. }
  136. if mem.Etcd.ClientAutoTLS && mem.Etcd.ClientCertFile != "" {
  137. return nil, fmt.Errorf("Etcd.ClientAutoTLS 'true', but Etcd.ClientCertFile is %q", mem.Etcd.ClientCertFile)
  138. }
  139. if mem.Etcd.ClientAutoTLS && mem.Etcd.ClientKeyFile != "" {
  140. return nil, fmt.Errorf("Etcd.ClientAutoTLS 'true', but Etcd.ClientKeyFile is %q", mem.Etcd.ClientKeyFile)
  141. }
  142. if mem.Etcd.ClientAutoTLS && mem.Etcd.ClientTrustedCAFile != "" {
  143. return nil, fmt.Errorf("Etcd.ClientAutoTLS 'true', but Etcd.ClientTrustedCAFile is %q", mem.Etcd.ClientTrustedCAFile)
  144. }
  145. if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerCertFile == "" {
  146. return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'true', but Etcd.PeerCertFile is %q", mem.Etcd.PeerCertFile)
  147. }
  148. if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerKeyFile == "" {
  149. return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'true', but Etcd.PeerKeyFile is %q", mem.Etcd.PeerCertFile)
  150. }
  151. // only support self-signed certs
  152. if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerTrustedCAFile == "" {
  153. return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'true', but Etcd.PeerTrustedCAFile is %q", mem.Etcd.PeerCertFile)
  154. }
  155. if !mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerCertFile != "" {
  156. return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'false', but Etcd.PeerCertFile is %q", mem.Etcd.PeerCertFile)
  157. }
  158. if !mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerKeyFile != "" {
  159. return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'false', but Etcd.PeerKeyFile is %q", mem.Etcd.PeerCertFile)
  160. }
  161. if !mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerTrustedCAFile != "" {
  162. return nil, fmt.Errorf("Etcd.PeerClientCertAuth 'false', but Etcd.PeerTrustedCAFile is %q", mem.Etcd.PeerTrustedCAFile)
  163. }
  164. if mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerAutoTLS {
  165. return nil, fmt.Errorf("Etcd.PeerClientCertAuth and Etcd.PeerAutoTLS cannot be both 'true'")
  166. }
  167. if (mem.Etcd.PeerCertFile == "") != (mem.Etcd.PeerKeyFile == "") {
  168. return nil, fmt.Errorf("Both Etcd.PeerCertFile %q and Etcd.PeerKeyFile %q must be either empty or non-empty", mem.Etcd.PeerCertFile, mem.Etcd.PeerKeyFile)
  169. }
  170. if mem.Etcd.ClientCertAuth && mem.Etcd.ClientAutoTLS {
  171. return nil, fmt.Errorf("Etcd.ClientCertAuth and Etcd.ClientAutoTLS cannot be both 'true'")
  172. }
  173. if mem.Etcd.ClientCertAuth && mem.Etcd.ClientCertFile == "" {
  174. return nil, fmt.Errorf("Etcd.ClientCertAuth 'true', but Etcd.ClientCertFile is %q", mem.Etcd.PeerCertFile)
  175. }
  176. if mem.Etcd.ClientCertAuth && mem.Etcd.ClientKeyFile == "" {
  177. return nil, fmt.Errorf("Etcd.ClientCertAuth 'true', but Etcd.ClientKeyFile is %q", mem.Etcd.PeerCertFile)
  178. }
  179. if mem.Etcd.ClientCertAuth && mem.Etcd.ClientTrustedCAFile == "" {
  180. return nil, fmt.Errorf("Etcd.ClientCertAuth 'true', but Etcd.ClientTrustedCAFile is %q", mem.Etcd.ClientTrustedCAFile)
  181. }
  182. if !mem.Etcd.ClientCertAuth && mem.Etcd.ClientCertFile != "" {
  183. return nil, fmt.Errorf("Etcd.ClientCertAuth 'false', but Etcd.ClientCertFile is %q", mem.Etcd.PeerCertFile)
  184. }
  185. if !mem.Etcd.ClientCertAuth && mem.Etcd.ClientKeyFile != "" {
  186. return nil, fmt.Errorf("Etcd.ClientCertAuth 'false', but Etcd.ClientKeyFile is %q", mem.Etcd.PeerCertFile)
  187. }
  188. if !mem.Etcd.ClientCertAuth && mem.Etcd.ClientTrustedCAFile != "" {
  189. return nil, fmt.Errorf("Etcd.ClientCertAuth 'false', but Etcd.ClientTrustedCAFile is %q", mem.Etcd.PeerCertFile)
  190. }
  191. if (mem.Etcd.ClientCertFile == "") != (mem.Etcd.ClientKeyFile == "") {
  192. return nil, fmt.Errorf("Both Etcd.ClientCertFile %q and Etcd.ClientKeyFile %q must be either empty or non-empty", mem.Etcd.ClientCertFile, mem.Etcd.ClientKeyFile)
  193. }
  194. peerTLS := mem.Etcd.PeerAutoTLS ||
  195. (mem.Etcd.PeerClientCertAuth && mem.Etcd.PeerCertFile != "" && mem.Etcd.PeerKeyFile != "" && mem.Etcd.PeerTrustedCAFile != "")
  196. if peerTLS {
  197. for _, cu := range mem.Etcd.ListenPeerURLs {
  198. var u *url.URL
  199. u, err = url.Parse(cu)
  200. if err != nil {
  201. return nil, err
  202. }
  203. if u.Scheme != "https" { // TODO: support unix
  204. return nil, fmt.Errorf("peer TLS is enabled with wrong scheme %q", cu)
  205. }
  206. }
  207. for _, cu := range mem.Etcd.AdvertisePeerURLs {
  208. var u *url.URL
  209. u, err = url.Parse(cu)
  210. if err != nil {
  211. return nil, err
  212. }
  213. if u.Scheme != "https" { // TODO: support unix
  214. return nil, fmt.Errorf("peer TLS is enabled with wrong scheme %q", cu)
  215. }
  216. }
  217. clus.Members[i].PeerCertPath = mem.Etcd.PeerCertFile
  218. if mem.Etcd.PeerCertFile != "" {
  219. var data []byte
  220. data, err = ioutil.ReadFile(mem.Etcd.PeerCertFile)
  221. if err != nil {
  222. return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.PeerCertFile, err)
  223. }
  224. clus.Members[i].PeerCertData = string(data)
  225. }
  226. clus.Members[i].PeerKeyPath = mem.Etcd.PeerKeyFile
  227. if mem.Etcd.PeerKeyFile != "" {
  228. var data []byte
  229. data, err = ioutil.ReadFile(mem.Etcd.PeerKeyFile)
  230. if err != nil {
  231. return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.PeerKeyFile, err)
  232. }
  233. clus.Members[i].PeerCertData = string(data)
  234. }
  235. clus.Members[i].PeerTrustedCAPath = mem.Etcd.PeerTrustedCAFile
  236. if mem.Etcd.PeerTrustedCAFile != "" {
  237. var data []byte
  238. data, err = ioutil.ReadFile(mem.Etcd.PeerTrustedCAFile)
  239. if err != nil {
  240. return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.PeerTrustedCAFile, err)
  241. }
  242. clus.Members[i].PeerCertData = string(data)
  243. }
  244. }
  245. clientTLS := mem.Etcd.ClientAutoTLS ||
  246. (mem.Etcd.ClientCertAuth && mem.Etcd.ClientCertFile != "" && mem.Etcd.ClientKeyFile != "" && mem.Etcd.ClientTrustedCAFile != "")
  247. if clientTLS {
  248. for _, cu := range mem.Etcd.ListenClientURLs {
  249. var u *url.URL
  250. u, err = url.Parse(cu)
  251. if err != nil {
  252. return nil, err
  253. }
  254. if u.Scheme != "https" { // TODO: support unix
  255. return nil, fmt.Errorf("client TLS is enabled with wrong scheme %q", cu)
  256. }
  257. }
  258. for _, cu := range mem.Etcd.AdvertiseClientURLs {
  259. var u *url.URL
  260. u, err = url.Parse(cu)
  261. if err != nil {
  262. return nil, err
  263. }
  264. if u.Scheme != "https" { // TODO: support unix
  265. return nil, fmt.Errorf("client TLS is enabled with wrong scheme %q", cu)
  266. }
  267. }
  268. clus.Members[i].ClientCertPath = mem.Etcd.ClientCertFile
  269. if mem.Etcd.ClientCertFile != "" {
  270. var data []byte
  271. data, err = ioutil.ReadFile(mem.Etcd.ClientCertFile)
  272. if err != nil {
  273. return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.ClientCertFile, err)
  274. }
  275. clus.Members[i].ClientCertData = string(data)
  276. }
  277. clus.Members[i].ClientKeyPath = mem.Etcd.ClientKeyFile
  278. if mem.Etcd.ClientKeyFile != "" {
  279. var data []byte
  280. data, err = ioutil.ReadFile(mem.Etcd.ClientKeyFile)
  281. if err != nil {
  282. return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.ClientKeyFile, err)
  283. }
  284. clus.Members[i].ClientCertData = string(data)
  285. }
  286. clus.Members[i].ClientTrustedCAPath = mem.Etcd.ClientTrustedCAFile
  287. if mem.Etcd.ClientTrustedCAFile != "" {
  288. var data []byte
  289. data, err = ioutil.ReadFile(mem.Etcd.ClientTrustedCAFile)
  290. if err != nil {
  291. return nil, fmt.Errorf("failed to read %q (%v)", mem.Etcd.ClientTrustedCAFile, err)
  292. }
  293. clus.Members[i].ClientCertData = string(data)
  294. }
  295. }
  296. }
  297. if len(clus.Tester.FailureCases) == 0 {
  298. return nil, errors.New("FailureCases not found")
  299. }
  300. if clus.Tester.DelayLatencyMs <= clus.Tester.DelayLatencyMsRv*5 {
  301. return nil, fmt.Errorf("delay latency %d ms must be greater than 5x of delay latency random variable %d ms", clus.Tester.DelayLatencyMs, clus.Tester.DelayLatencyMsRv)
  302. }
  303. if clus.Tester.UpdatedDelayLatencyMs == 0 {
  304. clus.Tester.UpdatedDelayLatencyMs = clus.Tester.DelayLatencyMs
  305. }
  306. for _, v := range clus.Tester.FailureCases {
  307. if _, ok := rpcpb.FailureCase_value[v]; !ok {
  308. return nil, fmt.Errorf("%q is not defined in 'rpcpb.FailureCase_value'", v)
  309. }
  310. }
  311. for _, v := range clus.Tester.StressTypes {
  312. if _, ok := rpcpb.StressType_value[v]; !ok {
  313. return nil, fmt.Errorf("StressType is unknown; got %q", v)
  314. }
  315. }
  316. if clus.Tester.StressKeySuffixRangeTxn > 100 {
  317. return nil, fmt.Errorf("StressKeySuffixRangeTxn maximum value is 100, got %v", clus.Tester.StressKeySuffixRangeTxn)
  318. }
  319. if clus.Tester.StressKeyTxnOps > 64 {
  320. return nil, fmt.Errorf("StressKeyTxnOps maximum value is 64, got %v", clus.Tester.StressKeyTxnOps)
  321. }
  322. return clus, err
  323. }