Skip to content

Commit 358df4b

Browse files
Akrogclaude
andcommitted
Add console plugin deployment
Deploy the OpenShift Lightspeed console plugin as part of the operator reconciliation loop. Adds the Deployment, Service, ConsolePlugin CR, nginx ConfigMap, NetworkPolicy, and ServiceAccount using the existing CreateOrPatch pattern. Image used for the console depends on the OCP version. Versions prior to 4.19 use one with PatterFly 5 and for later one with PatternFly 6. Jira: OSPRH-29746 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1b86a11 commit 358df4b

17 files changed

Lines changed: 1087 additions & 3 deletions

api/v1beta1/openstacklightspeed_types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ const (
3838
// PostgresContainerImage is the fall-back container image for PostgreSQL
3939
PostgresContainerImage = "registry.redhat.io/rhel9/postgresql-16:latest"
4040

41+
// ConsoleContainerImage is the fall-back container image for the Console Plugin (PatternFly 6, OCP >= 4.19)
42+
ConsoleContainerImage = "registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-rhel9:1.0.12"
43+
44+
// ConsoleContainerImagePF5 is the fall-back console image for PatternFly 5 (OCP < 4.19)
45+
ConsoleContainerImagePF5 = "registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-pf5-rhel9:1.0.12"
46+
4147
// MaxTokensForResponseDefault is the default maximum number of tokens that should be used for response
4248
MaxTokensForResponseDefault = 2048
4349
)
@@ -186,6 +192,8 @@ type OpenStackLightspeedDefaults struct {
186192
LCoreImageURL string
187193
ExporterImageURL string
188194
PostgresImageURL string
195+
ConsoleImageURL string
196+
ConsoleImagePF5URL string
189197
MaxTokensForResponse int
190198
}
191199

@@ -203,6 +211,10 @@ func SetupDefaults() {
203211
"RELATED_IMAGE_EXPORTER_IMAGE_URL_DEFAULT", ExporterContainerImage),
204212
PostgresImageURL: util.GetEnvVar(
205213
"RELATED_IMAGE_POSTGRES_IMAGE_URL_DEFAULT", PostgresContainerImage),
214+
ConsoleImageURL: util.GetEnvVar(
215+
"RELATED_IMAGE_CONSOLE_IMAGE_URL_DEFAULT", ConsoleContainerImage),
216+
ConsoleImagePF5URL: util.GetEnvVar(
217+
"RELATED_IMAGE_CONSOLE_PF5_IMAGE_URL_DEFAULT", ConsoleContainerImagePF5),
206218
MaxTokensForResponse: MaxTokensForResponseDefault,
207219
}
208220

bundle/manifests/openstack-lightspeed-operator.clusterserviceversion.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ spec:
162162
- get
163163
- list
164164
- watch
165+
- apiGroups:
166+
- console.openshift.io
167+
resources:
168+
- consoleplugins
169+
verbs:
170+
- create
171+
- delete
172+
- get
173+
- list
174+
- patch
175+
- update
176+
- watch
165177
- apiGroups:
166178
- lightspeed.openstack.org
167179
resources:
@@ -188,6 +200,15 @@ spec:
188200
- get
189201
- patch
190202
- update
203+
- apiGroups:
204+
- operator.openshift.io
205+
resources:
206+
- consoles
207+
verbs:
208+
- get
209+
- list
210+
- update
211+
- watch
191212
- apiGroups:
192213
- operators.coreos.com
193214
resources:

cmd/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import (
3838
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
3939
"sigs.k8s.io/controller-runtime/pkg/webhook"
4040

41+
consolev1 "github.com/openshift/api/console/v1"
42+
openshiftv1 "github.com/openshift/api/operator/v1"
4143
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
4244

4345
apiv1beta1 "github.com/openstack-lightspeed/operator/api/v1beta1"
@@ -56,6 +58,10 @@ func init() {
5658
utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme))
5759

