Skip to content

Commit c851c39

Browse files
committed
Enable OKP support
This commit adds the first step in supporting OKP for OpenStack Lightspeed. Two new configuration sections were added: "okp" and "dev". The "okp" section holds configurations that are more final to the deployment where the "dev" is an easy to way for developers to test the feature while tests/developing it. The options available in "okp" section are: - offline: Whether OKP should be in "offline" mode or not. - accessKey: The OKP access key to decrypt the paid content in the image The options available in the "dev" section are: - featureFlags: This is a list of features that operator wants to expose to be enabled. The value "okp" in the list will deploy OKP. - okpChunkFilterQuery: This is where we can tweak the OKP filter to filter by product, version etc... Signed-off-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
1 parent 34d4f23 commit c851c39

14 files changed

Lines changed: 499 additions & 6 deletions

api/v1beta1/openstacklightspeed_types.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,43 @@ const (
4545
// ConsoleContainerImagePF5 is the fall-back console image for PatternFly 5 (OCP < 4.19)
4646
ConsoleContainerImagePF5 = "registry.redhat.io/openshift-lightspeed/lightspeed-console-plugin-pf5-rhel9:1.0.12"
4747

48+
// OKPContainerImage is the fall-back container image for OKP (Offline Knowledge Portal)
49+
OKPContainerImage = "registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest"
50+
4851
// MaxTokensForResponseDefault is the default maximum number of tokens that should be used for response
4952
MaxTokensForResponseDefault = 2048
5053
)
5154

55+
// DevSpec defines developer/experimental feature configuration.
56+
type DevSpec struct {
57+
// +kubebuilder:validation:Optional
58+
// FeatureFlags is a list of feature flag names to enable experimental features.
59+
// Supported flags: "okp" (Offline Knowledge Portal).
60+
FeatureFlags []string `json:"featureFlags,omitempty"`
61+
62+
// +kubebuilder:validation:Optional
63+
// OKPChunkFilterQuery is a static Solr filter query appended to OKP searches.
64+
// Combined with the default "is_chunk:true" filter using AND.
65+
// Example: "product:*openstack*"
66+
OKPChunkFilterQuery string `json:"okpChunkFilterQuery,omitempty"`
67+
}
68+
69+
// OKPSpec defines configuration for the Offline Knowledge Portal (OKP).
70+
type OKPSpec struct {
71+
// +kubebuilder:validation:Optional
72+
// +kubebuilder:default=true
73+
// Offline controls how source URLs are resolved.
74+
// When true, uses parent_id (offline/Mimir-style).
75+
// When false, uses reference_url (online).
76+
Offline *bool `json:"offline,omitempty"`
77+
78+
// +kubebuilder:validation:Optional
79+
// AccessKey is the name of the Secret containing the access key for the OKP server.
80+
// The secret must contain a key named "access_key".
81+
// An access key can be obtained from https://access.redhat.com/offline/access
82+
AccessKey string `json:"accessKey,omitempty"`
83+
}
84+
5285
// DatabaseSpec defines configuration for persistent PostgreSQL storage.
5386
type DatabaseSpec struct {
5487
// +kubebuilder:validation:Optional
@@ -84,6 +117,15 @@ type OpenStackLightspeedSpec struct {
84117
// When omitted, an emptyDir volume is used (data is lost on pod reschedule).
85118
// When set, a PersistentVolumeClaim is created and mounted.
86119
Database *DatabaseSpec `json:"database,omitempty"`
120+
121+
// +kubebuilder:validation:Optional
122+
// Dev contains developer/experimental feature configuration.
123+
Dev *DevSpec `json:"dev,omitempty"`
124+
125+
// +kubebuilder:validation:Optional
126+
// OKP configures the Offline Knowledge Portal (OKP) RAG source.
127+
// Only used when "okp" is present in dev.featureFlags.
128+
OKP *OKPSpec `json:"okp,omitempty"`
87129
}
88130

89131
// LoggingConfig defines logging configuration for OpenStackLightspeed components
@@ -242,6 +284,7 @@ type OpenStackLightspeedDefaults struct {
242284
PostgresImageURL string
243285
ConsoleImageURL string
244286
ConsoleImagePF5URL string
287+
OKPImageURL string
245288
MaxTokensForResponse int
246289
}
247290

@@ -263,6 +306,8 @@ func SetupDefaults() {
263306
"RELATED_IMAGE_CONSOLE_IMAGE_URL_DEFAULT", ConsoleContainerImage),
264307
ConsoleImagePF5URL: util.GetEnvVar(
265308
"RELATED_IMAGE_CONSOLE_PF5_IMAGE_URL_DEFAULT", ConsoleContainerImagePF5),
309+
OKPImageURL: util.GetEnvVar(
310+
"RELATED_IMAGE_OKP_IMAGE_URL_DEFAULT", OKPContainerImage),
266311
MaxTokensForResponse: MaxTokensForResponseDefault,
267312
}
268313

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,23 @@ spec:
6969
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
7070
x-kubernetes-int-or-string: true
7171
type: object
72+
dev:
73+
description: Dev contains developer/experimental feature configuration.
74+
properties:
75+
featureFlags:
76+
description: |-
77+
FeatureFlags is a list of feature flag names to enable experimental features.
78+
Supported flags: "okp" (Offline Knowledge Portal).
79+
items:
80+
type: string
81+
type: array
82+
okpChunkFilterQuery:
83+
description: |-
84+
OKPChunkFilterQuery is a static Solr filter query appended to OKP searches.
85+
Combined with the default "is_chunk:true" filter using AND.
86+
Example: "product:*openstack*"
87+
type: string
88+
type: object
7289
enableOCPRAG:
7390
default: false
7491
description: Enables automatic OCP documentation based on cluster
@@ -155,6 +172,25 @@ spec:
155172
Allows forcing a specific OCP version instead of auto-detection.
156173
Format should be like "4.15", "4.16", etc.
157174
type: string
175+
okp:
176+
description: |-
177+
OKP configures the Offline Knowledge Portal (OKP) RAG source.
178+
Only used when "okp" is present in dev.featureFlags.
179+
properties:
180+
accessKey:
181+
description: |-
182+
AccessKey is the name of the Secret containing the access key for the OKP server.
183+
The secret must contain a key named "access_key".
184+
An access key can be obtained from https://access.redhat.com/offline/access
185+
type: string
186+
offline:
187+
default: true
188+
description: |-
189+
Offline controls how source URLs are resolved.
190+
When true, uses parent_id (offline/Mimir-style).
191+
When false, uses reference_url (online).
192+
type: boolean
193+
type: object
158194
ragImage:
159195
description: ContainerImage for the OpenStack Lightspeed RAG container
160196
(will be set to environmental default if empty)

