github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/daemon/config/config_test.go (about) 1 package config // import "github.com/docker/docker/daemon/config" 2 3 import ( 4 "io/ioutil" 5 "os" 6 "strings" 7 "testing" 8 9 "github.com/docker/docker/daemon/discovery" 10 "github.com/docker/docker/opts" 11 "github.com/spf13/pflag" 12 "gotest.tools/assert" 13 is "gotest.tools/assert/cmp" 14 "gotest.tools/fs" 15 "gotest.tools/skip" 16 ) 17 18 func TestDaemonConfigurationNotFound(t *testing.T) { 19 _, err := MergeDaemonConfigurations(&Config{}, nil, "/tmp/foo-bar-baz-docker") 20 if err == nil || !os.IsNotExist(err) { 21 t.Fatalf("expected does not exist error, got %v", err) 22 } 23 } 24 25 func TestDaemonBrokenConfiguration(t *testing.T) { 26 f, err := ioutil.TempFile("", "docker-config-") 27 if err != nil { 28 t.Fatal(err) 29 } 30 31 configFile := f.Name() 32 f.Write([]byte(`{"Debug": tru`)) 33 f.Close() 34 35 _, err = MergeDaemonConfigurations(&Config{}, nil, configFile) 36 if err == nil { 37 t.Fatalf("expected error, got %v", err) 38 } 39 } 40 41 func TestParseClusterAdvertiseSettings(t *testing.T) { 42 _, err := ParseClusterAdvertiseSettings("something", "") 43 if err != discovery.ErrDiscoveryDisabled { 44 t.Fatalf("expected discovery disabled error, got %v\n", err) 45 } 46 47 _, err = ParseClusterAdvertiseSettings("", "something") 48 if err == nil { 49 t.Fatalf("expected discovery store error, got %v\n", err) 50 } 51 52 _, err = ParseClusterAdvertiseSettings("etcd", "127.0.0.1:8080") 53 if err != nil { 54 t.Fatal(err) 55 } 56 } 57 58 func TestFindConfigurationConflicts(t *testing.T) { 59 config := map[string]interface{}{"authorization-plugins": "foobar"} 60 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 61 62 flags.String("authorization-plugins", "", "") 63 assert.Check(t, flags.Set("authorization-plugins", "asdf")) 64 assert.Check(t, is.ErrorContains(findConfigurationConflicts(config, flags), "authorization-plugins: (from flag: asdf, from file: foobar)")) 65 } 66 67 func TestFindConfigurationConflictsWithNamedOptions(t *testing.T) { 68 config := map[string]interface{}{"hosts": []string{"qwer"}} 69 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 70 71 var hosts []string 72 flags.VarP(opts.NewNamedListOptsRef("hosts", &hosts, opts.ValidateHost), "host", "H", "Daemon socket(s) to connect to") 73 assert.Check(t, flags.Set("host", "tcp://127.0.0.1:4444")) 74 assert.Check(t, flags.Set("host", "unix:///var/run/docker.sock")) 75 assert.Check(t, is.ErrorContains(findConfigurationConflicts(config, flags), "hosts")) 76 } 77 78 func TestDaemonConfigurationMergeConflicts(t *testing.T) { 79 f, err := ioutil.TempFile("", "docker-config-") 80 if err != nil { 81 t.Fatal(err) 82 } 83 84 configFile := f.Name() 85 f.Write([]byte(`{"debug": true}`)) 86 f.Close() 87 88 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 89 flags.Bool("debug", false, "") 90 flags.Set("debug", "false") 91 92 _, err = MergeDaemonConfigurations(&Config{}, flags, configFile) 93 if err == nil { 94 t.Fatal("expected error, got nil") 95 } 96 if !strings.Contains(err.Error(), "debug") { 97 t.Fatalf("expected debug conflict, got %v", err) 98 } 99 } 100 101 func TestDaemonConfigurationMergeConcurrent(t *testing.T) { 102 f, err := ioutil.TempFile("", "docker-config-") 103 if err != nil { 104 t.Fatal(err) 105 } 106 107 configFile := f.Name() 108 f.Write([]byte(`{"max-concurrent-downloads": 1}`)) 109 f.Close() 110 111 _, err = MergeDaemonConfigurations(&Config{}, nil, configFile) 112 if err != nil { 113 t.Fatal("expected error, got nil") 114 } 115 } 116 117 func TestDaemonConfigurationMergeConcurrentError(t *testing.T) { 118 f, err := ioutil.TempFile("", "docker-config-") 119 if err != nil { 120 t.Fatal(err) 121 } 122 123 configFile := f.Name() 124 f.Write([]byte(`{"max-concurrent-downloads": -1}`)) 125 f.Close() 126 127 _, err = MergeDaemonConfigurations(&Config{}, nil, configFile) 128 if err == nil { 129 t.Fatalf("expected no error, got error %v", err) 130 } 131 } 132 133 func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) { 134 f, err := ioutil.TempFile("", "docker-config-") 135 if err != nil { 136 t.Fatal(err) 137 } 138 139 configFile := f.Name() 140 f.Write([]byte(`{"tlscacert": "/etc/certificates/ca.pem"}`)) 141 f.Close() 142 143 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 144 flags.String("tlscacert", "", "") 145 flags.Set("tlscacert", "~/.docker/ca.pem") 146 147 _, err = MergeDaemonConfigurations(&Config{}, flags, configFile) 148 if err == nil { 149 t.Fatal("expected error, got nil") 150 } 151 if !strings.Contains(err.Error(), "tlscacert") { 152 t.Fatalf("expected tlscacert conflict, got %v", err) 153 } 154 } 155 156 func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) { 157 config := map[string]interface{}{"tls-verify": "true"} 158 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 159 160 flags.Bool("tlsverify", false, "") 161 err := findConfigurationConflicts(config, flags) 162 if err == nil { 163 t.Fatal("expected error, got nil") 164 } 165 if !strings.Contains(err.Error(), "the following directives don't match any configuration option: tls-verify") { 166 t.Fatalf("expected tls-verify conflict, got %v", err) 167 } 168 } 169 170 func TestFindConfigurationConflictsWithMergedValues(t *testing.T) { 171 var hosts []string 172 config := map[string]interface{}{"hosts": "tcp://127.0.0.1:2345"} 173 flags := pflag.NewFlagSet("base", pflag.ContinueOnError) 174 flags.VarP(opts.NewNamedListOptsRef("hosts", &hosts, nil), "host", "H", "") 175 176 err := findConfigurationConflicts(config, flags) 177 if err != nil { 178 t.Fatal(err) 179 } 180 181 flags.Set("host", "unix:///var/run/docker.sock") 182 err = findConfigurationConflicts(config, flags) 183 if err == nil { 184 t.Fatal("expected error, got nil") 185 } 186 if !strings.Contains(err.Error(), "hosts: (from flag: [unix:///var/run/docker.sock], from file: tcp://127.0.0.1:2345)") { 187 t.Fatalf("expected hosts conflict, got %v", err) 188 } 189 } 190 191 func TestValidateReservedNamespaceLabels(t *testing.T) { 192 for _, validLabels := range [][]string{ 193 nil, // no error if there are no labels 194 { // no error if there aren't any reserved namespace labels 195 "hello=world", 196 "label=me", 197 }, 198 { // only reserved namespaces that end with a dot are invalid 199 "com.dockerpsychnotreserved.label=value", 200 "io.dockerproject.not=reserved", 201 "org.docker.not=reserved", 202 }, 203 } { 204 assert.Check(t, ValidateReservedNamespaceLabels(validLabels)) 205 } 206 207 for _, invalidLabel := range []string{ 208 "com.docker.feature=enabled", 209 "io.docker.configuration=0", 210 "org.dockerproject.setting=on", 211 // casing doesn't matter 212 "COM.docker.feature=enabled", 213 "io.DOCKER.CONFIGURATION=0", 214 "Org.Dockerproject.Setting=on", 215 } { 216 err := ValidateReservedNamespaceLabels([]string{ 217 "valid=label", 218 invalidLabel, 219 "another=valid", 220 }) 221 assert.Check(t, is.ErrorContains(err, invalidLabel)) 222 } 223 } 224 225 func TestValidateConfigurationErrors(t *testing.T) { 226 minusNumber := -10 227 testCases := []struct { 228 config *Config 229 }{ 230 { 231 config: &Config{ 232 CommonConfig: CommonConfig{ 233 Labels: []string{"one"}, 234 }, 235 }, 236 }, 237 { 238 config: &Config{ 239 CommonConfig: CommonConfig{ 240 Labels: []string{"foo=bar", "one"}, 241 }, 242 }, 243 }, 244 { 245 config: &Config{ 246 CommonConfig: CommonConfig{ 247 DNSConfig: DNSConfig{ 248 DNS: []string{"1.1.1.1o"}, 249 }, 250 }, 251 }, 252 }, 253 { 254 config: &Config{ 255 CommonConfig: CommonConfig{ 256 DNSConfig: DNSConfig{ 257 DNS: []string{"2.2.2.2", "1.1.1.1o"}, 258 }, 259 }, 260 }, 261 }, 262 { 263 config: &Config{ 264 CommonConfig: CommonConfig{ 265 DNSConfig: DNSConfig{ 266 DNSSearch: []string{"123456"}, 267 }, 268 }, 269 }, 270 }, 271 { 272 config: &Config{ 273 CommonConfig: CommonConfig{ 274 DNSConfig: DNSConfig{ 275 DNSSearch: []string{"a.b.c", "123456"}, 276 }, 277 }, 278 }, 279 }, 280 { 281 config: &Config{ 282 CommonConfig: CommonConfig{ 283 MaxConcurrentDownloads: &minusNumber, 284 // This is weird... 285 ValuesSet: map[string]interface{}{ 286 "max-concurrent-downloads": -1, 287 }, 288 }, 289 }, 290 }, 291 { 292 config: &Config{ 293 CommonConfig: CommonConfig{ 294 MaxConcurrentUploads: &minusNumber, 295 // This is weird... 296 ValuesSet: map[string]interface{}{ 297 "max-concurrent-uploads": -1, 298 }, 299 }, 300 }, 301 }, 302 { 303 config: &Config{ 304 CommonConfig: CommonConfig{ 305 NodeGenericResources: []string{"foo"}, 306 }, 307 }, 308 }, 309 { 310 config: &Config{ 311 CommonConfig: CommonConfig{ 312 NodeGenericResources: []string{"foo=bar", "foo=1"}, 313 }, 314 }, 315 }, 316 } 317 for _, tc := range testCases { 318 err := Validate(tc.config) 319 if err == nil { 320 t.Fatalf("expected error, got nil for config %v", tc.config) 321 } 322 } 323 } 324 325 func TestValidateConfiguration(t *testing.T) { 326 minusNumber := 4 327 testCases := []struct { 328 config *Config 329 }{ 330 { 331 config: &Config{ 332 CommonConfig: CommonConfig{ 333 Labels: []string{"one=two"}, 334 }, 335 }, 336 }, 337 { 338 config: &Config{ 339 CommonConfig: CommonConfig{ 340 DNSConfig: DNSConfig{ 341 DNS: []string{"1.1.1.1"}, 342 }, 343 }, 344 }, 345 }, 346 { 347 config: &Config{ 348 CommonConfig: CommonConfig{ 349 DNSConfig: DNSConfig{ 350 DNSSearch: []string{"a.b.c"}, 351 }, 352 }, 353 }, 354 }, 355 { 356 config: &Config{ 357 CommonConfig: CommonConfig{ 358 MaxConcurrentDownloads: &minusNumber, 359 // This is weird... 360 ValuesSet: map[string]interface{}{ 361 "max-concurrent-downloads": -1, 362 }, 363 }, 364 }, 365 }, 366 { 367 config: &Config{ 368 CommonConfig: CommonConfig{ 369 MaxConcurrentUploads: &minusNumber, 370 // This is weird... 371 ValuesSet: map[string]interface{}{ 372 "max-concurrent-uploads": -1, 373 }, 374 }, 375 }, 376 }, 377 { 378 config: &Config{ 379 CommonConfig: CommonConfig{ 380 NodeGenericResources: []string{"foo=bar", "foo=baz"}, 381 }, 382 }, 383 }, 384 { 385 config: &Config{ 386 CommonConfig: CommonConfig{ 387 NodeGenericResources: []string{"foo=1"}, 388 }, 389 }, 390 }, 391 } 392 for _, tc := range testCases { 393 err := Validate(tc.config) 394 if err != nil { 395 t.Fatalf("expected no error, got error %v", err) 396 } 397 } 398 } 399 400 func TestModifiedDiscoverySettings(t *testing.T) { 401 cases := []struct { 402 current *Config 403 modified *Config 404 expected bool 405 }{ 406 { 407 current: discoveryConfig("foo", "bar", map[string]string{}), 408 modified: discoveryConfig("foo", "bar", map[string]string{}), 409 expected: false, 410 }, 411 { 412 current: discoveryConfig("foo", "bar", map[string]string{"foo": "bar"}), 413 modified: discoveryConfig("foo", "bar", map[string]string{"foo": "bar"}), 414 expected: false, 415 }, 416 { 417 current: discoveryConfig("foo", "bar", map[string]string{}), 418 modified: discoveryConfig("foo", "bar", nil), 419 expected: false, 420 }, 421 { 422 current: discoveryConfig("foo", "bar", nil), 423 modified: discoveryConfig("foo", "bar", map[string]string{}), 424 expected: false, 425 }, 426 { 427 current: discoveryConfig("foo", "bar", nil), 428 modified: discoveryConfig("baz", "bar", nil), 429 expected: true, 430 }, 431 { 432 current: discoveryConfig("foo", "bar", nil), 433 modified: discoveryConfig("foo", "baz", nil), 434 expected: true, 435 }, 436 { 437 current: discoveryConfig("foo", "bar", nil), 438 modified: discoveryConfig("foo", "bar", map[string]string{"foo": "bar"}), 439 expected: true, 440 }, 441 } 442 443 for _, c := range cases { 444 got := ModifiedDiscoverySettings(c.current, c.modified.ClusterStore, c.modified.ClusterAdvertise, c.modified.ClusterOpts) 445 if c.expected != got { 446 t.Fatalf("expected %v, got %v: current config %v, new config %v", c.expected, got, c.current, c.modified) 447 } 448 } 449 } 450 451 func discoveryConfig(backendAddr, advertiseAddr string, opts map[string]string) *Config { 452 return &Config{ 453 CommonConfig: CommonConfig{ 454 ClusterStore: backendAddr, 455 ClusterAdvertise: advertiseAddr, 456 ClusterOpts: opts, 457 }, 458 } 459 } 460 461 // TestReloadSetConfigFileNotExist tests that when `--config-file` is set 462 // and it doesn't exist the `Reload` function returns an error. 463 func TestReloadSetConfigFileNotExist(t *testing.T) { 464 configFile := "/tmp/blabla/not/exists/config.json" 465 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 466 flags.String("config-file", "", "") 467 flags.Set("config-file", configFile) 468 469 err := Reload(configFile, flags, func(c *Config) {}) 470 assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file")) 471 } 472 473 // TestReloadDefaultConfigNotExist tests that if the default configuration file 474 // doesn't exist the daemon still will be reloaded. 475 func TestReloadDefaultConfigNotExist(t *testing.T) { 476 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 477 reloaded := false 478 configFile := "/etc/docker/daemon.json" 479 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 480 flags.String("config-file", configFile, "") 481 err := Reload(configFile, flags, func(c *Config) { 482 reloaded = true 483 }) 484 assert.Check(t, err) 485 assert.Check(t, reloaded) 486 } 487 488 // TestReloadBadDefaultConfig tests that when `--config-file` is not set 489 // and the default configuration file exists and is bad return an error 490 func TestReloadBadDefaultConfig(t *testing.T) { 491 f, err := ioutil.TempFile("", "docker-config-") 492 if err != nil { 493 t.Fatal(err) 494 } 495 496 configFile := f.Name() 497 f.Write([]byte(`{wrong: "configuration"}`)) 498 f.Close() 499 500 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 501 flags.String("config-file", configFile, "") 502 err = Reload(configFile, flags, func(c *Config) {}) 503 assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file")) 504 } 505 506 func TestReloadWithConflictingLabels(t *testing.T) { 507 tempFile := fs.NewFile(t, "config", fs.WithContent(`{"labels":["foo=bar","foo=baz"]}`)) 508 defer tempFile.Remove() 509 configFile := tempFile.Path() 510 511 var lbls []string 512 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 513 flags.String("config-file", configFile, "") 514 flags.StringSlice("labels", lbls, "") 515 err := Reload(configFile, flags, func(c *Config) {}) 516 assert.Check(t, is.ErrorContains(err, "conflict labels for foo=baz and foo=bar")) 517 } 518 519 func TestReloadWithDuplicateLabels(t *testing.T) { 520 tempFile := fs.NewFile(t, "config", fs.WithContent(`{"labels":["foo=the-same","foo=the-same"]}`)) 521 defer tempFile.Remove() 522 configFile := tempFile.Path() 523 524 var lbls []string 525 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 526 flags.String("config-file", configFile, "") 527 flags.StringSlice("labels", lbls, "") 528 err := Reload(configFile, flags, func(c *Config) {}) 529 assert.Check(t, err) 530 }