From 17e9b74ae3697e108855b12f1d5d4f73c430426e Mon Sep 17 00:00:00 2001 From: Ziwen Ning Date: Mon, 25 Sep 2023 12:53:48 -0700 Subject: [PATCH] test: add more test scenarios for awslogs shim logger Signed-off-by: Ziwen Ning --- .github/workflows/ci.yaml | 2 - Makefile | 2 +- README.md | 22 +-- e2e/awslogs_test.go | 254 ++++++++++++++++++++++++++++--- e2e/fluentd_test.go | 5 +- e2e/main_test.go | 62 +++++--- e2e/splunk_test.go | 5 +- go.mod | 4 +- scripts/start-ecs-local-endpoint | 5 +- 9 files changed, 302 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 90ea31e..25d9064 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,8 +71,6 @@ jobs: echo "Waiting for LocalStack startup..." # Wait 30 seconds for the LocalStack container localstack wait -t 30 # to become ready before timing out echo "Startup complete" - - awslocal logs create-log-group --log-group-name test-shim-logger - name: install and start containerd shell: bash run: sudo scripts/install-containerd diff --git a/Makefile b/Makefile index 5c64d14..9f0e981 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ test-e2e: go test -timeout 30m ./e2e -test.v -ginkgo.v --binary "$(AWS_CONTAINERD_LOGGERS_BINARY)" .PHONY: test-e2e-for-awslogs -test-e2e-for-aws-logs: +test-e2e-for-awslogs: go test -timeout 30m ./e2e -test.v -ginkgo.v --binary "$(AWS_CONTAINERD_LOGGERS_BINARY)" --log-driver "awslogs" .PHONY: test-e2e-for-fluentd diff --git a/README.md b/README.md index c30c8e6..cfecb69 100644 --- a/README.md +++ b/README.md @@ -85,17 +85,17 @@ The following list of arguments apply to Windows shim logger binaries in this re The following additional arguments are supported for the `awslogs` shim logger binary, which can be used to send container logs to [Amazon CloudWatch Logs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html). -|Name|Required|Description| -|-|-|-| -| awslogs-group | Yes | The [log group](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatchLogsConcepts.html) in which the log stream for the container will be created.| -| awslogs-stream | Yes | The [log stream name](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatchLogsConcepts.html) to stream container logs to. | -| awslogs-region | Yes | The region name in which the log group and log stream needs to be created in.| -| awslogs-credentials-endpoint | Yes | The endpoint from which credentials are retrieved from to connect to Amazon CloudWatch Logs.| -| awslogs-create-group | No | Set to `false` by default. If the provided log group name does not exist and this value is set to `false`, the binary will directly exit with an error| -| awslogs-create-stream | No | Set to `true` by default. The log stream will always be created unless this value specified to `false` explicitly.| -| awslogs-multiline-pattern | No | Matches the behavior of the [`awslogs` Docker log driver](https://docs.docker.com/config/containers/logging/awslogs/#amazon-cloudwatch-logs-options#awslogs-multiline-pattern).| -| awslogs-datetime-format | No | Matches the behavior of the [`awslogs` Docker log driver](https://docs.docker.com/config/containers/logging/awslogs/#amazon-cloudwatch-logs-options#awslogs-datetime-format)| -| awslogs-endpoint | No | Matches the behavior of the [`awslogs` Docker log driver](https://docs.docker.com/config/containers/logging/awslogs/#awslogs-endpoint)| +| Name | Required | Description | +|------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| awslogs-group | Yes | The [log group](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatchLogsConcepts.html) in which the log stream for the container will be created. | +| awslogs-stream | Yes | The [log stream name](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatchLogsConcepts.html) to stream container logs to. | +| awslogs-region | Yes | The region name in which the log group and log stream needs to be created in. | +| awslogs-credentials-endpoint | Yes | The endpoint from which credentials are retrieved from to connect to Amazon CloudWatch Logs. | +| awslogs-create-group | No | Set to `false` by default. If the provided log group name does not exist and this value is set to `false`, the binary will directly exit with an error | +| awslogs-create-stream | No | Set to `true` by default. The log stream will always be created unless this value specified to `false` explicitly. If the value is `false` and the log stream does not exist, logging will fail silently instead of failing the container task. | +| awslogs-multiline-pattern | No | Matches the behavior of the [`awslogs` Docker log driver](https://docs.docker.com/config/containers/logging/awslogs/#amazon-cloudwatch-logs-options#awslogs-multiline-pattern). | +| awslogs-datetime-format | No | Matches the behavior of the [`awslogs` Docker log driver](https://docs.docker.com/config/containers/logging/awslogs/#amazon-cloudwatch-logs-options#awslogs-datetime-format) | +| awslogs-endpoint | No | Matches the behavior of the [`awslogs` Docker log driver](https://docs.docker.com/config/containers/logging/awslogs/#awslogs-endpoint) | #### Splunk diff --git a/e2e/awslogs_test.go b/e2e/awslogs_test.go index 6b46620..827eaa4 100644 --- a/e2e/awslogs_test.go +++ b/e2e/awslogs_test.go @@ -5,11 +5,15 @@ package e2e import ( "context" + "errors" + "fmt" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/smithy-go" "github.com/containerd/containerd/cio" + "github.com/google/uuid" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) @@ -20,14 +24,22 @@ const ( awslogsStreamKey = "--awslogs-stream" awslogsGroupKey = "--awslogs-group" awslogsEndpointKey = "--awslogs-endpoint" + awslogsCreateGroupKey = "--awslogs-create-group" + awslogsCreateStreamKey = "--awslogs-create-stream" + awslogsMultilinePatternKey = "--awslogs-multiline-pattern" + awslogsDatetimeFormatKey = "--awslogs-datetime-format" testEcsLocalEndpointPort = "51679" testAwslogsCredentialEndpoint = ":" + testEcsLocalEndpointPort + "/creds" testAwslogsRegion = "us-east-1" testAwslogsStream = "test-stream" + nonExistentAwslogsStream = "non-existent-stream" testAwslogsGroup = "test-shim-logger" - testAwslogsEndpoint = "localhost.localstack.cloud" // Recommended endpoint: + nonExistentAwslogsGroup = "non-existent-group" + testAwslogsEndpoint = "http://localhost.localstack.cloud:4566" // Recommended endpoint: //nolint:lll // url // https://docs.localstack.cloud/getting-started/faq/#is-using-localhostlocalstackcloud4566-to-set-as-the-endpoint-for-aws-services-recommended + testAwslogsMultilinePattern = "TEST" + testAwslogsDatetimeFormat = "\\[%b %d, %Y %H:%M:%S\\]" ) var testAwslogs = func() { @@ -47,12 +59,199 @@ var testAwslogs = func() { config.WithEndpointResolverWithOptions(customResolver)) gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) cwClient = cloudwatchlogs.NewFromConfig(cfg) - cleanupAwslogs(cwClient, testAwslogsGroup, testAwslogsStream) + deleteLogGroup(cwClient, testAwslogsGroup) + deleteLogGroup(cwClient, nonExistentAwslogsGroup) + _, err = cwClient.CreateLogGroup(context.TODO(), &cloudwatchlogs.CreateLogGroupInput{ + LogGroupName: aws.String(testAwslogsGroup), + }) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + _, err = cwClient.CreateLogStream(context.TODO(), &cloudwatchlogs.CreateLogStreamInput{ + LogGroupName: aws.String(testAwslogsGroup), + LogStreamName: aws.String(testAwslogsStream), + }) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) }) ginkgo.AfterEach(func() { - cleanupAwslogs(cwClient, testAwslogsGroup, testAwslogsStream) + deleteLogGroup(cwClient, testAwslogsGroup) + deleteLogGroup(cwClient, nonExistentAwslogsGroup) }) - ginkgo.It("should send logs to awslogs log driver", func() { + ginkgo.It("should send logs to awslogs log driver with existing log group and non-existent log stream "+ + "when the configs are default", func() { + testLog := testLogPrefix + uuid.New().String() + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: testAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsStream, + awslogsEndpointKey: testAwslogsEndpoint, + } + creator := cio.BinaryIO(*Binary, args) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, testAwslogsGroup, nonExistentAwslogsStream, []string{testLog}) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + }) + ginkgo.It("should send logs to awslogs log driver with respecting datatime format and ignoring multiline pattern "+ + "when both datetime format and multiline pattern are set", func() { + firstLineLog := "[May 01, 2017 19:00:01] " + testLogPrefix + uuid.New().String() + secondLineLog := "[May 01, 2017 19:00:04] " + testLogPrefix + uuid.New().String() + thirdLineLog := fmt.Sprintf("%s %s", testAwslogsMultilinePattern, testLogPrefix+uuid.New().String()) + fourthLineLog := fmt.Sprintf("%s %s", testAwslogsMultilinePattern, testLogPrefix+uuid.New().String()) + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: testAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsStream, + awslogsEndpointKey: testAwslogsEndpoint, + awslogsMultilinePatternKey: "^" + testAwslogsMultilinePattern, + awslogsDatetimeFormatKey: testAwslogsDatetimeFormat, + } + creator := cio.BinaryIO(*Binary, args) + // The last matched line cannot be logged with multiline pattern. Append a pattern for now. + // TODO: Investigate and fix. https://github.com/aws/shim-loggers-for-containerd/issues/78 + err := sendTestLogByContainerd(creator, fmt.Sprintf("%s\n%s\n%s\n%s\n%s", firstLineLog, + secondLineLog, thirdLineLog, fourthLineLog, "[May 01, 2017 19:00:05]")) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, testAwslogsGroup, nonExistentAwslogsStream, []string{fmt.Sprintf("%s\n", firstLineLog), + fmt.Sprintf("%s\n%s\n%s\n", secondLineLog, thirdLineLog, fourthLineLog)}) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + }) + ginkgo.It("should send logs to awslogs log driver with respecting multiline pattern "+ + "when multiline pattern is set", func() { + firstLineLog := fmt.Sprintf("%s %s", testAwslogsMultilinePattern, testLogPrefix+uuid.New().String()) + secondLineLog := fmt.Sprintf("%s %s", testAwslogsMultilinePattern, testLogPrefix+uuid.New().String()) + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: testAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsStream, + awslogsEndpointKey: testAwslogsEndpoint, + awslogsMultilinePatternKey: "^" + testAwslogsMultilinePattern, + } + creator := cio.BinaryIO(*Binary, args) + err := sendTestLogByContainerd(creator, fmt.Sprintf("%s\n%s\n%s", firstLineLog, secondLineLog, testAwslogsMultilinePattern)) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, testAwslogsGroup, nonExistentAwslogsStream, + []string{fmt.Sprintf("%s\n", firstLineLog), fmt.Sprintf("%s\n", secondLineLog)}) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + }) + ginkgo.It("should send logs to awslogs log driver with respecting datatime format "+ + "when datetime format is set", func() { + testLog := testLogPrefix + uuid.New().String() + firstLineLog := "[May 01, 2017 19:00:01] " + testLog + secondLineLog := "[May 01, 2017 19:00:04] " + testLog + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: testAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsStream, + awslogsEndpointKey: testAwslogsEndpoint, + awslogsDatetimeFormatKey: testAwslogsDatetimeFormat, + } + creator := cio.BinaryIO(*Binary, args) + err := sendTestLogByContainerd(creator, fmt.Sprintf("%s\n%s\n%s", firstLineLog, secondLineLog, "[May 01, 2017 19:00:05]")) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, testAwslogsGroup, nonExistentAwslogsStream, + []string{fmt.Sprintf("%s\n", firstLineLog), fmt.Sprintf("%s\n", secondLineLog)}) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + }) + ginkgo.It("should send logs to awslogs log driver with existing log group and non-existent log stream "+ + "when createGroup is false and createStream is true", func() { + testLog := testLogPrefix + uuid.New().String() + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: testAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsStream, + awslogsEndpointKey: testAwslogsEndpoint, + awslogsCreateGroupKey: "false", + awslogsCreateStreamKey: "true", + } + creator := cio.BinaryIO(*Binary, args) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, testAwslogsGroup, nonExistentAwslogsStream, []string{testLog}) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + }) + ginkgo.It("should fail to send logs to awslogs log driver with non-existent log group "+ + "when createGroup is false", func() { + testLog := testLogPrefix + uuid.New().String() + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: nonExistentAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsGroup, + awslogsEndpointKey: testAwslogsEndpoint, + awslogsCreateGroupKey: "false", + } + creator := cio.BinaryIO(*Binary, args) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).Should(gomega.HaveOccurred()) + gomega.Expect(err.Error()).Should(gomega.Equal(containerdTaskExitNonZeroMessage)) + }) + ginkgo.It("should silently fail to send logs to awslogs log driver with existing log group and non-existent log stream "+ + "when createGroup is false and createStream is false", func() { + testLog := testLogPrefix + uuid.New().String() + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: testAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsStream, + awslogsEndpointKey: testAwslogsEndpoint, + awslogsCreateGroupKey: "false", + awslogsCreateStreamKey: "false", + } + creator := cio.BinaryIO(*Binary, args) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, testAwslogsGroup, nonExistentAwslogsStream, []string{testLog}) + gomega.Expect(err).Should(gomega.HaveOccurred()) + }) + ginkgo.It("should send logs to awslogs log driver with non-existent log group and non-existent log stream "+ + "when createGroup is true and createStream is true", func() { + testLog := testLogPrefix + uuid.New().String() + args := map[string]string{ + logDriverTypeKey: awslogsDriverName, + containerIDKey: testContainerID, + containerNameKey: testContainerName, + awslogsCredentialsEndpointKey: testAwslogsCredentialEndpoint, + awslogsRegionKey: testAwslogsRegion, + awslogsGroupKey: nonExistentAwslogsGroup, + awslogsStreamKey: nonExistentAwslogsStream, + awslogsEndpointKey: testAwslogsEndpoint, + awslogsCreateGroupKey: "true", + awslogsCreateStreamKey: "true", + } + creator := cio.BinaryIO(*Binary, args) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, nonExistentAwslogsGroup, nonExistentAwslogsStream, []string{testLog}) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + }) + ginkgo.It("should send logs to awslogs log driver with existing log group and log stream when createGroup "+ + "is false and createStream is false", func() { + testLog := testLogPrefix + uuid.New().String() args := map[string]string{ logDriverTypeKey: awslogsDriverName, containerIDKey: testContainerID, @@ -62,28 +261,49 @@ var testAwslogs = func() { awslogsGroupKey: testAwslogsGroup, awslogsStreamKey: testAwslogsStream, awslogsEndpointKey: testAwslogsEndpoint, + awslogsCreateGroupKey: "false", + awslogsCreateStreamKey: "false", } creator := cio.BinaryIO(*Binary, args) - sendTestLogByContainerd(creator, testLog) - validateTestLogsInAwslogs(cwClient, testAwslogsGroup, testAwslogsStream, testLog) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + err = validateTestLogsInAwslogs(cwClient, testAwslogsGroup, testAwslogsStream, []string{testLog}) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) }) }) } -func validateTestLogsInAwslogs(client *cloudwatchlogs.Client, logGroupName string, logStreamName string, testLog string) { +func validateTestLogsInAwslogs(client *cloudwatchlogs.Client, logGroupName string, logStreamName string, testLogs []string) error { cwOutput, err := client.GetLogEvents(context.TODO(), &cloudwatchlogs.GetLogEventsInput{ - LogStreamName: aws.String(logGroupName), - LogGroupName: aws.String(logStreamName), - Limit: aws.Int32(1), + LogGroupName: aws.String(logGroupName), + LogStreamName: aws.String(logStreamName), }) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - gomega.Expect(*cwOutput.Events[0].Message).Should(gomega.Equal(testLog)) + if err != nil { + return err + } + co := *cwOutput + if len(co.Events) != len(testLogs) { + return fmt.Errorf("the number of test log lines are not matching") + } + for i := 0; i < len(testLogs); i++ { + if *co.Events[i].Message != testLogs[i] { + return fmt.Errorf("test log messages are not matching at %d line", i) + } + } + return nil } -func cleanupAwslogs(client *cloudwatchlogs.Client, logGroupName string, logStreamName string) { - _, err := client.DeleteLogStream(context.TODO(), &cloudwatchlogs.DeleteLogStreamInput{ - LogStreamName: aws.String(logGroupName), - LogGroupName: aws.String(logStreamName), +func deleteLogGroup(client *cloudwatchlogs.Client, logGroupName string) { + _, err := client.DeleteLogGroup(context.TODO(), &cloudwatchlogs.DeleteLogGroupInput{ + LogGroupName: aws.String(logGroupName), }) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + if err != nil { + var oe *smithy.OperationError + if errors.As(err, &oe) { + gomega.Expect(oe.Unwrap().Error()).Should( + gomega.ContainSubstring("ResourceNotFoundException: The specified log group does not exist")) + } + } else { + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + } } diff --git a/e2e/fluentd_test.go b/e2e/fluentd_test.go index dfb42f3..e16dff8 100644 --- a/e2e/fluentd_test.go +++ b/e2e/fluentd_test.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/containerd/containerd/cio" + "github.com/google/uuid" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) @@ -23,13 +24,15 @@ var testFluentd = func() { // These tests are run in serial because we only define one log driver instance. ginkgo.Describe("fluentd shim logger", ginkgo.Serial, func() { ginkgo.It("should send logs to fluentd log driver", func() { + testLog := testLogPrefix + uuid.New().String() args := map[string]string{ logDriverTypeKey: fluentdDriverName, containerIDKey: testContainerID, containerNameKey: testContainerName, } creator := cio.BinaryIO(*Binary, args) - sendTestLogByContainerd(creator, testLog) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) validateTestLogsInFluentd(fluentdLogDirName, testLog) }) }) diff --git a/e2e/main_test.go b/e2e/main_test.go index 2f07feb..437ace0 100644 --- a/e2e/main_test.go +++ b/e2e/main_test.go @@ -5,6 +5,7 @@ package e2e import ( "context" + "errors" "flag" "fmt" "testing" @@ -19,17 +20,18 @@ import ( const ( // LogDriver options. - logDriverTypeKey = "--log-driver" - awslogsDriverName = "awslogs" - fluentdDriverName = "fluentd" - splunkDriverName = "splunk" - containerIDKey = "--container-id" - containerNameKey = "--container-name" - testContainerID = "test-container-id" - testContainerName = "test-container-name" - containerdAddress = "/run/containerd/containerd.sock" - testImage = "public.ecr.aws/docker/library/ubuntu:latest" - testLog = "test-e2e-log" + logDriverTypeKey = "--log-driver" + awslogsDriverName = "awslogs" + fluentdDriverName = "fluentd" + splunkDriverName = "splunk" + containerIDKey = "--container-id" + containerNameKey = "--container-name" + testContainerID = "test-container-id" + testContainerName = "test-container-name" + containerdAddress = "/run/containerd/containerd.sock" + testImage = "public.ecr.aws/docker/library/ubuntu:latest" + testLogPrefix = "test-e2e-log-" + containerdTaskExitNonZeroMessage = "\"containerd task exits with non-zero\"" ) var ( @@ -60,35 +62,51 @@ func TestShimLoggers(t *testing.T) { ginkgo.RunSpecs(t, description) } -func sendTestLogByContainerd(creator cio.Creator, testLog string) { +func sendTestLogByContainerd(creator cio.Creator, testLog string) error { // Create a new client connected to the containerd daemon client, err := containerd.New(containerdAddress) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + if err != nil { + return err + } defer client.Close() //nolint:errcheck // closing client // Create a new context with a customized namespace ctx := namespaces.WithNamespace(context.Background(), "testShimLoggers") // Pull an image image, err := client.Pull(ctx, testImage, containerd.WithPullUnpack) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - // Create a new container with the pulled image + if err != nil { + return err + } // Create a new container with the pulled image container, err := client.NewContainer(ctx, testContainerID, containerd.WithImage(image), containerd.WithNewSnapshot("test-snapshot", image), containerd.WithNewSpec(oci.WithImageConfig(image), - oci.WithProcessArgs("/bin/sh", "-c", fmt.Sprintf("echo '%s'", testLog)))) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + oci.WithProcessArgs("/bin/sh", "-c", fmt.Sprintf("printf \"%s\"", testLog)))) + if err != nil { + return err + } defer container.Delete(ctx, containerd.WithSnapshotCleanup) //nolint:errcheck // testing only // Create a new task from the container and start it task, err := container.NewTask(ctx, creator) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + if err != nil { + return err + } defer task.Delete(ctx) //nolint:errcheck // testing only err = task.Start(ctx) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + if err != nil { + return err + } statusC, err := task.Wait(ctx) - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) + if err != nil { + return err + } // Waiting for the task to finish status := <-statusC code, _, err := status.Result() - gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) - gomega.Expect(code).Should(gomega.Equal(uint32(0))) + if err != nil { + return err + } + if code != uint32(0) { + return errors.New(containerdTaskExitNonZeroMessage) + } + return nil } diff --git a/e2e/splunk_test.go b/e2e/splunk_test.go index e6810f4..2a2e8c5 100644 --- a/e2e/splunk_test.go +++ b/e2e/splunk_test.go @@ -5,6 +5,7 @@ package e2e import ( "github.com/containerd/containerd/cio" + "github.com/google/uuid" "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) @@ -21,6 +22,7 @@ var testSplunk = func(token string) { ginkgo.Describe("splunk shim logger", ginkgo.Serial, func() { gomega.Expect(token).ShouldNot(gomega.BeEmpty()) ginkgo.It("should send logs to splunk log driver", func() { + testLog := testLogPrefix + uuid.New().String() args := map[string]string{ logDriverTypeKey: splunkDriverName, containerIDKey: testContainerID, @@ -30,7 +32,8 @@ var testSplunk = func(token string) { splunkInsecureskipverifyKey: "true", } creator := cio.BinaryIO(*Binary, args) - sendTestLogByContainerd(creator, testLog) + err := sendTestLogByContainerd(creator, testLog) + gomega.Expect(err).ShouldNot(gomega.HaveOccurred()) // TODO: Validate logs in Splunk local. https://github.com/aws/shim-loggers-for-containerd/issues/74 }) }) diff --git a/go.mod b/go.mod index 0ddc131..d4ef5a5 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,14 @@ require ( github.com/aws/aws-sdk-go-v2 v1.21.0 github.com/aws/aws-sdk-go-v2/config v1.18.39 github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.23.5 + github.com/aws/smithy-go v1.14.2 github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 github.com/containerd/containerd v1.6.18 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/docker/docker v20.10.13+incompatible github.com/docker/go-units v0.4.0 github.com/golang/mock v1.6.0 + github.com/google/uuid v1.3.0 github.com/onsi/ginkgo/v2 v2.11.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.4.0 @@ -32,7 +34,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect - github.com/aws/smithy-go v1.14.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/cgroups v1.0.4 // indirect @@ -52,7 +53,6 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.11.13 // indirect diff --git a/scripts/start-ecs-local-endpoint b/scripts/start-ecs-local-endpoint index 1920003..c900bd4 100755 --- a/scripts/start-ecs-local-endpoint +++ b/scripts/start-ecs-local-endpoint @@ -1,8 +1,9 @@ #!/bin/bash # ecs local endpoint can only use credentials in aws credential file instead of env vars -aws configure set aws_access_key_id dummy-aws-access-key-id --profile default -aws configure set aws_secret_access_key dummy-aws-secret-access-key --profile default +aws configure set aws_access_key_id test --profile default +aws configure set aws_secret_access_key test --profile default +aws configure set aws_session_token test --profile default docker run -d --name ecs-local-endpoint -p 51679:51679 \ -v $HOME/.aws/:/home/.aws/ -e AWS_REGION=us-east-1 \ -e HOME="/home" -e AWS_PROFILE=default -e ECS_LOCAL_METADATA_PORT=51679 \