package handler import ( "bytes" "context" "encoding/base64" "encoding/json" "fmt" "io" "log" "main/argohandler" "main/db" "main/helpers" "main/models" "net/http" "os" "os/exec" "strconv" "strings" "time" "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" resourceapi "k8s.io/apimachinery/pkg/api/resource" "k8s.io/kubectl/pkg/scheme" syaml "sigs.k8s.io/yaml" // "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" autoscalingv1 "k8s.io/api/autoscaling/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/remotecommand" ) func Authorization(w http.ResponseWriter, r *http.Request) { var header models.Header var user models.User header.Authorization = r.Header.Get("Authorization") err := helpers.DecodeJwt(&header.Authorization, &user) if err != nil { http.Error(w, "Anauthorized User", http.StatusUnauthorized) return } err = helpers.ValidateUser(user.Username) if err != nil { http.Error(w, "Anauthorized User", http.StatusUnauthorized) return } } func CreateClusterHandler(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var cluster models.Cluster _ = json.NewDecoder(r.Body).Decode(&cluster) // vclusterCollection := db.Vclusters_details.FindOne(context.TODO(), bson.M{"name": Cluster.Name}).Decode(&existsCluster) if strings.ToLower(cluster.Name) == "" || cluster.ControlPlane == "" || cluster.PlatformVersion == "" || cluster.Cpu == "" || cluster.Memory == "" { http.Error(w, "Invalid input", http.StatusBadRequest) return } var existsCluster models.Cluster _ = db.Vclusters_details.FindOne(context.TODO(), bson.M{"name": cluster.Name}).Decode(&existsCluster) if existsCluster.Name == cluster.Name { http.Error(w, "Cluster name is duplicated", http.StatusBadRequest) return } res, err := db.Vclusters_details.InsertOne(context.TODO(), cluster) if err != nil { http.Error(w, `{"message": "Could not create cluster"}`, http.StatusInternalServerError) } objectID := res.InsertedID.(primitive.ObjectID) idStr := objectID.Hex() argohandler.CreateApp(idStr, cluster.Name, cluster.ControlPlane, cluster.PlatformVersion, cluster.Cpu, cluster.Memory, "userid") response := map[string]string{"message": "Cluster created"} w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } func Deletecluster(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clusterName := r.URL.Query().Get("Name") if clusterName == "" { http.Error(w, "Invalid input", http.StatusBadRequest) return } err := argohandler.DeleteApp(clusterName) if err != nil { http.Error(w, "File to delete "+clusterName, http.StatusInternalServerError) return } res, err := db.Vclusters_details.DeleteOne(context.TODO(), bson.M{"name": clusterName}) if err != nil { http.Error(w, `{"message": "Could not delete cluster"}`, http.StatusInternalServerError) return } if res.DeletedCount == 0 { http.Error(w, `{"message": "No cluster found to delete"}`, http.StatusNotFound) return } } func getClientset(w http.ResponseWriter, clustername string) (*kubernetes.Clientset, *rest.Config, error) { kubeconfig, err := getClusterConfig(clustername) if err != nil { http.Error(w, "File to get kubeconfig", http.StatusInternalServerError) return nil, nil, err } kubeconfigbyte := []byte(kubeconfig) config, err := clientcmd.RESTConfigFromKubeConfig(kubeconfigbyte) if err != nil { log.Println("Error creating rest config:", err) return nil, nil, err } clientset, err := kubernetes.NewForConfig(config) if err != nil { log.Println("Error creating clientSet:", err) return nil, nil, err } return clientset, config, nil } func ListUserClusters(w http.ResponseWriter, r *http.Request) { Authorization(w, r) _, clusterList := argohandler.ListUserClusters("userid") w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(clusterList) } func Cluster_namespaces(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting clientset: ", err) } listOfnamespaces, err := clientset.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of pods: ", err) } Allnamespaces := []models.Namespace{} var namespace models.Namespace now := time.Now() for _, s := range listOfnamespaces.Items { namespace.Name = s.Name namespace.Status = string(s.Status.Phase) age := now.Sub(s.CreationTimestamp.Time) namespace.Age = helpers.Human(age) Allnamespaces = append(Allnamespaces, namespace) } //pod_list, err := json.Marshal(Allservice) if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(Allnamespaces) } func Cluster_services(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting clientset: ", err) } services, err := clientset.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of services: ", err) } Allservice := []models.Service{} var service models.Service now := time.Now() for _, s := range services.Items { service.Name = s.Name service.Namespace = s.Namespace service.Type = string(s.Spec.Type) service.Ports = servicePortsToString(s.Spec.Ports) service.ClusterIP = s.Spec.ClusterIP age := now.Sub(s.CreationTimestamp.Time) service.Age = helpers.Human(age) if len(s.Spec.ExternalIPs) > 0 { service.ExternalIP = s.Spec.ExternalIPs[0] } Allservice = append(Allservice, service) } //pod_list, err := json.Marshal(Allservice) if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(Allservice) } func Cluster_statefulset(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting clientset: ", err) } statefulSets, err := clientset.AppsV1().StatefulSets(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of StatefulSets: ", err) } AllstatefulSets := []models.StatefulSet{} var StatefulSet models.StatefulSet now := time.Now() for _, s := range statefulSets.Items { desired := int32(1) if s.Spec.Replicas != nil { desired = *s.Spec.Replicas } StatefulSet.Ready = fmt.Sprintf("%d/%d", s.Status.ReadyReplicas, desired) StatefulSet.Name = s.Name StatefulSet.Namespace = s.Namespace age := now.Sub(s.CreationTimestamp.Time) // same as kubectl AGE StatefulSet.Age = helpers.Human(age) AllstatefulSets = append(AllstatefulSets, StatefulSet) } //pod_list, err := json.Marshal(Allservice) if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(AllstatefulSets) } func Cluster_daemonsets(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting clientset: ", err) } DaemonSetss, err := clientset.AppsV1().DaemonSets(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of DaemonSets: ", err) } AllDaemonSets := []models.Daemonset{} var DaemonSets models.Daemonset now := time.Now() for _, s := range DaemonSetss.Items { DaemonSets.Name = s.Name DaemonSets.Namespace = s.Namespace DaemonSets.DESIRED = s.Status.DesiredNumberScheduled DaemonSets.CURRENT = s.Status.CurrentNumberScheduled DaemonSets.Available = s.Status.NumberAvailable DaemonSets.Ready = s.Status.NumberReady DaemonSets.UpdateToDate = s.Status.UpdatedNumberScheduled DaemonSets.Node = s.Spec.Template.Spec.NodeName DaemonSets.Selector = helpers.JoinNodeSelector(s.Spec.Template.Spec.NodeSelector) age := now.Sub(s.CreationTimestamp.Time) // same as kubectl AGE DaemonSets.Age = helpers.Human(age) AllDaemonSets = append(AllDaemonSets, DaemonSets) } //pod_list, err := json.Marshal(Allservice) if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(AllDaemonSets) } func getDeploymentreadtStratus(clientset *kubernetes.Clientset, deploymentName string, namespace string, labels []string) (string, error) { _, err := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) if err != nil { return "", err } //pod, _ := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) //fmt.Print(len(pod.Items)) // Get the pods matching the label selector podList, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{ LabelSelector: labels[0], // Ensure correct label selector }) if err != nil { return "", err } totalPods := len(podList.Items) readyPods := 0 for _, pod := range podList.Items { readyContainer := 0 totalContainers := len(pod.Status.ContainerStatuses) for _, contianer := range pod.Status.ContainerStatuses { if contianer.Ready { readyContainer++ } } if readyContainer == totalContainers { readyPods++ } } return fmt.Sprintf("%d/%d", readyPods, totalPods), nil } func Cluster_deployments(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting clientset: ", err) } deployments, err := clientset.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of deployments: ", err) } Alldeployment := []models.Deployment{} var deployment models.Deployment var avail bool var reason, msg string for _, d := range deployments.Items { deployment.Name = d.Name deployment.Namespace = d.Namespace deployment.Replicas = *d.Spec.Replicas labels := deployments.Items[0].Spec.Selector.MatchLabels now := time.Now() age := now.Sub(d.CreationTimestamp.Time) // same as kubectl AGE deployment.Age = helpers.Human(age) deployment.Image = d.Spec.Template.Spec.Containers[0].Image deployment.Strategy = string(d.Spec.Strategy.Type) deployment.UpdateToDate = d.Status.UpdatedReplicas var label_result []string for k, v := range labels { label_result = append(label_result, fmt.Sprintf("%s=%s", k, v)) } deployment.Ready, _ = getDeploymentreadtStratus(clientset, deployment.Name, deployment.Namespace, label_result) for _, c := range d.Status.Conditions { if c.Type == appsv1.DeploymentAvailable { avail = (c.Status == corev1.ConditionTrue) reason, msg = c.Reason, c.Message deployment.Available = fmt.Sprintf("%t", avail) deployment.Message = msg deployment.Reason = reason } } Alldeployment = append(Alldeployment, deployment) } if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(Alldeployment) } func servicePortsToString(ports []corev1.ServicePort) string { var parts []string for _, p := range ports { // Example format: "80/TCP" parts = append(parts, fmt.Sprintf("%d/%s", p.Port, p.Protocol)) } return strings.Join(parts, ", ") } func Cluster_pods(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting clientset: ", err) } pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of pods: ", err) } Allpod := []models.Pod{} var pod models.Pod now := time.Now() for _, p := range pods.Items { fmt.Printf(p.Name, p.Namespace) pod.Name = p.Name pod.Namespace = p.Namespace pod.Status = string(p.Status.Phase) age := now.Sub(p.CreationTimestamp.Time) // same as kubectl AGE pod.Age = helpers.Human(age) var restartedCount int32 var restarts int32 for _, st := range p.Status.ContainerStatuses { restarts += st.RestartCount } pod.Restarts = restartedCount total := len(p.Status.ContainerStatuses) ready := 0 for _, cs := range p.Status.ContainerStatuses { if cs.Ready { ready++ } } pod.Ready = fmt.Sprintf("%d/%d", ready, total) pod.Ip = p.Status.PodIP pod.Node = p.Spec.NodeName pod.Image = p.Status.ContainerStatuses[0].Image Allpod = append(Allpod, pod) } //pod_list, err := json.Marshal(Allpod) if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(Allpod) } func getClusterConfig(clustername string) (string, error) { var existsCluster models.Cluster err := db.Vclusters_details.FindOne(context.TODO(), bson.M{"name": clustername}).Decode(&existsCluster) if err != nil { return "", err } decoded, err := base64.StdEncoding.DecodeString(existsCluster.Cluster_config) if err == nil { return string(decoded), nil } return "", err } func Connect(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clusterName := r.URL.Query().Get("Name") if clusterName == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } var existsCluster models.Cluster _ = db.Vclusters_details.FindOne(context.TODO(), bson.M{"name": clusterName}).Decode(&existsCluster) decoded, err := base64.StdEncoding.DecodeString(existsCluster.Cluster_config) if err != nil { http.Error(w, "Failed to decode cluster config", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/x-yaml") w.Header().Set("Content-Disposition", `attachment; filename="`+clusterName+`.yaml"`) w.Write(decoded) } func Cluster_jobs(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting Jobs: ", err) } jobs, err := clientset.BatchV1().Jobs(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of Jobs: ", err) } AllJob := []models.Jobs{} var job models.Jobs now := time.Now() for _, d := range jobs.Items { job.Name = d.Name job.Namespace = d.Namespace status := "Active" if d.Status.Succeeded > 0 { status = "Complete" } else if d.Status.Failed > 0 { status = "Failed" } job.Status = status completions := fmt.Sprintf("%d/%d", d.Status.Succeeded, *d.Spec.Completions) job.Completion = completions duration := "-" if d.Status.StartTime != nil && d.Status.CompletionTime != nil { d := d.Status.CompletionTime.Time.Sub(d.Status.StartTime.Time) duration = d.String() } job.Duration = duration age := now.Sub(d.CreationTimestamp.Time) // same as kubectl AGE job.Age = helpers.Human(age) AllJob = append(AllJob, job) } if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(AllJob) } func Cluster_replicasets(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting replicasets: ", err) } replicasets, err := clientset.AppsV1().ReplicaSets(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of Replicaset: ", err) } Allreplicaset := []models.Replicaset{} var replicaset models.Replicaset now := time.Now() for _, d := range replicasets.Items { replicaset.Name = d.Name replicaset.Namespace = d.Namespace replicaset.Desired = *d.Spec.Replicas replicaset.Current = d.Status.Replicas replicaset.Ready = d.Status.ReadyReplicas age := now.Sub(d.CreationTimestamp.Time) // same as kubectl AGE replicaset.Age = helpers.Human(age) Allreplicaset = append(Allreplicaset, replicaset) } if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(Allreplicaset) } func Cluster_replicationcontrollers(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting Replicationcontrollers: ", err) } replicationcontrollers, err := clientset.CoreV1().ReplicationControllers(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of ReplicationController: ", err) } AllreplicationController := []models.ReplicationController{} var ReplicationController models.ReplicationController now := time.Now() for _, d := range replicationcontrollers.Items { ReplicationController.Name = d.Name ReplicationController.Namespace = d.Namespace age := now.Sub(d.CreationTimestamp.Time) // same as kubectl AGE ReplicationController.Age = helpers.Human(age) AllreplicationController = append(AllreplicationController, ReplicationController) } if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(AllreplicationController) } func Cluster_cronjobs(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { log.Fatal("Error getting cronjobs: ", err) } cjs, err := clientset.BatchV1().CronJobs(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { log.Fatal("Error getting list of CronJobs: ", err) } allCronJobs := []models.CronJob{} var item models.CronJob now := time.Now() for _, cj := range cjs.Items { item.Name = cj.Name item.Namespace = cj.Namespace item.Schedule = cj.Spec.Schedule if cj.Spec.Suspend != nil { item.Suspend = *cj.Spec.Suspend } else { item.Suspend = false } item.Active = len(cj.Status.Active) if cj.Status.LastScheduleTime != nil { item.LastScheduleTime = cj.Status.LastScheduleTime.Time.UTC().Format(time.RFC3339) } age := now.Sub(cj.CreationTimestamp.Time) item.Age = helpers.Human(age) allCronJobs = append(allCronJobs, item) } if err != nil { http.Error(w, "Internal Error", http.StatusInternalServerError) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(allCronJobs) } func Pod_logs(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var podlog models.PodLog _ = json.NewDecoder(r.Body).Decode(&podlog) // containerName := podName if podlog.Clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, podlog.Clustername) if err != nil { log.Fatal("Error getting Replicationcontrollers: ", err) } podLogOpts := corev1.PodLogOptions{} req := clientset.CoreV1().Pods(podlog.Namespace).GetLogs(podlog.Podname, &podLogOpts) podLogs, err := req.Stream(context.TODO()) if err != nil { http.Error(w, "an error happend in getting logs", http.StatusBadRequest) return } defer podLogs.Close() buf := new([]byte) *buf, err = io.ReadAll(podLogs) if err != nil { http.Error(w, "an error happend in getting logs", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(*buf)) } func Replicaset_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var podlog models.PodLog _ = json.NewDecoder(r.Body).Decode(&podlog) // containerName := podName if podlog.Clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, podlog.Clustername) if err != nil { log.Fatal("Error getting Replicaset: ", err) } pod, err := clientset.AppsV1().ReplicaSets(podlog.Namespace).Get(context.TODO(), podlog.Replicasetname, metav1.GetOptions{}) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } podYAML, err := yaml.Marshal(pod) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(podYAML)) } func Jobs_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) // Expecting JSON body: {"Clustername":"...", "Namespace":"...", "Jobsname":"..."} var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Jobsname string `json:"Jobsname"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Jobsname == "" { http.Error(w, "Missing required fields: Clustername and Jobsname", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } job, err := clientset.BatchV1().Jobs(req.Namespace).Get(context.TODO(), req.Jobsname, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get job: "+err.Error(), http.StatusBadRequest) return } jobYAML, err := yaml.Marshal(job) if err != nil { http.Error(w, "Failed to marshal job yaml: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(jobYAML)) } func CronJobs_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Cronjobsname string `json:"Cronjobsname"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Cronjobsname == "" { http.Error(w, "Missing required fields: Clustername and CronjobName", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } cj, err := clientset.BatchV1().CronJobs(req.Namespace).Get(context.TODO(), req.Cronjobsname, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get cronjob: "+err.Error(), http.StatusBadRequest) return } cjYAML, err := yaml.Marshal(cj) if err != nil { http.Error(w, "Failed to marshal cronjob yaml: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(cjYAML)) } func Cronjobs_trigger(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Cronjobsname string `json:"Cronjobsname"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Cronjobsname == "" { http.Error(w, "Missing required fields: Clustername and Cronjobsname", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } // Get the CronJob cj, err := clientset.BatchV1().CronJobs(req.Namespace).Get(context.TODO(), req.Cronjobsname, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get cronjob: "+err.Error(), http.StatusBadRequest) return } // Build a Job from the CronJob's job template job := &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ GenerateName: cj.Name + "-", Namespace: req.Namespace, Labels: cj.Spec.JobTemplate.Labels, Annotations: cj.Spec.JobTemplate.Annotations, }, Spec: cj.Spec.JobTemplate.Spec, } // Optionally set owner reference so garbage collection can relate it back block := true controller := true job.OwnerReferences = []metav1.OwnerReference{ { APIVersion: "batch/v1", Kind: "CronJob", Name: cj.Name, UID: cj.UID, Controller: &controller, BlockOwnerDeletion: &block, }, } created, err := clientset.BatchV1().Jobs(req.Namespace).Create(context.TODO(), job, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create job from cronjob: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]string{ "Clustername": req.Clustername, "Cronjobsname": req.Cronjobsname, "Namespace": req.Namespace, "JobName": created.Name, }) } func Cronjobs_suspend(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Cronjobsname string `json:"Cronjobsname"` Suspend bool `json:"Suspend"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Cronjobsname == "" { http.Error(w, "Missing required fields: Clustername and Cronjobsname", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } cj, err := clientset.BatchV1().CronJobs(req.Namespace).Get(context.TODO(), req.Cronjobsname, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get cronjob: "+err.Error(), http.StatusBadRequest) return } cj.Spec.Suspend = &req.Suspend updated, err := clientset.BatchV1().CronJobs(req.Namespace).Update(context.TODO(), cj, metav1.UpdateOptions{}) if err != nil { http.Error(w, "Failed to update cronjob suspend: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]interface{}{ "Clustername": req.Clustername, "Cronjobsname": req.Cronjobsname, "Namespace": req.Namespace, "Suspend": req.Suspend, "ResourceVersion": updated.ResourceVersion, }) } func Jobs_logs(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Jobsname string `json:"Jobsname"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Jobsname == "" { http.Error(w, "Missing required fields: Clustername and Jobsname", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } // Get the Job to derive its Pod selector job, err := clientset.BatchV1().Jobs(req.Namespace).Get(context.TODO(), req.Jobsname, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get job: "+err.Error(), http.StatusBadRequest) return } var labelSelector string if job.Spec.Selector != nil { if sel, err := metav1.LabelSelectorAsSelector(job.Spec.Selector); err == nil { labelSelector = sel.String() } } // Fallback commonly used label if selector was empty if labelSelector == "" { labelSelector = "job-name=" + req.Jobsname } pods, err := clientset.CoreV1().Pods(req.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector}) if err != nil { http.Error(w, "Failed to list pods for job: "+err.Error(), http.StatusBadRequest) return } results := map[string]interface{}{} for _, p := range pods.Items { podLogOpts := corev1.PodLogOptions{} // if multiple containers, fetch logs from each if len(p.Spec.Containers) <= 1 { reqLog := clientset.CoreV1().Pods(req.Namespace).GetLogs(p.Name, &podLogOpts) stream, err := reqLog.Stream(context.TODO()) if err != nil { results[p.Name] = map[string]string{"error": err.Error()} continue } buf, _ := io.ReadAll(stream) _ = stream.Close() results[p.Name] = strings.Split(strings.TrimSpace(string(buf)), "\n") } else { containerLogs := map[string][]string{} for _, c := range p.Spec.Containers { opts := corev1.PodLogOptions{Container: c.Name} reqLog := clientset.CoreV1().Pods(req.Namespace).GetLogs(p.Name, &opts) stream, err := reqLog.Stream(context.TODO()) if err != nil { containerLogs[c.Name] = []string{"error: " + err.Error()} continue } buf, _ := io.ReadAll(stream) _ = stream.Close() containerLogs[c.Name] = strings.Split(strings.TrimSpace(string(buf)), "\n") } results[p.Name] = containerLogs } } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(results) } func Pod_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var podlog models.PodLog _ = json.NewDecoder(r.Body).Decode(&podlog) // containerName := podName if podlog.Clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, podlog.Clustername) if err != nil { log.Fatal("Error getting Replicationcontrollers: ", err) } pod, err := clientset.CoreV1().Pods(podlog.Namespace).Get(context.TODO(), podlog.Podname, metav1.GetOptions{}) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } podYAML, err := yaml.Marshal(pod) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(podYAML)) } func Deployment_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var podlog models.PodLog _ = json.NewDecoder(r.Body).Decode(&podlog) // containerName := podName if podlog.Clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, podlog.Clustername) if err != nil { log.Fatal("Error getting Replicationcontrollers: ", err) } deployment, err := clientset.AppsV1().Deployments(podlog.Namespace).Get(context.TODO(), podlog.Podname, metav1.GetOptions{}) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } deploymentYAML, err := yaml.Marshal(deployment) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(deploymentYAML)) } func StatefulSet_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var podlog models.PodLog _ = json.NewDecoder(r.Body).Decode(&podlog) if podlog.Clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, podlog.Clustername) if err != nil { log.Fatal("Error getting statefulset: ", err) } ss, err := clientset.AppsV1().StatefulSets(podlog.Namespace).Get(context.TODO(), podlog.Statefulset, metav1.GetOptions{}) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } ssYAML, err := yaml.Marshal(ss) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(ssYAML)) } func DaemonSet_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog _ = json.NewDecoder(r.Body).Decode(&req) if req.Clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, req.Clustername) if err != nil { log.Fatal("Error getting daemonset: ", err) } // Reuse Podname field to carry DaemonSet name, similar to Deployment_manifest ds, err := clientset.AppsV1().DaemonSets(req.Namespace).Get(context.TODO(), req.Daemonsetsname, metav1.GetOptions{}) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } dsYAML, err := yaml.Marshal(ds) if err != nil { http.Error(w, "an error happend ", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(dsYAML)) } func ReplicaSet_scale(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.ReplicasetScaleReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON body", http.StatusBadRequest) return } // Basic validation if req.Clustername == "" || req.Replicasetname == "" { http.Error(w, "missing required fields: Clustername and ReplicaSet", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } if req.Replicas < 0 { http.Error(w, "Replicas must be >= 0", http.StatusBadRequest) return } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } ctx := context.TODO() // Get current scale scale, err := clientset.AppsV1(). ReplicaSets(req.Namespace). GetScale(ctx, req.Replicasetname, metav1.GetOptions{}) if err != nil { http.Error(w, "failed to get current scale: "+err.Error(), http.StatusBadRequest) return } // Update desired replicas scale.Spec.Replicas = req.Replicas // Apply via the scale subresource _, err = clientset.AppsV1(). ReplicaSets(req.Namespace). UpdateScale(ctx, req.Replicasetname, &autoscalingv1.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: req.Replicasetname, Namespace: req.Namespace, // ResourceVersion optional for concurrency safety // ResourceVersion: scale.ResourceVersion, }, Spec: autoscalingv1.ScaleSpec{ Replicas: req.Replicas, }, }, metav1.UpdateOptions{}) if err != nil { http.Error(w, "failed to update scale: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(models.DeploymentScaleReq{ Clustername: req.Clustername, Replicas: req.Replicas, }) } func Deployment_scale(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.DeploymentScaleReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON body", http.StatusBadRequest) return } // Basic validation if req.Clustername == "" || req.Deployment == "" { http.Error(w, "missing required fields: Clustername and Deployment", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } if req.Replicas < 0 { http.Error(w, "Replicas must be >= 0", http.StatusBadRequest) return } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } ctx := context.TODO() // Get current scale scale, err := clientset.AppsV1(). Deployments(req.Namespace). GetScale(ctx, req.Deployment, metav1.GetOptions{}) if err != nil { http.Error(w, "failed to get current scale: "+err.Error(), http.StatusBadRequest) return } // Update desired replicas scale.Spec.Replicas = req.Replicas // Apply via the scale subresource _, err = clientset.AppsV1(). Deployments(req.Namespace). UpdateScale(ctx, req.Deployment, &autoscalingv1.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: req.Deployment, Namespace: req.Namespace, // Optionally carry resourceVersion to be strict about concurrency: // ResourceVersion: scale.ResourceVersion, }, Spec: autoscalingv1.ScaleSpec{ Replicas: req.Replicas, }, }, metav1.UpdateOptions{}) if err != nil { http.Error(w, "failed to update scale: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(models.DeploymentScaleReq{ Clustername: req.Clustername, Replicas: req.Replicas, }) } func StatefulSet_scale(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.StatefulsetRolloutReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Statefulset == "" { // reuse struct: Deployment field will carry StatefulSet name http.Error(w, "missing required fields: Clustername and StatefulSet", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } if req.Replicas < 0 { http.Error(w, "Replicas must be >= 0", http.StatusBadRequest) return } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } ctx := context.TODO() scale, err := clientset.AppsV1().StatefulSets(req.Namespace).GetScale(ctx, req.Statefulset, metav1.GetOptions{}) if err != nil { http.Error(w, "failed to get current scale: "+err.Error(), http.StatusBadRequest) return } scale.Spec.Replicas = req.Replicas _, err = clientset.AppsV1().StatefulSets(req.Namespace).UpdateScale(ctx, req.Statefulset, &autoscalingv1.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: req.Statefulset, Namespace: req.Namespace, }, Spec: autoscalingv1.ScaleSpec{ Replicas: req.Replicas, }, }, metav1.UpdateOptions{}) if err != nil { http.Error(w, "failed to update scale: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(models.DeploymentScaleReq{ Clustername: req.Clustername, Replicas: req.Replicas, }) } func Deployment_rollout(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.DeploymentRolloutReq if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Deployment == "" || req.Action == "" { http.Error(w, "missing required fields: Clustername, Deployment, Action", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } ctx := context.TODO() switch req.Action { case "restart": // Patch the pod template annotation to force a new ReplicaSet now := time.Now().UTC().Format(time.RFC3339) patch := []byte(`{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"` + now + `"}}}}}`) _, err := clientset.AppsV1(). Deployments(req.Namespace). Patch(ctx, req.Deployment, types.StrategicMergePatchType, patch, metav1.PatchOptions{}) if err != nil { http.Error(w, "failed to patch deployment for restart: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(models.DeploymentRolloutResp{ Clustername: req.Clustername, Message: "rollout restart triggered", }) return case "status": dep, err := clientset.AppsV1(). Deployments(req.Namespace). Get(ctx, req.Deployment, metav1.GetOptions{}) if err != nil { http.Error(w, "failed to get deployment: "+err.Error(), http.StatusBadRequest) return } resp := makeStatusSnapshot(dep, req.Namespace) w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(models.DeploymentRolloutResp{ Clustername: req.Clustername, Message: "rollout status retrieved", Status: resp, }) return default: http.Error(w, "unsupported Action (use \"restart\" or \"status\")", http.StatusBadRequest) return } } func StatefulSet_rollout(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.StatefulsetRolloutReq // reuse struct; Deployment field will carry StatefulSet name if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Statefulset == "" || req.Action == "" { http.Error(w, "missing required fields: Clustername, StatefulSet, Action", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } ctx := context.TODO() switch req.Action { case "restart": // Force a rolling restart by updating pod template annotation now := time.Now().UTC().Format(time.RFC3339) patch := []byte(`{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"` + now + `"}}}}}`) _, err := clientset.AppsV1().StatefulSets(req.Namespace).Patch(ctx, req.Statefulset, types.StrategicMergePatchType, patch, metav1.PatchOptions{}) if err != nil { http.Error(w, "failed to patch statefulset for restart: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(models.DeploymentRolloutResp{ Clustername: req.Clustername, Message: "rollout restart triggered", }) return case "status": ss, err := clientset.AppsV1().StatefulSets(req.Namespace).Get(ctx, req.Statefulset, metav1.GetOptions{}) if err != nil { http.Error(w, "failed to get statefulset: "+err.Error(), http.StatusBadRequest) return } unavailable := ss.Status.Replicas - ss.Status.ReadyReplicas status := &models.DeploymentRolloutStatus{ Deployment: ss.Name, Namespace: req.Namespace, ObservedGeneration: ss.Status.ObservedGeneration, Replicas: ss.Status.Replicas, UpdatedReplicas: ss.Status.UpdatedReplicas, ReadyReplicas: ss.Status.ReadyReplicas, AvailableReplicas: ss.Status.ReadyReplicas, UnavailableReplicas: unavailable, } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(models.DeploymentRolloutResp{ Clustername: req.Clustername, Message: "rollout status retrieved", Status: status, }) return default: http.Error(w, "unsupported Action (use \"restart\" or \"status\")", http.StatusBadRequest) return } } func DaemonSet_rollout(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.DeamonSetsRolloutReq // reuse struct; Deployment carries DaemonSet name if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Daemonsetsname == "" || req.Action == "" { http.Error(w, "missing required fields: Clustername, DaemonSet, Action", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } ctx := context.TODO() switch req.Action { case "restart": // Force rolling restart by touching pod template annotation now := time.Now().UTC().Format(time.RFC3339) patch := []byte(`{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"` + now + `"}}}}}`) _, err := clientset.AppsV1().DaemonSets(req.Namespace).Patch(ctx, req.Daemonsetsname, types.StrategicMergePatchType, patch, metav1.PatchOptions{}) if err != nil { http.Error(w, "failed to patch daemonset for restart: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(models.DeploymentRolloutResp{ Clustername: req.Clustername, Message: "rollout restart triggered", }) return case "status": ds, err := clientset.AppsV1().DaemonSets(req.Namespace).Get(ctx, req.Daemonsetsname, metav1.GetOptions{}) if err != nil { http.Error(w, "failed to get daemonset: "+err.Error(), http.StatusBadRequest) return } unavailable := ds.Status.DesiredNumberScheduled - ds.Status.NumberAvailable status := &models.DeploymentRolloutStatus{ Deployment: ds.Name, Namespace: req.Namespace, ObservedGeneration: ds.Status.ObservedGeneration, Replicas: ds.Status.DesiredNumberScheduled, UpdatedReplicas: ds.Status.UpdatedNumberScheduled, ReadyReplicas: ds.Status.NumberReady, AvailableReplicas: ds.Status.NumberAvailable, UnavailableReplicas: unavailable, } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(models.DeploymentRolloutResp{ Clustername: req.Clustername, Message: "rollout status retrieved", Status: status, }) return default: http.Error(w, "unsupported Action (use \"restart\" or \"status\")", http.StatusBadRequest) return } } func makeStatusSnapshot(dep *appsv1.Deployment, ns string) *models.DeploymentRolloutStatus { var condProgressing, condAvailable string for _, c := range dep.Status.Conditions { switch c.Type { case appsv1.DeploymentProgressing: condProgressing = string(c.Status) + ": " + c.Reason case appsv1.DeploymentAvailable: condAvailable = string(c.Status) + ": " + c.Reason } } unavailable := dep.Status.Replicas - dep.Status.AvailableReplicas return &models.DeploymentRolloutStatus{ Deployment: dep.Name, Namespace: ns, ObservedGeneration: dep.Status.ObservedGeneration, Replicas: dep.Status.Replicas, UpdatedReplicas: dep.Status.UpdatedReplicas, ReadyReplicas: dep.Status.ReadyReplicas, AvailableReplicas: dep.Status.AvailableReplicas, UnavailableReplicas: unavailable, ConditionProgressing: condProgressing, ConditionAvailable: condAvailable, } } func Deployment_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var deployment appsv1.Deployment if err := syaml.Unmarshal(decoded, &deployment); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } // Debug logs for visibility log.Println("Decoded Deployment Manifest:", deployment.Name, deployment.Namespace) if deployment.APIVersion == "" { deployment.APIVersion = "apps/v1" } if deployment.Kind == "" { deployment.Kind = "Deployment" } if deployment.Name == "" { http.Error(w, "Missing deployment name", http.StatusBadRequest) return } // Ensure at least one container exists in the pod template if len(deployment.Spec.Template.Spec.Containers) == 0 { http.Error(w, "Deployment must define at least one container", http.StatusBadRequest) return } if deployment.Namespace == "" { deployment.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.AppsV1().Deployments(deployment.Namespace).Create(context.TODO(), &deployment, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create deployment: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func StatefulSet_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var ss appsv1.StatefulSet if err := syaml.Unmarshal(decoded, &ss); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } // Debug logs for visibility log.Println("Decoded StatefulSet Manifest:", ss.Name, ss.Namespace) if ss.APIVersion == "" { ss.APIVersion = "apps/v1" } if ss.Kind == "" { ss.Kind = "StatefulSet" } if ss.Name == "" { http.Error(w, "Missing statefulset name", http.StatusBadRequest) return } if len(ss.Spec.Template.Spec.Containers) == 0 { http.Error(w, "StatefulSet must define at least one container", http.StatusBadRequest) return } if ss.Namespace == "" { ss.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.AppsV1().StatefulSets(ss.Namespace).Create(context.TODO(), &ss, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create statefulset: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func DaemonSet_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var ds appsv1.DaemonSet if err := syaml.Unmarshal(decoded, &ds); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } log.Println("Decoded DaemonSet Manifest:", ds.Name, ds.Namespace) if ds.APIVersion == "" { ds.APIVersion = "apps/v1" } if ds.Kind == "" { ds.Kind = "DaemonSet" } if ds.Name == "" { http.Error(w, "Missing daemonset name", http.StatusBadRequest) return } if len(ds.Spec.Template.Spec.Containers) == 0 { http.Error(w, "DaemonSet must define at least one container", http.StatusBadRequest) return } if ds.Namespace == "" { ds.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.AppsV1().DaemonSets(ds.Namespace).Create(context.TODO(), &ds, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create daemonset: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func ReplicationController_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var rc corev1.ReplicationController if err := syaml.Unmarshal(decoded, &rc); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } if rc.APIVersion == "" { rc.APIVersion = "v1" } if rc.Kind == "" { rc.Kind = "ReplicationController" } if rc.Name == "" { http.Error(w, "Missing replicationcontroller name", http.StatusBadRequest) return } if rc.Spec.Template == nil || len(rc.Spec.Template.Spec.Containers) == 0 { http.Error(w, "ReplicationController must define at least one container", http.StatusBadRequest) return } if rc.Namespace == "" { rc.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.CoreV1().ReplicationControllers(rc.Namespace).Create(context.TODO(), &rc, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create replicationcontroller: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func Replicationcontroller_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Replicationcontroller string `json:"Replicationcontroller"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Replicationcontroller == "" { http.Error(w, "Missing required fields: Clustername and Replicationcontroller", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } rc, err := clientset.CoreV1().ReplicationControllers(req.Namespace).Get(context.TODO(), req.Replicationcontroller, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get replicationcontroller: "+err.Error(), http.StatusBadRequest) return } rcYAML, err := yaml.Marshal(rc) if err != nil { http.Error(w, "Failed to marshal replicationcontroller yaml: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(rcYAML)) } func Replicationcontroller_scale(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Replicationcontroller string `json:"Replicationcontroller"` Replicas int32 `json:"Replicas"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Replicationcontroller == "" { http.Error(w, "missing required fields: Clustername and Replicationcontroller", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } if req.Replicas < 0 { http.Error(w, "Replicas must be >= 0", http.StatusBadRequest) return } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } ctx := context.TODO() scale, err := clientset.CoreV1().ReplicationControllers(req.Namespace).GetScale(ctx, req.Replicationcontroller, metav1.GetOptions{}) if err != nil { http.Error(w, "failed to get current scale: "+err.Error(), http.StatusBadRequest) return } scale.Spec.Replicas = req.Replicas _, err = clientset.CoreV1().ReplicationControllers(req.Namespace).UpdateScale(ctx, req.Replicationcontroller, &autoscalingv1.Scale{ ObjectMeta: metav1.ObjectMeta{ Name: req.Replicationcontroller, Namespace: req.Namespace, }, Spec: autoscalingv1.ScaleSpec{ Replicas: req.Replicas, }, }, metav1.UpdateOptions{}) if err != nil { http.Error(w, "failed to update scale: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "Clustername": req.Clustername, "Replicationcontroller": req.Replicationcontroller, "Namespace": req.Namespace, "Replicas": req.Replicas, }) } func Replicationcontroller_migrate(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Replicationcontroller string `json:"Replicationcontroller"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Replicationcontroller == "" { http.Error(w, "Missing required fields: Clustername and Replicationcontroller", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } // Fetch the source ReplicationController rc, err := clientset.CoreV1().ReplicationControllers(req.Namespace).Get(context.TODO(), req.Replicationcontroller, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get replicationcontroller: "+err.Error(), http.StatusBadRequest) return } // Construct a Deployment with equivalent spec replicas := int32(1) if rc.Spec.Replicas != nil { replicas = *rc.Spec.Replicas } if rc.Spec.Template == nil { http.Error(w, "ReplicationController has no pod template", http.StatusBadRequest) return } dep := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: rc.Name, Namespace: rc.Namespace, Labels: rc.Labels, Annotations: rc.Annotations, }, Spec: appsv1.DeploymentSpec{ Replicas: &replicas, Selector: &metav1.LabelSelector{MatchLabels: rc.Spec.Selector}, Template: *rc.Spec.Template, Strategy: appsv1.DeploymentStrategy{Type: appsv1.RollingUpdateDeploymentStrategyType}, }, } created, err := clientset.AppsV1().Deployments(dep.Namespace).Create(context.TODO(), dep, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create deployment: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, "from": rc.Name, "kind": "Deployment", }) } func Service_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var svc corev1.Service if err := syaml.Unmarshal(decoded, &svc); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } if svc.APIVersion == "" { svc.APIVersion = "v1" } if svc.Kind == "" { svc.Kind = "Service" } if svc.Name == "" { http.Error(w, "Missing service name", http.StatusBadRequest) return } if svc.Namespace == "" { svc.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.CoreV1().Services(svc.Namespace).Create(context.TODO(), &svc, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create service: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func Service_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Servicename string `json:"Servicename"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Servicename == "" { http.Error(w, "Missing required fields: Clustername and Service", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } svc, err := clientset.CoreV1().Services(req.Namespace).Get(context.TODO(), req.Servicename, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get service: "+err.Error(), http.StatusBadRequest) return } svcYAML, err := yaml.Marshal(svc) if err != nil { http.Error(w, "Failed to marshal service yaml: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(svcYAML)) } func Cluster_configmap(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting clientset", http.StatusInternalServerError) return } cms, err := clientset.CoreV1().ConfigMaps(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { http.Error(w, "Error getting list of ConfigMaps", http.StatusInternalServerError) return } type ConfigMapItem struct { Name string `json:"Name"` Namespace string `json:"Namespace"` DataKeys []string `json:"DataKeys"` Age string `json:"Age"` } var all []ConfigMapItem now := time.Now() for _, cm := range cms.Items { var keys []string for k := range cm.Data { keys = append(keys, k) } age := now.Sub(cm.CreationTimestamp.Time) all = append(all, ConfigMapItem{ Name: cm.Name, Namespace: cm.Namespace, DataKeys: keys, Age: helpers.Human(age), }) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(all) } func ConfigMap_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var cm corev1.ConfigMap if err := syaml.Unmarshal(decoded, &cm); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } if cm.APIVersion == "" { cm.APIVersion = "v1" } if cm.Kind == "" { cm.Kind = "ConfigMap" } if cm.Name == "" { http.Error(w, "Missing configmap name", http.StatusBadRequest) return } if cm.Namespace == "" { cm.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.CoreV1().ConfigMaps(cm.Namespace).Create(context.TODO(), &cm, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create configmap: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func Secret_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var secret corev1.Secret if err := syaml.Unmarshal(decoded, &secret); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } if secret.APIVersion == "" { secret.APIVersion = "v1" } if secret.Kind == "" { secret.Kind = "Secret" } if secret.Name == "" { http.Error(w, "Missing secret name", http.StatusBadRequest) return } if secret.Namespace == "" { secret.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.CoreV1().Secrets(secret.Namespace).Create(context.TODO(), &secret, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create secret: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func ConfigMap_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Configmapname string `json:"Configmapname"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Configmapname == "" { http.Error(w, "Missing required fields: Clustername and Configmapname", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } cm, err := clientset.CoreV1().ConfigMaps(req.Namespace).Get(context.TODO(), req.Configmapname, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get configmap: "+err.Error(), http.StatusBadRequest) return } cmYAML, err := yaml.Marshal(cm) if err != nil { http.Error(w, "Failed to marshal configmap yaml: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(cmYAML)) } func Configmap_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") configmapName := r.URL.Query().Get("configmapName") if clustername == "" || namespace == "" || configmapName == "" { http.Error(w, "Missing required parameters (Name, Namespace, configmapName)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.CoreV1().ConfigMaps(namespace).Delete(context.TODO(), configmapName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting ConfigMap", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(configmapName + " Has been deleted") } func Cluster_secret(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting clientset", http.StatusInternalServerError) return } secs, err := clientset.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { http.Error(w, "Error getting list of Secrets", http.StatusInternalServerError) return } type SecretItem struct { Name string `json:"Name"` Namespace string `json:"Namespace"` Type string `json:"Type"` DataKeys []string `json:"DataKeys"` Age string `json:"Age"` } var all []SecretItem now := time.Now() for _, s := range secs.Items { var keys []string for k := range s.Data { keys = append(keys, k) } age := now.Sub(s.CreationTimestamp.Time) all = append(all, SecretItem{ Name: s.Name, Namespace: s.Namespace, Type: string(s.Type), DataKeys: keys, Age: helpers.Human(age), }) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(all) } func Secret_manifest(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Secretname string `json:"Secretname"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Clustername == "" || req.Secretname == "" { http.Error(w, "Missing required fields: Clustername and Secretname", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } s, err := clientset.CoreV1().Secrets(req.Namespace).Get(context.TODO(), req.Secretname, metav1.GetOptions{}) if err != nil { http.Error(w, "Failed to get secret: "+err.Error(), http.StatusBadRequest) return } y, err := yaml.Marshal(s) if err != nil { http.Error(w, "Failed to marshal secret yaml: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(string(y)) } func Secret_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") secretName := r.URL.Query().Get("secretName") if clustername == "" || namespace == "" || secretName == "" { http.Error(w, "Missing required parameters (Name, Namespace, secretName)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.CoreV1().Secrets(namespace).Delete(context.TODO(), secretName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting Secret", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(secretName + " Has been deleted") } func Job_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var job batchv1.Job if err := syaml.Unmarshal(decoded, &job); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } log.Println("Decoded Job Manifest:", job.Name, job.Namespace) if job.APIVersion == "" { job.APIVersion = "batch/v1" } if job.Kind == "" { job.Kind = "Job" } if job.Name == "" { http.Error(w, "Missing job name", http.StatusBadRequest) return } if len(job.Spec.Template.Spec.Containers) == 0 { http.Error(w, "Job must define at least one container", http.StatusBadRequest) return } if job.Namespace == "" { job.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.BatchV1().Jobs(job.Namespace).Create(context.TODO(), &job, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create job: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func CronJob_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req models.PodLog if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } if req.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } decoded, err := base64.StdEncoding.DecodeString(req.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } var cj batchv1.CronJob if err := syaml.Unmarshal(decoded, &cj); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } log.Println("Decoded CronJob Manifest:", cj.Name, cj.Namespace) if cj.APIVersion == "" { cj.APIVersion = "batch/v1" } if cj.Kind == "" { cj.Kind = "CronJob" } if cj.Name == "" { http.Error(w, "Missing cronjob name", http.StatusBadRequest) return } if cj.Spec.JobTemplate.Spec.Template.Spec.Containers == nil || len(cj.Spec.JobTemplate.Spec.Template.Spec.Containers) == 0 { http.Error(w, "CronJob must define at least one container", http.StatusBadRequest) return } if cj.Namespace == "" { cj.Namespace = "default" } clientset, _, err := getClientset(w, req.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } created, err := clientset.BatchV1().CronJobs(cj.Namespace).Create(context.TODO(), &cj, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create cronjob: "+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func Pod_fromYaml(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var podlog models.PodLog // Decode the incoming JSON request body to get the podlog structure if err := json.NewDecoder(r.Body).Decode(&podlog); err != nil { http.Error(w, "Invalid JSON body: "+err.Error(), http.StatusBadRequest) return } // Ensure Clustername is provided if podlog.Clustername == "" { http.Error(w, "Missing 'Clustername' parameter", http.StatusBadRequest) return } // Decode the base64-encoded manifest decoded, err := base64.StdEncoding.DecodeString(podlog.Manifest) if err != nil { http.Error(w, "Failed to decode base64: "+err.Error(), http.StatusBadRequest) return } // Unmarshal the decoded YAML into the pod struct var pod corev1.Pod // Use sigs.k8s.io/yaml which respects json tags (maps metadata -> ObjectMeta) if err := syaml.Unmarshal(decoded, &pod); err != nil { http.Error(w, "Failed to unmarshal YAML: "+err.Error(), http.StatusBadRequest) return } // Log the decoded pod structure log.Println("Decoded Pod Manifest:", pod) log.Println("Decoded Pod Name:", pod.Name) log.Println("Decoded Pod Namespace:", pod.Namespace) log.Println("Decoded Pod Spec:", pod.Spec) // Set APIVersion and Kind if they are missing if pod.APIVersion == "" { pod.APIVersion = "v1" // Set the default APIVersion } if pod.Kind == "" { pod.Kind = "Pod" // Set the default Kind } // Ensure the pod name is set (in metadata.name) if pod.Name == "" { http.Error(w, "Missing pod name", http.StatusBadRequest) return } // Ensure containerPort is set if len(pod.Spec.Containers) == 0 || len(pod.Spec.Containers[0].Ports) == 0 || pod.Spec.Containers[0].Ports[0].ContainerPort == 0 { http.Error(w, "Missing containerPort in pod manifest", http.StatusBadRequest) return } // Use the provided or default namespace if pod.Namespace == "" { pod.Namespace = "default" // Use the namespace from request or "default" } // Get clientset for the specified cluster clientset, _, err := getClientset(w, podlog.Clustername) if err != nil { http.Error(w, "Error getting clientset: "+err.Error(), http.StatusInternalServerError) return } // Create the pod in the Kubernetes cluster created, err := clientset.CoreV1().Pods(pod.Namespace).Create(context.TODO(), &pod, metav1.CreateOptions{}) if err != nil { http.Error(w, "Failed to create pod: "+err.Error(), http.StatusInternalServerError) return } // Respond with the name and namespace of the created pod w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "name": created.Name, "namespace": created.Namespace, }) } func Pod_exec(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") podName := r.URL.Query().Get("Pod") command := r.URL.Query().Get("Command") //var cmd []string parsed, err := strconv.Unquote(command) if err != nil { http.Error(w, "Invalid command string: "+err.Error(), http.StatusBadRequest) return } // cmd = strings.Fields(parsed) cmd := []string{"sh", "-c", parsed} if clustername == "" || namespace == "" || podName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, config, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } req := clientset.CoreV1().RESTClient(). Post(). Resource("pods"). Name(podName). Namespace(namespace). SubResource("exec"). VersionedParams(&corev1.PodExecOptions{ Command: cmd, Stdout: true, Stderr: true, Stdin: false, TTY: false, }, scheme.ParameterCodec) exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL()) if err != nil { http.Error(w, "Error creating executor: "+err.Error(), http.StatusInternalServerError) return } var stdout, stderr bytes.Buffer err = exec.StreamWithContext(r.Context(), remotecommand.StreamOptions{ Stdout: &stdout, Stderr: &stderr, Tty: false, }) if err != nil { http.Error(w, "Error streaming command output: "+err.Error(), http.StatusInternalServerError) return } output := map[string]interface{}{ "stdout": strings.Split(strings.TrimSpace(stdout.String()), "\n"), "stderr": strings.TrimSpace(stderr.String()), } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(output) } func Pod_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") podName := r.URL.Query().Get("Pod") if clustername == "" || namespace == "" || podName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting pod", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(podName + " Has been deleted") } func Service_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") serviceName := r.URL.Query().Get("serviceName") if clustername == "" || namespace == "" || serviceName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.CoreV1().Services(namespace).Delete(context.TODO(), serviceName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting service", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(serviceName + " Has been deleted") } func Deployment_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") deploymenteName := r.URL.Query().Get("deploymenteName") if clustername == "" || namespace == "" || deploymenteName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.AppsV1().Deployments(namespace).Delete(context.TODO(), deploymenteName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting deploymente", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(deploymenteName + " Has been deleted") } func StatefulSet_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") statefulSetName := r.URL.Query().Get("statefulSetName") if clustername == "" || namespace == "" || statefulSetName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.AppsV1().StatefulSets(namespace).Delete(context.TODO(), statefulSetName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting statefulSet", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(statefulSetName + " Has been deleted") } func Daemonsets_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") daemonsetsName := r.URL.Query().Get("daemonsetsName") if clustername == "" || namespace == "" || daemonsetsName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.AppsV1().DaemonSets(namespace).Delete(context.TODO(), daemonsetsName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting daemonsets"+err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(daemonsetsName + " Has been deleted") } func JobsName_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") jobsName := r.URL.Query().Get("jobsName") if clustername == "" || namespace == "" || jobsName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.BatchV1().Jobs(namespace).Delete(context.TODO(), jobsName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting Jobs", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(jobsName + " Has been deleted") } func Replicaset_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") replicasetName := r.URL.Query().Get("replicasetName") if clustername == "" || namespace == "" || replicasetName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.AppsV1().ReplicaSets(namespace).Delete(context.TODO(), replicasetName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting ReplicaSets", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(replicasetName + " Has been deleted") } func Replicationcontroller_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") replicationcontrollerName := r.URL.Query().Get("replicationcontrollerName") if clustername == "" || namespace == "" || replicationcontrollerName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.CoreV1().ReplicationControllers(namespace).Delete(context.TODO(), replicationcontrollerName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting ReplicationControllers", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(replicationcontrollerName + " Has been deleted") } func Cronjob_delete(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") cronjobName := r.URL.Query().Get("cronjobName") if clustername == "" || namespace == "" || cronjobName == "" { http.Error(w, "Missing required parameters (Name, Namespace, Pod)", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Error getting Kubernetes clientset", http.StatusInternalServerError) return } err = clientset.BatchV1().CronJobs(namespace).Delete(context.TODO(), cronjobName, metav1.DeleteOptions{}) if err != nil { http.Error(w, "Error deleting CronJobs", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(cronjobName + " Has been deleted") } func Worker_nodes_plan(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var workerNodesPlan []models.WorkerNodesPlans cursor, err := db.Worker_nodes_plan.Find(context.TODO(), bson.M{}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer cursor.Close(context.TODO()) for cursor.Next(context.TODO()) { var plan models.WorkerNodesPlans if err := cursor.Decode(&plan); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } workerNodesPlan = append(workerNodesPlan, plan) } if err := cursor.Err(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(workerNodesPlan) } func ClusterStats(w http.ResponseWriter, r *http.Request) { data := models.ClusterStats{ Name: "vcluster-cluster", ClusterId: "6547", Status: "Healthy", Version: "v3.31.0", Alerts: "0/1", Endpoint: "http://aa.bugx.ir", ResourceUsage: models.ResourceUsage{ CPU: models.Usage{Used: 65, Total: 100, Unit: "cores"}, Memory: models.Usage{Used: 8.2, Total: 16, Unit: "GB"}, Storage: models.Usage{Used: 45, Total: 100, Unit: "GB"}, Network: models.Usage{Used: 2.1, Total: 10, Unit: "Gbps"}, }, Performance: models.Performance{ PodStartupTime: "2.3s", APILatency: "45ms", EtcdLatency: "12ms", SchedulerLatency: "8ms", }, Health: models.Health{ Status: "Healthy", NodesHealthy: 3, NodesTotal: 3, PodsRunning: 10, PodsTotal: 12, Alerts: 2, Warnings: 1, }, Uptime: models.Uptime{ ClusterUptime: "15d 8h 32m", LastMaintenance: "3d ago", NextMaintenance: "11d from now", }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(data) } // Cluster_resource_usage returns basic aggregated resource usage across nodes (CPU cores, memory, ephemeral storage). // It sums capacity and allocatable from Node status. Network is a placeholder (0 total) unless metrics are integrated. func Cluster_resource_usage(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err != nil { http.Error(w, "Failed to list nodes: "+err.Error(), http.StatusInternalServerError) return } var totalCPU, allocCPU resourceapi.Quantity var totalMem, allocMem resourceapi.Quantity var totalStorage, allocStorage resourceapi.Quantity for _, n := range nodes.Items { cap := n.Status.Capacity alloc := n.Status.Allocatable totalCPU.Add(cap[corev1.ResourceCPU]) allocCPU.Add(alloc[corev1.ResourceCPU]) totalMem.Add(cap[corev1.ResourceMemory]) allocMem.Add(alloc[corev1.ResourceMemory]) // Ephemeral storage if q, ok := cap[corev1.ResourceEphemeralStorage]; ok { totalStorage.Add(q) } if q, ok := alloc[corev1.ResourceEphemeralStorage]; ok { allocStorage.Add(q) } } // Convert to human-friendly units cpuTotalCores := float64(totalCPU.MilliValue()) / 1000.0 cpuAllocCores := float64(allocCPU.MilliValue()) / 1000.0 memTotalGi := float64(totalMem.Value()) / (1024.0 * 1024.0 * 1024.0) memAllocGi := float64(allocMem.Value()) / (1024.0 * 1024.0 * 1024.0) storTotalGi := float64(totalStorage.Value()) / (1024.0 * 1024.0 * 1024.0) storAllocGi := float64(allocStorage.Value()) / (1024.0 * 1024.0 * 1024.0) usage := models.ResourceUsage{ CPU: models.Usage{ Used: cpuAllocCores, Total: cpuTotalCores, Unit: "cores", }, Memory: models.Usage{ Used: memAllocGi, Total: memTotalGi, Unit: "GiB", }, Storage: models.Usage{ Used: storAllocGi, Total: storTotalGi, Unit: "GiB", }, Network: models.Usage{ Used: 0, Total: 0, Unit: "Gbps", }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(usage) } func Cluster_health(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } // Nodes health nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err != nil { http.Error(w, "Failed to list nodes: "+err.Error(), http.StatusInternalServerError) return } totalNodes := len(nodes.Items) healthyNodes := 0 for _, n := range nodes.Items { for _, c := range n.Status.Conditions { if c.Type == corev1.NodeReady && c.Status == corev1.ConditionTrue { healthyNodes++ break } } } // Pods running pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{}) if err != nil { http.Error(w, "Failed to list pods: "+err.Error(), http.StatusInternalServerError) return } totalPods := len(pods.Items) runningPods := 0 failedOrPending := 0 for _, p := range pods.Items { switch p.Status.Phase { case corev1.PodRunning: runningPods++ case corev1.PodFailed, corev1.PodPending, corev1.PodUnknown: failedOrPending++ } } // Warnings from events (cluster-wide) events, _ := clientset.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{}) warningCount := 0 if events != nil { for _, e := range events.Items { if strings.EqualFold(e.Type, "Warning") { warningCount++ } } } alerts := failedOrPending // treat non-running pods as alerts proxy status := "Healthy" if healthyNodes < totalNodes || alerts > 0 || warningCount > 0 { status = "Degraded" } resp := models.Health{ Status: status, NodesHealthy: healthyNodes, NodesTotal: totalNodes, PodsRunning: runningPods, PodsTotal: totalPods, Alerts: alerts, Warnings: warningCount, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } func Cluster_uptime(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) if err != nil || len(nodes.Items) == 0 { http.Error(w, "Failed to list nodes", http.StatusInternalServerError) return } oldest := time.Now() for _, n := range nodes.Items { t := n.CreationTimestamp.Time if t.Before(oldest) { oldest = t } } d := time.Since(oldest) // Format as 15d 8h 32m days := int(d.Hours()) / 24 hours := int(d.Hours()) % 24 minutes := int(d.Minutes()) % 60 uptimeStr := fmt.Sprintf("%dd %dh %dm", days, hours, minutes) resp := models.Uptime{ ClusterUptime: uptimeStr, LastMaintenance: "-", NextMaintenance: "-", } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) } func Cluster_performance(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } // API latency: measure a small list call apiStart := time.Now() _, _ = clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{Limit: 1}) apiLatency := time.Since(apiStart) // Gather recent pods (cap to avoid heavy calls) pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{}) if err != nil { http.Error(w, "Failed to list pods: "+err.Error(), http.StatusInternalServerError) return } // Limit sample size maxSample := 300 if len(pods.Items) < maxSample { maxSample = len(pods.Items) } var totalStartup time.Duration var countStartup int var totalSched time.Duration var countSched int now := time.Now() for i := 0; i < maxSample; i++ { p := pods.Items[i] // Skip very old pods to focus on recent behavior (e.g., last 14 days) if now.Sub(p.CreationTimestamp.Time) > 14*24*time.Hour { continue } // Scheduler latency: Creation -> PodScheduled=True var schedTime *time.Time for _, c := range p.Status.Conditions { if c.Type == corev1.PodScheduled && c.Status == corev1.ConditionTrue { t := c.LastTransitionTime.Time schedTime = &t break } } if schedTime != nil { totalSched += schedTime.Sub(p.CreationTimestamp.Time) countSched++ } // Pod startup time: Creation -> first container StartedAt // If multiple containers, take the latest StartedAt (worst case) var startedAt *time.Time for _, cs := range p.Status.ContainerStatuses { if cs.State.Running != nil && !cs.State.Running.StartedAt.IsZero() { t := cs.State.Running.StartedAt.Time if startedAt == nil || t.After(*startedAt) { startedAt = &t } } } if startedAt != nil { totalStartup += startedAt.Sub(p.CreationTimestamp.Time) countStartup++ } } // Format helpers formatDur := func(d time.Duration) string { if d < time.Second { return fmt.Sprintf("%dms", d.Milliseconds()) } // show with one decimal up to minutes if d < time.Minute { return fmt.Sprintf("%.1fs", d.Seconds()) } // mm:ss for longer return fmt.Sprintf("%dm %ds", int(d.Minutes()), int(d.Seconds())%60) } var avgStartup, avgSched time.Duration if countStartup > 0 { avgStartup = time.Duration(int64(totalStartup) / int64(countStartup)) } if countSched > 0 { avgSched = time.Duration(int64(totalSched) / int64(countSched)) } perf := models.Performance{ PodStartupTime: formatDur(avgStartup), APILatency: formatDur(apiLatency), EtcdLatency: "-", // etcd latency requires apiserver/etcd metrics; integrate Prometheus to populate SchedulerLatency: formatDur(avgSched), } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(perf) } func Cluster_events(w http.ResponseWriter, r *http.Request) { Authorization(w, r) clustername := r.URL.Query().Get("Name") namespace := r.URL.Query().Get("Namespace") if clustername == "" { http.Error(w, "Missing 'Name' parameter", http.StatusBadRequest) return } clientset, _, err := getClientset(w, clustername) if err != nil { http.Error(w, "Failed to create kubernetes client: "+err.Error(), http.StatusInternalServerError) return } evList, err := clientset.CoreV1().Events(namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { http.Error(w, "Failed to list events: "+err.Error(), http.StatusInternalServerError) return } type EventItem struct { Type string `json:"type"` Reason string `json:"reason"` Message string `json:"message"` Count int32 `json:"count"` ObjectKind string `json:"objectKind"` ObjectName string `json:"objectName"` Namespace string `json:"namespace"` FirstSeen string `json:"firstSeen,omitempty"` LastSeen string `json:"lastSeen,omitempty"` Age string `json:"age"` } var items []EventItem now := time.Now() for _, e := range evList.Items { // Timestamps (handle both v1 Event fields) first := e.FirstTimestamp.Time last := e.LastTimestamp.Time if last.IsZero() && !e.EventTime.IsZero() { last = e.EventTime.Time } if first.IsZero() { first = last } age := "-" if !last.IsZero() { age = helpers.Human(now.Sub(last)) } items = append(items, EventItem{ Type: e.Type, Reason: e.Reason, Message: e.Message, Count: e.Count, ObjectKind: e.InvolvedObject.Kind, ObjectName: e.InvolvedObject.Name, Namespace: e.InvolvedObject.Namespace, FirstSeen: first.UTC().Format(time.RFC3339), LastSeen: last.UTC().Format(time.RFC3339), Age: age, }) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(items) } func Helm_install(w http.ResponseWriter, r *http.Request) { Authorization(w, r) var req struct { Chart string `json:"Chart"` Clustername string `json:"Clustername"` Namespace string `json:"Namespace"` Release string `json:"Release"` Repo string `json:"Repo"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON body", http.StatusBadRequest) return } if req.Chart == "" || req.Clustername == "" || req.Release == "" || req.Repo == "" { http.Error(w, "Missing required fields: Chart, Clustername, Release, Repo", http.StatusBadRequest) return } if req.Namespace == "" { req.Namespace = "default" } // Get kubeconfig for the cluster kubeconfig, err := getClusterConfig(req.Clustername) if err != nil { http.Error(w, "Failed to get cluster config: "+err.Error(), http.StatusInternalServerError) return } // Write kubeconfig to temp file tmpFile, err := os.CreateTemp("", "kubeconfig-*.yaml") if err != nil { http.Error(w, "Failed to create temp file: "+err.Error(), http.StatusInternalServerError) return } defer os.Remove(tmpFile.Name()) if _, err := tmpFile.WriteString(kubeconfig); err != nil { http.Error(w, "Failed to write kubeconfig: "+err.Error(), http.StatusInternalServerError) return } tmpFile.Close() // Add repo if not exists cmd := exec.Command("helm", "repo", "add", "temp-repo", req.Repo) cmd.Env = append(os.Environ(), "KUBECONFIG="+tmpFile.Name()) output, err := cmd.CombinedOutput() if err != nil && !strings.Contains(string(output), "already exists") { http.Error(w, "Failed to add helm repo: "+string(output), http.StatusInternalServerError) return } // Update repo cmd = exec.Command("helm", "repo", "update") cmd.Env = append(os.Environ(), "KUBECONFIG="+tmpFile.Name()) output, err = cmd.CombinedOutput() if err != nil { http.Error(w, "Failed to update helm repo: "+string(output), http.StatusInternalServerError) return } // Install chart cmd = exec.Command("helm", "install", req.Release, req.Chart, "--namespace", req.Namespace, "--create-namespace") cmd.Env = append(os.Environ(), "KUBECONFIG="+tmpFile.Name()) output, err = cmd.CombinedOutput() if err != nil { http.Error(w, "Failed to install helm chart: "+string(output), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "message": "Helm chart installed successfully", "release": req.Release, "namespace": req.Namespace, "chart": req.Chart, "output": string(output), }) }