package argohandler import ( "context" "encoding/base64" "fmt" "log" "main/db" "math/rand" "strings" "sync" "time" "github.com/argoproj/argo-cd/v2/pkg/apiclient" "github.com/argoproj/argo-cd/v2/pkg/apiclient/application" argoprojv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" ) type ApplicationValues struct { Name string `yaml:"name"` Namespace string `yaml:"namespace"` Path string `yaml:"path"` Cluster string `yaml:"cluster"` RepoURL string `yaml:"repoURL"` Server string `yaml:"server"` UserID string `yaml:"userID"` } type Clusters struct { Name string `yaml:"name"` ClusterID string `yaml:"clusterid"` Status string `yaml:"status"` Version string `yaml:version` HealthCheck string `yaml:"healthcheck` Alert string `yaml:"alert"` EndPoint string `yaml:"endpoint"` } type HostConfig struct { Name string `yaml:"name"` Kubeconfig string `yaml:"kubeconfig"` } type SyncTask struct { AppName string Clustername string Namespace string ObjectID string } var ( client apiclient.Client once sync.Once err error ) func InitializeClient() { once.Do(func() { argocdServer := "192.168.2.172:32200" argocdToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmdvY2QiLCJzdWIiOiJhZG1pbjphcGlLZXkiLCJuYmYiOjE3NDM4NTAwNzIsImlhdCI6MTc0Mzg1MDA3MiwianRpIjoiNWZhNmQ5MDgtMzljNi00ZWQ4LWE5YzgtMzI4YzMzYjkyNzk4In0.ZvhJk4L5vBQldtJyReKYXCQCWF8j8gHLZlY8PninSFA" config := apiclient.ClientOptions{ ServerAddr: argocdServer, AuthToken: argocdToken, Insecure: true, } // Initialize the ArgoCD client client, err = apiclient.NewClient(&config) if err != nil { log.Fatalf("Failed to create ArgoCD client: %v", err) } }) } func generateRandomString(length int) string { const charset = "abcdefghijklmnopqrstuvwxyz0123456789" seededRand := rand.New(rand.NewSource(time.Now().UnixNano())) b := make([]byte, length) for i := range b { b[i] = charset[seededRand.Intn(len(charset))] } return string(b) } func CreateApp(objectID string, clustername string, ControlPlane string, PlatformVersion string, Cpu string, Memory string, userID string) { InitializeClient() // Create an application client _, appClient, err := client.NewApplicationClient() if err != nil { log.Fatalf("Failed to create application client: %v", err) } // Generate unique cluster name with user prefix uniqueClusterName := clustername namespace := "ns-" + generateRandomString(4) app := ApplicationValues{ Name: uniqueClusterName, Namespace: namespace, Path: "vcluster-0.21.1", Cluster: "bugx", Server: "https://kubernetes.default.svc", RepoURL: "http://192.168.2.20:8015/root/application.git", UserID: userID, } // Define the ArgoCD application argoApp := &argoprojv1alpha1.Application{ ObjectMeta: metav1.ObjectMeta{ Name: app.Name, Namespace: "argocd", Labels: map[string]string{ "user-id": userID, "type": "vcluster", "clustername": uniqueClusterName, "namespace": namespace, }, Finalizers: []string{ "resources-finalizer.argocd.argoproj.io", }, }, Spec: argoprojv1alpha1.ApplicationSpec{ Destination: argoprojv1alpha1.ApplicationDestination{ Namespace: app.Namespace, Name: app.Cluster, }, Source: &argoprojv1alpha1.ApplicationSource{ Path: app.Path, RepoURL: app.RepoURL, TargetRevision: "HEAD", Helm: &argoprojv1alpha1.ApplicationSourceHelm{ Parameters: []argoprojv1alpha1.HelmParameter{ { Name: "controlPlane.advanced.defaultImageRegistry", Value: "localhost:30516", }, { Name: "controlPlane.distro.k8s.version", Value: PlatformVersion, }, { Name: "controlPlane.distro.k8s.resources.limits.cpu", Value: Cpu, }, { Name: "controlPlane.distro.k8s.resources.limits.memory", Value: Memory + "Mi", }, { Name: "controlPlane.distro.k8s.resources.requests.cpu", Value: Cpu, }, { Name: "controlPlane.distro.k8s.resources.requests.memory", Value: Memory, }, { Name: "controlPlane.ingress.enabled", Value: "true", }, { Name: "controlPlane.ingress.host", Value: app.Namespace + ".bugx.ir", }, { Name: "controlPlane.proxy.extraSANs[0]", Value: app.Namespace + ".bugx.ir", }, { Name: "exportKubeConfig.server", Value: "https://" + app.Namespace + ".bugx.ir", }, }, }, }, Project: "default", SyncPolicy: &argoprojv1alpha1.SyncPolicy{ SyncOptions: []string{"CreateNamespace=true"}, }, }, } // Create the application createdApp, err := appClient.Create(context.Background(), &application.ApplicationCreateRequest{ Application: argoApp, }) if err != nil { log.Fatalf("Failed to create application: %v", err) } fmt.Printf("Application created: %s\n", createdApp.Name) SyncApp(objectID, app.Name, clustername, namespace) } func getConfig(objectID string, cluster string, namespace string) { var hostcluster HostConfig // "vc-"+cluster will be vcluster secret // kubectl get secret vc-test-prod -n ns-ekf7 -o yaml err := db.Host_cluster_details.FindOne(context.TODO(), bson.M{"name": "host-cluster-1"}).Decode(&hostcluster) if err != nil { fmt.Println("Can't get host config file") } kubeconfigBytes := []byte(hostcluster.Kubeconfig) config, err := clientcmd.RESTConfigFromKubeConfig(kubeconfigBytes) if err != nil { log.Fatalf("Error loading kubeconfig from string %v", err) } clientset, err := kubernetes.NewForConfig(config) if err != nil { log.Fatalf("Error creating clientSet: %v", err) } secret, err := clientset.CoreV1().Secrets(namespace).Get(context.TODO(), "vc-"+cluster, metav1.GetOptions{}) if err != nil { fmt.Println("Error in getting cluster config") return } configStrings := string(secret.Data["config"]) configStrings = base64.StdEncoding.EncodeToString([]byte(configStrings)) updateConfig(objectID, configStrings, namespace) } func updateConfig(objectID string, configStrings string, namespace string) { id, _ := primitive.ObjectIDFromHex(objectID) filter := bson.M{"_id": id} update := bson.M{ "$set": bson.M{ "cluster_config": configStrings, "namespace": namespace, }, } _, err = db.Vclusters_details.UpdateOne(context.TODO(), filter, update) if err != nil { fmt.Println("update cluster config error: ", err) } } func SyncApp(objectID string, appName string, cluster string, namesname string) { InitializeClient() // Create an application client _, appClient, err := client.NewApplicationClient() if err != nil { log.Fatalf("Failed to create application client: %v", err) } // Synchronize the application _, err = appClient.Sync(context.Background(), &application.ApplicationSyncRequest{ Name: &appName, }) if err != nil { log.Fatalf("Failed to sync application: %v", err) } var syncQueue chan SyncTask syncQueue = make(chan SyncTask, 100) go func() { for task := range syncQueue { for { app, err := appClient.Get(context.TODO(), &application.ApplicationQuery{Name: &task.AppName}) if err != nil { log.Printf("Error fetching app %s: %v", task.AppName, err) break } status := app.Status.Sync.Status health := app.Status.Health.Status log.Printf("App %s: sync=%s, health=%s", task.AppName, status, health) if status == "Synced" && health == "Healthy" { log.Printf("App %s synced successfully", task.AppName) getConfig(task.ObjectID, task.Clustername, task.Namespace) break } if status == "Unknown" || health == "Degraded" { log.Printf("App %s sync might be stuck or failed", task.AppName) } time.Sleep(3 * time.Second) } } }() syncQueue <- SyncTask{ AppName: appName, Clustername: cluster, Namespace: namesname, ObjectID: objectID, } fmt.Printf("Application synced successfully: %s\n", appName) } func ListUserClusters(userID string) (error, []Clusters) { InitializeClient() _, appClient, err := client.NewApplicationClient() if err != nil { return fmt.Errorf("failed to create application client: %v", err), nil } // List all applications with the user's label selector := fmt.Sprintf("user-id=%s", userID) apps, err := appClient.List(context.Background(), &application.ApplicationQuery{ Selector: &selector, }) if err != nil { return fmt.Errorf("failed to list applications: %v", err), nil } clusters := []Clusters{} for _, app := range apps.Items { newCluster := Clusters{ Name: app.Name, Status: string(app.Status.Health.Status), ClusterID: strings.Split(string(app.ObjectMeta.UID), "-")[0], } clusters = append(clusters, newCluster) } return nil, clusters }