Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions api/v1beta1/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const (
// OpenShift Lightspeed Operator Status=True condition which indicates if OpenShift Lightspeed is installed and
// operational and it can be used by OpenStack Lightspeed operator.
OpenShiftLightspeedOperatorReadyCondition condition.Type = "OpenShiftLightspeedOperatorReady"

// OpenStackLightspeedMCPServerReadyCondition is set to True when the MCP server
// deployment succeeds. False indicates a failure during MCP server deployment.
OpenStackLightspeedMCPServerReadyCondition condition.Type = "OpenStackLightspeedMCPServerReady"
)

// Common Messages used by API objects.
Expand All @@ -46,6 +50,24 @@ const (
// OpenShiftLightspeedOperatorReady
OpenShiftLightspeedOperatorReady = "OpenShift Lightspeed operator is ready."

// OpenStackLightspeedMCPServerInitMessage
OpenStackLightspeedMCPServerInitMessage = "MCP server deployment has not resolved"

// OpenStackLightspeedMCPServerDeployed
OpenStackLightspeedMCPServerDeployed = "MCP server is ready"

// OpenStackLightspeedMCPServerWaitingOpenStack
OpenStackLightspeedMCPServerWaitingOpenStack = "MCP server deployed, waiting for OpenStackControlPlane to become ready"

// OpenStackLightspeedMCPServerCreatingUser
OpenStackLightspeedMCPServerCreatingUser = "Creating OpenStack service user"

// OpenStackLightspeedMCPServerWaitingAC
OpenStackLightspeedMCPServerWaitingAC = "Waiting for application credential secret"

// OpenStackLightspeedMCPServerDisabledMessage
OpenStackLightspeedMCPServerDisabledMessage = "RHOSO MCP server is disabled (rhoso_mcps feature flag not set)"

// DeploymentCheckFailedMessage
DeploymentCheckFailedMessage = "Failed to check deployment status: %s"

Expand Down
21 changes: 20 additions & 1 deletion api/v1beta1/openstacklightspeed_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ const (
// OKPContainerImage is the fall-back container image for OKP (Offline Knowledge Portal)
OKPContainerImage = "registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest"

// MCPServerContainerImage is the fall-back container image for the MCP server
MCPServerContainerImage = "quay.io/openstack-lightspeed/lightspeed-mcps:latest"

// MaxTokensForResponseDefault is the default maximum number of tokens that should be used for response
MaxTokensForResponseDefault = 2048
)
Expand All @@ -54,13 +57,15 @@ const (
// May change at any time without backward compatibility.
//
// Supported fields:
// - featureFlags: list of experimental feature flags to enable
// - featureFlags: list of experimental feature flags to enable. Configuration options for experimental features must also live within the `DevSpec`.
// - okpChunkFilterQuery: Solr filter query for OKP searches (default: version-aware query combining detected OpenStack and OCP versions)
// - okpRagOnly: when true, only OKP is used as a RAG source (default: true)
// - rhosMCPConfig: custom YAML configuration for the rhos-mcps service; deep-merged on top of the operator defaults, openstack.enabled and openshift.enabled are always overridden by the operator
type DevSpec struct {
FeatureFlags []string `json:"featureFlags,omitempty"`
OKPChunkFilterQuery string `json:"okpChunkFilterQuery,omitempty"`
OKPRagOnly *bool `json:"okpRagOnly,omitempty"`
RhosMCPConfig string `json:"rhosMCPConfig,omitempty"`
}

// OKPSpec defines configuration for the Offline Knowledge Portal (OKP).
Expand Down Expand Up @@ -205,6 +210,16 @@ type OpenStackLightspeedStatus struct {

// ObservedGeneration - the most recent generation observed for this object.
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// +optional
// OpenStackReady indicates whether an OpenStackControlPlane was detected and
// is ready. When true, the OpenStack MCP tools are included in lightspeed-stack config.
OpenStackReady bool `json:"openStackReady,omitempty"`
Comment thread
lpiwowar marked this conversation as resolved.

// +optional
// ApplicationCredentialSecret is the name of the current AC secret in the
// OpenStack namespace. Tracked for rotation detection.
ApplicationCredentialSecret string `json:"applicationCredentialSecret,omitempty"`
}

// +kubebuilder:object:root=true
Expand All @@ -229,6 +244,7 @@ type OpenStackLightspeedStatus struct {
// +operator-sdk:csv:customresourcedefinitions:resources={{PersistentVolumeClaim,v1,openstack-lightspeed-database}}
// +operator-sdk:csv:customresourcedefinitions:resources={{ClusterRole,v1,lightspeed-app-server-sar-role}}
// +operator-sdk:csv:customresourcedefinitions:resources={{ClusterRoleBinding,v1,lightspeed-app-server-sar-role-binding}}
// +operator-sdk:csv:customresourcedefinitions:resources={{ConfigMap,v1,mcp-config}}
// +operator-sdk:csv:customresourcedefinitions:resources={{Subscription,v1alpha1}}
// +operator-sdk:csv:customresourcedefinitions:resources={{ClusterServiceVersion,v1alpha1}}
// +operator-sdk:csv:customresourcedefinitions:resources={{InstallPlan,v1alpha1}}
Expand Down Expand Up @@ -268,6 +284,7 @@ type OpenStackLightspeedDefaults struct {
ConsoleImageURL string
ConsoleImagePF5URL string
OKPImageURL string
MCPServerImageURL string
MaxTokensForResponse int
}

Expand All @@ -291,6 +308,8 @@ func SetupDefaults() {
"RELATED_IMAGE_CONSOLE_PF5_IMAGE_URL_DEFAULT", ConsoleContainerImagePF5),
OKPImageURL: util.GetEnvVar(
"RELATED_IMAGE_OKP_IMAGE_URL_DEFAULT", OKPContainerImage),
MCPServerImageURL: util.GetEnvVar(
"RELATED_IMAGE_MCP_SERVER_IMAGE_URL_DEFAULT", MCPServerContainerImage),
MaxTokensForResponse: MaxTokensForResponseDefault,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ spec:
status:
description: OpenStackLightspeedStatus defines the observed state of OpenStackLightspeed
properties:
applicationCredentialSecret:
description: |-
ApplicationCredentialSecret is the name of the current AC secret in the
OpenStack namespace. Tracked for rotation detection.
type: string
conditions:
description: Conditions
items:
Expand Down Expand Up @@ -234,6 +239,11 @@ spec:
for this object.
format: int64
type: integer
openStackReady:
description: |-
OpenStackReady indicates whether an OpenStackControlPlane was detected and
is ready. When true, the OpenStack MCP tools are included in lightspeed-stack config.
type: boolean
type: object
type: object
served: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ metadata:
]
capabilities: Basic Install
categories: AI/Machine Learning
createdAt: "2026-07-13T09:32:13Z"
createdAt: "2026-07-13T15:45:52Z"
description: AI-powered virtual assistant for Red Hat OpenStack Services on OpenShift
features.operators.openshift.io/cnf: "false"
features.operators.openshift.io/cni: "false"
Expand Down Expand Up @@ -110,6 +110,9 @@ spec:
- kind: ConfigMap
name: llama-stack-config
version: v1
- kind: ConfigMap
name: mcp-config
version: v1
- kind: Secret
name: metrics-reader-token
version: v1
Expand Down Expand Up @@ -162,6 +165,30 @@ spec:
spec:
clusterPermissions:
- rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- apiGroups:
- ""
resources:
- secrets
verbs:
- create
- get
- update
- apiGroups:
- ""
resourceNames:
- lightspeed-password
resources:
- secrets
verbs:
- delete
- get
- update
- apiGroups:
- ""
resourceNames:
Expand All @@ -170,6 +197,14 @@ spec:
- secrets
verbs:
- get
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- watch
- apiGroups:
- config.openshift.io
resources:
Expand All @@ -190,6 +225,32 @@ spec:
- patch
- update
- watch
- apiGroups:
- core.openstack.org
resources:
- openstackcontrolplanes
verbs:
- get
- list
- watch
- apiGroups:
- keystone.openstack.org
resources:
- keystoneapplicationcredentials
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- keystone.openstack.org
resources:
- keystoneapplicationcredentials/status
verbs:
- get
- apiGroups:
- lightspeed.openstack.org
resources:
Expand Down Expand Up @@ -305,6 +366,8 @@ spec:
value: registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-rhel9:1.0.12
- name: RELATED_IMAGE_CONSOLE_PF5_IMAGE_URL_DEFAULT
value: registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-pf5-rhel9:1.0.12
- name: RELATED_IMAGE_MCP_SERVER_IMAGE_URL_DEFAULT
value: quay.io/openstack-lightspeed/lightspeed-mcps:latest
image: quay.io/openstack-lightspeed/operator:latest
livenessProbe:
httpGet:
Expand Down Expand Up @@ -500,4 +563,6 @@ spec:
name: console-image-url-default
- image: registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-pf5-rhel9:1.0.12
name: console-pf5-image-url-default
- image: quay.io/openstack-lightspeed/lightspeed-mcps:latest
name: mcp-server-image-url-default
version: 0.0.1
48 changes: 45 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ import (
"fmt"
"os"
"strings"
"sync/atomic"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
Expand Down Expand Up @@ -62,6 +66,8 @@ func init() {
utilruntime.Must(consolev1.AddToScheme(scheme))

utilruntime.Must(openshiftv1.AddToScheme(scheme))

utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}

Expand Down Expand Up @@ -154,7 +160,15 @@ func main() {
setupLog.Info(fmt.Sprintf("openstack-lightspeed operator watches %s namespace", namespace))
}

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
cfg := ctrl.GetConfigOrDie()

kclient, err := kubernetes.NewForConfig(cfg)
if err != nil {
setupLog.Error(err, "unable to create kubernetes client")
os.Exit(1)
}

mgr, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,
Expand Down Expand Up @@ -184,9 +198,14 @@ func main() {
// Defaults for OpenStackLightspeed
apiv1beta1.SetupDefaults()

dynamicWatchCRDs := getDynamicWatchCRDs()

if err = (&controller.OpenStackLightspeedReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Kclient: kclient,
Scheme: mgr.GetScheme(),
Cache: mgr.GetCache(),
DynamicWatchCRD: dynamicWatchCRDs,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "OpenStackLightspeed")
os.Exit(1)
Expand Down Expand Up @@ -224,3 +243,26 @@ func getWatchNamespaces() ([]string, error) {

return strings.Split(ns, ","), nil
}

// getDynamicWatchCRDs returns a map of GroupVersionKind to *atomic.Bool
// representing resources that should be watched dynamically. The watch starts
// once they appear in the cluster for the first time (not required at operator
// start time).
//
// The OpenStackControlPlane GVK is hard-coded here to avoid pulling in the
// openstack-operator/api dependency (which is pinned to an older k8s version).
// The CRD is watched using unstructured types, so the Go type is not needed.
func getDynamicWatchCRDs() map[schema.GroupVersionKind]*atomic.Bool {
return map[schema.GroupVersionKind]*atomic.Bool{
{
Group: "core.openstack.org",
Version: "v1beta1",
Kind: "OpenStackControlPlane",
}: new(atomic.Bool),
{
Group: "keystone.openstack.org",
Version: "v1beta1",
Kind: "KeystoneApplicationCredential",
}: new(atomic.Bool),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ spec:
status:
description: OpenStackLightspeedStatus defines the observed state of OpenStackLightspeed
properties:
applicationCredentialSecret:
description: |-
ApplicationCredentialSecret is the name of the current AC secret in the
OpenStack namespace. Tracked for rotation detection.
type: string
conditions:
description: Conditions
items:
Expand Down Expand Up @@ -234,6 +239,11 @@ spec:
for this object.
format: int64
type: integer
openStackReady:
description: |-
OpenStackReady indicates whether an OpenStackControlPlane was detected and
is ready. When true, the OpenStack MCP tools are included in lightspeed-stack config.
type: boolean
type: object
type: object
served: true
Expand Down
2 changes: 2 additions & 0 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ spec:
value: registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-rhel9:1.0.12
- name: RELATED_IMAGE_CONSOLE_PF5_IMAGE_URL_DEFAULT
value: registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-pf5-rhel9:1.0.12
- name: RELATED_IMAGE_MCP_SERVER_IMAGE_URL_DEFAULT
value: quay.io/openstack-lightspeed/lightspeed-mcps:latest
securityContext:
allowPrivilegeEscalation: false
capabilities:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ spec:
- kind: ConfigMap
name: llama-stack-config
version: v1
- kind: ConfigMap
name: mcp-config
version: v1
- kind: Secret
name: metrics-reader-token
version: v1
Expand Down
Loading
Loading