277 lines
8.0 KiB
Go
277 lines
8.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/RyanCopley/skybridge/faas/internal/domain"
|
|
"github.com/RyanCopley/skybridge/faas/internal/services"
|
|
)
|
|
|
|
type ExecutionHandler struct {
|
|
executionService services.ExecutionService
|
|
authService services.AuthService
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewExecutionHandler(executionService services.ExecutionService, authService services.AuthService, logger *zap.Logger) *ExecutionHandler {
|
|
return &ExecutionHandler{
|
|
executionService: executionService,
|
|
authService: authService,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (h *ExecutionHandler) Execute(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
functionID, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid function ID"})
|
|
return
|
|
}
|
|
|
|
var req domain.ExecuteFunctionRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.logger.Warn("Invalid execute function request", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
|
|
return
|
|
}
|
|
req.FunctionID = functionID
|
|
|
|
authCtx, err := h.authService.GetAuthContext(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to get auth context", zap.Error(err))
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
|
|
return
|
|
}
|
|
|
|
if !h.authService.HasPermission(c.Request.Context(), "faas.execute") {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
return
|
|
}
|
|
|
|
response, err := h.executionService.Execute(c.Request.Context(), &req, authCtx.UserID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to execute function", zap.String("function_id", idStr), zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
h.logger.Info("Function execution initiated",
|
|
zap.String("function_id", functionID.String()),
|
|
zap.String("execution_id", response.ExecutionID.String()),
|
|
zap.String("user_id", authCtx.UserID),
|
|
zap.Bool("async", req.Async))
|
|
|
|
c.JSON(http.StatusOK, response)
|
|
}
|
|
|
|
func (h *ExecutionHandler) Invoke(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
functionID, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid function ID"})
|
|
return
|
|
}
|
|
|
|
var req domain.ExecuteFunctionRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
// Allow empty body
|
|
req = domain.ExecuteFunctionRequest{
|
|
FunctionID: functionID,
|
|
Async: true,
|
|
}
|
|
}
|
|
req.FunctionID = functionID
|
|
req.Async = true // Force async for invoke endpoint
|
|
|
|
authCtx, err := h.authService.GetAuthContext(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to get auth context", zap.Error(err))
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
|
|
return
|
|
}
|
|
|
|
if !h.authService.HasPermission(c.Request.Context(), "faas.execute") {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
return
|
|
}
|
|
|
|
response, err := h.executionService.Execute(c.Request.Context(), &req, authCtx.UserID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to invoke function", zap.String("function_id", idStr), zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
h.logger.Info("Function invoked successfully",
|
|
zap.String("function_id", functionID.String()),
|
|
zap.String("execution_id", response.ExecutionID.String()),
|
|
zap.String("user_id", authCtx.UserID))
|
|
|
|
c.JSON(http.StatusAccepted, response)
|
|
}
|
|
|
|
func (h *ExecutionHandler) GetByID(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid execution ID"})
|
|
return
|
|
}
|
|
|
|
if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
return
|
|
}
|
|
|
|
execution, err := h.executionService.GetByID(c.Request.Context(), id)
|
|
if err != nil {
|
|
h.logger.Error("Failed to get execution", zap.String("id", idStr), zap.Error(err))
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Execution not found"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, execution)
|
|
}
|
|
|
|
func (h *ExecutionHandler) List(c *gin.Context) {
|
|
if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
return
|
|
}
|
|
|
|
var functionID *uuid.UUID
|
|
functionIDStr := c.Query("function_id")
|
|
if functionIDStr != "" {
|
|
id, err := uuid.Parse(functionIDStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid function ID"})
|
|
return
|
|
}
|
|
functionID = &id
|
|
}
|
|
|
|
limitStr := c.DefaultQuery("limit", "50")
|
|
offsetStr := c.DefaultQuery("offset", "0")
|
|
|
|
limit, err := strconv.Atoi(limitStr)
|
|
if err != nil || limit <= 0 {
|
|
limit = 50
|
|
}
|
|
|
|
offset, err := strconv.Atoi(offsetStr)
|
|
if err != nil || offset < 0 {
|
|
offset = 0
|
|
}
|
|
|
|
executions, err := h.executionService.List(c.Request.Context(), functionID, limit, offset)
|
|
if err != nil {
|
|
h.logger.Error("Failed to list executions", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"executions": executions,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
})
|
|
}
|
|
|
|
func (h *ExecutionHandler) Cancel(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid execution ID"})
|
|
return
|
|
}
|
|
|
|
authCtx, err := h.authService.GetAuthContext(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to get auth context", zap.Error(err))
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
|
|
return
|
|
}
|
|
|
|
if !h.authService.HasPermission(c.Request.Context(), "faas.execute") {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
return
|
|
}
|
|
|
|
if err := h.executionService.Cancel(c.Request.Context(), id, authCtx.UserID); err != nil {
|
|
h.logger.Error("Failed to cancel execution", zap.String("id", idStr), zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
h.logger.Info("Execution canceled successfully",
|
|
zap.String("execution_id", id.String()),
|
|
zap.String("user_id", authCtx.UserID))
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "Execution canceled successfully"})
|
|
}
|
|
|
|
func (h *ExecutionHandler) GetLogs(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
h.logger.Debug("GetLogs endpoint called",
|
|
zap.String("execution_id", idStr),
|
|
zap.String("client_ip", c.ClientIP()))
|
|
|
|
id, err := uuid.Parse(idStr)
|
|
if err != nil {
|
|
h.logger.Warn("Invalid execution ID provided to GetLogs",
|
|
zap.String("id", idStr),
|
|
zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid execution ID"})
|
|
return
|
|
}
|
|
|
|
if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
|
|
h.logger.Warn("Insufficient permissions for GetLogs",
|
|
zap.String("execution_id", idStr))
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
return
|
|
}
|
|
|
|
h.logger.Debug("Calling execution service GetLogs",
|
|
zap.String("execution_id", idStr))
|
|
|
|
logs, err := h.executionService.GetLogs(c.Request.Context(), id)
|
|
if err != nil {
|
|
h.logger.Error("Failed to get execution logs", zap.String("id", idStr), zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
h.logger.Debug("Successfully retrieved logs from execution service",
|
|
zap.String("execution_id", idStr),
|
|
zap.Int("log_count", len(logs)))
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"logs": logs,
|
|
})
|
|
}
|
|
|
|
func (h *ExecutionHandler) GetRunning(c *gin.Context) {
|
|
if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
|
|
return
|
|
}
|
|
|
|
executions, err := h.executionService.GetRunningExecutions(c.Request.Context())
|
|
if err != nil {
|
|
h.logger.Error("Failed to get running executions", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"executions": executions,
|
|
"count": len(executions),
|
|
})
|
|
} |