Skip to content

Commit 6ea5e2b

Browse files
Akrogclaude
andcommitted
MCP Use Keystone Application Credentials
Initial implementation of the MCP deployment uses the credentials from the `openstackclient` pod, which means that we are not the owners of that secret, just the copy we make in the `openstack-lightspeed` namespace, so those credentials could be removed/deleted and that would break our `openstack-cli` tool. In this patch we change the credentials and we leverage the `KeystoneApplicationCredential` CR to get our own credentials. Credential Rotation is handled by the code as well. Jira: OSPRH-27075 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1d35a51 commit 6ea5e2b

67 files changed

Lines changed: 1589 additions & 350 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/v1beta1/conditions.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ const (
5959
// OpenStackLightspeedMCPServerWaitingOpenStack
6060
OpenStackLightspeedMCPServerWaitingOpenStack = "MCP server deployed, waiting for OpenStackControlPlane to become ready"
6161

62+
// OpenStackLightspeedMCPServerCreatingUser
63+
OpenStackLightspeedMCPServerCreatingUser = "Creating OpenStack service user"
64+
65+
// OpenStackLightspeedMCPServerWaitingAC
66+
OpenStackLightspeedMCPServerWaitingAC = "Waiting for application credential secret"
67+
6268
// OpenStackLightspeedMCPServerDisabledMessage
6369
OpenStackLightspeedMCPServerDisabledMessage = "RHOS MCP server is disabled (rhos_mcps feature flag not set)"
6470

api/v1beta1/openstacklightspeed_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ type OpenStackLightspeedStatus struct {
210210
// OpenStackReady indicates whether an OpenStackControlPlane was detected and
211211
// is ready. When true, the OpenStack MCP tools are included in lightspeed-stack config.
212212
OpenStackReady bool `json:"openStackReady,omitempty"`
213+
214+
// +optional
215+
// ApplicationCredentialSecret is the name of the current AC secret in the
216+
// OpenStack namespace. Tracked for rotation detection.
217+
ApplicationCredentialSecret string `json:"applicationCredentialSecret,omitempty"`
213218
}
214219

215220
// +kubebuilder:object:root=true

bundle/manifests/lightspeed.openstack.org_openstacklightspeeds.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ spec:
184184
status:
185185
description: OpenStackLightspeedStatus defines the observed state of OpenStackLightspeed
186186
properties:
187+
applicationCredentialSecret:
188+
description: |-
189+
ApplicationCredentialSecret is the name of the current AC secret in the
190+
OpenStack namespace. Tracked for rotation detection.
191+
type: string
187192
conditions:
188193
description: Conditions
189194
items:

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ metadata:
2525
]
2626
capabilities: Basic Install
2727
categories: AI/Machine Learning
28-
createdAt: "2026-07-13T09:32:13Z"
28+
createdAt: "2026-07-13T15:45:52Z"
2929
description: AI-powered virtual assistant for Red Hat OpenStack Services on OpenShift
3030
features.operators.openshift.io/cnf: "false"
3131
features.operators.openshift.io/cni: "false"
@@ -38,7 +38,7 @@ metadata:
3838
features.operators.openshift.io/token-auth-azure: "false"
3939
features.operators.openshift.io/token-auth-gcp: "false"
4040
operatorframework.io/suggested-namespace: openstack-lightspeed
41-
operators.operatorframework.io/builder: operator-sdk-v1.38.0
41+
operators.operatorframework.io/builder: operator-sdk-v1.41.1
4242
operators.operatorframework.io/project_layout: go.kubebuilder.io/v4
4343
repository: https://github.com/openstack-k8s-operators/lightspeed-operator
4444
name: openstack-lightspeed-operator.v0.0.1
@@ -169,9 +169,26 @@ spec:
169169
- ""
170170
resources:
171171
- configmaps
172+
verbs:
173+
- get
174+
- apiGroups:
175+
- ""
176+
resources:
172177
- secrets
173178
verbs:
179+
- create
180+
- get
181+
- update
182+
- apiGroups:
183+
- ""
184+
resourceNames:
185+
- lightspeed-password
186+
resources:
187+
- secrets
188+
verbs:
189+
- delete
174190
- get
191+
- update
175192
- apiGroups:
176193
- ""
177194
resourceNames:
@@ -216,6 +233,24 @@ spec:
216233
- get
217234
- list
218235
- watch
236+
- apiGroups:
237+
- keystone.openstack.org
238+
resources:
239+
- keystoneapplicationcredentials
240+
verbs:
241+
- create
242+
- delete
243+
- get
244+
- list
245+
- patch
246+
- update
247+
- watch
248+
- apiGroups:
249+
- keystone.openstack.org
250+
resources:
251+
- keystoneapplicationcredentials/status
252+
verbs:
253+
- get
219254
- apiGroups:
220255
- lightspeed.openstack.org
221256
resources:

cmd/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,5 +257,10 @@ func getDynamicWatchCRDs() map[schema.GroupVersionKind]*atomic.Bool {
257257
Version: "v1beta1",
258258
Kind: "OpenStackControlPlane",
259259
}: new(atomic.Bool),
260+
{
261+
Group: "keystone.openstack.org",
262+
Version: "v1beta1",
263+
Kind: "KeystoneApplicationCredential",
264+
}: new(atomic.Bool),
260265
}
261266
}

config/crd/bases/lightspeed.openstack.org_openstacklightspeeds.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ spec:
184184
status:
185185
description: OpenStackLightspeedStatus defines the observed state of OpenStackLightspeed
186186
properties:
187+
applicationCredentialSecret:
188+
description: |-
189+
ApplicationCredentialSecret is the name of the current AC secret in the
190+
OpenStack namespace. Tracked for rotation detection.
191+
type: string
187192
conditions:
188193
description: Conditions
189194
items:

