Skip to content

Commit 36fafaf

Browse files
authored
Merge pull request kubernetes#126318 from hoskeri/node-self-register-continue-on-forbidden
node self-register continue if CREATE is 403.
2 parents 8e5d7cb + c7e4e83 commit 36fafaf

File tree

5 files changed

+128
-58
lines changed

5 files changed

+128
-58
lines changed

pkg/features/kube_features.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,13 @@ const (
219219
// Remove in v1.33
220220
AllowInsecureKubeletCertificateSigningRequests featuregate.Feature = "AllowInsecureKubeletCertificateSigningRequests"
221221

222+
// owner: @hoskeri
223+
// Deprecated: v1.32
224+
//
225+
// Restores previous behavior where Kubelet fails self registration if node create returns 403 Forbidden.
226+
// Remove in v1.34
227+
KubeletRegistrationGetOnExistsOnly featuregate.Feature = "KubeletRegistrationGetOnExistsOnly"
228+
222229
// owner: @HirazawaUi
223230
// kep: http://kep.k8s.io/4004
224231
// Deprecated: v1.29 (default off)

pkg/features/versioned_kube_features.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,4 +443,7 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
443443
ImageVolume: {
444444
{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Alpha},
445445
},
446+
KubeletRegistrationGetOnExistsOnly: {
447+
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Deprecated},
448+
},
446449
}

