Skip to content

Commit 5dcaf60

Browse files
committed
Set OKD as default featureset for OKD clusters
Add 'scos' build tag support to enable OKD-specific behavior: - Use OKD featureset as default when FeatureGate is not found - Migrate existing clusters from Default to OKD featureset on upgrade
1 parent af3e38b commit 5dcaf60

7 files changed

Lines changed: 76 additions & 14 deletions

File tree

Dockerfile.rhel

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang-1.24-openshift-4.21 AS builder
22
WORKDIR /go/src/github.com/openshift/cluster-version-operator
33
COPY . .
4-
RUN hack/build-go.sh; \
4+
ARG TAGS=""
5+
RUN TAGS="${TAGS}" hack/build-go.sh; \
56
mkdir -p /tmp/build; \
67
cp _output/linux/$(go env GOARCH)/cluster-version-operator _output/linux/$(go env GOARCH)/cluster-version-operator-tests.gz /tmp/build/
78

hack/build-go.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ LDFLAGS_TEST_EXTENSION+=" -X '${OPENSHIFT_TESTS_EXTENSION_MODULE}/pkg/version.Bu
2222
LDFLAGS_TEST_EXTENSION+=" -X '${OPENSHIFT_TESTS_EXTENSION_MODULE}/pkg/version.GitTreeState=${GIT_TREE_STATE}'"
2323

2424
echo "Building ${REPO} cluster-version-operator-tests binary (${VERSION_OVERRIDE})"
25+
TAGS_FLAG=""
26+
if [ -n "${TAGS:-}" ]; then
27+
TAGS_FLAG="-tags=${TAGS}"
28+
fi
2529
GO_COMPLIANCE_POLICY="exempt_all" CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} \
2630
go build \
2731
${GOFLAGS} \
32+
${TAGS_FLAG} \
2833
-ldflags "${LDFLAGS_TEST_EXTENSION}" \
2934
-o "${BIN_PATH}/cluster-version-operator-tests" \
3035
"${REPO}/cmd/cluster-version-operator-tests/..."
@@ -35,4 +40,4 @@ gzip --keep --force "${BIN_PATH}/cluster-version-operator-tests"
3540
# Build the cluster-version-operator binary
3641
GLDFLAGS+="-X ${REPO}/pkg/version.Raw=${VERSION_OVERRIDE}"
3742
echo "Building ${REPO} cluster-version-operator binary (${VERSION_OVERRIDE})"
38-
GOOS=${GOOS} GOARCH=${GOARCH} go build ${GOFLAGS} -ldflags "${GLDFLAGS}" -o ${BIN_PATH}/cluster-version-operator ${REPO}/cmd/cluster-version-operator/...
43+
GOOS=${GOOS} GOARCH=${GOARCH} go build ${GOFLAGS} ${TAGS_FLAG} -ldflags "${GLDFLAGS}" -o ${BIN_PATH}/cluster-version-operator ${REPO}/cmd/cluster-version-operator/...

