@@ -15,7 +15,9 @@ package tls
1515import (
1616 "errors"
1717 "fmt"
18+ "maps"
1819 "os"
20+ "slices"
1921 "sort"
2022 "strings"
2123
@@ -213,7 +215,7 @@ func (c *CertificatesReconciler) syncGitTrustedCertificates(ctx *chetypes.Deploy
213215 return err == nil , err
214216 }
215217
216- if gitTrustedCertsCM .Data ["ca.crt" ] != "" {
218+ if gitTrustedCertsCM .Data [constants . GitSelfSignedCertsConfigMapCertKey ] != "" {
217219 gitTrustedCertsCM .TypeMeta = metav1.TypeMeta {
218220 Kind : "ConfigMap" ,
219221 APIVersion : "v1" ,
@@ -318,22 +320,27 @@ func (c *CertificatesReconciler) syncCheCABundleCerts(ctx *chetypes.DeployContex
318320 return false , err
319321 }
320322
321- // Sort configmaps by name, always have the same order and content
322- // to avoid endless reconcile loop
323+ // Sort ConfigMaps by name and their data keys alphabetically to ensure
324+ // deterministic ordering. This prevents spurious reconcile loops that occur
325+ // when Go's random map iteration produces different output each time.
323326 sort .Slice (cheCABundlesCMs , func (i , j int ) bool {
324327 return strings .Compare (cheCABundlesCMs [i ].Name , cheCABundlesCMs [j ].Name ) < 0
325328 })
326329
327- // Calculated revisions and content
328330 cheCABundlesContent := ""
329331 for _ , cm := range cheCABundlesCMs {
330- for dataKey , dataValue := range cm .Data {
331- cheCABundlesContent += fmt .Sprintf (
332- "# ConfigMap: %s, Key: %s\n %s\n \n " ,
333- cm .Name ,
334- dataKey ,
335- dataValue ,
336- )
332+ // Sort keys to produce deterministic output and avoid endless reconcile loop
333+ dataKeys := slices .Collect (maps .Keys (cm .Data ))
334+ sort .Strings (dataKeys )
335+
336+ for _ , dataKey := range dataKeys {
337+ // Skip the "githost" key from the git trusted certs ConfigMap:
338+ // it contains a hostname, not a certificate, and should not be included in the CA bundle.
339+ if dataKey == constants .GitSelfSignedCertsConfigMapGitHostKey && isGitTrustedCertsConfigMap (ctx , & cm ) {
340+ continue
341+ }
342+
343+ cheCABundlesContent += printCert (& cm , dataKey )
337344 }
338345 }
339346
@@ -394,3 +401,26 @@ func readKubernetesCaBundle() ([]byte, error) {
394401
395402 return data , nil
396403}
404+
405+ // printCert formats a single certificate entry with its ConfigMap name and key as a header comment.
406+ func printCert (cm * corev1.ConfigMap , key string ) string {
407+ return fmt .Sprintf (
408+ "# ConfigMap: %s, Key: %s\n %s\n \n " ,
409+ cm .Name ,
410+ key ,
411+ cm .Data [key ],
412+ )
413+ }
414+
415+ func isGitTrustedCertsConfigMap (ctx * chetypes.DeployContext , cm * corev1.ConfigMap ) bool {
416+ if cm .Name == constants .DefaultGitSelfSignedCertsConfigMapName {
417+ return true
418+ }
419+
420+ if ctx .CheCluster .Spec .DevEnvironments .TrustedCerts != nil &&
421+ cm .Name == ctx .CheCluster .Spec .DevEnvironments .TrustedCerts .GitTrustedCertsConfigMapName {
422+ return true
423+ }
424+
425+ return false
426+ }
0 commit comments