mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
KEP-3619: kubelet now rejects Pods with SupplementalGroupsPolicy=Strict on Nodes not supported this feature.
This commit is contained in:
parent
45846f6b4e
commit
2a0e51825b
@ -245,6 +245,7 @@ var (
|
|||||||
lifecycle.PodOSNotSupported,
|
lifecycle.PodOSNotSupported,
|
||||||
lifecycle.InvalidNodeInfo,
|
lifecycle.InvalidNodeInfo,
|
||||||
lifecycle.InitContainerRestartPolicyForbidden,
|
lifecycle.InitContainerRestartPolicyForbidden,
|
||||||
|
lifecycle.SupplementalGroupsPolicyNotSupported,
|
||||||
lifecycle.UnexpectedAdmissionError,
|
lifecycle.UnexpectedAdmissionError,
|
||||||
lifecycle.UnknownReason,
|
lifecycle.UnknownReason,
|
||||||
lifecycle.UnexpectedPredicateFailureType,
|
lifecycle.UnexpectedPredicateFailureType,
|
||||||
|
@ -3657,6 +3657,15 @@ func TestRecordAdmissionRejection(t *testing.T) {
|
|||||||
# HELP kubelet_admission_rejections_total [ALPHA] Cumulative number pod admission rejections by the Kubelet.
|
# HELP kubelet_admission_rejections_total [ALPHA] Cumulative number pod admission rejections by the Kubelet.
|
||||||
# TYPE kubelet_admission_rejections_total counter
|
# TYPE kubelet_admission_rejections_total counter
|
||||||
kubelet_admission_rejections_total{reason="InitContainerRestartPolicyForbidden"} 1
|
kubelet_admission_rejections_total{reason="InitContainerRestartPolicyForbidden"} 1
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SupplementalGroupsPolicyNotSupported",
|
||||||
|
reason: lifecycle.SupplementalGroupsPolicyNotSupported,
|
||||||
|
wants: `
|
||||||
|
# HELP kubelet_admission_rejections_total [ALPHA] Cumulative number pod admission rejections by the Kubelet.
|
||||||
|
# TYPE kubelet_admission_rejections_total counter
|
||||||
|
kubelet_admission_rejections_total{reason="SupplementalGroupsPolicyNotSupported"} 1
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -21,13 +21,17 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/component-base/featuregate"
|
||||||
"k8s.io/component-helpers/scheduling/corev1"
|
"k8s.io/component-helpers/scheduling/corev1"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
"k8s.io/kubernetes/pkg/scheduler"
|
"k8s.io/kubernetes/pkg/scheduler"
|
||||||
schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
|
schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/tainttoleration"
|
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/tainttoleration"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -49,6 +53,11 @@ const (
|
|||||||
// than Always for some of its init containers.
|
// than Always for some of its init containers.
|
||||||
InitContainerRestartPolicyForbidden = "InitContainerRestartPolicyForbidden"
|
InitContainerRestartPolicyForbidden = "InitContainerRestartPolicyForbidden"
|
||||||
|
|
||||||
|
// SupplementalGroupsPolicyNotSupported is used to denote that the pod was
|
||||||
|
// rejected admission to the node because the node does not support
|
||||||
|
// the pod's SupplementalGroupsPolicy.
|
||||||
|
SupplementalGroupsPolicyNotSupported = "SupplementalGroupsPolicyNotSupported"
|
||||||
|
|
||||||
// UnexpectedAdmissionError is used to denote that the pod was rejected
|
// UnexpectedAdmissionError is used to denote that the pod was rejected
|
||||||
// admission to the node because of an error during admission that could not
|
// admission to the node because of an error during admission that could not
|
||||||
// be categorized.
|
// be categorized.
|
||||||
@ -132,6 +141,16 @@ func (w *predicateAdmitHandler) Admit(attrs *PodAdmitAttributes) PodAdmitResult
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rejectPodAdmissionBasedOnSupplementalGroupsPolicy(admitPod, node) {
|
||||||
|
message := fmt.Sprintf("SupplementalGroupsPolicy=%s is not supported in this node", v1.SupplementalGroupsPolicyStrict)
|
||||||
|
klog.InfoS("Failed to admit pod", "pod", klog.KObj(admitPod), "message", message)
|
||||||
|
return PodAdmitResult{
|
||||||
|
Admit: false,
|
||||||
|
Reason: SupplementalGroupsPolicyNotSupported,
|
||||||
|
Message: message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pods := attrs.OtherPods
|
pods := attrs.OtherPods
|
||||||
nodeInfo := schedulerframework.NewNodeInfo(pods...)
|
nodeInfo := schedulerframework.NewNodeInfo(pods...)
|
||||||
nodeInfo.SetNode(node)
|
nodeInfo.SetNode(node)
|
||||||
@ -254,6 +273,45 @@ func rejectPodAdmissionBasedOnOSField(pod *v1.Pod) bool {
|
|||||||
return string(pod.Spec.OS.Name) != runtime.GOOS
|
return string(pod.Spec.OS.Name) != runtime.GOOS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rejectPodAdmissionBasedOnSupplementalGroupsPolicy rejects pod only if
|
||||||
|
// - the feature is beta or above, and SupplementalPolicy=Strict is set in the pod
|
||||||
|
// - but, the node does not support the feature
|
||||||
|
//
|
||||||
|
// Note: During the feature is alpha or before(not yet released) in emulated version,
|
||||||
|
// it should admit for backward compatibility
|
||||||
|
func rejectPodAdmissionBasedOnSupplementalGroupsPolicy(pod *v1.Pod, node *v1.Node) bool {
|
||||||
|
admit, reject := false, true // just for readability
|
||||||
|
|
||||||
|
inUse := (pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SupplementalGroupsPolicy != nil)
|
||||||
|
if !inUse {
|
||||||
|
return admit
|
||||||
|
}
|
||||||
|
|
||||||
|
isBetaOrAbove := false
|
||||||
|
if featureSpec, ok := utilfeature.DefaultMutableFeatureGate.GetAll()[features.SupplementalGroupsPolicy]; ok {
|
||||||
|
isBetaOrAbove = (featureSpec.PreRelease == featuregate.Beta) || (featureSpec.PreRelease == featuregate.GA)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isBetaOrAbove {
|
||||||
|
return admit
|
||||||
|
}
|
||||||
|
|
||||||
|
featureSupportedOnNode := ptr.Deref(
|
||||||
|
ptr.Deref(node.Status.Features, v1.NodeFeatures{SupplementalGroupsPolicy: ptr.To(false)}).SupplementalGroupsPolicy,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
effectivePolicy := ptr.Deref(
|
||||||
|
pod.Spec.SecurityContext.SupplementalGroupsPolicy,
|
||||||
|
v1.SupplementalGroupsPolicyMerge,
|
||||||
|
)
|
||||||
|
|
||||||
|
if effectivePolicy == v1.SupplementalGroupsPolicyStrict && !featureSupportedOnNode {
|
||||||
|
return reject
|
||||||
|
}
|
||||||
|
|
||||||
|
return admit
|
||||||
|
}
|
||||||
|
|
||||||
func removeMissingExtendedResources(pod *v1.Pod, nodeInfo *schedulerframework.NodeInfo) *v1.Pod {
|
func removeMissingExtendedResources(pod *v1.Pod, nodeInfo *schedulerframework.NodeInfo) *v1.Pod {
|
||||||
filterExtendedResources := func(containers []v1.Container) {
|
filterExtendedResources := func(containers []v1.Container) {
|
||||||
for i, c := range containers {
|
for i, c := range containers {
|
||||||
|
@ -24,12 +24,16 @@ import (
|
|||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
utilversion "k8s.io/apimachinery/pkg/util/version"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
|
schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
|
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports"
|
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports"
|
||||||
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/tainttoleration"
|
"k8s.io/kubernetes/pkg/scheduler/framework/plugins/tainttoleration"
|
||||||
|
"k8s.io/utils/ptr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -431,3 +435,115 @@ func TestRejectPodAdmissionBasedOnOSField(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPodAdmissionBasedOnSupplementalGroupsPolicy(t *testing.T) {
|
||||||
|
nodeSupportedTheFeature := &v1.Node{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||||
|
Status: v1.NodeStatus{
|
||||||
|
Features: &v1.NodeFeatures{
|
||||||
|
SupplementalGroupsPolicy: ptr.To(true),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodeNotSupportedTheFeature := &v1.Node{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||||
|
}
|
||||||
|
podNotUsedTheFeature := &v1.Pod{}
|
||||||
|
podUsedTheFeature := &v1.Pod{Spec: v1.PodSpec{
|
||||||
|
SecurityContext: &v1.PodSecurityContext{
|
||||||
|
SupplementalGroupsPolicy: ptr.To(v1.SupplementalGroupsPolicyStrict),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
emulationVersion *utilversion.Version
|
||||||
|
node *v1.Node
|
||||||
|
pod *v1.Pod
|
||||||
|
expectRejection bool
|
||||||
|
}{
|
||||||
|
// The feature is Beta in v1.33
|
||||||
|
{
|
||||||
|
name: "feature=Beta, node=feature not supported, pod=in use: it should REJECT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.33"),
|
||||||
|
node: nodeNotSupportedTheFeature,
|
||||||
|
pod: podUsedTheFeature,
|
||||||
|
expectRejection: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "feature=Beta, node=feature supported, pod=in use: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.33"),
|
||||||
|
node: nodeSupportedTheFeature,
|
||||||
|
pod: podUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "feature=Beta, node=feature not supported, pod=not in use: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.33"),
|
||||||
|
node: nodeNotSupportedTheFeature,
|
||||||
|
pod: podNotUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "feature=Beta, node=feature supported, pod=not in use: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.33"),
|
||||||
|
node: nodeSupportedTheFeature,
|
||||||
|
pod: podNotUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
// The feature is Alpha(v1.31, v1.32) in emulated version
|
||||||
|
// Note: When the feature is alpha in emulated version, it should always admit for backward compatibility
|
||||||
|
{
|
||||||
|
name: "feature=Alpha, node=feature not supported, pod=feature used: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.32"),
|
||||||
|
node: nodeNotSupportedTheFeature,
|
||||||
|
pod: podUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "feature=Alpha, node=feature not supported, pod=feature not used: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.32"),
|
||||||
|
node: nodeNotSupportedTheFeature,
|
||||||
|
pod: podNotUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "feature=Alpha, node=feature supported, pod=feature used: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.32"),
|
||||||
|
node: nodeSupportedTheFeature,
|
||||||
|
pod: podUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "feature=Alpha, node=feature supported, pod=feature not used: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.32"),
|
||||||
|
node: nodeSupportedTheFeature,
|
||||||
|
pod: podNotUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
// The feature is not yet released (< v1.31) in emulated version (this can happen when only kubelet downgraded).
|
||||||
|
// Note: When the feature is not yet released in emulated version, it should always admit for backward compatibility
|
||||||
|
{
|
||||||
|
name: "feature=NotReleased, node=feature not supported, pod=feature used: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.30"),
|
||||||
|
node: nodeNotSupportedTheFeature,
|
||||||
|
pod: podUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "feature=NotReleased, node=feature not supported, pod=feature not used: it should ADMIT",
|
||||||
|
emulationVersion: utilversion.MustParse("1.30"),
|
||||||
|
node: nodeNotSupportedTheFeature,
|
||||||
|
pod: podNotUsedTheFeature,
|
||||||
|
expectRejection: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
featuregatetesting.SetFeatureGateEmulationVersionDuringTest(t, utilfeature.DefaultFeatureGate, test.emulationVersion)
|
||||||
|
actualResult := rejectPodAdmissionBasedOnSupplementalGroupsPolicy(test.pod, test.node)
|
||||||
|
if test.expectRejection != actualResult {
|
||||||
|
t.Errorf("unexpected result, expected %v but got %v", test.expectRejection, actualResult)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user