config/rbac/role.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,26 @@ rules:
88
- ""
99
resources:
1010
- configmaps
11+
verbs:
12+
- get
13+
- apiGroups:
14+
- ""
15+
resources:
1116
- secrets
1217
verbs:
18+
- create
19+
- get
20+
- update
21+
- apiGroups:
22+
- ""
23+
resourceNames:
24+
- lightspeed-password
25+
resources:
26+
- secrets
27+
verbs:
28+
- delete
1329
- get
30+
- update
1431
- apiGroups:
1532
- ""
1633
resourceNames:
@@ -55,6 +72,24 @@ rules:
5572
- get
5673
- list
5774
- watch
75+
- apiGroups:
76+
- keystone.openstack.org
77+
resources:
78+
- keystoneapplicationcredentials
79+
verbs:
80+
- create
81+
- delete
82+
- get
83+
- list
84+
- patch
85+
- update
86+
- watch
87+
- apiGroups:
88+
- keystone.openstack.org
89+
resources:
90+
- keystoneapplicationcredentials/status
91+
verbs:
92+
- get
5893
- apiGroups:
5994
- lightspeed.openstack.org
6095
resources:

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/onsi/gomega v1.39.0
1212
github.com/openshift/api v3.9.0+incompatible // from lib-common
1313
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.0
14+
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.0
1415
github.com/operator-framework/api v0.37.0
1516
k8s.io/api v0.34.2
1617
k8s.io/apiextensions-apiserver v0.34.2
@@ -24,6 +25,8 @@ require (
2425
// must be consistent within modules and service operators
2526
replace github.com/openshift/api => github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e
2627

28+
require k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
29+
2730
require (
2831
cel.dev/expr v0.24.0 // indirect
2932
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
@@ -50,6 +53,7 @@ require (
5053
github.com/google/go-cmp v0.7.0 // indirect
5154
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
5255
github.com/google/uuid v1.6.0 // indirect
56+
github.com/gophercloud/gophercloud v1.14.1 // indirect
5357
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
5458
github.com/inconshreveable/mousetrap v1.1.0 // indirect
5559
github.com/josharian/intern v1.0.0 // indirect
@@ -104,7 +108,6 @@ require (
104108
k8s.io/component-base v0.34.2 // indirect
105109
k8s.io/klog/v2 v2.130.1 // indirect
106110
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
107-
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
108111
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect
109112
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
110113
sigs.k8s.io/randfill v1.0.0 // indirect

go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
7171
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
7272
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
7373
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
74+
github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw=
75+
github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
7476
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
7577
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
7678
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -113,6 +115,8 @@ github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e h1:E1OdwSpqWuDPCedyU
113115
github.com/openshift/api v0.0.0-20250711200046-c86d80652a9e/go.mod h1:Shkl4HanLwDiiBzakv+con/aMGnVE2MAGvoKp5oyYUo=
114116
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.0 h1:2TD4hi+MLt67jKxJUs2tuBKFMxibrLJQqKqhsTMsHeQ=
115117
github.com/openstack-k8s-operators/lib-common/modules/common v0.6.0/go.mod h1:rgpcv2tLD+/vudXx/gpIQSTuRpk4GOxHx84xwfvQalM=
118+
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.0 h1:8BniQwsPk8qjqoniLFDLnBEJgA0FLOwIrPDv93URiMo=
119+
github.com/openstack-k8s-operators/lib-common/modules/openstack v0.6.0/go.mod h1:tfMa+ochq7Dyilq9hQr2CEPfPtsj6IUgMmMqi4CWDmo=
116120
github.com/operator-framework/api v0.37.0 h1:2XCMWitBnumtJTqzip6LQKUwpM2pXVlt3gkpdlkbaCE=
117121
github.com/operator-framework/api v0.37.0/go.mod h1:NZs4vB+Jiamyv3pdPDjZtuC4U7KX0eq4z2r5hKY5fUA=
118122
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -196,6 +200,7 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
196200
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
197201
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
198202
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
203+
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
199204
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
200205
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
201206
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -206,6 +211,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
206211
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
207212
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
208213
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
214+
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
209215
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
210216
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
211217
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
@@ -218,13 +224,18 @@ golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
218224
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
219225
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
220226
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
227+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
228+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
229+
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
221230
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
222231
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
223232
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
233+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
224234
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
225235
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
226236
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
227237
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
238+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
228239
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
229240
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
230241
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
@@ -258,6 +269,7 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP
258269
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
259270
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
260271
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
272+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
261273
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
262274
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
263275
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/controller/common.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,15 @@ func OpenStackControlPlaneGVK() schema.GroupVersionKind {
261261
}
262262
}
263263

264+
// KeystoneApplicationCredentialGVK returns the GroupVersionKind for KeystoneApplicationCredential.
265+
func KeystoneApplicationCredentialGVK() schema.GroupVersionKind {
266+
return schema.GroupVersionKind{
267+
Group: KeystoneApplicationCredentialGroup,
268+
Version: KeystoneApplicationCredentialVersion,
269+
Kind: KeystoneApplicationCredentialKind,
270+
}
271+
}
272+
264273
// IsDynamicCRDWatched reports whether a controller-runtime watch is currently
265274
// registered for the given GVK. The flag is reset to false when a CRD is
266275
// removed and its broken informer is cleaned up. This does NOT indicate

0 commit comments

Comments
 (0)