github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/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 DNS: []string{"1.1.1.1o"}, 248 }, 249 }, 250 }, 251 { 252 config: &Config{ 253 CommonConfig: CommonConfig{ 254 DNS: []string{"2.2.2.2", "1.1.1.1o"}, 255 }, 256 }, 257 }, 258 { 259 config: &Config{ 260 CommonConfig: CommonConfig{ 261 DNSSearch: []string{"123456"}, 262 }, 263 }, 264 }, 265 { 266 config: &Config{ 267 CommonConfig: CommonConfig{ 268 DNSSearch: []string{"a.b.c", "123456"}, 269 }, 270 }, 271 }, 272 { 273 config: &Config{ 274 CommonConfig: CommonConfig{ 275 MaxConcurrentDownloads: &minusNumber, 276 // This is weird... 277 ValuesSet: map[string]interface{}{ 278 "max-concurrent-downloads": -1, 279 }, 280 }, 281 }, 282 }, 283 { 284 config: &Config{ 285 CommonConfig: CommonConfig{ 286 MaxConcurrentUploads: &minusNumber, 287 // This is weird... 288 ValuesSet: map[string]interface{}{ 289 "max-concurrent-uploads": -1, 290 }, 291 }, 292 }, 293 }, 294 { 295 config: &Config{ 296 CommonConfig: CommonConfig{ 297 NodeGenericResources: []string{"foo"}, 298 }, 299 }, 300 }, 301 { 302 config: &Config{ 303 CommonConfig: CommonConfig{ 304 NodeGenericResources: []string{"foo=bar", "foo=1"}, 305 }, 306 }, 307 }, 308 } 309 for _, tc := range testCases { 310 err := Validate(tc.config) 311 if err == nil { 312 t.Fatalf("expected error, got nil for config %v", tc.config) 313 } 314 } 315 } 316 317 func TestValidateConfiguration(t *testing.T) { 318 minusNumber := 4 319 testCases := []struct { 320 config *Config 321 }{ 322 { 323 config: &Config{ 324 CommonConfig: CommonConfig{ 325 Labels: []string{"one=two"}, 326 }, 327 }, 328 }, 329 { 330 config: &Config{ 331 CommonConfig: CommonConfig{ 332 DNS: []string{"1.1.1.1"}, 333 }, 334 }, 335 }, 336 { 337 config: &Config{ 338 CommonConfig: CommonConfig{ 339 DNSSearch: []string{"a.b.c"}, 340 }, 341 }, 342 }, 343 { 344 config: &Config{ 345 CommonConfig: CommonConfig{ 346 MaxConcurrentDownloads: &minusNumber, 347 // This is weird... 348 ValuesSet: map[string]interface{}{ 349 "max-concurrent-downloads": -1, 350 }, 351 }, 352 }, 353 }, 354 { 355 config: &Config{ 356 CommonConfig: CommonConfig{ 357 MaxConcurrentUploads: &minusNumber, 358 // This is weird... 359 ValuesSet: map[string]interface{}{ 360 "max-concurrent-uploads": -1, 361 }, 362 }, 363 }, 364 }, 365 { 366 config: &Config{ 367 CommonConfig: CommonConfig{ 368 NodeGenericResources: []string{"foo=bar", "foo=baz"}, 369 }, 370 }, 371 }, 372 { 373 config: &Config{ 374 CommonConfig: CommonConfig{ 375 NodeGenericResources: []string{"foo=1"}, 376 }, 377 }, 378 }, 379 } 380 for _, tc := range testCases { 381 err := Validate(tc.config) 382 if err != nil { 383 t.Fatalf("expected no error, got error %v", err) 384 } 385 } 386 } 387 388 func TestModifiedDiscoverySettings(t *testing.T) { 389 cases := []struct { 390 current *Config 391 modified *Config 392 expected bool 393 }{ 394 { 395 current: discoveryConfig("foo", "bar", map[string]string{}), 396 modified: discoveryConfig("foo", "bar", map[string]string{}), 397 expected: false, 398 }, 399 { 400 current: discoveryConfig("foo", "bar", map[string]string{"foo": "bar"}), 401 modified: discoveryConfig("foo", "bar", map[string]string{"foo": "bar"}), 402 expected: false, 403 }, 404 { 405 current: discoveryConfig("foo", "bar", map[string]string{}), 406 modified: discoveryConfig("foo", "bar", nil), 407 expected: false, 408 }, 409 { 410 current: discoveryConfig("foo", "bar", nil), 411 modified: discoveryConfig("foo", "bar", map[string]string{}), 412 expected: false, 413 }, 414 { 415 current: discoveryConfig("foo", "bar", nil), 416 modified: discoveryConfig("baz", "bar", nil), 417 expected: true, 418 }, 419 { 420 current: discoveryConfig("foo", "bar", nil), 421 modified: discoveryConfig("foo", "baz", nil), 422 expected: true, 423 }, 424 { 425 current: discoveryConfig("foo", "bar", nil), 426 modified: discoveryConfig("foo", "bar", map[string]string{"foo": "bar"}), 427 expected: true, 428 }, 429 } 430 431 for _, c := range cases { 432 got := ModifiedDiscoverySettings(c.current, c.modified.ClusterStore, c.modified.ClusterAdvertise, c.modified.ClusterOpts) 433 if c.expected != got { 434 t.Fatalf("expected %v, got %v: current config %v, new config %v", c.expected, got, c.current, c.modified) 435 } 436 } 437 } 438 439 func discoveryConfig(backendAddr, advertiseAddr string, opts map[string]string) *Config { 440 return &Config{ 441 CommonConfig: CommonConfig{ 442 ClusterStore: backendAddr, 443 ClusterAdvertise: advertiseAddr, 444 ClusterOpts: opts, 445 }, 446 } 447 } 448 449 // TestReloadSetConfigFileNotExist tests that when `--config-file` is set 450 // and it doesn't exist the `Reload` function returns an error. 451 func TestReloadSetConfigFileNotExist(t *testing.T) { 452 configFile := "/tmp/blabla/not/exists/config.json" 453 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 454 flags.String("config-file", "", "") 455 flags.Set("config-file", configFile) 456 457 err := Reload(configFile, flags, func(c *Config) {}) 458 assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file")) 459 } 460 461 // TestReloadDefaultConfigNotExist tests that if the default configuration file 462 // doesn't exist the daemon still will be reloaded. 463 func TestReloadDefaultConfigNotExist(t *testing.T) { 464 skip.If(t, os.Getuid() != 0, "skipping test that requires root") 465 reloaded := false 466 configFile := "/etc/docker/daemon.json" 467 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 468 flags.String("config-file", configFile, "") 469 err := Reload(configFile, flags, func(c *Config) { 470 reloaded = true 471 }) 472 assert.Check(t, err) 473 assert.Check(t, reloaded) 474 } 475 476 // TestReloadBadDefaultConfig tests that when `--config-file` is not set 477 // and the default configuration file exists and is bad return an error 478 func TestReloadBadDefaultConfig(t *testing.T) { 479 f, err := ioutil.TempFile("", "docker-config-") 480 if err != nil { 481 t.Fatal(err) 482 } 483 484 configFile := f.Name() 485 f.Write([]byte(`{wrong: "configuration"}`)) 486 f.Close() 487 488 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 489 flags.String("config-file", configFile, "") 490 err = Reload(configFile, flags, func(c *Config) {}) 491 assert.Check(t, is.ErrorContains(err, "unable to configure the Docker daemon with file")) 492 } 493 494 func TestReloadWithConflictingLabels(t *testing.T) { 495 tempFile := fs.NewFile(t, "config", fs.WithContent(`{"labels":["foo=bar","foo=baz"]}`)) 496 defer tempFile.Remove() 497 configFile := tempFile.Path() 498 499 var lbls []string 500 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 501 flags.String("config-file", configFile, "") 502 flags.StringSlice("labels", lbls, "") 503 err := Reload(configFile, flags, func(c *Config) {}) 504 assert.Check(t, is.ErrorContains(err, "conflict labels for foo=baz and foo=bar")) 505 } 506 507 func TestReloadWithDuplicateLabels(t *testing.T) { 508 tempFile := fs.NewFile(t, "config", fs.WithContent(`{"labels":["foo=the-same","foo=the-same"]}`)) 509 defer tempFile.Remove() 510 configFile := tempFile.Path() 511 512 var lbls []string 513 flags := pflag.NewFlagSet("test", pflag.ContinueOnError) 514 flags.String("config-file", configFile, "") 515 flags.StringSlice("labels", lbls, "") 516 err := Reload(configFile, flags, func(c *Config) {}) 517 assert.Check(t, err) 518 }