Skip to content

Commit cd028ec

Browse files
sushrkhaouc
authored andcommitted
Add support to configure branch ENI cooldown period via configmap (#342)
* Add support to configure branch ENI cooldown period via configmap * support configurable branch ENI cooldown period * moving error check out from CM update * Fix logs and remove mutex lock in Get function * Update to go1.21.5 --------- Co-authored-by: Hao Zhou <[email protected]>
1 parent 468eb44 commit cd028ec

File tree

14 files changed

+381
-12
lines changed

14 files changed

+381
-12
lines changed

.github/workflows/presubmit.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
uses: actions/checkout@v3
4646
- uses: actions/setup-go@v4
4747
with:
48-
go-version: '1.21.4'
48+
go-version: '1.21.5'
4949
cache-dependency-path: "**/go.sum"
5050
- name: Install `govulncheck`
5151
run: go install golang.org/x/vuln/cmd/govulncheck@latest

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ MAKEFILE_PATH = $(dir $(realpath -s $(firstword $(MAKEFILE_LIST))))
1212
VERSION ?= $(GIT_VERSION)
1313
IMAGE ?= $(REPO):$(VERSION)
1414
BASE_IMAGE ?= public.ecr.aws/eks-distro-build-tooling/eks-distro-minimal-base-nonroot:latest.2
15-
BUILD_IMAGE ?= public.ecr.aws/bitnami/golang:1.21.4
15+
BUILD_IMAGE ?= public.ecr.aws/bitnami/golang:1.21.5
1616
GOARCH ?= amd64
1717
PLATFORM ?= linux/amd64
1818

controllers/core/configmap_controller.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ import (
2222
rcHealthz "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/healthz"
2323
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s"
2424
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/node/manager"
25+
cooldown "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider/branch/cooldown"
26+
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils"
2527

2628
"github.com/go-logr/logr"
2729
corev1 "k8s.io/api/core/v1"
30+
v1 "k8s.io/api/core/v1"
2831
"k8s.io/apimachinery/pkg/api/errors"
2932
"k8s.io/apimachinery/pkg/runtime"
3033
ctrl "sigs.k8s.io/controller-runtime"
@@ -73,6 +76,24 @@ func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
7376
}
7477
}
7578

79+
// Check if branch ENI cooldown period is updated
80+
curCoolDownPeriod := cooldown.GetCoolDown().GetCoolDownPeriod()
81+
if newCoolDownPeriod, err := cooldown.GetVpcCniConfigMapCoolDownPeriodOrDefault(r.K8sAPI, r.Log); err == nil {
82+
if curCoolDownPeriod != newCoolDownPeriod {
83+
r.Log.Info("Branch ENI cool down period has been updated", "newCoolDownPeriod", newCoolDownPeriod, "OldCoolDownPeriod", curCoolDownPeriod)
84+
cooldown.GetCoolDown().SetCoolDownPeriod(newCoolDownPeriod)
85+
utils.SendBroadcastNodeEvent(
86+
r.K8sAPI,
87+
utils.BranchENICoolDownUpdateReason,
88+
fmt.Sprintf("Branch ENI cool down period has been updated to %s", cooldown.GetCoolDown().GetCoolDownPeriod()),
89+
v1.EventTypeNormal,
90+
r.Log,
91+
)
92+
}
93+
} else {
94+
r.Log.Error(err, "failed to retrieve branch ENI cool down period from amazon-vpc-cni configmap, will retain the current cooldown period", "cool down period", curCoolDownPeriod)
95+
}
96+
7697
// Check if the Windows IPAM flag has changed
7798
newWinIPAMEnabledCond := r.Condition.IsWindowsIPAMEnabled()
7899

