Skip to content
Draft
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
15 changes: 15 additions & 0 deletions api/v1beta1/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ const (
// operational and it can be used by OpenStack Lightspeed operator.
OpenShiftLightspeedOperatorReadyCondition condition.Type = "OpenShiftLightspeedOperatorReady"

// OpenStackLightspeedMCPServerReadyCondition is set to True when the reconciliation of the MCP server succeeds.
// This can indicate either that no OpenStack deployment was detected (thus, no MCP server was needed), or that an
// MCP server was successfully deployed because an OpenStack deployment was present. If set to False, it means that
// there was a failure during the MCP server deployment process.
OpenStackLightspeedMCPServerReadyCondition condition.Type = "OpenStackLightspeedMCPServerReady"

// OCPRAGCondition Status=True condition which indicates the OCP RAG version resolution status
OCPRAGCondition condition.Type = "OCPRAGReady"
)
Expand Down Expand Up @@ -63,4 +69,13 @@ const (

// OCPRAGOverrideInvalidMessage
OCPRAGOverrideInvalidMessage = "Invalid OCP RAG version override"

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

// OpenStackLightspeedMCPServerInitNoDeployment
OpenStackLightspeedMCPServerNoDeployment = "MCP server not deployed (no OpenStack instance detected)"

// OpenStackLightspeedMCPServerDeployed
OpenStackLightspeedMCPServerDeployed = "MCP server is ready"
)
5 changes: 5 additions & 0 deletions api/v1beta1/openstacklightspeed_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (

// OpenStackLightspeedContainerImage is the fall-back container image for OpenStackLightspeed
OpenStackLightspeedContainerImage = "quay.io/openstack-lightspeed/rag-content:os-docs-2025.2"
MCPServerContainerImage = "quay.io/openstack-lightspeed/rhos-mcps:latest"
MaxTokensForResponseDefault = 2048
)

Expand Down Expand Up @@ -165,6 +166,7 @@ func (instance OpenStackLightspeed) IsReady() bool {

type OpenStackLightspeedDefaults struct {
RAGImageURL string
MCPServerImageURL string
MaxTokensForResponse int
}

Expand All @@ -176,6 +178,9 @@ func SetupDefaults() {
openStackLightspeedDefaults := OpenStackLightspeedDefaults{
RAGImageURL: util.GetEnvVar(
"RELATED_IMAGE_OPENSTACK_LIGHTSPEED_IMAGE_URL_DEFAULT", OpenStackLightspeedContainerImage),
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 @@ -98,6 +98,27 @@ spec:
spec:
clusterPermissions:
- rules:
- apiGroups:
- ""
resources:
- configmaps
- secrets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- watch
- apiGroups:
- config.openshift.io
resources:
Expand All @@ -106,6 +127,14 @@ spec:
- get
- list
- watch
- apiGroups:
- core.openstack.org
resources:
- openstackcontrolplanes
verbs:
- get
- list
- watch
- apiGroups:
- lightspeed.openstack.org
resources:
Expand Down Expand Up @@ -214,6 +243,8 @@ spec:
fieldPath: metadata.annotations['olm.targetNamespaces']
- name: RELATED_IMAGE_OPENSTACK_LIGHTSPEED_IMAGE_URL_DEFAULT
value: quay.io/openstack-lightspeed/rag-content:os-docs-2025.2
- name: RELATED_IMAGE_MCP_SERVER_IMAGE_URL_DEFAULT
value: quay.io/openstack-lightspeed/rhos-mcps:latest
image: quay.io/openstack-lightspeed/operator:latest
livenessProbe:
httpGet:
Expand Down Expand Up @@ -341,4 +372,6 @@ spec:
relatedImages:
- image: quay.io/openstack-lightspeed/rag-content:os-docs-2025.2
name: openstack-lightspeed-image-url-default
- image: quay.io/openstack-lightspeed/rhos-mcps:latest
name: mcp-server-image-url-default
version: 0.0.1
39 changes: 37 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ 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"

openstackv1beta1 "github.com/openstack-k8s-operators/openstack-operator/api/core/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
Expand All @@ -39,6 +42,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook"

operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"

apiv1beta1 "github.com/openstack-lightspeed/operator/api/v1beta1"
"github.com/openstack-lightspeed/operator/internal/controller"
Expand All @@ -56,6 +60,10 @@ func init() {
utilruntime.Must(operatorsv1alpha1.AddToScheme(scheme))

utilruntime.Must(apiv1beta1.AddToScheme(scheme))

utilruntime.Must(apiextensionsv1.AddToScheme(scheme))

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

Expand Down Expand Up @@ -170,9 +178,17 @@ func main() {
// Defaults for OpenStackLightspeed
apiv1beta1.SetupDefaults()

dynamicWatchCRDs, err := getDynamicWatchCRDs()
if err != nil {
setupLog.Error(err, "unable to retrieve DynamicWatchCRDs")
os.Exit(1)
}

if err = (&controller.OpenStackLightspeedReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
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 @@ -210,3 +226,22 @@ 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).
func getDynamicWatchCRDs() (map[schema.GroupVersionKind]*atomic.Bool, error) {
var openStackControlPlane openstackv1beta1.OpenStackControlPlane
openStackControlPlaneGVKs, _, err := scheme.ObjectKinds(&openStackControlPlane)
if err != nil {
return nil, err
}

dynamicWatchCRDs := make(map[schema.GroupVersionKind]*atomic.Bool)
for _, gvk := range openStackControlPlaneGVKs {
dynamicWatchCRDs[gvk] = new(atomic.Bool)
}

return dynamicWatchCRDs, nil
}
2 changes: 2 additions & 0 deletions config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ spec:
fieldPath: metadata.namespace
- name: RELATED_IMAGE_OPENSTACK_LIGHTSPEED_IMAGE_URL_DEFAULT
value: quay.io/openstack-lightspeed/rag-content:os-docs-2025.2
- name: RELATED_IMAGE_MCP_SERVER_IMAGE_URL_DEFAULT
value: quay.io/openstack-lightspeed/rhos-mcps:latest
securityContext:
allowPrivilegeEscalation: false
capabilities:
Expand Down
29 changes: 29 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ kind: ClusterRole
metadata:
name: manager-role
rules:
- apiGroups:
- ""
resources:
- configmaps
- secrets
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- apiextensions.k8s.io
resources:
- customresourcedefinitions
verbs:
- get
- list
- watch
- apiGroups:
- config.openshift.io
resources:
Expand All @@ -12,6 +33,14 @@ rules:
- get
- list
- watch
- apiGroups:
- core.openstack.org
resources:
- openstackcontrolplanes
verbs:
- get
- list
- watch
- apiGroups:
- lightspeed.openstack.org
resources:
Expand Down
Loading
Loading