diff --git a/internal/controller/constants.go b/internal/controller/constants.go index ab38e867..a7cb3081 100644 --- a/internal/controller/constants.go +++ b/internal/controller/constants.go @@ -263,4 +263,14 @@ ssl_ca_file = '/etc/certs/cm-olspostgresca/service-ca.crt' MCPServerTimeout = 60 // MCP server SSE read timeout, sec MCPServerHTTPReadTimeout = 30 + + /*** Data Exporter Constants ***/ + // ExporterConfigCmName is the name of the exporter configmap + ExporterConfigCmName = "lightspeed-exporter-config" + // ExporterConfigVolumeName is the name of the volume for exporter configuration + ExporterConfigVolumeName = "exporter-config" + // ExporterConfigMountPath is the path where exporter config is mounted + ExporterConfigMountPath = "/etc/config" + // ExporterConfigFilename is the name of the exporter configuration file + ExporterConfigFilename = "config.yaml" ) diff --git a/internal/controller/ols_app_server_assets.go b/internal/controller/ols_app_server_assets.go index 046a0598..a6c530a2 100644 --- a/internal/controller/ols_app_server_assets.go +++ b/internal/controller/ols_app_server_assets.go @@ -400,6 +400,36 @@ func (r *OLSConfigReconciler) generateOLSConfigMap(ctx context.Context, cr *olsv return &cm, nil } +func (r *OLSConfigReconciler) generateExporterConfigMap(cr *olsv1alpha1.OLSConfig) (*corev1.ConfigMap, error) { + exporterConfigContent := `service_id: "ols" +ingress_server_url: "https://console.redhat.com/api/ingress/v1/upload" +allowed_subdirs: + - feedback + - transcripts + +# Collection settings +collection_interval: 5 +cleanup_after_send: true +ingress_connection_timeout: 30` + + cm := corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: ExporterConfigCmName, + Namespace: r.Options.Namespace, + Labels: generateAppServerSelectorLabels(), + }, + Data: map[string]string{ + ExporterConfigFilename: exporterConfigContent, + }, + } + + if err := controllerutil.SetControllerReference(cr, &cm, r.Scheme); err != nil { + return nil, err + } + + return &cm, nil +} + func (r *OLSConfigReconciler) getAdditionalCAFileNames(cr *olsv1alpha1.OLSConfig) ([]string, error) { if cr.Spec.OLSConfig.AdditionalCAConfigMapRef == nil { return nil, nil diff --git a/internal/controller/ols_app_server_deployment.go b/internal/controller/ols_app_server_deployment.go index 1ff96e4d..d0173941 100644 --- a/internal/controller/ols_app_server_deployment.go +++ b/internal/controller/ols_app_server_deployment.go @@ -149,6 +149,20 @@ func (r *OLSConfigReconciler) generateOLSDeployment(cr *olsv1alpha1.OLSConfig) ( }, } volumes = append(volumes, olsUserDataVolume) + + // Add exporter config volume + exporterConfigVolume := corev1.Volume{ + Name: ExporterConfigVolumeName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: ExporterConfigCmName, + }, + DefaultMode: &volumeDefaultMode, + }, + }, + } + volumes = append(volumes, exporterConfigVolume) } // User provided additional CA certificates @@ -224,8 +238,13 @@ func (r *OLSConfigReconciler) generateOLSDeployment(cr *olsv1alpha1.OLSConfig) ( Name: OLSUserDataVolumeName, MountPath: OLSUserDataMountPath, } + exporterConfigVolumeMount := corev1.VolumeMount{ + Name: ExporterConfigVolumeName, + MountPath: ExporterConfigMountPath, + ReadOnly: true, + } if dataCollectorEnabled { - volumeMounts = append(volumeMounts, olsUserDataVolumeMount) + volumeMounts = append(volumeMounts, olsUserDataVolumeMount, exporterConfigVolumeMount) } if cr.Spec.OLSConfig.AdditionalCAConfigMapRef != nil { additionalCAVolumeMount := corev1.VolumeMount{ @@ -357,26 +376,29 @@ func (r *OLSConfigReconciler) generateOLSDeployment(cr *olsv1alpha1.OLSConfig) ( return &deployment, nil } - // Add telemetry container - telemetryContainer := corev1.Container{ - Name: "lightspeed-service-user-data-collector", - Image: r.Options.LightspeedServiceImage, + // Add data exporter container + exporterContainer := corev1.Container{ + Name: "lightspeed-to-dataverse-exporter", + Image: "quay.io/lightspeed-core/lightspeed-to-dataverse-exporter:dev-latest", ImagePullPolicy: corev1.PullAlways, SecurityContext: &corev1.SecurityContext{ AllowPrivilegeEscalation: &[]bool{false}[0], ReadOnlyRootFilesystem: &[]bool{true}[0], }, VolumeMounts: volumeMounts, - Env: []corev1.EnvVar{ - { - Name: "OLS_CONFIG_FILE", - Value: path.Join(OLSConfigMountPath, OLSConfigFilename), - }, + Args: []string{ + "--mode", + "openshift", + "--config", + path.Join(ExporterConfigMountPath, ExporterConfigFilename), + "--log-level", + "DEBUG", + "--data-dir", + OLSUserDataMountPath, }, - Command: []string{"python3.11", "/app-root/ols/user_data_collection/data_collector.py"}, Resources: *data_collector_resources, } - deployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, telemetryContainer) + deployment.Spec.Template.Spec.Containers = append(deployment.Spec.Template.Spec.Containers, exporterContainer) // Add MCP sidecar container if introspection is enabled if cr.Spec.OLSConfig.IntrospectionEnabled { diff --git a/internal/controller/ols_app_server_reconciliator.go b/internal/controller/ols_app_server_reconciliator.go index aa5fb917..baf1e0de 100644 --- a/internal/controller/ols_app_server_reconciliator.go +++ b/internal/controller/ols_app_server_reconciliator.go @@ -38,6 +38,10 @@ func (r *OLSConfigReconciler) reconcileAppServer(ctx context.Context, olsconfig Name: "reconcile OLSConfigMap", Task: r.reconcileOLSConfigMap, }, + { + Name: "reconcile ExporterConfigMap", + Task: r.reconcileExporterConfigMap, + }, { Name: "reconcile Additional CA ConfigMap", Task: r.reconcileOLSAdditionalCAConfigMap, @@ -141,6 +145,47 @@ func (r *OLSConfigReconciler) reconcileOLSConfigMap(ctx context.Context, cr *ols return nil } +func (r *OLSConfigReconciler) reconcileExporterConfigMap(ctx context.Context, cr *olsv1alpha1.OLSConfig) error { + // Only create exporter configmap if data collector is enabled + dataCollectorEnabled, err := r.dataCollectorEnabled(cr) + if err != nil { + return err + } + + if !dataCollectorEnabled { + r.logger.Info("Data collector not enabled, exporter configmap reconciliation skipped") + return nil + } + + cm, err := r.generateExporterConfigMap(cr) + if err != nil { + return fmt.Errorf("failed to generate exporter configmap: %w", err) + } + + foundCm := &corev1.ConfigMap{} + err = r.Client.Get(ctx, client.ObjectKey{Name: ExporterConfigCmName, Namespace: r.Options.Namespace}, foundCm) + if err != nil && errors.IsNotFound(err) { + r.logger.Info("creating a new exporter configmap", "configmap", cm.Name) + err = r.Create(ctx, cm) + if err != nil { + return fmt.Errorf("failed to create exporter configmap: %w", err) + } + return nil + } else if err != nil { + return fmt.Errorf("failed to get exporter configmap: %w", err) + } + + // Update existing configmap + foundCm.Data = cm.Data + err = r.Update(ctx, foundCm) + if err != nil { + return fmt.Errorf("failed to update exporter configmap: %w", err) + } + + r.logger.Info("Exporter configmap reconciled", "configmap", cm.Name) + return nil +} + func (r *OLSConfigReconciler) reconcileOLSAdditionalCAConfigMap(ctx context.Context, cr *olsv1alpha1.OLSConfig) error { if cr.Spec.OLSConfig.AdditionalCAConfigMapRef == nil { // no additional CA certs, skip