controllers/core/configmap_controller_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import (
2323
mock_node "github.com/aws/amazon-vpc-resource-controller-k8s/mocks/amazon-vcp-resource-controller-k8s/pkg/node"
2424
mock_manager "github.com/aws/amazon-vpc-resource-controller-k8s/mocks/amazon-vcp-resource-controller-k8s/pkg/node/manager"
2525
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config"
26+
cooldown "github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider/branch/cooldown"
2627
"github.com/golang/mock/gomock"
2728
"github.com/stretchr/testify/assert"
2829
corev1 "k8s.io/api/core/v1"
30+
v1 "k8s.io/api/core/v1"
2931
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3032
"k8s.io/apimachinery/pkg/runtime"
3133
"k8s.io/apimachinery/pkg/types"
@@ -112,6 +114,9 @@ func Test_Reconcile_ConfigMap_Updated(t *testing.T) {
112114
mock.MockNodeManager.EXPECT().GetNode(mockNodeName).Return(mock.MockNode, true)
113115
mock.MockNodeManager.EXPECT().UpdateNode(mockNodeName).Return(nil)
114116

117+
mock.MockK8sAPI.EXPECT().GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace).Return(createCoolDownMockCM("30"), nil).AnyTimes()
118+
119+
cooldown.InitCoolDownPeriod(mock.MockK8sAPI, zap.New(zap.UseDevMode(true)).WithName("cooldown"))
115120
res, err := mock.ConfigMapReconciler.Reconcile(context.TODO(), mockConfigMapReq)
116121
assert.NoError(t, err)
117122
assert.Equal(t, res, reconcile.Result{})
@@ -125,6 +130,9 @@ func Test_Reconcile_ConfigMap_PD_Disabled_If_IPAM_Disabled(t *testing.T) {
125130
mock := NewConfigMapMock(ctrl, mockConfigMapPD)
126131
mock.MockCondition.EXPECT().IsWindowsIPAMEnabled().Return(false)
127132
mock.MockCondition.EXPECT().IsWindowsPrefixDelegationEnabled().Return(false)
133+
mock.MockK8sAPI.EXPECT().GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace).Return(createCoolDownMockCM("30"), nil).AnyTimes()
134+
135+
cooldown.InitCoolDownPeriod(mock.MockK8sAPI, zap.New(zap.UseDevMode(true)).WithName("cooldown"))
128136

129137
res, err := mock.ConfigMapReconciler.Reconcile(context.TODO(), mockConfigMapReq)
130138
assert.NoError(t, err)
@@ -142,6 +150,9 @@ func Test_Reconcile_ConfigMap_NoData(t *testing.T) {
142150

143151
mock.MockCondition.EXPECT().IsWindowsIPAMEnabled().Return(false)
144152
mock.MockCondition.EXPECT().IsWindowsPrefixDelegationEnabled().Return(false)
153+
mock.MockK8sAPI.EXPECT().GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace).Return(createCoolDownMockCM("30"), nil).AnyTimes()
154+
155+
cooldown.InitCoolDownPeriod(mock.MockK8sAPI, zap.New(zap.UseDevMode(true)).WithName("cooldown"))
145156
res, err := mock.ConfigMapReconciler.Reconcile(context.TODO(), mockConfigMapReq)
146157
assert.NoError(t, err)
147158
assert.Equal(t, res, reconcile.Result{})
@@ -154,7 +165,9 @@ func Test_Reconcile_ConfigMap_Deleted(t *testing.T) {
154165
mock := NewConfigMapMock(ctrl)
155166
mock.MockCondition.EXPECT().IsWindowsIPAMEnabled().Return(false)
156167
mock.MockCondition.EXPECT().IsWindowsPrefixDelegationEnabled().Return(false)
168+
mock.MockK8sAPI.EXPECT().GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace).Return(createCoolDownMockCM("30"), nil).AnyTimes()
157169

170+
cooldown.InitCoolDownPeriod(mock.MockK8sAPI, zap.New(zap.UseDevMode(true)).WithName("cooldown"))
158171
res, err := mock.ConfigMapReconciler.Reconcile(context.TODO(), mockConfigMapReq)
159172
assert.NoError(t, err)
160173
assert.Equal(t, res, reconcile.Result{})
@@ -170,9 +183,23 @@ func Test_Reconcile_UpdateNode_Error(t *testing.T) {
170183
mock.MockK8sAPI.EXPECT().ListNodes().Return(nodeList, nil)
171184
mock.MockNodeManager.EXPECT().GetNode(mockNodeName).Return(mock.MockNode, true)
172185
mock.MockNodeManager.EXPECT().UpdateNode(mockNodeName).Return(errMock)
186+
mock.MockK8sAPI.EXPECT().GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace).Return(createCoolDownMockCM("30"), nil).AnyTimes()
173187

188+
cooldown.InitCoolDownPeriod(mock.MockK8sAPI, zap.New(zap.UseDevMode(true)).WithName("cooldown"))
174189
res, err := mock.ConfigMapReconciler.Reconcile(context.TODO(), mockConfigMapReq)
175190
assert.Error(t, err)
176191
assert.Equal(t, res, reconcile.Result{})
177192

178193
}
194+
195+
func createCoolDownMockCM(cooldownTime string) *v1.ConfigMap {
196+
return &v1.ConfigMap{
197+
ObjectMeta: metav1.ObjectMeta{
198+
Name: config.VpcCniConfigMapName,
199+
Namespace: config.KubeSystemNamespace,
200+
},
201+
Data: map[string]string{
202+
config.BranchENICooldownPeriodKey: cooldownTime,
203+
},
204+
}
205+
}

