Skip to content

Commit c9090c7

Browse files
committed
feat: switch to LSC app backend using magic configmap
Signed-off-by: Haoyu Sun <[email protected]>
1 parent d6cca73 commit c9090c7

File tree

8 files changed

+460
-1
lines changed

8 files changed

+460
-1
lines changed

internal/controller/constants.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const (
2828
/*** application server configuration file ***/
2929
// OLSConfigName is the name of the OLSConfig configmap
3030
OLSConfigCmName = "olsconfig"
31+
// AppServerConfigCmName is the name of the app server configmap
32+
AppServerConfigCmName = "olsconfig"
3133
// OLSCAConfigMap is the name of the OLS TLS ca certificate configmap
3234
OLSCAConfigMap = "openshift-service-ca.crt"
3335
// OLSNamespaceDefault is the default namespace for OLS
@@ -277,4 +279,6 @@ ssl_ca_file = '/etc/certs/cm-olspostgresca/service-ca.crt'
277279
OpenShiftMCPServerTimeout = 60
278280
// MCP server SSE read timeout, sec
279281
OpenShiftMCPServerHTTPReadTimeout = 30
282+
// LSCAppServerActivatorCmName is the name of the LSC app server activator configmap
283+
LSCAppServerActivatorCmName = "lsc-app-server-activator"
280284
)

