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