mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
go.mod still declared `module github.com/hanzoai/deploy` after the repo was renamed. A Go module path is not cosmetic — it is the resolvable location. So `go get github.com/hanzoai/cd` could not work, and anything importing this module had to spell a name that no longer exists at that address. The build log of v3.6.0 is where it surfaced: the compiler was still emitting github.com/hanzoai/deploy/cmd/cd/commands. 3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from the module path, replaced separately so neither could clobber the other. go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean. Co-authored-by: Hanzo Dev <[email protected]>
189 lines
6.0 KiB
Go
189 lines
6.0 KiB
Go
package generators
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/hanzoai/cd/applicationset/utils"
|
|
"github.com/hanzoai/cd/common"
|
|
argoappsetv1alpha1 "github.com/hanzoai/cd/pkg/apis/application/v1alpha1"
|
|
)
|
|
|
|
var _ Generator = (*ClusterGenerator)(nil)
|
|
|
|
// ClusterGenerator generates Applications for some or all clusters registered with Hanzo CD.
|
|
type ClusterGenerator struct {
|
|
client.Client
|
|
// namespace is the Hanzo CD namespace
|
|
namespace string
|
|
}
|
|
|
|
var render = &utils.Render{}
|
|
|
|
func NewClusterGenerator(c client.Client, namespace string) Generator {
|
|
g := &ClusterGenerator{
|
|
Client: c,
|
|
namespace: namespace,
|
|
}
|
|
return g
|
|
}
|
|
|
|
// GetRequeueAfter never requeue the cluster generator because the `clusterSecretEventHandler` will requeue the appsets
|
|
// when the cluster secrets change
|
|
func (g *ClusterGenerator) GetRequeueAfter(_ *argoappsetv1alpha1.ApplicationSetGenerator) time.Duration {
|
|
return NoRequeueAfter
|
|
}
|
|
|
|
func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) *argoappsetv1alpha1.ApplicationSetTemplate {
|
|
return &appSetGenerator.Clusters.Template
|
|
}
|
|
|
|
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
|
logCtx := log.WithField("applicationset", appSet.GetName()).WithField("namespace", appSet.GetNamespace())
|
|
if appSetGenerator == nil {
|
|
return nil, ErrEmptyAppSetGenerator
|
|
}
|
|
|
|
if appSetGenerator.Clusters == nil {
|
|
return nil, ErrEmptyAppSetGenerator
|
|
}
|
|
|
|
// Do not include the local cluster in the cluster parameters IF there is a non-empty selector
|
|
// - Since local clusters do not have secrets, they do not have labels to match against
|
|
ignoreLocalClusters := len(appSetGenerator.Clusters.Selector.MatchExpressions) > 0 || len(appSetGenerator.Clusters.Selector.MatchLabels) > 0
|
|
|
|
// Get cluster secrets using the cached controller-runtime client
|
|
clusterSecrets, err := g.getSecretsByClusterName(logCtx, appSetGenerator)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
|
|
}
|
|
|
|
paramHolder := ¶mHolder{isFlatMode: appSetGenerator.Clusters.FlatList}
|
|
logCtx.Debugf("Using flat mode = %t for cluster generator", paramHolder.isFlatMode)
|
|
|
|
// Convert map values to slice to check for an in-cluster secret
|
|
secretsList := make([]corev1.Secret, 0, len(clusterSecrets))
|
|
for _, secret := range clusterSecrets {
|
|
secretsList = append(secretsList, secret)
|
|
}
|
|
|
|
// For each matching cluster secret (non-local clusters only)
|
|
for _, cluster := range clusterSecrets {
|
|
params := g.getClusterParameters(cluster, appSet)
|
|
|
|
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error appending templated values for cluster: %w", err)
|
|
}
|
|
|
|
paramHolder.append(params)
|
|
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
|
|
}
|
|
|
|
// Add the in-cluster last if it doesn't have a secret, and we're not ignoring in-cluster
|
|
if !ignoreLocalClusters && !utils.SecretsContainInClusterCredentials(secretsList) {
|
|
params := map[string]any{}
|
|
params["name"] = argoappsetv1alpha1.KubernetesInClusterName
|
|
params["nameNormalized"] = argoappsetv1alpha1.KubernetesInClusterName
|
|
params["server"] = argoappsetv1alpha1.KubernetesInternalAPIServerAddr
|
|
params["project"] = ""
|
|
|
|
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
|
|
}
|
|
|
|
paramHolder.append(params)
|
|
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
|
}
|
|
|
|
return paramHolder.consolidate(), nil
|
|
}
|
|
|
|
type paramHolder struct {
|
|
isFlatMode bool
|
|
params []map[string]any
|
|
}
|
|
|
|
func (p *paramHolder) append(params map[string]any) {
|
|
p.params = append(p.params, params)
|
|
}
|
|
|
|
func (p *paramHolder) consolidate() []map[string]any {
|
|
if p.isFlatMode {
|
|
p.params = []map[string]any{
|
|
{"clusters": p.params},
|
|
}
|
|
}
|
|
return p.params
|
|
}
|
|
|
|
func (g *ClusterGenerator) getClusterParameters(cluster corev1.Secret, appSet *argoappsetv1alpha1.ApplicationSet) map[string]any {
|
|
params := map[string]any{}
|
|
|
|
params["name"] = string(cluster.Data["name"])
|
|
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
|
|
params["server"] = string(cluster.Data["server"])
|
|
|
|
project, ok := cluster.Data["project"]
|
|
if ok {
|
|
params["project"] = string(project)
|
|
} else {
|
|
params["project"] = ""
|
|
}
|
|
|
|
if appSet.Spec.GoTemplate {
|
|
meta := map[string]any{}
|
|
|
|
if len(cluster.Annotations) > 0 {
|
|
meta["annotations"] = cluster.Annotations
|
|
}
|
|
if len(cluster.Labels) > 0 {
|
|
meta["labels"] = cluster.Labels
|
|
}
|
|
|
|
params["metadata"] = meta
|
|
} else {
|
|
for key, value := range cluster.Annotations {
|
|
params["metadata.annotations."+key] = value
|
|
}
|
|
|
|
for key, value := range cluster.Labels {
|
|
params["metadata.labels."+key] = value
|
|
}
|
|
}
|
|
return params
|
|
}
|
|
|
|
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
|
clusterSecretList := &corev1.SecretList{}
|
|
|
|
selector := metav1.AddLabelToSelector(&appSetGenerator.Clusters.Selector, common.LabelKeySecretType, common.LabelValueSecretTypeCluster)
|
|
secretSelector, err := metav1.LabelSelectorAsSelector(selector)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error converting label selector: %w", err)
|
|
}
|
|
|
|
if err := g.List(context.Background(), clusterSecretList, client.InNamespace(g.namespace), client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
|
return nil, err
|
|
}
|
|
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))
|
|
|
|
res := map[string]corev1.Secret{}
|
|
|
|
for _, cluster := range clusterSecretList.Items {
|
|
clusterName := string(cluster.Data["name"])
|
|
|
|
res[clusterName] = cluster
|
|
}
|
|
|
|
return res, nil
|
|
}
|