config_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. // Copyright 2015 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 etcdmain
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "net/url"
  19. "os"
  20. "reflect"
  21. "strings"
  22. "testing"
  23. "github.com/coreos/etcd/embed"
  24. "sigs.k8s.io/yaml"
  25. )
  26. func TestConfigParsingMemberFlags(t *testing.T) {
  27. args := []string{
  28. "-data-dir=testdir",
  29. "-name=testname",
  30. "-max-wals=10",
  31. "-max-snapshots=10",
  32. "-snapshot-count=10",
  33. "-listen-peer-urls=http://localhost:8000,https://localhost:8001",
  34. "-listen-client-urls=http://localhost:7000,https://localhost:7001",
  35. // it should be set if -listen-client-urls is set
  36. "-advertise-client-urls=http://localhost:7000,https://localhost:7001",
  37. }
  38. cfg := newConfig()
  39. err := cfg.parse(args)
  40. if err != nil {
  41. t.Fatal(err)
  42. }
  43. validateMemberFlags(t, cfg)
  44. }
  45. func TestConfigFileMemberFields(t *testing.T) {
  46. yc := struct {
  47. Dir string `json:"data-dir"`
  48. MaxSnapFiles uint `json:"max-snapshots"`
  49. MaxWalFiles uint `json:"max-wals"`
  50. Name string `json:"name"`
  51. SnapCount uint64 `json:"snapshot-count"`
  52. LPUrls string `json:"listen-peer-urls"`
  53. LCUrls string `json:"listen-client-urls"`
  54. AcurlsCfgFile string `json:"advertise-client-urls"`
  55. }{
  56. "testdir",
  57. 10,
  58. 10,
  59. "testname",
  60. 10,
  61. "http://localhost:8000,https://localhost:8001",
  62. "http://localhost:7000,https://localhost:7001",
  63. "http://localhost:7000,https://localhost:7001",
  64. }
  65. b, err := yaml.Marshal(&yc)
  66. if err != nil {
  67. t.Fatal(err)
  68. }
  69. tmpfile := mustCreateCfgFile(t, b)
  70. defer os.Remove(tmpfile.Name())
  71. args := []string{fmt.Sprintf("--config-file=%s", tmpfile.Name())}
  72. cfg := newConfig()
  73. if err = cfg.parse(args); err != nil {
  74. t.Fatal(err)
  75. }
  76. validateMemberFlags(t, cfg)
  77. }
  78. func TestConfigParsingClusteringFlags(t *testing.T) {
  79. args := []string{
  80. "-initial-cluster=0=http://localhost:8000",
  81. "-initial-cluster-state=existing",
  82. "-initial-cluster-token=etcdtest",
  83. "-initial-advertise-peer-urls=http://localhost:8000,https://localhost:8001",
  84. "-advertise-client-urls=http://localhost:7000,https://localhost:7001",
  85. "-discovery-fallback=exit",
  86. }
  87. cfg := newConfig()
  88. if err := cfg.parse(args); err != nil {
  89. t.Fatal(err)
  90. }
  91. validateClusteringFlags(t, cfg)
  92. }
  93. func TestConfigFileClusteringFields(t *testing.T) {
  94. yc := struct {
  95. InitialCluster string `json:"initial-cluster"`
  96. ClusterState string `json:"initial-cluster-state"`
  97. InitialClusterToken string `json:"initial-cluster-token"`
  98. Apurls string `json:"initial-advertise-peer-urls"`
  99. Acurls string `json:"advertise-client-urls"`
  100. Fallback string `json:"discovery-fallback"`
  101. }{
  102. "0=http://localhost:8000",
  103. "existing",
  104. "etcdtest",
  105. "http://localhost:8000,https://localhost:8001",
  106. "http://localhost:7000,https://localhost:7001",
  107. "exit",
  108. }
  109. b, err := yaml.Marshal(&yc)
  110. if err != nil {
  111. t.Fatal(err)
  112. }
  113. tmpfile := mustCreateCfgFile(t, b)
  114. defer os.Remove(tmpfile.Name())
  115. args := []string{fmt.Sprintf("--config-file=%s", tmpfile.Name())}
  116. cfg := newConfig()
  117. err = cfg.parse(args)
  118. if err != nil {
  119. t.Fatal(err)
  120. }
  121. validateClusteringFlags(t, cfg)
  122. }
  123. func TestConfigFileClusteringFlags(t *testing.T) {
  124. tests := []struct {
  125. Name string `json:"name"`
  126. InitialCluster string `json:"initial-cluster"`
  127. DNSCluster string `json:"discovery-srv"`
  128. Durl string `json:"discovery"`
  129. }{
  130. // Use default name and generate a default initial-cluster
  131. {},
  132. {
  133. Name: "non-default",
  134. },
  135. {
  136. InitialCluster: "0=localhost:8000",
  137. },
  138. {
  139. Name: "non-default",
  140. InitialCluster: "0=localhost:8000",
  141. },
  142. {
  143. DNSCluster: "example.com",
  144. },
  145. {
  146. Name: "non-default",
  147. DNSCluster: "example.com",
  148. },
  149. {
  150. Durl: "http://example.com/abc",
  151. },
  152. {
  153. Name: "non-default",
  154. Durl: "http://example.com/abc",
  155. },
  156. }
  157. for i, tt := range tests {
  158. b, err := yaml.Marshal(&tt)
  159. if err != nil {
  160. t.Fatal(err)
  161. }
  162. tmpfile := mustCreateCfgFile(t, b)
  163. defer os.Remove(tmpfile.Name())
  164. args := []string{fmt.Sprintf("--config-file=%s", tmpfile.Name())}
  165. cfg := newConfig()
  166. if err := cfg.parse(args); err != nil {
  167. t.Errorf("%d: err = %v", i, err)
  168. }
  169. }
  170. }
  171. func TestConfigParsingOtherFlags(t *testing.T) {
  172. args := []string{"-proxy=readonly"}
  173. cfg := newConfig()
  174. err := cfg.parse(args)
  175. if err != nil {
  176. t.Fatal(err)
  177. }
  178. validateOtherFlags(t, cfg)
  179. }
  180. func TestConfigFileOtherFields(t *testing.T) {
  181. yc := struct {
  182. ProxyCfgFile string `json:"proxy"`
  183. }{
  184. "readonly",
  185. }
  186. b, err := yaml.Marshal(&yc)
  187. if err != nil {
  188. t.Fatal(err)
  189. }
  190. tmpfile := mustCreateCfgFile(t, b)
  191. defer os.Remove(tmpfile.Name())
  192. args := []string{fmt.Sprintf("--config-file=%s", tmpfile.Name())}
  193. cfg := newConfig()
  194. err = cfg.parse(args)
  195. if err != nil {
  196. t.Fatal(err)
  197. }
  198. validateOtherFlags(t, cfg)
  199. }
  200. func TestConfigParsingConflictClusteringFlags(t *testing.T) {
  201. conflictArgs := [][]string{
  202. {
  203. "-initial-cluster=0=localhost:8000",
  204. "-discovery=http://example.com/abc",
  205. },
  206. {
  207. "-discovery-srv=example.com",
  208. "-discovery=http://example.com/abc",
  209. },
  210. {
  211. "-initial-cluster=0=localhost:8000",
  212. "-discovery-srv=example.com",
  213. },
  214. {
  215. "-initial-cluster=0=localhost:8000",
  216. "-discovery=http://example.com/abc",
  217. "-discovery-srv=example.com",
  218. },
  219. }
  220. for i, tt := range conflictArgs {
  221. cfg := newConfig()
  222. if err := cfg.parse(tt); err != embed.ErrConflictBootstrapFlags {
  223. t.Errorf("%d: err = %v, want %v", i, err, embed.ErrConflictBootstrapFlags)
  224. }
  225. }
  226. }
  227. func TestConfigFileConflictClusteringFlags(t *testing.T) {
  228. tests := []struct {
  229. InitialCluster string `json:"initial-cluster"`
  230. DNSCluster string `json:"discovery-srv"`
  231. Durl string `json:"discovery"`
  232. }{
  233. {
  234. InitialCluster: "0=localhost:8000",
  235. Durl: "http://example.com/abc",
  236. },
  237. {
  238. DNSCluster: "example.com",
  239. Durl: "http://example.com/abc",
  240. },
  241. {
  242. InitialCluster: "0=localhost:8000",
  243. DNSCluster: "example.com",
  244. },
  245. {
  246. InitialCluster: "0=localhost:8000",
  247. Durl: "http://example.com/abc",
  248. DNSCluster: "example.com",
  249. },
  250. }
  251. for i, tt := range tests {
  252. b, err := yaml.Marshal(&tt)
  253. if err != nil {
  254. t.Fatal(err)
  255. }
  256. tmpfile := mustCreateCfgFile(t, b)
  257. defer os.Remove(tmpfile.Name())
  258. args := []string{fmt.Sprintf("--config-file=%s", tmpfile.Name())}
  259. cfg := newConfig()
  260. if err := cfg.parse(args); err != embed.ErrConflictBootstrapFlags {
  261. t.Errorf("%d: err = %v, want %v", i, err, embed.ErrConflictBootstrapFlags)
  262. }
  263. }
  264. }
  265. func TestConfigParsingMissedAdvertiseClientURLsFlag(t *testing.T) {
  266. tests := []struct {
  267. args []string
  268. werr error
  269. }{
  270. {
  271. []string{
  272. "-initial-cluster=infra1=http://127.0.0.1:2380",
  273. "-listen-client-urls=http://127.0.0.1:2379",
  274. },
  275. embed.ErrUnsetAdvertiseClientURLsFlag,
  276. },
  277. {
  278. []string{
  279. "-discovery-srv=example.com",
  280. "-listen-client-urls=http://127.0.0.1:2379",
  281. },
  282. embed.ErrUnsetAdvertiseClientURLsFlag,
  283. },
  284. {
  285. []string{
  286. "-discovery=http://example.com/abc",
  287. "-discovery-fallback=exit",
  288. "-listen-client-urls=http://127.0.0.1:2379",
  289. },
  290. embed.ErrUnsetAdvertiseClientURLsFlag,
  291. },
  292. {
  293. []string{
  294. "-listen-client-urls=http://127.0.0.1:2379",
  295. },
  296. embed.ErrUnsetAdvertiseClientURLsFlag,
  297. },
  298. {
  299. []string{
  300. "-discovery=http://example.com/abc",
  301. "-listen-client-urls=http://127.0.0.1:2379",
  302. },
  303. nil,
  304. },
  305. {
  306. []string{
  307. "-proxy=on",
  308. "-listen-client-urls=http://127.0.0.1:2379",
  309. },
  310. nil,
  311. },
  312. {
  313. []string{
  314. "-proxy=readonly",
  315. "-listen-client-urls=http://127.0.0.1:2379",
  316. },
  317. nil,
  318. },
  319. }
  320. for i, tt := range tests {
  321. cfg := newConfig()
  322. if err := cfg.parse(tt.args); err != tt.werr {
  323. t.Errorf("%d: err = %v, want %v", i, err, tt.werr)
  324. }
  325. }
  326. }
  327. func TestConfigIsNewCluster(t *testing.T) {
  328. tests := []struct {
  329. state string
  330. wIsNew bool
  331. }{
  332. {embed.ClusterStateFlagExisting, false},
  333. {embed.ClusterStateFlagNew, true},
  334. }
  335. for i, tt := range tests {
  336. cfg := newConfig()
  337. args := []string{"--initial-cluster-state", tests[i].state}
  338. if err := cfg.parse(args); err != nil {
  339. t.Fatalf("#%d: unexpected clusterState.Set error: %v", i, err)
  340. }
  341. if g := cfg.ec.IsNewCluster(); g != tt.wIsNew {
  342. t.Errorf("#%d: isNewCluster = %v, want %v", i, g, tt.wIsNew)
  343. }
  344. }
  345. }
  346. func TestConfigIsProxy(t *testing.T) {
  347. tests := []struct {
  348. proxy string
  349. wIsProxy bool
  350. }{
  351. {proxyFlagOff, false},
  352. {proxyFlagReadonly, true},
  353. {proxyFlagOn, true},
  354. }
  355. for i, tt := range tests {
  356. cfg := newConfig()
  357. if err := cfg.cf.proxy.Set(tt.proxy); err != nil {
  358. t.Fatalf("#%d: unexpected proxy.Set error: %v", i, err)
  359. }
  360. if g := cfg.isProxy(); g != tt.wIsProxy {
  361. t.Errorf("#%d: isProxy = %v, want %v", i, g, tt.wIsProxy)
  362. }
  363. }
  364. }
  365. func TestConfigIsReadonlyProxy(t *testing.T) {
  366. tests := []struct {
  367. proxy string
  368. wIsReadonly bool
  369. }{
  370. {proxyFlagOff, false},
  371. {proxyFlagReadonly, true},
  372. {proxyFlagOn, false},
  373. }
  374. for i, tt := range tests {
  375. cfg := newConfig()
  376. if err := cfg.cf.proxy.Set(tt.proxy); err != nil {
  377. t.Fatalf("#%d: unexpected proxy.Set error: %v", i, err)
  378. }
  379. if g := cfg.isReadonlyProxy(); g != tt.wIsReadonly {
  380. t.Errorf("#%d: isReadonlyProxy = %v, want %v", i, g, tt.wIsReadonly)
  381. }
  382. }
  383. }
  384. func TestConfigShouldFallbackToProxy(t *testing.T) {
  385. tests := []struct {
  386. fallback string
  387. wFallback bool
  388. }{
  389. {fallbackFlagProxy, true},
  390. {fallbackFlagExit, false},
  391. }
  392. for i, tt := range tests {
  393. cfg := newConfig()
  394. if err := cfg.cf.fallback.Set(tt.fallback); err != nil {
  395. t.Fatalf("#%d: unexpected fallback.Set error: %v", i, err)
  396. }
  397. if g := cfg.shouldFallbackToProxy(); g != tt.wFallback {
  398. t.Errorf("#%d: shouldFallbackToProxy = %v, want %v", i, g, tt.wFallback)
  399. }
  400. }
  401. }
  402. func TestConfigFileElectionTimeout(t *testing.T) {
  403. tests := []struct {
  404. TickMs uint `json:"heartbeat-interval"`
  405. ElectionMs uint `json:"election-timeout"`
  406. errStr string
  407. }{
  408. {
  409. ElectionMs: 1000,
  410. TickMs: 800,
  411. errStr: "should be at least as 5 times as",
  412. },
  413. {
  414. ElectionMs: 60000,
  415. TickMs: 10000,
  416. errStr: "is too long, and should be set less than",
  417. },
  418. {
  419. ElectionMs: 100,
  420. TickMs: 0,
  421. errStr: "--heartbeat-interval must be >0 (set to 0ms)",
  422. },
  423. {
  424. ElectionMs: 0,
  425. TickMs: 100,
  426. errStr: "--election-timeout must be >0 (set to 0ms)",
  427. },
  428. }
  429. for i, tt := range tests {
  430. b, err := yaml.Marshal(&tt)
  431. if err != nil {
  432. t.Fatal(err)
  433. }
  434. tmpfile := mustCreateCfgFile(t, b)
  435. defer os.Remove(tmpfile.Name())
  436. args := []string{fmt.Sprintf("--config-file=%s", tmpfile.Name())}
  437. cfg := newConfig()
  438. if err := cfg.parse(args); err == nil || !strings.Contains(err.Error(), tt.errStr) {
  439. t.Errorf("%d: Wrong err = %v", i, err)
  440. }
  441. }
  442. }
  443. func mustCreateCfgFile(t *testing.T, b []byte) *os.File {
  444. tmpfile, err := ioutil.TempFile("", "servercfg")
  445. if err != nil {
  446. t.Fatal(err)
  447. }
  448. _, err = tmpfile.Write(b)
  449. if err != nil {
  450. t.Fatal(err)
  451. }
  452. err = tmpfile.Close()
  453. if err != nil {
  454. t.Fatal(err)
  455. }
  456. return tmpfile
  457. }
  458. func validateMemberFlags(t *testing.T, cfg *config) {
  459. wcfg := &embed.Config{
  460. Dir: "testdir",
  461. LPUrls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
  462. LCUrls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
  463. MaxSnapFiles: 10,
  464. MaxWalFiles: 10,
  465. Name: "testname",
  466. SnapCount: 10,
  467. }
  468. if cfg.ec.Dir != wcfg.Dir {
  469. t.Errorf("dir = %v, want %v", cfg.ec.Dir, wcfg.Dir)
  470. }
  471. if cfg.ec.MaxSnapFiles != wcfg.MaxSnapFiles {
  472. t.Errorf("maxsnap = %v, want %v", cfg.ec.MaxSnapFiles, wcfg.MaxSnapFiles)
  473. }
  474. if cfg.ec.MaxWalFiles != wcfg.MaxWalFiles {
  475. t.Errorf("maxwal = %v, want %v", cfg.ec.MaxWalFiles, wcfg.MaxWalFiles)
  476. }
  477. if cfg.ec.Name != wcfg.Name {
  478. t.Errorf("name = %v, want %v", cfg.ec.Name, wcfg.Name)
  479. }
  480. if cfg.ec.SnapCount != wcfg.SnapCount {
  481. t.Errorf("snapcount = %v, want %v", cfg.ec.SnapCount, wcfg.SnapCount)
  482. }
  483. if !reflect.DeepEqual(cfg.ec.LPUrls, wcfg.LPUrls) {
  484. t.Errorf("listen-peer-urls = %v, want %v", cfg.ec.LPUrls, wcfg.LPUrls)
  485. }
  486. if !reflect.DeepEqual(cfg.ec.LCUrls, wcfg.LCUrls) {
  487. t.Errorf("listen-client-urls = %v, want %v", cfg.ec.LCUrls, wcfg.LCUrls)
  488. }
  489. }
  490. func validateClusteringFlags(t *testing.T, cfg *config) {
  491. wcfg := newConfig()
  492. wcfg.ec.APUrls = []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}}
  493. wcfg.ec.ACUrls = []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}}
  494. wcfg.ec.ClusterState = embed.ClusterStateFlagExisting
  495. wcfg.cf.fallback.Set(fallbackFlagExit)
  496. wcfg.ec.InitialCluster = "0=http://localhost:8000"
  497. wcfg.ec.InitialClusterToken = "etcdtest"
  498. if cfg.ec.ClusterState != wcfg.ec.ClusterState {
  499. t.Errorf("clusterState = %v, want %v", cfg.ec.ClusterState, wcfg.ec.ClusterState)
  500. }
  501. if cfg.cf.fallback.String() != wcfg.cf.fallback.String() {
  502. t.Errorf("fallback = %v, want %v", cfg.cf.fallback, wcfg.cf.fallback)
  503. }
  504. if cfg.ec.InitialCluster != wcfg.ec.InitialCluster {
  505. t.Errorf("initialCluster = %v, want %v", cfg.ec.InitialCluster, wcfg.ec.InitialCluster)
  506. }
  507. if cfg.ec.InitialClusterToken != wcfg.ec.InitialClusterToken {
  508. t.Errorf("initialClusterToken = %v, want %v", cfg.ec.InitialClusterToken, wcfg.ec.InitialClusterToken)
  509. }
  510. if !reflect.DeepEqual(cfg.ec.APUrls, wcfg.ec.APUrls) {
  511. t.Errorf("initial-advertise-peer-urls = %v, want %v", cfg.ec.LPUrls, wcfg.ec.LPUrls)
  512. }
  513. if !reflect.DeepEqual(cfg.ec.ACUrls, wcfg.ec.ACUrls) {
  514. t.Errorf("advertise-client-urls = %v, want %v", cfg.ec.LCUrls, wcfg.ec.LCUrls)
  515. }
  516. }
  517. func validateOtherFlags(t *testing.T, cfg *config) {
  518. wcfg := newConfig()
  519. wcfg.cf.proxy.Set(proxyFlagReadonly)
  520. if cfg.cf.proxy.String() != wcfg.cf.proxy.String() {
  521. t.Errorf("proxy = %v, want %v", cfg.cf.proxy, wcfg.cf.proxy)
  522. }
  523. }