Skip to content

Commit e409b04

Browse files
committed
Add major version filtering for manifests
1 parent bcd739a commit e409b04

2 files changed

Lines changed: 369 additions & 10 deletions

File tree

pkg/manifest/manifest.go

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"path/filepath"
99
"reflect"
10+
"strconv"
1011
"strings"
1112

1213
"k8s.io/apimachinery/pkg/util/sets"
@@ -23,10 +24,11 @@ import (
2324
)
2425

2526
const (
26-
CapabilityAnnotation = "capability.openshift.io/name"
27-
DefaultClusterProfile = "self-managed-high-availability"
28-
featureSetAnnotation = "release.openshift.io/feature-set"
29-
featureGateAnnotation = "release.openshift.io/feature-gates"
27+
CapabilityAnnotation = "capability.openshift.io/name"
28+
DefaultClusterProfile = "self-managed-high-availability"
29+
featureSetAnnotation = "release.openshift.io/feature-set"
30+
featureGateAnnotation = "release.openshift.io/feature-gates"
31+
majorVersionAnnotation = "release.openshift.io/major-version"
3032
)
3133

3234
var knownFeatureSets = sets.Set[string]{}
@@ -222,12 +224,64 @@ func checkFeatureGates(enabledGates sets.Set[string], annotations map[string]str
222224
return nil
223225
}
224226

227+
// checkMajorVersion validates if manifest should be included based on major version requirements
228+
func checkMajorVersion(clusterMajorVersion uint64, annotations map[string]string) error {
229+
if annotations == nil {
230+
return nil // No annotations, include by default
231+
}
232+
majorVersionRequirements, ok := annotations[majorVersionAnnotation]
233+
if !ok {
234+
return nil // No requirements, include by default
235+
}
236+
237+
requirements := strings.Split(majorVersionRequirements, ",")
238+
includedVersions := sets.New[uint64]()
239+
excludedVersions := sets.New[uint64]()
240+
241+
for _, req := range requirements {
242+
req = strings.TrimSpace(req)
243+
if req == "" {
244+
continue
245+
}
246+
247+
if strings.HasPrefix(req, "-") {
248+
excludedVersionStr := req[1:]
249+
excludedVersion, err := strconv.ParseUint(excludedVersionStr, 10, 64)
250+
if err != nil {
251+
return fmt.Errorf("invalid excluded major version %q in annotation: %v", excludedVersionStr, err)
252+
}
253+
excludedVersions.Insert(excludedVersion)
254+
} else {
255+
includedVersionStr := req
256+
includedVersion, err := strconv.ParseUint(includedVersionStr, 10, 64)
257+
if err != nil {
258+
return fmt.Errorf("invalid included major version %q in annotation: %v", includedVersionStr, err)
259+
}
260+
includedVersions.Insert(includedVersion)
261+
}
262+
}
263+
264+
switch {
265+
case includedVersions.Intersection(excludedVersions).Len() > 0:
266+
return fmt.Errorf("inclusion and exclusion requirements overlap: %v", includedVersions.Intersection(excludedVersions).UnsortedList())
267+
case includedVersions.Has(clusterMajorVersion):
268+
return nil // Found matching version, include this manifest
269+
case excludedVersions.Has(clusterMajorVersion):
270+
return fmt.Errorf("major version %d matches excluded version %d", clusterMajorVersion, clusterMajorVersion)
271+
case len(includedVersions) > 0:
272+
return fmt.Errorf("major version %d does not match any required versions", clusterMajorVersion)
273+
default:
274+
// No positive requirement and did not match any excluded versions
275+
return nil
276+
}
277+
}
278+
225279
// Include returns an error if the manifest fails an inclusion filter and should be excluded from further
226280
// processing by cluster version operator. Pointer arguments can be set nil to avoid excluding based on that
227281
// filter. For example, setting profile non-nil and capabilities nil will return an error if the manifest's
228282
// profile does not match, but will never return an error about capability issues.
229-
func (m *Manifest) Include(excludeIdentifier *string, requiredFeatureSet *string, profile *string, capabilities *configv1.ClusterVersionCapabilitiesStatus, overrides []configv1.ComponentOverride, enabledFeatureGates sets.Set[string]) error {
230-
return m.IncludeAllowUnknownCapabilities(excludeIdentifier, requiredFeatureSet, profile, capabilities, overrides, enabledFeatureGates, false)
283+
func (m *Manifest) Include(excludeIdentifier *string, requiredFeatureSet *string, profile *string, capabilities *configv1.ClusterVersionCapabilitiesStatus, overrides []configv1.ComponentOverride, enabledFeatureGates sets.Set[string], majorVersion *uint64) error {
284+
return m.IncludeAllowUnknownCapabilities(excludeIdentifier, requiredFeatureSet, profile, capabilities, overrides, enabledFeatureGates, majorVersion, false)
231285
}
232286

233287
// IncludeAllowUnknownCapabilities returns an error if the manifest fails an inclusion filter and should be excluded from
@@ -237,7 +291,7 @@ func (m *Manifest) Include(excludeIdentifier *string, requiredFeatureSet *string
237291
// to capabilities filtering. When set to true a manifest will not be excluded simply because it contains an unknown
238292
// capability. This is necessary to allow updates to an OCP version containing newly defined capabilities.
239293
func (m *Manifest) IncludeAllowUnknownCapabilities(excludeIdentifier *string, requiredFeatureSet *string, profile *string,
240-
capabilities *configv1.ClusterVersionCapabilitiesStatus, overrides []configv1.ComponentOverride, enabledFeatureGates sets.Set[string], allowUnknownCapabilities bool) error {
294+
capabilities *configv1.ClusterVersionCapabilitiesStatus, overrides []configv1.ComponentOverride, enabledFeatureGates sets.Set[string], majorVersion *uint64, allowUnknownCapabilities bool) error {
241295

242296
annotations := m.Obj.GetAnnotations()
243297
if annotations == nil {
@@ -266,6 +320,14 @@ func (m *Manifest) IncludeAllowUnknownCapabilities(excludeIdentifier *string, re
266320
}
267321
}
268322

323+
// Major version filtering
324+
if majorVersion != nil {
325+
err := checkMajorVersion(*majorVersion, annotations)
326+
if err != nil {
327+
return err
328+
}
329+
}
330+
269331
if profile != nil {
270332
profileAnnotation := fmt.Sprintf("include.release.openshift.io/%s", *profile)
271333
if val, ok := annotations[profileAnnotation]; ok && val != "true" {

0 commit comments

Comments
 (0)