Files
skybridge/faas/internal/services/runtime_service.go
2025-08-30 23:52:37 -04:00

199 lines
5.1 KiB
Go

package services
import (
"context"
"fmt"
"sync"
"go.uber.org/zap"
"github.com/RyanCopley/skybridge/faas/internal/domain"
"github.com/RyanCopley/skybridge/faas/internal/runtime"
"github.com/RyanCopley/skybridge/faas/internal/runtime/docker"
)
type runtimeService struct {
backends map[string]runtime.RuntimeBackend
mutex sync.RWMutex
logger *zap.Logger
config *RuntimeConfig
}
type RuntimeConfig struct {
DefaultRuntime string `json:"default_runtime"`
Backends map[string]map[string]interface{} `json:"backends"`
}
func NewRuntimeService(logger *zap.Logger, config *RuntimeConfig) RuntimeService {
if config == nil {
config = &RuntimeConfig{
DefaultRuntime: "docker",
Backends: make(map[string]map[string]interface{}),
}
}
service := &runtimeService{
backends: make(map[string]runtime.RuntimeBackend),
logger: logger,
config: config,
}
// Initialize default Docker backend
if err := service.initializeDockerBackend(); err != nil {
logger.Warn("Failed to initialize Docker backend", zap.Error(err))
}
return service
}
func (s *runtimeService) initializeDockerBackend() error {
// Use simple Docker backend for now
dockerBackend, err := docker.NewSimpleDockerRuntime(s.logger)
if err != nil {
s.logger.Error("Failed to create Docker runtime", zap.Error(err))
return err
}
s.mutex.Lock()
s.backends["docker"] = dockerBackend
s.mutex.Unlock()
s.logger.Info("Simple Docker runtime backend initialized")
return nil
}
func (s *runtimeService) GetBackend(ctx context.Context, runtimeType string) (runtime.RuntimeBackend, error) {
// Map domain runtime types to backend types
backendType := s.mapRuntimeToBackend(runtimeType)
s.mutex.RLock()
backend, exists := s.backends[backendType]
s.mutex.RUnlock()
if !exists {
return nil, fmt.Errorf("runtime backend '%s' not available", backendType)
}
// Check backend health
if err := backend.HealthCheck(ctx); err != nil {
s.logger.Warn("Runtime backend health check failed",
zap.String("backend", backendType),
zap.Error(err))
return nil, fmt.Errorf("runtime backend '%s' is not healthy: %w", backendType, err)
}
return backend, nil
}
func (s *runtimeService) ListSupportedRuntimes(ctx context.Context) ([]*domain.RuntimeInfo, error) {
runtimes := []*domain.RuntimeInfo{
{
Type: domain.RuntimeNodeJS18,
Version: "18.x",
Available: s.isRuntimeAvailable(ctx, "nodejs18"),
DefaultImage: "node:18-alpine",
Description: "Node.js 18.x runtime with Alpine Linux",
},
{
Type: domain.RuntimePython39,
Version: "3.9.x",
Available: s.isRuntimeAvailable(ctx, "python3.9"),
DefaultImage: "python:3.9-alpine",
Description: "Python 3.9.x runtime with Alpine Linux",
},
{
Type: domain.RuntimeGo120,
Version: "1.20.x",
Available: s.isRuntimeAvailable(ctx, "go1.20"),
DefaultImage: "golang:1.20-alpine",
Description: "Go 1.20.x runtime with Alpine Linux",
},
{
Type: domain.RuntimeCustom,
Version: "custom",
Available: s.isRuntimeAvailable(ctx, "custom"),
DefaultImage: "alpine:latest",
Description: "Custom runtime with user-defined image",
},
}
return runtimes, nil
}
func (s *runtimeService) HealthCheck(ctx context.Context, runtimeType string) error {
backendType := s.mapRuntimeToBackend(runtimeType)
s.mutex.RLock()
backend, exists := s.backends[backendType]
s.mutex.RUnlock()
if !exists {
return fmt.Errorf("runtime backend '%s' not available", backendType)
}
return backend.HealthCheck(ctx)
}
func (s *runtimeService) GetRuntimeInfo(ctx context.Context, runtimeType string) (*runtime.RuntimeInfo, error) {
backendType := s.mapRuntimeToBackend(runtimeType)
s.mutex.RLock()
backend, exists := s.backends[backendType]
s.mutex.RUnlock()
if !exists {
return nil, fmt.Errorf("runtime backend '%s' not available", backendType)
}
return backend.GetInfo(ctx)
}
func (s *runtimeService) ListContainers(ctx context.Context, runtimeType string) ([]runtime.ContainerInfo, error) {
backendType := s.mapRuntimeToBackend(runtimeType)
s.mutex.RLock()
backend, exists := s.backends[backendType]
s.mutex.RUnlock()
if !exists {
return nil, fmt.Errorf("runtime backend '%s' not available", backendType)
}
return backend.ListContainers(ctx)
}
func (s *runtimeService) mapRuntimeToBackend(runtimeType string) string {
// For now, all runtimes use Docker backend
// In the future, we could support different backends for different runtimes
switch runtimeType {
case string(domain.RuntimeNodeJS18):
return "docker"
case string(domain.RuntimePython39):
return "docker"
case string(domain.RuntimeGo120):
return "docker"
case string(domain.RuntimeCustom):
return "docker"
default:
return s.config.DefaultRuntime
}
}
func (s *runtimeService) isRuntimeAvailable(ctx context.Context, runtimeType string) bool {
backendType := s.mapRuntimeToBackend(runtimeType)
s.mutex.RLock()
backend, exists := s.backends[backendType]
s.mutex.RUnlock()
if !exists {
return false
}
if err := backend.HealthCheck(ctx); err != nil {
return false
}
return true
}