@@ -7,8 +7,11 @@ import (
77 "io/ioutil"
88 "net/http"
99 "net/url"
10+ "slices"
1011 "strings"
1112
13+ "github.com/pkg/errors"
14+ "github.com/sirupsen/logrus"
1215 "k8s.io/apimachinery/pkg/runtime/schema"
1316
1417 corev1 "k8s.io/api/core/v1"
@@ -22,6 +25,7 @@ import (
2225 operatorv1 "github.com/openshift/api/operator/v1"
2326 configclient "github.com/openshift/client-go/config/clientset/versioned"
2427 operatorclient "github.com/openshift/client-go/operator/clientset/versioned"
28+
2529 "github.com/openshift/origin/test/extended/util/azure"
2630)
2731
@@ -70,6 +74,13 @@ type ClusterConfiguration struct {
7074
7175 // IsNoOptionalCapabilities indicates the cluster has no optional capabilities enabled
7276 HasNoOptionalCapabilities bool
77+
78+ // APIGroups contains the set of API groups available in the cluster
79+ APIGroups sets.Set [string ] `json:"-"`
80+ // EnabledFeatureGates contains the set of enabled feature gates in the cluster
81+ EnabledFeatureGates sets.Set [string ] `json:"-"`
82+ // DisabledFeatureGates contains the set of disabled feature gates in the cluster
83+ DisabledFeatureGates sets.Set [string ] `json:"-"`
7384}
7485
7586func (c * ClusterConfiguration ) ToJSONString () string {
@@ -91,6 +102,80 @@ type ClusterState struct {
91102 ControlPlaneTopology * configv1.TopologyMode
92103 OptionalCapabilities []configv1.ClusterVersionCapability
93104 Version * configv1.ClusterVersion
105+ APIGroups sets.Set [string ]
106+ EnabledFeatureGates sets.Set [string ]
107+ DisabledFeatureGates sets.Set [string ]
108+ }
109+
110+ // discoverAPIGroups discovers available API groups in the cluster
111+ func discoverAPIGroups (coreClient clientset.Interface ) (sets.Set [string ], error ) {
112+ logrus .Debugf ("Discovering API Groups..." )
113+ discoveryClient := coreClient .Discovery ()
114+ groups , err := discoveryClient .ServerGroups ()
115+ if err != nil {
116+ return nil , err
117+ }
118+
119+ apiGroups := sets .New [string ]()
120+ for _ , apiGroup := range groups .Groups {
121+ // ignore the empty group
122+ if apiGroup .Name == "" {
123+ continue
124+ }
125+ apiGroups .Insert (apiGroup .Name )
126+ }
127+
128+ sortedAPIGroups := apiGroups .UnsortedList ()
129+ slices .Sort (sortedAPIGroups )
130+
131+ logrus .WithField ("apiGroups" , strings .Join (sortedAPIGroups , ", " )).
132+ Debugf ("Discovered %d API Groups" , apiGroups .Len ())
133+
134+ return apiGroups , nil
135+ }
136+
137+ // discoverFeatureGates discovers feature gates in the cluster
138+ func discoverFeatureGates (configClient configclient.Interface , clusterVersion * configv1.ClusterVersion ) (enabled , disabled sets.Set [string ], err error ) {
139+ logrus .Debugf ("Discovering feature gates..." )
140+ featureGate , err := configClient .ConfigV1 ().FeatureGates ().Get (context .Background (), "cluster" , metav1.GetOptions {})
141+ if err != nil {
142+ return nil , nil , errors .WithMessage (err , "encountered an error while discovering feature gates" )
143+ }
144+
145+ desiredVersion := clusterVersion .Status .Desired .Version
146+ if len (desiredVersion ) == 0 && len (clusterVersion .Status .History ) > 0 {
147+ desiredVersion = clusterVersion .Status .History [0 ].Version
148+ }
149+
150+ enabled = sets .New [string ]()
151+ disabled = sets .New [string ]()
152+ for _ , featureGateValues := range featureGate .Status .FeatureGates {
153+ if featureGateValues .Version != desiredVersion {
154+ logrus .Warningf ("Feature gates for version %s not found, skipping" , desiredVersion )
155+ continue
156+ }
157+ for _ , enabledGate := range featureGateValues .Enabled {
158+ enabled .Insert (string (enabledGate .Name ))
159+ }
160+ for _ , disabledGate := range featureGateValues .Disabled {
161+ disabled .Insert (string (disabledGate .Name ))
162+ }
163+ break
164+ }
165+
166+ sortedEnabledGates := enabled .UnsortedList ()
167+ slices .Sort (sortedEnabledGates )
168+
169+ logrus .WithField ("featureGates" , strings .Join (sortedEnabledGates , ", " )).
170+ Debugf ("Discovered %d enabled feature gates" , len (sortedEnabledGates ))
171+
172+ sortedDisabledGates := disabled .UnsortedList ()
173+ slices .Sort (sortedDisabledGates )
174+
175+ logrus .WithField ("featureGates" , strings .Join (sortedDisabledGates , ", " )).
176+ Debugf ("Discovered %d disabled feature gates" , len (sortedDisabledGates ))
177+
178+ return enabled , disabled , nil
94179}
95180
96181// DiscoverClusterState creates a ClusterState based on a live cluster
@@ -156,6 +241,24 @@ func DiscoverClusterState(clientConfig *rest.Config) (*ClusterState, error) {
156241 state .Version = clusterVersion
157242 state .OptionalCapabilities = clusterVersion .Status .Capabilities .EnabledCapabilities
158243
244+ // Discover available API groups
245+ state .APIGroups , err = discoverAPIGroups (coreClient )
246+ if err != nil {
247+ return nil , errors .WithMessage (err , "encountered an error while discovering API groups" )
248+ }
249+
250+ // Discover feature gates
251+ if state .APIGroups .Has ("config.openshift.io" ) {
252+ state .EnabledFeatureGates , state .DisabledFeatureGates , err = discoverFeatureGates (configClient , clusterVersion )
253+ if err != nil {
254+ logrus .WithError (err ).Warn ("ignoring error from discoverFeatureGates" )
255+ }
256+ } else {
257+ state .EnabledFeatureGates = sets .New [string ]()
258+ state .DisabledFeatureGates = sets .New [string ]()
259+ logrus .Infof ("config.openshift.io API group not found, skipping feature gate discovery" )
260+ }
261+
159262 return state , nil
160263}
161264
@@ -271,63 +374,10 @@ func LoadConfig(state *ClusterState) (*ClusterConfiguration, error) {
271374 // have to scan MachineConfig objects to figure this out? For now, callers can
272375 // can just manually override with --provider...
273376
274- return config , nil
275- }
276-
277- // MatchFn returns a function that tests if a named function should be run based on
278- // the cluster configuration
279- func (c * ClusterConfiguration ) MatchFn () func (string ) bool {
280- var skips []string
281- skips = append (skips , fmt .Sprintf ("[Skipped:%s]" , c .ProviderName ))
282-
283- if c .IsIBMROKS {
284- skips = append (skips , "[Skipped:ibmroks]" )
285- }
286- if c .NetworkPlugin != "" {
287- skips = append (skips , fmt .Sprintf ("[Skipped:Network/%s]" , c .NetworkPlugin ))
288- if c .NetworkPluginMode != "" {
289- skips = append (skips , fmt .Sprintf ("[Skipped:Network/%s/%s]" , c .NetworkPlugin , c .NetworkPluginMode ))
290- }
291- }
292-
293- if c .Disconnected {
294- skips = append (skips , "[Skipped:Disconnected]" )
295- }
377+ // Copy API groups and feature gates from cluster state
378+ config .APIGroups = state .APIGroups
379+ config .EnabledFeatureGates = state .EnabledFeatureGates
380+ config .DisabledFeatureGates = state .DisabledFeatureGates
296381
297- if c .IsProxied {
298- skips = append (skips , "[Skipped:Proxy]" )
299- }
300-
301- if c .SingleReplicaTopology {
302- skips = append (skips , "[Skipped:SingleReplicaTopology]" )
303- }
304-
305- if ! c .HasIPv4 {
306- skips = append (skips , "[Feature:Networking-IPv4]" )
307- }
308- if ! c .HasIPv6 {
309- skips = append (skips , "[Feature:Networking-IPv6]" )
310- }
311- if ! c .HasIPv4 || ! c .HasIPv6 {
312- // lack of "]" is intentional; this matches multiple tags
313- skips = append (skips , "[Feature:IPv6DualStack" )
314- }
315-
316- if ! c .HasSCTP {
317- skips = append (skips , "[Feature:SCTPConnectivity]" )
318- }
319-
320- if c .HasNoOptionalCapabilities {
321- skips = append (skips , "[Skipped:NoOptionalCapabilities]" )
322- }
323-
324- matchFn := func (name string ) bool {
325- for _ , skip := range skips {
326- if strings .Contains (name , skip ) {
327- return false
328- }
329- }
330- return true
331- }
332- return matchFn
382+ return config , nil
333383}
0 commit comments