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
81 changes: 70 additions & 11 deletions controllers/consoleplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package controllers

import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"reflect"
"sort"
"strings"

argocommon "github.com/argoproj-labs/argocd-operator/common"
argocdutil "github.com/argoproj-labs/argocd-operator/controllers/argoutil"
Expand Down Expand Up @@ -240,18 +244,40 @@ func securityContextForPlugin() *corev1.SecurityContext {
}
}

var httpdConfig = fmt.Sprintf(`LoadModule ssl_module modules/mod_ssl.so
// buildHttpdConfig generates httpd.conf with dynamic TLS settings
func (r *ReconcileGitopsService) buildHttpdConfig() string {
minVersionTLS := string(r.CentralTLSProfile.MinTLSVersion)
httpdConfigBase := fmt.Sprintf(`LoadModule ssl_module modules/mod_ssl.so
Listen %d https
ServerRoot "/etc/httpd"

<VirtualHost *:%d>
DocumentRoot /var/www/html/plugin
SSLEngine on
SSLCertificateFile "/etc/httpd-ssl/certs/tls.crt"
SSLCertificateKeyFile "/etc/httpd-ssl/private/tls.key"
</VirtualHost>`, servicePort, servicePort)
SSLCertificateKeyFile "/etc/httpd-ssl/private/tls.key"`, servicePort, servicePort)
// Add SSLProtocol only if explicitly set
switch minVersionTLS {
case "VersionTLS10":
httpdConfigBase += "\n\tSSLProtocol -all +TLSv1 +TLSv1.1 +TLSv1.2 +TLSv1.3"
case "VersionTLS11":
httpdConfigBase += "\n\tSSLProtocol -all +TLSv1.1 +TLSv1.2 +TLSv1.3"
case "VersionTLS12":
httpdConfigBase += "\n\tSSLProtocol -all +TLSv1.2 +TLSv1.3"
case "VersionTLS13":
httpdConfigBase += "\n\tSSLProtocol -all +TLSv1.3"
}
if minVersionTLS != "VersionTLS13" && strings.Join(r.CentralTLSProfile.Ciphers, ":") != "" {
httpdConfigBase += fmt.Sprintf("\n\tSSLCipherSuite %s", strings.Join(r.CentralTLSProfile.Ciphers, ":"))
}
// Close VirtualHost
httpdConfigBase += "\n</VirtualHost>"
return httpdConfigBase
}

