feat: initial commit
This commit is contained in:
124
internal/agent/controller/gateway/controller.go
Normal file
124
internal/agent/controller/gateway/controller.go
Normal file
@ -0,0 +1,124 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/agent"
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
proxies map[spec.GatewayID]*ReverseProxy
|
||||
currentSpecRevision int
|
||||
}
|
||||
|
||||
// Name implements node.Controller.
|
||||
func (c *Controller) Name() string {
|
||||
return "gateway-controller"
|
||||
}
|
||||
|
||||
// Reconcile implements node.Controller.
|
||||
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
|
||||
gatewaySpec := spec.NewGatewaySpec()
|
||||
|
||||
if err := state.GetSpec(spec.NameGateway, gatewaySpec); err != nil {
|
||||
if errors.Is(err, agent.ErrSpecNotFound) {
|
||||
logger.Info(ctx, "could not find gateway spec, stopping all remaining proxies")
|
||||
|
||||
c.stopAllProxies(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
logger.Info(ctx, "retrieved spec", logger.F("spec", gatewaySpec.SpecName()), logger.F("revision", gatewaySpec.SpecRevision()))
|
||||
|
||||
if c.currentSpecRevision == gatewaySpec.SpecRevision() {
|
||||
logger.Info(ctx, "spec revision did not change, doing nothing")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
c.updateProxies(ctx, gatewaySpec)
|
||||
|
||||
c.currentSpecRevision = gatewaySpec.SpecRevision()
|
||||
logger.Info(ctx, "updating current spec revision", logger.F("revision", c.currentSpecRevision))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) stopAllProxies(ctx context.Context) {
|
||||
for gatewayID, proxy := range c.proxies {
|
||||
logger.Info(ctx, "stopping proxy", logger.F("gatewayID", gatewayID))
|
||||
|
||||
if err := proxy.Stop(); err != nil {
|
||||
logger.Error(
|
||||
ctx, "error while stopping proxy",
|
||||
logger.F("gatewayID", gatewayID),
|
||||
logger.E(errors.WithStack(err)),
|
||||
)
|
||||
|
||||
delete(c.proxies, gatewayID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) updateProxies(ctx context.Context, spec *spec.Gateway) {
|
||||
// Stop and remove obsolete gateways
|
||||
for gatewayID, proxy := range c.proxies {
|
||||
if _, exists := spec.Gateways[gatewayID]; exists {
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Info(ctx, "stopping proxy", logger.F("gatewayID", gatewayID))
|
||||
|
||||
if err := proxy.Stop(); err != nil {
|
||||
logger.Error(
|
||||
ctx, "error while stopping proxy",
|
||||
logger.F("gatewayID", gatewayID),
|
||||
logger.E(errors.WithStack(err)),
|
||||
)
|
||||
|
||||
delete(c.proxies, gatewayID)
|
||||
}
|
||||
}
|
||||
|
||||
// (Re)start gateways
|
||||
for gatewayID, gatewaySpec := range spec.Gateways {
|
||||
proxy, exists := c.proxies[gatewayID]
|
||||
if !exists {
|
||||
proxy = NewReverseProxy()
|
||||
c.proxies[gatewayID] = proxy
|
||||
}
|
||||
|
||||
logger.Info(
|
||||
ctx, "starting proxy",
|
||||
logger.F("gatewayID", gatewayID),
|
||||
logger.F("addr", gatewaySpec.Address),
|
||||
logger.F("target", gatewaySpec.Target),
|
||||
)
|
||||
|
||||
if err := proxy.Start(ctx, gatewaySpec.Address, gatewaySpec.Target); err != nil {
|
||||
logger.Error(
|
||||
ctx, "error while starting proxy",
|
||||
logger.F("gatewayID", gatewayID),
|
||||
logger.E(errors.WithStack(err)),
|
||||
)
|
||||
|
||||
delete(c.proxies, gatewayID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewController() *Controller {
|
||||
return &Controller{
|
||||
proxies: make(map[spec.GatewayID]*ReverseProxy),
|
||||
currentSpecRevision: -1,
|
||||
}
|
||||
}
|
||||
|
||||
var _ agent.Controller = &Controller{}
|
78
internal/agent/controller/gateway/reverse_proxy.go
Normal file
78
internal/agent/controller/gateway/reverse_proxy.go
Normal file
@ -0,0 +1,78 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
type ReverseProxy struct {
|
||||
addr string
|
||||
target string
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
func (p *ReverseProxy) Start(ctx context.Context, addr, target string) error {
|
||||
alreadyRunning := p.server != nil && target == p.target && addr == p.target
|
||||
|
||||
if alreadyRunning {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.server != nil {
|
||||
if err := p.Stop(); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
}
|
||||
|
||||
url, err := url.Parse(target)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||
|
||||
server.Handler = proxy
|
||||
|
||||
p.server = server
|
||||
p.addr = addr
|
||||
p.target = target
|
||||
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
logger.Error(ctx, "error while listening", logger.E(errors.WithStack(err)))
|
||||
}
|
||||
|
||||
if err := p.Stop(); err != nil {
|
||||
logger.Error(ctx, "error while stopping gateway", logger.E(errors.WithStack(err)))
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ReverseProxy) Stop() error {
|
||||
if p.server == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := p.server.Close(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
p.server = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewReverseProxy() *ReverseProxy {
|
||||
return &ReverseProxy{}
|
||||
}
|
Reference in New Issue
Block a user