github.com/LazyboyChen7/engine@v17.12.1-ce-rc2+incompatible/daemon/logger/splunk/splunk_test.go (about) 1 package splunk 2 3 import ( 4 "compress/gzip" 5 "context" 6 "fmt" 7 "net/http" 8 "os" 9 "runtime" 10 "testing" 11 "time" 12 13 "github.com/docker/docker/daemon/logger" 14 "github.com/gotestyourself/gotestyourself/env" 15 "github.com/stretchr/testify/require" 16 ) 17 18 // Validate options 19 func TestValidateLogOpt(t *testing.T) { 20 err := ValidateLogOpt(map[string]string{ 21 splunkURLKey: "http://127.0.0.1", 22 splunkTokenKey: "2160C7EF-2CE9-4307-A180-F852B99CF417", 23 splunkSourceKey: "mysource", 24 splunkSourceTypeKey: "mysourcetype", 25 splunkIndexKey: "myindex", 26 splunkCAPathKey: "/usr/cert.pem", 27 splunkCANameKey: "ca_name", 28 splunkInsecureSkipVerifyKey: "true", 29 splunkFormatKey: "json", 30 splunkVerifyConnectionKey: "true", 31 splunkGzipCompressionKey: "true", 32 splunkGzipCompressionLevelKey: "1", 33 envKey: "a", 34 envRegexKey: "^foo", 35 labelsKey: "b", 36 tagKey: "c", 37 }) 38 if err != nil { 39 t.Fatal(err) 40 } 41 42 err = ValidateLogOpt(map[string]string{ 43 "not-supported-option": "a", 44 }) 45 if err == nil { 46 t.Fatal("Expecting error on unsupported options") 47 } 48 } 49 50 // Driver require user to specify required options 51 func TestNewMissedConfig(t *testing.T) { 52 info := logger.Info{ 53 Config: map[string]string{}, 54 } 55 _, err := New(info) 56 if err == nil { 57 t.Fatal("Logger driver should fail when no required parameters specified") 58 } 59 } 60 61 // Driver require user to specify splunk-url 62 func TestNewMissedUrl(t *testing.T) { 63 info := logger.Info{ 64 Config: map[string]string{ 65 splunkTokenKey: "4642492F-D8BD-47F1-A005-0C08AE4657DF", 66 }, 67 } 68 _, err := New(info) 69 if err.Error() != "splunk: splunk-url is expected" { 70 t.Fatal("Logger driver should fail when no required parameters specified") 71 } 72 } 73 74 // Driver require user to specify splunk-token 75 func TestNewMissedToken(t *testing.T) { 76 info := logger.Info{ 77 Config: map[string]string{ 78 splunkURLKey: "http://127.0.0.1:8088", 79 }, 80 } 81 _, err := New(info) 82 if err.Error() != "splunk: splunk-token is expected" { 83 t.Fatal("Logger driver should fail when no required parameters specified") 84 } 85 } 86 87 func TestNewWithProxy(t *testing.T) { 88 proxy := "http://proxy.testing:8888" 89 reset := env.Patch(t, "HTTP_PROXY", proxy) 90 defer reset() 91 92 // must not be localhost 93 splunkURL := "http://example.com:12345" 94 logger, err := New(logger.Info{ 95 Config: map[string]string{ 96 splunkURLKey: splunkURL, 97 splunkTokenKey: "token", 98 splunkVerifyConnectionKey: "false", 99 }, 100 ContainerID: "containeriid", 101 }) 102 require.NoError(t, err) 103 splunkLogger := logger.(*splunkLoggerInline) 104 105 proxyFunc := splunkLogger.transport.Proxy 106 require.NotNil(t, proxyFunc) 107 108 req, err := http.NewRequest("GET", splunkURL, nil) 109 require.NoError(t, err) 110 111 proxyURL, err := proxyFunc(req) 112 require.NoError(t, err) 113 require.NotNil(t, proxyURL) 114 require.Equal(t, proxy, proxyURL.String()) 115 } 116 117 // Test default settings 118 func TestDefault(t *testing.T) { 119 hec := NewHTTPEventCollectorMock(t) 120 121 go hec.Serve() 122 123 info := logger.Info{ 124 Config: map[string]string{ 125 splunkURLKey: hec.URL(), 126 splunkTokenKey: hec.token, 127 }, 128 ContainerID: "containeriid", 129 ContainerName: "container_name", 130 ContainerImageID: "contaimageid", 131 ContainerImageName: "container_image_name", 132 } 133 134 hostname, err := info.Hostname() 135 if err != nil { 136 t.Fatal(err) 137 } 138 139 loggerDriver, err := New(info) 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 if loggerDriver.Name() != driverName { 145 t.Fatal("Unexpected logger driver name") 146 } 147 148 if !hec.connectionVerified { 149 t.Fatal("By default connection should be verified") 150 } 151 152 splunkLoggerDriver, ok := loggerDriver.(*splunkLoggerInline) 153 if !ok { 154 t.Fatal("Unexpected Splunk Logging Driver type") 155 } 156 157 if splunkLoggerDriver.url != hec.URL()+"/services/collector/event/1.0" || 158 splunkLoggerDriver.auth != "Splunk "+hec.token || 159 splunkLoggerDriver.nullMessage.Host != hostname || 160 splunkLoggerDriver.nullMessage.Source != "" || 161 splunkLoggerDriver.nullMessage.SourceType != "" || 162 splunkLoggerDriver.nullMessage.Index != "" || 163 splunkLoggerDriver.gzipCompression || 164 splunkLoggerDriver.postMessagesFrequency != defaultPostMessagesFrequency || 165 splunkLoggerDriver.postMessagesBatchSize != defaultPostMessagesBatchSize || 166 splunkLoggerDriver.bufferMaximum != defaultBufferMaximum || 167 cap(splunkLoggerDriver.stream) != defaultStreamChannelSize { 168 t.Fatal("Found not default values setup in Splunk Logging Driver.") 169 } 170 171 message1Time := time.Now() 172 if err := loggerDriver.Log(&logger.Message{Line: []byte("{\"a\":\"b\"}"), Source: "stdout", Timestamp: message1Time}); err != nil { 173 t.Fatal(err) 174 } 175 message2Time := time.Now() 176 if err := loggerDriver.Log(&logger.Message{Line: []byte("notajson"), Source: "stdout", Timestamp: message2Time}); err != nil { 177 t.Fatal(err) 178 } 179 180 err = loggerDriver.Close() 181 if err != nil { 182 t.Fatal(err) 183 } 184 185 if len(hec.messages) != 2 { 186 t.Fatal("Expected two messages") 187 } 188 189 if *hec.gzipEnabled { 190 t.Fatal("Gzip should not be used") 191 } 192 193 message1 := hec.messages[0] 194 if message1.Time != fmt.Sprintf("%f", float64(message1Time.UnixNano())/float64(time.Second)) || 195 message1.Host != hostname || 196 message1.Source != "" || 197 message1.SourceType != "" || 198 message1.Index != "" { 199 t.Fatalf("Unexpected values of message 1 %v", message1) 200 } 201 202 if event, err := message1.EventAsMap(); err != nil { 203 t.Fatal(err) 204 } else { 205 if event["line"] != "{\"a\":\"b\"}" || 206 event["source"] != "stdout" || 207 event["tag"] != "containeriid" || 208 len(event) != 3 { 209 t.Fatalf("Unexpected event in message %v", event) 210 } 211 } 212 213 message2 := hec.messages[1] 214 if message2.Time != fmt.Sprintf("%f", float64(message2Time.UnixNano())/float64(time.Second)) || 215 message2.Host != hostname || 216 message2.Source != "" || 217 message2.SourceType != "" || 218 message2.Index != "" { 219 t.Fatalf("Unexpected values of message 1 %v", message2) 220 } 221 222 if event, err := message2.EventAsMap(); err != nil { 223 t.Fatal(err) 224 } else { 225 if event["line"] != "notajson" || 226 event["source"] != "stdout" || 227 event["tag"] != "containeriid" || 228 len(event) != 3 { 229 t.Fatalf("Unexpected event in message %v", event) 230 } 231 } 232 233 err = hec.Close() 234 if err != nil { 235 t.Fatal(err) 236 } 237 } 238 239 // Verify inline format with a not default settings for most of options 240 func TestInlineFormatWithNonDefaultOptions(t *testing.T) { 241 hec := NewHTTPEventCollectorMock(t) 242 243 go hec.Serve() 244 245 info := logger.Info{ 246 Config: map[string]string{ 247 splunkURLKey: hec.URL(), 248 splunkTokenKey: hec.token, 249 splunkSourceKey: "mysource", 250 splunkSourceTypeKey: "mysourcetype", 251 splunkIndexKey: "myindex", 252 splunkFormatKey: splunkFormatInline, 253 splunkGzipCompressionKey: "true", 254 tagKey: "{{.ImageName}}/{{.Name}}", 255 labelsKey: "a", 256 envRegexKey: "^foo", 257 }, 258 ContainerID: "containeriid", 259 ContainerName: "/container_name", 260 ContainerImageID: "contaimageid", 261 ContainerImageName: "container_image_name", 262 ContainerLabels: map[string]string{ 263 "a": "b", 264 }, 265 ContainerEnv: []string{"foo_finder=bar"}, 266 } 267 268 hostname, err := info.Hostname() 269 if err != nil { 270 t.Fatal(err) 271 } 272 273 loggerDriver, err := New(info) 274 if err != nil { 275 t.Fatal(err) 276 } 277 278 if !hec.connectionVerified { 279 t.Fatal("By default connection should be verified") 280 } 281 282 splunkLoggerDriver, ok := loggerDriver.(*splunkLoggerInline) 283 if !ok { 284 t.Fatal("Unexpected Splunk Logging Driver type") 285 } 286 287 if splunkLoggerDriver.url != hec.URL()+"/services/collector/event/1.0" || 288 splunkLoggerDriver.auth != "Splunk "+hec.token || 289 splunkLoggerDriver.nullMessage.Host != hostname || 290 splunkLoggerDriver.nullMessage.Source != "mysource" || 291 splunkLoggerDriver.nullMessage.SourceType != "mysourcetype" || 292 splunkLoggerDriver.nullMessage.Index != "myindex" || 293 !splunkLoggerDriver.gzipCompression || 294 splunkLoggerDriver.gzipCompressionLevel != gzip.DefaultCompression || 295 splunkLoggerDriver.postMessagesFrequency != defaultPostMessagesFrequency || 296 splunkLoggerDriver.postMessagesBatchSize != defaultPostMessagesBatchSize || 297 splunkLoggerDriver.bufferMaximum != defaultBufferMaximum || 298 cap(splunkLoggerDriver.stream) != defaultStreamChannelSize { 299 t.Fatal("Values do not match configuration.") 300 } 301 302 messageTime := time.Now() 303 if err := loggerDriver.Log(&logger.Message{Line: []byte("1"), Source: "stdout", Timestamp: messageTime}); err != nil { 304 t.Fatal(err) 305 } 306 307 err = loggerDriver.Close() 308 if err != nil { 309 t.Fatal(err) 310 } 311 312 if len(hec.messages) != 1 { 313 t.Fatal("Expected one message") 314 } 315 316 if !*hec.gzipEnabled { 317 t.Fatal("Gzip should be used") 318 } 319 320 message := hec.messages[0] 321 if message.Time != fmt.Sprintf("%f", float64(messageTime.UnixNano())/float64(time.Second)) || 322 message.Host != hostname || 323 message.Source != "mysource" || 324 message.SourceType != "mysourcetype" || 325 message.Index != "myindex" { 326 t.Fatalf("Unexpected values of message %v", message) 327 } 328 329 if event, err := message.EventAsMap(); err != nil { 330 t.Fatal(err) 331 } else { 332 if event["line"] != "1" || 333 event["source"] != "stdout" || 334 event["tag"] != "container_image_name/container_name" || 335 event["attrs"].(map[string]interface{})["a"] != "b" || 336 event["attrs"].(map[string]interface{})["foo_finder"] != "bar" || 337 len(event) != 4 { 338 t.Fatalf("Unexpected event in message %v", event) 339 } 340 } 341 342 err = hec.Close() 343 if err != nil { 344 t.Fatal(err) 345 } 346 } 347 348 // Verify JSON format 349 func TestJsonFormat(t *testing.T) { 350 hec := NewHTTPEventCollectorMock(t) 351 352 go hec.Serve() 353 354 info := logger.Info{ 355 Config: map[string]string{ 356 splunkURLKey: hec.URL(), 357 splunkTokenKey: hec.token, 358 splunkFormatKey: splunkFormatJSON, 359 splunkGzipCompressionKey: "true", 360 splunkGzipCompressionLevelKey: "1", 361 }, 362 ContainerID: "containeriid", 363 ContainerName: "/container_name", 364 ContainerImageID: "contaimageid", 365 ContainerImageName: "container_image_name", 366 } 367 368 hostname, err := info.Hostname() 369 if err != nil { 370 t.Fatal(err) 371 } 372 373 loggerDriver, err := New(info) 374 if err != nil { 375 t.Fatal(err) 376 } 377 378 if !hec.connectionVerified { 379 t.Fatal("By default connection should be verified") 380 } 381 382 splunkLoggerDriver, ok := loggerDriver.(*splunkLoggerJSON) 383 if !ok { 384 t.Fatal("Unexpected Splunk Logging Driver type") 385 } 386 387 if splunkLoggerDriver.url != hec.URL()+"/services/collector/event/1.0" || 388 splunkLoggerDriver.auth != "Splunk "+hec.token || 389 splunkLoggerDriver.nullMessage.Host != hostname || 390 splunkLoggerDriver.nullMessage.Source != "" || 391 splunkLoggerDriver.nullMessage.SourceType != "" || 392 splunkLoggerDriver.nullMessage.Index != "" || 393 !splunkLoggerDriver.gzipCompression || 394 splunkLoggerDriver.gzipCompressionLevel != gzip.BestSpeed || 395 splunkLoggerDriver.postMessagesFrequency != defaultPostMessagesFrequency || 396 splunkLoggerDriver.postMessagesBatchSize != defaultPostMessagesBatchSize || 397 splunkLoggerDriver.bufferMaximum != defaultBufferMaximum || 398 cap(splunkLoggerDriver.stream) != defaultStreamChannelSize { 399 t.Fatal("Values do not match configuration.") 400 } 401 402 message1Time := time.Now() 403 if err := loggerDriver.Log(&logger.Message{Line: []byte("{\"a\":\"b\"}"), Source: "stdout", Timestamp: message1Time}); err != nil { 404 t.Fatal(err) 405 } 406 message2Time := time.Now() 407 if err := loggerDriver.Log(&logger.Message{Line: []byte("notjson"), Source: "stdout", Timestamp: message2Time}); err != nil { 408 t.Fatal(err) 409 } 410 411 err = loggerDriver.Close() 412 if err != nil { 413 t.Fatal(err) 414 } 415 416 if len(hec.messages) != 2 { 417 t.Fatal("Expected two messages") 418 } 419 420 message1 := hec.messages[0] 421 if message1.Time != fmt.Sprintf("%f", float64(message1Time.UnixNano())/float64(time.Second)) || 422 message1.Host != hostname || 423 message1.Source != "" || 424 message1.SourceType != "" || 425 message1.Index != "" { 426 t.Fatalf("Unexpected values of message 1 %v", message1) 427 } 428 429 if event, err := message1.EventAsMap(); err != nil { 430 t.Fatal(err) 431 } else { 432 if event["line"].(map[string]interface{})["a"] != "b" || 433 event["source"] != "stdout" || 434 event["tag"] != "containeriid" || 435 len(event) != 3 { 436 t.Fatalf("Unexpected event in message 1 %v", event) 437 } 438 } 439 440 message2 := hec.messages[1] 441 if message2.Time != fmt.Sprintf("%f", float64(message2Time.UnixNano())/float64(time.Second)) || 442 message2.Host != hostname || 443 message2.Source != "" || 444 message2.SourceType != "" || 445 message2.Index != "" { 446 t.Fatalf("Unexpected values of message 2 %v", message2) 447 } 448 449 // If message cannot be parsed as JSON - it should be sent as a line 450 if event, err := message2.EventAsMap(); err != nil { 451 t.Fatal(err) 452 } else { 453 if event["line"] != "notjson" || 454 event["source"] != "stdout" || 455 event["tag"] != "containeriid" || 456 len(event) != 3 { 457 t.Fatalf("Unexpected event in message 2 %v", event) 458 } 459 } 460 461 err = hec.Close() 462 if err != nil { 463 t.Fatal(err) 464 } 465 } 466 467 // Verify raw format 468 func TestRawFormat(t *testing.T) { 469 hec := NewHTTPEventCollectorMock(t) 470 471 go hec.Serve() 472 473 info := logger.Info{ 474 Config: map[string]string{ 475 splunkURLKey: hec.URL(), 476 splunkTokenKey: hec.token, 477 splunkFormatKey: splunkFormatRaw, 478 }, 479 ContainerID: "containeriid", 480 ContainerName: "/container_name", 481 ContainerImageID: "contaimageid", 482 ContainerImageName: "container_image_name", 483 } 484 485 hostname, err := info.Hostname() 486 require.NoError(t, err) 487 488 loggerDriver, err := New(info) 489 require.NoError(t, err) 490 491 if !hec.connectionVerified { 492 t.Fatal("By default connection should be verified") 493 } 494 495 splunkLoggerDriver, ok := loggerDriver.(*splunkLoggerRaw) 496 if !ok { 497 t.Fatal("Unexpected Splunk Logging Driver type") 498 } 499 500 if splunkLoggerDriver.url != hec.URL()+"/services/collector/event/1.0" || 501 splunkLoggerDriver.auth != "Splunk "+hec.token || 502 splunkLoggerDriver.nullMessage.Host != hostname || 503 splunkLoggerDriver.nullMessage.Source != "" || 504 splunkLoggerDriver.nullMessage.SourceType != "" || 505 splunkLoggerDriver.nullMessage.Index != "" || 506 splunkLoggerDriver.gzipCompression || 507 splunkLoggerDriver.postMessagesFrequency != defaultPostMessagesFrequency || 508 splunkLoggerDriver.postMessagesBatchSize != defaultPostMessagesBatchSize || 509 splunkLoggerDriver.bufferMaximum != defaultBufferMaximum || 510 cap(splunkLoggerDriver.stream) != defaultStreamChannelSize || 511 string(splunkLoggerDriver.prefix) != "containeriid " { 512 t.Fatal("Values do not match configuration.") 513 } 514 515 message1Time := time.Now() 516 if err := loggerDriver.Log(&logger.Message{Line: []byte("{\"a\":\"b\"}"), Source: "stdout", Timestamp: message1Time}); err != nil { 517 t.Fatal(err) 518 } 519 message2Time := time.Now() 520 if err := loggerDriver.Log(&logger.Message{Line: []byte("notjson"), Source: "stdout", Timestamp: message2Time}); err != nil { 521 t.Fatal(err) 522 } 523 524 err = loggerDriver.Close() 525 if err != nil { 526 t.Fatal(err) 527 } 528 529 if len(hec.messages) != 2 { 530 t.Fatal("Expected two messages") 531 } 532 533 message1 := hec.messages[0] 534 if message1.Time != fmt.Sprintf("%f", float64(message1Time.UnixNano())/float64(time.Second)) || 535 message1.Host != hostname || 536 message1.Source != "" || 537 message1.SourceType != "" || 538 message1.Index != "" { 539 t.Fatalf("Unexpected values of message 1 %v", message1) 540 } 541 542 if event, err := message1.EventAsString(); err != nil { 543 t.Fatal(err) 544 } else { 545 if event != "containeriid {\"a\":\"b\"}" { 546 t.Fatalf("Unexpected event in message 1 %v", event) 547 } 548 } 549 550 message2 := hec.messages[1] 551 if message2.Time != fmt.Sprintf("%f", float64(message2Time.UnixNano())/float64(time.Second)) || 552 message2.Host != hostname || 553 message2.Source != "" || 554 message2.SourceType != "" || 555 message2.Index != "" { 556 t.Fatalf("Unexpected values of message 2 %v", message2) 557 } 558 559 if event, err := message2.EventAsString(); err != nil { 560 t.Fatal(err) 561 } else { 562 if event != "containeriid notjson" { 563 t.Fatalf("Unexpected event in message 1 %v", event) 564 } 565 } 566 567 err = hec.Close() 568 if err != nil { 569 t.Fatal(err) 570 } 571 } 572 573 // Verify raw format with labels 574 func TestRawFormatWithLabels(t *testing.T) { 575 hec := NewHTTPEventCollectorMock(t) 576 577 go hec.Serve() 578 579 info := logger.Info{ 580 Config: map[string]string{ 581 splunkURLKey: hec.URL(), 582 splunkTokenKey: hec.token, 583 splunkFormatKey: splunkFormatRaw, 584 labelsKey: "a", 585 }, 586 ContainerID: "containeriid", 587 ContainerName: "/container_name", 588 ContainerImageID: "contaimageid", 589 ContainerImageName: "container_image_name", 590 ContainerLabels: map[string]string{ 591 "a": "b", 592 }, 593 } 594 595 hostname, err := info.Hostname() 596 if err != nil { 597 t.Fatal(err) 598 } 599 600 loggerDriver, err := New(info) 601 if err != nil { 602 t.Fatal(err) 603 } 604 605 if !hec.connectionVerified { 606 t.Fatal("By default connection should be verified") 607 } 608 609 splunkLoggerDriver, ok := loggerDriver.(*splunkLoggerRaw) 610 if !ok { 611 t.Fatal("Unexpected Splunk Logging Driver type") 612 } 613 614 if splunkLoggerDriver.url != hec.URL()+"/services/collector/event/1.0" || 615 splunkLoggerDriver.auth != "Splunk "+hec.token || 616 splunkLoggerDriver.nullMessage.Host != hostname || 617 splunkLoggerDriver.nullMessage.Source != "" || 618 splunkLoggerDriver.nullMessage.SourceType != "" || 619 splunkLoggerDriver.nullMessage.Index != "" || 620 splunkLoggerDriver.gzipCompression || 621 splunkLoggerDriver.postMessagesFrequency != defaultPostMessagesFrequency || 622 splunkLoggerDriver.postMessagesBatchSize != defaultPostMessagesBatchSize || 623 splunkLoggerDriver.bufferMaximum != defaultBufferMaximum || 624 cap(splunkLoggerDriver.stream) != defaultStreamChannelSize || 625 string(splunkLoggerDriver.prefix) != "containeriid a=b " { 626 t.Fatal("Values do not match configuration.") 627 } 628 629 message1Time := time.Now() 630 if err := loggerDriver.Log(&logger.Message{Line: []byte("{\"a\":\"b\"}"), Source: "stdout", Timestamp: message1Time}); err != nil { 631 t.Fatal(err) 632 } 633 message2Time := time.Now() 634 if err := loggerDriver.Log(&logger.Message{Line: []byte("notjson"), Source: "stdout", Timestamp: message2Time}); err != nil { 635 t.Fatal(err) 636 } 637 638 err = loggerDriver.Close() 639 if err != nil { 640 t.Fatal(err) 641 } 642 643 if len(hec.messages) != 2 { 644 t.Fatal("Expected two messages") 645 } 646 647 message1 := hec.messages[0] 648 if message1.Time != fmt.Sprintf("%f", float64(message1Time.UnixNano())/float64(time.Second)) || 649 message1.Host != hostname || 650 message1.Source != "" || 651 message1.SourceType != "" || 652 message1.Index != "" { 653 t.Fatalf("Unexpected values of message 1 %v", message1) 654 } 655 656 if event, err := message1.EventAsString(); err != nil { 657 t.Fatal(err) 658 } else { 659 if event != "containeriid a=b {\"a\":\"b\"}" { 660 t.Fatalf("Unexpected event in message 1 %v", event) 661 } 662 } 663 664 message2 := hec.messages[1] 665 if message2.Time != fmt.Sprintf("%f", float64(message2Time.UnixNano())/float64(time.Second)) || 666 message2.Host != hostname || 667 message2.Source != "" || 668 message2.SourceType != "" || 669 message2.Index != "" { 670 t.Fatalf("Unexpected values of message 2 %v", message2) 671 } 672 673 if event, err := message2.EventAsString(); err != nil { 674 t.Fatal(err) 675 } else { 676 if event != "containeriid a=b notjson" { 677 t.Fatalf("Unexpected event in message 2 %v", event) 678 } 679 } 680 681 err = hec.Close() 682 if err != nil { 683 t.Fatal(err) 684 } 685 } 686 687 // Verify that Splunk Logging Driver can accept tag="" which will allow to send raw messages 688 // in the same way we get them in stdout/stderr 689 func TestRawFormatWithoutTag(t *testing.T) { 690 hec := NewHTTPEventCollectorMock(t) 691 692 go hec.Serve() 693 694 info := logger.Info{ 695 Config: map[string]string{ 696 splunkURLKey: hec.URL(), 697 splunkTokenKey: hec.token, 698 splunkFormatKey: splunkFormatRaw, 699 tagKey: "", 700 }, 701 ContainerID: "containeriid", 702 ContainerName: "/container_name", 703 ContainerImageID: "contaimageid", 704 ContainerImageName: "container_image_name", 705 } 706 707 hostname, err := info.Hostname() 708 if err != nil { 709 t.Fatal(err) 710 } 711 712 loggerDriver, err := New(info) 713 if err != nil { 714 t.Fatal(err) 715 } 716 717 if !hec.connectionVerified { 718 t.Fatal("By default connection should be verified") 719 } 720 721 splunkLoggerDriver, ok := loggerDriver.(*splunkLoggerRaw) 722 if !ok { 723 t.Fatal("Unexpected Splunk Logging Driver type") 724 } 725 726 if splunkLoggerDriver.url != hec.URL()+"/services/collector/event/1.0" || 727 splunkLoggerDriver.auth != "Splunk "+hec.token || 728 splunkLoggerDriver.nullMessage.Host != hostname || 729 splunkLoggerDriver.nullMessage.Source != "" || 730 splunkLoggerDriver.nullMessage.SourceType != "" || 731 splunkLoggerDriver.nullMessage.Index != "" || 732 splunkLoggerDriver.gzipCompression || 733 splunkLoggerDriver.postMessagesFrequency != defaultPostMessagesFrequency || 734 splunkLoggerDriver.postMessagesBatchSize != defaultPostMessagesBatchSize || 735 splunkLoggerDriver.bufferMaximum != defaultBufferMaximum || 736 cap(splunkLoggerDriver.stream) != defaultStreamChannelSize || 737 string(splunkLoggerDriver.prefix) != "" { 738 t.Log(string(splunkLoggerDriver.prefix) + "a") 739 t.Fatal("Values do not match configuration.") 740 } 741 742 message1Time := time.Now() 743 if err := loggerDriver.Log(&logger.Message{Line: []byte("{\"a\":\"b\"}"), Source: "stdout", Timestamp: message1Time}); err != nil { 744 t.Fatal(err) 745 } 746 message2Time := time.Now() 747 if err := loggerDriver.Log(&logger.Message{Line: []byte("notjson"), Source: "stdout", Timestamp: message2Time}); err != nil { 748 t.Fatal(err) 749 } 750 message3Time := time.Now() 751 if err := loggerDriver.Log(&logger.Message{Line: []byte(" "), Source: "stdout", Timestamp: message3Time}); err != nil { 752 t.Fatal(err) 753 } 754 755 err = loggerDriver.Close() 756 if err != nil { 757 t.Fatal(err) 758 } 759 760 // message3 would have an empty or whitespace only string in the "event" field 761 // both of which are not acceptable to HEC 762 // thus here we must expect 2 messages, not 3 763 if len(hec.messages) != 2 { 764 t.Fatal("Expected two messages") 765 } 766 767 message1 := hec.messages[0] 768 if message1.Time != fmt.Sprintf("%f", float64(message1Time.UnixNano())/float64(time.Second)) || 769 message1.Host != hostname || 770 message1.Source != "" || 771 message1.SourceType != "" || 772 message1.Index != "" { 773 t.Fatalf("Unexpected values of message 1 %v", message1) 774 } 775 776 if event, err := message1.EventAsString(); err != nil { 777 t.Fatal(err) 778 } else { 779 if event != "{\"a\":\"b\"}" { 780 t.Fatalf("Unexpected event in message 1 %v", event) 781 } 782 } 783 784 message2 := hec.messages[1] 785 if message2.Time != fmt.Sprintf("%f", float64(message2Time.UnixNano())/float64(time.Second)) || 786 message2.Host != hostname || 787 message2.Source != "" || 788 message2.SourceType != "" || 789 message2.Index != "" { 790 t.Fatalf("Unexpected values of message 2 %v", message2) 791 } 792 793 if event, err := message2.EventAsString(); err != nil { 794 t.Fatal(err) 795 } else { 796 if event != "notjson" { 797 t.Fatalf("Unexpected event in message 2 %v", event) 798 } 799 } 800 801 err = hec.Close() 802 if err != nil { 803 t.Fatal(err) 804 } 805 } 806 807 // Verify that we will send messages in batches with default batching parameters, 808 // but change frequency to be sure that numOfRequests will match expected 17 requests 809 func TestBatching(t *testing.T) { 810 if err := os.Setenv(envVarPostMessagesFrequency, "10h"); err != nil { 811 t.Fatal(err) 812 } 813 814 hec := NewHTTPEventCollectorMock(t) 815 816 go hec.Serve() 817 818 info := logger.Info{ 819 Config: map[string]string{ 820 splunkURLKey: hec.URL(), 821 splunkTokenKey: hec.token, 822 }, 823 ContainerID: "containeriid", 824 ContainerName: "/container_name", 825 ContainerImageID: "contaimageid", 826 ContainerImageName: "container_image_name", 827 } 828 829 loggerDriver, err := New(info) 830 if err != nil { 831 t.Fatal(err) 832 } 833 834 for i := 0; i < defaultStreamChannelSize*4; i++ { 835 if err := loggerDriver.Log(&logger.Message{Line: []byte(fmt.Sprintf("%d", i)), Source: "stdout", Timestamp: time.Now()}); err != nil { 836 t.Fatal(err) 837 } 838 } 839 840 err = loggerDriver.Close() 841 if err != nil { 842 t.Fatal(err) 843 } 844 845 if len(hec.messages) != defaultStreamChannelSize*4 { 846 t.Fatal("Not all messages delivered") 847 } 848 849 for i, message := range hec.messages { 850 if event, err := message.EventAsMap(); err != nil { 851 t.Fatal(err) 852 } else { 853 if event["line"] != fmt.Sprintf("%d", i) { 854 t.Fatalf("Unexpected event in message %v", event) 855 } 856 } 857 } 858 859 // 1 to verify connection and 16 batches 860 if hec.numOfRequests != 17 { 861 t.Fatalf("Unexpected number of requests %d", hec.numOfRequests) 862 } 863 864 err = hec.Close() 865 if err != nil { 866 t.Fatal(err) 867 } 868 869 if err := os.Setenv(envVarPostMessagesFrequency, ""); err != nil { 870 t.Fatal(err) 871 } 872 } 873 874 // Verify that test is using time to fire events not rare than specified frequency 875 func TestFrequency(t *testing.T) { 876 if err := os.Setenv(envVarPostMessagesFrequency, "5ms"); err != nil { 877 t.Fatal(err) 878 } 879 880 hec := NewHTTPEventCollectorMock(t) 881 882 go hec.Serve() 883 884 info := logger.Info{ 885 Config: map[string]string{ 886 splunkURLKey: hec.URL(), 887 splunkTokenKey: hec.token, 888 }, 889 ContainerID: "containeriid", 890 ContainerName: "/container_name", 891 ContainerImageID: "contaimageid", 892 ContainerImageName: "container_image_name", 893 } 894 895 loggerDriver, err := New(info) 896 if err != nil { 897 t.Fatal(err) 898 } 899 900 for i := 0; i < 10; i++ { 901 if err := loggerDriver.Log(&logger.Message{Line: []byte(fmt.Sprintf("%d", i)), Source: "stdout", Timestamp: time.Now()}); err != nil { 902 t.Fatal(err) 903 } 904 time.Sleep(15 * time.Millisecond) 905 } 906 907 err = loggerDriver.Close() 908 if err != nil { 909 t.Fatal(err) 910 } 911 912 if len(hec.messages) != 10 { 913 t.Fatal("Not all messages delivered") 914 } 915 916 for i, message := range hec.messages { 917 if event, err := message.EventAsMap(); err != nil { 918 t.Fatal(err) 919 } else { 920 if event["line"] != fmt.Sprintf("%d", i) { 921 t.Fatalf("Unexpected event in message %v", event) 922 } 923 } 924 } 925 926 // 1 to verify connection and 10 to verify that we have sent messages with required frequency, 927 // but because frequency is too small (to keep test quick), instead of 11, use 9 if context switches will be slow 928 if hec.numOfRequests < 9 { 929 t.Fatalf("Unexpected number of requests %d", hec.numOfRequests) 930 } 931 932 err = hec.Close() 933 if err != nil { 934 t.Fatal(err) 935 } 936 937 if err := os.Setenv(envVarPostMessagesFrequency, ""); err != nil { 938 t.Fatal(err) 939 } 940 } 941 942 // Simulate behavior similar to first version of Splunk Logging Driver, when we were sending one message 943 // per request 944 func TestOneMessagePerRequest(t *testing.T) { 945 if err := os.Setenv(envVarPostMessagesFrequency, "10h"); err != nil { 946 t.Fatal(err) 947 } 948 949 if err := os.Setenv(envVarPostMessagesBatchSize, "1"); err != nil { 950 t.Fatal(err) 951 } 952 953 if err := os.Setenv(envVarBufferMaximum, "1"); err != nil { 954 t.Fatal(err) 955 } 956 957 if err := os.Setenv(envVarStreamChannelSize, "0"); err != nil { 958 t.Fatal(err) 959 } 960 961 hec := NewHTTPEventCollectorMock(t) 962 963 go hec.Serve() 964 965 info := logger.Info{ 966 Config: map[string]string{ 967 splunkURLKey: hec.URL(), 968 splunkTokenKey: hec.token, 969 }, 970 ContainerID: "containeriid", 971 ContainerName: "/container_name", 972 ContainerImageID: "contaimageid", 973 ContainerImageName: "container_image_name", 974 } 975 976 loggerDriver, err := New(info) 977 if err != nil { 978 t.Fatal(err) 979 } 980 981 for i := 0; i < 10; i++ { 982 if err := loggerDriver.Log(&logger.Message{Line: []byte(fmt.Sprintf("%d", i)), Source: "stdout", Timestamp: time.Now()}); err != nil { 983 t.Fatal(err) 984 } 985 } 986 987 err = loggerDriver.Close() 988 if err != nil { 989 t.Fatal(err) 990 } 991 992 if len(hec.messages) != 10 { 993 t.Fatal("Not all messages delivered") 994 } 995 996 for i, message := range hec.messages { 997 if event, err := message.EventAsMap(); err != nil { 998 t.Fatal(err) 999 } else { 1000 if event["line"] != fmt.Sprintf("%d", i) { 1001 t.Fatalf("Unexpected event in message %v", event) 1002 } 1003 } 1004 } 1005 1006 // 1 to verify connection and 10 messages 1007 if hec.numOfRequests != 11 { 1008 t.Fatalf("Unexpected number of requests %d", hec.numOfRequests) 1009 } 1010 1011 err = hec.Close() 1012 if err != nil { 1013 t.Fatal(err) 1014 } 1015 1016 if err := os.Setenv(envVarPostMessagesFrequency, ""); err != nil { 1017 t.Fatal(err) 1018 } 1019 1020 if err := os.Setenv(envVarPostMessagesBatchSize, ""); err != nil { 1021 t.Fatal(err) 1022 } 1023 1024 if err := os.Setenv(envVarBufferMaximum, ""); err != nil { 1025 t.Fatal(err) 1026 } 1027 1028 if err := os.Setenv(envVarStreamChannelSize, ""); err != nil { 1029 t.Fatal(err) 1030 } 1031 } 1032 1033 // Driver should not be created when HEC is unresponsive 1034 func TestVerify(t *testing.T) { 1035 hec := NewHTTPEventCollectorMock(t) 1036 hec.simulateServerError = true 1037 go hec.Serve() 1038 1039 info := logger.Info{ 1040 Config: map[string]string{ 1041 splunkURLKey: hec.URL(), 1042 splunkTokenKey: hec.token, 1043 }, 1044 ContainerID: "containeriid", 1045 ContainerName: "/container_name", 1046 ContainerImageID: "contaimageid", 1047 ContainerImageName: "container_image_name", 1048 } 1049 1050 _, err := New(info) 1051 if err == nil { 1052 t.Fatal("Expecting driver to fail, when server is unresponsive") 1053 } 1054 1055 err = hec.Close() 1056 if err != nil { 1057 t.Fatal(err) 1058 } 1059 } 1060 1061 // Verify that user can specify to skip verification that Splunk HEC is working. 1062 // Also in this test we verify retry logic. 1063 func TestSkipVerify(t *testing.T) { 1064 hec := NewHTTPEventCollectorMock(t) 1065 hec.simulateServerError = true 1066 go hec.Serve() 1067 1068 info := logger.Info{ 1069 Config: map[string]string{ 1070 splunkURLKey: hec.URL(), 1071 splunkTokenKey: hec.token, 1072 splunkVerifyConnectionKey: "false", 1073 }, 1074 ContainerID: "containeriid", 1075 ContainerName: "/container_name", 1076 ContainerImageID: "contaimageid", 1077 ContainerImageName: "container_image_name", 1078 } 1079 1080 loggerDriver, err := New(info) 1081 if err != nil { 1082 t.Fatal(err) 1083 } 1084 1085 if hec.connectionVerified { 1086 t.Fatal("Connection should not be verified") 1087 } 1088 1089 for i := 0; i < defaultStreamChannelSize*2; i++ { 1090 if err := loggerDriver.Log(&logger.Message{Line: []byte(fmt.Sprintf("%d", i)), Source: "stdout", Timestamp: time.Now()}); err != nil { 1091 t.Fatal(err) 1092 } 1093 } 1094 1095 if len(hec.messages) != 0 { 1096 t.Fatal("No messages should be accepted at this point") 1097 } 1098 1099 hec.simulateErr(false) 1100 1101 for i := defaultStreamChannelSize * 2; i < defaultStreamChannelSize*4; i++ { 1102 if err := loggerDriver.Log(&logger.Message{Line: []byte(fmt.Sprintf("%d", i)), Source: "stdout", Timestamp: time.Now()}); err != nil { 1103 t.Fatal(err) 1104 } 1105 } 1106 1107 err = loggerDriver.Close() 1108 if err != nil { 1109 t.Fatal(err) 1110 } 1111 1112 if len(hec.messages) != defaultStreamChannelSize*4 { 1113 t.Fatal("Not all messages delivered") 1114 } 1115 1116 for i, message := range hec.messages { 1117 if event, err := message.EventAsMap(); err != nil { 1118 t.Fatal(err) 1119 } else { 1120 if event["line"] != fmt.Sprintf("%d", i) { 1121 t.Fatalf("Unexpected event in message %v", event) 1122 } 1123 } 1124 } 1125 1126 err = hec.Close() 1127 if err != nil { 1128 t.Fatal(err) 1129 } 1130 } 1131 1132 // Verify logic for when we filled whole buffer 1133 func TestBufferMaximum(t *testing.T) { 1134 if err := os.Setenv(envVarPostMessagesBatchSize, "2"); err != nil { 1135 t.Fatal(err) 1136 } 1137 1138 if err := os.Setenv(envVarBufferMaximum, "10"); err != nil { 1139 t.Fatal(err) 1140 } 1141 1142 if err := os.Setenv(envVarStreamChannelSize, "0"); err != nil { 1143 t.Fatal(err) 1144 } 1145 1146 hec := NewHTTPEventCollectorMock(t) 1147 hec.simulateErr(true) 1148 go hec.Serve() 1149 1150 info := logger.Info{ 1151 Config: map[string]string{ 1152 splunkURLKey: hec.URL(), 1153 splunkTokenKey: hec.token, 1154 splunkVerifyConnectionKey: "false", 1155 }, 1156 ContainerID: "containeriid", 1157 ContainerName: "/container_name", 1158 ContainerImageID: "contaimageid", 1159 ContainerImageName: "container_image_name", 1160 } 1161 1162 loggerDriver, err := New(info) 1163 if err != nil { 1164 t.Fatal(err) 1165 } 1166 1167 if hec.connectionVerified { 1168 t.Fatal("Connection should not be verified") 1169 } 1170 1171 for i := 0; i < 11; i++ { 1172 if err := loggerDriver.Log(&logger.Message{Line: []byte(fmt.Sprintf("%d", i)), Source: "stdout", Timestamp: time.Now()}); err != nil { 1173 t.Fatal(err) 1174 } 1175 } 1176 1177 if len(hec.messages) != 0 { 1178 t.Fatal("No messages should be accepted at this point") 1179 } 1180 1181 hec.simulateServerError = false 1182 1183 err = loggerDriver.Close() 1184 if err != nil { 1185 t.Fatal(err) 1186 } 1187 1188 if len(hec.messages) != 9 { 1189 t.Fatalf("Expected # of messages %d, got %d", 9, len(hec.messages)) 1190 } 1191 1192 // First 1000 messages are written to daemon log when buffer was full 1193 for i, message := range hec.messages { 1194 if event, err := message.EventAsMap(); err != nil { 1195 t.Fatal(err) 1196 } else { 1197 if event["line"] != fmt.Sprintf("%d", i+2) { 1198 t.Fatalf("Unexpected event in message %v", event) 1199 } 1200 } 1201 } 1202 1203 err = hec.Close() 1204 if err != nil { 1205 t.Fatal(err) 1206 } 1207 1208 if err := os.Setenv(envVarPostMessagesBatchSize, ""); err != nil { 1209 t.Fatal(err) 1210 } 1211 1212 if err := os.Setenv(envVarBufferMaximum, ""); err != nil { 1213 t.Fatal(err) 1214 } 1215 1216 if err := os.Setenv(envVarStreamChannelSize, ""); err != nil { 1217 t.Fatal(err) 1218 } 1219 } 1220 1221 // Verify that we are not blocking close when HEC is down for the whole time 1222 func TestServerAlwaysDown(t *testing.T) { 1223 if err := os.Setenv(envVarPostMessagesBatchSize, "2"); err != nil { 1224 t.Fatal(err) 1225 } 1226 1227 if err := os.Setenv(envVarBufferMaximum, "4"); err != nil { 1228 t.Fatal(err) 1229 } 1230 1231 if err := os.Setenv(envVarStreamChannelSize, "0"); err != nil { 1232 t.Fatal(err) 1233 } 1234 1235 hec := NewHTTPEventCollectorMock(t) 1236 hec.simulateServerError = true 1237 go hec.Serve() 1238 1239 info := logger.Info{ 1240 Config: map[string]string{ 1241 splunkURLKey: hec.URL(), 1242 splunkTokenKey: hec.token, 1243 splunkVerifyConnectionKey: "false", 1244 }, 1245 ContainerID: "containeriid", 1246 ContainerName: "/container_name", 1247 ContainerImageID: "contaimageid", 1248 ContainerImageName: "container_image_name", 1249 } 1250 1251 loggerDriver, err := New(info) 1252 if err != nil { 1253 t.Fatal(err) 1254 } 1255 1256 if hec.connectionVerified { 1257 t.Fatal("Connection should not be verified") 1258 } 1259 1260 for i := 0; i < 5; i++ { 1261 if err := loggerDriver.Log(&logger.Message{Line: []byte(fmt.Sprintf("%d", i)), Source: "stdout", Timestamp: time.Now()}); err != nil { 1262 t.Fatal(err) 1263 } 1264 } 1265 1266 err = loggerDriver.Close() 1267 if err != nil { 1268 t.Fatal(err) 1269 } 1270 1271 if len(hec.messages) != 0 { 1272 t.Fatal("No messages should be sent") 1273 } 1274 1275 err = hec.Close() 1276 if err != nil { 1277 t.Fatal(err) 1278 } 1279 1280 if err := os.Setenv(envVarPostMessagesBatchSize, ""); err != nil { 1281 t.Fatal(err) 1282 } 1283 1284 if err := os.Setenv(envVarBufferMaximum, ""); err != nil { 1285 t.Fatal(err) 1286 } 1287 1288 if err := os.Setenv(envVarStreamChannelSize, ""); err != nil { 1289 t.Fatal(err) 1290 } 1291 } 1292 1293 // Cannot send messages after we close driver 1294 func TestCannotSendAfterClose(t *testing.T) { 1295 hec := NewHTTPEventCollectorMock(t) 1296 go hec.Serve() 1297 1298 info := logger.Info{ 1299 Config: map[string]string{ 1300 splunkURLKey: hec.URL(), 1301 splunkTokenKey: hec.token, 1302 }, 1303 ContainerID: "containeriid", 1304 ContainerName: "/container_name", 1305 ContainerImageID: "contaimageid", 1306 ContainerImageName: "container_image_name", 1307 } 1308 1309 loggerDriver, err := New(info) 1310 if err != nil { 1311 t.Fatal(err) 1312 } 1313 1314 if err := loggerDriver.Log(&logger.Message{Line: []byte("message1"), Source: "stdout", Timestamp: time.Now()}); err != nil { 1315 t.Fatal(err) 1316 } 1317 1318 err = loggerDriver.Close() 1319 if err != nil { 1320 t.Fatal(err) 1321 } 1322 1323 if err := loggerDriver.Log(&logger.Message{Line: []byte("message2"), Source: "stdout", Timestamp: time.Now()}); err == nil { 1324 t.Fatal("Driver should not allow to send messages after close") 1325 } 1326 1327 if len(hec.messages) != 1 { 1328 t.Fatal("Only one message should be sent") 1329 } 1330 1331 message := hec.messages[0] 1332 if event, err := message.EventAsMap(); err != nil { 1333 t.Fatal(err) 1334 } else { 1335 if event["line"] != "message1" { 1336 t.Fatalf("Unexpected event in message %v", event) 1337 } 1338 } 1339 1340 err = hec.Close() 1341 if err != nil { 1342 t.Fatal(err) 1343 } 1344 } 1345 1346 func TestDeadlockOnBlockedEndpoint(t *testing.T) { 1347 hec := NewHTTPEventCollectorMock(t) 1348 go hec.Serve() 1349 info := logger.Info{ 1350 Config: map[string]string{ 1351 splunkURLKey: hec.URL(), 1352 splunkTokenKey: hec.token, 1353 }, 1354 ContainerID: "containeriid", 1355 ContainerName: "/container_name", 1356 ContainerImageID: "contaimageid", 1357 ContainerImageName: "container_image_name", 1358 } 1359 1360 l, err := New(info) 1361 if err != nil { 1362 t.Fatal(err) 1363 } 1364 1365 ctx, unblock := context.WithCancel(context.Background()) 1366 hec.withBlock(ctx) 1367 defer unblock() 1368 1369 batchSendTimeout = 1 * time.Second 1370 1371 if err := l.Log(&logger.Message{}); err != nil { 1372 t.Fatal(err) 1373 } 1374 1375 done := make(chan struct{}) 1376 go func() { 1377 l.Close() 1378 close(done) 1379 }() 1380 1381 select { 1382 case <-time.After(60 * time.Second): 1383 buf := make([]byte, 1e6) 1384 buf = buf[:runtime.Stack(buf, true)] 1385 t.Logf("STACK DUMP: \n\n%s\n\n", string(buf)) 1386 t.Fatal("timeout waiting for close to finish") 1387 case <-done: 1388 } 1389 }