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
20 changes: 18 additions & 2 deletions api/v1alpha1/olsconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,9 +505,25 @@ type ProxyConfig struct {
// +kubebuilder:validation:Pattern=`^https?://.*$`
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Proxy URL"
ProxyURL string `json:"proxyURL,omitempty"`
// The configmap holding proxy CA certificate
// The configmap and key holding proxy CA certificate.
// The key is optional and defaults to "proxy-ca.crt" for backward compatibility.
// If you use a different key name in your ConfigMap, specify it in the ConfigMapKeySelector.
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Proxy CA Certificate"
ProxyCACertificateRef *corev1.LocalObjectReference `json:"proxyCACertificate,omitempty"`
ProxyCACertificateRef *ProxyCACertConfigMapRef `json:"proxyCACertificate,omitempty"`
}

// ProxyCACertConfigMapRef references a ConfigMap containing the proxy CA certificate.
// Provides backward compatibility by making the key field optional with a default value.
type ProxyCACertConfigMapRef struct {
// Name of the ConfigMap containing the proxy CA certificate
// +kubebuilder:validation:Required
// +required
Name string `json:"name"`
// Key in the ConfigMap that contains the proxy CA certificate.
// Defaults to "proxy-ca.crt" if not specified.
// +kubebuilder:default="proxy-ca.crt"
// +optional
Key string `json:"key,omitempty"`
}

// MCPServer defines the settings for a single MCP server.
Expand Down
17 changes: 16 additions & 1 deletion api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 14 additions & 9 deletions config/crd/bases/ols.openshift.io_olsconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4346,19 +4346,24 @@ spec:
such as LLM providers.
properties:
proxyCACertificate:
description: The configmap holding proxy CA certificate
description: |-
The configmap and key holding proxy CA certificate.
The key is optional and defaults to "proxy-ca.crt" for backward compatibility.
If you use a different key name in your ConfigMap, specify it in the ConfigMapKeySelector.
properties:
name:
default: ""
key:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There definitely should be a default value for the key matching the value we currently expect. We should not require clients who have gotten this to work to now have to specify the key

default: proxy-ca.crt
description: |-
Name of the referent.
This field is effectively required, but due to backwards compatibility is
allowed to be empty. Instances of this type with an empty value here are
almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
Key in the ConfigMap that contains the proxy CA certificate.
Defaults to "proxy-ca.crt" if not specified.
type: string
name:
description: Name of the ConfigMap containing the proxy
CA certificate
type: string
required:
- name
type: object
x-kubernetes-map-type: atomic
proxyURL:
description: |-
Proxy URL, e.g. https://proxy.example.com:8080
Expand Down
11 changes: 7 additions & 4 deletions internal/controller/appserver/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,15 @@ func GenerateOLSConfigMap(r reconciler.Reconciler, ctx context.Context, cr *olsv
ProxyURL: cr.Spec.OLSConfig.ProxyConfig.ProxyURL,
ProxyCACertPath: "",
}
if cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef != nil && cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef.Name != "" {
err := validateCertificateInConfigMap(r, ctx, cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef.Name, utils.ProxyCACertFileName)
proxyCACertRef := cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef
cmName := utils.GetProxyCACertConfigMapName(proxyCACertRef)
if cmName != "" {
certKey := utils.GetProxyCACertKey(proxyCACertRef)
err := validateCertificateInConfigMap(r, ctx, cmName, certKey)
if err != nil {
return nil, fmt.Errorf("failed to validate proxy CA certificate %s: %w", cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef.Name, err)
return nil, fmt.Errorf("failed to validate proxy CA certificate %s: %w", cmName, err)
}
proxyConfig.ProxyCACertPath = path.Join(utils.OLSAppCertsMountRoot, utils.ProxyCACertVolumeName, utils.ProxyCACertFileName)
proxyConfig.ProxyCACertPath = path.Join(utils.OLSAppCertsMountRoot, utils.ProxyCACertVolumeName, certKey)
}
}

Expand Down
51 changes: 49 additions & 2 deletions internal/controller/appserver/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1648,8 +1648,9 @@ user_data_collector_config: {}

