stanza_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. // Copyright 2015 CoreOS, Inc.
  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 network
  15. import (
  16. "net"
  17. "reflect"
  18. "strings"
  19. "testing"
  20. )
  21. func TestSplitStanzasNoParent(t *testing.T) {
  22. in := []string{"test"}
  23. e := "missing stanza start"
  24. _, err := splitStanzas(in)
  25. if err == nil || !strings.HasPrefix(err.Error(), e) {
  26. t.Fatalf("bad error for splitStanzas(%q): got %q, want %q", in, err, e)
  27. }
  28. }
  29. func TestBadParseStanzas(t *testing.T) {
  30. for in, e := range map[string]string{
  31. "": "missing stanza start",
  32. "iface": "malformed stanza start",
  33. "allow-?? unknown": "unknown stanza",
  34. } {
  35. _, err := parseStanzas([]string{in})
  36. if err == nil || !strings.HasPrefix(err.Error(), e) {
  37. t.Fatalf("bad error for parseStanzas(%q): got %q, want %q", in, err, e)
  38. }
  39. }
  40. }
  41. func TestBadParseInterfaceStanza(t *testing.T) {
  42. for _, tt := range []struct {
  43. in []string
  44. opts []string
  45. e string
  46. }{
  47. {[]string{}, nil, "incorrect number of attributes"},
  48. {[]string{"eth", "inet", "invalid"}, nil, "invalid config method"},
  49. {[]string{"eth", "inet", "static"}, []string{"address 192.168.1.100"}, "malformed static network config"},
  50. {[]string{"eth", "inet", "static"}, []string{"netmask 255.255.255.0"}, "malformed static network config"},
  51. {[]string{"eth", "inet", "static"}, []string{"address invalid", "netmask 255.255.255.0"}, "malformed static network config"},
  52. {[]string{"eth", "inet", "static"}, []string{"address 192.168.1.100", "netmask invalid"}, "malformed static network config"},
  53. {[]string{"eth", "inet", "static"}, []string{"address 192.168.1.100", "netmask 255.255.255.0", "hwaddress ether NotAnAddress"}, "malformed hwaddress option"},
  54. {[]string{"eth", "inet", "dhcp"}, []string{"hwaddress ether NotAnAddress"}, "malformed hwaddress option"},
  55. } {
  56. _, err := parseInterfaceStanza(tt.in, tt.opts)
  57. if err == nil || !strings.HasPrefix(err.Error(), tt.e) {
  58. t.Fatalf("bad error parsing interface stanza %q: got %q, want %q", tt.in, err.Error(), tt.e)
  59. }
  60. }
  61. }
  62. func TestBadParseVLANStanzas(t *testing.T) {
  63. conf := configMethodManual{}
  64. options := map[string][]string{}
  65. for _, in := range []string{"myvlan", "eth.vlan"} {
  66. _, err := parseVLANStanza(in, conf, nil, options)
  67. if err == nil || !strings.HasPrefix(err.Error(), "malformed vlan name") {
  68. t.Fatalf("did not error on bad vlan %q", in)
  69. }
  70. }
  71. }
  72. func TestSplitStanzas(t *testing.T) {
  73. expect := [][]string{
  74. {"auto lo"},
  75. {"iface eth1", "option: 1"},
  76. {"mapping"},
  77. {"allow-"},
  78. }
  79. lines := make([]string, 0, 5)
  80. for _, stanza := range expect {
  81. for _, line := range stanza {
  82. lines = append(lines, line)
  83. }
  84. }
  85. stanzas, err := splitStanzas(lines)
  86. if err != nil {
  87. t.FailNow()
  88. }
  89. for i, stanza := range stanzas {
  90. if len(stanza) != len(expect[i]) {
  91. t.FailNow()
  92. }
  93. for j, line := range stanza {
  94. if line != expect[i][j] {
  95. t.FailNow()
  96. }
  97. }
  98. }
  99. }
  100. func TestParseStanzaNil(t *testing.T) {
  101. defer func() {
  102. if r := recover(); r == nil {
  103. t.Fatal("parseStanza(nil) did not panic")
  104. }
  105. }()
  106. parseStanza(nil)
  107. }
  108. func TestParseStanzaSuccess(t *testing.T) {
  109. for _, in := range []string{
  110. "auto a",
  111. "iface a inet manual",
  112. } {
  113. if _, err := parseStanza([]string{in}); err != nil {
  114. t.Fatalf("unexpected error parsing stanza %q: %s", in, err)
  115. }
  116. }
  117. }
  118. func TestParseAutoStanza(t *testing.T) {
  119. interfaces := []string{"test", "attribute"}
  120. stanza, err := parseAutoStanza(interfaces, nil)
  121. if err != nil {
  122. t.Fatalf("unexpected error parsing auto stanza %q: %s", interfaces, err)
  123. }
  124. if !reflect.DeepEqual(stanza.interfaces, interfaces) {
  125. t.FailNow()
  126. }
  127. }
  128. func TestParseBondStanzaNoSlaves(t *testing.T) {
  129. bond, err := parseBondStanza("", nil, nil, map[string][]string{})
  130. if err != nil {
  131. t.FailNow()
  132. }
  133. if bond.options["bond-slaves"] != nil {
  134. t.FailNow()
  135. }
  136. }
  137. func TestParseBondStanza(t *testing.T) {
  138. conf := configMethodManual{}
  139. options := map[string][]string{
  140. "bond-slaves": {"1", "2"},
  141. }
  142. bond, err := parseBondStanza("test", conf, nil, options)
  143. if err != nil {
  144. t.FailNow()
  145. }
  146. if bond.name != "test" {
  147. t.FailNow()
  148. }
  149. if bond.kind != interfaceBond {
  150. t.FailNow()
  151. }
  152. if bond.configMethod != conf {
  153. t.FailNow()
  154. }
  155. }
  156. func TestParsePhysicalStanza(t *testing.T) {
  157. conf := configMethodManual{}
  158. options := map[string][]string{
  159. "a": {"1", "2"},
  160. "b": {"1"},
  161. }
  162. physical, err := parsePhysicalStanza("test", conf, nil, options)
  163. if err != nil {
  164. t.FailNow()
  165. }
  166. if physical.name != "test" {
  167. t.FailNow()
  168. }
  169. if physical.kind != interfacePhysical {
  170. t.FailNow()
  171. }
  172. if physical.configMethod != conf {
  173. t.FailNow()
  174. }
  175. if !reflect.DeepEqual(physical.options, options) {
  176. t.FailNow()
  177. }
  178. }
  179. func TestParseVLANStanzas(t *testing.T) {
  180. conf := configMethodManual{}
  181. options := map[string][]string{}
  182. for _, in := range []string{"vlan25", "eth.25"} {
  183. vlan, err := parseVLANStanza(in, conf, nil, options)
  184. if err != nil {
  185. t.Fatalf("unexpected error from parseVLANStanza(%q): %s", in, err)
  186. }
  187. if !reflect.DeepEqual(vlan.options["id"], []string{"25"}) {
  188. t.FailNow()
  189. }
  190. }
  191. }
  192. func TestParseInterfaceStanzaStaticAddress(t *testing.T) {
  193. options := []string{"address 192.168.1.100", "netmask 255.255.255.0"}
  194. expect := []net.IPNet{
  195. {
  196. IP: net.IPv4(192, 168, 1, 100),
  197. Mask: net.IPv4Mask(255, 255, 255, 0),
  198. },
  199. }
  200. iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, options)
  201. if err != nil {
  202. t.FailNow()
  203. }
  204. static, ok := iface.configMethod.(configMethodStatic)
  205. if !ok {
  206. t.FailNow()
  207. }
  208. if !reflect.DeepEqual(static.addresses, expect) {
  209. t.FailNow()
  210. }
  211. }
  212. func TestParseInterfaceStanzaStaticGateway(t *testing.T) {
  213. options := []string{"address 192.168.1.100", "netmask 255.255.255.0", "gateway 192.168.1.1"}
  214. expect := []route{
  215. {
  216. destination: net.IPNet{
  217. IP: net.IPv4(0, 0, 0, 0),
  218. Mask: net.IPv4Mask(0, 0, 0, 0),
  219. },
  220. gateway: net.IPv4(192, 168, 1, 1),
  221. },
  222. }
  223. iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, options)
  224. if err != nil {
  225. t.FailNow()
  226. }
  227. static, ok := iface.configMethod.(configMethodStatic)
  228. if !ok {
  229. t.FailNow()
  230. }
  231. if !reflect.DeepEqual(static.routes, expect) {
  232. t.FailNow()
  233. }
  234. }
  235. func TestParseInterfaceStanzaStaticDNS(t *testing.T) {
  236. options := []string{"address 192.168.1.100", "netmask 255.255.255.0", "dns-nameservers 192.168.1.10 192.168.1.11 192.168.1.12"}
  237. expect := []net.IP{
  238. net.IPv4(192, 168, 1, 10),
  239. net.IPv4(192, 168, 1, 11),
  240. net.IPv4(192, 168, 1, 12),
  241. }
  242. iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, options)
  243. if err != nil {
  244. t.FailNow()
  245. }
  246. static, ok := iface.configMethod.(configMethodStatic)
  247. if !ok {
  248. t.FailNow()
  249. }
  250. if !reflect.DeepEqual(static.nameservers, expect) {
  251. t.FailNow()
  252. }
  253. }
  254. func TestBadParseInterfaceStanzasStaticPostUp(t *testing.T) {
  255. for _, in := range []string{
  256. "post-up invalid",
  257. "post-up route add",
  258. "post-up route add -net",
  259. "post-up route add gw",
  260. "post-up route add netmask",
  261. "gateway",
  262. "gateway 192.168.1.1 192.168.1.2",
  263. } {
  264. options := []string{"address 192.168.1.100", "netmask 255.255.255.0", in}
  265. iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, options)
  266. if err != nil {
  267. t.Fatalf("parseInterfaceStanza with options %s got unexpected error", options)
  268. }
  269. static, ok := iface.configMethod.(configMethodStatic)
  270. if !ok {
  271. t.Fatalf("parseInterfaceStanza with options %s did not return configMethodStatic", options)
  272. }
  273. if len(static.routes) != 0 {
  274. t.Fatalf("parseInterfaceStanza with options %s did not return zero-length static routes", options)
  275. }
  276. }
  277. }
  278. func TestParseInterfaceStanzaStaticPostUp(t *testing.T) {
  279. for _, tt := range []struct {
  280. options []string
  281. expect []route
  282. }{
  283. {
  284. options: []string{
  285. "address 192.168.1.100",
  286. "netmask 255.255.255.0",
  287. "post-up route add gw 192.168.1.1 -net 192.168.1.0 netmask 255.255.255.0",
  288. },
  289. expect: []route{
  290. {
  291. destination: net.IPNet{
  292. IP: net.IPv4(192, 168, 1, 0),
  293. Mask: net.IPv4Mask(255, 255, 255, 0),
  294. },
  295. gateway: net.IPv4(192, 168, 1, 1),
  296. },
  297. },
  298. },
  299. {
  300. options: []string{
  301. "address 192.168.1.100",
  302. "netmask 255.255.255.0",
  303. "post-up route add gw 192.168.1.1 -net 192.168.1.0/24 || true",
  304. },
  305. expect: []route{
  306. {
  307. destination: func() net.IPNet {
  308. _, net, err := net.ParseCIDR("192.168.1.0/24")
  309. if err != nil {
  310. panic(err)
  311. }
  312. return *net
  313. }(),
  314. gateway: net.IPv4(192, 168, 1, 1),
  315. },
  316. },
  317. },
  318. } {
  319. iface, err := parseInterfaceStanza([]string{"eth", "inet", "static"}, tt.options)
  320. if err != nil {
  321. t.Fatalf("bad error (%+v): want nil, got %s\n", tt, err)
  322. }
  323. static, ok := iface.configMethod.(configMethodStatic)
  324. if !ok {
  325. t.Fatalf("bad config method (%+v): want configMethodStatic, got %T\n", tt, iface.configMethod)
  326. }
  327. if !reflect.DeepEqual(static.routes, tt.expect) {
  328. t.Fatalf("bad routes (%+v): want %#v, got %#v\n", tt, tt.expect, static.routes)
  329. }
  330. }
  331. }
  332. func TestParseInterfaceStanzaLoopback(t *testing.T) {
  333. iface, err := parseInterfaceStanza([]string{"eth", "inet", "loopback"}, nil)
  334. if err != nil {
  335. t.FailNow()
  336. }
  337. if _, ok := iface.configMethod.(configMethodLoopback); !ok {
  338. t.FailNow()
  339. }
  340. }
  341. func TestParseInterfaceStanzaManual(t *testing.T) {
  342. iface, err := parseInterfaceStanza([]string{"eth", "inet", "manual"}, nil)
  343. if err != nil {
  344. t.FailNow()
  345. }
  346. if _, ok := iface.configMethod.(configMethodManual); !ok {
  347. t.FailNow()
  348. }
  349. }
  350. func TestParseInterfaceStanzaDHCP(t *testing.T) {
  351. iface, err := parseInterfaceStanza([]string{"eth", "inet", "dhcp"}, nil)
  352. if err != nil {
  353. t.FailNow()
  354. }
  355. if _, ok := iface.configMethod.(configMethodDHCP); !ok {
  356. t.FailNow()
  357. }
  358. }
  359. func TestParseInterfaceStanzaPostUpOption(t *testing.T) {
  360. options := []string{
  361. "post-up",
  362. "post-up 1 2",
  363. "post-up 3 4",
  364. }
  365. iface, err := parseInterfaceStanza([]string{"eth", "inet", "manual"}, options)
  366. if err != nil {
  367. t.FailNow()
  368. }
  369. if !reflect.DeepEqual(iface.options["post-up"], []string{"1 2", "3 4"}) {
  370. t.Log(iface.options["post-up"])
  371. t.FailNow()
  372. }
  373. }
  374. func TestParseInterfaceStanzaPreDownOption(t *testing.T) {
  375. options := []string{
  376. "pre-down",
  377. "pre-down 3",
  378. "pre-down 4",
  379. }
  380. iface, err := parseInterfaceStanza([]string{"eth", "inet", "manual"}, options)
  381. if err != nil {
  382. t.FailNow()
  383. }
  384. if !reflect.DeepEqual(iface.options["pre-down"], []string{"3", "4"}) {
  385. t.Log(iface.options["pre-down"])
  386. t.FailNow()
  387. }
  388. }
  389. func TestParseInterfaceStanzaEmptyOption(t *testing.T) {
  390. options := []string{
  391. "test",
  392. }
  393. iface, err := parseInterfaceStanza([]string{"eth", "inet", "manual"}, options)
  394. if err != nil {
  395. t.FailNow()
  396. }
  397. if !reflect.DeepEqual(iface.options["test"], []string{}) {
  398. t.FailNow()
  399. }
  400. }
  401. func TestParseInterfaceStanzaOptions(t *testing.T) {
  402. options := []string{
  403. "test1 1",
  404. "test2 2 3",
  405. "test1 5 6",
  406. }
  407. iface, err := parseInterfaceStanza([]string{"eth", "inet", "manual"}, options)
  408. if err != nil {
  409. t.FailNow()
  410. }
  411. if !reflect.DeepEqual(iface.options["test1"], []string{"5", "6"}) {
  412. t.Log(iface.options["test1"])
  413. t.FailNow()
  414. }
  415. if !reflect.DeepEqual(iface.options["test2"], []string{"2", "3"}) {
  416. t.Log(iface.options["test2"])
  417. t.FailNow()
  418. }
  419. }
  420. func TestParseInterfaceStanzaHwaddress(t *testing.T) {
  421. for _, tt := range []struct {
  422. attr []string
  423. opt []string
  424. hw net.HardwareAddr
  425. }{
  426. {
  427. []string{"mybond", "inet", "dhcp"},
  428. []string{},
  429. nil,
  430. },
  431. {
  432. []string{"mybond", "inet", "dhcp"},
  433. []string{"hwaddress ether 00:01:02:03:04:05"},
  434. net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5}),
  435. },
  436. {
  437. []string{"mybond", "inet", "static"},
  438. []string{"hwaddress ether 00:01:02:03:04:05", "address 192.168.1.100", "netmask 255.255.255.0"},
  439. net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5}),
  440. },
  441. } {
  442. iface, err := parseInterfaceStanza(tt.attr, tt.opt)
  443. if err != nil {
  444. t.Fatalf("error in parseInterfaceStanza (%q, %q): %q", tt.attr, tt.opt, err)
  445. }
  446. switch c := iface.configMethod.(type) {
  447. case configMethodStatic:
  448. if !reflect.DeepEqual(c.hwaddress, tt.hw) {
  449. t.Fatalf("bad hwaddress (%q, %q): got %q, want %q", tt.attr, tt.opt, c.hwaddress, tt.hw)
  450. }
  451. case configMethodDHCP:
  452. if !reflect.DeepEqual(c.hwaddress, tt.hw) {
  453. t.Fatalf("bad hwaddress (%q, %q): got %q, want %q", tt.attr, tt.opt, c.hwaddress, tt.hw)
  454. }
  455. }
  456. }
  457. }
  458. func TestParseInterfaceStanzaBond(t *testing.T) {
  459. iface, err := parseInterfaceStanza([]string{"mybond", "inet", "manual"}, []string{"bond-slaves eth"})
  460. if err != nil {
  461. t.FailNow()
  462. }
  463. if iface.kind != interfaceBond {
  464. t.FailNow()
  465. }
  466. }
  467. func TestParseInterfaceStanzaVLANName(t *testing.T) {
  468. iface, err := parseInterfaceStanza([]string{"eth0.1", "inet", "manual"}, nil)
  469. if err != nil {
  470. t.FailNow()
  471. }
  472. if iface.kind != interfaceVLAN {
  473. t.FailNow()
  474. }
  475. }
  476. func TestParseInterfaceStanzaVLANOption(t *testing.T) {
  477. iface, err := parseInterfaceStanza([]string{"vlan1", "inet", "manual"}, []string{"vlan_raw_device eth"})
  478. if err != nil {
  479. t.FailNow()
  480. }
  481. if iface.kind != interfaceVLAN {
  482. t.FailNow()
  483. }
  484. }
  485. func TestParseStanzasNone(t *testing.T) {
  486. stanzas, err := parseStanzas(nil)
  487. if err != nil {
  488. t.FailNow()
  489. }
  490. if len(stanzas) != 0 {
  491. t.FailNow()
  492. }
  493. }
  494. func TestParseStanzas(t *testing.T) {
  495. lines := []string{
  496. "auto lo",
  497. "iface lo inet loopback",
  498. "iface eth1 inet manual",
  499. "iface eth2 inet manual",
  500. "iface eth3 inet manual",
  501. "auto eth1 eth3",
  502. }
  503. expect := []stanza{
  504. &stanzaAuto{
  505. interfaces: []string{"lo"},
  506. },
  507. &stanzaInterface{
  508. name: "lo",
  509. kind: interfacePhysical,
  510. auto: true,
  511. configMethod: configMethodLoopback{},
  512. options: map[string][]string{},
  513. },
  514. &stanzaInterface{
  515. name: "eth1",
  516. kind: interfacePhysical,
  517. auto: true,
  518. configMethod: configMethodManual{},
  519. options: map[string][]string{},
  520. },
  521. &stanzaInterface{
  522. name: "eth2",
  523. kind: interfacePhysical,
  524. auto: false,
  525. configMethod: configMethodManual{},
  526. options: map[string][]string{},
  527. },
  528. &stanzaInterface{
  529. name: "eth3",
  530. kind: interfacePhysical,
  531. auto: true,
  532. configMethod: configMethodManual{},
  533. options: map[string][]string{},
  534. },
  535. &stanzaAuto{
  536. interfaces: []string{"eth1", "eth3"},
  537. },
  538. }
  539. stanzas, err := parseStanzas(lines)
  540. if err != err {
  541. t.FailNow()
  542. }
  543. if !reflect.DeepEqual(stanzas, expect) {
  544. t.FailNow()
  545. }
  546. }