config/manager/manager.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ spec:
8181
value: quay.io/lightspeed-core/lightspeed-to-dataverse-exporter:latest
8282
- name: RELATED_IMAGE_POSTGRES_IMAGE_URL_DEFAULT
8383
value: registry.redhat.io/rhel9/postgresql-16:latest
84+
- name: RELATED_IMAGE_OKP_IMAGE_URL_DEFAULT
85+
value: registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest
8486
securityContext:
8587
allowPrivilegeEscalation: false
8688
capabilities:

config/samples/api_v1beta1_openstacklightspeed.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,10 @@ spec:
1515
# database:
1616
# size: "5Gi"
1717
# class: "my-storage-class"
18+
# Uncomment to enable OKP (Offline Knowledge Portal) as an Inline RAG source:
19+
# dev:
20+
# featureFlags:
21+
# - okp
22+
# okpChunkFilterQuery: "product:*openstack*"
23+
# okp:
24+
# accessKey: okp-access-key-secret

hack/env.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export RELATED_IMAGE_POSTGRES_IMAGE_URL_DEFAULT="registry.redhat.io/rhel9/postgr
66
# the automated pipeline for building OGX-compatible vector database images
77
# is ready.
88
export RELATED_IMAGE_OPENSTACK_LIGHTSPEED_IMAGE_URL_DEFAULT="quay.io/openstack-lightspeed/rag-content:alpha-ogx-os-docs-2025.2"
9+
export RELATED_IMAGE_OKP_IMAGE_URL_DEFAULT="registry.redhat.io/offline-knowledge-portal/rhokp-rhel9:latest"
910
export WATCH_NAMESPACE="openstack-lightspeed"

