mirror of
https://github.com/GlueOps/autoglue.git
synced 2026-02-13 21:00:06 +01:00
187 lines
5.3 KiB
Go
187 lines
5.3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/glueops/autoglue/internal/api/httpmiddleware"
|
|
"github.com/glueops/autoglue/internal/common"
|
|
"github.com/glueops/autoglue/internal/handlers/dto"
|
|
"github.com/glueops/autoglue/internal/models"
|
|
"github.com/glueops/autoglue/internal/utils"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// ListClusters godoc
|
|
//
|
|
// @ID ListClusters
|
|
// @Summary List clusters (org scoped)
|
|
// @Description Returns clusters for the organization in X-Org-ID. Filter by `q` (name contains).
|
|
// @Tags Clusters
|
|
// @Produce json
|
|
// @Param X-Org-ID header string false "Organization UUID"
|
|
// @Param q query string false "Name contains (case-insensitive)"
|
|
// @Success 200 {array} dto.ClusterResponse
|
|
// @Failure 401 {string} string "Unauthorized"
|
|
// @Failure 403 {string} string "organization required"
|
|
// @Failure 500 {string} string "failed to list clusters"
|
|
// @Router /clusters [get]
|
|
// @Security BearerAuth
|
|
// @Security OrgKeyAuth
|
|
// @Security OrgSecretAuth
|
|
func ListClusters(db *gorm.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
orgID, ok := httpmiddleware.OrgIDFrom(r.Context())
|
|
if !ok {
|
|
utils.WriteError(w, http.StatusForbidden, "org_required", "specify X-Org-ID")
|
|
return
|
|
}
|
|
|
|
q := db.Where("organization_id = ?", orgID)
|
|
if needle := strings.TrimSpace(r.URL.Query().Get("q")); needle != "" {
|
|
q = q.Where(`name ILIKE ?`, "%"+needle+"%")
|
|
}
|
|
|
|
var rows []models.Cluster
|
|
if err := q.
|
|
Preload("NodePools").
|
|
Preload("NodePools.Labels").
|
|
Preload("NodePools.Annotations").
|
|
Preload("NodePools.Labels").
|
|
Preload("NodePools.Taints").
|
|
Preload("NodePools.Servers").
|
|
Preload("BastionServer").
|
|
Find(&rows).Error; err != nil {
|
|
utils.WriteError(w, http.StatusInternalServerError, "db_error", "db error")
|
|
return
|
|
}
|
|
|
|
out := make([]dto.ClusterResponse, 0, len(rows))
|
|
for _, row := range rows {
|
|
out = append(out, clusterToDTO(row))
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// CreateCluster godoc
|
|
//
|
|
// @ID CreateCluster
|
|
// @Summary Create cluster (org scoped)
|
|
// @Description Creates a cluster. If `kubeconfig` is provided, it will be encrypted per-organization and stored securely (never returned).
|
|
// @Tags Clusters
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param X-Org-ID header string false "Organization UUID"
|
|
// @Param body body dto.CreateClusterRequest true "payload"
|
|
// @Success 201 {object} dto.ClusterResponse
|
|
// @Failure 400 {string} string "invalid json"
|
|
// @Failure 401 {string} string "Unauthorized"
|
|
// @Failure 403 {string} string "organization required"
|
|
// @Failure 500 {string} string "create failed"
|
|
// @Router /clusters [post]
|
|
// @Security BearerAuth
|
|
// @Security OrgKeyAuth
|
|
// @Security OrgSecretAuth
|
|
func CreateCluster(db *gorm.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
}
|
|
|
|
// -- Helpers
|
|
|
|
func clusterToDTO(c models.Cluster) dto.ClusterResponse {
|
|
var bastion *dto.ServerResponse
|
|
if c.BastionServer != nil {
|
|
b := serverToDTO(*c.BastionServer)
|
|
bastion = &b
|
|
}
|
|
|
|
nps := make([]dto.NodePoolResponse, 0, len(c.NodePools))
|
|
for _, np := range c.NodePools {
|
|
nps = append(nps, nodePoolToDTO(np))
|
|
}
|
|
|
|
return dto.ClusterResponse{
|
|
ID: c.ID,
|
|
Name: c.Name,
|
|
Provider: c.Provider,
|
|
Region: c.Region,
|
|
Status: c.Status,
|
|
CaptainDomain: c.CaptainDomain,
|
|
ClusterLoadBalancer: c.ClusterLoadBalancer,
|
|
RandomToken: c.RandomToken,
|
|
CertificateKey: c.CertificateKey,
|
|
ControlLoadBalancer: c.ControlLoadBalancer,
|
|
NodePools: nps,
|
|
BastionServer: bastion,
|
|
CreatedAt: c.CreatedAt,
|
|
UpdatedAt: c.UpdatedAt,
|
|
}
|
|
}
|
|
|
|
func nodePoolToDTO(np models.NodePool) dto.NodePoolResponse {
|
|
labels := make([]dto.LabelResponse, 0, len(np.Labels))
|
|
for _, l := range np.Labels {
|
|
labels = append(labels, dto.LabelResponse{
|
|
Key: l.Key,
|
|
Value: l.Value,
|
|
})
|
|
}
|
|
|
|
annotations := make([]dto.AnnotationResponse, 0, len(np.Annotations))
|
|
for _, a := range np.Annotations {
|
|
annotations = append(annotations, dto.AnnotationResponse{
|
|
Key: a.Key,
|
|
Value: a.Value,
|
|
})
|
|
}
|
|
|
|
taints := make([]dto.TaintResponse, 0, len(np.Taints))
|
|
for _, t := range np.Taints {
|
|
taints = append(taints, dto.TaintResponse{
|
|
Key: t.Key,
|
|
Value: t.Value,
|
|
Effect: t.Effect,
|
|
})
|
|
}
|
|
|
|
servers := make([]dto.ServerResponse, 0, len(np.Servers))
|
|
for _, s := range np.Servers {
|
|
servers = append(servers, serverToDTO(s))
|
|
}
|
|
|
|
return dto.NodePoolResponse{
|
|
AuditFields: common.AuditFields{
|
|
ID: np.ID,
|
|
OrganizationID: np.OrganizationID,
|
|
CreatedAt: np.CreatedAt,
|
|
UpdatedAt: np.UpdatedAt,
|
|
},
|
|
Name: np.Name,
|
|
Role: dto.NodeRole(np.Role),
|
|
Labels: labels,
|
|
Annotations: annotations,
|
|
Taints: taints,
|
|
Servers: servers,
|
|
}
|
|
}
|
|
|
|
func serverToDTO(s models.Server) dto.ServerResponse {
|
|
return dto.ServerResponse{
|
|
ID: s.ID,
|
|
Hostname: s.Hostname,
|
|
PrivateIPAddress: s.PrivateIPAddress,
|
|
PublicIPAddress: s.PublicIPAddress,
|
|
Role: s.Role,
|
|
Status: s.Status,
|
|
SSHUser: s.SSHUser,
|
|
SshKeyID: s.SshKeyID,
|
|
CreatedAt: s.CreatedAt.UTC().Format(time.RFC3339),
|
|
UpdatedAt: s.UpdatedAt.UTC().Format(time.RFC3339),
|
|
}
|
|
}
|