feat: rename gateway spec to proxy

This commit is contained in:
2023-03-21 13:28:41 +01:00
parent fa36d55163
commit fbcd3ca806
21 changed files with 216 additions and 183 deletions

View File

@ -11,6 +11,7 @@ import (
"forge.cadoles.com/Cadoles/emissary/internal/spec/app"
"forge.cadoles.com/arcad/edge/pkg/bundle"
"forge.cadoles.com/arcad/edge/pkg/storage/sqlite"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/mitchellh/hashstructure/v2"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
@ -96,18 +97,32 @@ func (c *Controller) updateApps(ctx context.Context, spec *app.Spec) {
}
}
var (
key jwk.Key
err error
)
if spec.Auth != nil {
key, err = jwk.FromRaw(spec.Auth.Key)
if err != nil {
logger.Error(ctx, "could not parse authentication key", logger.E(errors.WithStack(err)))
return
}
}
// (Re)start apps
for appID, appSpec := range spec.Apps {
appCtx := logger.With(ctx, logger.F("appID", appID))
if err := c.updateApp(ctx, appID, appSpec); err != nil {
if err := c.updateApp(ctx, appID, appSpec, key); err != nil {
logger.Error(appCtx, "could not update app", logger.E(errors.WithStack(err)))
continue
}
}
}
func (c *Controller) updateApp(ctx context.Context, appID string, appSpec app.AppEntry) (err error) {
func (c *Controller) updateApp(ctx context.Context, appID string, appSpec app.AppEntry, key jwk.Key) (err error) {
newAppSpecHash, err := hashstructure.Hash(appSpec, hashstructure.FormatV2, nil)
if err != nil {
return errors.WithStack(err)
@ -150,7 +165,7 @@ func (c *Controller) updateApp(ctx context.Context, appID string, appSpec app.Ap
}
entry = &serverEntry{
Server: NewServer(bundle, db),
Server: NewServer(bundle, db, key),
SpecHash: 0,
}

View File

@ -22,6 +22,7 @@ import (
"github.com/dop251/goja"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/chi/v5"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/pkg/errors"
)
@ -30,6 +31,7 @@ type Server struct {
db *sql.DB
server *http.Server
serverMutex sync.RWMutex
key jwk.Key
}
func (s *Server) Start(ctx context.Context, addr string) (err error) {
@ -129,7 +131,9 @@ func (s *Server) getAppModules(bus bus.Bus, ds storage.DocumentStore, bs storage
module.StoreModuleFactory(ds),
module.BlobModuleFactory(bus, bs),
module.Extends(
auth.ModuleFactory(),
auth.ModuleFactory(
auth.WithJWT(s.getJWTKeySet),
),
func(o *goja.Object) {
if err := o.Set("CLAIM_ROLE", "role"); err != nil {
panic(errors.New("could not set 'CLAIM_ROLE' property"))
@ -143,9 +147,20 @@ func (s *Server) getAppModules(bus bus.Bus, ds storage.DocumentStore, bs storage
}
}
func NewServer(bundle bundle.Bundle, db *sql.DB) *Server {
func (s *Server) getJWTKeySet() (jwk.Set, error) {
set := jwk.NewSet()
if err := set.AddKey(s.key); err != nil {
return nil, errors.WithStack(err)
}
return set, nil
}
func NewServer(bundle bundle.Bundle, db *sql.DB, key jwk.Key) *Server {
return &Server{
bundle: bundle,
db: db,
key: key,
}
}

View File

@ -1,124 +0,0 @@
package gateway
import (
"context"
"forge.cadoles.com/Cadoles/emissary/internal/agent"
"forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
type Controller struct {
proxies map[gateway.ID]*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 := gateway.NewSpec()
if err := state.GetSpec(gateway.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 *gateway.Spec) {
// 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[gateway.ID]*ReverseProxy),
currentSpecRevision: -1,
}
}
var _ agent.Controller = &Controller{}

View File

@ -0,0 +1,124 @@
package proxy
import (
"context"
"forge.cadoles.com/Cadoles/emissary/internal/agent"
"forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
)
type Controller struct {
proxies map[proxy.ID]*ReverseProxy
currentSpecRevision int
}
// Name implements node.Controller.
func (c *Controller) Name() string {
return "proxy-controller"
}
// Reconcile implements node.Controller.
func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error {
proxySpec := proxy.NewSpec()
if err := state.GetSpec(proxy.NameProxy, proxySpec); err != nil {
if errors.Is(err, agent.ErrSpecNotFound) {
logger.Info(ctx, "could not find proxy spec, stopping all remaining proxies")
c.stopAllProxies(ctx)
return nil
}
return errors.WithStack(err)
}
logger.Info(ctx, "retrieved spec", logger.F("spec", proxySpec.SpecName()), logger.F("revision", proxySpec.SpecRevision()))
if c.currentSpecRevision == proxySpec.SpecRevision() {
logger.Info(ctx, "spec revision did not change, doing nothing")
return nil
}
c.updateProxies(ctx, proxySpec)
c.currentSpecRevision = proxySpec.SpecRevision()
logger.Info(ctx, "updating current spec revision", logger.F("revision", c.currentSpecRevision))
return nil
}
func (c *Controller) stopAllProxies(ctx context.Context) {
for proxyID, proxy := range c.proxies {
logger.Info(ctx, "stopping proxy", logger.F("proxyID", proxyID))
if err := proxy.Stop(); err != nil {
logger.Error(
ctx, "error while stopping proxy",
logger.F("proxyID", proxyID),
logger.E(errors.WithStack(err)),
)
delete(c.proxies, proxyID)
}
}
}
func (c *Controller) updateProxies(ctx context.Context, spec *proxy.Spec) {
// Stop and remove obsolete proxys
for proxyID, proxy := range c.proxies {
if _, exists := spec.Proxies[proxyID]; exists {
continue
}
logger.Info(ctx, "stopping proxy", logger.F("proxyID", proxyID))
if err := proxy.Stop(); err != nil {
logger.Error(
ctx, "error while stopping proxy",
logger.F("proxyID", proxyID),
logger.E(errors.WithStack(err)),
)
delete(c.proxies, proxyID)
}
}
// (Re)start proxys
for proxyID, proxySpec := range spec.Proxies {
proxy, exists := c.proxies[proxyID]
if !exists {
proxy = NewReverseProxy()
c.proxies[proxyID] = proxy
}
logger.Info(
ctx, "starting proxy",
logger.F("proxyID", proxyID),
logger.F("addr", proxySpec.Address),
logger.F("target", proxySpec.Target),
)
if err := proxy.Start(ctx, proxySpec.Address, proxySpec.Target); err != nil {
logger.Error(
ctx, "error while starting proxy",
logger.F("proxyID", proxyID),
logger.E(errors.WithStack(err)),
)
delete(c.proxies, proxyID)
}
}
}
func NewController() *Controller {
return &Controller{
proxies: make(map[proxy.ID]*ReverseProxy),
currentSpecRevision: -1,
}
}
var _ agent.Controller = &Controller{}

View File

@ -1,4 +1,4 @@
package gateway
package proxy
import (
"context"

View File

@ -5,9 +5,9 @@ import (
"forge.cadoles.com/Cadoles/emissary/internal/agent"
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/app"
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/gateway"
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/openwrt"
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/persistence"
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/proxy"
"forge.cadoles.com/Cadoles/emissary/internal/agent/controller/spec"
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata"
"forge.cadoles.com/Cadoles/emissary/internal/agent/metadata/collector/buildinfo"
@ -49,8 +49,8 @@ func RunCommand() *cli.Command {
controllers = append(controllers, spec.NewController())
}
if ctrlConf.Gateway.Enabled {
controllers = append(controllers, gateway.NewController())
if ctrlConf.Proxy.Enabled {
controllers = append(controllers, proxy.NewController())
}
if ctrlConf.UCI.Enabled {

View File

@ -16,7 +16,7 @@ import (
// Import specs
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
)

View File

@ -11,7 +11,7 @@ import (
"github.com/urfave/cli/v2"
// Spec validation
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
)

View File

@ -17,7 +17,7 @@ type ShellCollectorConfig struct {
type ControllersConfig struct {
Persistence PersistenceControllerConfig `yaml:"persistence"`
Spec SpecControllerConfig `yaml:"spec"`
Gateway GatewayControllerConfig `yaml:"gateway"`
Proxy ProxyControllerConfig `yaml:"proxy"`
UCI UCIControllerConfig `yaml:"uci"`
App AppControllerConfig `yaml:"app"`
}
@ -30,7 +30,7 @@ type PersistenceControllerConfig struct {
type SpecControllerConfig struct {
Enabled InterpolatedBool `yaml:"enabled"`
}
type GatewayControllerConfig struct {
type ProxyControllerConfig struct {
Enabled InterpolatedBool `yaml:"enabled"`
}
@ -59,7 +59,7 @@ func NewDefaultAgentConfig() AgentConfig {
Enabled: true,
StateFile: "state.json",
},
Gateway: GatewayControllerConfig{
Proxy: ProxyControllerConfig{
Enabled: true,
},
UCI: UCIControllerConfig{

View File

@ -13,7 +13,7 @@ import (
// Import specs
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/app"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/gateway"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/proxy"
_ "forge.cadoles.com/Cadoles/emissary/internal/spec/uci"
)

View File

@ -29,6 +29,15 @@
"additionalProperties": false
}
}
},
"auth": {
"type": "object",
"properties": {
"key": {
"type": "object"
}
},
"required": ["key"]
}
},
"required": ["apps"],

View File

@ -7,8 +7,9 @@ import (
const NameApp spec.Name = "app.emissary.cadoles.com"
type Spec struct {
Revision int `json:"revisions"`
Apps map[string]AppEntry
Revision int `json:"revisions"`
Apps map[string]AppEntry `json:"apps"`
Auth *Auth `json:"auth"`
}
type AppEntry struct {
@ -18,6 +19,10 @@ type AppEntry struct {
Format string `json:"format"`
}
type Auth struct {
Key any `json:"key"`
}
func (s *Spec) SpecName() spec.Name {
return NameApp
}
@ -29,6 +34,7 @@ func (s *Spec) SpecRevision() int {
func (s *Spec) SpecData() map[string]any {
return map[string]any{
"apps": s.Apps,
"auth": s.Auth,
}
}

View File

@ -1,4 +1,4 @@
package gateway
package proxy
import (
_ "embed"
@ -11,7 +11,7 @@ import (
var schema []byte
func init() {
if err := spec.Register(NameGateway, schema); err != nil {
if err := spec.Register(NameProxy, schema); err != nil {
panic(errors.WithStack(err))
}
}

View File

@ -1,11 +1,11 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://gateway.emissary.cadoles.com/spec.json",
"title": "GatewaySpec",
"description": "Emissary 'Gateway' specification",
"$id": "https://proxy.emissary.cadoles.com/spec.json",
"title": "ProxySpec",
"description": "Emissary 'Proxy' specification",
"type": "object",
"properties": {
"gateways": {
"proxies": {
"type": "object",
"patternProperties": {
".*": {
@ -24,6 +24,6 @@
}
}
},
"required": ["gateways"],
"required": ["proxies"],
"additionalProperties": false
}

View File

@ -1,23 +1,23 @@
package gateway
package proxy
import "forge.cadoles.com/Cadoles/emissary/internal/spec"
const NameGateway spec.Name = "gateway.emissary.cadoles.com"
const NameProxy spec.Name = "proxy.emissary.cadoles.com"
type ID string
type Spec struct {
Revision int `json:"revision"`
Gateways map[ID]GatewayEntry `json:"gateways"`
Revision int `json:"revision"`
Proxies map[ID]ProxyEntry `json:"proxies"`
}
type GatewayEntry struct {
type ProxyEntry struct {
Address string `json:"address"`
Target string `json:"target"`
}
func (s *Spec) SpecName() spec.Name {
return NameGateway
return NameProxy
}
func (s *Spec) SpecRevision() int {
@ -26,14 +26,14 @@ func (s *Spec) SpecRevision() int {
func (s *Spec) SpecData() map[string]any {
return map[string]any{
"gateways": s.Gateways,
"proxies": s.Proxies,
}
}
func NewSpec() *Spec {
return &Spec{
Revision: -1,
Gateways: make(map[ID]GatewayEntry),
Proxies: make(map[ID]ProxyEntry),
}
}

View File

@ -1,7 +1,7 @@
{
"name": "gateway.emissary.cadoles.com",
"name": "proxy.emissary.cadoles.com",
"data": {
"gateways": {
"proxies": {
"cadoles.com": {
"address": ":3003",
"target": "https://www.cadoles.com",

View File

@ -1,7 +1,7 @@
{
"name": "gateway.emissary.cadoles.com",
"name": "proxy.emissary.cadoles.com",
"data": {
"gateways": {
"proxies": {
"cadoles.com": {
"address": ":3003"
}

View File

@ -1,7 +1,7 @@
{
"name": "gateway.emissary.cadoles.com",
"name": "proxy.emissary.cadoles.com",
"data": {
"gateways": {
"proxies": {
"cadoles.com": {
"address": ":3003",
"target": "https://www.cadoles.com"

View File

@ -1,4 +1,4 @@
package gateway
package proxy
import (
"context"
@ -38,7 +38,7 @@ func TestValidator(t *testing.T) {
t.Parallel()
validator := spec.NewValidator()
if err := validator.Register(NameGateway, schema); err != nil {
if err := validator.Register(NameProxy, schema); err != nil {
t.Fatalf("+%v", errors.WithStack(err))
}