-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
configobserver: add minimumkubeletversion
Signed-off-by: Peter Hunt <[email protected]>
- Loading branch information
1 parent
93e98b5
commit 49beb0d
Showing
5 changed files
with
206 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package node | ||
|
||
import ( | ||
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1" | ||
) | ||
|
||
type NodeLister interface { | ||
NodeLister() configlistersv1.NodeLister | ||
} |
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,24 @@ | ||
package node | ||
|
||
import ( | ||
"k8s.io/client-go/tools/cache" | ||
|
||
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1" | ||
"github.com/openshift/library-go/pkg/operator/resourcesynccontroller" | ||
) | ||
|
||
type testLister struct { | ||
nodeLister configlistersv1.NodeLister | ||
} | ||
|
||
func (l testLister) NodeLister() configlistersv1.NodeLister { | ||
return l.nodeLister | ||
} | ||
|
||
func (l testLister) ResourceSyncer() resourcesynccontroller.ResourceSyncer { | ||
return nil | ||
} | ||
|
||
func (l testLister) PreRunHasSynced() []cache.InformerSynced { | ||
return nil | ||
} |
73 changes: 73 additions & 0 deletions
73
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,73 @@ | ||
package node | ||
|
||
import ( | ||
configv1 "github.com/openshift/api/config/v1" | ||
"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" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/klog/v2" | ||
) | ||
|
||
var 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) { | ||
if !o.featureGateAccessor.AreInitialFeatureGatesObserved() { | ||
// if we haven't observed featuregates yet, return the existing | ||
return existingConfig, nil | ||
} | ||
|
||
featureGates, err := o.featureGateAccessor.CurrentFeatureGates() | ||
if err != nil { | ||
return existingConfig, append(errs, err) | ||
} | ||
|
||
if !featureGates.Enabled(features.FeatureGateMinimumKubeletVersion) { | ||
return existingConfig, nil | ||
} | ||
|
||
defer func() { | ||
// Prune the observed config so that it only contains minimumKubeletVersion field. | ||
ret = configobserver.Pruned(ret, []string{minimumKubeletVersionConfigPath}) | ||
}() | ||
nodeLister := genericListers.(NodeLister) | ||
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 | ||
} | ||
|
||
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 map[string]interface{}{}, errs | ||
} | ||
|
||
ret = map[string]interface{}{} | ||
if err := unstructured.SetNestedField(ret, configNode.Spec.MinimumKubeletVersion, minimumKubeletVersionConfigPath); err != nil { | ||
errs = append(errs, err) | ||
} | ||
|
||
return ret, errs | ||
} |
99 changes: 99 additions & 0 deletions
99
pkg/operator/configobservation/node/observe_minimum_kubelet_version_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,99 @@ | ||
package node | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
configv1 "github.com/openshift/api/config/v1" | ||
"github.com/openshift/api/features" | ||
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1" | ||
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates" | ||
"github.com/openshift/library-go/pkg/operator/events" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/tools/cache" | ||
clocktesting "k8s.io/utils/clock/testing" | ||
) | ||
|
||
func TestObserveKubeletMinimumVersion(t *testing.T) { | ||
type Test struct { | ||
name string | ||
existingConfig map[string]interface{} | ||
expectedObservedConfig map[string]interface{} | ||
minimumKubeletVersion string | ||
featureOn bool | ||
} | ||
tests := []Test{ | ||
{ | ||
name: "feature off", | ||
existingConfig: map[string]interface{}{}, | ||
expectedObservedConfig: map[string]interface{}{}, | ||
minimumKubeletVersion: "1.30.0", | ||
featureOn: false, | ||
}, | ||
{ | ||
name: "empty minimumKubeletVersion", | ||
expectedObservedConfig: map[string]interface{}{}, | ||
minimumKubeletVersion: "", | ||
featureOn: true, | ||
}, | ||
{ | ||
name: "set minimumKubeletVersion", | ||
expectedObservedConfig: map[string]interface{}{ | ||
"minimumKubeletVersion": string("1.30.0"), | ||
}, | ||
minimumKubeletVersion: "1.30.0", | ||
featureOn: true, | ||
}, | ||
{ | ||
name: "existing minimumKubeletVersion", | ||
expectedObservedConfig: map[string]interface{}{ | ||
"minimumKubeletVersion": string("1.30.0"), | ||
}, | ||
existingConfig: map[string]interface{}{ | ||
"minimumKubeletVersion": string("1.29.0"), | ||
}, | ||
minimumKubeletVersion: "1.30.0", | ||
featureOn: true, | ||
}, | ||
{ | ||
name: "existing minimumKubeletVersion unset", | ||
expectedObservedConfig: map[string]interface{}{}, | ||
existingConfig: map[string]interface{}{ | ||
"minimumKubeletVersion": string("1.29.0"), | ||
}, | ||
minimumKubeletVersion: "", | ||
featureOn: true, | ||
}, | ||
} | ||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
// test data | ||
eventRecorder := events.NewInMemoryRecorder("", clocktesting.NewFakePassiveClock(time.Now())) | ||
configNodeIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) | ||
configNodeIndexer.Add(&configv1.Node{ | ||
ObjectMeta: metav1.ObjectMeta{Name: "cluster"}, | ||
Spec: configv1.NodeSpec{MinimumKubeletVersion: test.minimumKubeletVersion}, | ||
}) | ||
listers := testLister{ | ||
nodeLister: configlistersv1.NewNodeLister(configNodeIndexer), | ||
} | ||
|
||
fg := featuregates.NewHardcodedFeatureGateAccess([]configv1.FeatureGateName{features.FeatureGateMinimumKubeletVersion}, []configv1.FeatureGateName{}) | ||
if !test.featureOn { | ||
fg = featuregates.NewHardcodedFeatureGateAccess([]configv1.FeatureGateName{}, []configv1.FeatureGateName{features.FeatureGateMinimumKubeletVersion}) | ||
} | ||
|
||
// act | ||
actualObservedConfig, errs := NewMinimumKubeletVersionObserver(fg)(listers, eventRecorder, test.existingConfig) | ||
|
||
// validate | ||
if len(errs) > 0 { | ||
t.Fatal(errs) | ||
} | ||
if diff := cmp.Diff(test.expectedObservedConfig, actualObservedConfig); diff != "" { | ||
t.Fatalf("unexpected configuration, diff = %v", diff) | ||
} | ||
}) | ||
} | ||
} |