199 lines
5.1 KiB
Go
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
|
|
}
|