Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions pkg/controller/kubelet-config/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,6 @@ func validateUserKubeletConfig(cfg *mcfgv1.KubeletConfig) error {
if len(kcDecoded.FeatureGates) > 0 {
return fmt.Errorf("KubeletConfiguration: featureGates is not allowed to be set, but contains: %v", kcDecoded.FeatureGates)
}
if kcDecoded.StaticPodPath != "" {
return fmt.Errorf("KubeletConfiguration: staticPodPath is not allowed to be set, but contains: %s", kcDecoded.StaticPodPath)
}
if kcDecoded.FailSwapOn != nil {
return fmt.Errorf("KubeletConfiguration: failSwapOn is not allowed to be set, but contains: %v", *kcDecoded.FailSwapOn)
}
Expand All @@ -393,6 +390,26 @@ func validateUserKubeletConfig(cfg *mcfgv1.KubeletConfig) error {
return nil
}

// validateStaticPodPathForPool checks that staticPodPath is not set to a non-empty value.
// Control plane pools (master, arbiter) cannot set staticPodPath at all.
// Worker and custom pools may only set staticPodPath to "" to disable static pods.
func validateStaticPodPathForPool(cfg *mcfgv1.KubeletConfig, poolName string) error {
if cfg.Spec.KubeletConfig == nil || cfg.Spec.KubeletConfig.Raw == nil {
return nil
}
kcDecoded, err := DecodeKubeletConfig(cfg.Spec.KubeletConfig.Raw)
if err != nil {
return fmt.Errorf("KubeletConfig could not be unmarshalled, err: %w", err)
}
if kcDecoded.StaticPodPath == "" {
return nil
}
if poolName == ctrlcommon.MachineConfigPoolMaster || poolName == ctrlcommon.MachineConfigPoolArbiter {
return fmt.Errorf("KubeletConfiguration: staticPodPath is not allowed to be set for pool %q, but contains: %s", poolName, kcDecoded.StaticPodPath)
}
return fmt.Errorf("KubeletConfiguration: staticPodPath must be empty for non-control-plane pool %q, but contains: %s", poolName, kcDecoded.StaticPodPath)
}

func wrapErrorWithCondition(err error, args ...interface{}) mcfgv1.KubeletConfigCondition {
var condition *mcfgv1.KubeletConfigCondition
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/controller/kubelet-config/kubelet_config_bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ func RunKubeletBootstrap(templateDir string, kubeletConfigs []*mcfgv1.KubeletCon
}
role := pool.Name

// Validate staticPodPath is not set for control plane pools
if err := validateStaticPodPathForPool(kubeletConfig, role); err != nil {
return nil, err
}

originalKubeConfig, err := generateOriginalKubeletConfigWithFeatureGates(controllerConfig, templateDir, role, fgHandler, apiServer)
if err != nil {
return nil, err
Expand Down
7 changes: 7 additions & 0 deletions pkg/controller/kubelet-config/kubelet_config_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,13 @@ func (ctrl *Controller) syncKubeletConfig(key string) error {
return ctrl.syncStatusOnly(cfg, err, "could not get the TLSSecurityProfile from %v: %v", ctrlcommon.APIServerInstanceName, err)
}

// Validate staticPodPath for all pools before applying any changes
for _, pool := range mcpPools {
if err := validateStaticPodPathForPool(cfg, pool.Name); err != nil {
return ctrl.syncStatusOnly(cfg, err)
}
}

for _, pool := range mcpPools {
role := pool.Name
// Get MachineConfig
Expand Down
56 changes: 50 additions & 6 deletions pkg/controller/kubelet-config/kubelet_config_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -913,12 +913,6 @@ func TestKubeletConfigDenylistedOptions(t *testing.T) {
ClusterDomain: "some_value",
},
},
{
name: "test banned staticpodpath",
config: &kubeletconfigv1beta1.KubeletConfiguration{
StaticPodPath: "some_value",
},
},
{
name: "user cannot supply features gates",
config: &kubeletconfigv1beta1.KubeletConfiguration{
Expand Down Expand Up @@ -974,6 +968,56 @@ func TestKubeletConfigDenylistedOptions(t *testing.T) {
}
}

func TestStaticPodPathPoolValidation(t *testing.T) {
kcWithNonEmptyStaticPodPath := newKubeletConfig(
"test-staticpodpath-nonempty",
&kubeletconfigv1beta1.KubeletConfiguration{
StaticPodPath: "/some/path",
},
metav1.AddLabelToSelector(&metav1.LabelSelector{}, "", ""),
)

// non-empty staticPodPath should be rejected for all pools
err := validateStaticPodPathForPool(kcWithNonEmptyStaticPodPath, ctrlcommon.MachineConfigPoolMaster)
if err == nil {
t.Error("expected error for non-empty staticPodPath on master pool, got nil")
}

err = validateStaticPodPathForPool(kcWithNonEmptyStaticPodPath, ctrlcommon.MachineConfigPoolArbiter)
if err == nil {
t.Error("expected error for non-empty staticPodPath on arbiter pool, got nil")
}

err = validateStaticPodPathForPool(kcWithNonEmptyStaticPodPath, ctrlcommon.MachineConfigPoolWorker)
if err == nil {
t.Error("expected error for non-empty staticPodPath on worker pool, got nil")
}

err = validateStaticPodPathForPool(kcWithNonEmptyStaticPodPath, "custom-pool")
if err == nil {
t.Error("expected error for non-empty staticPodPath on custom pool, got nil")
}

// empty staticPodPath should be allowed for worker and custom pools
kcWithEmptyStaticPodPath := newKubeletConfig(
"test-staticpodpath-empty",
&kubeletconfigv1beta1.KubeletConfiguration{
StaticPodPath: "",
},
metav1.AddLabelToSelector(&metav1.LabelSelector{}, "", ""),
)

err = validateStaticPodPathForPool(kcWithEmptyStaticPodPath, ctrlcommon.MachineConfigPoolWorker)
if err != nil {
t.Errorf("expected no error for empty staticPodPath on worker pool, got: %v", err)
}

err = validateStaticPodPathForPool(kcWithEmptyStaticPodPath, "custom-pool")
if err != nil {
t.Errorf("expected no error for empty staticPodPath on custom pool, got: %v", err)
}
}

func TestKubeletConfigLogLevel(t *testing.T) {
// Test cases for valid LogLevel values
validLogLevels := []struct {
Expand Down