feat: add additional routes from defined directory

This commit is contained in:
Jonas Kaninda
2024-11-18 08:50:49 +01:00
parent 0b5e11a5dd
commit a9d365daa4
9 changed files with 137 additions and 13 deletions

View File

@@ -76,7 +76,7 @@ func (GatewayServer) Config(configFile string, ctx context.Context) (*GatewaySer
} }
logger.Info("Generating new configuration file...") logger.Info("Generating new configuration file...")
// check if config directory does exist // check if config Directory does exist
if !util.FolderExists(ConfigDir) { if !util.FolderExists(ConfigDir) {
err := os.MkdirAll(ConfigDir, os.ModePerm) err := os.MkdirAll(ConfigDir, os.ModePerm)
if err != nil { if err != nil {

47
internal/extra_config.go Normal file
View File

@@ -0,0 +1,47 @@
/*
* Copyright 2024 Jonas Kaninda
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package pkg
import (
"fmt"
"os"
"path/filepath"
)
// loadExtraFiles loads routes files in .yml and .yaml based on defined directory
func loadExtraFiles(routePath string) ([]string, error) {
// Slice to store YAML/YML files
var yamlFiles []string
// Walk through the Directory
err := filepath.Walk(routePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// Check for .yaml or .yml file extension
if !info.IsDir() && (filepath.Ext(path) == ".yaml" || filepath.Ext(path) == ".yml") {
yamlFiles = append(yamlFiles, path)
}
return nil
})
if err != nil {
//log.Fatalf("error walking the path %v: %v", routePath, err)
return nil, fmt.Errorf("error loading extra route files: %v", err)
}
return yamlFiles, nil
}

View File

@@ -34,10 +34,11 @@ type Gateway struct {
// RateLimit Defines the number of request peer minutes // RateLimit Defines the number of request peer minutes
RateLimit int `yaml:"rateLimit" env:"GOMA_RATE_LIMIT, overwrite"` RateLimit int `yaml:"rateLimit" env:"GOMA_RATE_LIMIT, overwrite"`
// BlockCommonExploits enable, disable block common exploits // BlockCommonExploits enable, disable block common exploits
BlockCommonExploits bool `yaml:"blockCommonExploits"` BlockCommonExploits bool `yaml:"blockCommonExploits"`
AccessLog string `yaml:"accessLog" env:"GOMA_ACCESS_LOG, overwrite"` AccessLog string `yaml:"accessLog" env:"GOMA_ACCESS_LOG, overwrite"`
ErrorLog string `yaml:"errorLog" env:"GOMA_ERROR_LOG=, overwrite"` ErrorLog string `yaml:"errorLog" env:"GOMA_ERROR_LOG=, overwrite"`
LogLevel string `yaml:"logLevel" env:"GOMA_LOG_LEVEL, overwrite"` LogLevel string `yaml:"logLevel" env:"GOMA_LOG_LEVEL, overwrite"`
ExtraRoutes ExtraRouteConfig `yaml:"extraRoutes"`
// DisableHealthCheckStatus enable and disable routes health check // DisableHealthCheckStatus enable and disable routes health check
DisableHealthCheckStatus bool `yaml:"disableHealthCheckStatus"` DisableHealthCheckStatus bool `yaml:"disableHealthCheckStatus"`
// DisableRouteHealthCheckError allows enabling and disabling backend healthcheck errors // DisableRouteHealthCheckError allows enabling and disabling backend healthcheck errors

View File

@@ -35,18 +35,27 @@ func init() {
// Initialize initializes the routes // Initialize initializes the routes
func (gatewayServer GatewayServer) Initialize() *mux.Router { func (gatewayServer GatewayServer) Initialize() *mux.Router {
gateway := gatewayServer.gateway gateway := gatewayServer.gateway
dynamicRoutes = gateway.Routes
// Load Extra Routes
if len(gateway.ExtraRoutes.Directory) != 0 {
extraRoutes, err := loadExtraRoutes(gateway.ExtraRoutes.Directory)
if err != nil {
logger.Error(err.Error())
}
dynamicRoutes = append(dynamicRoutes, extraRoutes...)
}
m := gatewayServer.middlewares m := gatewayServer.middlewares
redisBased := false redisBased := false
if len(gateway.Redis.Addr) != 0 { if len(gateway.Redis.Addr) != 0 {
redisBased = true redisBased = true
} }
// Routes background healthcheck // Routes background healthcheck
routesHealthCheck(gateway.Routes) routesHealthCheck(dynamicRoutes)
r := mux.NewRouter() r := mux.NewRouter()
heath := HealthCheckRoute{ heath := HealthCheckRoute{
DisableRouteHealthCheckError: gateway.DisableRouteHealthCheckError, DisableRouteHealthCheckError: gateway.DisableRouteHealthCheckError,
Routes: gateway.Routes, Routes: dynamicRoutes,
} }
if gateway.EnableMetrics { if gateway.EnableMetrics {
// Prometheus endpoint // Prometheus endpoint
@@ -80,7 +89,7 @@ func (gatewayServer GatewayServer) Initialize() *mux.Router {
// Add rate limit middlewares // Add rate limit middlewares
r.Use(limiter.RateLimitMiddleware()) r.Use(limiter.RateLimitMiddleware())
} }
for rIndex, route := range gateway.Routes { for rIndex, route := range dynamicRoutes {
if len(route.Path) != 0 { if len(route.Path) != 0 {
// Checks if route destination and backend are empty // Checks if route destination and backend are empty
if len(route.Destination) == 0 && len(route.Backends) == 0 { if len(route.Destination) == 0 && len(route.Backends) == 0 {

55
internal/route_config.go Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright 2024 Jonas Kaninda
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package pkg
import (
"fmt"
"github.com/jkaninda/goma-gateway/pkg/logger"
"gopkg.in/yaml.v3"
"os"
)
// loadExtraRoutes loads additional routes
func loadExtraRoutes(routePath string) ([]Route, error) {
logger.Info("Loading additional routes from %s", routePath)
yamlFiles, err := loadExtraFiles(routePath)
if err != nil {
return nil, fmt.Errorf("error loading extra files: %v", err)
}
var extraRoutes []Route
for _, yamlFile := range yamlFiles {
buf, err := os.ReadFile(yamlFile)
if err != nil {
return nil, fmt.Errorf("error loading extra file: %v", err)
}
ex := &ExtraRoute{}
err = yaml.Unmarshal(buf, ex)
if err != nil {
return nil, fmt.Errorf("in file %q: %w", ConfigFile, err)
}
extraRoutes = append(extraRoutes, ex.Routes...)
}
if len(extraRoutes) == 0 {
return nil, fmt.Errorf("no extra routes found in %s", routePath)
} else {
logger.Info("Loaded %d extra routes from %s", len(extraRoutes), routePath)
}
return extraRoutes, nil
}

View File

@@ -56,3 +56,8 @@ type Route struct {
// Middlewares Defines route middlewares from Middleware names // Middlewares Defines route middlewares from Middleware names
Middlewares []string `yaml:"middlewares"` Middlewares []string `yaml:"middlewares"`
} }
type ExtraRoute struct {
// Routes holds proxy routes
Routes []Route `yaml:"routes"`
}

View File

@@ -41,7 +41,7 @@ func (gatewayServer GatewayServer) Start() error {
} }
if !gatewayServer.gateway.DisableDisplayRouteOnStart { if !gatewayServer.gateway.DisableDisplayRouteOnStart {
printRoute(gatewayServer.gateway.Routes) printRoute(dynamicRoutes)
} }
httpServer := gatewayServer.createServer(":8080", route, nil) httpServer := gatewayServer.createServer(":8080", route, nil)

View File

@@ -168,3 +168,8 @@ type Redis struct {
Addr string `yaml:"addr"` Addr string `yaml:"addr"`
Password string `yaml:"password"` Password string `yaml:"password"`
} }
type ExtraRouteConfig struct {
Directory string `yaml:"directory"`
Watch bool `yaml:"watch"`
}

View File

@@ -8,7 +8,9 @@ const AccessMiddleware = "access" // access middlewares
const BasicAuth = "basic" // basic authentication middlewares const BasicAuth = "basic" // basic authentication middlewares
const JWTAuth = "jwt" // JWT authentication middlewares const JWTAuth = "jwt" // JWT authentication middlewares
const OAuth = "oauth" // OAuth authentication middlewares const OAuth = "oauth" // OAuth authentication middlewares
// Round-robin counter var (
var counter uint32 // Round-robin counter
counter uint32
var Routes *[]Route // dynamicRoutes routes
dynamicRoutes []Route
)