5860
utilruntime.Must(apiv1beta1.AddToScheme(scheme))
61+
62+
utilruntime.Must(consolev1.AddToScheme(scheme))
63+
64+
utilruntime.Must(openshiftv1.AddToScheme(scheme))
5965
// +kubebuilder:scaffold:scheme
6066
}
6167

config/rbac/role.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ rules:
2020
- get
2121
- list
2222
- watch
23+
- apiGroups:
24+
- console.openshift.io
25+
resources:
26+
- consoleplugins
27+
verbs:
28+
- create
29+
- delete
30+
- get
31+
- list
32+
- patch
33+
- update
34+
- watch
2335
- apiGroups:
2436
- lightspeed.openstack.org
2537
resources:
@@ -46,6 +58,15 @@ rules:
4658
- get
4759
- patch
4860
- update
61+
- apiGroups:
62+
- operator.openshift.io
63+
resources:
64+
- consoles
65+
verbs:
66+
- get
67+
- list
68+
- update
69+
- watch
4970
- apiGroups:
5071
- operators.coreos.com
5172
resources:

go.mod

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@ require (
66
github.com/go-logr/logr v1.4.3
77
github.com/onsi/ginkgo/v2 v2.27.5
88
github.com/onsi/gomega v1.39.0
9+
github.com/openshift/api v3.9.0+incompatible // from lib-common
910
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.0
1011
github.com/operator-framework/api v0.37.0
12+
k8s.io/api v0.34.2
1113
k8s.io/apimachinery v0.34.3
1214
k8s.io/client-go v0.34.2
13-
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
1415
sigs.k8s.io/controller-runtime v0.22.4
16+
sigs.k8s.io/yaml v1.6.0
1517
)
1618

