From 6ba7f1ea301df8179d8bdf96368332a92a442375 Mon Sep 17 00:00:00 2001 From: Dane H Lim Date: Mon, 24 Jul 2023 14:32:42 -0700 Subject: [PATCH] Increase test coverage of some ACS responders --- .../ecs-agent/acs/session/generate_mocks.go | 16 +++ .../acs/session/testconst/test_const.go | 2 +- .../tmds/handlers/v4/state/response.go | 3 +- .../attach_instance_eni_responder_test.go | 42 ++++++++ .../session/attach_resource_responder_test.go | 57 +++++++++++ .../session/attach_task_eni_responder_test.go | 41 ++++++++ ecs-agent/acs/session/generate_mocks.go | 16 +++ ecs-agent/acs/session/mocks/session_mock.go | 99 +++++++++++++++++++ ecs-agent/acs/session/testconst/test_const.go | 2 +- 9 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/generate_mocks.go create mode 100644 ecs-agent/acs/session/generate_mocks.go create mode 100644 ecs-agent/acs/session/mocks/session_mock.go diff --git a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/generate_mocks.go b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/generate_mocks.go new file mode 100644 index 0000000000..eecfbf1591 --- /dev/null +++ b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/generate_mocks.go @@ -0,0 +1,16 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +//go:generate mockgen -destination=mocks/session_mock.go -copyright_file=../../../scripts/copyright_file . ENIHandler,ResourceHandler + +package session diff --git a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/testconst/test_const.go b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/testconst/test_const.go index 72c85ecdc5..ec7894e2d9 100644 --- a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/testconst/test_const.go +++ b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/testconst/test_const.go @@ -17,7 +17,7 @@ package testconst // should only be called in test files. const ( ClusterName = "default" - ContainerInstanceARN = "instance" + ContainerInstanceARN = "arn:aws:ecs:us-west-2:123456789012:container-instance/a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" TaskARN = "arn:aws:ecs:us-west-2:1234567890:task/test-cluster/abc" MessageID = "123" RandomMAC = "00:0a:95:9d:68:16" diff --git a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v4/state/response.go b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v4/state/response.go index 9738f473b7..9a6d523d8b 100644 --- a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v4/state/response.go +++ b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v4/state/response.go @@ -57,7 +57,8 @@ type EphemeralStorageMetrics struct { // with the v2 container response object. type ContainerResponse struct { *v2.ContainerResponse - Networks []Network `json:"Networks,omitempty"` + Networks []Network `json:"Networks,omitempty"` + Snapshotter string `json:"Snapshotter,omitempty"` } // Network is the v4 Network response. It adds a bunch of information about network diff --git a/ecs-agent/acs/session/attach_instance_eni_responder_test.go b/ecs-agent/acs/session/attach_instance_eni_responder_test.go index 9a419f7e2b..0b403d3eec 100644 --- a/ecs-agent/acs/session/attach_instance_eni_responder_test.go +++ b/ecs-agent/acs/session/attach_instance_eni_responder_test.go @@ -18,12 +18,15 @@ package session import ( "fmt" + "sync" "testing" "github.com/aws/aws-sdk-go/aws" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/aws/amazon-ecs-agent/ecs-agent/acs/model/ecsacs" + mock_session "github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/mocks" "github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/testconst" apieni "github.com/aws/amazon-ecs-agent/ecs-agent/api/eni" ) @@ -193,3 +196,42 @@ func TestAttachInstanceENIMessageWithMissingTimeout(t *testing.T) { testAttachInstanceENIMessage.WaitTimeoutMs = tempWaitTimeoutMs } + +// TestInstanceENIAckHappyPath tests the happy path for a typical AttachInstanceNetworkInterfacesMessage and confirms +// expected ACK request is made +func TestInstanceENIAckHappyPath(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ackSent := make(chan *ecsacs.AckRequest) + + // WaitGroup is necessary to wait for function to be called in separate goroutine before exiting the test. + wg := sync.WaitGroup{} + wg.Add(len(testAttachInstanceENIMessage.ElasticNetworkInterfaces)) + + mockENIHandler := mock_session.NewMockENIHandler(ctrl) + mockENIHandler.EXPECT(). + HandleENIAttachment(gomock.Any()). + Times(len(testAttachInstanceENIMessage.ElasticNetworkInterfaces)). + Return(nil). + Do(func(arg0 interface{}) { + defer wg.Done() // decrement WaitGroup counter now that HandleENIAttachment function has been called + }) + + testResponseSender := func(response interface{}) error { + resp := response.(*ecsacs.AckRequest) + ackSent <- resp + return nil + } + testAttachInstanceENIResponder := NewAttachInstanceENIResponder( + mockENIHandler, + testResponseSender) + + handleAttachMessage := + testAttachInstanceENIResponder.HandlerFunc().(func(*ecsacs.AttachInstanceNetworkInterfacesMessage)) + go handleAttachMessage(testAttachInstanceENIMessage) + + attachInstanceEniAckSent := <-ackSent + wg.Wait() + assert.Equal(t, aws.StringValue(attachInstanceEniAckSent.MessageId), testconst.MessageID) +} diff --git a/ecs-agent/acs/session/attach_resource_responder_test.go b/ecs-agent/acs/session/attach_resource_responder_test.go index c9cb49b78b..779a5da988 100644 --- a/ecs-agent/acs/session/attach_resource_responder_test.go +++ b/ecs-agent/acs/session/attach_resource_responder_test.go @@ -17,8 +17,12 @@ package session import ( + "sync" "testing" + mock_session "github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/mocks" + "github.com/aws/amazon-ecs-agent/ecs-agent/metrics" + mock_metrics "github.com/aws/amazon-ecs-agent/ecs-agent/metrics/mocks" "github.com/aws/aws-sdk-go/aws" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" @@ -109,6 +113,13 @@ func TestValidateAttachResourceMessage(t *testing.T) { _, err = validateAttachResourceMessage(&confirmAttachmentMessageCopy) require.Error(t, err) + + confirmAttachmentMessageCopy = *testConfirmAttachmentMessage + confirmAttachmentMessageCopy.Attachment.AttachmentArn = aws.String("incorrectArn") + + _, err = validateAttachResourceMessage(&confirmAttachmentMessageCopy) + + require.Error(t, err) } func TestValidateAttachmentAndReturnProperties(t *testing.T) { @@ -145,3 +156,49 @@ func TestValidateAttachmentAndReturnProperties(t *testing.T) { }) } } + +// TestResourceAckHappyPath tests the happy path for a typical ConfirmAttachmentMessage and confirms expected +// ACK request is made +func TestResourceAckHappyPath(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + confirmAttachmentMessageCopy := *testConfirmAttachmentMessage + + ackSent := make(chan *ecsacs.AckRequest) + + _, err := validateAttachmentAndReturnProperties(&confirmAttachmentMessageCopy) + require.NoError(t, err) + + // WaitGroup is necessary to wait for function to be called in separate goroutine before exiting the test. + wg := sync.WaitGroup{} + wg.Add(1) + + mockMetricsFactory := mock_metrics.NewMockEntryFactory(ctrl) + mockEntry := mock_metrics.NewMockEntry(ctrl) + mockEntry.EXPECT().Done(err) + mockMetricsFactory.EXPECT().New(metrics.ResourceValidationMetricName).Return(mockEntry) + mockResourceHandler := mock_session.NewMockResourceHandler(ctrl) + mockResourceHandler.EXPECT(). + HandleResourceAttachment(gomock.Any()). + Do(func(arg0 interface{}) { + defer wg.Done() // decrement WaitGroup counter now that HandleResourceAttachment function has been called + }) + + testResponseSender := func(response interface{}) error { + resp := response.(*ecsacs.AckRequest) + ackSent <- resp + return nil + } + testResourceResponder := NewAttachResourceResponder( + mockResourceHandler, + mockMetricsFactory, + testResponseSender) + + handleAttachMessage := testResourceResponder.HandlerFunc().(func(*ecsacs.ConfirmAttachmentMessage)) + go handleAttachMessage(&confirmAttachmentMessageCopy) + + attachResourceAckSent := <-ackSent + wg.Wait() + require.Equal(t, aws.StringValue(attachResourceAckSent.MessageId), testconst.MessageID) +} diff --git a/ecs-agent/acs/session/attach_task_eni_responder_test.go b/ecs-agent/acs/session/attach_task_eni_responder_test.go index 27b14b3281..b7656e24dc 100644 --- a/ecs-agent/acs/session/attach_task_eni_responder_test.go +++ b/ecs-agent/acs/session/attach_task_eni_responder_test.go @@ -18,9 +18,12 @@ package session import ( "fmt" + "sync" "testing" + mock_session "github.com/aws/amazon-ecs-agent/ecs-agent/acs/session/mocks" "github.com/aws/aws-sdk-go/aws" + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/aws/amazon-ecs-agent/ecs-agent/acs/model/ecsacs" @@ -203,3 +206,41 @@ func TestAttachTaskENIMessageWithMissingTimeout(t *testing.T) { testAttachTaskENIMessage.WaitTimeoutMs = tempWaitTimeoutMs } + +// TestTaskENIAckHappyPath tests the happy path for a typical AttachTaskNetworkInterfacesMessage and confirms expected +// ACK request is made +func TestTaskENIAckHappyPath(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ackSent := make(chan *ecsacs.AckRequest) + + // WaitGroup is necessary to wait for function to be called in separate goroutine before exiting the test. + wg := sync.WaitGroup{} + wg.Add(len(testAttachTaskENIMessage.ElasticNetworkInterfaces)) + + mockENIHandler := mock_session.NewMockENIHandler(ctrl) + mockENIHandler.EXPECT(). + HandleENIAttachment(gomock.Any()). + Times(len(testAttachTaskENIMessage.ElasticNetworkInterfaces)). + Return(nil). + Do(func(arg0 interface{}) { + defer wg.Done() // decrement WaitGroup counter now that HandleENIAttachment function has been called + }) + + testResponseSender := func(response interface{}) error { + resp := response.(*ecsacs.AckRequest) + ackSent <- resp + return nil + } + testAttachTaskENIResponder := NewAttachTaskENIResponder( + mockENIHandler, + testResponseSender) + + handleAttachMessage := testAttachTaskENIResponder.HandlerFunc().(func(*ecsacs.AttachTaskNetworkInterfacesMessage)) + go handleAttachMessage(testAttachTaskENIMessage) + + attachTaskEniAckSent := <-ackSent + wg.Wait() + assert.Equal(t, aws.StringValue(attachTaskEniAckSent.MessageId), testconst.MessageID) +} diff --git a/ecs-agent/acs/session/generate_mocks.go b/ecs-agent/acs/session/generate_mocks.go new file mode 100644 index 0000000000..eecfbf1591 --- /dev/null +++ b/ecs-agent/acs/session/generate_mocks.go @@ -0,0 +1,16 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +//go:generate mockgen -destination=mocks/session_mock.go -copyright_file=../../../scripts/copyright_file . ENIHandler,ResourceHandler + +package session diff --git a/ecs-agent/acs/session/mocks/session_mock.go b/ecs-agent/acs/session/mocks/session_mock.go new file mode 100644 index 0000000000..9361679149 --- /dev/null +++ b/ecs-agent/acs/session/mocks/session_mock.go @@ -0,0 +1,99 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +// + +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/aws/amazon-ecs-agent/ecs-agent/acs/session (interfaces: ENIHandler,ResourceHandler) + +// Package mock_session is a generated GoMock package. +package mock_session + +import ( + reflect "reflect" + + eni "github.com/aws/amazon-ecs-agent/ecs-agent/api/eni" + resource "github.com/aws/amazon-ecs-agent/ecs-agent/api/resource" + gomock "github.com/golang/mock/gomock" +) + +// MockENIHandler is a mock of ENIHandler interface. +type MockENIHandler struct { + ctrl *gomock.Controller + recorder *MockENIHandlerMockRecorder +} + +// MockENIHandlerMockRecorder is the mock recorder for MockENIHandler. +type MockENIHandlerMockRecorder struct { + mock *MockENIHandler +} + +// NewMockENIHandler creates a new mock instance. +func NewMockENIHandler(ctrl *gomock.Controller) *MockENIHandler { + mock := &MockENIHandler{ctrl: ctrl} + mock.recorder = &MockENIHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockENIHandler) EXPECT() *MockENIHandlerMockRecorder { + return m.recorder +} + +// HandleENIAttachment mocks base method. +func (m *MockENIHandler) HandleENIAttachment(arg0 *eni.ENIAttachment) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleENIAttachment", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// HandleENIAttachment indicates an expected call of HandleENIAttachment. +func (mr *MockENIHandlerMockRecorder) HandleENIAttachment(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleENIAttachment", reflect.TypeOf((*MockENIHandler)(nil).HandleENIAttachment), arg0) +} + +// MockResourceHandler is a mock of ResourceHandler interface. +type MockResourceHandler struct { + ctrl *gomock.Controller + recorder *MockResourceHandlerMockRecorder +} + +// MockResourceHandlerMockRecorder is the mock recorder for MockResourceHandler. +type MockResourceHandlerMockRecorder struct { + mock *MockResourceHandler +} + +// NewMockResourceHandler creates a new mock instance. +func NewMockResourceHandler(ctrl *gomock.Controller) *MockResourceHandler { + mock := &MockResourceHandler{ctrl: ctrl} + mock.recorder = &MockResourceHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockResourceHandler) EXPECT() *MockResourceHandlerMockRecorder { + return m.recorder +} + +// HandleResourceAttachment mocks base method. +func (m *MockResourceHandler) HandleResourceAttachment(arg0 *resource.ResourceAttachment) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandleResourceAttachment", arg0) +} + +// HandleResourceAttachment indicates an expected call of HandleResourceAttachment. +func (mr *MockResourceHandlerMockRecorder) HandleResourceAttachment(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleResourceAttachment", reflect.TypeOf((*MockResourceHandler)(nil).HandleResourceAttachment), arg0) +} diff --git a/ecs-agent/acs/session/testconst/test_const.go b/ecs-agent/acs/session/testconst/test_const.go index 72c85ecdc5..ec7894e2d9 100644 --- a/ecs-agent/acs/session/testconst/test_const.go +++ b/ecs-agent/acs/session/testconst/test_const.go @@ -17,7 +17,7 @@ package testconst // should only be called in test files. const ( ClusterName = "default" - ContainerInstanceARN = "instance" + ContainerInstanceARN = "arn:aws:ecs:us-west-2:123456789012:container-instance/a1b2c3d4-5678-90ab-cdef-11111EXAMPLE" TaskARN = "arn:aws:ecs:us-west-2:1234567890:task/test-cluster/abc" MessageID = "123" RandomMAC = "00:0a:95:9d:68:16"