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