config_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. package config // import "github.com/docker/docker/daemon/config"
  2. import (
  3. "encoding/json"
  4. "os"
  5. "path/filepath"
  6. "reflect"
  7. "strings"
  8. "testing"
  9. "github.com/docker/docker/api"
  10. "github.com/docker/docker/libnetwork/ipamutils"
  11. "github.com/docker/docker/opts"
  12. "github.com/google/go-cmp/cmp"
  13. "github.com/google/go-cmp/cmp/cmpopts"
  14. "github.com/imdario/mergo"
  15. "github.com/spf13/pflag"
  16. "golang.org/x/text/encoding"
  17. "golang.org/x/text/encoding/unicode"
  18. "gotest.tools/v3/assert"
  19. is "gotest.tools/v3/assert/cmp"
  20. "gotest.tools/v3/skip"
  21. )
  22. func makeConfigFile(t *testing.T, content string) string {
  23. t.Helper()
  24. name := filepath.Join(t.TempDir(), "daemon.json")
  25. err := os.WriteFile(name, []byte(content), 0o666)
  26. assert.NilError(t, err)
  27. return name
  28. }
  29. func TestDaemonConfigurationNotFound(t *testing.T) {
  30. _, err := MergeDaemonConfigurations(&Config{}, nil, "/tmp/foo-bar-baz-docker")
  31. assert.Check(t, os.IsNotExist(err), "got: %[1]T: %[1]v", err)
  32. }
  33. func TestDaemonBrokenConfiguration(t *testing.T) {
  34. configFile := makeConfigFile(t, `{"Debug": tru`)
  35. _, err := MergeDaemonConfigurations(&Config{}, nil, configFile)
  36. assert.ErrorContains(t, err, `invalid character ' ' in literal true`)
  37. }
  38. // TestDaemonConfigurationUnicodeVariations feeds various variations of Unicode into the JSON parser, ensuring that we
  39. // respect a BOM and otherwise default to UTF-8.
  40. func TestDaemonConfigurationUnicodeVariations(t *testing.T) {
  41. jsonData := `{"debug": true}`
  42. testCases := []struct {
  43. name string
  44. encoding encoding.Encoding
  45. }{
  46. {
  47. name: "UTF-8",
  48. encoding: unicode.UTF8,
  49. },
  50. {
  51. name: "UTF-8 (with BOM)",
  52. encoding: unicode.UTF8BOM,
  53. },
  54. {
  55. name: "UTF-16 (BE with BOM)",
  56. encoding: unicode.UTF16(unicode.BigEndian, unicode.UseBOM),
  57. },
  58. {
  59. name: "UTF-16 (LE with BOM)",
  60. encoding: unicode.UTF16(unicode.LittleEndian, unicode.UseBOM),
  61. },
  62. }
  63. for _, tc := range testCases {
  64. t.Run(tc.name, func(t *testing.T) {
  65. encodedJson, err := tc.encoding.NewEncoder().String(jsonData)
  66. assert.NilError(t, err)
  67. configFile := makeConfigFile(t, encodedJson)
  68. _, err = MergeDaemonConfigurations(&Config{}, nil, configFile)
  69. assert.NilError(t, err)
  70. })
  71. }
  72. }
  73. // TestDaemonConfigurationInvalidUnicode ensures that the JSON parser returns a useful error message if malformed UTF-8
  74. // is provided.
  75. func TestDaemonConfigurationInvalidUnicode(t *testing.T) {
  76. configFileBOM := makeConfigFile(t, "\xef\xbb\xbf{\"debug\": true}\xff")
  77. _, err := MergeDaemonConfigurations(&Config{}, nil, configFileBOM)
  78. assert.ErrorIs(t, err, encoding.ErrInvalidUTF8)
  79. configFileNoBOM := makeConfigFile(t, "{\"debug\": true}\xff")
  80. _, err = MergeDaemonConfigurations(&Config{}, nil, configFileNoBOM)
  81. assert.ErrorIs(t, err, encoding.ErrInvalidUTF8)
  82. }
  83. func TestFindConfigurationConflicts(t *testing.T) {
  84. config := map[string]interface{}{"authorization-plugins": "foobar"}
  85. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  86. flags.String("authorization-plugins", "", "")
  87. assert.Check(t, flags.Set("authorization-plugins", "asdf"))
  88. assert.Check(t, is.ErrorContains(findConfigurationConflicts(config, flags), "authorization-plugins: (from flag: asdf, from file: foobar)"))
  89. }
  90. func TestFindConfigurationConflictsWithNamedOptions(t *testing.T) {
  91. config := map[string]interface{}{"hosts": []string{"qwer"}}
  92. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  93. var hosts []string
  94. flags.VarP(opts.NewNamedListOptsRef("hosts", &hosts, opts.ValidateHost), "host", "H", "Daemon socket(s) to connect to")
  95. assert.Check(t, flags.Set("host", "tcp://127.0.0.1:4444"))
  96. assert.Check(t, flags.Set("host", "unix:///var/run/docker.sock"))
  97. assert.Check(t, is.ErrorContains(findConfigurationConflicts(config, flags), "hosts"))
  98. }
  99. func TestDaemonConfigurationMergeConflicts(t *testing.T) {
  100. configFile := makeConfigFile(t, `{"debug": true}`)
  101. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  102. flags.Bool("debug", false, "")
  103. assert.Check(t, flags.Set("debug", "false"))
  104. _, err := MergeDaemonConfigurations(&Config{}, flags, configFile)
  105. if err == nil {
  106. t.Fatal("expected error, got nil")
  107. }
  108. if !strings.Contains(err.Error(), "debug") {
  109. t.Fatalf("expected debug conflict, got %v", err)
  110. }
  111. }
  112. func TestDaemonConfigurationMergeConcurrent(t *testing.T) {
  113. configFile := makeConfigFile(t, `{"max-concurrent-downloads": 1}`)
  114. _, err := MergeDaemonConfigurations(&Config{}, nil, configFile)
  115. assert.NilError(t, err)
  116. }
  117. func TestDaemonConfigurationMergeConcurrentError(t *testing.T) {
  118. configFile := makeConfigFile(t, `{"max-concurrent-downloads": -1}`)
  119. _, err := MergeDaemonConfigurations(&Config{}, nil, configFile)
  120. assert.ErrorContains(t, err, `invalid max concurrent downloads: -1`)
  121. }
  122. func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) {
  123. configFile := makeConfigFile(t, `{"tlscacert": "/etc/certificates/ca.pem"}`)
  124. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  125. flags.String("tlscacert", "", "")
  126. assert.Check(t, flags.Set("tlscacert", "~/.docker/ca.pem"))
  127. _, err := MergeDaemonConfigurations(&Config{}, flags, configFile)
  128. assert.ErrorContains(t, err, `the following directives are specified both as a flag and in the configuration file: tlscacert`)
  129. }
  130. // TestDaemonConfigurationMergeDefaultAddressPools is a regression test for #40711.
  131. func TestDaemonConfigurationMergeDefaultAddressPools(t *testing.T) {
  132. emptyConfigFile := makeConfigFile(t, `{}`)
  133. configFile := makeConfigFile(t, `{"default-address-pools":[{"base": "10.123.0.0/16", "size": 24 }]}`)
  134. expected := []*ipamutils.NetworkToSplit{{Base: "10.123.0.0/16", Size: 24}}
  135. t.Run("empty config file", func(t *testing.T) {
  136. conf := Config{}
  137. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  138. flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "")
  139. assert.Check(t, flags.Set("default-address-pool", "base=10.123.0.0/16,size=24"))
  140. config, err := MergeDaemonConfigurations(&conf, flags, emptyConfigFile)
  141. assert.NilError(t, err)
  142. assert.DeepEqual(t, config.DefaultAddressPools.Value(), expected)
  143. })
  144. t.Run("config file", func(t *testing.T) {
  145. conf := Config{}
  146. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  147. flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "")
  148. config, err := MergeDaemonConfigurations(&conf, flags, configFile)
  149. assert.NilError(t, err)
  150. assert.DeepEqual(t, config.DefaultAddressPools.Value(), expected)
  151. })
  152. t.Run("with conflicting options", func(t *testing.T) {
  153. conf := Config{}
  154. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  155. flags.Var(&conf.NetworkConfig.DefaultAddressPools, "default-address-pool", "")
  156. assert.Check(t, flags.Set("default-address-pool", "base=10.123.0.0/16,size=24"))
  157. _, err := MergeDaemonConfigurations(&conf, flags, configFile)
  158. assert.ErrorContains(t, err, "the following directives are specified both as a flag and in the configuration file")
  159. assert.ErrorContains(t, err, "default-address-pools")
  160. })
  161. }
  162. func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) {
  163. config := map[string]interface{}{"tls-verify": "true"}
  164. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  165. flags.Bool("tlsverify", false, "")
  166. err := findConfigurationConflicts(config, flags)
  167. assert.ErrorContains(t, err, "the following directives don't match any configuration option: tls-verify")
  168. }
  169. func TestFindConfigurationConflictsWithMergedValues(t *testing.T) {
  170. var hosts []string
  171. config := map[string]interface{}{"hosts": "tcp://127.0.0.1:2345"}
  172. flags := pflag.NewFlagSet("base", pflag.ContinueOnError)
  173. flags.VarP(opts.NewNamedListOptsRef("hosts", &hosts, nil), "host", "H", "")
  174. err := findConfigurationConflicts(config, flags)
  175. assert.NilError(t, err)
  176. assert.Check(t, flags.Set("host", "unix:///var/run/docker.sock"))
  177. err = findConfigurationConflicts(config, flags)
  178. assert.ErrorContains(t, err, "hosts: (from flag: [unix:///var/run/docker.sock], from file: tcp://127.0.0.1:2345)")
  179. }
  180. func TestValidateConfigurationErrors(t *testing.T) {
  181. testCases := []struct {
  182. name string
  183. field string
  184. config *Config
  185. expectedErr string
  186. }{
  187. {
  188. name: "single label without value",
  189. config: &Config{
  190. CommonConfig: CommonConfig{
  191. Labels: []string{"one"},
  192. },
  193. },
  194. expectedErr: "bad attribute format: one",
  195. },
  196. {
  197. name: "multiple label without value",
  198. config: &Config{
  199. CommonConfig: CommonConfig{
  200. Labels: []string{"foo=bar", "one"},
  201. },
  202. },
  203. expectedErr: "bad attribute format: one",
  204. },
  205. {
  206. name: "single DNSSearch",
  207. config: &Config{
  208. CommonConfig: CommonConfig{
  209. DNSConfig: DNSConfig{
  210. DNSSearch: []string{"123456"},
  211. },
  212. },
  213. },
  214. expectedErr: "123456 is not a valid domain",
  215. },
  216. {
  217. name: "multiple DNSSearch",
  218. config: &Config{
  219. CommonConfig: CommonConfig{
  220. DNSConfig: DNSConfig{
  221. DNSSearch: []string{"a.b.c", "123456"},
  222. },
  223. },
  224. },
  225. expectedErr: "123456 is not a valid domain",
  226. },
  227. {
  228. name: "negative MTU",
  229. config: &Config{
  230. CommonConfig: CommonConfig{
  231. BridgeConfig: BridgeConfig{
  232. DefaultBridgeConfig: DefaultBridgeConfig{
  233. MTU: -10,
  234. },
  235. },
  236. },
  237. },
  238. expectedErr: "invalid default MTU: -10",
  239. },
  240. {
  241. name: "negative max-concurrent-downloads",
  242. config: &Config{
  243. CommonConfig: CommonConfig{
  244. MaxConcurrentDownloads: -10,
  245. },
  246. },
  247. expectedErr: "invalid max concurrent downloads: -10",
  248. },
  249. {
  250. name: "negative max-concurrent-uploads",
  251. config: &Config{
  252. CommonConfig: CommonConfig{
  253. MaxConcurrentUploads: -10,
  254. },
  255. },
  256. expectedErr: "invalid max concurrent uploads: -10",
  257. },
  258. {
  259. name: "negative max-download-attempts",
  260. config: &Config{
  261. CommonConfig: CommonConfig{
  262. MaxDownloadAttempts: -10,
  263. },
  264. },
  265. expectedErr: "invalid max download attempts: -10",
  266. },
  267. // TODO(thaJeztah) temporarily excluding this test as it assumes defaults are set before validating and applying updated configs
  268. /*
  269. {
  270. name: "zero max-download-attempts",
  271. field: "MaxDownloadAttempts",
  272. config: &Config{
  273. CommonConfig: CommonConfig{
  274. MaxDownloadAttempts: 0,
  275. },
  276. },
  277. expectedErr: "invalid max download attempts: 0",
  278. },
  279. */
  280. {
  281. name: "generic resource without =",
  282. config: &Config{
  283. CommonConfig: CommonConfig{
  284. NodeGenericResources: []string{"foo"},
  285. },
  286. },
  287. expectedErr: "could not parse GenericResource: incorrect term foo, missing '=' or malformed expression",
  288. },
  289. {
  290. name: "generic resource mixed named and discrete",
  291. config: &Config{
  292. CommonConfig: CommonConfig{
  293. NodeGenericResources: []string{"foo=bar", "foo=1"},
  294. },
  295. },
  296. expectedErr: "could not parse GenericResource: mixed discrete and named resources in expression 'foo=[bar 1]'",
  297. },
  298. {
  299. name: "with invalid hosts",
  300. config: &Config{
  301. CommonConfig: CommonConfig{
  302. Hosts: []string{"127.0.0.1:2375/path"},
  303. },
  304. },
  305. expectedErr: "invalid bind address (127.0.0.1:2375/path): should not contain a path element",
  306. },
  307. {
  308. name: "with invalid log-level",
  309. config: &Config{
  310. CommonConfig: CommonConfig{
  311. LogLevel: "foobar",
  312. },
  313. },
  314. expectedErr: "invalid logging level: foobar",
  315. },
  316. }
  317. for _, tc := range testCases {
  318. t.Run(tc.name, func(t *testing.T) {
  319. cfg, err := New()
  320. assert.NilError(t, err)
  321. if tc.field != "" {
  322. assert.Check(t, mergo.Merge(cfg, tc.config, mergo.WithOverride, withForceOverwrite(tc.field)))
  323. } else {
  324. assert.Check(t, mergo.Merge(cfg, tc.config, mergo.WithOverride))
  325. }
  326. err = Validate(cfg)
  327. assert.Error(t, err, tc.expectedErr)
  328. })
  329. }
  330. }
  331. func withForceOverwrite(fieldName string) func(config *mergo.Config) {
  332. return mergo.WithTransformers(overwriteTransformer{fieldName: fieldName})
  333. }
  334. type overwriteTransformer struct {
  335. fieldName string
  336. }
  337. func (tf overwriteTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
  338. if typ == reflect.TypeOf(CommonConfig{}) {
  339. return func(dst, src reflect.Value) error {
  340. dst.FieldByName(tf.fieldName).Set(src.FieldByName(tf.fieldName))
  341. return nil
  342. }
  343. }
  344. return nil
  345. }
  346. func TestValidateConfiguration(t *testing.T) {
  347. testCases := []struct {
  348. name string
  349. field string
  350. config *Config
  351. }{
  352. {
  353. name: "with label",
  354. field: "Labels",
  355. config: &Config{
  356. CommonConfig: CommonConfig{
  357. Labels: []string{"one=two"},
  358. },
  359. },
  360. },
  361. {
  362. name: "with dns-search",
  363. field: "DNSConfig",
  364. config: &Config{
  365. CommonConfig: CommonConfig{
  366. DNSConfig: DNSConfig{
  367. DNSSearch: []string{"a.b.c"},
  368. },
  369. },
  370. },
  371. },
  372. {
  373. name: "with mtu",
  374. field: "MTU",
  375. config: &Config{
  376. CommonConfig: CommonConfig{
  377. BridgeConfig: BridgeConfig{
  378. DefaultBridgeConfig: DefaultBridgeConfig{
  379. MTU: 1234,
  380. },
  381. },
  382. },
  383. },
  384. },
  385. {
  386. name: "with max-concurrent-downloads",
  387. field: "MaxConcurrentDownloads",
  388. config: &Config{
  389. CommonConfig: CommonConfig{
  390. MaxConcurrentDownloads: 4,
  391. },
  392. },
  393. },
  394. {
  395. name: "with max-concurrent-uploads",
  396. field: "MaxConcurrentUploads",
  397. config: &Config{
  398. CommonConfig: CommonConfig{
  399. MaxConcurrentUploads: 4,
  400. },
  401. },
  402. },
  403. {
  404. name: "with max-download-attempts",
  405. field: "MaxDownloadAttempts",
  406. config: &Config{
  407. CommonConfig: CommonConfig{
  408. MaxDownloadAttempts: 4,
  409. },
  410. },
  411. },
  412. {
  413. name: "with multiple node generic resources",
  414. field: "NodeGenericResources",
  415. config: &Config{
  416. CommonConfig: CommonConfig{
  417. NodeGenericResources: []string{"foo=bar", "foo=baz"},
  418. },
  419. },
  420. },
  421. {
  422. name: "with node generic resources",
  423. field: "NodeGenericResources",
  424. config: &Config{
  425. CommonConfig: CommonConfig{
  426. NodeGenericResources: []string{"foo=1"},
  427. },
  428. },
  429. },
  430. {
  431. name: "with hosts",
  432. field: "Hosts",
  433. config: &Config{
  434. CommonConfig: CommonConfig{
  435. Hosts: []string{"tcp://127.0.0.1:2375"},
  436. },
  437. },
  438. },
  439. {
  440. name: "with log-level warn",
  441. field: "LogLevel",
  442. config: &Config{
  443. CommonConfig: CommonConfig{
  444. LogLevel: "warn",
  445. },
  446. },
  447. },
  448. }
  449. for _, tc := range testCases {
  450. t.Run(tc.name, func(t *testing.T) {
  451. // Start with a config with all defaults set, so that we only
  452. cfg, err := New()
  453. assert.NilError(t, err)
  454. assert.Check(t, mergo.Merge(cfg, tc.config, mergo.WithOverride))
  455. // Check that the override happened :)
  456. assert.Check(t, is.DeepEqual(cfg, tc.config, field(tc.field)))
  457. err = Validate(cfg)
  458. assert.NilError(t, err)
  459. })
  460. }
  461. }
  462. func TestValidateMinAPIVersion(t *testing.T) {
  463. t.Parallel()
  464. tests := []struct {
  465. doc string
  466. input string
  467. expectedErr string
  468. }{
  469. {
  470. doc: "empty",
  471. expectedErr: "value is empty",
  472. },
  473. {
  474. doc: "with prefix",
  475. input: "v1.43",
  476. expectedErr: `API version must be provided without "v" prefix`,
  477. },
  478. {
  479. doc: "major only",
  480. input: "1",
  481. expectedErr: `minimum supported API version is`,
  482. },
  483. {
  484. doc: "too low",
  485. input: "1.0",
  486. expectedErr: `minimum supported API version is`,
  487. },
  488. {
  489. doc: "minor too high",
  490. input: "1.99",
  491. expectedErr: `maximum supported API version is`,
  492. },
  493. {
  494. doc: "major too high",
  495. input: "9.0",
  496. expectedErr: `maximum supported API version is`,
  497. },
  498. {
  499. doc: "current version",
  500. input: api.DefaultVersion,
  501. },
  502. }
  503. for _, tc := range tests {
  504. tc := tc
  505. t.Run(tc.doc, func(t *testing.T) {
  506. err := ValidateMinAPIVersion(tc.input)
  507. if tc.expectedErr != "" {
  508. assert.Check(t, is.ErrorContains(err, tc.expectedErr))
  509. } else {
  510. assert.Check(t, err)
  511. }
  512. })
  513. }
  514. }
  515. func TestConfigInvalidDNS(t *testing.T) {
  516. tests := []struct {
  517. doc string
  518. input string
  519. expectedErr string
  520. }{
  521. {
  522. doc: "single DNS, invalid IP-address",
  523. input: `{"dns": ["1.1.1.1o"]}`,
  524. expectedErr: `invalid IP address: 1.1.1.1o`,
  525. },
  526. {
  527. doc: "multiple DNS, invalid IP-address",
  528. input: `{"dns": ["2.2.2.2", "1.1.1.1o"]}`,
  529. expectedErr: `invalid IP address: 1.1.1.1o`,
  530. },
  531. }
  532. for _, tc := range tests {
  533. tc := tc
  534. t.Run(tc.doc, func(t *testing.T) {
  535. var cfg Config
  536. err := json.Unmarshal([]byte(tc.input), &cfg)
  537. assert.Check(t, is.Error(err, tc.expectedErr))
  538. })
  539. }
  540. }
  541. func field(field string) cmp.Option {
  542. tmp := reflect.TypeOf(Config{})
  543. ignoreFields := make([]string, 0, tmp.NumField())
  544. for i := 0; i < tmp.NumField(); i++ {
  545. if tmp.Field(i).Name != field {
  546. ignoreFields = append(ignoreFields, tmp.Field(i).Name)
  547. }
  548. }
  549. return cmpopts.IgnoreFields(Config{}, ignoreFields...)
  550. }
  551. // TestReloadSetConfigFileNotExist tests that when `--config-file` is set, and it doesn't exist the `Reload` function
  552. // returns an error.
  553. func TestReloadSetConfigFileNotExist(t *testing.T) {
  554. configFile := "/tmp/blabla/not/exists/config.json"
  555. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  556. flags.String("config-file", "", "")
  557. assert.Check(t, flags.Set("config-file", configFile))
  558. err := Reload(configFile, flags, func(c *Config) {})
  559. assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file"))
  560. }
  561. // TestReloadDefaultConfigNotExist tests that if the default configuration file doesn't exist the daemon still will
  562. // still be reloaded.
  563. func TestReloadDefaultConfigNotExist(t *testing.T) {
  564. skip.If(t, os.Getuid() != 0, "skipping test that requires root")
  565. defaultConfigFile := "/tmp/blabla/not/exists/daemon.json"
  566. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  567. flags.String("config-file", defaultConfigFile, "")
  568. reloaded := false
  569. err := Reload(defaultConfigFile, flags, func(c *Config) {
  570. reloaded = true
  571. })
  572. assert.Check(t, err)
  573. assert.Check(t, reloaded)
  574. }
  575. // TestReloadBadDefaultConfig tests that when `--config-file` is not set and the default configuration file exists and
  576. // is bad, an error is returned.
  577. func TestReloadBadDefaultConfig(t *testing.T) {
  578. configFile := makeConfigFile(t, `{wrong: "configuration"}`)
  579. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  580. flags.String("config-file", configFile, "")
  581. reloaded := false
  582. err := Reload(configFile, flags, func(c *Config) {
  583. reloaded = true
  584. })
  585. assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file"))
  586. assert.Check(t, reloaded == false)
  587. }
  588. func TestReloadWithConflictingLabels(t *testing.T) {
  589. configFile := makeConfigFile(t, `{"labels": ["foo=bar", "foo=baz"]}`)
  590. var lbls []string
  591. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  592. flags.String("config-file", configFile, "")
  593. flags.StringSlice("labels", lbls, "")
  594. reloaded := false
  595. err := Reload(configFile, flags, func(c *Config) {
  596. reloaded = true
  597. })
  598. assert.Check(t, is.ErrorContains(err, "conflict labels for foo=baz and foo=bar"))
  599. assert.Check(t, reloaded == false)
  600. }
  601. func TestReloadWithDuplicateLabels(t *testing.T) {
  602. configFile := makeConfigFile(t, `{"labels": ["foo=the-same", "foo=the-same"]}`)
  603. var lbls []string
  604. flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
  605. flags.String("config-file", configFile, "")
  606. flags.StringSlice("labels", lbls, "")
  607. reloaded := false
  608. err := Reload(configFile, flags, func(c *Config) {
  609. reloaded = true
  610. assert.Check(t, is.DeepEqual(c.Labels, []string{"foo=the-same"}))
  611. })
  612. assert.Check(t, err)
  613. assert.Check(t, reloaded)
  614. }
  615. func TestMaskURLCredentials(t *testing.T) {
  616. tests := []struct {
  617. rawURL string
  618. maskedURL string
  619. }{
  620. {
  621. rawURL: "",
  622. maskedURL: "",
  623. }, {
  624. rawURL: "invalidURL",
  625. maskedURL: "invalidURL",
  626. }, {
  627. rawURL: "http://proxy.example.com:80/",
  628. maskedURL: "http://proxy.example.com:80/",
  629. }, {
  630. rawURL: "http://USER:PASSWORD@proxy.example.com:80/",
  631. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
  632. }, {
  633. rawURL: "http://PASSWORD:PASSWORD@proxy.example.com:80/",
  634. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
  635. }, {
  636. rawURL: "http://USER:@proxy.example.com:80/",
  637. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
  638. }, {
  639. rawURL: "http://:PASSWORD@proxy.example.com:80/",
  640. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
  641. }, {
  642. rawURL: "http://USER@docker:password@proxy.example.com:80/",
  643. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
  644. }, {
  645. rawURL: "http://USER%40docker:password@proxy.example.com:80/",
  646. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
  647. }, {
  648. rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/",
  649. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/",
  650. }, {
  651. rawURL: "http://USER%40docker:pa%3Fsword@proxy.example.com:80/hello%20world",
  652. maskedURL: "http://xxxxx:xxxxx@proxy.example.com:80/hello%20world",
  653. },
  654. }
  655. for _, test := range tests {
  656. maskedURL := MaskCredentials(test.rawURL)
  657. assert.Equal(t, maskedURL, test.maskedURL)
  658. }
  659. }