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 }