-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1754 from haircommander/min-kubelet-version
OCPNODE-2940: add support for minimumKubeletVersion
- Loading branch information
Showing
10 changed files
with
363 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
81 changes: 81 additions & 0 deletions
81
pkg/operator/configobservation/node/observe_authorization_mode.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package node | ||
|
||
import ( | ||
"github.com/openshift/api/features" | ||
"github.com/openshift/library-go/pkg/operator/configobserver" | ||
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates" | ||
"github.com/openshift/library-go/pkg/operator/events" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
) | ||
|
||
// There are scopes authorizer tests that fail if this order is changed. | ||
// So this should not be sorted | ||
var defaultAuthenticationModes = []string{ | ||
"Scope", | ||
"SystemMasters", | ||
"RBAC", | ||
"Node", | ||
} | ||
|
||
var authenticationModesWithMinimumKubeletVersion = []string{ | ||
"Scope", | ||
"SystemMasters", | ||
"RBAC", | ||
ModeMinimumKubeletVersion, // before "Node" to have a chance to deny a node | ||
"Node", | ||
} | ||
var ( | ||
authModeFlag = "authorization-mode" | ||
apiServerArgs = "apiServerArguments" | ||
authModePath = []string{apiServerArgs, authModeFlag} | ||
) | ||
|
||
type authorizationModeObserver struct { | ||
featureGateAccessor featuregates.FeatureGateAccess | ||
authModes []string | ||
} | ||
|
||
func NewAuthorizationModeObserver(featureGateAccessor featuregates.FeatureGateAccess) configobserver.ObserveConfigFunc { | ||
return (&authorizationModeObserver{ | ||
featureGateAccessor: featureGateAccessor, | ||
}).ObserveAuthorizationMode | ||
} | ||
|
||
// ObserveAuthorizationMode watches the featuregate configuration and generates the apiServerArguments.authorization-mode | ||
// It currently hardcodes the default set and adds MinimumKubeletVersion if the feature is set to on. | ||
func (o *authorizationModeObserver) ObserveAuthorizationMode(genericListers configobserver.Listers, _ events.Recorder, existingConfig map[string]interface{}) (ret map[string]interface{}, errs []error) { | ||
defer func() { | ||
// Prune the observed config so that it only contains minimumKubeletVersion field. | ||
ret = configobserver.Pruned(ret, authModePath) | ||
}() | ||
|
||
if !o.featureGateAccessor.AreInitialFeatureGatesObserved() { | ||
return existingConfig, nil | ||
} | ||
|
||
featureGates, err := o.featureGateAccessor.CurrentFeatureGates() | ||
if err != nil { | ||
return existingConfig, append(errs, err) | ||
} | ||
|
||
ret = map[string]interface{}{} | ||
if err := AddAuthorizationModes(ret, featureGates.Enabled(features.FeatureGateMinimumKubeletVersion)); err != nil { | ||
return existingConfig, append(errs, err) | ||
} | ||
return ret, nil | ||
} | ||
|
||
// AddAuthorizationModes modifies the passed in config | ||
// to add the "authorization-mode": "MinimumKubeletVersion" if the feature is on. If it's off, it | ||
// removes it instead. | ||
// This function assumes MinimumKubeletVersion auth mode isn't present by default, | ||
// and should likely be removed when it is. | ||
func AddAuthorizationModes(observedConfig map[string]interface{}, isMinimumKubeletVersionEnabled bool) error { | ||
modes := defaultAuthenticationModes | ||
if isMinimumKubeletVersionEnabled { | ||
modes = authenticationModesWithMinimumKubeletVersion | ||
} | ||
|
||
unstructured.RemoveNestedField(observedConfig, authModePath...) | ||
return unstructured.SetNestedStringSlice(observedConfig, modes, authModePath...) | ||
} |
85 changes: 85 additions & 0 deletions
85
pkg/operator/configobservation/node/observe_authorization_mode_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package node | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
) | ||
|
||
func TestAddAuthorizationModes(t *testing.T) { | ||
for _, on := range []bool{false, true} { | ||
expectedSet := []any{"Scope", "SystemMasters", "RBAC", "Node"} | ||
if on { | ||
expectedSet = []any{"Scope", "SystemMasters", "RBAC", ModeMinimumKubeletVersion, "Node"} | ||
} | ||
for _, tc := range []struct { | ||
name string | ||
existingConfig map[string]interface{} | ||
expectedConfig map[string]interface{} | ||
}{ | ||
{ | ||
name: "should not fail if apiServerArguments not present", | ||
existingConfig: map[string]interface{}{ | ||
"fakeconfig": "fake", | ||
}, | ||
expectedConfig: map[string]interface{}{ | ||
"fakeconfig": "fake", | ||
"apiServerArguments": map[string]any{"authorization-mode": expectedSet}, | ||
}, | ||
}, | ||
{ | ||
name: "should not fail if authorization-mode not present", | ||
existingConfig: map[string]interface{}{ | ||
"apiServerArguments": map[string]any{"fake": []any{"fake"}}, | ||
}, | ||
expectedConfig: map[string]interface{}{ | ||
"apiServerArguments": map[string]any{"fake": []any{"fake"}, "authorization-mode": expectedSet}, | ||
}, | ||
}, | ||
{ | ||
name: "should clobber value if not expected", | ||
existingConfig: map[string]interface{}{ | ||
"apiServerArguments": map[string]any{"authorization-mode": []any{"fake"}}, | ||
}, | ||
expectedConfig: map[string]interface{}{ | ||
"apiServerArguments": map[string]any{"authorization-mode": expectedSet}, | ||
}, | ||
}, | ||
{ | ||
name: "should not fail if MinimumKubeletVersion already present", | ||
existingConfig: map[string]interface{}{ | ||
"apiServerArguments": map[string]any{"authorization-mode": []any{"MinimumKubeletVersion"}}, | ||
}, | ||
expectedConfig: map[string]interface{}{ | ||
"apiServerArguments": map[string]any{"authorization-mode": expectedSet}, | ||
}, | ||
}, | ||
{ | ||
name: "should not fail if apiServerArguments not present", | ||
existingConfig: map[string]interface{}{ | ||
"fakeconfig": "fake", | ||
}, | ||
expectedConfig: map[string]interface{}{ | ||
"fakeconfig": "fake", | ||
"apiServerArguments": map[string]any{"authorization-mode": expectedSet}, | ||
}, | ||
}, | ||
} { | ||
name := tc.name + " when feature is " | ||
if on { | ||
name += "on" | ||
} else { | ||
name += "off" | ||
} | ||
t.Run(name, func(t *testing.T) { | ||
if err := AddAuthorizationModes(tc.existingConfig, on); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if diff := cmp.Diff(tc.expectedConfig, tc.existingConfig); diff != "" { | ||
t.Errorf("unexpected config:\n%s", diff) | ||
} | ||
}) | ||
} | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
pkg/operator/configobservation/node/observe_minimum_kubelet_version.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package node | ||
|
||
import ( | ||
configv1 "github.com/openshift/api/config/v1" | ||
"github.com/openshift/api/features" | ||
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/configobservation" | ||
"github.com/openshift/library-go/pkg/operator/configobserver" | ||
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates" | ||
"github.com/openshift/library-go/pkg/operator/events" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/klog/v2" | ||
) | ||
|
||
var ( | ||
ModeMinimumKubeletVersion = "MinimumKubeletVersion" | ||
minimumKubeletVersionConfigPath = "minimumKubeletVersion" | ||
) | ||
|
||
type minimumKubeletVersionObserver struct { | ||
featureGateAccessor featuregates.FeatureGateAccess | ||
} | ||
|
||
func NewMinimumKubeletVersionObserver(featureGateAccessor featuregates.FeatureGateAccess) configobserver.ObserveConfigFunc { | ||
return (&minimumKubeletVersionObserver{ | ||
featureGateAccessor: featureGateAccessor, | ||
}).ObserveMinimumKubeletVersion | ||
} | ||
|
||
// ObserveKubeletMinimumVersion watches the node configuration and generates the minimumKubeletVersion | ||
func (o *minimumKubeletVersionObserver) ObserveMinimumKubeletVersion(genericListers configobserver.Listers, _ events.Recorder, existingConfig map[string]interface{}) (ret map[string]interface{}, errs []error) { | ||
defer func() { | ||
// Prune the observed config so that it only contains minimumKubeletVersion field. | ||
ret = configobserver.Pruned(ret, []string{minimumKubeletVersionConfigPath}) | ||
}() | ||
|
||
if !o.featureGateAccessor.AreInitialFeatureGatesObserved() { | ||
return existingConfig, nil | ||
} | ||
|
||
featureGates, err := o.featureGateAccessor.CurrentFeatureGates() | ||
if err != nil { | ||
return existingConfig, append(errs, err) | ||
} | ||
|
||
if !featureGates.Enabled(features.FeatureGateMinimumKubeletVersion) { | ||
return existingConfig, nil | ||
} | ||
|
||
nodeLister := genericListers.(configobservation.Listers) | ||
configNode, err := nodeLister.NodeLister().Get("cluster") | ||
// we got an error so without the node object we are not able to determine minimumKubeletVersion | ||
if err != nil { | ||
// if config/v1/node/cluster object is not found, that can be treated as a non-error case, but raise a warning | ||
if apierrors.IsNotFound(err) { | ||
klog.Warningf("ObserveMinimumKubeletVersion: nodes.%s/cluster not found", configv1.GroupName) | ||
} else { | ||
errs = append(errs, err) | ||
} | ||
return existingConfig, errs | ||
} | ||
|
||
ret = map[string]interface{}{} | ||
if configNode.Spec.MinimumKubeletVersion == "" { | ||
// in case minimum kubelet version is not set on cluster | ||
// return empty set of configs, this helps to unset the config | ||
// values related to the minimumKubeletVersion. | ||
// Also, ensures that this observer doesn't break cluster upgrades/downgrades | ||
return ret, errs | ||
} | ||
|
||
if err := unstructured.SetNestedField(ret, configNode.Spec.MinimumKubeletVersion, minimumKubeletVersionConfigPath); err != nil { | ||
return existingConfig, append(errs, err) | ||
} | ||
|
||
return ret, errs | ||
} |
Oops, something went wrong.