-
This commit is contained in:
253
faas/internal/services/function_service.go
Normal file
253
faas/internal/services/function_service.go
Normal file
@ -0,0 +1,253 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/RyanCopley/skybridge/faas/internal/domain"
|
||||
"github.com/RyanCopley/skybridge/faas/internal/repository"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type functionService struct {
|
||||
functionRepo repository.FunctionRepository
|
||||
runtimeService RuntimeService
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewFunctionService(functionRepo repository.FunctionRepository, runtimeService RuntimeService, logger *zap.Logger) FunctionService {
|
||||
return &functionService{
|
||||
functionRepo: functionRepo,
|
||||
runtimeService: runtimeService,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *functionService) Create(ctx context.Context, req *domain.CreateFunctionRequest, userID string) (*domain.FunctionDefinition, error) {
|
||||
s.logger.Info("Creating new function",
|
||||
zap.String("name", req.Name),
|
||||
zap.String("app_id", req.AppID),
|
||||
zap.String("user_id", userID))
|
||||
|
||||
// Check if function with same name exists
|
||||
_, err := s.functionRepo.GetByName(ctx, req.AppID, req.Name)
|
||||
if err == nil {
|
||||
return nil, fmt.Errorf("function with name '%s' already exists in app '%s'", req.Name, req.AppID)
|
||||
}
|
||||
|
||||
// Validate runtime
|
||||
if !s.isValidRuntime(string(req.Runtime)) {
|
||||
return nil, fmt.Errorf("unsupported runtime: %s", req.Runtime)
|
||||
}
|
||||
|
||||
// Create function definition
|
||||
function := &domain.FunctionDefinition{
|
||||
ID: uuid.New(),
|
||||
Name: req.Name,
|
||||
AppID: req.AppID,
|
||||
Runtime: req.Runtime,
|
||||
Image: req.Image,
|
||||
Handler: req.Handler,
|
||||
Code: req.Code,
|
||||
Environment: req.Environment,
|
||||
Timeout: req.Timeout,
|
||||
Memory: req.Memory,
|
||||
Owner: req.Owner,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Validate timeout and memory limits
|
||||
if function.Timeout.Duration < time.Second {
|
||||
return nil, fmt.Errorf("timeout must be at least 1 second")
|
||||
}
|
||||
if function.Timeout.Duration > 15*time.Minute {
|
||||
return nil, fmt.Errorf("timeout cannot exceed 15 minutes")
|
||||
}
|
||||
if function.Memory < 64 || function.Memory > 3008 {
|
||||
return nil, fmt.Errorf("memory must be between 64 and 3008 MB")
|
||||
}
|
||||
|
||||
// Store function
|
||||
created, err := s.functionRepo.Create(ctx, function)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to create function",
|
||||
zap.String("name", req.Name),
|
||||
zap.Error(err))
|
||||
return nil, fmt.Errorf("failed to create function: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("Function created successfully",
|
||||
zap.String("function_id", created.ID.String()),
|
||||
zap.String("name", created.Name))
|
||||
|
||||
return created, nil
|
||||
}
|
||||
|
||||
func (s *functionService) GetByID(ctx context.Context, id uuid.UUID) (*domain.FunctionDefinition, error) {
|
||||
function, err := s.functionRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("function not found: %w", err)
|
||||
}
|
||||
return function, nil
|
||||
}
|
||||
|
||||
func (s *functionService) GetByName(ctx context.Context, appID, name string) (*domain.FunctionDefinition, error) {
|
||||
function, err := s.functionRepo.GetByName(ctx, appID, name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("function not found: %w", err)
|
||||
}
|
||||
return function, nil
|
||||
}
|
||||
|
||||
func (s *functionService) Update(ctx context.Context, id uuid.UUID, req *domain.UpdateFunctionRequest, userID string) (*domain.FunctionDefinition, error) {
|
||||
s.logger.Info("Updating function",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.String("user_id", userID))
|
||||
|
||||
// Get existing function
|
||||
_, err := s.functionRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("function not found: %w", err)
|
||||
}
|
||||
|
||||
// Validate runtime if being updated
|
||||
if req.Runtime != nil && !s.isValidRuntime(string(*req.Runtime)) {
|
||||
return nil, fmt.Errorf("unsupported runtime: %s", *req.Runtime)
|
||||
}
|
||||
|
||||
// Validate timeout and memory if being updated
|
||||
if req.Timeout != nil {
|
||||
if req.Timeout.Duration < time.Second {
|
||||
return nil, fmt.Errorf("timeout must be at least 1 second")
|
||||
}
|
||||
if req.Timeout.Duration > 15*time.Minute {
|
||||
return nil, fmt.Errorf("timeout cannot exceed 15 minutes")
|
||||
}
|
||||
}
|
||||
if req.Memory != nil && (*req.Memory < 64 || *req.Memory > 3008) {
|
||||
return nil, fmt.Errorf("memory must be between 64 and 3008 MB")
|
||||
}
|
||||
|
||||
// Update function
|
||||
updated, err := s.functionRepo.Update(ctx, id, req)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to update function",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.Error(err))
|
||||
return nil, fmt.Errorf("failed to update function: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("Function updated successfully",
|
||||
zap.String("function_id", id.String()))
|
||||
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func (s *functionService) Delete(ctx context.Context, id uuid.UUID, userID string) error {
|
||||
s.logger.Info("Deleting function",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.String("user_id", userID))
|
||||
|
||||
// Get function to determine runtime
|
||||
function, err := s.functionRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("function not found: %w", err)
|
||||
}
|
||||
|
||||
// Clean up runtime resources
|
||||
backend, err := s.runtimeService.GetBackend(ctx, string(function.Runtime))
|
||||
if err != nil {
|
||||
s.logger.Warn("Failed to get runtime backend for cleanup", zap.Error(err))
|
||||
} else {
|
||||
if err := backend.Remove(ctx, id); err != nil {
|
||||
s.logger.Warn("Failed to remove runtime resources",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// Delete function
|
||||
if err := s.functionRepo.Delete(ctx, id); err != nil {
|
||||
s.logger.Error("Failed to delete function",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.Error(err))
|
||||
return fmt.Errorf("failed to delete function: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("Function deleted successfully",
|
||||
zap.String("function_id", id.String()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *functionService) List(ctx context.Context, appID string, limit, offset int) ([]*domain.FunctionDefinition, error) {
|
||||
if limit <= 0 {
|
||||
limit = 50 // Default limit
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100 // Max limit
|
||||
}
|
||||
|
||||
return s.functionRepo.List(ctx, appID, limit, offset)
|
||||
}
|
||||
|
||||
func (s *functionService) GetByAppID(ctx context.Context, appID string) ([]*domain.FunctionDefinition, error) {
|
||||
return s.functionRepo.GetByAppID(ctx, appID)
|
||||
}
|
||||
|
||||
func (s *functionService) Deploy(ctx context.Context, id uuid.UUID, req *domain.DeployFunctionRequest, userID string) (*domain.DeployFunctionResponse, error) {
|
||||
s.logger.Info("Deploying function",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.String("user_id", userID),
|
||||
zap.Bool("force", req.Force))
|
||||
|
||||
// Get function
|
||||
function, err := s.functionRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("function not found: %w", err)
|
||||
}
|
||||
|
||||
// Get runtime backend
|
||||
backend, err := s.runtimeService.GetBackend(ctx, string(function.Runtime))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get runtime backend: %w", err)
|
||||
}
|
||||
|
||||
// Deploy function
|
||||
if err := backend.Deploy(ctx, function); err != nil {
|
||||
s.logger.Error("Failed to deploy function",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.Error(err))
|
||||
return nil, fmt.Errorf("failed to deploy function: %w", err)
|
||||
}
|
||||
|
||||
s.logger.Info("Function deployed successfully",
|
||||
zap.String("function_id", id.String()),
|
||||
zap.String("image", function.Image))
|
||||
|
||||
return &domain.DeployFunctionResponse{
|
||||
Status: "deployed",
|
||||
Message: "Function deployed successfully",
|
||||
Image: function.Image,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *functionService) isValidRuntime(runtimeType string) bool {
|
||||
validRuntimes := []string{
|
||||
string(domain.RuntimeNodeJS18),
|
||||
string(domain.RuntimePython39),
|
||||
string(domain.RuntimeGo120),
|
||||
string(domain.RuntimeCustom),
|
||||
}
|
||||
|
||||
for _, valid := range validRuntimes {
|
||||
if runtimeType == valid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user