Files
vclusterapi/handler/handler.go
2025-09-27 17:01:54 +03:30

3635 lines
104 KiB
Go

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),
})
}