Skip to content

Commit ab6e100

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 f6df001 commit ab6e100

67 files changed

Lines changed: 1588 additions & 349 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
@@ -213,6 +213,11 @@ type OpenStackLightspeedStatus struct {
213213
// OpenStackReady indicates whether an OpenStackControlPlane was detected and
214214
// is ready. When true, the OpenStack MCP tools are included in lightspeed-stack config.
215215
OpenStackReady bool `json:"openStackReady,omitempty"`
216+
217+
// +optional
218+
// ApplicationCredentialSecret is the name of the current AC secret in the
219+
// OpenStack namespace. Tracked for rotation detection.
220+
ApplicationCredentialSecret string `json:"applicationCredentialSecret,omitempty"`
216221
}
217222

218223
// +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
@@ -187,6 +187,11 @@ spec:
187187
status:
188188
description: OpenStackLightspeedStatus defines the observed state of OpenStackLightspeed
189189
properties:
190+
applicationCredentialSecret:
191+
description: |-
192+
ApplicationCredentialSecret is the name of the current AC secret in the
193+
OpenStack namespace. Tracked for rotation detection.
194+
type: string
190195
conditions:
191196
description: Conditions
192197
items:

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

Lines changed: 36 additions & 1 deletion
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"
@@ -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
@@ -259,5 +259,10 @@ func getDynamicWatchCRDs() map[schema.GroupVersionKind]*atomic.Bool {
259259
Version: "v1beta1",
260260
Kind: "OpenStackControlPlane",
261261
}: new(atomic.Bool),
262+
{
263+
Group: "keystone.openstack.org",
264+
Version: "v1beta1",
265+
Kind: "KeystoneApplicationCredential",
266+
}: new(atomic.Bool),
262267
}
263268
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ spec:
187187
status:
188188
description: OpenStackLightspeedStatus defines the observed state of OpenStackLightspeed
189189
properties:
190+
applicationCredentialSecret:
191+
description: |-
192+
ApplicationCredentialSecret is the name of the current AC secret in the
193+
OpenStack namespace. Tracked for rotation detection.
194+
type: string
190195
conditions:
191196
description: Conditions
192197
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.40.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-20260402051712-545e8a4df936 // 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-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oX
7171
github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
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.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
210216
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
211217
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
@@ -218,13 +224,18 @@ golang.org/x/sync v0.21.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.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
223232
golang.org/x/sys v0.46.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.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
225235
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
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.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
229240
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
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
@@ -291,6 +291,15 @@ func OpenStackControlPlaneGVK() schema.GroupVersionKind {
291291
}
292292
}
293293

294+
// KeystoneApplicationCredentialGVK returns the GroupVersionKind for KeystoneApplicationCredential.
295+
func KeystoneApplicationCredentialGVK() schema.GroupVersionKind {
296+
return schema.GroupVersionKind{
297+
Group: KeystoneApplicationCredentialGroup,
298+
Version: KeystoneApplicationCredentialVersion,
299+
Kind: KeystoneApplicationCredentialKind,
300+
}
301+
}
302+
294303
// IsDynamicCRDWatched reports whether a controller-runtime watch is currently
295304
// registered for the given GVK. The flag is reset to false when a CRD is
296305
// removed and its broken informer is cleaned up. This does NOT indicate

0 commit comments

Comments
 (0)