pkg/payload/render.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ func renderDir(renderConfig manifestRenderConfig, idir, odir string, requiredFea
126126
if skipFiles.Has(file.Name()) {
127127
continue
128128
}
129-
if strings.Contains(file.Name(), "CustomNoUpgrade") || strings.Contains(file.Name(), "TechPreviewNoUpgrade") || strings.Contains(file.Name(), "DevPreviewNoUpgrade") {
130-
// CustomNoUpgrade, TechPreviewNoUpgrade and DevPreviewNoUpgrade may add features to manifests like the ClusterVersion CRD,
129+
if strings.Contains(file.Name(), "CustomNoUpgrade") || strings.Contains(file.Name(), "TechPreviewNoUpgrade") || strings.Contains(file.Name(), "DevPreviewNoUpgrade") || strings.Contains(file.Name(), "OKD") {
130+
// CustomNoUpgrade, TechPreviewNoUpgrade, DevPreviewNoUpgrade and OKD may add features to manifests like the ClusterVersion CRD,
131131
// but we do not need those features during bootstrap-render time. In those clusters, the production
132132
// CVO will be along shortly to update the manifests and deliver the gated features.
133133
// fixme: now that we have requiredFeatureSet, use it to do Manifest.Include() filtering here instead of making filename assumptions

pkg/start/start.go

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
apierrors "k8s.io/apimachinery/pkg/api/errors"
1818
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1919
"k8s.io/apimachinery/pkg/fields"
20+
"k8s.io/apimachinery/pkg/types"
2021
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2122
"k8s.io/apimachinery/pkg/util/sets"
2223
coreinformers "k8s.io/client-go/informers"
@@ -46,6 +47,7 @@ import (
4647
"github.com/openshift/cluster-version-operator/pkg/featuregates"
4748
"github.com/openshift/cluster-version-operator/pkg/internal"
4849
"github.com/openshift/cluster-version-operator/pkg/payload"
50+
"github.com/openshift/cluster-version-operator/pkg/version"
4951
)
5052

5153
const (
@@ -188,7 +190,8 @@ func (o *Options) Run(ctx context.Context) error {
188190
}
189191

190192
clusterVersionConfigInformerFactory, configInformerFactory := o.prepareConfigInformerFactories(cb)
191-
startingFeatureSet, startingCvoGates, err := o.processInitialFeatureGate(ctx, configInformerFactory)
193+
configClient := cb.ClientOrDie("feature-gate-migration")
194+
startingFeatureSet, startingCvoGates, err := o.processInitialFeatureGate(ctx, configInformerFactory, configClient)
192195
if err != nil {
193196
return fmt.Errorf("error processing feature gates: %w", err)
194197
}
@@ -242,7 +245,7 @@ func (o *Options) getOpenShiftVersion() string {
242245
return releaseMetadata.Version
243246
}
244247

245-
func (o *Options) processInitialFeatureGate(ctx context.Context, configInformerFactory configinformers.SharedInformerFactory) (configv1.FeatureSet, featuregates.CvoGates, error) {
248+
func (o *Options) processInitialFeatureGate(ctx context.Context, configInformerFactory configinformers.SharedInformerFactory, configClient clientset.Interface) (configv1.FeatureSet, featuregates.CvoGates, error) {
246249
var startingFeatureSet configv1.FeatureSet
247250
var cvoGates featuregates.CvoGates
248251

@@ -266,19 +269,52 @@ func (o *Options) processInitialFeatureGate(ctx context.Context, configInformerF
266269
gate, err := featureGates.Get("cluster")
267270
switch {
268271
case apierrors.IsNotFound(err):
269-
// if we have no featuregates, then the cluster is using the default featureset, which is "".
270-
// This excludes everything that could possibly depend on a different feature set.
271-
startingFeatureSet = ""
272-
klog.Infof("FeatureGate not found in cluster, will assume default feature set %q at startup", startingFeatureSet)
272+
// if we have no featuregates and this is an OKD build, then the cluster is using OKD featureset.
273+
// The FeatureGate resource will be created by the installer.
274+
if version.IsSCOS() {
275+
startingFeatureSet = configv1.FeatureSet(configv1.OKD)
276+
klog.Infof("FeatureGate not found in cluster, will assume OKD feature set %q at startup", startingFeatureSet)
277+
} else {
278+
klog.Infof("FeatureGate not found in cluster, using default feature set at startup")
279+
}
273280
case err != nil:
274281
// This should not happen because featureGates is backed by the informer cache which successfully synced earlier
275282
klog.Errorf("Failed to get FeatureGate from cluster: %v", err)
276283
return startingFeatureSet, cvoGates, fmt.Errorf("failed to get FeatureGate from informer cache: %w", err)
277284
default:
278285
clusterFeatureGate = gate
279286
startingFeatureSet = gate.Spec.FeatureSet
280-
cvoGates = featuregates.CvoGatesFromFeatureGate(clusterFeatureGate, cvoOpenShiftVersion)
281287
klog.Infof("FeatureGate found in cluster, using its feature set %q at startup", startingFeatureSet)
288+
289+
// Migrate from Default ("") to OKD for existing clusters during upgrade (only for OKD builds)
290+
if version.IsSCOS() && (startingFeatureSet == "" || startingFeatureSet == configv1.Default) {
291+
klog.Infof("Detected Default feature set, migrating to OKD feature set")
292+
293+
// Patch the FeatureGate to change from Default to OKD
294+
patchData := []byte(fmt.Sprintf(`{"spec":{"featureSet":"%s"}}`, configv1.OKD))
295+
patchedGate, patchErr := configClient.ConfigV1().FeatureGates().Patch(
296+
ctx,
297+
"cluster",
298+
types.MergePatchType,
299+
patchData,
300+
metav1.PatchOptions{},
301+
)
302+
if patchErr != nil {
303+
klog.Errorf("Failed to migrate FeatureGate from Default to OKD: %v", patchErr)
304+
klog.Warningf("Continuing with Default feature set; migration will be retried on next restart")
305+
// Continue with Default - don't fail startup
306+
} else {
307+
klog.Infof("Successfully migrated FeatureGate from Default to OKD")
308+
startingFeatureSet = configv1.FeatureSet(configv1.OKD)
309+
clusterFeatureGate = patchedGate
310+
311+
// Note: The FeatureChangeStopper will detect this change and trigger a restart
312+
// This is expected and ensures CVO starts cleanly with the new feature set
313+
klog.Infof("CVO will restart to apply OKD feature set changes")
314+
}
315+
}
316+
// Compute CVO gates from the feature gate (potentially migrated)
317+
cvoGates = featuregates.CvoGatesFromFeatureGate(clusterFeatureGate, cvoOpenShiftVersion)
282318
}
283319

284320
if cvoGates.UnknownVersion() {

pkg/start/start_integration_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ func TestIntegrationCVO_initializeAndUpgrade(t *testing.T) {
192192
}
193193

194194
clusterVersionConfigInformerFactory, configInformerFactory := options.prepareConfigInformerFactories(cb)
195-
featureset, gates, err := options.processInitialFeatureGate(context.Background(), configInformerFactory)
195+
configClient := cb.ClientOrDie("feature-gate-migration")
196+
featureset, gates, err := options.processInitialFeatureGate(context.Background(), configInformerFactory, configClient)
196197
if err != nil {
197198
t.Fatal(err)
198199
}
@@ -333,7 +334,8 @@ func TestIntegrationCVO_gracefulStepDown(t *testing.T) {
333334
}
334335

335336
clusterVersionConfigInformerFactory, configInformerFactory := options.prepareConfigInformerFactories(cb)
336-
featureset, gates, err := options.processInitialFeatureGate(context.Background(), configInformerFactory)
337+
configClient := cb.ClientOrDie("feature-gate-migration")
338+
featureset, gates, err := options.processInitialFeatureGate(context.Background(), configInformerFactory, configClient)
337339
if err != nil {
338340
t.Fatal(err)
339341
}
@@ -536,7 +538,8 @@ metadata:
536538
}
537539

538540
clusterVersionConfigInformerFactory, configInformerFactory := options.prepareConfigInformerFactories(cb)
539-
featureset, gates, err := options.processInitialFeatureGate(context.Background(), configInformerFactory)
541+
configClient := cb.ClientOrDie("feature-gate-migration")
542+
featureset, gates, err := options.processInitialFeatureGate(context.Background(), configInformerFactory, configClient)
540543
if err != nil {
541544
t.Fatal(err)
542545
}

pkg/version/scos.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build scos
2+
3+
package version
4+
5+
func init() {
6+
SCOS = true
7+
}

pkg/version/version.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,14 @@ var (
1717

1818
// String is the human-friendly representation of the version.
1919
String = fmt.Sprintf("ClusterVersionOperator %s", Raw)
20+
21+
// SCOS is a setting to enable CentOS Stream CoreOS-only modifications.
22+
// This is set via the scos build tag.
23+
SCOS = false
2024
)
25+
26+
// IsSCOS returns true if CentOS Stream CoreOS-only modifications are enabled.
27+
// This is enabled via the scos build tag for OKD builds.
28+
func IsSCOS() bool {
29+
return SCOS
30+
}

0 commit comments

Comments
 (0)