github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/daemon/logger/awslogs/cloudwatchlogs_test.go (about) 1 package awslogs 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "reflect" 8 "runtime" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/aws/aws-sdk-go/aws" 14 "github.com/aws/aws-sdk-go/aws/awserr" 15 "github.com/aws/aws-sdk-go/aws/request" 16 "github.com/aws/aws-sdk-go/service/cloudwatchlogs" 17 "github.com/docker/docker/daemon/logger" 18 "github.com/docker/docker/dockerversion" 19 ) 20 21 const ( 22 groupName = "groupName" 23 streamName = "streamName" 24 sequenceToken = "sequenceToken" 25 nextSequenceToken = "nextSequenceToken" 26 logline = "this is a log line" 27 ) 28 29 func TestNewAWSLogsClientUserAgentHandler(t *testing.T) { 30 ctx := logger.Context{ 31 Config: map[string]string{ 32 regionKey: "us-east-1", 33 }, 34 } 35 36 client, err := newAWSLogsClient(ctx) 37 if err != nil { 38 t.Fatal(err) 39 } 40 realClient, ok := client.(*cloudwatchlogs.CloudWatchLogs) 41 if !ok { 42 t.Fatal("Could not cast client to cloudwatchlogs.CloudWatchLogs") 43 } 44 buildHandlerList := realClient.Handlers.Build 45 request := &request.Request{ 46 HTTPRequest: &http.Request{ 47 Header: http.Header{}, 48 }, 49 } 50 buildHandlerList.Run(request) 51 expectedUserAgentString := fmt.Sprintf("Docker %s (%s) %s/%s (%s; %s; %s)", 52 dockerversion.Version, runtime.GOOS, aws.SDKName, aws.SDKVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH) 53 userAgent := request.HTTPRequest.Header.Get("User-Agent") 54 if userAgent != expectedUserAgentString { 55 t.Errorf("Wrong User-Agent string, expected \"%s\" but was \"%s\"", 56 expectedUserAgentString, userAgent) 57 } 58 } 59 60 func TestNewAWSLogsClientRegionDetect(t *testing.T) { 61 ctx := logger.Context{ 62 Config: map[string]string{}, 63 } 64 65 mockMetadata := newMockMetadataClient() 66 newRegionFinder = func() regionFinder { 67 return mockMetadata 68 } 69 mockMetadata.regionResult <- ®ionResult{ 70 successResult: "us-east-1", 71 } 72 73 _, err := newAWSLogsClient(ctx) 74 if err != nil { 75 t.Fatal(err) 76 } 77 } 78 79 func TestCreateSuccess(t *testing.T) { 80 mockClient := newMockClient() 81 stream := &logStream{ 82 client: mockClient, 83 logGroupName: groupName, 84 logStreamName: streamName, 85 } 86 mockClient.createLogStreamResult <- &createLogStreamResult{} 87 88 err := stream.create() 89 90 if err != nil { 91 t.Errorf("Received unexpected err: %v\n", err) 92 } 93 argument := <-mockClient.createLogStreamArgument 94 if argument.LogGroupName == nil { 95 t.Fatal("Expected non-nil LogGroupName") 96 } 97 if *argument.LogGroupName != groupName { 98 t.Errorf("Expected LogGroupName to be %s", groupName) 99 } 100 if argument.LogStreamName == nil { 101 t.Fatal("Expected non-nil LogGroupName") 102 } 103 if *argument.LogStreamName != streamName { 104 t.Errorf("Expected LogStreamName to be %s", streamName) 105 } 106 } 107 108 func TestCreateError(t *testing.T) { 109 mockClient := newMockClient() 110 stream := &logStream{ 111 client: mockClient, 112 } 113 mockClient.createLogStreamResult <- &createLogStreamResult{ 114 errorResult: errors.New("Error!"), 115 } 116 117 err := stream.create() 118 119 if err == nil { 120 t.Fatal("Expected non-nil err") 121 } 122 } 123 124 func TestCreateAlreadyExists(t *testing.T) { 125 mockClient := newMockClient() 126 stream := &logStream{ 127 client: mockClient, 128 } 129 mockClient.createLogStreamResult <- &createLogStreamResult{ 130 errorResult: awserr.New(resourceAlreadyExistsCode, "", nil), 131 } 132 133 err := stream.create() 134 135 if err != nil { 136 t.Fatal("Expected nil err") 137 } 138 } 139 140 func TestPublishBatchSuccess(t *testing.T) { 141 mockClient := newMockClient() 142 stream := &logStream{ 143 client: mockClient, 144 logGroupName: groupName, 145 logStreamName: streamName, 146 sequenceToken: aws.String(sequenceToken), 147 } 148 mockClient.putLogEventsResult <- &putLogEventsResult{ 149 successResult: &cloudwatchlogs.PutLogEventsOutput{ 150 NextSequenceToken: aws.String(nextSequenceToken), 151 }, 152 } 153 events := []wrappedEvent{ 154 { 155 inputLogEvent: &cloudwatchlogs.InputLogEvent{ 156 Message: aws.String(logline), 157 }, 158 }, 159 } 160 161 stream.publishBatch(events) 162 if stream.sequenceToken == nil { 163 t.Fatal("Expected non-nil sequenceToken") 164 } 165 if *stream.sequenceToken != nextSequenceToken { 166 t.Errorf("Expected sequenceToken to be %s, but was %s", nextSequenceToken, *stream.sequenceToken) 167 } 168 argument := <-mockClient.putLogEventsArgument 169 if argument == nil { 170 t.Fatal("Expected non-nil PutLogEventsInput") 171 } 172 if argument.SequenceToken == nil { 173 t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken") 174 } 175 if *argument.SequenceToken != sequenceToken { 176 t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", sequenceToken, *argument.SequenceToken) 177 } 178 if len(argument.LogEvents) != 1 { 179 t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents)) 180 } 181 if argument.LogEvents[0] != events[0].inputLogEvent { 182 t.Error("Expected event to equal input") 183 } 184 } 185 186 func TestPublishBatchError(t *testing.T) { 187 mockClient := newMockClient() 188 stream := &logStream{ 189 client: mockClient, 190 logGroupName: groupName, 191 logStreamName: streamName, 192 sequenceToken: aws.String(sequenceToken), 193 } 194 mockClient.putLogEventsResult <- &putLogEventsResult{ 195 errorResult: errors.New("Error!"), 196 } 197 198 events := []wrappedEvent{ 199 { 200 inputLogEvent: &cloudwatchlogs.InputLogEvent{ 201 Message: aws.String(logline), 202 }, 203 }, 204 } 205 206 stream.publishBatch(events) 207 if stream.sequenceToken == nil { 208 t.Fatal("Expected non-nil sequenceToken") 209 } 210 if *stream.sequenceToken != sequenceToken { 211 t.Errorf("Expected sequenceToken to be %s, but was %s", sequenceToken, *stream.sequenceToken) 212 } 213 } 214 215 func TestPublishBatchInvalidSeqSuccess(t *testing.T) { 216 mockClient := newMockClientBuffered(2) 217 stream := &logStream{ 218 client: mockClient, 219 logGroupName: groupName, 220 logStreamName: streamName, 221 sequenceToken: aws.String(sequenceToken), 222 } 223 mockClient.putLogEventsResult <- &putLogEventsResult{ 224 errorResult: awserr.New(invalidSequenceTokenCode, "use token token", nil), 225 } 226 mockClient.putLogEventsResult <- &putLogEventsResult{ 227 successResult: &cloudwatchlogs.PutLogEventsOutput{ 228 NextSequenceToken: aws.String(nextSequenceToken), 229 }, 230 } 231 232 events := []wrappedEvent{ 233 { 234 inputLogEvent: &cloudwatchlogs.InputLogEvent{ 235 Message: aws.String(logline), 236 }, 237 }, 238 } 239 240 stream.publishBatch(events) 241 if stream.sequenceToken == nil { 242 t.Fatal("Expected non-nil sequenceToken") 243 } 244 if *stream.sequenceToken != nextSequenceToken { 245 t.Errorf("Expected sequenceToken to be %s, but was %s", nextSequenceToken, *stream.sequenceToken) 246 } 247 248 argument := <-mockClient.putLogEventsArgument 249 if argument == nil { 250 t.Fatal("Expected non-nil PutLogEventsInput") 251 } 252 if argument.SequenceToken == nil { 253 t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken") 254 } 255 if *argument.SequenceToken != sequenceToken { 256 t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", sequenceToken, *argument.SequenceToken) 257 } 258 if len(argument.LogEvents) != 1 { 259 t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents)) 260 } 261 if argument.LogEvents[0] != events[0].inputLogEvent { 262 t.Error("Expected event to equal input") 263 } 264 265 argument = <-mockClient.putLogEventsArgument 266 if argument == nil { 267 t.Fatal("Expected non-nil PutLogEventsInput") 268 } 269 if argument.SequenceToken == nil { 270 t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken") 271 } 272 if *argument.SequenceToken != "token" { 273 t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", "token", *argument.SequenceToken) 274 } 275 if len(argument.LogEvents) != 1 { 276 t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents)) 277 } 278 if argument.LogEvents[0] != events[0].inputLogEvent { 279 t.Error("Expected event to equal input") 280 } 281 } 282 283 func TestPublishBatchAlreadyAccepted(t *testing.T) { 284 mockClient := newMockClient() 285 stream := &logStream{ 286 client: mockClient, 287 logGroupName: groupName, 288 logStreamName: streamName, 289 sequenceToken: aws.String(sequenceToken), 290 } 291 mockClient.putLogEventsResult <- &putLogEventsResult{ 292 errorResult: awserr.New(dataAlreadyAcceptedCode, "use token token", nil), 293 } 294 295 events := []wrappedEvent{ 296 { 297 inputLogEvent: &cloudwatchlogs.InputLogEvent{ 298 Message: aws.String(logline), 299 }, 300 }, 301 } 302 303 stream.publishBatch(events) 304 if stream.sequenceToken == nil { 305 t.Fatal("Expected non-nil sequenceToken") 306 } 307 if *stream.sequenceToken != "token" { 308 t.Errorf("Expected sequenceToken to be %s, but was %s", "token", *stream.sequenceToken) 309 } 310 311 argument := <-mockClient.putLogEventsArgument 312 if argument == nil { 313 t.Fatal("Expected non-nil PutLogEventsInput") 314 } 315 if argument.SequenceToken == nil { 316 t.Fatal("Expected non-nil PutLogEventsInput.SequenceToken") 317 } 318 if *argument.SequenceToken != sequenceToken { 319 t.Errorf("Expected PutLogEventsInput.SequenceToken to be %s, but was %s", sequenceToken, *argument.SequenceToken) 320 } 321 if len(argument.LogEvents) != 1 { 322 t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents)) 323 } 324 if argument.LogEvents[0] != events[0].inputLogEvent { 325 t.Error("Expected event to equal input") 326 } 327 } 328 329 func TestCollectBatchSimple(t *testing.T) { 330 mockClient := newMockClient() 331 stream := &logStream{ 332 client: mockClient, 333 logGroupName: groupName, 334 logStreamName: streamName, 335 sequenceToken: aws.String(sequenceToken), 336 messages: make(chan *logger.Message), 337 } 338 mockClient.putLogEventsResult <- &putLogEventsResult{ 339 successResult: &cloudwatchlogs.PutLogEventsOutput{ 340 NextSequenceToken: aws.String(nextSequenceToken), 341 }, 342 } 343 ticks := make(chan time.Time) 344 newTicker = func(_ time.Duration) *time.Ticker { 345 return &time.Ticker{ 346 C: ticks, 347 } 348 } 349 350 go stream.collectBatch() 351 352 stream.Log(&logger.Message{ 353 Line: []byte(logline), 354 Timestamp: time.Time{}, 355 }) 356 357 ticks <- time.Time{} 358 stream.Close() 359 360 argument := <-mockClient.putLogEventsArgument 361 if argument == nil { 362 t.Fatal("Expected non-nil PutLogEventsInput") 363 } 364 if len(argument.LogEvents) != 1 { 365 t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents)) 366 } 367 if *argument.LogEvents[0].Message != logline { 368 t.Errorf("Expected message to be %s but was %s", logline, *argument.LogEvents[0].Message) 369 } 370 } 371 372 func TestCollectBatchTicker(t *testing.T) { 373 mockClient := newMockClient() 374 stream := &logStream{ 375 client: mockClient, 376 logGroupName: groupName, 377 logStreamName: streamName, 378 sequenceToken: aws.String(sequenceToken), 379 messages: make(chan *logger.Message), 380 } 381 mockClient.putLogEventsResult <- &putLogEventsResult{ 382 successResult: &cloudwatchlogs.PutLogEventsOutput{ 383 NextSequenceToken: aws.String(nextSequenceToken), 384 }, 385 } 386 ticks := make(chan time.Time) 387 newTicker = func(_ time.Duration) *time.Ticker { 388 return &time.Ticker{ 389 C: ticks, 390 } 391 } 392 393 go stream.collectBatch() 394 395 stream.Log(&logger.Message{ 396 Line: []byte(logline + " 1"), 397 Timestamp: time.Time{}, 398 }) 399 stream.Log(&logger.Message{ 400 Line: []byte(logline + " 2"), 401 Timestamp: time.Time{}, 402 }) 403 404 ticks <- time.Time{} 405 406 // Verify first batch 407 argument := <-mockClient.putLogEventsArgument 408 if argument == nil { 409 t.Fatal("Expected non-nil PutLogEventsInput") 410 } 411 if len(argument.LogEvents) != 2 { 412 t.Errorf("Expected LogEvents to contain 2 elements, but contains %d", len(argument.LogEvents)) 413 } 414 if *argument.LogEvents[0].Message != logline+" 1" { 415 t.Errorf("Expected message to be %s but was %s", logline+" 1", *argument.LogEvents[0].Message) 416 } 417 if *argument.LogEvents[1].Message != logline+" 2" { 418 t.Errorf("Expected message to be %s but was %s", logline+" 2", *argument.LogEvents[0].Message) 419 } 420 421 stream.Log(&logger.Message{ 422 Line: []byte(logline + " 3"), 423 Timestamp: time.Time{}, 424 }) 425 426 ticks <- time.Time{} 427 argument = <-mockClient.putLogEventsArgument 428 if argument == nil { 429 t.Fatal("Expected non-nil PutLogEventsInput") 430 } 431 if len(argument.LogEvents) != 1 { 432 t.Errorf("Expected LogEvents to contain 1 elements, but contains %d", len(argument.LogEvents)) 433 } 434 if *argument.LogEvents[0].Message != logline+" 3" { 435 t.Errorf("Expected message to be %s but was %s", logline+" 3", *argument.LogEvents[0].Message) 436 } 437 438 stream.Close() 439 440 } 441 442 func TestCollectBatchClose(t *testing.T) { 443 mockClient := newMockClient() 444 stream := &logStream{ 445 client: mockClient, 446 logGroupName: groupName, 447 logStreamName: streamName, 448 sequenceToken: aws.String(sequenceToken), 449 messages: make(chan *logger.Message), 450 } 451 mockClient.putLogEventsResult <- &putLogEventsResult{ 452 successResult: &cloudwatchlogs.PutLogEventsOutput{ 453 NextSequenceToken: aws.String(nextSequenceToken), 454 }, 455 } 456 var ticks = make(chan time.Time) 457 newTicker = func(_ time.Duration) *time.Ticker { 458 return &time.Ticker{ 459 C: ticks, 460 } 461 } 462 463 go stream.collectBatch() 464 465 stream.Log(&logger.Message{ 466 Line: []byte(logline), 467 Timestamp: time.Time{}, 468 }) 469 470 // no ticks 471 stream.Close() 472 473 argument := <-mockClient.putLogEventsArgument 474 if argument == nil { 475 t.Fatal("Expected non-nil PutLogEventsInput") 476 } 477 if len(argument.LogEvents) != 1 { 478 t.Errorf("Expected LogEvents to contain 1 element, but contains %d", len(argument.LogEvents)) 479 } 480 if *argument.LogEvents[0].Message != logline { 481 t.Errorf("Expected message to be %s but was %s", logline, *argument.LogEvents[0].Message) 482 } 483 } 484 485 func TestCollectBatchLineSplit(t *testing.T) { 486 mockClient := newMockClient() 487 stream := &logStream{ 488 client: mockClient, 489 logGroupName: groupName, 490 logStreamName: streamName, 491 sequenceToken: aws.String(sequenceToken), 492 messages: make(chan *logger.Message), 493 } 494 mockClient.putLogEventsResult <- &putLogEventsResult{ 495 successResult: &cloudwatchlogs.PutLogEventsOutput{ 496 NextSequenceToken: aws.String(nextSequenceToken), 497 }, 498 } 499 var ticks = make(chan time.Time) 500 newTicker = func(_ time.Duration) *time.Ticker { 501 return &time.Ticker{ 502 C: ticks, 503 } 504 } 505 506 go stream.collectBatch() 507 508 longline := strings.Repeat("A", maximumBytesPerEvent) 509 stream.Log(&logger.Message{ 510 Line: []byte(longline + "B"), 511 Timestamp: time.Time{}, 512 }) 513 514 // no ticks 515 stream.Close() 516 517 argument := <-mockClient.putLogEventsArgument 518 if argument == nil { 519 t.Fatal("Expected non-nil PutLogEventsInput") 520 } 521 if len(argument.LogEvents) != 2 { 522 t.Errorf("Expected LogEvents to contain 2 elements, but contains %d", len(argument.LogEvents)) 523 } 524 if *argument.LogEvents[0].Message != longline { 525 t.Errorf("Expected message to be %s but was %s", longline, *argument.LogEvents[0].Message) 526 } 527 if *argument.LogEvents[1].Message != "B" { 528 t.Errorf("Expected message to be %s but was %s", "B", *argument.LogEvents[1].Message) 529 } 530 } 531 532 func TestCollectBatchMaxEvents(t *testing.T) { 533 mockClient := newMockClientBuffered(1) 534 stream := &logStream{ 535 client: mockClient, 536 logGroupName: groupName, 537 logStreamName: streamName, 538 sequenceToken: aws.String(sequenceToken), 539 messages: make(chan *logger.Message), 540 } 541 mockClient.putLogEventsResult <- &putLogEventsResult{ 542 successResult: &cloudwatchlogs.PutLogEventsOutput{ 543 NextSequenceToken: aws.String(nextSequenceToken), 544 }, 545 } 546 var ticks = make(chan time.Time) 547 newTicker = func(_ time.Duration) *time.Ticker { 548 return &time.Ticker{ 549 C: ticks, 550 } 551 } 552 553 go stream.collectBatch() 554 555 line := "A" 556 for i := 0; i <= maximumLogEventsPerPut; i++ { 557 stream.Log(&logger.Message{ 558 Line: []byte(line), 559 Timestamp: time.Time{}, 560 }) 561 } 562 563 // no ticks 564 stream.Close() 565 566 argument := <-mockClient.putLogEventsArgument 567 if argument == nil { 568 t.Fatal("Expected non-nil PutLogEventsInput") 569 } 570 if len(argument.LogEvents) != maximumLogEventsPerPut { 571 t.Errorf("Expected LogEvents to contain %d elements, but contains %d", maximumLogEventsPerPut, len(argument.LogEvents)) 572 } 573 574 argument = <-mockClient.putLogEventsArgument 575 if argument == nil { 576 t.Fatal("Expected non-nil PutLogEventsInput") 577 } 578 if len(argument.LogEvents) != 1 { 579 t.Errorf("Expected LogEvents to contain %d elements, but contains %d", 1, len(argument.LogEvents)) 580 } 581 } 582 583 func TestCollectBatchMaxTotalBytes(t *testing.T) { 584 mockClient := newMockClientBuffered(1) 585 stream := &logStream{ 586 client: mockClient, 587 logGroupName: groupName, 588 logStreamName: streamName, 589 sequenceToken: aws.String(sequenceToken), 590 messages: make(chan *logger.Message), 591 } 592 mockClient.putLogEventsResult <- &putLogEventsResult{ 593 successResult: &cloudwatchlogs.PutLogEventsOutput{ 594 NextSequenceToken: aws.String(nextSequenceToken), 595 }, 596 } 597 var ticks = make(chan time.Time) 598 newTicker = func(_ time.Duration) *time.Ticker { 599 return &time.Ticker{ 600 C: ticks, 601 } 602 } 603 604 go stream.collectBatch() 605 606 longline := strings.Repeat("A", maximumBytesPerPut) 607 stream.Log(&logger.Message{ 608 Line: []byte(longline + "B"), 609 Timestamp: time.Time{}, 610 }) 611 612 // no ticks 613 stream.Close() 614 615 argument := <-mockClient.putLogEventsArgument 616 if argument == nil { 617 t.Fatal("Expected non-nil PutLogEventsInput") 618 } 619 bytes := 0 620 for _, event := range argument.LogEvents { 621 bytes += len(*event.Message) 622 } 623 if bytes > maximumBytesPerPut { 624 t.Errorf("Expected <= %d bytes but was %d", maximumBytesPerPut, bytes) 625 } 626 627 argument = <-mockClient.putLogEventsArgument 628 if len(argument.LogEvents) != 1 { 629 t.Errorf("Expected LogEvents to contain 1 elements, but contains %d", len(argument.LogEvents)) 630 } 631 message := *argument.LogEvents[0].Message 632 if message[len(message)-1:] != "B" { 633 t.Errorf("Expected message to be %s but was %s", "B", message[len(message)-1:]) 634 } 635 } 636 637 func TestCollectBatchWithDuplicateTimestamps(t *testing.T) { 638 mockClient := newMockClient() 639 stream := &logStream{ 640 client: mockClient, 641 logGroupName: groupName, 642 logStreamName: streamName, 643 sequenceToken: aws.String(sequenceToken), 644 messages: make(chan *logger.Message), 645 } 646 mockClient.putLogEventsResult <- &putLogEventsResult{ 647 successResult: &cloudwatchlogs.PutLogEventsOutput{ 648 NextSequenceToken: aws.String(nextSequenceToken), 649 }, 650 } 651 ticks := make(chan time.Time) 652 newTicker = func(_ time.Duration) *time.Ticker { 653 return &time.Ticker{ 654 C: ticks, 655 } 656 } 657 658 go stream.collectBatch() 659 660 times := maximumLogEventsPerPut 661 expectedEvents := []*cloudwatchlogs.InputLogEvent{} 662 timestamp := time.Now() 663 for i := 0; i < times; i++ { 664 line := fmt.Sprintf("%d", i) 665 if i%2 == 0 { 666 timestamp.Add(1 * time.Nanosecond) 667 } 668 stream.Log(&logger.Message{ 669 Line: []byte(line), 670 Timestamp: timestamp, 671 }) 672 expectedEvents = append(expectedEvents, &cloudwatchlogs.InputLogEvent{ 673 Message: aws.String(line), 674 Timestamp: aws.Int64(timestamp.UnixNano() / int64(time.Millisecond)), 675 }) 676 } 677 678 ticks <- time.Time{} 679 stream.Close() 680 681 argument := <-mockClient.putLogEventsArgument 682 if argument == nil { 683 t.Fatal("Expected non-nil PutLogEventsInput") 684 } 685 if len(argument.LogEvents) != times { 686 t.Errorf("Expected LogEvents to contain %d elements, but contains %d", times, len(argument.LogEvents)) 687 } 688 for i := 0; i < times; i++ { 689 if !reflect.DeepEqual(*argument.LogEvents[i], *expectedEvents[i]) { 690 t.Errorf("Expected event to be %v but was %v", *expectedEvents[i], *argument.LogEvents[i]) 691 } 692 } 693 }