From 083489082f75782a13cae0b1e7fb9ff4558c0f11 Mon Sep 17 00:00:00 2001 From: Joel Speed Date: Wed, 24 Dec 2025 17:14:44 +0000 Subject: [PATCH 1/2] Introduce version based feature gating --- features/features.go | 280 +++++++----- features/okd_featureset_parity_test.go | 66 +-- features/util.go | 107 +++-- go.mod | 1 + go.sum | 2 + hack/update-payload-featuregates.sh | 1 + payload-command/render/render.go | 23 +- payload-command/render/write_featureset.go | 186 +++++++- vendor/github.com/blang/semver/v4/LICENSE | 22 + vendor/github.com/blang/semver/v4/json.go | 23 + vendor/github.com/blang/semver/v4/range.go | 416 +++++++++++++++++ vendor/github.com/blang/semver/v4/semver.go | 476 ++++++++++++++++++++ vendor/github.com/blang/semver/v4/sort.go | 28 ++ vendor/github.com/blang/semver/v4/sql.go | 30 ++ vendor/modules.txt | 3 + 15 files changed, 1439 insertions(+), 225 deletions(-) create mode 100644 vendor/github.com/blang/semver/v4/LICENSE create mode 100644 vendor/github.com/blang/semver/v4/json.go create mode 100644 vendor/github.com/blang/semver/v4/range.go create mode 100644 vendor/github.com/blang/semver/v4/semver.go create mode 100644 vendor/github.com/blang/semver/v4/sort.go create mode 100644 vendor/github.com/blang/semver/v4/sql.go diff --git a/features/features.go b/features/features.go index 660e5758b6f..4df7f42507f 100644 --- a/features/features.go +++ b/features/features.go @@ -1,46 +1,76 @@ package features import ( - "fmt" - configv1 "github.com/openshift/api/config/v1" + "k8s.io/apimachinery/pkg/util/sets" ) -func FeatureSets(clusterProfile ClusterProfileName, featureSet configv1.FeatureSet) (*FeatureGateEnabledDisabled, error) { - byFeatureSet, ok := allFeatureGates[clusterProfile] - if !ok { - return nil, fmt.Errorf("no information found for ClusterProfile=%q", clusterProfile) - } - featureGates, ok := byFeatureSet[featureSet] - if !ok { - return nil, fmt.Errorf("no information found for FeatureSet=%q under ClusterProfile=%q", featureSet, clusterProfile) +func FeatureSets(version uint64, clusterProfile ClusterProfileName, featureSet configv1.FeatureSet) *FeatureGateEnabledDisabled { + enabledDisabled := &FeatureGateEnabledDisabled{} + + for name, statuses := range allFeatureGates { + enabled := false + + for _, status := range statuses { + if status.isEnabled(version, clusterProfile, featureSet) { + enabled = true + break + } + } + + if enabled { + enabledDisabled.Enabled = append(enabledDisabled.Enabled, FeatureGateDescription{ + FeatureGateAttributes: configv1.FeatureGateAttributes{ + Name: name, + }, + }) + } else { + enabledDisabled.Disabled = append(enabledDisabled.Disabled, FeatureGateDescription{ + FeatureGateAttributes: configv1.FeatureGateAttributes{ + Name: name, + }, + }) + } } - return featureGates.DeepCopy(), nil + + return enabledDisabled } -func AllFeatureSets() map[ClusterProfileName]map[configv1.FeatureSet]*FeatureGateEnabledDisabled { - ret := map[ClusterProfileName]map[configv1.FeatureSet]*FeatureGateEnabledDisabled{} +func AllFeatureSets() map[uint64]map[ClusterProfileName]map[configv1.FeatureSet]*FeatureGateEnabledDisabled { + // Generating this many versions future proofs us until at least 2040. + versions := sets.New[uint64](4, 5, 6, 7, 8, 9, 10) + clusterProfiles := sets.New[ClusterProfileName](AllClusterProfiles...) + featureSets := sets.New[configv1.FeatureSet](configv1.AllFixedFeatureSets...) - for clusterProfile, byFeatureSet := range allFeatureGates { - newByFeatureSet := map[configv1.FeatureSet]*FeatureGateEnabledDisabled{} + // Check for versions explicitly being set among the gates. + for _, statuses := range allFeatureGates { + for _, status := range statuses { + versions.Insert(status.version.UnsortedList()...) + } + } - for featureSet, enabledDisabled := range byFeatureSet { - newByFeatureSet[featureSet] = enabledDisabled.DeepCopy() + ret := map[uint64]map[ClusterProfileName]map[configv1.FeatureSet]*FeatureGateEnabledDisabled{} + for version := range versions { + ret[version] = map[ClusterProfileName]map[configv1.FeatureSet]*FeatureGateEnabledDisabled{} + for clusterProfile := range clusterProfiles { + ret[version][clusterProfile] = map[configv1.FeatureSet]*FeatureGateEnabledDisabled{} + for featureSet := range featureSets { + ret[version][clusterProfile][featureSet] = FeatureSets(version, clusterProfile, featureSet) + } } - ret[clusterProfile] = newByFeatureSet } return ret } var ( - allFeatureGates = map[ClusterProfileName]map[configv1.FeatureSet]*FeatureGateEnabledDisabled{} + allFeatureGates = map[configv1.FeatureGateName][]featureGateStatus{} FeatureGateConsolePluginCSP = newFeatureGate("ConsolePluginContentSecurityPolicy"). reportProblemsToJiraComponent("Management Console"). contactPerson("jhadvig"). productScope(ocpSpecific). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). enhancementPR("https://github.com/openshift/enhancements/pull/1706"). mustRegister() @@ -49,7 +79,7 @@ var ( contactPerson("ibihim"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/4193"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMutatingAdmissionPolicy = newFeatureGate("MutatingAdmissionPolicy"). @@ -57,7 +87,7 @@ var ( contactPerson("benluddy"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/3962"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateGatewayAPI = newFeatureGate("GatewayAPI"). @@ -65,7 +95,7 @@ var ( contactPerson("miciah"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateOpenShiftPodSecurityAdmission = newFeatureGate("OpenShiftPodSecurityAdmission"). @@ -73,7 +103,7 @@ var ( contactPerson("ibihim"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/899"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateBuildCSIVolumes = newFeatureGate("BuildCSIVolumes"). @@ -81,7 +111,7 @@ var ( contactPerson("adkaplan"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAzureWorkloadIdentity = newFeatureGate("AzureWorkloadIdentity"). @@ -89,7 +119,7 @@ var ( contactPerson("abutcher"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAzureDedicatedHosts = newFeatureGate("AzureDedicatedHosts"). @@ -97,7 +127,7 @@ var ( contactPerson("rvanderp3"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1783"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMaxUnavailableStatefulSet = newFeatureGate("MaxUnavailableStatefulSet"). @@ -105,7 +135,7 @@ var ( contactPerson("atiratree"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/961"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateEventedPLEG = newFeatureGate("EventedPLEG"). @@ -120,7 +150,7 @@ var ( contactPerson("sgrunert"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateSigstoreImageVerificationPKI = newFeatureGate("SigstoreImageVerificationPKI"). @@ -128,7 +158,7 @@ var ( contactPerson("QiWang"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1658"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAlibabaPlatform = newFeatureGate("AlibabaPlatform"). @@ -136,7 +166,7 @@ var ( contactPerson("jspeed"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateVSphereHostVMGroupZonal = newFeatureGate("VSphereHostVMGroupZonal"). @@ -144,7 +174,7 @@ var ( contactPerson("jcpowermac"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1677"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateVSphereMultiDisk = newFeatureGate("VSphereMultiDisk"). @@ -152,7 +182,7 @@ var ( contactPerson("vr4manta"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1709"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateRouteExternalCertificate = newFeatureGate("RouteExternalCertificate"). @@ -160,7 +190,7 @@ var ( contactPerson("chiragkyal"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateCPMSMachineNamePrefix = newFeatureGate("CPMSMachineNamePrefix"). @@ -168,7 +198,7 @@ var ( contactPerson("chiragkyal"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1714"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAdminNetworkPolicy = newFeatureGate("AdminNetworkPolicy"). @@ -176,7 +206,7 @@ var ( contactPerson("tssurya"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNetworkSegmentation = newFeatureGate("NetworkSegmentation"). @@ -184,7 +214,7 @@ var ( contactPerson("tssurya"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1623"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNetworkConnect = newFeatureGate("NetworkConnect"). @@ -192,7 +222,7 @@ var ( contactPerson("tssurya"). productScope(ocpSpecific). enhancementPR("https://github.com/ovn-kubernetes/ovn-kubernetes/pull/5246"). - enableIn(configv1.DevPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade()). mustRegister() FeatureGateAdditionalRoutingCapabilities = newFeatureGate("AdditionalRoutingCapabilities"). @@ -200,7 +230,7 @@ var ( contactPerson("jcaamano"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateRouteAdvertisements = newFeatureGate("RouteAdvertisements"). @@ -208,7 +238,7 @@ var ( contactPerson("jcaamano"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNetworkLiveMigration = newFeatureGate("NetworkLiveMigration"). @@ -216,7 +246,7 @@ var ( contactPerson("pliu"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNetworkDiagnosticsConfig = newFeatureGate("NetworkDiagnosticsConfig"). @@ -224,7 +254,7 @@ var ( contactPerson("kyrtapz"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateOVNObservability = newFeatureGate("OVNObservability"). @@ -232,7 +262,7 @@ var ( contactPerson("npinaeva"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateBackendQuotaGiB = newFeatureGate("EtcdBackendQuota"). @@ -240,7 +270,7 @@ var ( contactPerson("hasbro17"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAutomatedEtcdBackup = newFeatureGate("AutomatedEtcdBackup"). @@ -248,7 +278,7 @@ var ( contactPerson("hasbro17"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMachineAPIOperatorDisableMachineHealthCheckController = newFeatureGate("MachineAPIOperatorDisableMachineHealthCheckController"). @@ -263,7 +293,7 @@ var ( contactPerson("miciah"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMachineConfigNodes = newFeatureGate("MachineConfigNodes"). @@ -271,7 +301,7 @@ var ( contactPerson("ijanssen"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1765"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateImageModeStatusReporting = newFeatureGate("ImageModeStatusReporting"). @@ -279,7 +309,7 @@ var ( contactPerson("ijanssen"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1809"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateClusterAPIInstall = newFeatureGate("ClusterAPIInstall"). @@ -294,7 +324,7 @@ var ( contactPerson("barbacbd"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAWSClusterHostedDNS = newFeatureGate("AWSClusterHostedDNS"). @@ -302,7 +332,7 @@ var ( contactPerson("barbacbd"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAzureClusterHostedDNSInstall = newFeatureGate("AzureClusterHostedDNSInstall"). @@ -310,7 +340,7 @@ var ( contactPerson("sadasu"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1468"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMixedCPUsAllocation = newFeatureGate("MixedCPUsAllocation"). @@ -318,7 +348,7 @@ var ( contactPerson("titzhak"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateManagedBootImages = newFeatureGate("ManagedBootImages"). @@ -326,7 +356,7 @@ var ( contactPerson("djoshy"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateManagedBootImagesAWS = newFeatureGate("ManagedBootImagesAWS"). @@ -334,7 +364,7 @@ var ( contactPerson("djoshy"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateManagedBootImagesvSphere = newFeatureGate("ManagedBootImagesvSphere"). @@ -342,7 +372,7 @@ var ( contactPerson("rsaini"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1496"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateManagedBootImagesAzure = newFeatureGate("ManagedBootImagesAzure"). @@ -350,7 +380,7 @@ var ( contactPerson("djoshy"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1761"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateManagedBootImagesCPMS = newFeatureGate("ManagedBootImagesCPMS"). @@ -358,7 +388,7 @@ var ( contactPerson("djoshy"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1818"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateBootImageSkewEnforcement = newFeatureGate("BootImageSkewEnforcement"). @@ -366,7 +396,7 @@ var ( contactPerson("djoshy"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1761"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateBootcNodeManagement = newFeatureGate("BootcNodeManagement"). @@ -374,7 +404,7 @@ var ( contactPerson("inesqyx"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateSignatureStores = newFeatureGate("SignatureStores"). @@ -382,7 +412,7 @@ var ( contactPerson("lmohanty"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateKMSv1 = newFeatureGate("KMSv1"). @@ -390,7 +420,7 @@ var ( contactPerson("dgrisonnet"). productScope(kubernetes). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGatePinnedImages = newFeatureGate("PinnedImages"). @@ -398,7 +428,7 @@ var ( contactPerson("RishabhSaini"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateUpgradeStatus = newFeatureGate("UpgradeStatus"). @@ -406,7 +436,7 @@ var ( contactPerson("pmuller"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateVolumeAttributesClass = newFeatureGate("VolumeAttributesClass"). @@ -414,7 +444,7 @@ var ( contactPerson("dfajmon"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/3751"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateVolumeGroupSnapshot = newFeatureGate("VolumeGroupSnapshot"). @@ -422,7 +452,7 @@ var ( contactPerson("fbertina"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/3476"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateExternalSnapshotMetadata = newFeatureGate("ExternalSnapshotMetadata"). @@ -430,7 +460,7 @@ var ( contactPerson("jdobson"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/3314"). - enableIn(configv1.DevPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade()). mustRegister() FeatureGateExternalOIDC = newFeatureGate("ExternalOIDC"). @@ -438,7 +468,7 @@ var ( contactPerson("liouk"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1596"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateExternalOIDCWithAdditionalClaimMappings = newFeatureGate("ExternalOIDCWithUIDAndExtraClaimMappings"). @@ -446,7 +476,7 @@ var ( contactPerson("bpalmer"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1777"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateExample = newFeatureGate("Example"). @@ -454,7 +484,7 @@ var ( contactPerson("deads"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateExample2 = newFeatureGate("Example2"). @@ -462,7 +492,7 @@ var ( contactPerson("JoelSpeed"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade()). mustRegister() FeatureGateNewOLM = newFeatureGate("NewOLM"). @@ -470,7 +500,7 @@ var ( contactPerson("joe"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableForClusterProfile(SelfManaged, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inClusterProfile(SelfManaged), inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNewOLMCatalogdAPIV1Metas = newFeatureGate("NewOLMCatalogdAPIV1Metas"). @@ -478,7 +508,7 @@ var ( contactPerson("jordank"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1749"). - enableForClusterProfile(SelfManaged, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inClusterProfile(SelfManaged), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNewOLMPreflightPermissionChecks = newFeatureGate("NewOLMPreflightPermissionChecks"). @@ -486,7 +516,7 @@ var ( contactPerson("tshort"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1768"). - enableForClusterProfile(SelfManaged, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inClusterProfile(SelfManaged), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNewOLMOwnSingleNamespace = newFeatureGate("NewOLMOwnSingleNamespace"). @@ -494,7 +524,7 @@ var ( contactPerson("nschieder"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1849"). - enableForClusterProfile(SelfManaged, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inClusterProfile(SelfManaged), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNewOLMWebhookProviderOpenshiftServiceCA = newFeatureGate("NewOLMWebhookProviderOpenshiftServiceCA"). @@ -502,7 +532,7 @@ var ( contactPerson("pegoncal"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1844"). - enableForClusterProfile(SelfManaged, configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inClusterProfile(SelfManaged), inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNewOLMBoxCutterRuntime = newFeatureGate("NewOLMBoxCutterRuntime"). @@ -517,7 +547,7 @@ var ( contactPerson("tremes"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateInsightsConfig = newFeatureGate("InsightsConfig"). @@ -525,7 +555,7 @@ var ( contactPerson("tremes"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMetricsCollectionProfiles = newFeatureGate("MetricsCollectionProfiles"). @@ -533,7 +563,7 @@ var ( contactPerson("rexagod"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateClusterAPIInstallIBMCloud = newFeatureGate("ClusterAPIInstallIBMCloud"). @@ -541,7 +571,7 @@ var ( contactPerson("cjschaef"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMachineAPIMigration = newFeatureGate("MachineAPIMigration"). @@ -549,7 +579,7 @@ var ( contactPerson("jspeed"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateClusterAPIMachineManagement = newFeatureGate("ClusterAPIMachineManagement"). @@ -557,7 +587,7 @@ var ( contactPerson("ddonati"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1465"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateClusterAPIMachineManagementVSphere = newFeatureGate("ClusterAPIMachineManagementVSphere"). @@ -565,7 +595,7 @@ var ( contactPerson("jcpowermac"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1465"). - enableIn(configv1.DevPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade()). mustRegister() FeatureGateClusterMonitoringConfig = newFeatureGate("ClusterMonitoringConfig"). @@ -573,7 +603,7 @@ var ( contactPerson("marioferh"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMultiArchInstallAzure = newFeatureGate("MultiArchInstallAzure"). @@ -588,7 +618,7 @@ var ( contactPerson("psundara"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateUserNamespacesSupport = newFeatureGate("UserNamespacesSupport"). @@ -596,7 +626,7 @@ var ( contactPerson("haircommander"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/127"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() // Note: this feature is perma-alpha, but it is safe and desireable to enable. @@ -607,7 +637,7 @@ var ( contactPerson("haircommander"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/127"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateProcMountType = newFeatureGate("ProcMountType"). @@ -615,7 +645,7 @@ var ( contactPerson("haircommander"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/4265"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateVSphereMultiNetworks = newFeatureGate("VSphereMultiNetworks"). @@ -623,7 +653,7 @@ var ( contactPerson("rvanderp"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateIngressControllerDynamicConfigurationManager = newFeatureGate("IngressControllerDynamicConfigurationManager"). @@ -631,14 +661,14 @@ var ( contactPerson("miciah"). productScope(ocpSpecific). enhancementPR(legacyFeatureGateWithoutEnhancement). - enableIn(configv1.DevPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade()). mustRegister() FeatureGateMinimumKubeletVersion = newFeatureGate("MinimumKubeletVersion"). reportProblemsToJiraComponent("Node"). contactPerson("haircommander"). productScope(ocpSpecific). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade(), inTechPreviewNoUpgrade()). enhancementPR("https://github.com/openshift/enhancements/pull/1697"). mustRegister() @@ -647,7 +677,7 @@ var ( contactPerson("yanhli"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1711"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade(), inTechPreviewNoUpgrade()). mustRegister() FeatureGateKMSEncryptionProvider = newFeatureGate("KMSEncryptionProvider"). @@ -655,7 +685,7 @@ var ( contactPerson("swghosh"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1682"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade(), inTechPreviewNoUpgrade()). mustRegister() FeatureGateHighlyAvailableArbiter = newFeatureGate("HighlyAvailableArbiter"). @@ -663,7 +693,7 @@ var ( contactPerson("eggfoobar"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1674"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateCVOConfiguration = newFeatureGate("ClusterVersionOperatorConfiguration"). @@ -671,7 +701,7 @@ var ( contactPerson("dhurta"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1492"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateGCPCustomAPIEndpoints = newFeatureGate("GCPCustomAPIEndpoints"). @@ -679,7 +709,7 @@ var ( contactPerson("barbacbd"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1492"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateDyanmicServiceEndpointIBMCloud = newFeatureGate("DyanmicServiceEndpointIBMCloud"). @@ -687,7 +717,7 @@ var ( contactPerson("jared-hayes-dev"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1712"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateSELinuxMount = newFeatureGate("SELinuxMount"). @@ -695,7 +725,7 @@ var ( contactPerson("jsafrane"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/1710"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateDualReplica = newFeatureGate("DualReplica"). @@ -703,7 +733,7 @@ var ( contactPerson("jaypoulz"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1675"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateGatewayAPIController = newFeatureGate("GatewayAPIController"). @@ -716,7 +746,7 @@ var ( // A dedicated feature gate now controls the Gateway Controller to distinguish // its production readiness from that of the CRDs. enhancementPR("https://github.com/openshift/enhancements/pull/1756"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureShortCertRotation = newFeatureGate("ShortCertRotation"). @@ -731,7 +761,7 @@ var ( contactPerson("rbednar"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1748"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAzureMultiDisk = newFeatureGate("AzureMultiDisk"). @@ -739,7 +769,7 @@ var ( contactPerson("jcpowermac"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1779"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateStoragePerformantSecurityPolicy = newFeatureGate("StoragePerformantSecurityPolicy"). @@ -747,7 +777,7 @@ var ( contactPerson("hekumar"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1804"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMultiDiskSetup = newFeatureGate("MultiDiskSetup"). @@ -755,7 +785,7 @@ var ( contactPerson("jcpowermac"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1805"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAWSDedicatedHosts = newFeatureGate("AWSDedicatedHosts"). @@ -763,7 +793,7 @@ var ( contactPerson("rvanderp3"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1781"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateVSphereMixedNodeEnv = newFeatureGate("VSphereMixedNodeEnv"). @@ -771,7 +801,7 @@ var ( contactPerson("vr4manta"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1772"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGatePreconfiguredUDNAddresses = newFeatureGate("PreconfiguredUDNAddresses"). @@ -779,7 +809,7 @@ var ( contactPerson("kyrtapz"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1793"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAWSServiceLBNetworkSecurityGroup = newFeatureGate("AWSServiceLBNetworkSecurityGroup"). @@ -787,7 +817,7 @@ var ( contactPerson("mtulio"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1802"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateImageVolume = newFeatureGate("ImageVolume"). @@ -795,7 +825,7 @@ var ( contactPerson("haircommander"). productScope(kubernetes). enhancementPR("https://github.com/openshift/enhancements/pull/1792"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateNoRegistryClusterInstall = newFeatureGate("NoRegistryClusterInstall"). @@ -803,7 +833,7 @@ var ( contactPerson("andfasano"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1821"). - enableForClusterProfile(SelfManaged, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inClusterProfile(SelfManaged), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateGCPClusterHostedDNSInstall = newFeatureGate("GCPClusterHostedDNSInstall"). @@ -811,7 +841,7 @@ var ( contactPerson("barbacbd"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1468"). - enableIn(configv1.Default, configv1.OKD, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAWSClusterHostedDNSInstall = newFeatureGate("AWSClusterHostedDNSInstall"). @@ -819,7 +849,7 @@ var ( contactPerson("barbacbd"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1468"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateGCPCustomAPIEndpointsInstall = newFeatureGate("GCPCustomAPIEndpointsInstall"). @@ -827,7 +857,7 @@ var ( contactPerson("barbacbd"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1492"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateIrreconcilableMachineConfig = newFeatureGate("IrreconcilableMachineConfig"). @@ -835,14 +865,14 @@ var ( contactPerson("pabrodri"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1785"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAWSDualStackInstall = newFeatureGate("AWSDualStackInstall"). reportProblemsToJiraComponent("Installer"). contactPerson("sadasu"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1806"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateAzureDualStackInstall = newFeatureGate("AzureDualStackInstall"). @@ -850,7 +880,7 @@ var ( contactPerson("jhixson74"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1806"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateGCPDualStackInstall = newFeatureGate("GCPDualStackInstall"). @@ -858,7 +888,7 @@ var ( contactPerson("barbacbd"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1806"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureCBORServingAndStorage = newFeatureGate("CBORServingAndStorage"). @@ -866,7 +896,7 @@ var ( contactPerson("benluddy"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/4222"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureCBORClientsAllowCBOR = newFeatureGate("ClientsAllowCBOR"). @@ -881,7 +911,7 @@ var ( contactPerson("benluddy"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/4222"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureEventTTL = newFeatureGate("EventTTL"). @@ -889,7 +919,7 @@ var ( contactPerson("tjungblu"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1857"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateMutableCSINodeAllocatableCount = newFeatureGate("MutableCSINodeAllocatableCount"). @@ -897,14 +927,14 @@ var ( contactPerson("jsafrane"). productScope(kubernetes). enhancementPR("https://github.com/kubernetes/enhancements/issues/4876"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateOSStreams = newFeatureGate("OSStreams"). reportProblemsToJiraComponent("MachineConfigOperator"). contactPerson("pabrodri"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1874"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateCRDCompatibilityRequirementOperator = newFeatureGate("CRDCompatibilityRequirementOperator"). @@ -912,14 +942,14 @@ var ( contactPerson("ddonati"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1845"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateOnPremDNSRecords = newFeatureGate("OnPremDNSRecords"). reportProblemsToJiraComponent("Networking / On-Prem DNS"). contactPerson("bnemec"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1803"). - enableIn(configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade). + enable(inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() FeatureGateProvisioningRequestAvailable = newFeatureGate("ProvisioningRequestAvailable"). @@ -927,7 +957,7 @@ var ( contactPerson("elmiko"). productScope(ocpSpecific). enhancementPR("https://github.com/openshift/enhancements/pull/1752"). - enableIn(configv1.DevPreviewNoUpgrade). + enable(inDevPreviewNoUpgrade()). mustRegister() FeatureGateHyperShiftOnlyDynamicResourceAllocation = newFeatureGate("HyperShiftOnlyDynamicResourceAllocation"). @@ -935,6 +965,6 @@ var ( contactPerson("csrwng"). productScope(ocpSpecific). enhancementPR("https://github.com/kubernetes/enhancements/issues/4381"). - enableForClusterProfile(Hypershift, configv1.DevPreviewNoUpgrade, configv1.TechPreviewNoUpgrade, configv1.Default, configv1.OKD). + enable(inClusterProfile(Hypershift), inDefault(), inOKD(), inTechPreviewNoUpgrade(), inDevPreviewNoUpgrade()). mustRegister() ) diff --git a/features/okd_featureset_parity_test.go b/features/okd_featureset_parity_test.go index d2fe2e7face..76aeca06864 100644 --- a/features/okd_featureset_parity_test.go +++ b/features/okd_featureset_parity_test.go @@ -16,38 +16,40 @@ import ( func TestOKDHasAllDefaultFeatureGates(t *testing.T) { allFeatureSets := AllFeatureSets() - // Check each cluster profile - for clusterProfile, byFeatureSet := range allFeatureSets { - defaultGates, hasDefault := byFeatureSet[configv1.Default] - okdGates, hasOKD := byFeatureSet[configv1.OKD] - - if !hasOKD || !hasDefault { - continue - } - - // Collect enabled feature gate names from Default and OKD - defaultEnabled := sets.NewString() - for _, gate := range defaultGates.Enabled { - defaultEnabled.Insert(string(gate.FeatureGateAttributes.Name)) - } - - okdEnabled := sets.NewString() - for _, gate := range okdGates.Enabled { - okdEnabled.Insert(string(gate.FeatureGateAttributes.Name)) - } - - // Check that all Default featuregates are in OKD - missingInOKD := defaultEnabled.Difference(okdEnabled) - - if missingInOKD.Len() > 0 { - missingList := missingInOKD.List() - sort.Strings(missingList) - - t.Errorf("ClusterProfile %q: OKD featureset is missing %d featuregate(s) that are enabled in Default:\n - %s\n\nAll featuregates enabled in Default must also be enabled in OKD.", - clusterProfile, - missingInOKD.Len(), - strings.Join(missingList, "\n - "), - ) + for _, byClusterProfileByFeatureSet := range allFeatureSets { + // Check each cluster profile + for clusterProfile, byFeatureSet := range byClusterProfileByFeatureSet { + defaultGates, hasDefault := byFeatureSet[configv1.Default] + okdGates, hasOKD := byFeatureSet[configv1.OKD] + + if !hasOKD || !hasDefault { + continue + } + + // Collect enabled feature gate names from Default and OKD + defaultEnabled := sets.NewString() + for _, gate := range defaultGates.Enabled { + defaultEnabled.Insert(string(gate.FeatureGateAttributes.Name)) + } + + okdEnabled := sets.NewString() + for _, gate := range okdGates.Enabled { + okdEnabled.Insert(string(gate.FeatureGateAttributes.Name)) + } + + // Check that all Default featuregates are in OKD + missingInOKD := defaultEnabled.Difference(okdEnabled) + + if missingInOKD.Len() > 0 { + missingList := missingInOKD.List() + sort.Strings(missingList) + + t.Errorf("ClusterProfile %q: OKD featureset is missing %d featuregate(s) that are enabled in Default:\n - %s\n\nAll featuregates enabled in Default must also be enabled in OKD.", + clusterProfile, + missingInOKD.Len(), + strings.Join(missingList, "\n - "), + ) + } } } } diff --git a/features/util.go b/features/util.go index 8606b6befd5..5d9725f6fb6 100644 --- a/features/util.go +++ b/features/util.go @@ -6,6 +6,7 @@ import ( "strings" configv1 "github.com/openshift/api/config/v1" + "k8s.io/apimachinery/pkg/util/sets" ) // FeatureGateDescription is a golang-only interface used to contains details for a feature gate. @@ -45,6 +46,46 @@ var ( kubernetes = OwningProduct("Kubernetes") ) +type featureGateEnableOption func(s *featureGateStatus) + +func inVersion(version uint64) featureGateEnableOption { + return func(s *featureGateStatus) { + s.version.Insert(version) + } +} + +func inClusterProfile(clusterProfile ClusterProfileName) featureGateEnableOption { + return func(s *featureGateStatus) { + s.clusterProfile.Insert(clusterProfile) + } +} + +func withFeatureSet(featureSet configv1.FeatureSet) featureGateEnableOption { + return func(s *featureGateStatus) { + s.featureSets.Insert(featureSet) + } +} + +func inDefault() featureGateEnableOption { + return withFeatureSet(configv1.Default) +} + +func inTechPreviewNoUpgrade() featureGateEnableOption { + return withFeatureSet(configv1.TechPreviewNoUpgrade) +} + +func inDevPreviewNoUpgrade() featureGateEnableOption { + return withFeatureSet(configv1.DevPreviewNoUpgrade) +} + +func inCustomNoUpgrade() featureGateEnableOption { + return withFeatureSet(configv1.CustomNoUpgrade) +} + +func inOKD() featureGateEnableOption { + return withFeatureSet(configv1.OKD) +} + type featureGateBuilder struct { name string owningJiraComponent string @@ -52,8 +93,25 @@ type featureGateBuilder struct { owningProduct OwningProduct enhancementPRURL string + status []featureGateStatus + statusByClusterProfileByFeatureSet map[ClusterProfileName]map[configv1.FeatureSet]bool } +type featureGateStatus struct { + version sets.Set[uint64] + clusterProfile sets.Set[ClusterProfileName] + featureSets sets.Set[configv1.FeatureSet] +} + +func (s *featureGateStatus) isEnabled(version uint64, clusterProfile ClusterProfileName, featureSet configv1.FeatureSet) bool { + // If either version or clusterprofile are empty, match all. + matchesVersion := len(s.version) == 0 || s.version.Has(version) + matchesClusterProfile := len(s.clusterProfile) == 0 || s.clusterProfile.Has(clusterProfile) + + matchesFeatureSet := s.featureSets.Has(featureSet) + + return matchesVersion && matchesClusterProfile && matchesFeatureSet +} const ( legacyFeatureGateWithoutEnhancement = "FeatureGate predates 4.18" @@ -95,19 +153,19 @@ func (b *featureGateBuilder) enhancementPR(url string) *featureGateBuilder { return b } -func (b *featureGateBuilder) enableIn(featureSets ...configv1.FeatureSet) *featureGateBuilder { - for clusterProfile := range b.statusByClusterProfileByFeatureSet { - for _, featureSet := range featureSets { - b.statusByClusterProfileByFeatureSet[clusterProfile][featureSet] = true - } +func (b *featureGateBuilder) enable(opts ...featureGateEnableOption) *featureGateBuilder { + status := featureGateStatus{ + version: sets.New[uint64](), + clusterProfile: sets.New[ClusterProfileName](), + featureSets: sets.New[configv1.FeatureSet](), } - return b -} -func (b *featureGateBuilder) enableForClusterProfile(clusterProfile ClusterProfileName, featureSets ...configv1.FeatureSet) *featureGateBuilder { - for _, featureSet := range featureSets { - b.statusByClusterProfileByFeatureSet[clusterProfile][featureSet] = true + for _, opt := range opts { + opt(&status) } + + b.status = append(b.status, status) + return b } @@ -144,33 +202,8 @@ func (b *featureGateBuilder) register() (configv1.FeatureGateName, error) { } featureGateName := configv1.FeatureGateName(b.name) - description := FeatureGateDescription{ - FeatureGateAttributes: configv1.FeatureGateAttributes{ - Name: featureGateName, - }, - OwningJiraComponent: b.owningJiraComponent, - ResponsiblePerson: b.responsiblePerson, - OwningProduct: b.owningProduct, - EnhancementPR: b.enhancementPRURL, - } - - // statusByClusterProfileByFeatureSet is initialized by constructor to be false for every combination - for clusterProfile, byFeatureSet := range b.statusByClusterProfileByFeatureSet { - for featureSet, enabled := range byFeatureSet { - if _, ok := allFeatureGates[clusterProfile]; !ok { - allFeatureGates[clusterProfile] = map[configv1.FeatureSet]*FeatureGateEnabledDisabled{} - } - if _, ok := allFeatureGates[clusterProfile][featureSet]; !ok { - allFeatureGates[clusterProfile][featureSet] = &FeatureGateEnabledDisabled{} - } - - if enabled { - allFeatureGates[clusterProfile][featureSet].Enabled = append(allFeatureGates[clusterProfile][featureSet].Enabled, description) - } else { - allFeatureGates[clusterProfile][featureSet].Disabled = append(allFeatureGates[clusterProfile][featureSet].Disabled, description) - } - } - } + + allFeatureGates[featureGateName] = b.status return featureGateName, nil } diff --git a/go.mod b/go.mod index 2aaaa3a4ba7..fae4c403d8d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/openshift/api go 1.24.0 require ( + github.com/blang/semver/v4 v4.0.0 github.com/gogo/protobuf v1.3.2 golang.org/x/tools v0.26.0 k8s.io/api v0.34.1 diff --git a/go.sum b/go.sum index 4f6574fe053..ec57e923e2c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/hack/update-payload-featuregates.sh b/hack/update-payload-featuregates.sh index 2c916fb4333..e96a14c73c1 100755 --- a/hack/update-payload-featuregates.sh +++ b/hack/update-payload-featuregates.sh @@ -2,6 +2,7 @@ source "$(dirname "${BASH_SOURCE}")/lib/init.sh" +rm -f ./payload-manifests/featuregates/* go run --mod=vendor -trimpath github.com/openshift/api/payload-command/cmd/write-available-featuresets --asset-output-dir=./payload-manifests/featuregates # Build codegen-crds when it's not present and not overridden for a specific file. diff --git a/payload-command/render/render.go b/payload-command/render/render.go index 3fa9c4bc6a3..58d74b53a1c 100644 --- a/payload-command/render/render.go +++ b/payload-command/render/render.go @@ -3,12 +3,14 @@ package render import ( "flag" "fmt" - "github.com/openshift/api/features" "os" "path/filepath" "sort" "strings" + "github.com/blang/semver/v4" + "github.com/openshift/api/features" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -68,6 +70,11 @@ func (o *RenderOpts) Run() error { } clusterProfileAnnotationName := fmt.Sprintf("include.release.openshift.io/%s", o.UnprefixedClusterProfile) + version, err := semver.ParseTolerant(o.PayloadVersion) + if err != nil { + return fmt.Errorf("problem parsing payload version: %w", err) + } + for _, featureGateFile := range featureGateFiles { uncastObj, err := featureGateFile.GetDecodedObj() if err != nil { @@ -84,7 +91,7 @@ func (o *RenderOpts) Run() error { if featureGates.Spec.FeatureSet == configv1.CustomNoUpgrade { featureSet = string(featureGates.Spec.FeatureSet) - renderedFeatureGates, err := renderCustomNoUpgradeFeatureGate(featureGates, features.ClusterProfileName(clusterProfileAnnotationName), o.PayloadVersion) + renderedFeatureGates, err := renderCustomNoUpgradeFeatureGate(featureGates, features.ClusterProfileName(clusterProfileAnnotationName), o.PayloadVersion, version.Major) if err != nil { return err } @@ -105,10 +112,7 @@ func (o *RenderOpts) Run() error { featureGates.Annotations[clusterProfileAnnotationName] = "true" } - featureGateStatus, err := features.FeatureSets(features.ClusterProfileName(clusterProfileAnnotationName), featureGates.Spec.FeatureSet) - if err != nil { - return fmt.Errorf("unable to resolve featureGateStatus: %w", err) - } + featureGateStatus := features.FeatureSets(version.Major, features.ClusterProfileName(clusterProfileAnnotationName), featureGates.Spec.FeatureSet) currentDetails := FeaturesGateDetailsFromFeatureSets(featureGateStatus, o.PayloadVersion) featureGates.Status.FeatureGates = []configv1.FeatureGateDetails{*currentDetails} @@ -133,7 +137,7 @@ func (o *RenderOpts) Run() error { return nil } -func renderCustomNoUpgradeFeatureGate(in *configv1.FeatureGate, clusterProfile features.ClusterProfileName, payloadVersion string) (*configv1.FeatureGate, error) { +func renderCustomNoUpgradeFeatureGate(in *configv1.FeatureGate, clusterProfile features.ClusterProfileName, payloadVersion string, payloadMajorVersion uint64) (*configv1.FeatureGate, error) { if in.Spec.FeatureSet != configv1.CustomNoUpgrade { return nil, fmt.Errorf("not CustomNoUpgrade") } @@ -162,10 +166,7 @@ func renderCustomNoUpgradeFeatureGate(in *configv1.FeatureGate, clusterProfile f }) } - defaultFeatureGates, err := features.FeatureSets(clusterProfile, configv1.Default) - if err != nil { - return nil, err - } + defaultFeatureGates := features.FeatureSets(payloadMajorVersion, clusterProfile, configv1.Default) enabled := []configv1.FeatureGateAttributes{} disabled := []configv1.FeatureGateAttributes{} diff --git a/payload-command/render/write_featureset.go b/payload-command/render/write_featureset.go index 009d31f6b78..0de99e19cf5 100644 --- a/payload-command/render/write_featureset.go +++ b/payload-command/render/write_featureset.go @@ -3,13 +3,15 @@ package render import ( "flag" "fmt" + "os" + "path/filepath" + "sort" + "strings" + configv1 "github.com/openshift/api/config/v1" "github.com/openshift/api/features" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" - "os" - "path/filepath" - "strings" ) var ( @@ -19,6 +21,122 @@ var ( } ) +// versionGroup represents a group of consecutive versions with identical FeatureGate content +type versionGroup struct { + startVersion uint64 + endVersion uint64 + content *features.FeatureGateEnabledDisabled +} + +// versionRangeString returns a string representation of the version range for filenames +func (vg versionGroup) versionRangeString() string { + if vg.startVersion == vg.endVersion { + return fmt.Sprintf("%d", vg.startVersion) + } + return fmt.Sprintf("%d-%d", vg.startVersion, vg.endVersion) +} + +func (vg versionGroup) versions() []string { + versions := []string{} + + for version := vg.startVersion; version <= vg.endVersion; version++ { + versions = append(versions, fmt.Sprintf("%d", version)) + } + return versions +} + +// featureGateContentEqual compares two FeatureGateEnabledDisabled structs for content equality +func featureGateContentEqual(a, b *features.FeatureGateEnabledDisabled) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + + // Create sorted slices of feature gate names for comparison + aEnabledNames := sets.New(a.Enabled...) + bEnabledNames := sets.New(b.Enabled...) + + if !aEnabledNames.Equal(bEnabledNames) { + return false + } + + aDisabledNames := sets.New(a.Disabled...) + bDisabledNames := sets.New(b.Disabled...) + if !aDisabledNames.Equal(bDisabledNames) { + return false + } + + return true +} + +// consolidateVersions groups consecutive versions with identical content +func consolidateVersions( + statusByVersionByClusterProfileByFeatureSet map[uint64]map[features.ClusterProfileName]map[configv1.FeatureSet]*features.FeatureGateEnabledDisabled, + clusterProfile features.ClusterProfileName, + featureSetName configv1.FeatureSet, +) []versionGroup { + var versionContents []versionGroup + for version, byClusterProfile := range statusByVersionByClusterProfileByFeatureSet { + if byFeatureSet, exists := byClusterProfile[clusterProfile]; exists { + if content, exists := byFeatureSet[featureSetName]; exists { + versionContents = append(versionContents, versionGroup{ + startVersion: version, + content: content, + }) + } + } + } + + // Sort by version + sort.Slice(versionContents, func(i, j int) bool { + return versionContents[i].startVersion < versionContents[j].startVersion + }) + + var groups []versionGroup + + currentGroup := versionContents[0] + currentGroup.endVersion = currentGroup.startVersion + + for i := 1; i < len(versionContents); i++ { + curr := versionContents[i] + + // Check if current version has same content as current group and is consecutive + if featureGateContentEqual(currentGroup.content, curr.content) && + curr.startVersion == currentGroup.endVersion+1 { + // Extend current group + currentGroup.endVersion = curr.startVersion + } else { + // Finalize current group and start new one + groups = append(groups, currentGroup) + currentGroup = curr + currentGroup.endVersion = currentGroup.startVersion + } + } + + // Add the final group + groups = append(groups, currentGroup) + + return groups +} + +// generateConsolidatedFilename creates filename for a version group +func generateConsolidatedFilename(group versionGroup, clusterProfile features.ClusterProfileName, featureSetName configv1.FeatureSet) string { + clusterProfileShort := clusterProfileToShortName[clusterProfile] + featureSetStr := string(featureSetName) + if len(featureSetStr) == 0 { + featureSetStr = "Default" + } + + return fmt.Sprintf("featureGate-%s-%s-%s.yaml", group.versionRangeString(), clusterProfileShort, featureSetStr) +} + +type profileFeatureSet struct { + clusterProfile features.ClusterProfileName + featureSetName configv1.FeatureSet +} + // WriteFeatureSets holds values to drive the render command. type WriteFeatureSets struct { PayloadVersion string @@ -47,21 +165,51 @@ func (o *WriteFeatureSets) Run() error { return err } - statusByClusterProfileByFeatureSet := features.AllFeatureSets() + statusByVersionByClusterProfileByFeatureSet := features.AllFeatureSets() newLegacyFeatureGates := sets.Set[string]{} - for clusterProfile, byFeatureSet := range statusByClusterProfileByFeatureSet { - for featureSetName, featureGateStatuses := range byFeatureSet { - for _, curr := range featureGateStatuses.Enabled { - if curr.EnhancementPR == "FeatureGate predates 4.18" { - newLegacyFeatureGates.Insert(string(curr.FeatureGateAttributes.Name)) + + // First, collect legacy feature gates from all versions (unchanged from original logic) + for _, statusByClusterProfileByFeatureSet := range statusByVersionByClusterProfileByFeatureSet { + for _, byFeatureSet := range statusByClusterProfileByFeatureSet { + for _, featureGateStatuses := range byFeatureSet { + for _, curr := range featureGateStatuses.Enabled { + if curr.EnhancementPR == "FeatureGate predates 4.18" { + newLegacyFeatureGates.Insert(string(curr.FeatureGateAttributes.Name)) + } } - } - for _, curr := range featureGateStatuses.Disabled { - if curr.EnhancementPR == "FeatureGate predates 4.18" { - newLegacyFeatureGates.Insert(string(curr.FeatureGateAttributes.Name)) + for _, curr := range featureGateStatuses.Disabled { + if curr.EnhancementPR == "FeatureGate predates 4.18" { + newLegacyFeatureGates.Insert(string(curr.FeatureGateAttributes.Name)) + } } } - currentDetails := FeaturesGateDetailsFromFeatureSets(featureGateStatuses, o.PayloadVersion) + } + } + + // Create consolidated groups by combining cluster profiles and feature sets + consolidatedGroups := make(map[profileFeatureSet][]versionGroup) + + // Gather all cluster profiles and feature sets to iterate over + allClusterProfiles := features.AllClusterProfiles + allFeatureSets := configv1.AllFixedFeatureSets + + for _, clusterProfile := range allClusterProfiles { + for _, featureSetName := range allFeatureSets { + key := profileFeatureSet{ + clusterProfile: clusterProfile, + featureSetName: featureSetName, + } + groups := consolidateVersions(statusByVersionByClusterProfileByFeatureSet, clusterProfile, featureSetName) + if len(groups) > 0 { + consolidatedGroups[key] = groups + } + } + } + + // Generate files for each consolidated group + for groupKey, versionGroups := range consolidatedGroups { + for _, group := range versionGroups { + currentDetails := FeaturesGateDetailsFromFeatureSets(group.content, o.PayloadVersion) featureGateInstance := &configv1.FeatureGate{ ObjectMeta: metav1.ObjectMeta{ @@ -70,12 +218,13 @@ func (o *WriteFeatureSets) Run() error { // we can't do this because it will get the manifest included by the CVO and that isn't what we want // this makes it interesting to indicate which cluster-profile the cluster-config-operator should use //string(clusterProfile): "true", - string(clusterProfile): "false-except-for-the-config-operator", + string(groupKey.clusterProfile): "false-except-for-the-config-operator", + "release.openshift.io/major-version": strings.Join(group.versions(), ","), }, }, Spec: configv1.FeatureGateSpec{ FeatureGateSelection: configv1.FeatureGateSelection{ - FeatureSet: featureSetName, + FeatureSet: groupKey.featureSetName, }, }, Status: configv1.FeatureGateStatus{ @@ -86,10 +235,7 @@ func (o *WriteFeatureSets) Run() error { } featureGateOutBytes := writeFeatureGateV1OrDie(featureGateInstance) - featureSetFileName := fmt.Sprintf("featureGate-%s-%s.yaml", clusterProfileToShortName[clusterProfile], featureSetName) - if len(featureSetName) == 0 { - featureSetFileName = fmt.Sprintf("featureGate-%s-%s.yaml", clusterProfileToShortName[clusterProfile], "Default") - } + featureSetFileName := generateConsolidatedFilename(group, groupKey.clusterProfile, groupKey.featureSetName) destFile := filepath.Join(o.AssetOutputDir, featureSetFileName) if err := os.WriteFile(destFile, []byte(featureGateOutBytes), 0644); err != nil { diff --git a/vendor/github.com/blang/semver/v4/LICENSE b/vendor/github.com/blang/semver/v4/LICENSE new file mode 100644 index 00000000000..5ba5c86fcb0 --- /dev/null +++ b/vendor/github.com/blang/semver/v4/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Benedikt Lang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/github.com/blang/semver/v4/json.go b/vendor/github.com/blang/semver/v4/json.go new file mode 100644 index 00000000000..a74bf7c4494 --- /dev/null +++ b/vendor/github.com/blang/semver/v4/json.go @@ -0,0 +1,23 @@ +package semver + +import ( + "encoding/json" +) + +// MarshalJSON implements the encoding/json.Marshaler interface. +func (v Version) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements the encoding/json.Unmarshaler interface. +func (v *Version) UnmarshalJSON(data []byte) (err error) { + var versionString string + + if err = json.Unmarshal(data, &versionString); err != nil { + return + } + + *v, err = Parse(versionString) + + return +} diff --git a/vendor/github.com/blang/semver/v4/range.go b/vendor/github.com/blang/semver/v4/range.go new file mode 100644 index 00000000000..95f7139b977 --- /dev/null +++ b/vendor/github.com/blang/semver/v4/range.go @@ -0,0 +1,416 @@ +package semver + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +type wildcardType int + +const ( + noneWildcard wildcardType = iota + majorWildcard wildcardType = 1 + minorWildcard wildcardType = 2 + patchWildcard wildcardType = 3 +) + +func wildcardTypefromInt(i int) wildcardType { + switch i { + case 1: + return majorWildcard + case 2: + return minorWildcard + case 3: + return patchWildcard + default: + return noneWildcard + } +} + +type comparator func(Version, Version) bool + +var ( + compEQ comparator = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 0 + } + compNE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) != 0 + } + compGT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 1 + } + compGE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) >= 0 + } + compLT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == -1 + } + compLE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) <= 0 + } +) + +type versionRange struct { + v Version + c comparator +} + +// rangeFunc creates a Range from the given versionRange. +func (vr *versionRange) rangeFunc() Range { + return Range(func(v Version) bool { + return vr.c(v, vr.v) + }) +} + +// Range represents a range of versions. +// A Range can be used to check if a Version satisfies it: +// +// range, err := semver.ParseRange(">1.0.0 <2.0.0") +// range(semver.MustParse("1.1.1") // returns true +type Range func(Version) bool + +// OR combines the existing Range with another Range using logical OR. +func (rf Range) OR(f Range) Range { + return Range(func(v Version) bool { + return rf(v) || f(v) + }) +} + +// AND combines the existing Range with another Range using logical AND. +func (rf Range) AND(f Range) Range { + return Range(func(v Version) bool { + return rf(v) && f(v) + }) +} + +// ParseRange parses a range and returns a Range. +// If the range could not be parsed an error is returned. +// +// Valid ranges are: +// - "<1.0.0" +// - "<=1.0.0" +// - ">1.0.0" +// - ">=1.0.0" +// - "1.0.0", "=1.0.0", "==1.0.0" +// - "!1.0.0", "!=1.0.0" +// +// A Range can consist of multiple ranges separated by space: +// Ranges can be linked by logical AND: +// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0" +// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2 +// +// Ranges can also be linked by logical OR: +// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x" +// +// AND has a higher precedence than OR. It's not possible to use brackets. +// +// Ranges can be combined by both AND and OR +// +// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` +func ParseRange(s string) (Range, error) { + parts := splitAndTrim(s) + orParts, err := splitORParts(parts) + if err != nil { + return nil, err + } + expandedParts, err := expandWildcardVersion(orParts) + if err != nil { + return nil, err + } + var orFn Range + for _, p := range expandedParts { + var andFn Range + for _, ap := range p { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + vr, err := buildVersionRange(opStr, vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err) + } + rf := vr.rangeFunc() + + // Set function + if andFn == nil { + andFn = rf + } else { // Combine with existing function + andFn = andFn.AND(rf) + } + } + if orFn == nil { + orFn = andFn + } else { + orFn = orFn.OR(andFn) + } + + } + return orFn, nil +} + +// splitORParts splits the already cleaned parts by '||'. +// Checks for invalid positions of the operator and returns an +// error if found. +func splitORParts(parts []string) ([][]string, error) { + var ORparts [][]string + last := 0 + for i, p := range parts { + if p == "||" { + if i == 0 { + return nil, fmt.Errorf("First element in range is '||'") + } + ORparts = append(ORparts, parts[last:i]) + last = i + 1 + } + } + if last == len(parts) { + return nil, fmt.Errorf("Last element in range is '||'") + } + ORparts = append(ORparts, parts[last:]) + return ORparts, nil +} + +// buildVersionRange takes a slice of 2: operator and version +// and builds a versionRange, otherwise an error. +func buildVersionRange(opStr, vStr string) (*versionRange, error) { + c := parseComparator(opStr) + if c == nil { + return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, "")) + } + v, err := Parse(vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err) + } + + return &versionRange{ + v: v, + c: c, + }, nil + +} + +// inArray checks if a byte is contained in an array of bytes +func inArray(s byte, list []byte) bool { + for _, el := range list { + if el == s { + return true + } + } + return false +} + +// splitAndTrim splits a range string by spaces and cleans whitespaces +func splitAndTrim(s string) (result []string) { + last := 0 + var lastChar byte + excludeFromSplit := []byte{'>', '<', '='} + for i := 0; i < len(s); i++ { + if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) { + if last < i-1 { + result = append(result, s[last:i]) + } + last = i + 1 + } else if s[i] != ' ' { + lastChar = s[i] + } + } + if last < len(s)-1 { + result = append(result, s[last:]) + } + + for i, v := range result { + result[i] = strings.Replace(v, " ", "", -1) + } + + // parts := strings.Split(s, " ") + // for _, x := range parts { + // if s := strings.TrimSpace(x); len(s) != 0 { + // result = append(result, s) + // } + // } + return +} + +// splitComparatorVersion splits the comparator from the version. +// Input must be free of leading or trailing spaces. +func splitComparatorVersion(s string) (string, string, error) { + i := strings.IndexFunc(s, unicode.IsDigit) + if i == -1 { + return "", "", fmt.Errorf("Could not get version from string: %q", s) + } + return strings.TrimSpace(s[0:i]), s[i:], nil +} + +// getWildcardType will return the type of wildcard that the +// passed version contains +func getWildcardType(vStr string) wildcardType { + parts := strings.Split(vStr, ".") + nparts := len(parts) + wildcard := parts[nparts-1] + + possibleWildcardType := wildcardTypefromInt(nparts) + if wildcard == "x" { + return possibleWildcardType + } + + return noneWildcard +} + +// createVersionFromWildcard will convert a wildcard version +// into a regular version, replacing 'x's with '0's, handling +// special cases like '1.x.x' and '1.x' +func createVersionFromWildcard(vStr string) string { + // handle 1.x.x + vStr2 := strings.Replace(vStr, ".x.x", ".x", 1) + vStr2 = strings.Replace(vStr2, ".x", ".0", 1) + parts := strings.Split(vStr2, ".") + + // handle 1.x + if len(parts) == 2 { + return vStr2 + ".0" + } + + return vStr2 +} + +// incrementMajorVersion will increment the major version +// of the passed version +func incrementMajorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[0]) + if err != nil { + return "", err + } + parts[0] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// incrementMajorVersion will increment the minor version +// of the passed version +func incrementMinorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[1]) + if err != nil { + return "", err + } + parts[1] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// expandWildcardVersion will expand wildcards inside versions +// following these rules: +// +// * when dealing with patch wildcards: +// >= 1.2.x will become >= 1.2.0 +// <= 1.2.x will become < 1.3.0 +// > 1.2.x will become >= 1.3.0 +// < 1.2.x will become < 1.2.0 +// != 1.2.x will become < 1.2.0 >= 1.3.0 +// +// * when dealing with minor wildcards: +// >= 1.x will become >= 1.0.0 +// <= 1.x will become < 2.0.0 +// > 1.x will become >= 2.0.0 +// < 1.0 will become < 1.0.0 +// != 1.x will become < 1.0.0 >= 2.0.0 +// +// * when dealing with wildcards without +// version operator: +// 1.2.x will become >= 1.2.0 < 1.3.0 +// 1.x will become >= 1.0.0 < 2.0.0 +func expandWildcardVersion(parts [][]string) ([][]string, error) { + var expandedParts [][]string + for _, p := range parts { + var newParts []string + for _, ap := range p { + if strings.Contains(ap, "x") { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + + versionWildcardType := getWildcardType(vStr) + flatVersion := createVersionFromWildcard(vStr) + + var resultOperator string + var shouldIncrementVersion bool + switch opStr { + case ">": + resultOperator = ">=" + shouldIncrementVersion = true + case ">=": + resultOperator = ">=" + case "<": + resultOperator = "<" + case "<=": + resultOperator = "<" + shouldIncrementVersion = true + case "", "=", "==": + newParts = append(newParts, ">="+flatVersion) + resultOperator = "<" + shouldIncrementVersion = true + case "!=", "!": + newParts = append(newParts, "<"+flatVersion) + resultOperator = ">=" + shouldIncrementVersion = true + } + + var resultVersion string + if shouldIncrementVersion { + switch versionWildcardType { + case patchWildcard: + resultVersion, _ = incrementMinorVersion(flatVersion) + case minorWildcard: + resultVersion, _ = incrementMajorVersion(flatVersion) + } + } else { + resultVersion = flatVersion + } + + ap = resultOperator + resultVersion + } + newParts = append(newParts, ap) + } + expandedParts = append(expandedParts, newParts) + } + + return expandedParts, nil +} + +func parseComparator(s string) comparator { + switch s { + case "==": + fallthrough + case "": + fallthrough + case "=": + return compEQ + case ">": + return compGT + case ">=": + return compGE + case "<": + return compLT + case "<=": + return compLE + case "!": + fallthrough + case "!=": + return compNE + } + + return nil +} + +// MustParseRange is like ParseRange but panics if the range cannot be parsed. +func MustParseRange(s string) Range { + r, err := ParseRange(s) + if err != nil { + panic(`semver: ParseRange(` + s + `): ` + err.Error()) + } + return r +} diff --git a/vendor/github.com/blang/semver/v4/semver.go b/vendor/github.com/blang/semver/v4/semver.go new file mode 100644 index 00000000000..307de610f92 --- /dev/null +++ b/vendor/github.com/blang/semver/v4/semver.go @@ -0,0 +1,476 @@ +package semver + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + numbers string = "0123456789" + alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + alphanum = alphas + numbers +) + +// SpecVersion is the latest fully supported spec version of semver +var SpecVersion = Version{ + Major: 2, + Minor: 0, + Patch: 0, +} + +// Version represents a semver compatible version +type Version struct { + Major uint64 + Minor uint64 + Patch uint64 + Pre []PRVersion + Build []string //No Precedence +} + +// Version to string +func (v Version) String() string { + b := make([]byte, 0, 5) + b = strconv.AppendUint(b, v.Major, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Minor, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Patch, 10) + + if len(v.Pre) > 0 { + b = append(b, '-') + b = append(b, v.Pre[0].String()...) + + for _, pre := range v.Pre[1:] { + b = append(b, '.') + b = append(b, pre.String()...) + } + } + + if len(v.Build) > 0 { + b = append(b, '+') + b = append(b, v.Build[0]...) + + for _, build := range v.Build[1:] { + b = append(b, '.') + b = append(b, build...) + } + } + + return string(b) +} + +// FinalizeVersion discards prerelease and build number and only returns +// major, minor and patch number. +func (v Version) FinalizeVersion() string { + b := make([]byte, 0, 5) + b = strconv.AppendUint(b, v.Major, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Minor, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Patch, 10) + return string(b) +} + +// Equals checks if v is equal to o. +func (v Version) Equals(o Version) bool { + return (v.Compare(o) == 0) +} + +// EQ checks if v is equal to o. +func (v Version) EQ(o Version) bool { + return (v.Compare(o) == 0) +} + +// NE checks if v is not equal to o. +func (v Version) NE(o Version) bool { + return (v.Compare(o) != 0) +} + +// GT checks if v is greater than o. +func (v Version) GT(o Version) bool { + return (v.Compare(o) == 1) +} + +// GTE checks if v is greater than or equal to o. +func (v Version) GTE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// GE checks if v is greater than or equal to o. +func (v Version) GE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// LT checks if v is less than o. +func (v Version) LT(o Version) bool { + return (v.Compare(o) == -1) +} + +// LTE checks if v is less than or equal to o. +func (v Version) LTE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// LE checks if v is less than or equal to o. +func (v Version) LE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// Compare compares Versions v to o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v Version) Compare(o Version) int { + if v.Major != o.Major { + if v.Major > o.Major { + return 1 + } + return -1 + } + if v.Minor != o.Minor { + if v.Minor > o.Minor { + return 1 + } + return -1 + } + if v.Patch != o.Patch { + if v.Patch > o.Patch { + return 1 + } + return -1 + } + + // Quick comparison if a version has no prerelease versions + if len(v.Pre) == 0 && len(o.Pre) == 0 { + return 0 + } else if len(v.Pre) == 0 && len(o.Pre) > 0 { + return 1 + } else if len(v.Pre) > 0 && len(o.Pre) == 0 { + return -1 + } + + i := 0 + for ; i < len(v.Pre) && i < len(o.Pre); i++ { + if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 { + continue + } else if comp == 1 { + return 1 + } else { + return -1 + } + } + + // If all pr versions are the equal but one has further prversion, this one greater + if i == len(v.Pre) && i == len(o.Pre) { + return 0 + } else if i == len(v.Pre) && i < len(o.Pre) { + return -1 + } else { + return 1 + } + +} + +// IncrementPatch increments the patch version +func (v *Version) IncrementPatch() error { + v.Patch++ + return nil +} + +// IncrementMinor increments the minor version +func (v *Version) IncrementMinor() error { + v.Minor++ + v.Patch = 0 + return nil +} + +// IncrementMajor increments the major version +func (v *Version) IncrementMajor() error { + v.Major++ + v.Minor = 0 + v.Patch = 0 + return nil +} + +// Validate validates v and returns error in case +func (v Version) Validate() error { + // Major, Minor, Patch already validated using uint64 + + for _, pre := range v.Pre { + if !pre.IsNum { //Numeric prerelease versions already uint64 + if len(pre.VersionStr) == 0 { + return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr) + } + if !containsOnly(pre.VersionStr, alphanum) { + return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr) + } + } + } + + for _, build := range v.Build { + if len(build) == 0 { + return fmt.Errorf("Build meta data can not be empty %q", build) + } + if !containsOnly(build, alphanum) { + return fmt.Errorf("Invalid character(s) found in build meta data %q", build) + } + } + + return nil +} + +// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error +func New(s string) (*Version, error) { + v, err := Parse(s) + vp := &v + return vp, err +} + +// Make is an alias for Parse, parses version string and returns a validated Version or error +func Make(s string) (Version, error) { + return Parse(s) +} + +// ParseTolerant allows for certain version specifications that do not strictly adhere to semver +// specs to be parsed by this library. It does so by normalizing versions before passing them to +// Parse(). It currently trims spaces, removes a "v" prefix, adds a 0 patch number to versions +// with only major and minor components specified, and removes leading 0s. +func ParseTolerant(s string) (Version, error) { + s = strings.TrimSpace(s) + s = strings.TrimPrefix(s, "v") + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + // Remove leading zeros. + for i, p := range parts { + if len(p) > 1 { + p = strings.TrimLeft(p, "0") + if len(p) == 0 || !strings.ContainsAny(p[0:1], "0123456789") { + p = "0" + p + } + parts[i] = p + } + } + // Fill up shortened versions. + if len(parts) < 3 { + if strings.ContainsAny(parts[len(parts)-1], "+-") { + return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data") + } + for len(parts) < 3 { + parts = append(parts, "0") + } + } + s = strings.Join(parts, ".") + + return Parse(s) +} + +// Parse parses version string and returns a validated Version or error +func Parse(s string) (Version, error) { + if len(s) == 0 { + return Version{}, errors.New("Version string empty") + } + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + if len(parts) != 3 { + return Version{}, errors.New("No Major.Minor.Patch elements found") + } + + // Major + if !containsOnly(parts[0], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0]) + } + if hasLeadingZeroes(parts[0]) { + return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0]) + } + major, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return Version{}, err + } + + // Minor + if !containsOnly(parts[1], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1]) + } + if hasLeadingZeroes(parts[1]) { + return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1]) + } + minor, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return Version{}, err + } + + v := Version{} + v.Major = major + v.Minor = minor + + var build, prerelease []string + patchStr := parts[2] + + if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 { + build = strings.Split(patchStr[buildIndex+1:], ".") + patchStr = patchStr[:buildIndex] + } + + if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 { + prerelease = strings.Split(patchStr[preIndex+1:], ".") + patchStr = patchStr[:preIndex] + } + + if !containsOnly(patchStr, numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr) + } + if hasLeadingZeroes(patchStr) { + return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr) + } + patch, err := strconv.ParseUint(patchStr, 10, 64) + if err != nil { + return Version{}, err + } + + v.Patch = patch + + // Prerelease + for _, prstr := range prerelease { + parsedPR, err := NewPRVersion(prstr) + if err != nil { + return Version{}, err + } + v.Pre = append(v.Pre, parsedPR) + } + + // Build meta data + for _, str := range build { + if len(str) == 0 { + return Version{}, errors.New("Build meta data is empty") + } + if !containsOnly(str, alphanum) { + return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str) + } + v.Build = append(v.Build, str) + } + + return v, nil +} + +// MustParse is like Parse but panics if the version cannot be parsed. +func MustParse(s string) Version { + v, err := Parse(s) + if err != nil { + panic(`semver: Parse(` + s + `): ` + err.Error()) + } + return v +} + +// PRVersion represents a PreRelease Version +type PRVersion struct { + VersionStr string + VersionNum uint64 + IsNum bool +} + +// NewPRVersion creates a new valid prerelease version +func NewPRVersion(s string) (PRVersion, error) { + if len(s) == 0 { + return PRVersion{}, errors.New("Prerelease is empty") + } + v := PRVersion{} + if containsOnly(s, numbers) { + if hasLeadingZeroes(s) { + return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s) + } + num, err := strconv.ParseUint(s, 10, 64) + + // Might never be hit, but just in case + if err != nil { + return PRVersion{}, err + } + v.VersionNum = num + v.IsNum = true + } else if containsOnly(s, alphanum) { + v.VersionStr = s + v.IsNum = false + } else { + return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s) + } + return v, nil +} + +// IsNumeric checks if prerelease-version is numeric +func (v PRVersion) IsNumeric() bool { + return v.IsNum +} + +// Compare compares two PreRelease Versions v and o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v PRVersion) Compare(o PRVersion) int { + if v.IsNum && !o.IsNum { + return -1 + } else if !v.IsNum && o.IsNum { + return 1 + } else if v.IsNum && o.IsNum { + if v.VersionNum == o.VersionNum { + return 0 + } else if v.VersionNum > o.VersionNum { + return 1 + } else { + return -1 + } + } else { // both are Alphas + if v.VersionStr == o.VersionStr { + return 0 + } else if v.VersionStr > o.VersionStr { + return 1 + } else { + return -1 + } + } +} + +// PreRelease version to string +func (v PRVersion) String() string { + if v.IsNum { + return strconv.FormatUint(v.VersionNum, 10) + } + return v.VersionStr +} + +func containsOnly(s string, set string) bool { + return strings.IndexFunc(s, func(r rune) bool { + return !strings.ContainsRune(set, r) + }) == -1 +} + +func hasLeadingZeroes(s string) bool { + return len(s) > 1 && s[0] == '0' +} + +// NewBuildVersion creates a new valid build version +func NewBuildVersion(s string) (string, error) { + if len(s) == 0 { + return "", errors.New("Buildversion is empty") + } + if !containsOnly(s, alphanum) { + return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s) + } + return s, nil +} + +// FinalizeVersion returns the major, minor and patch number only and discards +// prerelease and build number. +func FinalizeVersion(s string) (string, error) { + v, err := Parse(s) + if err != nil { + return "", err + } + v.Pre = nil + v.Build = nil + + finalVer := v.String() + return finalVer, nil +} diff --git a/vendor/github.com/blang/semver/v4/sort.go b/vendor/github.com/blang/semver/v4/sort.go new file mode 100644 index 00000000000..e18f880826a --- /dev/null +++ b/vendor/github.com/blang/semver/v4/sort.go @@ -0,0 +1,28 @@ +package semver + +import ( + "sort" +) + +// Versions represents multiple versions. +type Versions []Version + +// Len returns length of version collection +func (s Versions) Len() int { + return len(s) +} + +// Swap swaps two versions inside the collection by its indices +func (s Versions) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less checks if version at index i is less than version at index j +func (s Versions) Less(i, j int) bool { + return s[i].LT(s[j]) +} + +// Sort sorts a slice of versions +func Sort(versions []Version) { + sort.Sort(Versions(versions)) +} diff --git a/vendor/github.com/blang/semver/v4/sql.go b/vendor/github.com/blang/semver/v4/sql.go new file mode 100644 index 00000000000..db958134f3b --- /dev/null +++ b/vendor/github.com/blang/semver/v4/sql.go @@ -0,0 +1,30 @@ +package semver + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements the database/sql.Scanner interface. +func (v *Version) Scan(src interface{}) (err error) { + var str string + switch src := src.(type) { + case string: + str = src + case []byte: + str = string(src) + default: + return fmt.Errorf("version.Scan: cannot convert %T to string", src) + } + + if t, err := Parse(str); err == nil { + *v = t + } + + return +} + +// Value implements the database/sql/driver.Valuer interface. +func (v Version) Value() (driver.Value, error) { + return v.String(), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4a5067abe15..4467481e774 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,6 @@ +# github.com/blang/semver/v4 v4.0.0 +## explicit; go 1.14 +github.com/blang/semver/v4 # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew From 5c2c76c9def5166d97627890d84350c8ef0bde7b Mon Sep 17 00:00:00 2001 From: Joel Speed Date: Wed, 24 Dec 2025 17:34:38 +0000 Subject: [PATCH 2/2] Update generated feature gates --- ...t-Default.yaml => featureGate-4-10-Hypershift-Default.yaml} | 3 ++- ...ml => featureGate-4-10-Hypershift-DevPreviewNoUpgrade.yaml} | 3 ++- ...ypershift-OKD.yaml => featureGate-4-10-Hypershift-OKD.yaml} | 3 ++- ...l => featureGate-4-10-Hypershift-TechPreviewNoUpgrade.yaml} | 3 ++- ...efault.yaml => featureGate-4-10-SelfManagedHA-Default.yaml} | 3 ++- ...=> featureGate-4-10-SelfManagedHA-DevPreviewNoUpgrade.yaml} | 3 ++- ...agedHA-OKD.yaml => featureGate-4-10-SelfManagedHA-OKD.yaml} | 3 ++- ...> featureGate-4-10-SelfManagedHA-TechPreviewNoUpgrade.yaml} | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) rename payload-manifests/featuregates/{featureGate-Hypershift-Default.yaml => featureGate-4-10-Hypershift-Default.yaml} (99%) rename payload-manifests/featuregates/{featureGate-Hypershift-DevPreviewNoUpgrade.yaml => featureGate-4-10-Hypershift-DevPreviewNoUpgrade.yaml} (99%) rename payload-manifests/featuregates/{featureGate-Hypershift-OKD.yaml => featureGate-4-10-Hypershift-OKD.yaml} (99%) rename payload-manifests/featuregates/{featureGate-Hypershift-TechPreviewNoUpgrade.yaml => featureGate-4-10-Hypershift-TechPreviewNoUpgrade.yaml} (99%) rename payload-manifests/featuregates/{featureGate-SelfManagedHA-Default.yaml => featureGate-4-10-SelfManagedHA-Default.yaml} (99%) rename payload-manifests/featuregates/{featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml => featureGate-4-10-SelfManagedHA-DevPreviewNoUpgrade.yaml} (99%) rename payload-manifests/featuregates/{featureGate-SelfManagedHA-OKD.yaml => featureGate-4-10-SelfManagedHA-OKD.yaml} (99%) rename payload-manifests/featuregates/{featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml => featureGate-4-10-SelfManagedHA-TechPreviewNoUpgrade.yaml} (99%) diff --git a/payload-manifests/featuregates/featureGate-Hypershift-Default.yaml b/payload-manifests/featuregates/featureGate-4-10-Hypershift-Default.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-Hypershift-Default.yaml rename to payload-manifests/featuregates/featureGate-4-10-Hypershift-Default.yaml index 49bfa1ad505..99fb3c81550 100644 --- a/payload-manifests/featuregates/featureGate-Hypershift-Default.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-Hypershift-Default.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator" + "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" }, diff --git a/payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-4-10-Hypershift-DevPreviewNoUpgrade.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml rename to payload-manifests/featuregates/featureGate-4-10-Hypershift-DevPreviewNoUpgrade.yaml index 86116dbdffc..f957919a87b 100644 --- a/payload-manifests/featuregates/featureGate-Hypershift-DevPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-Hypershift-DevPreviewNoUpgrade.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator" + "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" }, diff --git a/payload-manifests/featuregates/featureGate-Hypershift-OKD.yaml b/payload-manifests/featuregates/featureGate-4-10-Hypershift-OKD.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-Hypershift-OKD.yaml rename to payload-manifests/featuregates/featureGate-4-10-Hypershift-OKD.yaml index cce0b86cb81..1cf243315dd 100644 --- a/payload-manifests/featuregates/featureGate-Hypershift-OKD.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-Hypershift-OKD.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator" + "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" }, diff --git a/payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-4-10-Hypershift-TechPreviewNoUpgrade.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml rename to payload-manifests/featuregates/featureGate-4-10-Hypershift-TechPreviewNoUpgrade.yaml index 6880fa3e6b6..a14e3d25468 100644 --- a/payload-manifests/featuregates/featureGate-Hypershift-TechPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-Hypershift-TechPreviewNoUpgrade.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator" + "include.release.openshift.io/ibm-cloud-managed": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" }, diff --git a/payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-Default.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml rename to payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-Default.yaml index 4d1fd6dd684..8995b5929c7 100644 --- a/payload-manifests/featuregates/featureGate-SelfManagedHA-Default.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-Default.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator" + "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" }, diff --git a/payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-DevPreviewNoUpgrade.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml rename to payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-DevPreviewNoUpgrade.yaml index fbb2ed5c184..c5d4e6d3fdd 100644 --- a/payload-manifests/featuregates/featureGate-SelfManagedHA-DevPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-DevPreviewNoUpgrade.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator" + "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" }, diff --git a/payload-manifests/featuregates/featureGate-SelfManagedHA-OKD.yaml b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-OKD.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-SelfManagedHA-OKD.yaml rename to payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-OKD.yaml index 3b097317f4a..13d6c9e5ed3 100644 --- a/payload-manifests/featuregates/featureGate-SelfManagedHA-OKD.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-OKD.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator" + "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" }, diff --git a/payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-TechPreviewNoUpgrade.yaml similarity index 99% rename from payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml rename to payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-TechPreviewNoUpgrade.yaml index 9c68cf67d44..9213b1474bb 100644 --- a/payload-manifests/featuregates/featureGate-SelfManagedHA-TechPreviewNoUpgrade.yaml +++ b/payload-manifests/featuregates/featureGate-4-10-SelfManagedHA-TechPreviewNoUpgrade.yaml @@ -3,7 +3,8 @@ "kind": "FeatureGate", "metadata": { "annotations": { - "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator" + "include.release.openshift.io/self-managed-high-availability": "false-except-for-the-config-operator", + "release.openshift.io/major-version": "4,5,6,7,8,9,10" }, "name": "cluster" },