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