main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s"
3535
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s/pod"
3636
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/node/manager"
37+
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/provider/branch/cooldown"
3738
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/resource"
3839
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/utils"
3940
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/version"
@@ -290,6 +291,9 @@ func main() {
290291
controllerConditions := condition.NewControllerConditions(
291292
ctrl.Log.WithName("controller conditions"), k8sApi, enableWindowsPrefixDelegation)
292293

294+
// initialize the branch ENI cool down period
295+
cooldown.InitCoolDownPeriod(k8sApi, ctrl.Log)
296+
293297
// when Windows PD feature flag is OFF, do not initialize resource for prefix IPs
294298
var supportedResources []string
295299
if enableWindowsPrefixDelegation {

mocks/amazon-vcp-resource-controller-k8s/pkg/provider/branch/cooldown/mock_cooldown.go

Lines changed: 74 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/config/type.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const (
8080
KubeSystemNamespace = "kube-system"
8181
VpcCNIDaemonSetName = "aws-node"
8282
OldVPCControllerDeploymentName = "vpc-resource-controller"
83+
BranchENICooldownPeriodKey = "branch-eni-cooldown"
8384
)
8485

8586
type ResourceType string
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package cooldown
15+
16+
import (
17+
"fmt"
18+
"strconv"
19+
"sync"
20+
"time"
21+
22+
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/config"
23+
"github.com/aws/amazon-vpc-resource-controller-k8s/pkg/k8s"
24+
"github.com/go-logr/logr"
25+
)
26+
27+
// Global variable for CoolDownPeriod allows packages to Get and Set the coolDown period
28+
var coolDown *cooldown
29+
30+
type cooldown struct {
31+
mu sync.RWMutex
32+
// CoolDownPeriod is the period to wait before deleting the branch ENI for propagation of ip tables rule for deleted pod
33+
coolDownPeriod time.Duration
34+
}
35+
36+
type CoolDown interface {
37+
GetCoolDownPeriod() time.Duration
38+
SetCoolDownPeriod(time.Duration)
39+
}
40+
41+
const (
42+
DefaultCoolDownPeriod = time.Second * 60
43+
MinimalCoolDownPeriod = time.Second * 30
44+
)
45+
46+
// Initialize coolDown period by setting the value in configmap or to default
47+
func InitCoolDownPeriod(k8sApi k8s.K8sWrapper, log logr.Logger) {
48+
coolDown = &cooldown{}
49+
coolDownPeriod, err := GetVpcCniConfigMapCoolDownPeriodOrDefault(k8sApi, log)
50+
if err != nil {
51+
log.Info("setting coolDown period to default", "cool down period", coolDownPeriod)
52+
}
53+
coolDown.SetCoolDownPeriod(coolDownPeriod)
54+
}
55+
56+
func GetCoolDown() CoolDown {
57+
return coolDown
58+
}
59+
60+
func GetVpcCniConfigMapCoolDownPeriodOrDefault(k8sApi k8s.K8sWrapper, log logr.Logger) (time.Duration, error) {
61+
vpcCniConfigMap, err := k8sApi.GetConfigMap(config.VpcCniConfigMapName, config.KubeSystemNamespace)
62+
if err == nil && vpcCniConfigMap.Data != nil {
63+
if val, ok := vpcCniConfigMap.Data[config.BranchENICooldownPeriodKey]; ok {
64+
coolDownPeriodInt, err := strconv.Atoi(val)
65+
if err != nil {
66+
log.Error(err, "failed to parse branch ENI coolDown period", "cool down period", val)
67+
} else {
68+
return time.Second * time.Duration(coolDownPeriodInt), nil
69+
}
70+
}
71+
}
72+
// If configmap not found, or configmap data not found, or error in parsing coolDown period, return default coolDown period and error
73+
return DefaultCoolDownPeriod, fmt.Errorf("failed to get cool down period:%v", err)
74+
}
75+
76+
func (c *cooldown) GetCoolDownPeriod() time.Duration {
77+
if c.coolDownPeriod < 30*time.Second {
78+
return MinimalCoolDownPeriod
79+
}
80+
return c.coolDownPeriod
81+
}
82+
83+
func (c *cooldown) SetCoolDownPeriod(newCoolDownPeriod time.Duration) {
84+
c.mu.Lock()
85+
defer c.mu.Unlock()
86+
c.coolDownPeriod = newCoolDownPeriod
87+
}

0 commit comments

Comments
 (0)