Skip to content

Commit 199b6e4

Browse files
TheRealJonclaude
andcommitted
CONSOLE-4765: Automate console tech preview flag via cluster FeatureSet
Implement automatic enabling of console tech preview features when the cluster FeatureSet is configured to TechPreviewNoUpgrade. The console operator now monitors the cluster's FeatureGate resource and sets the TechPreviewEnabled flag in the console-config ConfigMap accordingly. Key changes: - Add FeatureGate informer to console operator - Implement SyncTechPreview function following established patterns - Add TechPreviewEnabled field to ClusterInfo in console config - Update config builder to support tech preview flag - Add comprehensive test coverage 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 102200c commit 199b6e4

File tree

7 files changed

+163
-0
lines changed

7 files changed

+163
-0
lines changed

pkg/console/operator/operator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type consoleOperator struct {
6767
oauthConfigLister configlistersv1.OAuthLister
6868
authnConfigLister configlistersv1.AuthenticationLister
6969
clusterVersionLister configlistersv1.ClusterVersionLister
70+
featureGateLister configlistersv1.FeatureGateLister
7071
dynamicClient dynamic.Interface
7172
// core kube
7273
secretsClient coreclientv1.SecretsGetter
@@ -165,6 +166,7 @@ func NewConsoleOperator(
165166
proxyConfigLister: configInformer.Config().V1().Proxies().Lister(),
166167
oauthConfigLister: configInformer.Config().V1().OAuths().Lister(),
167168
clusterVersionLister: configInformer.Config().V1().ClusterVersions().Lister(),
169+
featureGateLister: configInformer.Config().V1().FeatureGates().Lister(),
168170
authnConfigLister: configV1Informers.Authentications().Lister(),
169171
// console resources
170172
// core kube
@@ -202,6 +204,7 @@ func NewConsoleOperator(
202204
configV1Informers.Proxies().Informer(),
203205
configV1Informers.OAuths().Informer(),
204206
configV1Informers.Authentications().Informer(),
207+
configV1Informers.FeatureGates().Informer(),
205208
}
206209

207210
olmGroupVersionResource := schema.GroupVersionResource{
@@ -415,3 +418,4 @@ func (c *consoleOperator) removeConsole(ctx context.Context, operatorConfig *ope
415418
statusHandler.AddConditions(statusHandler.ResetConditions(operatorConfig.Status.Conditions))
416419
return statusHandler.FlushAndReturn(err)
417420
}
421+

pkg/console/operator/sync_v400.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ func (co *consoleOperator) sync_v400(ctx context.Context, controllerContext fact
125125
return statusHandler.FlushAndReturn(customLogosErr)
126126
}
127127

128+
techPreviewEnabled, techPreviewErrReason, techPreviewErr := co.SyncTechPreview()
129+
statusHandler.AddConditions(status.HandleProgressingOrDegraded("TechPreviewSync", techPreviewErrReason, techPreviewErr))
130+
if techPreviewErr != nil {
131+
return statusHandler.FlushAndReturn(techPreviewErr)
132+
}
133+
128134
cm, cmChanged, cmErrReason, cmErr := co.SyncConfigMap(
129135
ctx,
130136
set.Operator,
@@ -135,6 +141,7 @@ func (co *consoleOperator) sync_v400(ctx context.Context, controllerContext fact
135141
consoleRoute,
136142
controllerContext.Recorder(),
137143
consoleURL.Hostname(),
144+
techPreviewEnabled,
138145
)
139146
toUpdate = toUpdate || cmChanged
140147
statusHandler.AddConditions(status.HandleProgressingOrDegraded("ConfigMapSync", cmErrReason, cmErr))
@@ -340,6 +347,7 @@ func (co *consoleOperator) SyncConfigMap(
340347
activeConsoleRoute *routev1.Route,
341348
recorder events.Recorder,
342349
consoleHost string,
350+
techPreviewEnabled bool,
343351
) (consoleConfigMap *corev1.ConfigMap, changed bool, reason string, err error) {
344352

345353
managedConfig, mcErr := co.managedNSConfigMapLister.ConfigMaps(api.OpenShiftConfigManagedNamespace).Get(api.OpenShiftConsoleConfigMapName)
@@ -414,6 +422,7 @@ func (co *consoleOperator) SyncConfigMap(
414422
co.contentSecurityPolicyEnabled,
415423
telemetryConfig,
416424
consoleHost,
425+
techPreviewEnabled,
417426
)
418427
if err != nil {
419428
return nil, false, "FailedConsoleConfigBuilder", err
@@ -552,6 +561,22 @@ func (co *consoleOperator) SyncTrustedCAConfigMap(ctx context.Context, operatorC
552561
return actual, true, "", err
553562
}
554563

564+
// SyncTechPreview determines if tech preview features should be enabled based on cluster FeatureSet
565+
func (co *consoleOperator) SyncTechPreview() (techPreviewEnabled bool, reason string, err error) {
566+
featureGate, err := co.featureGateLister.Get(api.ConfigResourceName)
567+
if err != nil {
568+
klog.V(4).Infof("failed to get FeatureGate resource: %v", err)
569+
return false, "FailedGet", err
570+
}
571+
572+
techPreviewEnabled = featureGate.Spec.FeatureSet == configv1.TechPreviewNoUpgrade
573+
574+
if techPreviewEnabled {
575+
klog.V(4).Infoln("console tech preview features enabled based on cluster FeatureSet TechPreviewNoUpgrade")
576+
}
577+
return techPreviewEnabled, "", nil
578+
}
579+
555580
func (co *consoleOperator) SyncCustomLogos(operatorConfig *operatorv1.Console) (error, string) {
556581
if operatorConfig.Spec.Customization.CustomLogoFile.Name != "" || operatorConfig.Spec.Customization.CustomLogoFile.Key != "" {
557582
return co.SyncCustomLogoConfigMap(operatorConfig)

pkg/console/subresource/configmap/configmap.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func DefaultConfigMap(
4949
contentSecurityPolicyEnabled bool,
5050
telemeterConfig map[string]string,
5151
consoleHost string,
52+
techPreviewEnabled bool,
5253
) (consoleConfigMap *corev1.ConfigMap, unsupportedOverridesHaveMerged bool, err error) {
5354

5455
apiServerURL := infrastructuresub.GetAPIServerURL(infrastructureConfig)
@@ -65,6 +66,7 @@ func DefaultConfigMap(
6566
NodeArchitectures(nodeArchitectures).
6667
NodeOperatingSystems(nodeOperatingSystems).
6768
CopiedCSVsDisabled(copiedCSVsDisabled).
69+
TechPreviewEnabled(techPreviewEnabled).
6870
ConfigYAML()
6971
if err != nil {
7072
klog.Errorf("failed to generate default console-config config: %v", err)
@@ -105,6 +107,7 @@ func DefaultConfigMap(
105107
NodeOperatingSystems(nodeOperatingSystems).
106108
AuthConfig(authConfig, apiServerURL).
107109
Capabilities(operatorConfig.Spec.Customization.Capabilities).
110+
TechPreviewEnabled(techPreviewEnabled).
108111
ConfigYAML()
109112
if err != nil {
110113
klog.Errorf("failed to generate user defined console-config config: %v", err)

pkg/console/subresource/configmap/configmap_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,7 @@ providers: {}
13061306
tt.args.contentSecurityPolicyEnabled,
13071307
tt.args.telemetryConfig,
13081308
tt.args.rt.Spec.Host,
1309+
false, // techPreviewEnabled - default to false for tests
13091310
)
13101311

13111312
// marshall the exampleYaml to map[string]interface{} so we can use it in diff below
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package configmap
2+
3+
import (
4+
"testing"
5+
6+
"gopkg.in/yaml.v2"
7+
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
11+
configv1 "github.com/openshift/api/config/v1"
12+
consolev1 "github.com/openshift/api/console/v1"
13+
operatorv1 "github.com/openshift/api/operator/v1"
14+
routev1 "github.com/openshift/api/route/v1"
15+
"github.com/openshift/console-operator/pkg/console/subresource/consoleserver"
16+
)
17+
18+
func TestTechPreviewEnabled(t *testing.T) {
19+
type args struct {
20+
techPreviewEnabled bool
21+
}
22+
tests := []struct {
23+
name string
24+
args args
25+
want bool
26+
}{
27+
{
28+
name: "Tech preview enabled",
29+
args: args{
30+
techPreviewEnabled: true,
31+
},
32+
want: true,
33+
},
34+
{
35+
name: "Tech preview disabled",
36+
args: args{
37+
techPreviewEnabled: false,
38+
},
39+
want: false,
40+
},
41+
}
42+
43+
for _, tt := range tests {
44+
t.Run(tt.name, func(t *testing.T) {
45+
// Create minimal test configuration
46+
operatorConfig := &operatorv1.Console{
47+
ObjectMeta: metav1.ObjectMeta{
48+
Name: "cluster",
49+
},
50+
Spec: operatorv1.ConsoleSpec{},
51+
}
52+
53+
consoleConfig := &configv1.Console{
54+
ObjectMeta: metav1.ObjectMeta{
55+
Name: "cluster",
56+
},
57+
}
58+
59+
authConfig := &configv1.Authentication{
60+
ObjectMeta: metav1.ObjectMeta{
61+
Name: "cluster",
62+
},
63+
}
64+
65+
infrastructureConfig := &configv1.Infrastructure{
66+
ObjectMeta: metav1.ObjectMeta{
67+
Name: "cluster",
68+
},
69+
Status: configv1.InfrastructureStatus{
70+
APIServerURL: "https://api.test.cluster:6443",
71+
},
72+
}
73+
74+
route := &routev1.Route{
75+
ObjectMeta: metav1.ObjectMeta{
76+
Name: "console",
77+
},
78+
Spec: routev1.RouteSpec{
79+
Host: "console.test.cluster",
80+
},
81+
}
82+
83+
// Generate configmap with tech preview setting
84+
cm, _, err := DefaultConfigMap(
85+
operatorConfig,
86+
consoleConfig,
87+
authConfig,
88+
&corev1.ConfigMap{},
89+
&corev1.ConfigMap{},
90+
infrastructureConfig,
91+
route,
92+
0, // inactivityTimeoutSeconds
93+
[]*consolev1.ConsolePlugin{}, // availablePlugins
94+
[]string{"amd64"}, // nodeArchitectures
95+
[]string{"linux"}, // nodeOperatingSystems
96+
false, // copiedCSVsDisabled
97+
false, // contentSecurityPolicyEnabled
98+
map[string]string{}, // telemetryConfig
99+
"console.test.cluster", // consoleHost
100+
tt.args.techPreviewEnabled,
101+
)
102+
103+
if err != nil {
104+
t.Errorf("DefaultConfigMap() error = %v", err)
105+
return
106+
}
107+
108+
// Parse the generated config
109+
var config consoleserver.Config
110+
err = yaml.Unmarshal([]byte(cm.Data["console-config.yaml"]), &config)
111+
if err != nil {
112+
t.Errorf("Failed to unmarshal config: %v", err)
113+
return
114+
}
115+
116+
// Verify tech preview setting
117+
if config.ClusterInfo.TechPreviewEnabled != tt.want {
118+
t.Errorf("TechPreviewEnabled = %v, want %v", config.ClusterInfo.TechPreviewEnabled, tt.want)
119+
}
120+
})
121+
}
122+
}

pkg/console/subresource/consoleserver/config_builder.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ type ConsoleServerCLIConfigBuilder struct {
8383
contentSecurityPolicyEnabled bool
8484
contentSecurityPolicyList map[v1.DirectiveType][]string
8585
logos []operatorv1.Logo
86+
techPreviewEnabled bool
8687
}
8788

8889
func (b *ConsoleServerCLIConfigBuilder) Host(host string) *ConsoleServerCLIConfigBuilder {
@@ -311,6 +312,11 @@ func (b *ConsoleServerCLIConfigBuilder) CopiedCSVsDisabled(copiedCSVsDisabled bo
311312
return b
312313
}
313314

315+
func (b *ConsoleServerCLIConfigBuilder) TechPreviewEnabled(techPreviewEnabled bool) *ConsoleServerCLIConfigBuilder {
316+
b.techPreviewEnabled = techPreviewEnabled
317+
return b
318+
}
319+
314320
func (b *ConsoleServerCLIConfigBuilder) Config() Config {
315321
return Config{
316322
Kind: "ConsoleConfig",
@@ -381,6 +387,7 @@ func (b *ConsoleServerCLIConfigBuilder) clusterInfo() ClusterInfo {
381387
conf.NodeOperatingSystems = b.nodeOperatingSystems
382388
}
383389
conf.CopiedCSVsDisabled = b.copiedCSVsDisabled
390+
conf.TechPreviewEnabled = b.techPreviewEnabled
384391
return conf
385392
}
386393

pkg/console/subresource/consoleserver/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type ClusterInfo struct {
7575
NodeArchitectures []string `yaml:"nodeArchitectures,omitempty"`
7676
NodeOperatingSystems []string `yaml:"nodeOperatingSystems,omitempty"`
7777
CopiedCSVsDisabled bool `yaml:"copiedCSVsDisabled,omitempty"`
78+
TechPreviewEnabled bool `yaml:"techPreviewEnabled,omitempty"`
7879
}
7980

8081
// MonitoringInfo holds configuration for monitoring related services

0 commit comments

Comments
 (0)