server_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package app
  14. import (
  15. "errors"
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "path/filepath"
  20. "reflect"
  21. "runtime"
  22. "strings"
  23. "testing"
  24. "time"
  25. "github.com/stretchr/testify/assert"
  26. utilpointer "k8s.io/utils/pointer"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/util/diff"
  29. componentbaseconfig "k8s.io/component-base/config"
  30. kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
  31. "k8s.io/kubernetes/pkg/util/configz"
  32. )
  33. // This test verifies that NewProxyServer does not crash when CleanupAndExit is true.
  34. func TestProxyServerWithCleanupAndExit(t *testing.T) {
  35. // Each bind address below is a separate test case
  36. bindAddresses := []string{
  37. "0.0.0.0",
  38. "::",
  39. }
  40. for _, addr := range bindAddresses {
  41. options := NewOptions()
  42. options.config = &kubeproxyconfig.KubeProxyConfiguration{
  43. BindAddress: addr,
  44. }
  45. options.CleanupAndExit = true
  46. proxyserver, err := NewProxyServer(options)
  47. assert.Nil(t, err, "unexpected error in NewProxyServer, addr: %s", addr)
  48. assert.NotNil(t, proxyserver, "nil proxy server obj, addr: %s", addr)
  49. assert.NotNil(t, proxyserver.IptInterface, "nil iptables intf, addr: %s", addr)
  50. // Clean up config for next test case
  51. configz.Delete(kubeproxyconfig.GroupName)
  52. }
  53. }
  54. func TestGetConntrackMax(t *testing.T) {
  55. ncores := runtime.NumCPU()
  56. testCases := []struct {
  57. min int32
  58. maxPerCore int32
  59. expected int
  60. err string
  61. }{
  62. {
  63. expected: 0,
  64. },
  65. {
  66. maxPerCore: 67890, // use this if Max is 0
  67. min: 1, // avoid 0 default
  68. expected: 67890 * ncores,
  69. },
  70. {
  71. maxPerCore: 1, // ensure that Min is considered
  72. min: 123456,
  73. expected: 123456,
  74. },
  75. {
  76. maxPerCore: 0, // leave system setting
  77. min: 123456,
  78. expected: 0,
  79. },
  80. }
  81. for i, tc := range testCases {
  82. cfg := kubeproxyconfig.KubeProxyConntrackConfiguration{
  83. Min: utilpointer.Int32Ptr(tc.min),
  84. MaxPerCore: utilpointer.Int32Ptr(tc.maxPerCore),
  85. }
  86. x, e := getConntrackMax(cfg)
  87. if e != nil {
  88. if tc.err == "" {
  89. t.Errorf("[%d] unexpected error: %v", i, e)
  90. } else if !strings.Contains(e.Error(), tc.err) {
  91. t.Errorf("[%d] expected an error containing %q: %v", i, tc.err, e)
  92. }
  93. } else if x != tc.expected {
  94. t.Errorf("[%d] expected %d, got %d", i, tc.expected, x)
  95. }
  96. }
  97. }
  98. // TestLoadConfig tests proper operation of loadConfig()
  99. func TestLoadConfig(t *testing.T) {
  100. yamlTemplate := `apiVersion: kubeproxy.config.k8s.io/v1alpha1
  101. bindAddress: %s
  102. clientConnection:
  103. acceptContentTypes: "abc"
  104. burst: 100
  105. contentType: content-type
  106. kubeconfig: "/path/to/kubeconfig"
  107. qps: 7
  108. clusterCIDR: "%s"
  109. configSyncPeriod: 15s
  110. conntrack:
  111. maxPerCore: 2
  112. min: 1
  113. tcpCloseWaitTimeout: 10s
  114. tcpEstablishedTimeout: 20s
  115. healthzBindAddress: "%s"
  116. hostnameOverride: "foo"
  117. iptables:
  118. masqueradeAll: true
  119. masqueradeBit: 17
  120. minSyncPeriod: 10s
  121. syncPeriod: 60s
  122. ipvs:
  123. minSyncPeriod: 10s
  124. syncPeriod: 60s
  125. excludeCIDRs:
  126. - "10.20.30.40/16"
  127. - "fd00:1::0/64"
  128. kind: KubeProxyConfiguration
  129. metricsBindAddress: "%s"
  130. mode: "%s"
  131. oomScoreAdj: 17
  132. portRange: "2-7"
  133. udpIdleTimeout: 123ms
  134. nodePortAddresses:
  135. - "10.20.30.40/16"
  136. - "fd00:1::0/64"
  137. `
  138. testCases := []struct {
  139. name string
  140. mode string
  141. bindAddress string
  142. clusterCIDR string
  143. healthzBindAddress string
  144. metricsBindAddress string
  145. extraConfig string
  146. }{
  147. {
  148. name: "iptables mode, IPv4 all-zeros bind address",
  149. mode: "iptables",
  150. bindAddress: "0.0.0.0",
  151. clusterCIDR: "1.2.3.0/24",
  152. healthzBindAddress: "1.2.3.4:12345",
  153. metricsBindAddress: "2.3.4.5:23456",
  154. },
  155. {
  156. name: "iptables mode, non-zeros IPv4 config",
  157. mode: "iptables",
  158. bindAddress: "9.8.7.6",
  159. clusterCIDR: "1.2.3.0/24",
  160. healthzBindAddress: "1.2.3.4:12345",
  161. metricsBindAddress: "2.3.4.5:23456",
  162. },
  163. {
  164. // Test for 'bindAddress: "::"' (IPv6 all-zeros) in kube-proxy
  165. // config file. The user will need to put quotes around '::' since
  166. // 'bindAddress: ::' is invalid yaml syntax.
  167. name: "iptables mode, IPv6 \"::\" bind address",
  168. mode: "iptables",
  169. bindAddress: "\"::\"",
  170. clusterCIDR: "fd00:1::0/64",
  171. healthzBindAddress: "[fd00:1::5]:12345",
  172. metricsBindAddress: "[fd00:2::5]:23456",
  173. },
  174. {
  175. // Test for 'bindAddress: "[::]"' (IPv6 all-zeros in brackets)
  176. // in kube-proxy config file. The user will need to use
  177. // surrounding quotes here since 'bindAddress: [::]' is invalid
  178. // yaml syntax.
  179. name: "iptables mode, IPv6 \"[::]\" bind address",
  180. mode: "iptables",
  181. bindAddress: "\"[::]\"",
  182. clusterCIDR: "fd00:1::0/64",
  183. healthzBindAddress: "[fd00:1::5]:12345",
  184. metricsBindAddress: "[fd00:2::5]:23456",
  185. },
  186. {
  187. // Test for 'bindAddress: ::0' (another form of IPv6 all-zeros).
  188. // No surrounding quotes are required around '::0'.
  189. name: "iptables mode, IPv6 ::0 bind address",
  190. mode: "iptables",
  191. bindAddress: "::0",
  192. clusterCIDR: "fd00:1::0/64",
  193. healthzBindAddress: "[fd00:1::5]:12345",
  194. metricsBindAddress: "[fd00:2::5]:23456",
  195. },
  196. {
  197. name: "ipvs mode, IPv6 config",
  198. mode: "ipvs",
  199. bindAddress: "2001:db8::1",
  200. clusterCIDR: "fd00:1::0/64",
  201. healthzBindAddress: "[fd00:1::5]:12345",
  202. metricsBindAddress: "[fd00:2::5]:23456",
  203. },
  204. {
  205. // Test for unknown field within config.
  206. // For v1alpha1 a lenient path is implemented and will throw a
  207. // strict decoding warning instead of failing to load
  208. name: "unknown field",
  209. mode: "iptables",
  210. bindAddress: "9.8.7.6",
  211. clusterCIDR: "1.2.3.0/24",
  212. healthzBindAddress: "1.2.3.4:12345",
  213. metricsBindAddress: "2.3.4.5:23456",
  214. extraConfig: "foo: bar",
  215. },
  216. {
  217. // Test for duplicate field within config.
  218. // For v1alpha1 a lenient path is implemented and will throw a
  219. // strict decoding warning instead of failing to load
  220. name: "duplicate field",
  221. mode: "iptables",
  222. bindAddress: "9.8.7.6",
  223. clusterCIDR: "1.2.3.0/24",
  224. healthzBindAddress: "1.2.3.4:12345",
  225. metricsBindAddress: "2.3.4.5:23456",
  226. extraConfig: "bindAddress: 9.8.7.6",
  227. },
  228. }
  229. for _, tc := range testCases {
  230. expBindAddr := tc.bindAddress
  231. if tc.bindAddress[0] == '"' {
  232. // Surrounding double quotes will get stripped by the yaml parser.
  233. expBindAddr = expBindAddr[1 : len(tc.bindAddress)-1]
  234. }
  235. expected := &kubeproxyconfig.KubeProxyConfiguration{
  236. BindAddress: expBindAddr,
  237. ClientConnection: componentbaseconfig.ClientConnectionConfiguration{
  238. AcceptContentTypes: "abc",
  239. Burst: 100,
  240. ContentType: "content-type",
  241. Kubeconfig: "/path/to/kubeconfig",
  242. QPS: 7,
  243. },
  244. ClusterCIDR: tc.clusterCIDR,
  245. ConfigSyncPeriod: metav1.Duration{Duration: 15 * time.Second},
  246. Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
  247. MaxPerCore: utilpointer.Int32Ptr(2),
  248. Min: utilpointer.Int32Ptr(1),
  249. TCPCloseWaitTimeout: &metav1.Duration{Duration: 10 * time.Second},
  250. TCPEstablishedTimeout: &metav1.Duration{Duration: 20 * time.Second},
  251. },
  252. FeatureGates: map[string]bool{},
  253. HealthzBindAddress: tc.healthzBindAddress,
  254. HostnameOverride: "foo",
  255. IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
  256. MasqueradeAll: true,
  257. MasqueradeBit: utilpointer.Int32Ptr(17),
  258. MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second},
  259. SyncPeriod: metav1.Duration{Duration: 60 * time.Second},
  260. },
  261. IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{
  262. MinSyncPeriod: metav1.Duration{Duration: 10 * time.Second},
  263. SyncPeriod: metav1.Duration{Duration: 60 * time.Second},
  264. ExcludeCIDRs: []string{"10.20.30.40/16", "fd00:1::0/64"},
  265. },
  266. MetricsBindAddress: tc.metricsBindAddress,
  267. Mode: kubeproxyconfig.ProxyMode(tc.mode),
  268. OOMScoreAdj: utilpointer.Int32Ptr(17),
  269. PortRange: "2-7",
  270. UDPIdleTimeout: metav1.Duration{Duration: 123 * time.Millisecond},
  271. NodePortAddresses: []string{"10.20.30.40/16", "fd00:1::0/64"},
  272. }
  273. options := NewOptions()
  274. baseYAML := fmt.Sprintf(
  275. yamlTemplate, tc.bindAddress, tc.clusterCIDR,
  276. tc.healthzBindAddress, tc.metricsBindAddress, tc.mode)
  277. // Append additional configuration to the base yaml template
  278. yaml := fmt.Sprintf("%s\n%s", baseYAML, tc.extraConfig)
  279. config, err := options.loadConfig([]byte(yaml))
  280. assert.NoError(t, err, "unexpected error for %s: %v", tc.name, err)
  281. if !reflect.DeepEqual(expected, config) {
  282. t.Fatalf("unexpected config for %s, diff = %s", tc.name, diff.ObjectDiff(config, expected))
  283. }
  284. }
  285. }
  286. // TestLoadConfigFailures tests failure modes for loadConfig()
  287. func TestLoadConfigFailures(t *testing.T) {
  288. // TODO(phenixblue): Uncomment below template when v1alpha2+ of kube-proxy config is
  289. // released with strict decoding. These associated tests will fail with
  290. // the lenient codec and only one config API version.
  291. /*
  292. yamlTemplate := `bindAddress: 0.0.0.0
  293. clusterCIDR: "1.2.3.0/24"
  294. configSyncPeriod: 15s
  295. kind: KubeProxyConfiguration`
  296. */
  297. testCases := []struct {
  298. name string
  299. config string
  300. expErr string
  301. checkFn func(err error) bool
  302. }{
  303. {
  304. name: "Decode error test",
  305. config: "Twas bryllyg, and ye slythy toves",
  306. expErr: "could not find expected ':'",
  307. },
  308. {
  309. name: "Bad config type test",
  310. config: "kind: KubeSchedulerConfiguration",
  311. expErr: "no kind",
  312. },
  313. {
  314. name: "Missing quotes around :: bindAddress",
  315. config: "bindAddress: ::",
  316. expErr: "mapping values are not allowed in this context",
  317. },
  318. // TODO(phenixblue): Uncomment below tests when v1alpha2+ of kube-proxy config is
  319. // released with strict decoding. These tests will fail with the
  320. // lenient codec and only one config API version.
  321. /*
  322. {
  323. name: "Duplicate fields",
  324. config: fmt.Sprintf("%s\nbindAddress: 1.2.3.4", yamlTemplate),
  325. checkFn: kuberuntime.IsStrictDecodingError,
  326. },
  327. {
  328. name: "Unknown field",
  329. config: fmt.Sprintf("%s\nfoo: bar", yamlTemplate),
  330. checkFn: kuberuntime.IsStrictDecodingError,
  331. },
  332. */
  333. }
  334. version := "apiVersion: kubeproxy.config.k8s.io/v1alpha1"
  335. for _, tc := range testCases {
  336. t.Run(tc.name, func(t *testing.T) {
  337. options := NewOptions()
  338. config := fmt.Sprintf("%s\n%s", version, tc.config)
  339. _, err := options.loadConfig([]byte(config))
  340. if assert.Error(t, err, tc.name) {
  341. if tc.expErr != "" {
  342. assert.Contains(t, err.Error(), tc.expErr)
  343. }
  344. if tc.checkFn != nil {
  345. assert.True(t, tc.checkFn(err), tc.name)
  346. }
  347. }
  348. })
  349. }
  350. }
  351. // TestProcessHostnameOverrideFlag tests processing hostname-override arg
  352. func TestProcessHostnameOverrideFlag(t *testing.T) {
  353. testCases := []struct {
  354. name string
  355. hostnameOverrideFlag string
  356. expectedHostname string
  357. expectError bool
  358. }{
  359. {
  360. name: "Hostname from config file",
  361. hostnameOverrideFlag: "",
  362. expectedHostname: "foo",
  363. expectError: false,
  364. },
  365. {
  366. name: "Hostname from flag",
  367. hostnameOverrideFlag: " bar ",
  368. expectedHostname: "bar",
  369. expectError: false,
  370. },
  371. {
  372. name: "Hostname is space",
  373. hostnameOverrideFlag: " ",
  374. expectError: true,
  375. },
  376. }
  377. for _, tc := range testCases {
  378. t.Run(tc.name, func(t *testing.T) {
  379. options := NewOptions()
  380. options.config = &kubeproxyconfig.KubeProxyConfiguration{
  381. HostnameOverride: "foo",
  382. }
  383. options.hostnameOverride = tc.hostnameOverrideFlag
  384. err := options.processHostnameOverrideFlag()
  385. if tc.expectError {
  386. if err == nil {
  387. t.Fatalf("should error for this case %s", tc.name)
  388. }
  389. } else {
  390. assert.NoError(t, err, "unexpected error %v", err)
  391. if tc.expectedHostname != options.config.HostnameOverride {
  392. t.Fatalf("expected hostname: %s, but got: %s", tc.expectedHostname, options.config.HostnameOverride)
  393. }
  394. }
  395. })
  396. }
  397. }
  398. func TestConfigChange(t *testing.T) {
  399. setUp := func() (*os.File, string, error) {
  400. tempDir, err := ioutil.TempDir("", "kubeproxy-config-change")
  401. if err != nil {
  402. return nil, "", fmt.Errorf("unable to create temporary directory: %v", err)
  403. }
  404. fullPath := filepath.Join(tempDir, "kube-proxy-config")
  405. file, err := os.Create(fullPath)
  406. if err != nil {
  407. return nil, "", fmt.Errorf("unexpected error when creating temp file: %v", err)
  408. }
  409. _, err = file.WriteString(`apiVersion: kubeproxy.config.k8s.io/v1alpha1
  410. bindAddress: 0.0.0.0
  411. clientConnection:
  412. acceptContentTypes: ""
  413. burst: 10
  414. contentType: application/vnd.kubernetes.protobuf
  415. kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
  416. qps: 5
  417. clusterCIDR: 10.244.0.0/16
  418. configSyncPeriod: 15m0s
  419. conntrack:
  420. maxPerCore: 32768
  421. min: 131072
  422. tcpCloseWaitTimeout: 1h0m0s
  423. tcpEstablishedTimeout: 24h0m0s
  424. enableProfiling: false
  425. healthzBindAddress: 0.0.0.0:10256
  426. hostnameOverride: ""
  427. iptables:
  428. masqueradeAll: false
  429. masqueradeBit: 14
  430. minSyncPeriod: 0s
  431. syncPeriod: 30s
  432. ipvs:
  433. excludeCIDRs: null
  434. minSyncPeriod: 0s
  435. scheduler: ""
  436. syncPeriod: 30s
  437. kind: KubeProxyConfiguration
  438. metricsBindAddress: 127.0.0.1:10249
  439. mode: ""
  440. nodePortAddresses: null
  441. oomScoreAdj: -999
  442. portRange: ""
  443. udpIdleTimeout: 250ms`)
  444. if err != nil {
  445. return nil, "", fmt.Errorf("unexpected error when writing content to temp kube-proxy config file: %v", err)
  446. }
  447. return file, tempDir, nil
  448. }
  449. tearDown := func(file *os.File, tempDir string) {
  450. file.Close()
  451. os.RemoveAll(tempDir)
  452. }
  453. testCases := []struct {
  454. name string
  455. proxyServer proxyRun
  456. append bool
  457. expectedErr string
  458. }{
  459. {
  460. name: "update config file",
  461. proxyServer: new(fakeProxyServerLongRun),
  462. append: true,
  463. expectedErr: "content of the proxy server's configuration file was updated",
  464. },
  465. {
  466. name: "fake error",
  467. proxyServer: new(fakeProxyServerError),
  468. expectedErr: "mocking error from ProxyServer.Run()",
  469. },
  470. }
  471. for _, tc := range testCases {
  472. file, tempDir, err := setUp()
  473. if err != nil {
  474. t.Fatalf("unexpected error when setting up environment: %v", err)
  475. }
  476. opt := NewOptions()
  477. opt.ConfigFile = file.Name()
  478. err = opt.Complete()
  479. if err != nil {
  480. t.Fatal(err)
  481. }
  482. opt.proxyServer = tc.proxyServer
  483. errCh := make(chan error)
  484. go func() {
  485. errCh <- opt.runLoop()
  486. }()
  487. if tc.append {
  488. file.WriteString("append fake content")
  489. }
  490. select {
  491. case err := <-errCh:
  492. if err != nil {
  493. if !strings.Contains(err.Error(), tc.expectedErr) {
  494. t.Errorf("[%s] Expected error containing %v, got %v", tc.name, tc.expectedErr, err)
  495. }
  496. }
  497. case <-time.After(10 * time.Second):
  498. t.Errorf("[%s] Timeout: unable to get any events or internal timeout.", tc.name)
  499. }
  500. tearDown(file, tempDir)
  501. }
  502. }
  503. type fakeProxyServerLongRun struct{}
  504. // Run runs the specified ProxyServer.
  505. func (s *fakeProxyServerLongRun) Run() error {
  506. for {
  507. time.Sleep(2 * time.Second)
  508. }
  509. }
  510. // CleanupAndExit runs in the specified ProxyServer.
  511. func (s *fakeProxyServerLongRun) CleanupAndExit() error {
  512. return nil
  513. }
  514. type fakeProxyServerError struct{}
  515. // Run runs the specified ProxyServer.
  516. func (s *fakeProxyServerError) Run() error {
  517. for {
  518. time.Sleep(2 * time.Second)
  519. return fmt.Errorf("mocking error from ProxyServer.Run()")
  520. }
  521. }
  522. // CleanupAndExit runs in the specified ProxyServer.
  523. func (s *fakeProxyServerError) CleanupAndExit() error {
  524. return errors.New("mocking error from ProxyServer.CleanupAndExit()")
  525. }
  526. func TestAddressFromDeprecatedFlags(t *testing.T) {
  527. testCases := []struct {
  528. name string
  529. healthzPort int32
  530. healthzBindAddress string
  531. metricsPort int32
  532. metricsBindAddress string
  533. expHealthz string
  534. expMetrics string
  535. }{
  536. {
  537. name: "IPv4 bind address",
  538. healthzBindAddress: "1.2.3.4",
  539. healthzPort: 12345,
  540. metricsBindAddress: "2.3.4.5",
  541. metricsPort: 23456,
  542. expHealthz: "1.2.3.4:12345",
  543. expMetrics: "2.3.4.5:23456",
  544. },
  545. {
  546. name: "IPv4 bind address has port",
  547. healthzBindAddress: "1.2.3.4:12345",
  548. healthzPort: 23456,
  549. metricsBindAddress: "2.3.4.5:12345",
  550. metricsPort: 23456,
  551. expHealthz: "1.2.3.4:12345",
  552. expMetrics: "2.3.4.5:12345",
  553. },
  554. {
  555. name: "IPv6 bind address",
  556. healthzBindAddress: "fd00:1::5",
  557. healthzPort: 12345,
  558. metricsBindAddress: "fd00:1::6",
  559. metricsPort: 23456,
  560. expHealthz: "[fd00:1::5]:12345",
  561. expMetrics: "[fd00:1::6]:23456",
  562. },
  563. {
  564. name: "IPv6 bind address has port",
  565. healthzBindAddress: "[fd00:1::5]:12345",
  566. healthzPort: 56789,
  567. metricsBindAddress: "[fd00:1::6]:56789",
  568. metricsPort: 12345,
  569. expHealthz: "[fd00:1::5]:12345",
  570. expMetrics: "[fd00:1::6]:56789",
  571. },
  572. {
  573. name: "Invalid IPv6 Config",
  574. healthzBindAddress: "[fd00:1::5]",
  575. healthzPort: 12345,
  576. metricsBindAddress: "[fd00:1::6]",
  577. metricsPort: 56789,
  578. expHealthz: "[fd00:1::5]",
  579. expMetrics: "[fd00:1::6]",
  580. },
  581. }
  582. for i := range testCases {
  583. gotHealthz := addressFromDeprecatedFlags(testCases[i].healthzBindAddress, testCases[i].healthzPort)
  584. gotMetrics := addressFromDeprecatedFlags(testCases[i].metricsBindAddress, testCases[i].metricsPort)
  585. errFn := func(name, except, got string) {
  586. t.Errorf("case %s: expected %v, got %v", name, except, got)
  587. }
  588. if gotHealthz != testCases[i].expHealthz {
  589. errFn(testCases[i].name, testCases[i].expHealthz, gotHealthz)
  590. }
  591. if gotMetrics != testCases[i].expMetrics {
  592. errFn(testCases[i].name, testCases[i].expMetrics, gotMetrics)
  593. }
  594. }
  595. }