376 lines
9.8 KiB
Go
376 lines
9.8 KiB
Go
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.28.0",
|
|
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 DeleteApp(appName string) error {
|
|
InitializeClient()
|
|
|
|
// Create an application client
|
|
_, appClient, err := client.NewApplicationClient()
|
|
if err != nil {
|
|
log.Fatalf("Failed to create application client: %v", err)
|
|
}
|
|
|
|
cascade := true
|
|
propagationPolicy := "Foreground"
|
|
|
|
_, err = appClient.Delete(context.Background(), &application.ApplicationDeleteRequest{
|
|
Name: &appName,
|
|
Cascade: &cascade, // Set to true if you want to delete related resources
|
|
PropagationPolicy: &propagationPolicy, // You can also use "Background" or "Orphan"
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete application: %v", err)
|
|
}
|
|
|
|
fmt.Printf("Application %s deleted successfully\n", appName)
|
|
return nil
|
|
}
|
|
|
|
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,
|
|
"EndPoine": "https://" + namespace + ".bugx.ir",
|
|
},
|
|
}
|
|
|
|
_, 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],
|
|
EndPoint: "https://" + app.Spec.Destination.Namespace + ".bugx.ir",
|
|
}
|
|
clusters = append(clusters, newCluster)
|
|
}
|
|
|
|
return nil, clusters
|
|
}
|