cr.Spec.OLSConfig.ProxyConfig = &olsv1alpha1.ProxyConfig{
ProxyURL: "https://proxy.example.com:8080",
ProxyCACertificateRef: &corev1.LocalObjectReference{
ProxyCACertificateRef: &olsv1alpha1.ProxyCACertConfigMapRef{
Name: caConfigMapName,
// No Key specified - tests backward compatibility
},
}

Expand All @@ -1668,6 +1669,12 @@ user_data_collector_config: {}
Name: caConfigMapName,
},
DefaultMode: &defaultVolumeMode,
Items: []corev1.KeyToPath{
{
Key: utils.ProxyCACertFileName,
Path: utils.ProxyCACertFileName,
},
},
},
},
}))
Expand All @@ -1687,14 +1694,54 @@ user_data_collector_config: {}

cr.Spec.OLSConfig.ProxyConfig = &olsv1alpha1.ProxyConfig{
ProxyURL: "https://proxy.example.com:8080",
ProxyCACertificateRef: &corev1.LocalObjectReference{
ProxyCACertificateRef: &olsv1alpha1.ProxyCACertConfigMapRef{
Name: caConfigMapName,
// No Key specified - tests backward compatibility
},
}
_, err = GenerateOLSConfigMap(testReconcilerInstance, ctx, cr)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to validate proxy CA certificate"))
})

It("should support custom ConfigMap key for proxy CA certificate", func() {
customKey := "service-ca.crt"
proxyCACm.Data = map[string]string{
customKey: utils.TestCACert,
}
err := testReconcilerInstance.Update(ctx, proxyCACm)
Expect(err).NotTo(HaveOccurred())

cr.Spec.OLSConfig.ProxyConfig = &olsv1alpha1.ProxyConfig{
ProxyURL: "https://proxy.example.com:8080",
ProxyCACertificateRef: &olsv1alpha1.ProxyCACertConfigMapRef{
Name: caConfigMapName,
Key: customKey,
},
}

// Test OLS ConfigMap has correct path with custom key
olsCm, err := GenerateOLSConfigMap(testReconcilerInstance, ctx, cr)
Expect(err).NotTo(HaveOccurred())
Expect(olsCm.Data[utils.OLSConfigFilename]).To(ContainSubstring(
fmt.Sprintf("proxy_ca_cert_path: /etc/certs/proxy-ca/%s", customKey)))

// Test deployment has Items projection with custom key
dep, err := GenerateOLSDeployment(testReconcilerInstance, cr)
Expect(err).NotTo(HaveOccurred())

var proxyCAVolume *corev1.Volume
for i := range dep.Spec.Template.Spec.Volumes {
if dep.Spec.Template.Spec.Volumes[i].Name == utils.ProxyCACertVolumeName {
proxyCAVolume = &dep.Spec.Template.Spec.Volumes[i]
break
}
}
Expect(proxyCAVolume).NotTo(BeNil())
Expect(proxyCAVolume.ConfigMap.Items).To(HaveLen(1))
Expect(proxyCAVolume.ConfigMap.Items[0].Key).To(Equal(customKey))
Expect(proxyCAVolume.ConfigMap.Items[0].Path).To(Equal(customKey))
})
})
})

Expand Down
32 changes: 25 additions & 7 deletions internal/controller/appserver/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,25 +263,43 @@ func GenerateOLSDeployment(r reconciler.Reconciler, cr *olsv1alpha1.OLSConfig) (
// User provided CA certificates - create both volumes and volume mounts in single pass
_ = utils.ForEachExternalConfigMap(cr, func(name, source string) error {
var volumeName, mountPath string
var volumeSource corev1.VolumeSource

switch source {
case "additional-ca":
volumeName = utils.AdditionalCAVolumeName
mountPath = UserCAMountPath
volumeSource = corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{Name: name},
DefaultMode: &volumeDefaultMode,
},
}
case "proxy-ca":
volumeName = utils.ProxyCACertVolumeName
mountPath = path.Join(utils.OLSAppCertsMountRoot, utils.ProxyCACertVolumeName)
// Get the key to use for the certificate
proxyCACertRef := cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef
certKey := utils.GetProxyCACertKey(proxyCACertRef)
volumeSource = corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{Name: name},
DefaultMode: &volumeDefaultMode,
Items: []corev1.KeyToPath{
{
Key: certKey,
Path: certKey, // Mount with same filename as key
},
},
},
}
default:
return nil
}