// pluginConfigMap creates the ConfigMap with dynamic httpd.conf
func (r *ReconcileGitopsService) pluginConfigMap() *corev1.ConfigMap {
httpdConfig := r.buildHttpdConfig()

func pluginConfigMap() *corev1.ConfigMap {
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: httpdConfigMapName,
Expand Down Expand Up @@ -337,7 +363,7 @@ func sortTolerations(tolerations []corev1.Toleration) []corev1.Toleration {
return sorted
}

func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) {
func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.GitopsService, request reconcile.Request, newPluginConfigMap *corev1.ConfigMap) (reconcile.Result, error) {
reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
newPluginDeployment := pluginDeployment(cr.Spec.ImagePullPolicy)

Expand All @@ -359,6 +385,13 @@ func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.Gitop
newPluginDeployment.Spec.Template.Spec.Tolerations = cr.Spec.Tolerations
}

// ADD THIS: Get ConfigMap and add hash to pod template annotations

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we can avoid all this complexity by computing the hash directly in the ConfigMap reconciliation function instead of storing it. During each reconciliation, we can compare the hash of the existing and desired httpd configs or simply compare the strings, if possible. That should be sufficient to detect drift and update the ConfigMap.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The hash annotation isn't only used to detect ConfigMap drift; it's used to trigger a Deployment rollout when the ConfigMap content changes. Since httpd reads its configuration at startup, updating only the ConfigMap wouldn't restart the pods. Unless we introduce another restart mechanism, we still need a pod template change (such as the hash annotation) to ensure the updated configuration is applied.

configMapHash := getConfigMapHash(newPluginConfigMap)
if newPluginDeployment.Spec.Template.ObjectMeta.Annotations == nil {
newPluginDeployment.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
}
newPluginDeployment.Spec.Template.ObjectMeta.Annotations["httpd-cfg-hash"] = configMapHash

// Check if this Deployment already exists
existingPluginDeployment := &appsv1.Deployment{}

Expand All @@ -381,6 +414,7 @@ func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.Gitop
!equality.Semantic.DeepEqual(existingPluginDeployment.Spec.Replicas, newPluginDeployment.Spec.Replicas) ||
!equality.Semantic.DeepEqual(existingPluginDeployment.Spec.Selector, newPluginDeployment.Spec.Selector) ||
!equality.Semantic.DeepEqual(existingSpecTemplate.Labels, newSpecTemplate.Labels) ||
!equality.Semantic.DeepEqual(existingSpecTemplate.ObjectMeta.Annotations["httpd-cfg-hash"], newSpecTemplate.ObjectMeta.Annotations["httpd-cfg-hash"]) ||
!equality.Semantic.DeepEqual(sortContainers(existingSpecTemplate.Spec.Containers), sortContainers(newSpecTemplate.Spec.Containers)) ||
!equality.Semantic.DeepEqual(sortVolumes(existingSpecTemplate.Spec.Volumes), sortVolumes(newSpecTemplate.Spec.Volumes)) ||
!equality.Semantic.DeepEqual(existingSpecTemplate.Spec.RestartPolicy, newSpecTemplate.Spec.RestartPolicy) ||
Expand All @@ -391,11 +425,15 @@ func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.Gitop
!equality.Semantic.DeepEqual(existingSpecTemplate.Spec.Containers[0].Resources, newSpecTemplate.Spec.Containers[0].Resources)

if changed {
if existingSpecTemplate.ObjectMeta.Annotations == nil {
existingSpecTemplate.ObjectMeta.Annotations = make(map[string]string)
}
reqLogger.Info("Reconciling plugin deployment", "Namespace", existingPluginDeployment.Namespace, "Name", existingPluginDeployment.Name)
existingPluginDeployment.Labels = newPluginDeployment.Labels
existingPluginDeployment.Spec.Replicas = newPluginDeployment.Spec.Replicas
existingPluginDeployment.Spec.Selector = newPluginDeployment.Spec.Selector
existingSpecTemplate.Labels = newSpecTemplate.Labels
existingSpecTemplate.ObjectMeta.Annotations["httpd-cfg-hash"] = newSpecTemplate.ObjectMeta.Annotations["httpd-cfg-hash"]
Comment thread
akhilnittala marked this conversation as resolved.
existingSpecTemplate.Spec.SecurityContext = newSpecTemplate.Spec.SecurityContext
existingSpecTemplate.Spec.Containers = newSpecTemplate.Spec.Containers
existingSpecTemplate.Spec.Volumes = newSpecTemplate.Spec.Volumes
Expand Down Expand Up @@ -487,9 +525,27 @@ func (r *ReconcileGitopsService) reconcileConsolePlugin(instance *pipelinesv1alp
return reconcile.Result{}, nil
}

func (r *ReconcileGitopsService) reconcileConfigMap(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) {
// getConfigMapHash returns a deterministic SHA256 hash of ConfigMap data for change detection.
func getConfigMapHash(cm *corev1.ConfigMap) string {
hash := sha256.New()
keys := make([]string, 0, len(cm.Data))
for key := range cm.Data {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
if _, err := io.WriteString(hash, key); err != nil {
panic(fmt.Sprintf("failed to hash configmap key: %v", err))
}
if _, err := io.WriteString(hash, cm.Data[key]); err != nil {
panic(fmt.Sprintf("failed to hash configmap value: %v", err))
}
}
return hex.EncodeToString(hash.Sum(nil))
}

func (r *ReconcileGitopsService) reconcileConfigMap(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request, newPluginConfigMap *corev1.ConfigMap) (reconcile.Result, error) {
reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
newPluginConfigMap := pluginConfigMap()

if err := controllerutil.SetControllerReference(instance, newPluginConfigMap, r.Scheme); err != nil {
return reconcile.Result{}, err
Expand All @@ -515,7 +571,7 @@ func (r *ReconcileGitopsService) reconcileConfigMap(instance *pipelinesv1alpha1.
reqLogger.Info("Reconciling plugin configMap", "Namespace", existingPluginConfigMap.Namespace, "Name", existingPluginConfigMap.Name)
existingPluginConfigMap.Data = newPluginConfigMap.Data
existingPluginConfigMap.Labels = newPluginConfigMap.Labels
return reconcile.Result{}, r.Client.Update(context.TODO(), newPluginConfigMap)
return reconcile.Result{}, r.Client.Update(context.TODO(), existingPluginConfigMap)
}
}
return reconcile.Result{}, nil
Expand All @@ -529,15 +585,18 @@ func (r *ReconcileGitopsService) reconcilePlugin(instance *pipelinesv1alpha1.Git
return reconcile.Result{}, nil
}

// Generate ConfigMap once
newPluginConfigMap := r.pluginConfigMap()

if result, err := r.reconcileService(instance, request); err != nil {
return result, err
}

if result, err := r.reconcileDeployment(instance, request); err != nil {
if result, err := r.reconcileConfigMap(instance, request, newPluginConfigMap); err != nil {
return result, err
}

if result, err := r.reconcileConfigMap(instance, request); err != nil {
if result, err := r.reconcileDeployment(instance, request, newPluginConfigMap); err != nil {
return result, err
}

Expand Down
Loading
Loading