internal/controller/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const (
6868
ErrGetConsolePluginDeployment = "failed to get Console Plugin deployment"
6969
ErrGetConsolePluginNetworkPolicy = "failed to get Console Plugin network policy"
7070
ErrGetConsolePluginService = "failed to get Console Plugin service"
71+
ErrGetLSCActivatorConfigmap = "failed to get LSC backend activator configmap"
7172
ErrGetLLMSecret = "failed to get LLM provider secret" // #nosec G101
7273
ErrGetOperatorNetworkPolicy = "failed to get operator network policy"
7374
ErrGetPostgresNetworkPolicy = "failed to get OLS Postgres network policy"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
6+
appsv1 "k8s.io/api/apps/v1"
7+
corev1 "k8s.io/api/core/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
10+
olsv1alpha1 "github.com/openshift/lightspeed-operator/api/v1alpha1"
11+
)
12+
13+
// todo: implement LSC config map generation
14+
func (r *OLSConfigReconciler) generateLSCConfigMap(ctx context.Context, cr *olsv1alpha1.OLSConfig) (*corev1.ConfigMap, error) {
15+
configMap := &corev1.ConfigMap{
16+
ObjectMeta: metav1.ObjectMeta{
17+
Name: AppServerConfigCmName,
18+
Namespace: r.Options.Namespace,
19+
},
20+
}
21+
return configMap, nil
22+
}
23+
24+
// todo: implement LSC deployment generation
25+
func (r *OLSConfigReconciler) generateLSCDeployment(ctx context.Context, cr *olsv1alpha1.OLSConfig) (*appsv1.Deployment, error) {
26+
deployment := &appsv1.Deployment{
27+
ObjectMeta: metav1.ObjectMeta{
28+
Name: OLSAppServerDeploymentName,
29+
Namespace: r.Options.Namespace,
30+
},
31+
Spec: appsv1.DeploymentSpec{
32+
Replicas: cr.Spec.OLSConfig.DeploymentConfig.Replicas,
33+
Selector: &metav1.LabelSelector{
34+
MatchLabels: generateAppServerSelectorLabels(),
35+
},
36+
Template: corev1.PodTemplateSpec{
37+
ObjectMeta: metav1.ObjectMeta{
38+
Labels: generateAppServerSelectorLabels(),
39+
},
40+
Spec: corev1.PodSpec{
41+
Containers: []corev1.Container{
42+
{
43+
Name: "lsc-app-server",
44+
Image: r.Options.LightspeedServiceImage,
45+
},
46+
},
47+
},
48+
},
49+
},
50+
}
51+
return deployment, nil
52+
}
53+
54+
// todo: implement LSC deployment update
55+
func (r *OLSConfigReconciler) updateLSCDeployment(ctx context.Context, existingDeployment, desiredDeployment *appsv1.Deployment) error {
56+
57+
return nil
58+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
appsv1 "k8s.io/api/apps/v1"
9+
logf "sigs.k8s.io/controller-runtime/pkg/log"
10+
11+
olsv1alpha1 "github.com/openshift/lightspeed-operator/api/v1alpha1"
12+
)
13+
14+
var _ = Describe("LSC App server assets", Label("LSCBackend"), Ordered, func() {
15+
var cr *olsv1alpha1.OLSConfig
16+
var r *OLSConfigReconciler
17+
var rOptions *OLSConfigReconcilerOptions
18+
var ctx context.Context
19+
20+
Context("LSC asset generation", func() {
21+
BeforeEach(func() {
22+
ctx = context.Background()
23+
rOptions = &OLSConfigReconcilerOptions{
24+
OpenShiftMajor: "123",
25+
OpenshiftMinor: "456",
26+
LightspeedServiceImage: "lightspeed-service:latest",
27+
OpenShiftMCPServerImage: "openshift-mcp-server:latest",
28+
Namespace: OLSNamespaceDefault,
29+
}
30+
cr = getDefaultOLSConfigCR()
31+
r = &OLSConfigReconciler{
32+
Options: *rOptions,
33+
logger: logf.Log.WithName("olsconfig.reconciler"),
34+
Client: k8sClient,
35+
Scheme: k8sClient.Scheme(),
36+
stateCache: make(map[string]string),
37+
}
38+
})
39+
40+
Describe("generateLSCConfigMap", func() {
41+
It("should generate a valid configmap", func() {
42+
cm, err := r.generateLSCConfigMap(ctx, cr)
43+
Expect(err).NotTo(HaveOccurred())
44+
Expect(cm).NotTo(BeNil())
45+
})
46+
47+
// TODO: Add more tests cases for once implementation is complete
48+
})
49+
50+
Describe("generateLSCDeployment", func() {
51+
It("should generate a valid deployment", func() {
52+
deployment, err := r.generateLSCDeployment(ctx, cr)
53+
Expect(err).NotTo(HaveOccurred())
54+
Expect(deployment).NotTo(BeNil())
55+
})
56+
57+
// TODO: Add more tests cases for once implementation is complete
58+
})
59+
60+
Describe("updateLSCDeployment", func() {
61+
var existingDeployment *appsv1.Deployment
62+
var desiredDeployment *appsv1.Deployment
63+
64+
BeforeEach(func() {
65+
existingDeployment, _ = r.generateLSCDeployment(ctx, cr)
66+
})
67+
68+
It("should successfully update deployment", func() {
69+
desiredDeployment, _ = r.generateLSCDeployment(ctx, cr)
70+
err := r.updateLSCDeployment(ctx, existingDeployment, desiredDeployment)
71+
Expect(err).NotTo(HaveOccurred())
72+
})
73+
74+
// TODO: Add more tests cases for once implementation is complete
75+
})
76+
})
77+
78+
})
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package controller
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
olsv1alpha1 "github.com/openshift/lightspeed-operator/api/v1alpha1"
8+
)
9+
10+
func (r *OLSConfigReconciler) reconcileAppServerLSC(ctx context.Context, olsconfig *olsv1alpha1.OLSConfig) error {
11+
r.logger.Info("reconcileAppServerLSC starts")
12+
tasks := []ReconcileTask{
13+
{
14+
Name: "reconcile ServiceAccount",
15+
Task: r.reconcileServiceAccount,
16+
},
17+
{
18+
Name: "reconcile SARRole",
19+
Task: r.reconcileSARRole,
20+
},
21+
{
22+
Name: "reconcile SARRoleBinding",
23+
Task: r.reconcileSARRoleBinding,
24+
},
25+
// todo: LSC config map generation
26+
// todo: Llama Stack configmap generation
27+
{
28+
Name: "reconcile OLSConfigMap",
29+
Task: r.reconcileLSCConfigMap,
30+
},
31+
{
32+
Name: "reconcile Additional CA ConfigMap",
33+
Task: r.reconcileOLSAdditionalCAConfigMap,
34+
},
35+
{
36+
Name: "reconcile App Service",
37+
Task: r.reconcileService,
38+
},
39+
{
40+
Name: "reconcile App TLS Certs",
41+
Task: r.reconcileTLSSecret,
42+
},
43+
// todo: LSC deployment generation
44+
{
45+
Name: "reconcile App Deployment",
46+
Task: r.reconcileLSCDeployment,
47+
},
48+
{
49+
Name: "reconcile Metrics Reader Secret",
50+
Task: r.reconcileMetricsReaderSecret,
51+
},
52+
{
53+
Name: "reconcile App ServiceMonitor",
54+
Task: r.reconcileServiceMonitor,
55+
},
56+
{
57+
Name: "reconcile App PrometheusRule",
58+
Task: r.reconcilePrometheusRule,
59+
},
60+
{
61+
Name: "reconcile App NetworkPolicy",
62+
Task: r.reconcileAppServerNetworkPolicy,
63+
},
64+
{
65+
Name: "reconcile Proxy CA ConfigMap",
66+
Task: r.reconcileProxyCAConfigMap,
67+
},
68+
}
69+
70+
for _, task := range tasks {
71+
err := task.Task(ctx, olsconfig)
72+
if err != nil {
73+
r.logger.Error(err, "reconcileAppServer error", "task", task.Name)
74+
return fmt.Errorf("failed to %s: %w", task.Name, err)
75+
}
76+
}
77+
78+
r.logger.Info("reconcileAppServer completes")
79+
80+
return nil
81+
}
82+
83+
func (r *OLSConfigReconciler) reconcileLSCConfigMap(ctx context.Context, cr *olsv1alpha1.OLSConfig) error {
84+
85+
return r.reconcileOLSConfigMap(ctx, cr)
86+
// TODO: implement LSC configmap reconciliation
87+
}
88+
89+
func (r *OLSConfigReconciler) reconcileLSCDeployment(ctx context.Context, cr *olsv1alpha1.OLSConfig) error {
90+
91+
return r.reconcileDeployment(ctx, cr)
92+
93+
// TODO: implement LSC deployment reconciliation
94+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package controller
2+
3+
import (
4+
"reflect"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
9+
appsv1 "k8s.io/api/apps/v1"
10+
corev1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/types"
13+
)
14+
15+
var _ = Describe("LSC App server reconciliator", Label("LSCBackend"), Ordered, func() {
16+
Context("Creation logic", Ordered, func() {
17+
var providerSecret *corev1.Secret
18+
var tlsSecret *corev1.Secret
19+
var configmap *corev1.ConfigMap
20+
BeforeEach(func() {
21+
By("create magic configmap")
22+
configmap, _ = generateRandomConfigMap()
23+
configmap.Name = LSCAppServerActivatorCmName
24+
configMapCreationErr := reconciler.Create(ctx, configmap)
25+
Expect(configMapCreationErr).NotTo(HaveOccurred())
26+
By("create the provider secret")
27+
providerSecret, _ = generateRandomSecret()
28+
secretCreationErr := reconciler.Create(ctx, providerSecret)
29+
Expect(secretCreationErr).NotTo(HaveOccurred())
30+
31+
By("create the default tls secret")
32+
tlsSecret, _ = generateRandomSecret()
33+
tlsSecret.Name = OLSCertsSecretName
34+
tlsSecret.SetOwnerReferences([]metav1.OwnerReference{
35+
{
36+
Kind: "Secret",
37+
APIVersion: "v1",
38+
UID: "ownerUID",
39+
Name: OLSCertsSecretName,
40+
},
41+
})
42+
secretCreationErr = reconciler.Create(ctx, tlsSecret)
43+
Expect(secretCreationErr).NotTo(HaveOccurred())
44+
45+
By("Set OLSConfig CR to default")
46+
crDefault := getDefaultOLSConfigCR()
47+
cr.Spec = crDefault.Spec
48+
})
49+
50+
AfterEach(func() {
51+
By("Delete the provider secret")
52+
secretDeletionErr := reconciler.Delete(ctx, providerSecret)
53+
Expect(secretDeletionErr).NotTo(HaveOccurred())
54+
55+
By("Delete the tls secret")
56+
secretDeletionErr = reconciler.Delete(ctx, tlsSecret)
57+
Expect(secretDeletionErr).NotTo(HaveOccurred())
58+
59+
By("Delete the magic configmap")
60+
configMapDeletionErr := reconciler.Delete(ctx, configmap)
61+
Expect(configMapDeletionErr).NotTo(HaveOccurred())
62+
})
63+
64+
It("should call reconcileAppServerLSC when the magic configmap exists", func() {
65+
By("Choose the correct reconcile function")
66+
reconcileFunc, err := reconciler.getAppServerReconcileFunction(ctx)
67+
Expect(err).NotTo(HaveOccurred())
68+
Expect(reflect.ValueOf(reconcileFunc).Pointer()).To(Equal(reflect.ValueOf(reconciler.reconcileAppServerLSC).Pointer()))
69+
})
70+
71+
It("should create a LSC configmap", func() {
72+
By("Reconcile the LSC app server")
73+
err := reconciler.reconcileAppServerLSC(ctx, cr)
74+
Expect(err).NotTo(HaveOccurred())
75+
76+
By("Get the config map")
77+
cm := &corev1.ConfigMap{}
78+
err = k8sClient.Get(ctx, types.NamespacedName{Name: AppServerConfigCmName, Namespace: OLSNamespaceDefault}, cm)
79+
Expect(err).NotTo(HaveOccurred())
80+
})
81+
82+
It("should create a Llama Stack configmap", func() {
83+
// todo: implement this test after the Llama Stack configmap implementation is complete
84+
Skip("Llama Stack configmap implementation is not complete")
85+
})
86+
87+
It("should create a deployment lightspeed-app-server", func() {
88+
By("Reconcile the LSC app server")
89+
err := reconciler.reconcileAppServerLSC(ctx, cr)
90+
Expect(err).NotTo(HaveOccurred())
91+
92+
By("Get the deployment")
93+
dep := &appsv1.Deployment{}
94+
err = k8sClient.Get(ctx, types.NamespacedName{Name: OLSAppServerDeploymentName, Namespace: OLSNamespaceDefault}, dep)
95+
Expect(err).NotTo(HaveOccurred())
96+
})
97+
98+
})
99+
100+
Context("LSC ConfigMap reconciliation", Ordered, func() {
101+
var providerSecret *corev1.Secret
102+
103+
BeforeEach(func() {
104+
By("create the provider secret")
105+
providerSecret, _ = generateRandomSecret()
106+
secretCreationErr := reconciler.Create(ctx, providerSecret)
107+
Expect(secretCreationErr).NotTo(HaveOccurred())
108+
109+
By("create the tls secret")
110+
tlsSecret, _ = generateRandomSecret()
111+
tlsSecret.Name = OLSCertsSecretName
112+
secretCreationErr = reconciler.Create(ctx, tlsSecret)
113+
Expect(secretCreationErr).NotTo(HaveOccurred())
114+
115+
By("Set OLSConfig CR to default")
116+
crDefault := getDefaultOLSConfigCR()
117+
cr.Spec = crDefault.Spec
118+
})
119+
120+
AfterEach(func() {
121+
By("Delete the provider secret")
122+
secretDeletionErr := reconciler.Delete(ctx, providerSecret)
123+
Expect(secretDeletionErr).NotTo(HaveOccurred())
124+
125+
By("Delete the tls secret")
126+
secretDeletionErr = reconciler.Delete(ctx, tlsSecret)
127+
Expect(secretDeletionErr).NotTo(HaveOccurred())
128+
})
129+
130+
It("should create a new LSC configmap when it does not exist", func() {
131+
By("Reconcile the LSC configmap")
132+
err := reconciler.reconcileLSCConfigMap(ctx, cr)
133+
Expect(err).NotTo(HaveOccurred())
134+
135+
By("Get the configmap")
136+
cm := &corev1.ConfigMap{}
137+
err = k8sClient.Get(ctx, types.NamespacedName{Name: AppServerConfigCmName, Namespace: OLSNamespaceDefault}, cm)
138+
Expect(err).NotTo(HaveOccurred())
139+
})
140+
})
141+
142+
})

0 commit comments

Comments
 (0)