pkg/kubelet/kubelet_node_status.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ import (
3232
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3333
"k8s.io/apimachinery/pkg/types"
3434
"k8s.io/apimachinery/pkg/util/sets"
35+
utilfeature "k8s.io/apiserver/pkg/util/feature"
3536
cloudprovider "k8s.io/cloud-provider"
3637
cloudproviderapi "k8s.io/cloud-provider/api"
3738
nodeutil "k8s.io/component-helpers/node/util"
3839
"k8s.io/klog/v2"
3940
kubeletapis "k8s.io/kubelet/pkg/apis"
4041
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
42+
"k8s.io/kubernetes/pkg/features"
4143
"k8s.io/kubernetes/pkg/kubelet/events"
4244
"k8s.io/kubernetes/pkg/kubelet/nodestatus"
4345
taintutil "k8s.io/kubernetes/pkg/util/taints"
@@ -91,7 +93,16 @@ func (kl *Kubelet) tryRegisterWithAPIServer(node *v1.Node) bool {
9193
return true
9294
}
9395

94-
if !apierrors.IsAlreadyExists(err) {
96+
switch {
97+
case apierrors.IsAlreadyExists(err):
98+
// Node already exists, proceed to reconcile node.
99+
case apierrors.IsForbidden(err):
100+
// Creating nodes is forbidden, but node may still exist, attempt to get the node.
101+
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletRegistrationGetOnExistsOnly) {
102+
klog.ErrorS(err, "Unable to register node with API server, reason is forbidden", "node", klog.KObj(node))
103+
return false
104+
}
105+
default:
95106
klog.ErrorS(err, "Unable to register node with API server", "node", klog.KObj(node))
96107
return false
97108
}
@@ -101,6 +112,7 @@ func (kl *Kubelet) tryRegisterWithAPIServer(node *v1.Node) bool {
101112
klog.ErrorS(err, "Unable to register node with API server, error getting existing node", "node", klog.KObj(node))
102113
return false
103114
}
115+
104116
if existingNode == nil {
105117
klog.InfoS("Unable to register node with API server, no node instance returned", "node", klog.KObj(node))
106118
return false

pkg/kubelet/kubelet_node_status_test.go

Lines changed: 99 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,17 @@ import (
4545
"k8s.io/apimachinery/pkg/util/rand"
4646
"k8s.io/apimachinery/pkg/util/strategicpatch"
4747
"k8s.io/apimachinery/pkg/util/uuid"
48+
utilversion "k8s.io/apimachinery/pkg/util/version"
4849
"k8s.io/apimachinery/pkg/util/wait"
50+
utilfeature "k8s.io/apiserver/pkg/util/feature"
4951
clientset "k8s.io/client-go/kubernetes"
5052
"k8s.io/client-go/kubernetes/fake"
5153
"k8s.io/client-go/rest"
5254
core "k8s.io/client-go/testing"
55+
featuregatetesting "k8s.io/component-base/featuregate/testing"
5356
"k8s.io/component-base/version"
5457
kubeletapis "k8s.io/kubelet/pkg/apis"
58+
"k8s.io/kubernetes/pkg/features"
5559
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
5660
"k8s.io/kubernetes/pkg/kubelet/cm"
5761
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
@@ -1386,6 +1390,10 @@ func TestTryRegisterWithApiServer(t *testing.T) {
13861390
ErrStatus: metav1.Status{Reason: metav1.StatusReasonConflict},
13871391
}
13881392

1393+
forbidden := &apierrors.StatusError{
1394+
ErrStatus: metav1.Status{Reason: metav1.StatusReasonForbidden},
1395+
}
1396+
13891397
newNode := func(cmad bool) *v1.Node {
13901398
node := &v1.Node{
13911399
ObjectMeta: metav1.ObjectMeta{
@@ -1408,18 +1416,19 @@ func TestTryRegisterWithApiServer(t *testing.T) {
14081416
}
14091417

14101418
cases := []struct {
1411-
name string
1412-
newNode *v1.Node
1413-
existingNode *v1.Node
1414-
createError error
1415-
getError error
1416-
patchError error
1417-
deleteError error
1418-
expectedResult bool
1419-
expectedActions int
1420-
testSavedNode bool
1421-
savedNodeIndex int
1422-
savedNodeCMAD bool
1419+
name string
1420+
newNode *v1.Node
1421+
existingNode *v1.Node
1422+
createError error
1423+
getError error
1424+
patchError error
1425+
deleteError error
1426+
expectedResult bool
1427+
expectedActions int
1428+
testSavedNode bool
1429+
getOnForbiddenDisabled bool
1430+
savedNodeIndex int
1431+
savedNodeCMAD bool
14231432
}{
14241433
{
14251434
name: "success case - new node",
@@ -1435,6 +1444,25 @@ func TestTryRegisterWithApiServer(t *testing.T) {
14351444
expectedResult: true,
14361445
expectedActions: 2,
14371446
},
1447+
{
1448+
name: "success case - existing node - create forbidden - no change in CMAD",
1449+
newNode: newNode(true),
1450+
createError: forbidden,
1451+
existingNode: newNode(true),
1452+
expectedResult: true,
1453+
expectedActions: 2,
1454+
},
1455+
{
1456+
name: "success case - existing node - create forbidden - CMAD disabled",
1457+
newNode: newNode(false),
1458+
createError: forbidden,
1459+
existingNode: newNode(true),
1460+
expectedResult: true,
1461+
expectedActions: 3,
1462+
testSavedNode: true,
1463+
savedNodeIndex: 2,
1464+
savedNodeCMAD: false,
1465+
},
14381466
{
14391467
name: "success case - existing node - CMAD disabled",
14401468
newNode: newNode(false),
@@ -1464,6 +1492,14 @@ func TestTryRegisterWithApiServer(t *testing.T) {
14641492
expectedResult: false,
14651493
expectedActions: 1,
14661494
},
1495+
{
1496+
name: "create failed with forbidden - get-on-forbidden feature is disabled",
1497+
newNode: newNode(false),
1498+
getOnForbiddenDisabled: true,
1499+
createError: forbidden,
1500+
expectedResult: false,
1501+
expectedActions: 1,
1502+
},
14671503
{
14681504
name: "get existing node failed",
14691505
newNode: newNode(false),
@@ -1484,55 +1520,61 @@ func TestTryRegisterWithApiServer(t *testing.T) {
14841520
}
14851521

14861522
for _, tc := range cases {
1487-
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled is a don't-care for this test */)
1488-
defer testKubelet.Cleanup()
1489-
kubelet := testKubelet.kubelet
1490-
kubeClient := testKubelet.fakeKubeClient
1491-
1492-
kubeClient.AddReactor("create", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1493-
return true, nil, tc.createError
1494-
})
1495-
kubeClient.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1496-
// Return an existing (matching) node on get.
1497-
return true, tc.existingNode, tc.getError
1498-
})
1499-
kubeClient.AddReactor("patch", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1500-
if action.GetSubresource() == "status" {
1501-
return true, nil, tc.patchError
1523+
t.Run(tc.name, func(t *testing.T) {
1524+
if tc.getOnForbiddenDisabled {
1525+
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, utilversion.MustParse("1.32"))
1526+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletRegistrationGetOnExistsOnly, true)
15021527
}
1503-
return notImplemented(action)
1504-
})
1505-
kubeClient.AddReactor("delete", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1506-
return true, nil, tc.deleteError
1507-
})
1508-
addNotImplatedReaction(kubeClient)
1528+
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled is a don't-care for this test */)
1529+
defer testKubelet.Cleanup()
1530+
kubelet := testKubelet.kubelet
1531+
kubeClient := testKubelet.fakeKubeClient
15091532

1510-
result := kubelet.tryRegisterWithAPIServer(tc.newNode)
1511-
require.Equal(t, tc.expectedResult, result, "test [%s]", tc.name)
1533+
kubeClient.AddReactor("create", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1534+
return true, nil, tc.createError
1535+
})
1536+
kubeClient.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1537+
// Return an existing (matching) node on get.
1538+
return true, tc.existingNode, tc.getError
1539+
})
1540+
kubeClient.AddReactor("patch", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1541+
if action.GetSubresource() == "status" {
1542+
return true, nil, tc.patchError
1543+
}
1544+
return notImplemented(action)
1545+
})
1546+
kubeClient.AddReactor("delete", "nodes", func(action core.Action) (bool, runtime.Object, error) {
1547+
return true, nil, tc.deleteError
1548+
})
1549+
addNotImplatedReaction(kubeClient)
15121550

1513-
actions := kubeClient.Actions()
1514-
assert.Len(t, actions, tc.expectedActions, "test [%s]", tc.name)
1515-
1516-
if tc.testSavedNode {
1517-
var savedNode *v1.Node
1518-
1519-
t.Logf("actions: %v: %+v", len(actions), actions)
1520-
action := actions[tc.savedNodeIndex]
1521-
if action.GetVerb() == "create" {
1522-
createAction := action.(core.CreateAction)
1523-
obj := createAction.GetObject()
1524-
require.IsType(t, &v1.Node{}, obj)
1525-
savedNode = obj.(*v1.Node)
1526-
} else if action.GetVerb() == "patch" {
1527-
patchAction := action.(core.PatchActionImpl)
1528-
var err error
1529-
savedNode, err = applyNodeStatusPatch(tc.existingNode, patchAction.GetPatch())
1530-
require.NoError(t, err)
1531-
}
1551+
result := kubelet.tryRegisterWithAPIServer(tc.newNode)
1552+
require.Equal(t, tc.expectedResult, result, "test [%s]", tc.name)
15321553

1533-
actualCMAD, _ := strconv.ParseBool(savedNode.Annotations[util.ControllerManagedAttachAnnotation])
1534-
assert.Equal(t, tc.savedNodeCMAD, actualCMAD, "test [%s]", tc.name)
1535-
}
1554+
actions := kubeClient.Actions()
1555+
assert.Len(t, actions, tc.expectedActions, "test [%s]", tc.name)
1556+
1557+
if tc.testSavedNode {
1558+
var savedNode *v1.Node
1559+
1560+
t.Logf("actions: %v: %+v", len(actions), actions)
1561+
action := actions[tc.savedNodeIndex]
1562+
if action.GetVerb() == "create" {
1563+
createAction := action.(core.CreateAction)
1564+
obj := createAction.GetObject()
1565+
require.IsType(t, &v1.Node{}, obj)
1566+
savedNode = obj.(*v1.Node)
1567+
} else if action.GetVerb() == "patch" {
1568+
patchAction := action.(core.PatchActionImpl)
1569+
var err error
1570+
savedNode, err = applyNodeStatusPatch(tc.existingNode, patchAction.GetPatch())
1571+
require.NoError(t, err)
1572+
}
1573+
1574+
actualCMAD, _ := strconv.ParseBool(savedNode.Annotations[util.ControllerManagedAttachAnnotation])
1575+
assert.Equal(t, tc.savedNodeCMAD, actualCMAD, "test [%s]", tc.name)
1576+
}
1577+
})
15361578
}
15371579
}
15381580

test/featuregates_linter/test_data/versioned_feature_list.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@
362362
lockToDefault: false
363363
preRelease: Alpha
364364
version: "1.27"
365+
- name: KubeletRegistrationGetOnExistsOnly
366+
versionedSpecs:
367+
- default: false
368+
lockToDefault: false
369+
preRelease: Deprecated
370+
version: "1.32"
365371
- name: KubeletSeparateDiskGC
366372
versionedSpecs:
367373
- default: false

0 commit comments

Comments
 (0)