diff --git a/controllers/crds/cninode_controller.go b/controllers/crds/cninode_controller.go index af5950fe..18d03978 100644 --- a/controllers/crds/cninode_controller.go +++ b/controllers/crds/cninode_controller.go @@ -25,8 +25,9 @@ import ( "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils" "github.com/go-logr/logr" "github.com/prometheus/client_golang/prometheus" + "golang.org/x/sync/semaphore" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" @@ -35,6 +36,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/metrics" ) @@ -75,7 +77,8 @@ type CNINodeReconciler struct { clusterName string vpcId string finalizerManager k8s.FinalizerManager - newResourceCleaner func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string) cleanup.ResourceCleaner + deletePool *semaphore.Weighted + newResourceCleaner func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string, log logr.Logger) cleanup.ResourceCleaner } func NewCNINodeReconciler( @@ -88,7 +91,8 @@ func NewCNINodeReconciler( clusterName string, vpcId string, finalizerManager k8s.FinalizerManager, - newResourceCleaner func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string) cleanup.ResourceCleaner, + maxConcurrentWorkers int, + newResourceCleaner func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string, log logr.Logger) cleanup.ResourceCleaner, ) *CNINodeReconciler { return &CNINodeReconciler{ Client: client, @@ -100,6 +104,7 @@ func NewCNINodeReconciler( clusterName: clusterName, vpcId: vpcId, finalizerManager: finalizerManager, + deletePool: semaphore.NewWeighted(int64(maxConcurrentWorkers)), newResourceCleaner: newResourceCleaner, } } @@ -118,7 +123,7 @@ func (r *CNINodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct nodeFound := true node := &v1.Node{} if err := r.Client.Get(ctx, req.NamespacedName, node); err != nil { - if errors.IsNotFound(err) { + if apierrors.IsNotFound(err) { nodeFound = false } else { r.log.Error(err, "failed to get the node object in CNINode reconciliation, will retry") @@ -128,66 +133,50 @@ func (r *CNINodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct } if cniNode.GetDeletionTimestamp().IsZero() { - shouldPatch := false cniNodeCopy := cniNode.DeepCopy() - // Add cluster name tag if it does not exist - val, ok := cniNode.Spec.Tags[config.VPCCNIClusterNameKey] - if !ok || val != r.clusterName { - if len(cniNodeCopy.Spec.Tags) != 0 { - cniNodeCopy.Spec.Tags[config.VPCCNIClusterNameKey] = r.clusterName - } else { - cniNodeCopy.Spec.Tags = map[string]string{ - config.VPCCNIClusterNameKey: r.clusterName, - } - } - shouldPatch = true - } - // if node exists, get & add OS label if it does not exist on CNINode - if nodeFound { - nodeLabelOS := node.ObjectMeta.Labels[config.NodeLabelOS] - val, ok = cniNode.ObjectMeta.Labels[config.NodeLabelOS] - if !ok || val != nodeLabelOS { - if len(cniNodeCopy.ObjectMeta.Labels) != 0 { - cniNodeCopy.ObjectMeta.Labels[config.NodeLabelOS] = nodeLabelOS - } else { - cniNodeCopy.ObjectMeta.Labels = map[string]string{ - config.NodeLabelOS: nodeLabelOS, - } - } - shouldPatch = true - } - } + shouldPatch, err := r.ensureTagsAndLabels(cniNodeCopy, node) + shouldPatch = controllerutil.AddFinalizer(cniNodeCopy, config.NodeTerminationFinalizer) || shouldPatch if shouldPatch { - r.log.Info("patching CNINode to add required fields Tags and Labels", "cninode", cniNode.Name) - return ctrl.Result{}, r.Client.Patch(ctx, cniNodeCopy, client.MergeFromWithOptions(cniNode, client.MergeFromWithOptimisticLock{})) - } - - // Add finalizer if it does not exist - if err := r.finalizerManager.AddFinalizers(ctx, cniNode, config.NodeTerminationFinalizer); err != nil { - r.log.Error(err, "failed to add finalizer on CNINode, will retry", "cniNode", cniNode.Name, "finalizer", config.NodeTerminationFinalizer) - return ctrl.Result{}, err + r.log.Info("patching CNINode to add fields Tags, Labels and finalizer", "cninode", cniNode.Name) + if err := r.Client.Patch(ctx, cniNodeCopy, client.MergeFromWithOptions(cniNode, client.MergeFromWithOptimisticLock{})); err != nil { + if apierrors.IsConflict(err) { + r.log.Info("failed to update cninode", "cninode", cniNode.Name, "error", err) + return ctrl.Result{Requeue: true}, nil + } + return ctrl.Result{}, err + } } - return ctrl.Result{}, nil - + return ctrl.Result{}, err } else { // CNINode is marked for deletion if !nodeFound { // node is also deleted, proceed with running the cleanup routine and remove the finalizer - // run cleanup for Linux nodes only if val, ok := cniNode.ObjectMeta.Labels[config.NodeLabelOS]; ok && val == config.OSLinux { r.log.Info("running the finalizer routine on cniNode", "cniNode", cniNode.Name) // run cleanup when node id is present if nodeID, ok := cniNode.Spec.Tags[config.NetworkInterfaceNodeIDKey]; ok && nodeID != "" { - if err := r.newResourceCleaner(nodeID, r.eC2Wrapper, r.vpcId).DeleteLeakedResources(); err != nil { - r.log.Error(err, "failed to cleanup resources during node termination") - ec2API.NodeTerminationENICleanupFailure.Inc() + if !r.deletePool.TryAcquire(1) { + r.log.Info("d, will requeue request") + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } + go func(nodeID string) { + defer r.deletePool.Release(1) + childCtx, cancel := context.WithTimeout(ctx, config.NodeTerminationTimeout) + defer cancel() + if err := r.newResourceCleaner(nodeID, r.eC2Wrapper, r.vpcId, r.log).DeleteLeakedResources(childCtx); err != nil { + r.log.Error(err, "failed to cleanup resources during node termination") + ec2API.NodeTerminationENICleanupFailure.Inc() + } + }(nodeID) } } - if err := r.finalizerManager.RemoveFinalizers(ctx, cniNode, config.NodeTerminationFinalizer); err != nil { + if err := r.removeFinalizer(ctx, cniNode, config.NodeTerminationFinalizer); err != nil { r.log.Error(err, "failed to remove finalizer on CNINode, will retry", "cniNode", cniNode.Name, "finalizer", config.NodeTerminationFinalizer) + if apierrors.IsConflict(err) { + return ctrl.Result{Requeue: true}, nil + } return ctrl.Result{}, err } return ctrl.Result{}, nil @@ -207,7 +196,7 @@ func (r *CNINodeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Spec: cniNode.Spec, } - if err := r.finalizerManager.RemoveFinalizers(ctx, cniNode, config.NodeTerminationFinalizer); err != nil { + if err := r.removeFinalizer(ctx, cniNode, config.NodeTerminationFinalizer); err != nil { r.log.Error(err, "failed to remove finalizer on CNINode, will retry") return ctrl.Result{}, err } @@ -252,7 +241,7 @@ func (r *CNINodeReconciler) waitTillCNINodeDeleted(nameSpacedCNINode types.Names oldCNINode := &v1alpha1.CNINode{} return wait.PollUntilContextTimeout(context.TODO(), 500*time.Millisecond, time.Second*3, true, func(ctx context.Context) (bool, error) { - if err := r.Client.Get(ctx, nameSpacedCNINode, oldCNINode); err != nil && errors.IsNotFound(err) { + if err := r.Client.Get(ctx, nameSpacedCNINode, oldCNINode); err != nil && apierrors.IsNotFound(err) { return true, nil } return false, nil @@ -266,3 +255,45 @@ func (r *CNINodeReconciler) createCNINodeFromObj(ctx context.Context, newCNINode return r.Client.Create(ctx, newCNINode) }) } + +func (r *CNINodeReconciler) ensureTagsAndLabels(cniNode *v1alpha1.CNINode, node *v1.Node) (bool, error) { + shouldPatch := false + var err error + if cniNode.Spec.Tags == nil { + cniNode.Spec.Tags = make(map[string]string) + } + // add cluster name tag if it does not exist + if cniNode.Spec.Tags[config.VPCCNIClusterNameKey] != r.clusterName { + cniNode.Spec.Tags[config.VPCCNIClusterNameKey] = r.clusterName + shouldPatch = true + } + if node != nil { + var nodeID string + nodeID, err = utils.GetNodeID(node) + + if nodeID != "" && cniNode.Spec.Tags[config.NetworkInterfaceNodeIDKey] != nodeID { + cniNode.Spec.Tags[config.NetworkInterfaceNodeIDKey] = nodeID + shouldPatch = true + } + + // add node label if it does not exist + if cniNode.ObjectMeta.Labels == nil { + cniNode.ObjectMeta.Labels = make(map[string]string) + } + if cniNode.ObjectMeta.Labels[config.NodeLabelOS] != node.ObjectMeta.Labels[config.NodeLabelOS] { + cniNode.ObjectMeta.Labels[config.NodeLabelOS] = node.ObjectMeta.Labels[config.NodeLabelOS] + shouldPatch = true + } + } + return shouldPatch, err +} + +func (r *CNINodeReconciler) removeFinalizer(ctx context.Context, cniNode *v1alpha1.CNINode, finalizer string) error { + cniNodeCopy := cniNode.DeepCopy() + + if controllerutil.RemoveFinalizer(cniNodeCopy, finalizer) { + r.log.Info("removing finalizer for cninode", "name", cniNode.GetName(), "finalizer", finalizer) + return r.Client.Patch(ctx, cniNodeCopy, client.MergeFromWithOptions(cniNode, client.MergeFromWithOptimisticLock{})) + } + return nil +} diff --git a/controllers/crds/cninode_controller_test.go b/controllers/crds/cninode_controller_test.go index 96021290..3d54e32d 100644 --- a/controllers/crds/cninode_controller_test.go +++ b/controllers/crds/cninode_controller_test.go @@ -11,8 +11,10 @@ import ( ec2API "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/ec2/api" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/aws/ec2/api/cleanup" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config" + "github.com/go-logr/logr" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + "golang.org/x/sync/semaphore" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -37,6 +39,9 @@ var ( config.NodeLabelOS: "linux", }, }, + Spec: corev1.NodeSpec{ + ProviderID: "aws:///us-west-2a/i-0123456789abcdef0", + }, } reconcileRequest = reconcile.Request{ NamespacedName: types.NamespacedName{ @@ -57,6 +62,7 @@ func NewCNINodeMock(ctrl *gomock.Controller, mockObjects ...client.Object) *CNIN log: zap.New(), clusterName: mockClusterName, vpcId: "vpc-000000000000", + deletePool: semaphore.NewWeighted(10), }, } } @@ -80,7 +86,7 @@ func TestCNINodeReconcile(t *testing.T) { asserts func(reconcile.Result, error, *v1alpha1.CNINode) }{ { - name: "verify clusterName tag and labels are added if missing", + name: "verify clusterName, instanceID, os label are added if missing", args: args{ mockNode: mockNodeWithLabel, mockCNINode: &v1alpha1.CNINode{ @@ -94,7 +100,7 @@ func TestCNINodeReconcile(t *testing.T) { assert.NoError(t, err) assert.Equal(t, res, reconcile.Result{}) assert.Equal(t, cniNode.Labels, map[string]string{config.NodeLabelOS: "linux"}) - assert.Equal(t, cniNode.Spec.Tags, map[string]string{config.VPCCNIClusterNameKey: mockClusterName}) + assert.Equal(t, cniNode.Spec.Tags, map[string]string{config.VPCCNIClusterNameKey: mockClusterName, config.NetworkInterfaceNodeIDKey: "i-0123456789abcdef0"}) }, }, { @@ -113,14 +119,10 @@ func TestCNINodeReconcile(t *testing.T) { }, }, prepare: func(f *fields) { - f.mockCNINode.Reconciler.newResourceCleaner = func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string) cleanup.ResourceCleaner { + f.mockCNINode.Reconciler.newResourceCleaner = func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string, log logr.Logger) cleanup.ResourceCleaner { return f.mockResourceCleaner } - f.mockResourceCleaner.EXPECT().DeleteLeakedResources().Times(0) - - f.mockFinalizerManager.EXPECT(). - RemoveFinalizers(gomock.Any(), gomock.Any(), config.NodeTerminationFinalizer). - Return(nil) + f.mockResourceCleaner.EXPECT().DeleteLeakedResources(gomock.Any()).Times(0) }, asserts: func(res reconcile.Result, err error, cniNode *v1alpha1.CNINode) { assert.NoError(t, err) @@ -142,20 +144,17 @@ func TestCNINodeReconcile(t *testing.T) { }, Spec: v1alpha1.CNINodeSpec{ Tags: map[string]string{ - config.NetworkInterfaceNodeIDKey: "i-1234567890", + config.NetworkInterfaceNodeIDKey: "i-0123456789abcdef0", }, }, }, }, prepare: func(f *fields) { - f.mockCNINode.Reconciler.newResourceCleaner = func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string) cleanup.ResourceCleaner { - assert.Equal(t, "i-1234567890", nodeID) + f.mockCNINode.Reconciler.newResourceCleaner = func(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string, log logr.Logger) cleanup.ResourceCleaner { + assert.Equal(t, "i-0123456789abcdef0", nodeID) return f.mockResourceCleaner } - f.mockResourceCleaner.EXPECT().DeleteLeakedResources().Times(1).Return(nil) - f.mockFinalizerManager.EXPECT(). - RemoveFinalizers(gomock.Any(), gomock.Any(), config.NodeTerminationFinalizer). - Return(nil) + f.mockResourceCleaner.EXPECT().DeleteLeakedResources(gomock.Any()).Times(1).Return(nil) }, asserts: func(res reconcile.Result, err error, cniNode *v1alpha1.CNINode) { @@ -163,6 +162,32 @@ func TestCNINodeReconcile(t *testing.T) { assert.Equal(t, res, reconcile.Result{}) }, }, + { + name: "verify finalizer is added when labels and tags are present", + args: args{ + mockNode: mockNodeWithLabel, + mockCNINode: &v1alpha1.CNINode{ + ObjectMeta: metav1.ObjectMeta{ + Name: mockName, + Labels: map[string]string{ + config.NodeLabelOS: "linux", + }, + }, + Spec: v1alpha1.CNINodeSpec{ + Tags: map[string]string{ + config.VPCCNIClusterNameKey: mockClusterName, + config.NetworkInterfaceNodeIDKey: "i-0123456789abcdef0", + }, + }, + }, + }, + prepare: nil, + asserts: func(res reconcile.Result, err error, cniNode *v1alpha1.CNINode) { + assert.NoError(t, err) + assert.Equal(t, res, reconcile.Result{}) + assert.Contains(t, cniNode.Finalizers, config.NodeTerminationFinalizer) + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -188,8 +213,10 @@ func TestCNINodeReconcile(t *testing.T) { res, err := mock.Reconciler.Reconcile(context.Background(), reconcileRequest) cniNode := &v1alpha1.CNINode{} - getErr := mock.Reconciler.Client.Get(context.Background(), reconcileRequest.NamespacedName, cniNode) - assert.NoError(t, getErr) + if tt.args.mockCNINode.GetDeletionTimestamp() == nil { + getErr := mock.Reconciler.Client.Get(context.Background(), reconcileRequest.NamespacedName, cniNode) + assert.NoError(t, getErr) + } if tt.asserts != nil { tt.asserts(res, err, cniNode) diff --git a/go.mod b/go.mod index 2d48f29a..e0eefe09 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/prometheus/common v0.62.0 github.com/stretchr/testify v1.10.0 go.uber.org/zap v1.27.0 + golang.org/x/sync v0.13.0 golang.org/x/time v0.11.0 gomodules.xyz/jsonpatch/v2 v2.4.0 k8s.io/api v0.33.0 @@ -50,7 +51,6 @@ require ( github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/sync v0.13.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect ) diff --git a/main.go b/main.go index f18a0fbd..7d152bf3 100644 --- a/main.go +++ b/main.go @@ -439,6 +439,7 @@ func main() { clusterName, vpcID, finalizerManager, + maxNodeConcurrentReconciles, cleanup.NewNodeResourceCleaner, ).SetupWithManager(mgr, maxNodeConcurrentReconciles)); err != nil { setupLog.Error(err, "unable to create controller", "controller", "CNINode") diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/cleanup/mock_resource_cleaner.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/cleanup/mock_resource_cleaner.go index 4ed17141..0031259c 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/cleanup/mock_resource_cleaner.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/cleanup/mock_resource_cleaner.go @@ -18,6 +18,7 @@ package mock_cleanup import ( + context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" @@ -47,15 +48,15 @@ func (m *MockResourceCleaner) EXPECT() *MockResourceCleanerMockRecorder { } // DeleteLeakedResources mocks base method. -func (m *MockResourceCleaner) DeleteLeakedResources() error { +func (m *MockResourceCleaner) DeleteLeakedResources(arg0 context.Context) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteLeakedResources") + ret := m.ctrl.Call(m, "DeleteLeakedResources", arg0) ret0, _ := ret[0].(error) return ret0 } // DeleteLeakedResources indicates an expected call of DeleteLeakedResources. -func (mr *MockResourceCleanerMockRecorder) DeleteLeakedResources() *gomock.Call { +func (mr *MockResourceCleanerMockRecorder) DeleteLeakedResources(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLeakedResources", reflect.TypeOf((*MockResourceCleaner)(nil).DeleteLeakedResources)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLeakedResources", reflect.TypeOf((*MockResourceCleaner)(nil).DeleteLeakedResources), arg0) } diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go index a49f1d43..35971b6c 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/aws/ec2/api/mock_ec2_wrapper.go @@ -18,6 +18,7 @@ package mock_api import ( + context "context" reflect "reflect" ec2 "github.com/aws/aws-sdk-go-v2/service/ec2" @@ -49,240 +50,240 @@ func (m *MockEC2Wrapper) EXPECT() *MockEC2WrapperMockRecorder { } // AssignPrivateIPAddresses mocks base method. -func (m *MockEC2Wrapper) AssignPrivateIPAddresses(arg0 *ec2.AssignPrivateIpAddressesInput) (*ec2.AssignPrivateIpAddressesOutput, error) { +func (m *MockEC2Wrapper) AssignPrivateIPAddresses(input *ec2.AssignPrivateIpAddressesInput) (*ec2.AssignPrivateIpAddressesOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AssignPrivateIPAddresses", arg0) + ret := m.ctrl.Call(m, "AssignPrivateIPAddresses", input) ret0, _ := ret[0].(*ec2.AssignPrivateIpAddressesOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // AssignPrivateIPAddresses indicates an expected call of AssignPrivateIPAddresses. -func (mr *MockEC2WrapperMockRecorder) AssignPrivateIPAddresses(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) AssignPrivateIPAddresses(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignPrivateIPAddresses", reflect.TypeOf((*MockEC2Wrapper)(nil).AssignPrivateIPAddresses), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssignPrivateIPAddresses", reflect.TypeOf((*MockEC2Wrapper)(nil).AssignPrivateIPAddresses), input) } // AssociateTrunkInterface mocks base method. -func (m *MockEC2Wrapper) AssociateTrunkInterface(arg0 *ec2.AssociateTrunkInterfaceInput) (*ec2.AssociateTrunkInterfaceOutput, error) { +func (m *MockEC2Wrapper) AssociateTrunkInterface(input *ec2.AssociateTrunkInterfaceInput) (*ec2.AssociateTrunkInterfaceOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AssociateTrunkInterface", arg0) + ret := m.ctrl.Call(m, "AssociateTrunkInterface", input) ret0, _ := ret[0].(*ec2.AssociateTrunkInterfaceOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // AssociateTrunkInterface indicates an expected call of AssociateTrunkInterface. -func (mr *MockEC2WrapperMockRecorder) AssociateTrunkInterface(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) AssociateTrunkInterface(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssociateTrunkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).AssociateTrunkInterface), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AssociateTrunkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).AssociateTrunkInterface), input) } // AttachNetworkInterface mocks base method. -func (m *MockEC2Wrapper) AttachNetworkInterface(arg0 *ec2.AttachNetworkInterfaceInput) (*ec2.AttachNetworkInterfaceOutput, error) { +func (m *MockEC2Wrapper) AttachNetworkInterface(input *ec2.AttachNetworkInterfaceInput) (*ec2.AttachNetworkInterfaceOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AttachNetworkInterface", arg0) + ret := m.ctrl.Call(m, "AttachNetworkInterface", input) ret0, _ := ret[0].(*ec2.AttachNetworkInterfaceOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // AttachNetworkInterface indicates an expected call of AttachNetworkInterface. -func (mr *MockEC2WrapperMockRecorder) AttachNetworkInterface(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) AttachNetworkInterface(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).AttachNetworkInterface), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).AttachNetworkInterface), input) } // CreateNetworkInterface mocks base method. -func (m *MockEC2Wrapper) CreateNetworkInterface(arg0 *ec2.CreateNetworkInterfaceInput) (*ec2.CreateNetworkInterfaceOutput, error) { +func (m *MockEC2Wrapper) CreateNetworkInterface(input *ec2.CreateNetworkInterfaceInput) (*ec2.CreateNetworkInterfaceOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateNetworkInterface", arg0) + ret := m.ctrl.Call(m, "CreateNetworkInterface", input) ret0, _ := ret[0].(*ec2.CreateNetworkInterfaceOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateNetworkInterface indicates an expected call of CreateNetworkInterface. -func (mr *MockEC2WrapperMockRecorder) CreateNetworkInterface(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) CreateNetworkInterface(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).CreateNetworkInterface), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).CreateNetworkInterface), input) } // CreateNetworkInterfacePermission mocks base method. -func (m *MockEC2Wrapper) CreateNetworkInterfacePermission(arg0 *ec2.CreateNetworkInterfacePermissionInput) (*ec2.CreateNetworkInterfacePermissionOutput, error) { +func (m *MockEC2Wrapper) CreateNetworkInterfacePermission(input *ec2.CreateNetworkInterfacePermissionInput) (*ec2.CreateNetworkInterfacePermissionOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateNetworkInterfacePermission", arg0) + ret := m.ctrl.Call(m, "CreateNetworkInterfacePermission", input) ret0, _ := ret[0].(*ec2.CreateNetworkInterfacePermissionOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateNetworkInterfacePermission indicates an expected call of CreateNetworkInterfacePermission. -func (mr *MockEC2WrapperMockRecorder) CreateNetworkInterfacePermission(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) CreateNetworkInterfacePermission(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNetworkInterfacePermission", reflect.TypeOf((*MockEC2Wrapper)(nil).CreateNetworkInterfacePermission), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNetworkInterfacePermission", reflect.TypeOf((*MockEC2Wrapper)(nil).CreateNetworkInterfacePermission), input) } // CreateTags mocks base method. -func (m *MockEC2Wrapper) CreateTags(arg0 *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { +func (m *MockEC2Wrapper) CreateTags(input *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateTags", arg0) + ret := m.ctrl.Call(m, "CreateTags", input) ret0, _ := ret[0].(*ec2.CreateTagsOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateTags indicates an expected call of CreateTags. -func (mr *MockEC2WrapperMockRecorder) CreateTags(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) CreateTags(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTags", reflect.TypeOf((*MockEC2Wrapper)(nil).CreateTags), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTags", reflect.TypeOf((*MockEC2Wrapper)(nil).CreateTags), input) } // DeleteNetworkInterface mocks base method. -func (m *MockEC2Wrapper) DeleteNetworkInterface(arg0 *ec2.DeleteNetworkInterfaceInput) (*ec2.DeleteNetworkInterfaceOutput, error) { +func (m *MockEC2Wrapper) DeleteNetworkInterface(ctx context.Context, input *ec2.DeleteNetworkInterfaceInput) (*ec2.DeleteNetworkInterfaceOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteNetworkInterface", arg0) + ret := m.ctrl.Call(m, "DeleteNetworkInterface", ctx, input) ret0, _ := ret[0].(*ec2.DeleteNetworkInterfaceOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // DeleteNetworkInterface indicates an expected call of DeleteNetworkInterface. -func (mr *MockEC2WrapperMockRecorder) DeleteNetworkInterface(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) DeleteNetworkInterface(ctx, input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).DeleteNetworkInterface), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).DeleteNetworkInterface), ctx, input) } // DescribeInstances mocks base method. -func (m *MockEC2Wrapper) DescribeInstances(arg0 *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { +func (m *MockEC2Wrapper) DescribeInstances(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DescribeInstances", arg0) + ret := m.ctrl.Call(m, "DescribeInstances", input) ret0, _ := ret[0].(*ec2.DescribeInstancesOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeInstances indicates an expected call of DescribeInstances. -func (mr *MockEC2WrapperMockRecorder) DescribeInstances(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) DescribeInstances(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeInstances", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeInstances), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeInstances", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeInstances), input) } // DescribeNetworkInterfaces mocks base method. -func (m *MockEC2Wrapper) DescribeNetworkInterfaces(arg0 *ec2.DescribeNetworkInterfacesInput) (*ec2.DescribeNetworkInterfacesOutput, error) { +func (m *MockEC2Wrapper) DescribeNetworkInterfaces(input *ec2.DescribeNetworkInterfacesInput) (*ec2.DescribeNetworkInterfacesOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DescribeNetworkInterfaces", arg0) + ret := m.ctrl.Call(m, "DescribeNetworkInterfaces", input) ret0, _ := ret[0].(*ec2.DescribeNetworkInterfacesOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeNetworkInterfaces indicates an expected call of DescribeNetworkInterfaces. -func (mr *MockEC2WrapperMockRecorder) DescribeNetworkInterfaces(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) DescribeNetworkInterfaces(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeNetworkInterfaces", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeNetworkInterfaces), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeNetworkInterfaces", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeNetworkInterfaces), input) } -// DescribeNetworkInterfacesPagesWithRetry mocks base method. -func (m *MockEC2Wrapper) DescribeNetworkInterfacesPagesWithRetry(arg0 *ec2.DescribeNetworkInterfacesInput) ([]*types.NetworkInterface, error) { +// DescribeNetworkInterfacesPages mocks base method. +func (m *MockEC2Wrapper) DescribeNetworkInterfacesPages(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput) ([]*types.NetworkInterface, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DescribeNetworkInterfacesPagesWithRetry", arg0) + ret := m.ctrl.Call(m, "DescribeNetworkInterfacesPages", ctx, input) ret0, _ := ret[0].([]*types.NetworkInterface) ret1, _ := ret[1].(error) return ret0, ret1 } -// DescribeNetworkInterfacesPagesWithRetry indicates an expected call of DescribeNetworkInterfacesPagesWithRetry. -func (mr *MockEC2WrapperMockRecorder) DescribeNetworkInterfacesPagesWithRetry(arg0 interface{}) *gomock.Call { +// DescribeNetworkInterfacesPages indicates an expected call of DescribeNetworkInterfacesPages. +func (mr *MockEC2WrapperMockRecorder) DescribeNetworkInterfacesPages(ctx, input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeNetworkInterfacesPagesWithRetry", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeNetworkInterfacesPagesWithRetry), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeNetworkInterfacesPages", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeNetworkInterfacesPages), ctx, input) } // DescribeSubnets mocks base method. -func (m *MockEC2Wrapper) DescribeSubnets(arg0 *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error) { +func (m *MockEC2Wrapper) DescribeSubnets(input *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DescribeSubnets", arg0) + ret := m.ctrl.Call(m, "DescribeSubnets", input) ret0, _ := ret[0].(*ec2.DescribeSubnetsOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeSubnets indicates an expected call of DescribeSubnets. -func (mr *MockEC2WrapperMockRecorder) DescribeSubnets(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) DescribeSubnets(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeSubnets", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeSubnets), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeSubnets", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeSubnets), input) } // DescribeTrunkInterfaceAssociations mocks base method. -func (m *MockEC2Wrapper) DescribeTrunkInterfaceAssociations(arg0 *ec2.DescribeTrunkInterfaceAssociationsInput) (*ec2.DescribeTrunkInterfaceAssociationsOutput, error) { +func (m *MockEC2Wrapper) DescribeTrunkInterfaceAssociations(input *ec2.DescribeTrunkInterfaceAssociationsInput) (*ec2.DescribeTrunkInterfaceAssociationsOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DescribeTrunkInterfaceAssociations", arg0) + ret := m.ctrl.Call(m, "DescribeTrunkInterfaceAssociations", input) ret0, _ := ret[0].(*ec2.DescribeTrunkInterfaceAssociationsOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // DescribeTrunkInterfaceAssociations indicates an expected call of DescribeTrunkInterfaceAssociations. -func (mr *MockEC2WrapperMockRecorder) DescribeTrunkInterfaceAssociations(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) DescribeTrunkInterfaceAssociations(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTrunkInterfaceAssociations", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeTrunkInterfaceAssociations), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeTrunkInterfaceAssociations", reflect.TypeOf((*MockEC2Wrapper)(nil).DescribeTrunkInterfaceAssociations), input) } // DetachNetworkInterface mocks base method. -func (m *MockEC2Wrapper) DetachNetworkInterface(arg0 *ec2.DetachNetworkInterfaceInput) (*ec2.DetachNetworkInterfaceOutput, error) { +func (m *MockEC2Wrapper) DetachNetworkInterface(input *ec2.DetachNetworkInterfaceInput) (*ec2.DetachNetworkInterfaceOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DetachNetworkInterface", arg0) + ret := m.ctrl.Call(m, "DetachNetworkInterface", input) ret0, _ := ret[0].(*ec2.DetachNetworkInterfaceOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // DetachNetworkInterface indicates an expected call of DetachNetworkInterface. -func (mr *MockEC2WrapperMockRecorder) DetachNetworkInterface(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) DetachNetworkInterface(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetachNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).DetachNetworkInterface), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetachNetworkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).DetachNetworkInterface), input) } // DisassociateTrunkInterface mocks base method. -func (m *MockEC2Wrapper) DisassociateTrunkInterface(arg0 *ec2.DisassociateTrunkInterfaceInput) error { +func (m *MockEC2Wrapper) DisassociateTrunkInterface(input *ec2.DisassociateTrunkInterfaceInput) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DisassociateTrunkInterface", arg0) + ret := m.ctrl.Call(m, "DisassociateTrunkInterface", input) ret0, _ := ret[0].(error) return ret0 } // DisassociateTrunkInterface indicates an expected call of DisassociateTrunkInterface. -func (mr *MockEC2WrapperMockRecorder) DisassociateTrunkInterface(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) DisassociateTrunkInterface(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisassociateTrunkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).DisassociateTrunkInterface), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DisassociateTrunkInterface", reflect.TypeOf((*MockEC2Wrapper)(nil).DisassociateTrunkInterface), input) } // ModifyNetworkInterfaceAttribute mocks base method. -func (m *MockEC2Wrapper) ModifyNetworkInterfaceAttribute(arg0 *ec2.ModifyNetworkInterfaceAttributeInput) (*ec2.ModifyNetworkInterfaceAttributeOutput, error) { +func (m *MockEC2Wrapper) ModifyNetworkInterfaceAttribute(input *ec2.ModifyNetworkInterfaceAttributeInput) (*ec2.ModifyNetworkInterfaceAttributeOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ModifyNetworkInterfaceAttribute", arg0) + ret := m.ctrl.Call(m, "ModifyNetworkInterfaceAttribute", input) ret0, _ := ret[0].(*ec2.ModifyNetworkInterfaceAttributeOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // ModifyNetworkInterfaceAttribute indicates an expected call of ModifyNetworkInterfaceAttribute. -func (mr *MockEC2WrapperMockRecorder) ModifyNetworkInterfaceAttribute(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) ModifyNetworkInterfaceAttribute(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyNetworkInterfaceAttribute", reflect.TypeOf((*MockEC2Wrapper)(nil).ModifyNetworkInterfaceAttribute), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyNetworkInterfaceAttribute", reflect.TypeOf((*MockEC2Wrapper)(nil).ModifyNetworkInterfaceAttribute), input) } // UnassignPrivateIPAddresses mocks base method. -func (m *MockEC2Wrapper) UnassignPrivateIPAddresses(arg0 *ec2.UnassignPrivateIpAddressesInput) (*ec2.UnassignPrivateIpAddressesOutput, error) { +func (m *MockEC2Wrapper) UnassignPrivateIPAddresses(input *ec2.UnassignPrivateIpAddressesInput) (*ec2.UnassignPrivateIpAddressesOutput, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UnassignPrivateIPAddresses", arg0) + ret := m.ctrl.Call(m, "UnassignPrivateIPAddresses", input) ret0, _ := ret[0].(*ec2.UnassignPrivateIpAddressesOutput) ret1, _ := ret[1].(error) return ret0, ret1 } // UnassignPrivateIPAddresses indicates an expected call of UnassignPrivateIPAddresses. -func (mr *MockEC2WrapperMockRecorder) UnassignPrivateIPAddresses(arg0 interface{}) *gomock.Call { +func (mr *MockEC2WrapperMockRecorder) UnassignPrivateIPAddresses(input interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnassignPrivateIPAddresses", reflect.TypeOf((*MockEC2Wrapper)(nil).UnassignPrivateIPAddresses), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnassignPrivateIPAddresses", reflect.TypeOf((*MockEC2Wrapper)(nil).UnassignPrivateIPAddresses), input) } diff --git a/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go b/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go index b2b1acb0..9a8000f5 100644 --- a/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go +++ b/mocks/amazon-vcp-resource-controller-k8s/pkg/k8s/mock_k8swrapper.go @@ -1,5 +1,18 @@ +// 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-vpc-resource-controller-k8s/pkg/k8s (interfaces: K8sWrapper) +// Source: github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s/wrapper.go // Package mock_k8s is a generated GoMock package. package mock_k8s @@ -42,162 +55,162 @@ func (m *MockK8sWrapper) EXPECT() *MockK8sWrapperMockRecorder { } // AddLabelToManageNode mocks base method. -func (m *MockK8sWrapper) AddLabelToManageNode(arg0 *v10.Node, arg1, arg2 string) (bool, error) { +func (m *MockK8sWrapper) AddLabelToManageNode(node *v10.Node, labelKey, labelValue string) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddLabelToManageNode", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "AddLabelToManageNode", node, labelKey, labelValue) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // AddLabelToManageNode indicates an expected call of AddLabelToManageNode. -func (mr *MockK8sWrapperMockRecorder) AddLabelToManageNode(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) AddLabelToManageNode(node, labelKey, labelValue interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddLabelToManageNode", reflect.TypeOf((*MockK8sWrapper)(nil).AddLabelToManageNode), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddLabelToManageNode", reflect.TypeOf((*MockK8sWrapper)(nil).AddLabelToManageNode), node, labelKey, labelValue) } // AdvertiseCapacityIfNotSet mocks base method. -func (m *MockK8sWrapper) AdvertiseCapacityIfNotSet(arg0, arg1 string, arg2 int) error { +func (m *MockK8sWrapper) AdvertiseCapacityIfNotSet(nodeName, resourceName string, capacity int) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AdvertiseCapacityIfNotSet", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "AdvertiseCapacityIfNotSet", nodeName, resourceName, capacity) ret0, _ := ret[0].(error) return ret0 } // AdvertiseCapacityIfNotSet indicates an expected call of AdvertiseCapacityIfNotSet. -func (mr *MockK8sWrapperMockRecorder) AdvertiseCapacityIfNotSet(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) AdvertiseCapacityIfNotSet(nodeName, resourceName, capacity interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdvertiseCapacityIfNotSet", reflect.TypeOf((*MockK8sWrapper)(nil).AdvertiseCapacityIfNotSet), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdvertiseCapacityIfNotSet", reflect.TypeOf((*MockK8sWrapper)(nil).AdvertiseCapacityIfNotSet), nodeName, resourceName, capacity) } // BroadcastEvent mocks base method. -func (m *MockK8sWrapper) BroadcastEvent(arg0 runtime.Object, arg1, arg2, arg3 string) { +func (m *MockK8sWrapper) BroadcastEvent(obj runtime.Object, reason, message, eventType string) { m.ctrl.T.Helper() - m.ctrl.Call(m, "BroadcastEvent", arg0, arg1, arg2, arg3) + m.ctrl.Call(m, "BroadcastEvent", obj, reason, message, eventType) } // BroadcastEvent indicates an expected call of BroadcastEvent. -func (mr *MockK8sWrapperMockRecorder) BroadcastEvent(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) BroadcastEvent(obj, reason, message, eventType interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BroadcastEvent", reflect.TypeOf((*MockK8sWrapper)(nil).BroadcastEvent), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BroadcastEvent", reflect.TypeOf((*MockK8sWrapper)(nil).BroadcastEvent), obj, reason, message, eventType) } // CreateCNINode mocks base method. -func (m *MockK8sWrapper) CreateCNINode(arg0 *v10.Node, arg1 string) error { +func (m *MockK8sWrapper) CreateCNINode(node *v10.Node, clusterName, nodeID string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateCNINode", arg0, arg1) + ret := m.ctrl.Call(m, "CreateCNINode", node, clusterName, nodeID) ret0, _ := ret[0].(error) return ret0 } // CreateCNINode indicates an expected call of CreateCNINode. -func (mr *MockK8sWrapperMockRecorder) CreateCNINode(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) CreateCNINode(node, clusterName, nodeID interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).CreateCNINode), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).CreateCNINode), node, clusterName, nodeID) } // DeleteCNINode mocks base method. -func (m *MockK8sWrapper) DeleteCNINode(arg0 *v1alpha10.CNINode) error { +func (m *MockK8sWrapper) DeleteCNINode(cniNode *v1alpha10.CNINode) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteCNINode", arg0) + ret := m.ctrl.Call(m, "DeleteCNINode", cniNode) ret0, _ := ret[0].(error) return ret0 } // DeleteCNINode indicates an expected call of DeleteCNINode. -func (mr *MockK8sWrapperMockRecorder) DeleteCNINode(arg0 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) DeleteCNINode(cniNode interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).DeleteCNINode), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).DeleteCNINode), cniNode) } // GetCNINode mocks base method. -func (m *MockK8sWrapper) GetCNINode(arg0 types.NamespacedName) (*v1alpha10.CNINode, error) { +func (m *MockK8sWrapper) GetCNINode(namespacedName types.NamespacedName) (*v1alpha10.CNINode, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCNINode", arg0) + ret := m.ctrl.Call(m, "GetCNINode", namespacedName) ret0, _ := ret[0].(*v1alpha10.CNINode) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCNINode indicates an expected call of GetCNINode. -func (mr *MockK8sWrapperMockRecorder) GetCNINode(arg0 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) GetCNINode(namespacedName interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).GetCNINode), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).GetCNINode), namespacedName) } // GetConfigMap mocks base method. -func (m *MockK8sWrapper) GetConfigMap(arg0, arg1 string) (*v10.ConfigMap, error) { +func (m *MockK8sWrapper) GetConfigMap(configMapName, configMapNamespace string) (*v10.ConfigMap, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetConfigMap", arg0, arg1) + ret := m.ctrl.Call(m, "GetConfigMap", configMapName, configMapNamespace) ret0, _ := ret[0].(*v10.ConfigMap) ret1, _ := ret[1].(error) return ret0, ret1 } // GetConfigMap indicates an expected call of GetConfigMap. -func (mr *MockK8sWrapperMockRecorder) GetConfigMap(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) GetConfigMap(configMapName, configMapNamespace interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfigMap", reflect.TypeOf((*MockK8sWrapper)(nil).GetConfigMap), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConfigMap", reflect.TypeOf((*MockK8sWrapper)(nil).GetConfigMap), configMapName, configMapNamespace) } // GetDaemonSet mocks base method. -func (m *MockK8sWrapper) GetDaemonSet(arg0, arg1 string) (*v1.DaemonSet, error) { +func (m *MockK8sWrapper) GetDaemonSet(namespace, name string) (*v1.DaemonSet, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDaemonSet", arg0, arg1) + ret := m.ctrl.Call(m, "GetDaemonSet", namespace, name) ret0, _ := ret[0].(*v1.DaemonSet) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDaemonSet indicates an expected call of GetDaemonSet. -func (mr *MockK8sWrapperMockRecorder) GetDaemonSet(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) GetDaemonSet(namespace, name interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDaemonSet", reflect.TypeOf((*MockK8sWrapper)(nil).GetDaemonSet), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDaemonSet", reflect.TypeOf((*MockK8sWrapper)(nil).GetDaemonSet), namespace, name) } // GetDeployment mocks base method. -func (m *MockK8sWrapper) GetDeployment(arg0, arg1 string) (*v1.Deployment, error) { +func (m *MockK8sWrapper) GetDeployment(namespace, name string) (*v1.Deployment, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetDeployment", arg0, arg1) + ret := m.ctrl.Call(m, "GetDeployment", namespace, name) ret0, _ := ret[0].(*v1.Deployment) ret1, _ := ret[1].(error) return ret0, ret1 } // GetDeployment indicates an expected call of GetDeployment. -func (mr *MockK8sWrapperMockRecorder) GetDeployment(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) GetDeployment(namespace, name interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeployment", reflect.TypeOf((*MockK8sWrapper)(nil).GetDeployment), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDeployment", reflect.TypeOf((*MockK8sWrapper)(nil).GetDeployment), namespace, name) } // GetENIConfig mocks base method. -func (m *MockK8sWrapper) GetENIConfig(arg0 string) (*v1alpha1.ENIConfig, error) { +func (m *MockK8sWrapper) GetENIConfig(eniConfigName string) (*v1alpha1.ENIConfig, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetENIConfig", arg0) + ret := m.ctrl.Call(m, "GetENIConfig", eniConfigName) ret0, _ := ret[0].(*v1alpha1.ENIConfig) ret1, _ := ret[1].(error) return ret0, ret1 } // GetENIConfig indicates an expected call of GetENIConfig. -func (mr *MockK8sWrapperMockRecorder) GetENIConfig(arg0 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) GetENIConfig(eniConfigName interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetENIConfig", reflect.TypeOf((*MockK8sWrapper)(nil).GetENIConfig), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetENIConfig", reflect.TypeOf((*MockK8sWrapper)(nil).GetENIConfig), eniConfigName) } // GetNode mocks base method. -func (m *MockK8sWrapper) GetNode(arg0 string) (*v10.Node, error) { +func (m *MockK8sWrapper) GetNode(nodeName string) (*v10.Node, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNode", arg0) + ret := m.ctrl.Call(m, "GetNode", nodeName) ret0, _ := ret[0].(*v10.Node) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNode indicates an expected call of GetNode. -func (mr *MockK8sWrapperMockRecorder) GetNode(arg0 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) GetNode(nodeName interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNode", reflect.TypeOf((*MockK8sWrapper)(nil).GetNode), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNode", reflect.TypeOf((*MockK8sWrapper)(nil).GetNode), nodeName) } // ListCNINodes mocks base method. @@ -216,18 +229,18 @@ func (mr *MockK8sWrapperMockRecorder) ListCNINodes() *gomock.Call { } // ListEvents mocks base method. -func (m *MockK8sWrapper) ListEvents(arg0 []client.ListOption) (*v11.EventList, error) { +func (m *MockK8sWrapper) ListEvents(ops []client.ListOption) (*v11.EventList, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListEvents", arg0) + ret := m.ctrl.Call(m, "ListEvents", ops) ret0, _ := ret[0].(*v11.EventList) ret1, _ := ret[1].(error) return ret0, ret1 } // ListEvents indicates an expected call of ListEvents. -func (mr *MockK8sWrapperMockRecorder) ListEvents(arg0 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) ListEvents(ops interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEvents", reflect.TypeOf((*MockK8sWrapper)(nil).ListEvents), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListEvents", reflect.TypeOf((*MockK8sWrapper)(nil).ListEvents), ops) } // ListNodes mocks base method. @@ -246,15 +259,15 @@ func (mr *MockK8sWrapperMockRecorder) ListNodes() *gomock.Call { } // PatchCNINode mocks base method. -func (m *MockK8sWrapper) PatchCNINode(arg0, arg1 *v1alpha10.CNINode) error { +func (m *MockK8sWrapper) PatchCNINode(oldCNINode, newCNINode *v1alpha10.CNINode) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PatchCNINode", arg0, arg1) + ret := m.ctrl.Call(m, "PatchCNINode", oldCNINode, newCNINode) ret0, _ := ret[0].(error) return ret0 } // PatchCNINode indicates an expected call of PatchCNINode. -func (mr *MockK8sWrapperMockRecorder) PatchCNINode(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockK8sWrapperMockRecorder) PatchCNINode(oldCNINode, newCNINode interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).PatchCNINode), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchCNINode", reflect.TypeOf((*MockK8sWrapper)(nil).PatchCNINode), oldCNINode, newCNINode) } diff --git a/pkg/aws/ec2/api/cleanup/eni_cleanup.go b/pkg/aws/ec2/api/cleanup/eni_cleanup.go index 19047c81..b80088d7 100644 --- a/pkg/aws/ec2/api/cleanup/eni_cleanup.go +++ b/pkg/aws/ec2/api/cleanup/eni_cleanup.go @@ -99,7 +99,7 @@ func (e *ClusterENICleaner) Start(ctx context.Context) error { // Perform ENI cleanup after fixed time intervals till shut down variable is set to true on receiving the shutdown // signal for !e.shutdown { - e.DeleteLeakedResources() + e.DeleteLeakedResources(ctx) time.Sleep(config.ENICleanUpInterval) } @@ -110,7 +110,7 @@ func (e *ClusterENICleaner) Start(ctx context.Context) error { // This is called by periodically by ClusterENICleaner which deletes available ENIs cluster-wide, and by the NodeTermination cleaner on node termination // The available ENIs are deleted if ShouldDeleteENI is true, defined in the respective cleaners // The function also updates metrics for the periodic cleanup routine and the node termination cleanup -func (e *ENICleaner) DeleteLeakedResources() error { +func (e *ENICleaner) DeleteLeakedResources(ctx context.Context) error { var errors []error availableENIs := make(map[string]struct{}) vpcrcAvailableCount := 0 @@ -140,7 +140,7 @@ func (e *ENICleaner) DeleteLeakedResources() error { Filters: filterCopy, } - tempNetworkInterfaces, err := e.EC2Wrapper.DescribeNetworkInterfacesPagesWithRetry(describeNetworkInterfaceIp) + tempNetworkInterfaces, err := e.EC2Wrapper.DescribeNetworkInterfacesPages(ctx, describeNetworkInterfaceIp) if err != nil { e.Log.Error(err, "failed to describe network interfaces, cleanup will be retried in next cycle") return err @@ -151,7 +151,7 @@ func (e *ENICleaner) DeleteLeakedResources() error { describeNetworkInterfaceIp := &ec2.DescribeNetworkInterfacesInput{ Filters: filters, } - networkInterfaces, err = e.EC2Wrapper.DescribeNetworkInterfacesPagesWithRetry(describeNetworkInterfaceIp) + networkInterfaces, err = e.EC2Wrapper.DescribeNetworkInterfacesPages(ctx, describeNetworkInterfaceIp) if err != nil { e.Log.Error(err, "failed to describe network interfaces, cleanup will be retried in next cycle") return err @@ -174,7 +174,7 @@ func (e *ENICleaner) DeleteLeakedResources() error { continue } } - _, err := e.EC2Wrapper.DeleteNetworkInterface(&ec2.DeleteNetworkInterfaceInput{ + _, err := e.EC2Wrapper.DeleteNetworkInterface(ctx, &ec2.DeleteNetworkInterfaceInput{ NetworkInterfaceId: nwInterface.NetworkInterfaceId, }) if err != nil { diff --git a/pkg/aws/ec2/api/cleanup/eni_cleanup_test.go b/pkg/aws/ec2/api/cleanup/eni_cleanup_test.go index 5c02ff46..6904099a 100644 --- a/pkg/aws/ec2/api/cleanup/eni_cleanup_test.go +++ b/pkg/aws/ec2/api/cleanup/eni_cleanup_test.go @@ -163,32 +163,37 @@ func TestENICleaner_DeleteLeakedResources(t *testing.T) { filtersWithFirstTag := append(append(commonFilters, vpcFilter), firstOrFilter) filtersWithSecondTag := append(append(commonFilters, vpcFilter), secondOrFilter) gomock.InOrder( - f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPagesWithRetry( + f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPages( + context.TODO(), &ec2.DescribeNetworkInterfacesInput{ Filters: filtersWithFirstTag, }, ).Return(NetworkInterfacesWith1And2, nil), - f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPagesWithRetry( + f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPages( + context.TODO(), &ec2.DescribeNetworkInterfacesInput{ Filters: filtersWithSecondTag, }, ).Return([]*ec2types.NetworkInterface{}, nil), // Return network interface 1 and 3 in the second cycle - f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPagesWithRetry( + f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPages( + context.TODO(), &ec2.DescribeNetworkInterfacesInput{ Filters: filtersWithFirstTag, }, ).Return(NetworkInterfacesWith1And3, nil), - f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPagesWithRetry( + f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPages( + context.TODO(), &ec2.DescribeNetworkInterfacesInput{ Filters: filtersWithSecondTag, }, ).Return([]*ec2types.NetworkInterface{}, nil), f.mockEC2Wrapper.EXPECT().DeleteNetworkInterface( + context.TODO(), &ec2.DeleteNetworkInterfaceInput{NetworkInterfaceId: &mockNetworkInterfaceId1}, ).Return(nil, nil), ) @@ -221,18 +226,22 @@ func TestENICleaner_DeleteLeakedResources(t *testing.T) { gomock.InOrder( // Return network interface 1 and 2 in first cycle, expect to call delete on both - f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPagesWithRetry(mockNodeIDTagInput). + f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPages(context.TODO(), mockNodeIDTagInput). Return(NetworkInterfacesWith1And2, nil), f.mockEC2Wrapper.EXPECT().DeleteNetworkInterface( + context.TODO(), &ec2.DeleteNetworkInterfaceInput{NetworkInterfaceId: &mockNetworkInterfaceId1}).Return(nil, nil), f.mockEC2Wrapper.EXPECT().DeleteNetworkInterface( + context.TODO(), &ec2.DeleteNetworkInterfaceInput{NetworkInterfaceId: &mockNetworkInterfaceId2}).Return(nil, nil), // Return network interface 1 and 3 in the second cycle, again expect to call delete on both - f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPagesWithRetry(mockNodeIDTagInput). + f.mockEC2Wrapper.EXPECT().DescribeNetworkInterfacesPages(context.TODO(), mockNodeIDTagInput). Return(NetworkInterfacesWith1And3, nil), f.mockEC2Wrapper.EXPECT().DeleteNetworkInterface( + context.TODO(), &ec2.DeleteNetworkInterfaceInput{NetworkInterfaceId: &mockNetworkInterfaceId1}).Return(nil, nil), f.mockEC2Wrapper.EXPECT().DeleteNetworkInterface( + context.TODO(), &ec2.DeleteNetworkInterfaceInput{NetworkInterfaceId: &mockNetworkInterfaceId3}).Return(nil, nil), ) }, @@ -257,13 +266,13 @@ func TestENICleaner_DeleteLeakedResources(t *testing.T) { tt.prepare(&f) } - err := mockENICleaner.DeleteLeakedResources() + err := mockENICleaner.DeleteLeakedResources(context.TODO()) assert.NoError(t, err) if tt.assertFirstCall != nil { tt.assertFirstCall(&f) } - err = mockENICleaner.DeleteLeakedResources() + err = mockENICleaner.DeleteLeakedResources(context.TODO()) assert.NoError(t, err) if tt.assertSecondCall != nil { tt.assertSecondCall(&f) diff --git a/pkg/aws/ec2/api/cleanup/node_cleanup.go b/pkg/aws/ec2/api/cleanup/node_cleanup.go index 4c16a33d..0fb40d78 100644 --- a/pkg/aws/ec2/api/cleanup/node_cleanup.go +++ b/pkg/aws/ec2/api/cleanup/node_cleanup.go @@ -18,7 +18,7 @@ import ( "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config" "github.com/aws/aws-sdk-go-v2/aws" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - ctrl "sigs.k8s.io/controller-runtime" + "github.com/go-logr/logr" ) // NodeTerminationCleanerto handle resource cleanup at node termination @@ -51,7 +51,7 @@ func (n *NodeTerminationCleaner) UpdateCleanupMetrics(vpcrcAvailableCount *int, return } -func NewNodeResourceCleaner(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string) ResourceCleaner { +func NewNodeResourceCleaner(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID string, log logr.Logger) ResourceCleaner { cleaner := &NodeTerminationCleaner{ NodeID: nodeID, } @@ -59,7 +59,7 @@ func NewNodeResourceCleaner(nodeID string, eC2Wrapper ec2API.EC2Wrapper, vpcID s EC2Wrapper: eC2Wrapper, Manager: cleaner, VpcId: vpcID, - Log: ctrl.Log.WithName("eniCleaner").WithName("node"), + Log: log.WithName("eniCleaner").WithName("node"), } return cleaner.ENICleaner } diff --git a/pkg/aws/ec2/api/cleanup/resource_cleaner.go b/pkg/aws/ec2/api/cleanup/resource_cleaner.go index d5c7e6d1..58c60f06 100644 --- a/pkg/aws/ec2/api/cleanup/resource_cleaner.go +++ b/pkg/aws/ec2/api/cleanup/resource_cleaner.go @@ -13,7 +13,9 @@ package cleanup +import "context" + // ResourceCleaner interface should be implemented by components that need to delete leaked AWS resources type ResourceCleaner interface { - DeleteLeakedResources() error + DeleteLeakedResources(ctx context.Context) error } diff --git a/pkg/aws/ec2/api/helper.go b/pkg/aws/ec2/api/helper.go index ddc02b48..442c93a7 100644 --- a/pkg/aws/ec2/api/helper.go +++ b/pkg/aws/ec2/api/helper.go @@ -14,6 +14,7 @@ package api import ( + "context" "fmt" "time" @@ -208,7 +209,7 @@ func (h *ec2APIHelper) DeleteNetworkInterface(interfaceId *string) error { } err := retry.OnError(defaultBackOff, func(err error) bool { return true }, func() error { - _, err := h.ec2Wrapper.DeleteNetworkInterface(deleteNetworkInterface) + _, err := h.ec2Wrapper.DeleteNetworkInterface(context.TODO(), deleteNetworkInterface) return err }) diff --git a/pkg/aws/ec2/api/helper_test.go b/pkg/aws/ec2/api/helper_test.go index d3dfd8f8..c2376ed0 100644 --- a/pkg/aws/ec2/api/helper_test.go +++ b/pkg/aws/ec2/api/helper_test.go @@ -14,6 +14,7 @@ package api import ( + "context" "fmt" "testing" "time" @@ -552,7 +553,7 @@ func TestEc2APIHelper_DeleteNetworkInterface(t *testing.T) { ec2ApiHelper, mockWrapper := getMockWrapper(ctrl) - mockWrapper.EXPECT().DeleteNetworkInterface(deleteNetworkInterfaceInput).Return(nil, nil) + mockWrapper.EXPECT().DeleteNetworkInterface(context.TODO(), deleteNetworkInterfaceInput).Return(nil, nil) err := ec2ApiHelper.DeleteNetworkInterface(&branchInterfaceId) assert.NoError(t, err) @@ -566,7 +567,7 @@ func TestEc2APIHelper_DeleteNetworkInterface_Error(t *testing.T) { ec2ApiHelper, mockWrapper := getMockWrapper(ctrl) - mockWrapper.EXPECT().DeleteNetworkInterface(deleteNetworkInterfaceInput).Return(nil, errMock).Times(maxRetryOnError) + mockWrapper.EXPECT().DeleteNetworkInterface(context.TODO(), deleteNetworkInterfaceInput).Return(nil, errMock).Times(maxRetryOnError) err := ec2ApiHelper.DeleteNetworkInterface(&branchInterfaceId) assert.Error(t, errMock, err) @@ -581,8 +582,8 @@ func TestEc2APIHelper_DeleteNetworkInterface_ErrorThenSuccess(t *testing.T) { ec2ApiHelper, mockWrapper := getMockWrapper(ctrl) gomock.InOrder( - mockWrapper.EXPECT().DeleteNetworkInterface(deleteNetworkInterfaceInput).Return(nil, errMock).Times(2), - mockWrapper.EXPECT().DeleteNetworkInterface(deleteNetworkInterfaceInput).Return(nil, nil).Times(1), + mockWrapper.EXPECT().DeleteNetworkInterface(context.TODO(), deleteNetworkInterfaceInput).Return(nil, errMock).Times(2), + mockWrapper.EXPECT().DeleteNetworkInterface(context.TODO(), deleteNetworkInterfaceInput).Return(nil, nil).Times(1), ) err := ec2ApiHelper.DeleteNetworkInterface(&branchInterfaceId) @@ -771,7 +772,7 @@ func TestEc2APIHelper_CreateAndAttachNetworkInterface_DeleteOnAttachFailed(t *te mockWrapper.EXPECT().AttachNetworkInterface(attachNetworkInterfaceInput).Return(attachNetworkInterfaceOutput, errMock) // Test delete is called - mockWrapper.EXPECT().DeleteNetworkInterface(deleteNetworkInterfaceInput).Return(nil, nil) + mockWrapper.EXPECT().DeleteNetworkInterface(context.TODO(), deleteNetworkInterfaceInput).Return(nil, nil) nwInterface, err := ec2ApiHelper.CreateAndAttachNetworkInterface(&instanceId, &subnetId, securityGroups, tags, &deviceIndex, &eniDescription, nil, nil) @@ -796,7 +797,7 @@ func TestEc2APIHelper_CreateAndAttachNetworkInterface_DeleteOnSetTerminationFail mockWrapper.EXPECT().DetachNetworkInterface(detachNetworkInterfaceInput).Return(nil, nil) mockWrapper.EXPECT().DescribeNetworkInterfaces(describeNetworkInterfaceInputUsingOneInterfaceId). Return(describeNetworkInterfaceOutputUsingOneInterfaceId, nil) - mockWrapper.EXPECT().DeleteNetworkInterface(deleteNetworkInterfaceInput).Return(nil, nil) + mockWrapper.EXPECT().DeleteNetworkInterface(context.TODO(), deleteNetworkInterfaceInput).Return(nil, nil) nwInterface, err := ec2ApiHelper.CreateAndAttachNetworkInterface(&instanceId, &subnetId, securityGroups, tags, &deviceIndex, &eniDescription, nil, nil) @@ -944,7 +945,7 @@ func TestEc2APIHelper_DetachAndDeleteNetworkInterface(t *testing.T) { mockWrapper.EXPECT().DetachNetworkInterface(detachNetworkInterfaceInput).Return(nil, nil) mockWrapper.EXPECT().DescribeNetworkInterfaces(describeNetworkInterfaceInputUsingOneInterfaceId). Return(describeNetworkInterfaceOutputUsingOneInterfaceId, nil) - mockWrapper.EXPECT().DeleteNetworkInterface(deleteNetworkInterfaceInput).Return(nil, nil) + mockWrapper.EXPECT().DeleteNetworkInterface(context.TODO(), deleteNetworkInterfaceInput).Return(nil, nil) err := ec2ApiHelper.DetachAndDeleteNetworkInterface(&attachmentId, &branchInterfaceId) assert.NoError(t, err) diff --git a/pkg/aws/ec2/api/wrapper.go b/pkg/aws/ec2/api/wrapper.go index e957bb5d..19169713 100644 --- a/pkg/aws/ec2/api/wrapper.go +++ b/pkg/aws/ec2/api/wrapper.go @@ -15,7 +15,6 @@ package api import ( "context" - "errors" "fmt" "net/http" "strings" @@ -23,7 +22,6 @@ import ( vpc_rc_config "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils" - "k8s.io/apimachinery/pkg/util/wait" "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/version" smithymiddleware "github.com/aws/smithy-go/middleware" @@ -37,7 +35,6 @@ import ( "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/smithy-go" "github.com/aws/aws-sdk-go-v2/service/ec2" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" @@ -60,11 +57,11 @@ type EC2Wrapper interface { CreateNetworkInterface(input *ec2.CreateNetworkInterfaceInput) (*ec2.CreateNetworkInterfaceOutput, error) AttachNetworkInterface(input *ec2.AttachNetworkInterfaceInput) (*ec2.AttachNetworkInterfaceOutput, error) DetachNetworkInterface(input *ec2.DetachNetworkInterfaceInput) (*ec2.DetachNetworkInterfaceOutput, error) - DeleteNetworkInterface(input *ec2.DeleteNetworkInterfaceInput) (*ec2.DeleteNetworkInterfaceOutput, error) + DeleteNetworkInterface(ctx context.Context, input *ec2.DeleteNetworkInterfaceInput) (*ec2.DeleteNetworkInterfaceOutput, error) AssignPrivateIPAddresses(input *ec2.AssignPrivateIpAddressesInput) (*ec2.AssignPrivateIpAddressesOutput, error) UnassignPrivateIPAddresses(input *ec2.UnassignPrivateIpAddressesInput) (*ec2.UnassignPrivateIpAddressesOutput, error) DescribeNetworkInterfaces(input *ec2.DescribeNetworkInterfacesInput) (*ec2.DescribeNetworkInterfacesOutput, error) - DescribeNetworkInterfacesPagesWithRetry(input *ec2.DescribeNetworkInterfacesInput) ([]*ec2types.NetworkInterface, error) + DescribeNetworkInterfacesPages(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput) ([]*ec2types.NetworkInterface, error) CreateTags(input *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) DescribeSubnets(input *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error) AssociateTrunkInterface(input *ec2.AssociateTrunkInterfaceInput) (*ec2.AssociateTrunkInterfaceOutput, error) @@ -668,9 +665,9 @@ func (e *ec2Wrapper) AttachNetworkInterface(input *ec2.AttachNetworkInterfaceInp return attachNetworkInterfaceOutput, err } -func (e *ec2Wrapper) DeleteNetworkInterface(input *ec2.DeleteNetworkInterfaceInput) (*ec2.DeleteNetworkInterfaceOutput, error) { +func (e *ec2Wrapper) DeleteNetworkInterface(ctx context.Context, input *ec2.DeleteNetworkInterfaceInput) (*ec2.DeleteNetworkInterfaceOutput, error) { start := time.Now() - deleteNetworkInterfaceOutput, err := e.userServiceClient.DeleteNetworkInterface(context.TODO(), input) + deleteNetworkInterfaceOutput, err := e.userServiceClient.DeleteNetworkInterface(ctx, input) ec2APICallLatencies.WithLabelValues("delete_network_interface").Observe(timeSinceMs(start)) // Metric updates @@ -721,7 +718,7 @@ func (e *ec2Wrapper) DescribeNetworkInterfaces(input *ec2.DescribeNetworkInterfa // DescribeNetworkInterfacesPagesWithRetry returns network interfaces that match the filters specified in the input // with retry mechanism for handling API throttling -func (e *ec2Wrapper) DescribeNetworkInterfacesPagesWithRetry(input *ec2.DescribeNetworkInterfacesInput) ([]*ec2types.NetworkInterface, error) { +func (e *ec2Wrapper) DescribeNetworkInterfacesPages(ctx context.Context, input *ec2.DescribeNetworkInterfacesInput) ([]*ec2types.NetworkInterface, error) { if input.MaxResults == nil { input.MaxResults = aws.Int32(int32(vpc_rc_config.DescribeNetworkInterfacesMaxResults)) } @@ -731,50 +728,29 @@ func (e *ec2Wrapper) DescribeNetworkInterfacesPagesWithRetry(input *ec2.Describe ec2APICallLatencies.WithLabelValues("describe_network_interfaces_pages").Observe(timeSinceMs(start)) }() - var apiError error - for attempt := 1; attempt <= MaxRetries; attempt++ { - attemptInterfaces := make([]*ec2types.NetworkInterface, 0, vpc_rc_config.DescribeNetworkInterfacesMaxResults) - - paginator := ec2.NewDescribeNetworkInterfacesPaginator(e.userServiceClient, input) - - for paginator.HasMorePages() { - output, err := paginator.NextPage(context.TODO()) - if err != nil { - ec2APIErrCnt.Inc() - ec2DescribeNetworkInterfacesPagesAPIErrCnt.Inc() - apiError = err - - var ae smithy.APIError - if errors.As(err, &ae) && ae.ErrorCode() == "Throttling" && attempt < MaxRetries { - e.log.Info("Throttling error, will retry", "attempt", attempt) - backoff := time.Duration(attempt) * 500 * time.Millisecond - time.Sleep(wait.Jitter(backoff, 0.1)) - goto Retry - } - return nil, err - } - - ec2APICallCnt.Inc() - ec2DescribeNetworkInterfacesPagesAPICallCnt.Inc() - - for _, nwInterface := range output.NetworkInterfaces { - attemptInterfaces = append(attemptInterfaces, &ec2types.NetworkInterface{ - NetworkInterfaceId: nwInterface.NetworkInterfaceId, - TagSet: nwInterface.TagSet, - Attachment: nwInterface.Attachment, - }) - } - - time.Sleep(wait.Jitter(100*time.Millisecond, 0.2)) - } + nwInterfaces := make([]*ec2types.NetworkInterface, 0, vpc_rc_config.DescribeNetworkInterfacesMaxResults) - return attemptInterfaces, nil + paginator := ec2.NewDescribeNetworkInterfacesPaginator(e.userServiceClient, input) - Retry: - continue + for paginator.HasMorePages() { + output, err := paginator.NextPage(ctx) + if err != nil { + ec2APIErrCnt.Inc() + ec2DescribeNetworkInterfacesPagesAPIErrCnt.Inc() + return nil, err + } + ec2APICallCnt.Inc() + ec2DescribeNetworkInterfacesPagesAPICallCnt.Inc() + + for _, nwInterface := range output.NetworkInterfaces { + nwInterfaces = append(nwInterfaces, &ec2types.NetworkInterface{ + NetworkInterfaceId: nwInterface.NetworkInterfaceId, + TagSet: nwInterface.TagSet, + Attachment: nwInterface.Attachment, + }) + } } - - return nil, apiError + return nwInterfaces, nil } func (e *ec2Wrapper) AssignPrivateIPAddresses(input *ec2.AssignPrivateIpAddressesInput) (*ec2.AssignPrivateIpAddressesOutput, error) { diff --git a/pkg/config/type.go b/pkg/config/type.go index f658fc49..b14e2130 100644 --- a/pkg/config/type.go +++ b/pkg/config/type.go @@ -118,9 +118,10 @@ const ( // customized configurations for BigCache const ( - InstancesCacheTTL = 30 * time.Minute // scaling < 1k nodes should be under 20 minutes - InstancesCacheShards = 32 // must be power of 2 - InstancesCacheMaxSize = 2 // in MB + InstancesCacheTTL = 30 * time.Minute // scaling < 1k nodes should be under 20 minutes + InstancesCacheShards = 32 // must be power of 2 + InstancesCacheMaxSize = 2 // in MB + NodeTerminationTimeout = 3 * time.Minute ) var ( diff --git a/pkg/k8s/wrapper.go b/pkg/k8s/wrapper.go index ffd46662..9acf7e5b 100644 --- a/pkg/k8s/wrapper.go +++ b/pkg/k8s/wrapper.go @@ -15,7 +15,6 @@ package k8s import ( "context" - "fmt" "strconv" "github.com/aws/amazon-vpc-cni-k8s/pkg/apis/crd/v1alpha1" @@ -81,7 +80,7 @@ type K8sWrapper interface { AddLabelToManageNode(node *v1.Node, labelKey string, labelValue string) (bool, error) ListEvents(ops []client.ListOption) (*eventsv1.EventList, error) GetCNINode(namespacedName types.NamespacedName) (*rcv1alpha1.CNINode, error) - CreateCNINode(node *v1.Node, clusterName string) error + CreateCNINode(node *v1.Node, clusterName string, nodeID string) error ListCNINodes() ([]*rcv1alpha1.CNINode, error) PatchCNINode(oldCNINode, newCNINode *rcv1alpha1.CNINode) error DeleteCNINode(cniNode *rcv1alpha1.CNINode) error @@ -237,7 +236,7 @@ func (k *k8sWrapper) GetCNINode(namespacedName types.NamespacedName) (*rcv1alpha return cninode, nil } -func (k *k8sWrapper) CreateCNINode(node *v1.Node, clusterName string) error { +func (k *k8sWrapper) CreateCNINode(node *v1.Node, clusterName string, nodeID string) error { cniNode := &rcv1alpha1.CNINode{ ObjectMeta: metav1.ObjectMeta{ Name: node.Name, @@ -260,7 +259,8 @@ func (k *k8sWrapper) CreateCNINode(node *v1.Node, clusterName string) error { }, Spec: rcv1alpha1.CNINodeSpec{ Tags: map[string]string{ - fmt.Sprintf(config.VPCCNIClusterNameKey): clusterName, + config.VPCCNIClusterNameKey: clusterName, + config.NetworkInterfaceNodeIDKey: nodeID, }, }, } diff --git a/pkg/k8s/wrapper_test.go b/pkg/k8s/wrapper_test.go index 00c63fc2..addc1c5c 100644 --- a/pkg/k8s/wrapper_test.go +++ b/pkg/k8s/wrapper_test.go @@ -38,6 +38,7 @@ import ( var ( nodeName = "node-name" mockClusterName = "cluster-name" + mockNodeID = "i-123456789" mockResourceName = config.ResourceNamePodENI existingResource = "extended-resource" @@ -200,22 +201,25 @@ func TestK8sWrapper_CreateCNINodeWithExistedObject_NoError(t *testing.T) { ctrl := gomock.NewController(t) wrapper, _, _ := getMockK8sWrapperWithClient(ctrl, []runtime.Object{mockCNINode}) - err := wrapper.CreateCNINode(mockNode, mockClusterName) + err := wrapper.CreateCNINode(mockNode, mockClusterName, mockNodeID) assert.NoError(t, err) cniNode, err := wrapper.GetCNINode(types.NamespacedName{Name: mockNode.Name}) assert.NoError(t, err) assert.Equal(t, mockNode.Name, cniNode.Name) - err = wrapper.CreateCNINode(mockNode, mockClusterName) + err = wrapper.CreateCNINode(mockNode, mockClusterName, mockNodeID) assert.NoError(t, err) } func TestK8sWrapper_CreateCNINode_NoError(t *testing.T) { ctrl := gomock.NewController(t) - wrapper, _, _ := getMockK8sWrapperWithClient(ctrl, []runtime.Object{mockCNINode}) + wrapper, _, _ := getMockK8sWrapperWithClient(ctrl, []runtime.Object{}) - err := wrapper.CreateCNINode(mockNode, mockClusterName) + err := wrapper.CreateCNINode(mockNode, mockClusterName, mockNodeID) assert.NoError(t, err) cniNode, err := wrapper.GetCNINode(types.NamespacedName{Name: mockNode.Name}) assert.NoError(t, err) assert.Equal(t, mockNode.Name, cniNode.Name) + assert.Equal(t, mockNodeID, cniNode.Spec.Tags[config.NetworkInterfaceNodeIDKey]) + assert.Equal(t, mockClusterName, cniNode.Spec.Tags[config.VPCCNIClusterNameKey]) + assert.Contains(t, cniNode.Finalizers, config.NodeTerminationFinalizer) } diff --git a/pkg/node/manager/manager.go b/pkg/node/manager/manager.go index d9d14b1a..560e4c41 100644 --- a/pkg/node/manager/manager.go +++ b/pkg/node/manager/manager.go @@ -229,8 +229,12 @@ func (m *manager) CreateCNINodeIfNotExisting(node *v1.Node) error { types.NamespacedName{Name: node.Name}, ); err != nil { if apierrors.IsNotFound(err) { + nodeID, err := utils.GetNodeID(node) + if err != nil { + m.Log.Error(err, "failed to get node ID") + } m.Log.Info("Will create a new CNINode", "CNINodeName", node.Name) - return m.wrapper.K8sAPI.CreateCNINode(node, m.clusterName) + return m.wrapper.K8sAPI.CreateCNINode(node, m.clusterName, nodeID) } return err } else { diff --git a/pkg/node/manager/manager_test.go b/pkg/node/manager/manager_test.go index bab5db98..fd6c917c 100644 --- a/pkg/node/manager/manager_test.go +++ b/pkg/node/manager/manager_test.go @@ -204,7 +204,7 @@ func Test_AddNode_CNINode_Existing(t *testing.T) { mock.MockK8sAPI.EXPECT().GetNode(nodeName).Return(v1Node, nil).Times(1) mock.MockWorker.EXPECT().SubmitJob(gomock.All(NewAsyncOperationMatcher(expectedJob))) - mock.MockK8sAPI.EXPECT().CreateCNINode(v1Node, mockClusterName).Return(nil).Times(0) + mock.MockK8sAPI.EXPECT().CreateCNINode(v1Node, mockClusterName, instanceID).Return(nil).Times(0) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: v1Node.Name}).Return(&rcV1alpha1.CNINode{}, nil).Times(2) err := mock.Manager.AddNode(nodeName) @@ -227,7 +227,7 @@ func Test_AddNode_CNINode_Not_Existing(t *testing.T) { mock.MockK8sAPI.EXPECT().GetNode(nodeName).Return(v1Node, nil).Times(1) mock.MockWorker.EXPECT().SubmitJob(gomock.All(NewAsyncOperationMatcher(expectedJob))) - mock.MockK8sAPI.EXPECT().CreateCNINode(v1Node, mock.Manager.clusterName).Return(nil).Times(1) + mock.MockK8sAPI.EXPECT().CreateCNINode(v1Node, mock.Manager.clusterName, instanceID).Return(nil).Times(1) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: v1Node.Name}).Return( &rcV1alpha1.CNINode{}, apierrors.NewNotFound(schema.GroupResource{Group: "vpcresources.k8s.aws", Resource: "1"}, "test")). Times(2) @@ -248,7 +248,7 @@ func Test_AddNode_UnManaged(t *testing.T) { nodeWithoutLabel.Labels = map[string]string{} mock.MockK8sAPI.EXPECT().GetNode(nodeName).Return(nodeWithoutLabel, nil).Times(1) - mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithoutLabel, mock.Manager.clusterName).Return(nil).Times(1) + mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithoutLabel, mock.Manager.clusterName, instanceID).Return(nil).Times(1) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: nodeWithoutLabel.Name}).Return( &rcV1alpha1.CNINode{}, apierrors.NewNotFound(schema.GroupResource{Group: "vpcresources.k8s.aws", Resource: "1"}, "test")). Times(1) // unmanaged node won't check custom networking subnets and call GetCNINode only once @@ -290,7 +290,7 @@ func Test_AddNode_CustomNetworking_CNINode(t *testing.T) { mock.MockK8sAPI.EXPECT().GetNode(nodeName).Return(nodeWithENIConfig, nil) mock.MockK8sAPI.EXPECT().GetENIConfig(eniConfigName).Return(eniConfig, nil).Times(1) mock.MockWorker.EXPECT().SubmitJob(gomock.All(NewAsyncOperationMatcher(job))) - mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName).Return(nil).Times(1) + mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName, instanceID).Return(nil).Times(1) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: nodeWithENIConfig.Name}).Return(&rcV1alpha1.CNINode{ Spec: rcV1alpha1.CNINodeSpec{ Features: []rcV1alpha1.Feature{{Name: rcV1alpha1.CustomNetworking, Value: eniConfigName}}, @@ -330,7 +330,7 @@ func Test_AddNode_CustomNetworking_CNINode_No_EniConfigName(t *testing.T) { mock.MockK8sAPI.EXPECT().BroadcastEvent(nodeWithENIConfig, utils.EniConfigNameNotFoundReason, msg, v1.EventTypeWarning).Times(1) mock.MockK8sAPI.EXPECT().GetENIConfig(eniConfigName).Return(eniConfig, nil).Times(0) mock.MockWorker.EXPECT().SubmitJob(gomock.All(NewAsyncOperationMatcher(job))).Times(0) - mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName).Return(nil).Times(1) + mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName, instanceID).Return(nil).Times(1) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: nodeWithENIConfig.Name}).Return(&rcV1alpha1.CNINode{ Spec: rcV1alpha1.CNINodeSpec{ Features: []rcV1alpha1.Feature{{Name: rcV1alpha1.CustomNetworking}}, @@ -367,7 +367,7 @@ func Test_AddNode_CustomNetworking_NodeLabel(t *testing.T) { mock.MockK8sAPI.EXPECT().GetNode(nodeName).Return(nodeWithENIConfig, nil) mock.MockK8sAPI.EXPECT().GetENIConfig(eniConfigName).Return(eniConfig, nil).Times(1) mock.MockWorker.EXPECT().SubmitJob(gomock.All(NewAsyncOperationMatcher(job))) - mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName).Return(nil).Times(1) + mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName, instanceID).Return(nil).Times(1) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: nodeWithENIConfig.Name}).Return(&rcV1alpha1.CNINode{ Spec: rcV1alpha1.CNINodeSpec{ Features: []rcV1alpha1.Feature{{Name: rcV1alpha1.CustomNetworking}}, @@ -400,7 +400,7 @@ func Test_AddNode_CustomNetworking_Incorrect_ENIConfig(t *testing.T) { mock.MockK8sAPI.EXPECT().GetNode(nodeName).Return(nodeWithENIConfig, nil) mock.MockK8sAPI.EXPECT().GetENIConfig(eniConfigName).Return(eniConfig_empty_sg, nil) mock.MockWorker.EXPECT().SubmitJob(gomock.All(NewAsyncOperationMatcher(job))) - mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mockClusterName).Return(nil).Times(1) + mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mockClusterName, instanceID).Return(nil).Times(1) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: nodeWithENIConfig.Name}).Return(&rcV1alpha1.CNINode{ Spec: rcV1alpha1.CNINodeSpec{ Features: []rcV1alpha1.Feature{{Name: rcV1alpha1.CustomNetworking}}, @@ -424,7 +424,7 @@ func Test_AddNode_CustomNetworking_NoENIConfig(t *testing.T) { nodeWithENIConfig.Labels[config.CustomNetworkingLabel] = eniConfigName mock.MockK8sAPI.EXPECT().GetNode(nodeName).Return(nodeWithENIConfig, nil) - mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName).Return(nil).Times(1) + mock.MockK8sAPI.EXPECT().CreateCNINode(nodeWithENIConfig, mock.Manager.clusterName, instanceID).Return(nil).Times(1) mock.MockK8sAPI.EXPECT().GetENIConfig(eniConfigName).Return(nil, mockError) mock.MockK8sAPI.EXPECT().GetCNINode(types.NamespacedName{Name: nodeWithENIConfig.Name}).Return(&rcV1alpha1.CNINode{}, apierrors.NewNotFound(schema.GroupResource{Group: "vpcresources.k8s.aws", Resource: "1"}, "test")) diff --git a/pkg/utils/helper.go b/pkg/utils/helper.go index b460f98e..ed6d8871 100644 --- a/pkg/utils/helper.go +++ b/pkg/utils/helper.go @@ -17,6 +17,7 @@ import ( "context" "errors" "fmt" + "regexp" "strconv" "strings" @@ -40,6 +41,8 @@ const ( AccountIndex = 4 ) +var instanceIDRegex = regexp.MustCompile(`^i-(?:[a-f0-9]{8}|[a-f0-9]{17})$`) + // RemoveDuplicatedSg removes duplicated items from a string slice. // It returns a no duplicates string slice. func RemoveDuplicatedSg(list []string) []string { @@ -261,3 +264,18 @@ func IntToInt32(value int) (int32, error) { return int32(value), nil } + +func GetNodeID(node *corev1.Node) (string, error) { + if node.Spec.ProviderID == "" { + return "", fmt.Errorf("provider ID is not set for node %s", node.Name) + } + idx := strings.LastIndex(node.Spec.ProviderID, "/") + if idx == -1 || idx >= len(node.Spec.ProviderID)-1 { + return "", fmt.Errorf("invalid provider ID format for node %s, with providerId %s", node.Name, node.Spec.ProviderID) + } + instanceID := node.Spec.ProviderID[idx+1:] + if !instanceIDRegex.MatchString(instanceID) { + return "", fmt.Errorf("provider ID for node %s does not match AWS EC2 instance ID format: %s", node.Name, instanceID) + } + return instanceID, nil +} diff --git a/pkg/utils/helper_test.go b/pkg/utils/helper_test.go index 2a603b75..820bc67f 100644 --- a/pkg/utils/helper_test.go +++ b/pkg/utils/helper_test.go @@ -18,6 +18,7 @@ import ( "github.com/samber/lo" "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -679,3 +680,69 @@ func TestPodHasENIRequest(t *testing.T) { }) } } + +func TestGetNodeID(t *testing.T) { + tests := []struct { + name string + providerID string + want string + wantErr bool + }{ + { + name: "valid 8char id", + providerID: "aws:///us-west-2a/i-1234abcd", + want: "i-1234abcd", + wantErr: false, + }, + { + name: "valid 17char id", + providerID: "aws:///us-west-2a/i-0123456789abcdef0", + want: "i-0123456789abcdef0", + wantErr: false, + }, + { + name: "missing provider id", + providerID: "", + want: "", + wantErr: true, + }, + { + name: "invalid format - no slash", + providerID: "i-1234abcd", + want: "", + wantErr: true, + }, + { + name: "invalid id - not hex", + providerID: "aws:///us-west-2a/i-1234abcg", + want: "", + wantErr: true, + }, + { + name: "invalid id - too short", + providerID: "aws:///us-west-2a/i-1234abc", + want: "", + wantErr: true, + }, + { + name: "invalid id - too long", + providerID: "aws:///us-west-2a/i-0123456789abcdef01", + want: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + node := &corev1.Node{} + node.Spec.ProviderID = tt.providerID + got, err := GetNodeID(node) + if (err != nil) != tt.wantErr { + t.Errorf("GetNodeID() error = %v, wantErr %v", err, tt.wantErr) + } + if got != tt.want { + t.Errorf("GetNodeID() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/test/framework/resource/aws/autoscaling/manager.go b/test/framework/resource/aws/autoscaling/manager.go index 7fa9d827..e2e9e858 100644 --- a/test/framework/resource/aws/autoscaling/manager.go +++ b/test/framework/resource/aws/autoscaling/manager.go @@ -25,6 +25,8 @@ import ( type Manager interface { DescribeAutoScalingGroup(autoScalingGroupName string) ([]autoscalingtypes.AutoScalingGroup, error) UpdateAutoScalingGroup(asgName string, desiredSize, minSize, maxSize int32) error + StartInstanceRefresh(asgName string) (string, error) + DescribeInstanceRefresh(asgName string, instanceRefreshId string) (autoscalingtypes.InstanceRefresh, error) } type defaultManager struct { @@ -62,3 +64,19 @@ func (d defaultManager) UpdateAutoScalingGroup(asgName string, desiredSize, minS _, err := d.AutoScalingAPI.UpdateAutoScalingGroup(context.TODO(), updateASGInput) return err } + +func (d defaultManager) StartInstanceRefresh(asgName string) (string, error) { + in := &autoscaling.StartInstanceRefreshInput{ + AutoScalingGroupName: aws.String(asgName), + } + out, err := d.AutoScalingAPI.StartInstanceRefresh(context.TODO(), in) + return *out.InstanceRefreshId, err +} + +func (d defaultManager) DescribeInstanceRefresh(asgName, instanceRefreshId string) (autoscalingtypes.InstanceRefresh, error) { + out, err := d.AutoScalingAPI.DescribeInstanceRefreshes(context.TODO(), &autoscaling.DescribeInstanceRefreshesInput{ + AutoScalingGroupName: aws.String(asgName), + InstanceRefreshIds: []string{instanceRefreshId}, + }) + return out.InstanceRefreshes[0], err +} diff --git a/test/framework/resource/aws/ec2/manager.go b/test/framework/resource/aws/ec2/manager.go index d246f732..8cd9da6e 100644 --- a/test/framework/resource/aws/ec2/manager.go +++ b/test/framework/resource/aws/ec2/manager.go @@ -308,3 +308,25 @@ func (d *Manager) ReCreateSG(securityGroupName string, ctx context.Context) (str } return groupID, nil } + +func (d *Manager) DetachNetworkInterface(ctx context.Context, attachmentID string, force bool) error { + _, err := d.ec2Client.DetachNetworkInterface(ctx, &ec2.DetachNetworkInterfaceInput{ + AttachmentId: aws.String(attachmentID), + Force: aws.Bool(force), + }) + return err +} + +func (d *Manager) GetInstances(ctx context.Context, instanceIds []string) ([]ec2types.Instance, error) { + out, err := d.ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{ + InstanceIds: instanceIds, + }) + if err != nil { + return nil, err + } + var instances []ec2types.Instance + for _, res := range out.Reservations { + instances = append(instances, res.Instances...) + } + return instances, nil +} diff --git a/test/framework/resource/k8s/deployment/manager.go b/test/framework/resource/k8s/deployment/manager.go index 99c5fb72..92aba8c7 100644 --- a/test/framework/resource/k8s/deployment/manager.go +++ b/test/framework/resource/k8s/deployment/manager.go @@ -48,7 +48,7 @@ func (m *defaultManager) CreateAndWaitUntilDeploymentReady(ctx context.Context, } observedDP := &appsv1.Deployment{} - return observedDP, wait.PollUntil(utils.PollIntervalShort, func() (bool, error) { + return observedDP, wait.PollUntilContextTimeout(ctx, utils.PollIntervalMedium, utils.ResourceOperationTimeout, false, func(ctx context.Context) (bool, error) { if err := m.k8sClient.Get(ctx, utils.NamespacedName(dp), observedDP); err != nil { return false, err } @@ -59,7 +59,7 @@ func (m *defaultManager) CreateAndWaitUntilDeploymentReady(ctx context.Context, return true, nil } return false, nil - }, ctx.Done()) + }) } func (m *defaultManager) DeleteAndWaitUntilDeploymentDeleted(ctx context.Context, dp *appsv1.Deployment) error { @@ -67,7 +67,7 @@ func (m *defaultManager) DeleteAndWaitUntilDeploymentDeleted(ctx context.Context if err != nil { return err } - return wait.PollUntil(utils.PollIntervalShort, func() (bool, error) { + return wait.PollUntilContextTimeout(ctx, utils.PollIntervalMedium, utils.ResourceOperationTimeout, false, func(ctx context.Context) (bool, error) { observedDP := &appsv1.Deployment{} if err := m.k8sClient.Get(ctx, utils.NamespacedName(dp), observedDP); err != nil { if errors.IsNotFound(err) { @@ -76,7 +76,7 @@ func (m *defaultManager) DeleteAndWaitUntilDeploymentDeleted(ctx context.Context return false, err } return false, nil - }, ctx.Done()) + }) } func (m *defaultManager) ScaleDeploymentAndWaitTillReady(ctx context.Context, namespace string, name string, replicas int32) error { diff --git a/test/framework/resource/k8s/node/wrapper.go b/test/framework/resource/k8s/node/wrapper.go index a486d63a..231c3433 100644 --- a/test/framework/resource/k8s/node/wrapper.go +++ b/test/framework/resource/k8s/node/wrapper.go @@ -29,7 +29,7 @@ import ( func GetNodeAndWaitTillCapacityPresent(manager Manager, os string, expectedResource string) *v1.NodeList { observedNodeList := &v1.NodeList{} var err error - err = wait.PollUntilContextTimeout(context.Background(), utils.PollIntervalShort, utils.ResourceCreationTimeout, true, + err = wait.PollUntilContextTimeout(context.Background(), utils.PollIntervalShort, utils.ResourceOperationTimeout, true, func(ctx context.Context) (bool, error) { By("checking nodes have capacity present") observedNodeList, err = manager.GetNodesWithOS(os) diff --git a/test/framework/resource/k8s/pod/wrapper.go b/test/framework/resource/k8s/pod/wrapper.go index 4e27c2c5..36ccb3fe 100644 --- a/test/framework/resource/k8s/pod/wrapper.go +++ b/test/framework/resource/k8s/pod/wrapper.go @@ -25,7 +25,7 @@ import ( func CreateAndWaitForPodToStart(podManager Manager, ctx context.Context, pod *v1.Pod) *v1.Pod { By("create the pod") - pod, err := podManager.CreateAndWaitTillPodIsRunning(ctx, pod, utils.ResourceCreationTimeout) + pod, err := podManager.CreateAndWaitTillPodIsRunning(ctx, pod, utils.ResourceOperationTimeout) Expect(err).NotTo(HaveOccurred()) return pod diff --git a/test/framework/utils/poll.go b/test/framework/utils/poll.go index ec62f251..aaa02bd9 100644 --- a/test/framework/utils/poll.go +++ b/test/framework/utils/poll.go @@ -20,9 +20,9 @@ const ( PollIntervalMedium = 10 * time.Second PollIntervalLong = 20 * time.Second PollTimeout = 30 * time.Second - // ResourceCreationTimeout is the number of seconds till the controller waits + // ResourceOperationTimeout is the number of seconds till the controller waits // for the resource creation to complete - ResourceCreationTimeout = 180 * time.Second + ResourceOperationTimeout = 180 * time.Second // Windows Container Images are much larger in size and pulling them the first // time takes much longer, so have higher timeout for Windows Pod to be Ready WindowsPodsCreationTimeout = 240 * time.Second diff --git a/test/integration/cninode/cninode_test.go b/test/integration/cninode/cninode_test.go index 1cbf269e..1d23ded3 100644 --- a/test/integration/cninode/cninode_test.go +++ b/test/integration/cninode/cninode_test.go @@ -41,7 +41,7 @@ var _ = Describe("[CANARY]CNINode test", func() { var asgName string BeforeEach(func() { By("getting autoscaling group name") - asgName = ListNodesAndGetAutoScalingGroupName() + asgName = GetAutoScalingGroupName(config.OSLinux) asg, err := frameWork.AutoScalingManager.DescribeAutoScalingGroup(asgName) Expect(err).ToNot(HaveOccurred()) oldDesiredSize = *asg[0].DesiredCapacity @@ -151,9 +151,9 @@ var _ = Describe("[CANARY]CNINode test", func() { }) -func ListNodesAndGetAutoScalingGroupName() string { +func GetAutoScalingGroupName(os string) string { By("getting instance details") - nodeList, err := frameWork.NodeManager.GetNodesWithOS(config.OSLinux) + nodeList, err := frameWork.NodeManager.GetNodesWithOS(os) Expect(err).ToNot(HaveOccurred()) Expect(nodeList.Items).ToNot(BeEmpty()) instanceID := frameWork.NodeManager.GetInstanceID(&nodeList.Items[0]) @@ -169,7 +169,7 @@ func ListNodesAndGetAutoScalingGroupName() string { // Verifies (linux) node size is updated after ASG is updated func WaitTillNodeSizeUpdated(desiredSize int) error { By("waiting till node list is updated") - err := wait.PollUntilContextTimeout(context.Background(), testUtils.PollIntervalShort, testUtils.ResourceCreationTimeout, true, + err := wait.PollUntilContextTimeout(context.Background(), testUtils.PollIntervalShort, testUtils.ResourceOperationTimeout, true, func(ctx context.Context) (bool, error) { nodes, err := frameWork.NodeManager.GetNodesWithOS(config.OSLinux) // since we are only updating the linux ASG in the test if err != nil { diff --git a/test/integration/ec2api/ec2api_test.go b/test/integration/ec2api/ec2api_test.go index fca49b28..f18c51b4 100644 --- a/test/integration/ec2api/ec2api_test.go +++ b/test/integration/ec2api/ec2api_test.go @@ -62,7 +62,7 @@ var _ = Describe("[LOCAL] Test IAM permissions for EC2 API calls", func() { err = frameWork.EC2Manager.TerminateInstances(instanceID) Expect(err).ToNot(HaveOccurred()) // allow time for instance to be deleted and ENI to be available, new node to be ready - time.Sleep(utils.ResourceCreationTimeout) + time.Sleep(utils.ResourceOperationTimeout) By("verifying ENI is not deleted by controller") err = frameWork.EC2Manager.DescribeNetworkInterface(nwInterfaceID) Expect(err).ToNot(HaveOccurred()) diff --git a/test/integration/metrics/metrics_suite_test.go b/test/integration/metrics/metrics_suite_test.go index ac09f16c..8174efc0 100644 --- a/test/integration/metrics/metrics_suite_test.go +++ b/test/integration/metrics/metrics_suite_test.go @@ -80,7 +80,7 @@ func createCurlPod() (*v1.Pod, error) { if err != nil { return nil, err } - return frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, pod, utils.ResourceCreationTimeout) + return frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, pod, utils.ResourceOperationTimeout) } func ensureControllerReadyTobeScraped() error { diff --git a/test/integration/perpodsg/job_test.go b/test/integration/perpodsg/job_test.go index 2382f954..32d473cb 100644 --- a/test/integration/perpodsg/job_test.go +++ b/test/integration/perpodsg/job_test.go @@ -130,7 +130,7 @@ var _ = Describe("Security Group Per Pod", func() { By("creating the server pod") serverPod, err = frameWork.PodManager. - CreateAndWaitTillPodIsRunning(ctx, serverPod, utils.ResourceCreationTimeout) + CreateAndWaitTillPodIsRunning(ctx, serverPod, utils.ResourceOperationTimeout) Expect(err).ToNot(HaveOccurred()) for i := 0; i < testNodeCount; i++ { diff --git a/test/integration/perpodsg/perpodsg_test.go b/test/integration/perpodsg/perpodsg_test.go index ef8f51e4..e7f767af 100644 --- a/test/integration/perpodsg/perpodsg_test.go +++ b/test/integration/perpodsg/perpodsg_test.go @@ -451,7 +451,7 @@ var _ = Describe("Branch ENI Pods", func() { firstPod := podTemplate.DeepCopy() By("creating a Pod on the un-managed node and verifying it fails") - _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, firstPod, utils.ResourceCreationTimeout) + _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, firstPod, utils.ResourceOperationTimeout) Expect(err).To(HaveOccurred()) By("deleting the pod") @@ -471,7 +471,7 @@ var _ = Describe("Branch ENI Pods", func() { By("creating the Pod on now managed node and verify it runs") secondPod := podTemplate.DeepCopy() - secondPod, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, secondPod, utils.ResourceCreationTimeout) + secondPod, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, secondPod, utils.ResourceOperationTimeout) Expect(err).ToNot(HaveOccurred()) verify.VerifyNetworkingOfPodUsingENI(*secondPod, []string{securityGroupID1}) diff --git a/test/integration/scale/node_termination_scale_test.go b/test/integration/scale/node_termination_scale_test.go new file mode 100644 index 00000000..cd9c0103 --- /dev/null +++ b/test/integration/scale/node_termination_scale_test.go @@ -0,0 +1,139 @@ +package scale_test + +import ( + "fmt" + "time" + + "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config" + utils_vpc_rc "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils" + "github.com/aws/amazon-vpc-resource-controller-k8s/test/framework/manifest" + "github.com/aws/amazon-vpc-resource-controller-k8s/test/framework/utils" + utils_test "github.com/aws/amazon-vpc-resource-controller-k8s/test/utils" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/apps/v1" +) + +var ns = "node-termination-cleaner-scale" +var _ = Describe("Node termination ENI Cleaner Scale test", Ordered, func() { + var ( + asgName string + oldDesiredSize int32 + oldMinSize int32 + oldMaxSize int32 + deployment *v1.Deployment + eniDetached []string + ) + + BeforeAll(func() { + Expect(nodes).To(BeNumerically(">", 0)) + Expect(deletePerMin).To(BeNumerically(">", 0)) + asgName = utils_test.GetAutoScalingGroupName(frameWork, config.OSLinux) + asg, err := frameWork.AutoScalingManager.DescribeAutoScalingGroup(asgName) + Expect(err).NotTo(HaveOccurred()) + oldDesiredSize = *asg[0].DesiredCapacity + oldMinSize = *asg[0].MinSize + oldMaxSize = *asg[0].MaxSize + + By(fmt.Sprintf("scaling the cluster to %d nodes", nodes)) + Expect(frameWork.AutoScalingManager.UpdateAutoScalingGroup(asgName, int32(nodes), 0, int32(nodes))).To(Succeed()) + Eventually(func() int { + list, err := frameWork.NodeManager.GetCNINodeList() + Expect(err).NotTo(HaveOccurred()) + return len(list.Items) + }, utils.ResourceOperationTimeout, utils.PollIntervalMedium).Should(BeNumerically("==", nodes)) + + By("Creating name space") + Expect(frameWork.NSManager.CreateNamespace(ctx, ns)).To(Succeed()) + + By("deploying pods to 70% capacity") + nodeList, err := frameWork.NodeManager.GetNodesWithOS(config.OSLinux) + Expect(err).NotTo(HaveOccurred()) + podCap, ok := nodeList.Items[0].Status.Allocatable.Pods().AsInt64() + Expect(ok).To(BeTrue()) + replicas := int(podCap * int64(len(nodeList.Items)) * 7 / 10) + fmt.Println("replicas", replicas) + deployment = manifest.NewDefaultDeploymentBuilder().Namespace(ns).Replicas(replicas).PodLabel("node-scale", "1").Build() + _, err = frameWork.DeploymentManager.CreateAndWaitUntilDeploymentReady(ctx, deployment) + Expect(err).NotTo(HaveOccurred()) + }) + AfterAll(func() { + + By("deleting deployment") + if deployment != nil { + Expect(frameWork.DeploymentManager.DeleteAndWaitUntilDeploymentDeleted(ctx, deployment)).To(Succeed()) + } + + By("cleaning up all old nodes") + Expect(frameWork.AutoScalingManager.UpdateAutoScalingGroup(asgName, 0, oldMinSize, oldMaxSize)).To(Succeed()) + + By("waiting for 3 minutes to take clean up all nodes", func() { + time.Sleep(3 * time.Minute) + }) + + By(fmt.Sprintf("oldDesiredSize %v, oldMinSize %v oldMaxSize %v", oldDesiredSize, oldMinSize, oldMaxSize)) + Expect(frameWork.AutoScalingManager.UpdateAutoScalingGroup(asgName, oldDesiredSize, oldMinSize, oldMaxSize)).To(Succeed()) + Eventually(func() int { + list, err := frameWork.NodeManager.GetCNINodeList() + Expect(err).NotTo(HaveOccurred()) + return len(list.Items) + }, utils.ResourceOperationTimeout, utils.PollIntervalMedium).Should(BeNumerically("==", oldDesiredSize)) + + By("deleting namespace") + Expect(frameWork.NSManager.DeleteAndWaitTillNamespaceDeleted(ctx, ns)).To(Succeed()) + + }) + + It("detaching Secondary ENI from worker nodes", func() { + nodeList, err := frameWork.NodeManager.GetNodeList() + Expect(err).NotTo(HaveOccurred()) + var instanceIDs []string + fmt.Println("no of nodes in list for detachment ", len(nodeList.Items)) + for _, node := range nodeList.Items { + id, err := utils_vpc_rc.GetNodeID(&node) + Expect(err).NotTo(HaveOccurred()) + instanceIDs = append(instanceIDs, id) + } + instances, err := frameWork.EC2Manager.GetInstances(ctx, instanceIDs) + Expect(err).NotTo(HaveOccurred()) + fmt.Println("no of instance objects we have", len(instances)) + var zero = int32(0) + for _, instance := range instances { + for _, nw := range instance.NetworkInterfaces { + if *nw.Attachment.DeviceIndex > zero { + //fmt.Println("eniID", *nw.NetworkInterfaceId) + eniDetached = append(eniDetached, *nw.NetworkInterfaceId) + Expect(frameWork.EC2Manager.DetachNetworkInterface(ctx, *nw.Attachment.AttachmentId, true)).To(Succeed()) + } + } + } + fmt.Println("total ENI's detached", len(eniDetached)) + }) + + It("waiting for 2 minutes to let eni state sync", func() { + time.Sleep(2 * time.Minute) + }) + + It(fmt.Sprintf("deleting node at rate of %d per minute", deletePerMin), func() { + nl, err := frameWork.NodeManager.GetNodeList() + Expect(err).NotTo(HaveOccurred()) + utils_test.DeleteNodesWithThrottle(frameWork, nl, deletePerMin) + Eventually(func() int { + list, err := frameWork.NodeManager.GetCNINodeList() + Expect(err).NotTo(HaveOccurred()) + return len(list.Items) + }, utils.ResourceOperationTimeout, utils.PollIntervalMedium).Should(BeNumerically("==", 1)) + }) + + It("waiting for 2 minutes for vpc-rc to clean up all ENI", func() { + time.Sleep(2 * time.Minute) + }) + + It("verifying all detached interfaces are deleted", func() { + for _, eniId := range eniDetached { + err := frameWork.EC2Manager.DescribeNetworkInterface(eniId) + Expect(err).To(HaveOccurred(), fmt.Sprintf("ENI with ID %s was not deleted. with error %s", eniId, err)) + } + }) + +}) diff --git a/test/integration/scale/scale_suite_test.go b/test/integration/scale/scale_suite_test.go index 4c0e528c..7e6d0ced 100644 --- a/test/integration/scale/scale_suite_test.go +++ b/test/integration/scale/scale_suite_test.go @@ -15,6 +15,7 @@ package scale_test import ( "context" + "flag" "testing" "github.com/aws/amazon-vpc-resource-controller-k8s/test/framework" @@ -24,12 +25,21 @@ import ( . "github.com/onsi/gomega" ) -var frameWork *framework.Framework -var verify *verifier.PodVerification -var ctx context.Context -var securityGroupID string -var err error -var namespace = "podsg-scale-" + utils.TestNameSpace +var ( + frameWork *framework.Framework + verify *verifier.PodVerification + ctx context.Context + securityGroupID string + err error + namespace = "podsg-scale-" + utils.TestNameSpace + nodes int + deletePerMin int +) + +func init() { + flag.IntVar(&nodes, "nodes", 20, "Number nodes to scale cluster to") + flag.IntVar(&deletePerMin, "deletePerMin", 5, "Number of nodes to delete per min") +} func TestScale(t *testing.T) { RegisterFailHandler(Fail) diff --git a/test/integration/webhook/validating_webhook_suite_test.go b/test/integration/webhook/validating_webhook_suite_test.go index 21fe152b..17af8a15 100644 --- a/test/integration/webhook/validating_webhook_suite_test.go +++ b/test/integration/webhook/validating_webhook_suite_test.go @@ -91,7 +91,7 @@ var _ = BeforeSuite(func() { Build() Expect(err).ToNot(HaveOccurred()) - pod, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, pod, utils.ResourceCreationTimeout) + pod, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, pod, utils.ResourceOperationTimeout) Expect(err).ToNot(HaveOccurred()) By("verifying the pod eni annotation is present on branch pod") diff --git a/test/integration/webhook/validating_webhook_test.go b/test/integration/webhook/validating_webhook_test.go index 6224d7a3..c8f509e2 100644 --- a/test/integration/webhook/validating_webhook_test.go +++ b/test/integration/webhook/validating_webhook_test.go @@ -69,7 +69,7 @@ var _ = Describe("validating webhook test cases", func() { Build() Expect(err).ToNot(HaveOccurred()) - _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, newPod, utils.ResourceCreationTimeout) + _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, newPod, utils.ResourceOperationTimeout) Expect(err).To(HaveOccurred()) }) @@ -81,7 +81,7 @@ var _ = Describe("validating webhook test cases", func() { Build() Expect(err).ToNot(HaveOccurred()) - _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, newPod, utils.ResourceCreationTimeout) + _, err = frameWork.PodManager.CreateAndWaitTillPodIsRunning(ctx, newPod, utils.ResourceOperationTimeout) Expect(err).ToNot(HaveOccurred()) // Allow the cache to sync diff --git a/test/utils/utils.go b/test/utils/utils.go new file mode 100644 index 00000000..e1759c49 --- /dev/null +++ b/test/utils/utils.go @@ -0,0 +1,37 @@ +package utils_test + +import ( + "context" + "time" + + "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils" + "github.com/aws/amazon-vpc-resource-controller-k8s/test/framework" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" +) + +func GetAutoScalingGroupName(frameWork *framework.Framework, os string) string { + By("getting instance details") + nodeList, err := frameWork.NodeManager.GetNodesWithOS(os) + Expect(err).ToNot(HaveOccurred()) + Expect(nodeList.Items).ToNot(BeEmpty()) + instanceID := frameWork.NodeManager.GetInstanceID(&nodeList.Items[0]) + Expect(instanceID).ToNot(BeEmpty()) + instance, err := frameWork.EC2Manager.GetInstanceDetails(instanceID) + Expect(err).ToNot(HaveOccurred()) + tags := utils.GetTagKeyValueMap(instance.Tags) + val, ok := tags["aws:autoscaling:groupName"] + Expect(ok).To(BeTrue()) + return val +} + +func DeleteNodesWithThrottle(frameWork *framework.Framework, nodeList *v1.NodeList, deletePerMin int) { + rate := time.Minute / time.Duration(deletePerMin) + ticker := time.NewTicker(rate) + defer ticker.Stop() + for _, node := range nodeList.Items { + <-ticker.C + Expect(frameWork.K8sClient.Delete(context.TODO(), &node)).To(Succeed()) + } +}