19+
// from https://github.com/openstack-k8s-operators/lib-common/blob/main/modules/common/go.mod
20+
// must be consistent within modules and service operators
21+
replace github.com/openshift/api => github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e
22+
1723
require (
1824
cel.dev/expr v0.24.0 // indirect
1925
github.com/Masterminds/semver/v3 v3.4.0 // indirect
@@ -91,15 +97,14 @@ require (
9197
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
9298
gopkg.in/inf.v0 v0.9.1 // indirect
9399
gopkg.in/yaml.v3 v3.0.1 // indirect
94-
k8s.io/api v0.34.2 // indirect
95100
k8s.io/apiextensions-apiserver v0.34.2 // indirect
96101
k8s.io/apiserver v0.34.2 // indirect
97102
k8s.io/component-base v0.34.2 // indirect
98103
k8s.io/klog/v2 v2.130.1 // indirect
99104
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
105+
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
100106
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect
101107
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
102108
sigs.k8s.io/randfill v1.0.0 // indirect
103109
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
104-
sigs.k8s.io/yaml v1.6.0 // indirect
105110
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ github.com/onsi/ginkgo/v2 v2.27.5 h1:ZeVgZMx2PDMdJm/+w5fE/OyG6ILo1Y3e+QX4zSR0zTE
109109
github.com/onsi/ginkgo/v2 v2.27.5/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
110110
github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
111111
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
112+
github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyUt0GEdoAE+r5TXy7YS21yNEo+2U=
113+
github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo=
112114
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.0 h1:2TD4hi+MLt67jKxJUs2tuBKFMxibrLJQqKqhsTMsHeQ=
113115
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.0/go.mod h1:rgpcv2tLD+/vudXx/gpIQSTuRpk4GOxHx84xwfvQalM=
114116
github.com/operator-framework/api v0.37.0 h1:2XCMWitBnumtJTqzip6LQKUwpM2pXVlt3gkpdlkbaCE=
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
pid /tmp/nginx/nginx.pid;
3+
error_log /dev/stdout info;
4+
events {}
5+
http {
6+
client_body_temp_path /tmp/nginx/client_body;
7+
proxy_temp_path /tmp/nginx/proxy;
8+
fastcgi_temp_path /tmp/nginx/fastcgi;
9+
uwsgi_temp_path /tmp/nginx/uwsgi;
10+
scgi_temp_path /tmp/nginx/scgi;
11+
access_log /dev/stdout;
12+
include /etc/nginx/mime.types;
13+
default_type application/octet-stream;
14+
keepalive_timeout 65;
15+
server {
16+
listen %[1]d ssl;
17+
listen [::]:%[1]d ssl;
18+
ssl_certificate /var/cert/tls.crt;
19+
ssl_certificate_key /var/cert/tls.key;
20+
root /usr/share/nginx/html;
21+
}
22+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
Copyright 2026.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controller
18+
19+
import (
20+
"fmt"
21+
22+
consolev1 "github.com/openshift/api/console/v1"
23+
appsv1 "k8s.io/api/apps/v1"
24+
corev1 "k8s.io/api/core/v1"
25+
networkingv1 "k8s.io/api/networking/v1"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/apimachinery/pkg/util/intstr"
28+
)
29+
30+
// generateConsoleSelectorLabels returns a map of labels used as selectors
31+
// for the console plugin pods.
32+
func generateConsoleSelectorLabels() map[string]string {
33+
return map[string]string{
34+
"app.kubernetes.io/component": "console-plugin",
35+
"app.kubernetes.io/managed-by": "openstack-lightspeed-operator",
36+
"app.kubernetes.io/name": "lightspeed-console-plugin",
37+
"app.kubernetes.io/part-of": "openstack-lightspeed",
38+
}
39+
}
40+
41+
// buildConsoleDeploymentSpec builds the Deployment spec for the console plugin.
42+
func buildConsoleDeploymentSpec(consoleImage string) appsv1.DeploymentSpec {
43+
replicas := int32(1)
44+
volumeDefaultMode := VolumeDefaultMode
45+
labels := generateConsoleSelectorLabels()
46+
47+
return appsv1.DeploymentSpec{
48+
Replicas: &replicas,
49+
Selector: &metav1.LabelSelector{
50+
MatchLabels: labels,
51+
},
52+
Template: corev1.PodTemplateSpec{
53+
ObjectMeta: metav1.ObjectMeta{
54+
Labels: labels,
55+
},
56+
Spec: corev1.PodSpec{
57+
SecurityContext: &corev1.PodSecurityContext{
58+
RunAsNonRoot: toPtr(true),
59+
SeccompProfile: &corev1.SeccompProfile{
60+
Type: corev1.SeccompProfileTypeRuntimeDefault,
61+
},
62+
},
63+
ServiceAccountName: ConsoleUIServiceAccountName,
64+
Containers: []corev1.Container{
65+
{
66+
Name: "lightspeed-console-plugin",
67+
Image: consoleImage,
68+
Ports: []corev1.ContainerPort{
69+
{
70+
ContainerPort: ConsoleUIHTTPSPort,
71+
Name: "https",
72+
Protocol: corev1.ProtocolTCP,
73+
},
74+
},
75+
ImagePullPolicy: corev1.PullAlways,
76+
SecurityContext: &corev1.SecurityContext{
77+
AllowPrivilegeEscalation: toPtr(false),
78+
},
79+
VolumeMounts: []corev1.VolumeMount{
80+
{
81+
Name: "lightspeed-console-plugin-cert",
82+
MountPath: "/var/cert",
83+
ReadOnly: true,
84+
},
85+
{
86+
Name: "nginx-config",
87+
MountPath: "/etc/nginx/nginx.conf",
88+
SubPath: "nginx.conf",
89+
ReadOnly: true,
90+
},
91+
{
92+
Name: "nginx-temp",
93+
MountPath: "/tmp/nginx",
94+
},
95+
},
96+
},
97+
},
98+
Volumes: []corev1.Volume{
99+
{
100+
Name: "lightspeed-console-plugin-cert",
101+
VolumeSource: corev1.VolumeSource{
102+
Secret: &corev1.SecretVolumeSource{
103+
SecretName: ConsoleUIServiceCertSecretName,
104+
DefaultMode: &volumeDefaultMode,
105+
},
106+
},
107+
},
108+
{
109+
Name: "nginx-config",
110+
VolumeSource: corev1.VolumeSource{
111+
ConfigMap: &corev1.ConfigMapVolumeSource{
112+
LocalObjectReference: corev1.LocalObjectReference{
113+
Name: ConsoleUIConfigMapName,
114+
},
115+
DefaultMode: &volumeDefaultMode,
116+
},
117+
},
118+
},
119+
{
120+
Name: "nginx-temp",
121+
VolumeSource: corev1.VolumeSource{
122+
EmptyDir: &corev1.EmptyDirVolumeSource{},
123+
},
124+
},
125+
},
126+
},
127+
},
128+
}
129+
}
130+
131+
// buildConsolePluginSpec builds the ConsolePlugin spec with backend and proxy configuration.
132+
func buildConsolePluginSpec(namespace string) consolev1.ConsolePluginSpec {
133+
return consolev1.ConsolePluginSpec{
134+
Backend: consolev1.ConsolePluginBackend{
135+
Service: &consolev1.ConsolePluginService{
136+
Name: ConsoleUIServiceName,
137+
Namespace: namespace,
138+
Port: ConsoleUIHTTPSPort,
139+
BasePath: "/",
140+
},
141+
Type: consolev1.Service,
142+
},
143+
DisplayName: "Lightspeed Console Plugin",
144+
I18n: consolev1.ConsolePluginI18n{
145+
LoadType: consolev1.Preload,
146+
},
147+
Proxy: []consolev1.ConsolePluginProxy{
148+
{
149+
Alias: ConsoleProxyAlias,
150+
Authorization: consolev1.UserToken,
151+
Endpoint: consolev1.ConsolePluginProxyEndpoint{
152+
Service: &consolev1.ConsolePluginProxyServiceConfig{
153+
Name: OpenStackLightspeedAppServerServiceName,
154+
Namespace: namespace,
155+
Port: OpenStackLightspeedAppServerServicePort,
156+
},
157+
Type: consolev1.ProxyTypeService,
158+
},
159+
},
160+
},
161+
}
162+
}
163+
164+
// buildConsoleNginxConfig returns the nginx configuration content for the console plugin.
165+
func buildConsoleNginxConfig() string {
166+
return fmt.Sprintf(consoleNginxConfigTemplate, ConsoleUIHTTPSPort)
167+
}
168+
169+
// buildConsoleNetworkPolicySpec builds the NetworkPolicy spec for the console plugin.
170+
func buildConsoleNetworkPolicySpec() networkingv1.NetworkPolicySpec {
171+
return networkingv1.NetworkPolicySpec{
172+
PodSelector: metav1.LabelSelector{
173+
MatchLabels: generateConsoleSelectorLabels(),
174+
},
175+
Ingress: []networkingv1.NetworkPolicyIngressRule{
176+
{
177+
From: []networkingv1.NetworkPolicyPeer{
178+
{
179+
NamespaceSelector: &metav1.LabelSelector{
180+
MatchLabels: map[string]string{
181+
"kubernetes.io/metadata.name": "openshift-console",
182+
},
183+
},
184+
PodSelector: &metav1.LabelSelector{
185+
MatchLabels: map[string]string{
186+
"app": "console",
187+
},
188+
},
189+
},
190+
},
191+
Ports: []networkingv1.NetworkPolicyPort{
192+
{
193+
Protocol: toPtr(corev1.ProtocolTCP),
194+
Port: toPtr(intstr.FromInt32(ConsoleUIHTTPSPort)),
195+
},
196+
},
197+
},
198+
},
199+
PolicyTypes: []networkingv1.PolicyType{
200+
networkingv1.PolicyTypeIngress,
201+
},
202+
}
203+
}

0 commit comments

Comments
 (0)