github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/generators/cluster.go (about)

     1  package generators
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	log "github.com/sirupsen/logrus"
     9  
    10  	"github.com/argoproj/argo-cd/v2/util/settings"
    11  
    12  	corev1 "k8s.io/api/core/v1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/client-go/kubernetes"
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  
    17  	"github.com/argoproj/argo-cd/v2/applicationset/utils"
    18  	argoappsetv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
    19  )
    20  
    21  const (
    22  	ArgoCDSecretTypeLabel   = "argocd.argoproj.io/secret-type"
    23  	ArgoCDSecretTypeCluster = "cluster"
    24  )
    25  
    26  var _ Generator = (*ClusterGenerator)(nil)
    27  
    28  // ClusterGenerator generates Applications for some or all clusters registered with ArgoCD.
    29  type ClusterGenerator struct {
    30  	client.Client
    31  	ctx       context.Context
    32  	clientset kubernetes.Interface
    33  	// namespace is the Argo CD namespace
    34  	namespace       string
    35  	settingsManager *settings.SettingsManager
    36  }
    37  
    38  var render = &utils.Render{}
    39  
    40  func NewClusterGenerator(c client.Client, ctx context.Context, clientset kubernetes.Interface, namespace string) Generator {
    41  
    42  	settingsManager := settings.NewSettingsManager(ctx, clientset, namespace)
    43  
    44  	g := &ClusterGenerator{
    45  		Client:          c,
    46  		ctx:             ctx,
    47  		clientset:       clientset,
    48  		namespace:       namespace,
    49  		settingsManager: settingsManager,
    50  	}
    51  	return g
    52  }
    53  
    54  // GetRequeueAfter never requeue the cluster generator because the `clusterSecretEventHandler` will requeue the appsets
    55  // when the cluster secrets change
    56  func (g *ClusterGenerator) GetRequeueAfter(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) time.Duration {
    57  	return NoRequeueAfter
    58  }
    59  
    60  func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) *argoappsetv1alpha1.ApplicationSetTemplate {
    61  	return &appSetGenerator.Clusters.Template
    62  }
    63  
    64  func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
    65  
    66  	if appSetGenerator == nil {
    67  		return nil, EmptyAppSetGeneratorError
    68  	}
    69  
    70  	if appSetGenerator.Clusters == nil {
    71  		return nil, EmptyAppSetGeneratorError
    72  	}
    73  
    74  	// Do not include the local cluster in the cluster parameters IF there is a non-empty selector
    75  	// - Since local clusters do not have secrets, they do not have labels to match against
    76  	ignoreLocalClusters := len(appSetGenerator.Clusters.Selector.MatchExpressions) > 0 || len(appSetGenerator.Clusters.Selector.MatchLabels) > 0
    77  
    78  	// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
    79  	clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
    80  	if err != nil {
    81  		return nil, fmt.Errorf("error listing clusters: %w", err)
    82  	}
    83  
    84  	if clustersFromArgoCD == nil {
    85  		return nil, nil
    86  	}
    87  
    88  	clusterSecrets, err := g.getSecretsByClusterName(appSetGenerator)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	res := []map[string]interface{}{}
    94  
    95  	secretsFound := []corev1.Secret{}
    96  
    97  	for _, cluster := range clustersFromArgoCD.Items {
    98  
    99  		// If there is a secret for this cluster, then it's a non-local cluster, so it will be
   100  		// handled by the next step.
   101  		if secretForCluster, exists := clusterSecrets[cluster.Name]; exists {
   102  			secretsFound = append(secretsFound, secretForCluster)
   103  
   104  		} else if !ignoreLocalClusters {
   105  			// If there is no secret for the cluster, it's the local cluster, so handle it here.
   106  			params := map[string]interface{}{}
   107  			params["name"] = cluster.Name
   108  			params["nameNormalized"] = cluster.Name
   109  			params["server"] = cluster.Server
   110  
   111  			err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
   112  			if err != nil {
   113  				return nil, err
   114  			}
   115  
   116  			res = append(res, params)
   117  
   118  			log.WithField("cluster", "local cluster").Info("matched local cluster")
   119  		}
   120  	}
   121  
   122  	// For each matching cluster secret (non-local clusters only)
   123  	for _, cluster := range secretsFound {
   124  		params := map[string]interface{}{}
   125  
   126  		params["name"] = string(cluster.Data["name"])
   127  		params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
   128  		params["server"] = string(cluster.Data["server"])
   129  
   130  		if appSet.Spec.GoTemplate {
   131  			meta := map[string]interface{}{}
   132  
   133  			if len(cluster.ObjectMeta.Annotations) > 0 {
   134  				meta["annotations"] = cluster.ObjectMeta.Annotations
   135  			}
   136  			if len(cluster.ObjectMeta.Labels) > 0 {
   137  				meta["labels"] = cluster.ObjectMeta.Labels
   138  			}
   139  
   140  			params["metadata"] = meta
   141  		} else {
   142  			for key, value := range cluster.ObjectMeta.Annotations {
   143  				params[fmt.Sprintf("metadata.annotations.%s", key)] = value
   144  			}
   145  
   146  			for key, value := range cluster.ObjectMeta.Labels {
   147  				params[fmt.Sprintf("metadata.labels.%s", key)] = value
   148  			}
   149  		}
   150  
   151  		err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  
   156  		res = append(res, params)
   157  
   158  		log.WithField("cluster", cluster.Name).Info("matched cluster secret")
   159  	}
   160  
   161  	return res, nil
   162  }
   163  
   164  func (g *ClusterGenerator) getSecretsByClusterName(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
   165  	// List all Clusters:
   166  	clusterSecretList := &corev1.SecretList{}
   167  
   168  	selector := metav1.AddLabelToSelector(&appSetGenerator.Clusters.Selector, ArgoCDSecretTypeLabel, ArgoCDSecretTypeCluster)
   169  	secretSelector, err := metav1.LabelSelectorAsSelector(selector)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	if err := g.Client.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
   175  		return nil, err
   176  	}
   177  	log.Debug("clusters matching labels", "count", len(clusterSecretList.Items))
   178  
   179  	res := map[string]corev1.Secret{}
   180  
   181  	for _, cluster := range clusterSecretList.Items {
   182  		clusterName := string(cluster.Data["name"])
   183  
   184  		res[clusterName] = cluster
   185  	}
   186  
   187  	return res, nil
   188  
   189  }