Skip to content

Commit 619b24b

Browse files
committed
payload-command: handle minimum kubelet version
Signed-off-by: Peter Hunt <[email protected]>
1 parent 46ff21c commit 619b24b

File tree

3 files changed

+118
-19
lines changed

3 files changed

+118
-19
lines changed

features/util.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func (b *featureGateBuilder) register() (configv1.FeatureGateName, error) {
192192
} else {
193193
allFeatureGates[clusterProfile][featureSet].Disabled = append(allFeatureGates[clusterProfile][featureSet].Disabled, description)
194194
}
195-
if b.minimumKubeletVersion != "" {
195+
if b.minimumKubeletVersion != "" && featureSet == configv1.Default {
196196
if allFeatureGates[clusterProfile][featureSet].EnabledGivenMinimumVersion == nil {
197197
allFeatureGates[clusterProfile][featureSet].EnabledGivenMinimumVersion = map[configv1.MinimumComponent]map[string][]configv1.FeatureGateAttributes{}
198198
}

payload-command/render/render.go

+112-14
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ package render
33
import (
44
"flag"
55
"fmt"
6-
"github.com/openshift/api/features"
76
"os"
87
"path/filepath"
98
"sort"
109
"strings"
1110

12-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13-
"k8s.io/apimachinery/pkg/runtime"
14-
11+
"github.com/blang/semver/v4"
1512
configv1 "github.com/openshift/api/config/v1"
13+
"github.com/openshift/api/features"
1614
assets "github.com/openshift/api/payload-command/render/renderassets"
15+
16+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
17+
"k8s.io/apimachinery/pkg/runtime"
1718
"k8s.io/apimachinery/pkg/util/sets"
1819
)
1920

@@ -66,6 +67,32 @@ func (o *RenderOpts) Run() error {
6667
if err != nil {
6768
return fmt.Errorf("problem with featuregate manifests: %w", err)
6869
}
70+
71+
nodeConfigManifests, err := nodeConfigManifests([]string{o.RenderedManifestInputFilename})
72+
if err != nil {
73+
return fmt.Errorf("problem with node config manifests: %w", err)
74+
}
75+
var minimumKubeletVersion *semver.Version
76+
for _, manifest := range nodeConfigManifests {
77+
uncastObj, err := manifest.GetDecodedObj()
78+
if err != nil {
79+
return fmt.Errorf("error decoding FeatureGate: %w", err)
80+
}
81+
nodeConfig := &configv1.Node{}
82+
err = runtime.DefaultUnstructuredConverter.FromUnstructured(uncastObj.(*unstructured.Unstructured).Object, nodeConfig)
83+
if err != nil {
84+
return fmt.Errorf("error converting NodeConfig: %w", err)
85+
}
86+
// TODO FIXME: how do we handle multiple?
87+
if nodeConfig.Spec.MinimumKubeletVersion != "" {
88+
v, err := semver.Parse(nodeConfig.Spec.MinimumKubeletVersion)
89+
if err != nil {
90+
return fmt.Errorf("failed to parse provided minimum kubelet version: %w", err)
91+
}
92+
minimumKubeletVersion = &v
93+
}
94+
}
95+
6996
clusterProfileAnnotationName := fmt.Sprintf("include.release.openshift.io/%s", o.UnprefixedClusterProfile)
7097

7198
for _, featureGateFile := range featureGateFiles {
@@ -109,7 +136,8 @@ func (o *RenderOpts) Run() error {
109136
if err != nil {
110137
return fmt.Errorf("unable to resolve featureGateStatus: %w", err)
111138
}
112-
currentDetails := FeaturesGateDetailsFromFeatureSets(featureGateStatus, o.PayloadVersion)
139+
currentDetails := FeaturesGateDetailsFromFeatureSets(featureGateStatus, o.PayloadVersion, minimumKubeletVersion, featureGates.Spec.FeatureSet)
140+
113141
featureGates.Status.FeatureGates = []configv1.FeatureGateDetails{*currentDetails}
114142

115143
featureGateOutBytes := writeFeatureGateV1OrDie(featureGates)
@@ -227,6 +255,27 @@ func clusterProfilesFrom(annotations map[string]string) sets.Set[string] {
227255
}
228256

229257
func featureGateManifests(renderedManifestInputFilenames []string) (assets.RenderedManifests, error) {
258+
inputManifests, err := loadManifests(renderedManifestInputFilenames)
259+
if err != nil {
260+
return nil, err
261+
}
262+
featureGates := inputManifests.ListManifestOfType(configv1.GroupVersion.WithKind("FeatureGate"))
263+
if len(featureGates) == 0 {
264+
return nil, fmt.Errorf("no FeatureGates found in manfest dir: %v", renderedManifestInputFilenames)
265+
}
266+
267+
return featureGates, nil
268+
}
269+
270+
func nodeConfigManifests(renderedManifestInputFilenames []string) (assets.RenderedManifests, error) {
271+
inputManifests, err := loadManifests(renderedManifestInputFilenames)
272+
if err != nil {
273+
return nil, err
274+
}
275+
return inputManifests.ListManifestOfType(configv1.GroupVersion.WithKind("Node")), nil
276+
}
277+
278+
func loadManifests(renderedManifestInputFilenames []string) (assets.RenderedManifests, error) {
230279
if len(renderedManifestInputFilenames) == 0 {
231280
return nil, fmt.Errorf("cannot return FeatureGate without rendered manifests")
232281
}
@@ -244,23 +293,72 @@ func featureGateManifests(renderedManifestInputFilenames []string) (assets.Rende
244293
})
245294
}
246295
}
247-
featureGates := inputManifests.ListManifestOfType(configv1.GroupVersion.WithKind("FeatureGate"))
248-
if len(featureGates) == 0 {
249-
return nil, fmt.Errorf("no FeatureGates found in manfest dir: %v", renderedManifestInputFilenames)
250-
}
251-
252-
return featureGates, nil
296+
return inputManifests, nil
253297
}
254298

255-
func FeaturesGateDetailsFromFeatureSets(featureGateStatus *features.FeatureGateEnabledDisabled, currentVersion string) *configv1.FeatureGateDetails {
299+
func FeaturesGateDetailsFromFeatureSets(featureGateStatus *features.FeatureGateEnabledDisabled, currentVersion string, minimumKubeletVersion *semver.Version, featureSet configv1.FeatureSet) *configv1.FeatureGateDetails {
256300
currentDetails := configv1.FeatureGateDetails{
257301
Version: currentVersion,
258302
}
303+
skippedForVersion := map[configv1.FeatureGateName]bool{}
259304
for _, gateName := range featureGateStatus.Enabled {
260-
currentDetails.Enabled = append(currentDetails.Enabled, *gateName.FeatureGateAttributes.DeepCopy())
305+
// Skip adding if we have a RequiredMinimumComponentVersion, as we'll handle that below
306+
if len(gateName.FeatureGateAttributes.RequiredMinimumComponentVersions) != 0 && featureSet == configv1.Default {
307+
skippedForVersion[gateName.FeatureGateAttributes.Name] = false
308+
} else {
309+
currentDetails.Enabled = append(currentDetails.Enabled, *gateName.FeatureGateAttributes.DeepCopy())
310+
}
261311
}
262312
for _, gateName := range featureGateStatus.Disabled {
263-
currentDetails.Disabled = append(currentDetails.Disabled, *gateName.FeatureGateAttributes.DeepCopy())
313+
// Skip adding if we have a RequiredMinimumComponentVersion, as we'll handle that below
314+
if len(gateName.FeatureGateAttributes.RequiredMinimumComponentVersions) != 0 && featureSet == configv1.Default {
315+
skippedForVersion[gateName.FeatureGateAttributes.Name] = false
316+
} else {
317+
currentDetails.Disabled = append(currentDetails.Disabled, *gateName.FeatureGateAttributes.DeepCopy())
318+
}
319+
}
320+
321+
if minimumKubeletVersion != nil {
322+
for versionStr, attrs := range featureGateStatus.EnabledGivenMinimumVersion[configv1.MinimumComponentKubelet] {
323+
gateVersion, err := semver.Parse(versionStr)
324+
if err != nil {
325+
// Programming error, as these are built into the binary in features/feature.go
326+
panic(err)
327+
}
328+
if minimumKubeletVersion.LTE(gateVersion) {
329+
for _, attr := range attrs {
330+
currentDetails.Enabled = append(currentDetails.Enabled, attr)
331+
skippedForVersion[attr.Name] = true
332+
}
333+
} else {
334+
for _, attr := range attrs {
335+
currentDetails.Disabled = append(currentDetails.Disabled, attr)
336+
skippedForVersion[attr.Name] = true
337+
}
338+
}
339+
}
340+
} else {
341+
for _, attrs := range featureGateStatus.EnabledGivenMinimumVersion[configv1.MinimumComponentKubelet] {
342+
for _, attr := range attrs {
343+
currentDetails.Disabled = append(currentDetails.Disabled, attr)
344+
skippedForVersion[attr.Name] = true
345+
}
346+
}
347+
}
348+
349+
for name, missed := range skippedForVersion {
350+
if !missed {
351+
// Programming error: a gate was registered as skipped but not addressed
352+
panic(fmt.Errorf("Missed feature gate name %s when constructing featureGateStatus", name))
353+
}
354+
}
355+
if minimumKubeletVersion != nil {
356+
currentDetails.RenderedMinimumComponentVersions = []configv1.MinimumComponentVersion{
357+
{
358+
Component: configv1.MinimumComponentKubelet,
359+
Version: minimumKubeletVersion.String(),
360+
},
361+
}
264362
}
265363

266364
// sort for stability

payload-command/render/write_featureset.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package render
33
import (
44
"flag"
55
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
610
configv1 "github.com/openshift/api/config/v1"
711
"github.com/openshift/api/features"
812
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
913
"k8s.io/apimachinery/pkg/util/sets"
10-
"os"
11-
"path/filepath"
12-
"strings"
1314
)
1415

1516
var (
@@ -61,7 +62,7 @@ func (o *WriteFeatureSets) Run() error {
6162
newLegacyFeatureGates.Insert(string(curr.FeatureGateAttributes.Name))
6263
}
6364
}
64-
currentDetails := FeaturesGateDetailsFromFeatureSets(featureGateStatuses, o.PayloadVersion)
65+
currentDetails := FeaturesGateDetailsFromFeatureSets(featureGateStatuses, o.PayloadVersion, nil, featureSetName)
6566

6667
featureGateInstance := &configv1.FeatureGate{
6768
ObjectMeta: metav1.ObjectMeta{

0 commit comments

Comments
 (0)