use the proxy as a package

This commit is contained in:
Zoe
2025-05-15 18:32:14 +00:00
parent c51eca5dab
commit fb2588cc3a
10 changed files with 62 additions and 142 deletions

View File

@@ -11,6 +11,7 @@ import (
proxyManagerService "github.com/juls0730/flux/internal/services/proxy"
"github.com/juls0730/flux/internal/util"
"github.com/juls0730/flux/pkg"
"github.com/juls0730/sentinel"
"go.uber.org/zap"
)
@@ -18,12 +19,12 @@ type AppManager struct {
util.TypedMap[uuid.UUID, *models.App]
nameIndex util.TypedMap[string, uuid.UUID]
logger *zap.SugaredLogger
proxyManager *proxyManagerService.ProxyManager
proxyManager *sentinel.ProxyManager
dockerClient *docker.DockerClient
db *sql.DB
}
func NewAppManager(db *sql.DB, dockerClient *docker.DockerClient, proxyManager *proxyManagerService.ProxyManager, logger *zap.SugaredLogger) *AppManager {
func NewAppManager(db *sql.DB, dockerClient *docker.DockerClient, proxyManager *sentinel.ProxyManager, logger *zap.SugaredLogger) *AppManager {
return &AppManager{
db: db,
dockerClient: dockerClient,
@@ -87,7 +88,7 @@ func (appManager *AppManager) CreateApp(ctx context.Context, imageName string, p
return nil, fmt.Errorf("failed to get internal url: %v", err)
}
newProxy, err := proxyManagerService.NewDeploymentProxy(*deploymentInternalUrl)
newProxy, err := sentinel.NewDeploymentProxy(deploymentInternalUrl.String(), proxyManagerService.GetTransport)
if err != nil {
appManager.logger.Errorw("Failed to create deployment proxy", zap.Error(err))
return nil, fmt.Errorf("failed to create deployment proxy: %v", err)
@@ -293,7 +294,7 @@ func (am *AppManager) Init() error {
continue
}
proxy, err := proxyManagerService.NewDeploymentProxy(*proxyURL)
proxy, err := sentinel.NewDeploymentProxy(proxyURL.String(), proxyManagerService.GetTransport)
if err != nil {
am.logger.Errorw("Failed to create proxy", zap.Error(err))
continue

View File

@@ -1,134 +1,18 @@
package proxyManagerService
import (
"context"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"sync/atomic"
"time"
"github.com/juls0730/flux/internal/util"
"go.uber.org/zap"
)
type DeploymentId int64
// this is the object that oversees the proxying of requests to the correct deployment
type ProxyManager struct {
util.TypedMap[string, *Proxy]
logger *zap.SugaredLogger
}
func NewProxyManager(logger *zap.SugaredLogger) *ProxyManager {
return &ProxyManager{
logger: logger,
}
}
func (proxyManager *ProxyManager) ListenAndServe(host string) error {
if host == "" {
host = "0.0.0.0:7465"
}
proxyManager.logger.Infof("Proxy server starting on http://%s", host)
if err := http.ListenAndServe(host, proxyManager); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("failed to start proxy server: %v", err)
}
return nil
}
// Stops forwarding traffic to a deployment
func (proxyManager *ProxyManager) RemoveDeployment(host string) {
proxyManager.logger.Debugw("Removing proxy", zap.String("host", host))
proxyManager.Delete(host)
}
// Starts forwarding traffic to a deployment. The deployment must be ready to recieve requests before this is called.
func (proxyManager *ProxyManager) AddProxy(host string, proxy *Proxy) {
proxyManager.logger.Debugw("Adding proxy", zap.String("host", host))
proxyManager.Store(host, proxy)
}
// This function is responsible for taking an http request and forwarding it to the correct deployment
func (proxyManager *ProxyManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
host := r.Host
proxyManager.logger.Debugw("Proxying request", zap.String("host", host))
proxy, ok := proxyManager.Load(host)
if !ok {
http.Error(w, "Not found", http.StatusNotFound)
return
}
proxy.proxyFunc.ServeHTTP(w, r)
}
type Proxy struct {
forwardingFor url.URL
proxyFunc *httputil.ReverseProxy
shutdownTimeout time.Duration
activeRequests int64
}
const PROXY_SHUTDOWN_TIMEOUT = 30 * time.Second
// Creates a proxy for a given deployment
func NewDeploymentProxy(forwardingFor url.URL) (*Proxy, error) {
proxy := &Proxy{
forwardingFor: forwardingFor,
shutdownTimeout: PROXY_SHUTDOWN_TIMEOUT,
activeRequests: 0,
}
proxy.proxyFunc = &httputil.ReverseProxy{
Director: func(req *http.Request) {
req.URL = &url.URL{
Scheme: forwardingFor.Scheme,
Host: forwardingFor.Host,
Path: req.URL.Path,
}
req.Host = forwardingFor.Host
atomic.AddInt64(&proxy.activeRequests, 1)
},
Transport: &http.Transport{
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
MaxIdleConnsPerHost: 100,
},
ModifyResponse: func(resp *http.Response) error {
atomic.AddInt64(&proxy.activeRequests, -1)
return nil
func GetTransport(target string) *http.Transport {
return &http.Transport{
Proxy: func(r *http.Request) (*url.URL, error) {
return url.Parse(target)
},
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
MaxIdleConnsPerHost: 100,
}
return proxy, nil
}
// Drains connections from a proxy
func (p *Proxy) GracefulShutdown(logger *zap.SugaredLogger, shutdownFunc func()) {
logger.Debugw("Shutting down proxy", zap.String("host", p.forwardingFor.Host))
ctx, cancel := context.WithTimeout(context.Background(), p.shutdownTimeout)
defer cancel()
done := false
for !done {
select {
case <-ctx.Done():
logger.Debugw("Proxy shutdown timed out", zap.String("host", p.forwardingFor.Host))
done = true
default:
if atomic.LoadInt64(&p.activeRequests) == 0 {
logger.Debugw("Proxy shutdown completed successfully", zap.String("host", p.forwardingFor.Host))
done = true
}
time.Sleep(time.Second)
}
}
shutdownFunc()
}