volumes = append(volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{Name: name},
DefaultMode: &volumeDefaultMode,
},
},
Name: volumeName,
VolumeSource: volumeSource,
})

volumeMounts = append(volumeMounts, corev1.VolumeMount{
Expand Down
9 changes: 8 additions & 1 deletion internal/controller/appserver/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,15 @@ func reconcileProxyCAConfigMap(r reconciler.Reconciler, ctx context.Context, cr
return nil
}

cmName := utils.GetProxyCACertConfigMapName(cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef)
if cmName == "" {
// no proxy CA certs, skip
r.GetLogger().Info("Proxy CA not configured, reconciliation skipped")
return nil
}

cm := &corev1.ConfigMap{}
err := r.Get(ctx, client.ObjectKey{Name: cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef.Name, Namespace: r.GetNamespace()}, cm)
err := r.Get(ctx, client.ObjectKey{Name: cmName, Namespace: r.GetNamespace()}, cm)
if err != nil {
return fmt.Errorf("%s: %w", utils.ErrGetProxyCACM, err)
}
Expand Down
8 changes: 7 additions & 1 deletion internal/controller/appserver/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ var _ = Describe("App server reconciliator", Ordered, func() {
By("Set up a proxy CA cert")
cr.Spec.OLSConfig.ProxyConfig = &olsv1alpha1.ProxyConfig{
ProxyURL: "https://proxy.example.com:8080",
ProxyCACertificateRef: &corev1.LocalObjectReference{
ProxyCACertificateRef: &olsv1alpha1.ProxyCACertConfigMapRef{
Name: cmCACertName,
},
}
Expand All @@ -1008,6 +1008,12 @@ var _ = Describe("App server reconciliator", Ordered, func() {
Name: cmCACertName,
},
DefaultMode: &volumeDefaultMode,
Items: []corev1.KeyToPath{
{
Key: utils.ProxyCACertFileName,
Path: utils.ProxyCACertFileName,
},
},
},
},
},
Expand Down
24 changes: 23 additions & 1 deletion internal/controller/lcore/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ func buildLCoreServiceConfig(_ reconciler.Reconciler, cr *olsv1alpha1.OLSConfig)
// color_log: enable colored logs for DEBUG, disable for production (INFO+)
colorLog := logLevel == olsv1alpha1.LogLevelDebug

return map[string]interface{}{
serviceConfig := map[string]interface{}{
"host": "0.0.0.0",
"port": utils.OLSAppServerContainerPort,
"auth_enabled": false,
Expand All @@ -649,6 +649,28 @@ func buildLCoreServiceConfig(_ reconciler.Reconciler, cr *olsv1alpha1.OLSConfig)
"tls_key_path": "/etc/certs/lightspeed-tls/tls.key",
},
}

// Add proxy configuration if specified
if cr.Spec.OLSConfig.ProxyConfig != nil {
proxyConfigMap := map[string]interface{}{}

if cr.Spec.OLSConfig.ProxyConfig.ProxyURL != "" {
proxyConfigMap["proxy_url"] = cr.Spec.OLSConfig.ProxyConfig.ProxyURL
}

proxyCACertRef := cr.Spec.OLSConfig.ProxyConfig.ProxyCACertificateRef
cmName := utils.GetProxyCACertConfigMapName(proxyCACertRef)
if cmName != "" {
certKey := utils.GetProxyCACertKey(proxyCACertRef)
proxyConfigMap["proxy_ca_cert_path"] = "/etc/certs/" + utils.ProxyCACertVolumeName + "/" + certKey
}

if len(proxyConfigMap) > 0 {
serviceConfig["proxy_config"] = proxyConfigMap
}
}

return serviceConfig
}

func buildLCoreLlamaStackConfig(_ reconciler.Reconciler, _ *olsv1alpha1.OLSConfig) map[string]interface{} {
Expand Down
Loading