internal/controller/common.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import (
2121
_ "embed"
2222
"errors"
2323
"fmt"
24+
"slices"
2425
"strings"
2526

2627
common_helper "github.com/openstack-k8s-operators/lib-common/modules/common/helper"
28+
apiv1beta1 "github.com/openstack-lightspeed/operator/api/v1beta1"
2729
appsv1 "k8s.io/api/apps/v1"
2830
corev1 "k8s.io/api/core/v1"
2931
k8s_errors "k8s.io/apimachinery/pkg/api/errors"
@@ -126,6 +128,24 @@ func isDeploymentReady(deploy *appsv1.Deployment) bool {
126128
deploy.Status.Replicas == replicas
127129
}
128130

131+
// generateOKPSelectorLabels returns selector labels for OKP components.
132+
func generateOKPSelectorLabels() map[string]string {
133+
return map[string]string{
134+
"app.kubernetes.io/component": "okp-server",
135+
"app.kubernetes.io/managed-by": "openstack-lightspeed-operator",
136+
"app.kubernetes.io/name": "openstack-lightspeed-okp-server",
137+
"app.kubernetes.io/part-of": "openstack-lightspeed",
138+
}
139+
}
140+
141+
// isOKPEnabled returns true if the "okp" feature flag is present in dev.featureFlags.
142+
func isOKPEnabled(instance *apiv1beta1.OpenStackLightspeed) bool {
143+
if instance.Spec.Dev == nil {
144+
return false
145+
}
146+
return slices.Contains(instance.Spec.Dev.FeatureFlags, "okp")
147+
}
148+
129149
// getDeployment retrieves deployment from the cluster
130150
func getDeployment(ctx context.Context, h *common_helper.Helper, name string, namespace string) (*appsv1.Deployment, error) {
131151
deployment := &appsv1.Deployment{}

internal/controller/constants.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@ const (
8787
RHOSOLightspeedOwnerIDLabel = "openstack.org/lightspeed-owner-id"
8888
ServiceIDRHOSO = "rhos-lightspeed"
8989

90+
// OKP (Offline Knowledge Portal)
91+
OKPContainerName = "okp"
92+
OKPContainerPort = int32(8080)
93+
OKPDeploymentName = "lightspeed-okp-server"
94+
OKPServiceName = "lightspeed-okp-server"
95+
OKPServicePort = int32(8080)
96+
OKPAccessKeySecretKey = "access_key"
97+
ExternalProvidersDir = "/app-root/providers.d"
98+
9099
// Console Plugin
91100
ConsoleUIConfigMapName = "lightspeed-console-plugin"
92101
ConsoleUIServiceCertSecretName = "lightspeed-console-plugin-cert"

internal/controller/errors.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ var (
5252
ErrDeactivateConsolePlugin = errors.New("failed to deactivate console plugin")
5353
ErrDeleteConsolePlugin = errors.New("failed to delete console plugin")
5454

55+
// OKP Errors
56+
ErrCreateOKPDeployment = errors.New("failed to create OKP deployment")
57+
ErrCreateOKPService = errors.New("failed to create OKP service")
58+
ErrDeleteOKPDeployment = errors.New("failed to delete OKP deployment")
59+
ErrDeleteOKPService = errors.New("failed to delete OKP service")
60+
5561
// Postgres Errors
5662
ErrCreatePostgresDeployment = errors.New("failed to create Postgres deployment")
5763
ErrCreatePostgresService = errors.New("failed to create Postgres service")

internal/controller/lcore_config.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,33 @@ ingress_connection_timeout: 30
202202
}
203203
}
204204

205+
func buildOKPConfig(instance *apiv1beta1.OpenStackLightspeed) map[string]interface{} {
206+
offline := true
207+
if instance.Spec.OKP != nil && instance.Spec.OKP.Offline != nil {
208+
offline = *instance.Spec.OKP.Offline
209+
}
210+
211+
okpConfig := map[string]interface{}{
212+
"rhokp_url": "${env.RH_SERVER_OKP}",
213+
"offline": offline,
214+
}
215+
if instance.Spec.Dev != nil && instance.Spec.Dev.OKPChunkFilterQuery != "" {
216+
okpConfig["chunk_filter_query"] = instance.Spec.Dev.OKPChunkFilterQuery
217+
}
218+
return okpConfig
219+
}
220+
205221
// buildLCoreConfigYAML assembles the complete Lightspeed Core Service configuration and converts to YAML.
206222
// NOTE: MCP servers, quota handlers, and tools approval features are disabled for OpenStack Lightspeed.
207223
func buildLCoreConfigYAML(h *common_helper.Helper, instance *apiv1beta1.OpenStackLightspeed) (string, error) {
224+
ragInline := []interface{}{}
225+
if isOKPEnabled(instance) {
226+
ragInline = append(ragInline, "okp")
227+
}
228+
ragConfig := map[string]interface{}{
229+
"inline": ragInline,
230+
}
231+
208232
// Build the complete config as a map
209233
config := map[string]interface{}{
210234
"name": "Lightspeed Core Service (LCS)",
@@ -217,9 +241,11 @@ func buildLCoreConfigYAML(h *common_helper.Helper, instance *apiv1beta1.OpenStac
217241
"customization": buildLCoreCustomizationConfig(),
218242
"conversation_cache": buildLCoreConversationCacheConfig(h, instance),
219243
"byok_rag": []interface{}{},
220-
"rag": map[string]interface{}{
221-
"inline": []interface{}{},
222-
},
244+
"rag": ragConfig,
245+
}
246+
247+
if isOKPEnabled(instance) {
248+
config["okp"] = buildOKPConfig(instance)
223249
}
224250

225251
// Convert to YAML

0 commit comments

Comments
 (0)