github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/custommapper.go (about)

     1  package argocd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/argoproj/argo-cd/v2/util/glob"
     9  
    10  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    11  	"github.com/argoproj-labs/argocd-operator/common"
    12  
    13  	corev1 "k8s.io/api/core/v1"
    14  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    17  )
    18  
    19  func (r *ReconcileArgoCD) clusterResourceMapper(ctx context.Context, o client.Object) []reconcile.Request {
    20  	crbAnnotations := o.GetAnnotations()
    21  	namespacedArgoCDObject := client.ObjectKey{}
    22  
    23  	for k, v := range crbAnnotations {
    24  		if k == common.AnnotationName {
    25  			namespacedArgoCDObject.Name = v
    26  		} else if k == common.AnnotationNamespace {
    27  			namespacedArgoCDObject.Namespace = v
    28  		}
    29  	}
    30  
    31  	var result = []reconcile.Request{}
    32  	if namespacedArgoCDObject.Name != "" && namespacedArgoCDObject.Namespace != "" {
    33  		result = []reconcile.Request{
    34  			{NamespacedName: namespacedArgoCDObject},
    35  		}
    36  	}
    37  	return result
    38  }
    39  
    40  // isSecretOfInterest returns true if the name of the given secret matches one of the
    41  // well-known tls secrets used to secure communication amongst the Argo CD components.
    42  func isSecretOfInterest(o client.Object) bool {
    43  	if strings.HasSuffix(o.GetName(), "-repo-server-tls") {
    44  		return true
    45  	}
    46  	if o.GetName() == common.ArgoCDRedisServerTLSSecretName {
    47  		return true
    48  	}
    49  	return false
    50  }
    51  
    52  // isOwnerOfInterest returns true if the given owner is one of the Argo CD services that
    53  // may have been made the owner of the tls secret created by the OpenShift service CA, used
    54  // to secure communication amongst the Argo CD components.
    55  func isOwnerOfInterest(owner v1.OwnerReference) bool {
    56  	if owner.Kind != "Service" {
    57  		return false
    58  	}
    59  	if strings.HasSuffix(owner.Name, "-repo-server") {
    60  		return true
    61  	}
    62  	if strings.HasSuffix(owner.Name, "-redis") {
    63  		return true
    64  	}
    65  	return false
    66  }
    67  
    68  // tlsSecretMapper maps a watch event on a secret of type TLS back to the
    69  // ArgoCD object that we want to reconcile.
    70  func (r *ReconcileArgoCD) tlsSecretMapper(ctx context.Context, o client.Object) []reconcile.Request {
    71  	var result = []reconcile.Request{}
    72  
    73  	if !isSecretOfInterest(o) {
    74  		return result
    75  	}
    76  	namespacedArgoCDObject := client.ObjectKey{}
    77  
    78  	secretOwnerRefs := o.GetOwnerReferences()
    79  	if len(secretOwnerRefs) > 0 {
    80  		// OpenShift service CA makes the owner reference for the TLS secret to the
    81  		// service, which in turn is owned by the controller. This method performs
    82  		// a lookup of the controller through the intermediate owning service.
    83  		for _, secretOwner := range secretOwnerRefs {
    84  			if isOwnerOfInterest(secretOwner) {
    85  				key := client.ObjectKey{Name: secretOwner.Name, Namespace: o.GetNamespace()}
    86  				svc := &corev1.Service{}
    87  
    88  				// Get the owning object of the secret
    89  				err := r.Client.Get(context.TODO(), key, svc)
    90  				if err != nil {
    91  					log.Error(err, fmt.Sprintf("could not get owner of secret %s", o.GetName()))
    92  					return result
    93  				}
    94  
    95  				// If there's an object of kind ArgoCD in the owner's list,
    96  				// this will be our reconciled object.
    97  				serviceOwnerRefs := svc.GetOwnerReferences()
    98  				for _, serviceOwner := range serviceOwnerRefs {
    99  					if serviceOwner.Kind == "ArgoCD" {
   100  						namespacedArgoCDObject.Name = serviceOwner.Name
   101  						namespacedArgoCDObject.Namespace = svc.ObjectMeta.Namespace
   102  						result = []reconcile.Request{
   103  							{NamespacedName: namespacedArgoCDObject},
   104  						}
   105  						return result
   106  					}
   107  				}
   108  			}
   109  		}
   110  	} else {
   111  		// For secrets without owner (i.e. manually created), we apply some
   112  		// heuristics. This may not be as accurate (e.g. if the user made a
   113  		// typo in the resource's name), but should be good enough for now.
   114  		secret, ok := o.(*corev1.Secret)
   115  		if !ok {
   116  			return result
   117  		}
   118  		if owner, ok := secret.Annotations[common.AnnotationName]; ok {
   119  			namespacedArgoCDObject.Name = owner
   120  			namespacedArgoCDObject.Namespace = o.GetNamespace()
   121  			result = []reconcile.Request{
   122  				{NamespacedName: namespacedArgoCDObject},
   123  			}
   124  		}
   125  	}
   126  
   127  	return result
   128  }
   129  
   130  // namespaceResourceMapper maps a watch event on a namespace, back to the
   131  // ArgoCD object that we want to reconcile.
   132  func (r *ReconcileArgoCD) namespaceResourceMapper(ctx context.Context, o client.Object) []reconcile.Request {
   133  	var result = []reconcile.Request{}
   134  
   135  	argocds := &argoproj.ArgoCDList{}
   136  	labels := o.GetLabels()
   137  	namespaceName := o.GetName()
   138  	if v, ok := labels[common.ArgoCDManagedByLabel]; ok {
   139  		if err := r.Client.List(context.TODO(), argocds, &client.ListOptions{Namespace: v}); err != nil {
   140  			return result
   141  		}
   142  		if len(argocds.Items) != 1 {
   143  			return result
   144  		}
   145  		argocd := argocds.Items[0]
   146  		namespacedName := client.ObjectKey{
   147  			Name:      argocd.Name,
   148  			Namespace: argocd.Namespace,
   149  		}
   150  		result = []reconcile.Request{
   151  			{NamespacedName: namespacedName},
   152  		}
   153  	} else {
   154  		// If the namespace does not have the expected managed-by label,
   155  		// iterate through each ArgoCD instance to identify if the observed namespace
   156  		// matches any configured sourceNamespace pattern. If a match is found,
   157  		// generate a reconcile request for the instances.
   158  		if err := r.Client.List(ctx, argocds, &client.ListOptions{}); err != nil {
   159  			return result
   160  		}
   161  		for _, argocd := range argocds.Items {
   162  			if glob.MatchStringInList(argocd.Spec.SourceNamespaces, namespaceName, false) {
   163  				namespacedName := client.ObjectKey{
   164  					Name:      argocd.Name,
   165  					Namespace: argocd.Namespace,
   166  				}
   167  				result = append(result, reconcile.Request{NamespacedName: namespacedName})
   168  			}
   169  		}
   170  	}
   171  
   172  	return result
   173  }
   174  
   175  // clusterSecretResourceMapper maps a watch event on a namespace, back to the
   176  // ArgoCD object that we want to reconcile.
   177  func (r *ReconcileArgoCD) clusterSecretResourceMapper(ctx context.Context, o client.Object) []reconcile.Request {
   178  	var result = []reconcile.Request{}
   179  
   180  	labels := o.GetLabels()
   181  	if v, ok := labels[common.ArgoCDSecretTypeLabel]; ok && v == "cluster" {
   182  		argocds := &argoproj.ArgoCDList{}
   183  		if err := r.Client.List(context.TODO(), argocds, &client.ListOptions{Namespace: o.GetNamespace()}); err != nil {
   184  			return result
   185  		}
   186  
   187  		if len(argocds.Items) != 1 {
   188  			return result
   189  		}
   190  
   191  		argocd := argocds.Items[0]
   192  		namespacedName := client.ObjectKey{
   193  			Name:      argocd.Name,
   194  			Namespace: argocd.Namespace,
   195  		}
   196  		result = []reconcile.Request{
   197  			{NamespacedName: namespacedName},
   198  		}
   199  	}
   200  
   201  	return result
   202  }
   203  
   204  // applicationSetSCMTLSConfigMapMapper maps a watch event on a configmap with name "argocd-appset-gitlab-scm-tls-certs-cm",
   205  // back to the ArgoCD object that we want to reconcile.
   206  func (r *ReconcileArgoCD) applicationSetSCMTLSConfigMapMapper(ctx context.Context, o client.Object) []reconcile.Request {
   207  	var result = []reconcile.Request{}
   208  
   209  	if o.GetName() == common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName {
   210  		argocds := &argoproj.ArgoCDList{}
   211  		if err := r.Client.List(context.TODO(), argocds, &client.ListOptions{Namespace: o.GetNamespace()}); err != nil {
   212  			return result
   213  		}
   214  
   215  		if len(argocds.Items) != 1 {
   216  			return result
   217  		}
   218  
   219  		argocd := argocds.Items[0]
   220  		namespacedName := client.ObjectKey{
   221  			Name:      argocd.Name,
   222  			Namespace: argocd.Namespace,
   223  		}
   224  		result = []reconcile.Request{
   225  			{NamespacedName: namespacedName},
   226  		}
   227  	}
   228  
   229  	return result
   230  }