mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 09:01:31 +00:00
WIP for AWS gateway daemon
This commit is contained in:
@@ -55,6 +55,11 @@ var hiveCmd = &cobra.Command{
|
||||
Short: "Manage the hive service",
|
||||
}
|
||||
|
||||
var awsgwCmd = &cobra.Command{
|
||||
Use: "awsgw",
|
||||
Short: "Manage the awsgw (AWS gateway) service",
|
||||
}
|
||||
|
||||
var predastoreStartCmd = &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start the predastore service",
|
||||
@@ -410,6 +415,88 @@ var hiveStatusCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
// AWS GW
|
||||
|
||||
var awsgwStartCmd = &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "Start the awsgw service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Starting awsgw service...")
|
||||
|
||||
cfgFile := viper.GetString("config")
|
||||
|
||||
if cfgFile == "" {
|
||||
fmt.Println("Config file is not set")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Support ENV vars, CLI, otherwise revert to config.LoadConfig()
|
||||
appConfig, err := config.LoadConfig(cfgFile)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error loading config file:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Overwrite defaults (CLI first, config second, env third)
|
||||
awsgwHost := viper.GetString("host")
|
||||
if awsgwHost != "" {
|
||||
fmt.Println("Overwriting awsgw host to:", awsgwHost)
|
||||
appConfig.AWSGW.Host = awsgwHost
|
||||
}
|
||||
|
||||
awsgwTlsCert := viper.GetString("tls-cert")
|
||||
if awsgwTlsCert != "" {
|
||||
fmt.Println("Overwriting awsgw tls-cert to:", awsgwTlsCert)
|
||||
appConfig.AWSGW.TLSCert = awsgwTlsCert
|
||||
}
|
||||
|
||||
awsgwTlsKey := viper.GetString("tls-key")
|
||||
|
||||
if awsgwTlsKey != "" {
|
||||
fmt.Println("Overwriting awsgw tls-key to:", awsgwTlsKey)
|
||||
appConfig.AWSGW.TLSKey = awsgwTlsKey
|
||||
}
|
||||
|
||||
service, err := service.New("awsgw", appConfig)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error starting awsgw service:", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Start()
|
||||
fmt.Println("AWSGW service started")
|
||||
},
|
||||
}
|
||||
|
||||
var awsgwStopCmd = &cobra.Command{
|
||||
Use: "stop",
|
||||
Short: "Stop the awsgw service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Stopping awsgw service...")
|
||||
|
||||
service, err := service.New("awsgw", &config.Config{})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error stopping awsgw service:", err)
|
||||
return
|
||||
}
|
||||
|
||||
service.Stop()
|
||||
|
||||
fmt.Println("AWSGW service stopped")
|
||||
},
|
||||
}
|
||||
|
||||
var awsgwStatusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Get status of the awsgw service",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("AWSGW service status: ...")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
viper.SetEnvPrefix("HIVE") // Prefix for environment variables
|
||||
@@ -521,4 +608,29 @@ func init() {
|
||||
viper.BindEnv("wal-dir", "HIVE_WAL_DIR")
|
||||
viper.BindPFlag("wal-dir", hiveCmd.PersistentFlags().Lookup("wal-dir"))
|
||||
|
||||
// AWS GW
|
||||
serviceCmd.AddCommand(awsgwCmd)
|
||||
|
||||
awsgwCmd.PersistentFlags().String("host", "0.0.0.0:9999", "AWS Gateway server host")
|
||||
viper.BindEnv("host", "HIVE_AWSGW_HOST")
|
||||
viper.BindPFlag("host", awsgwCmd.PersistentFlags().Lookup("host"))
|
||||
|
||||
// AWS GW TLS Cert
|
||||
awsgwCmd.PersistentFlags().String("tls-cert", "", "AWS Gateway TLS certificate")
|
||||
viper.BindEnv("tls-cert", "HIVE_AWSGW_TLS_CERT")
|
||||
viper.BindPFlag("tls-cert", awsgwCmd.PersistentFlags().Lookup("tls-cert"))
|
||||
|
||||
// AWS GW TLS Key
|
||||
awsgwCmd.PersistentFlags().String("tls-key", "", "AWS Gateway TLS key")
|
||||
viper.BindEnv("tls-key", "HIVE_AWSGW_TLS_KEY")
|
||||
viper.BindPFlag("tls-key", awsgwCmd.PersistentFlags().Lookup("tls-key"))
|
||||
|
||||
awsgwCmd.PersistentFlags().Bool("debug", false, "AWS Gateway Debug")
|
||||
viper.BindEnv("debug", "HIVE_AWSGW_DEBUG")
|
||||
viper.BindPFlag("debug", awsgwCmd.PersistentFlags().Lookup("debug"))
|
||||
|
||||
awsgwCmd.AddCommand(awsgwStartCmd)
|
||||
awsgwCmd.AddCommand(awsgwStopCmd)
|
||||
awsgwCmd.AddCommand(awsgwStatusCmd)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Test configuration file in TOML format.
|
||||
version = "1.0"
|
||||
region = "ap-southeast-2"
|
||||
|
||||
# Enable verbose debugging
|
||||
debug = false
|
||||
|
||||
[[auth]]
|
||||
access_key_id = "AKIAIOSFODNN7EXAMPLE"
|
||||
secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
policy = [
|
||||
{ bucket = "predastore", actions = ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListAllMyBuckets"] },
|
||||
]
|
||||
+8
-2
@@ -7,8 +7,14 @@ secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
|
||||
|
||||
[daemon]
|
||||
host = "0.0.0.0:4432"
|
||||
sslkey = "config/server.key"
|
||||
sslcert = "config/server.pem"
|
||||
tlskey = "config/server.key"
|
||||
tlscert = "config/server.pem"
|
||||
|
||||
[awsgw]
|
||||
host = "0.0.0.0:9999"
|
||||
tlskey = "config/server.key"
|
||||
tlscert = "config/server.pem"
|
||||
debug = true
|
||||
|
||||
[nats]
|
||||
host = "0.0.0.0:4222"
|
||||
|
||||
+11
-2
@@ -16,6 +16,7 @@ type Config struct {
|
||||
Daemon DaemonConfig `mapstructure:"daemon"`
|
||||
NATS NATSConfig `mapstructure:"nats"`
|
||||
Predastore PredastoreConfig `mapstructure:"predastore"`
|
||||
AWSGW AWSGWConfig `mapstructure:"awsgw"`
|
||||
|
||||
// Authentication
|
||||
AccessKey string `mapstructure:"access_key"`
|
||||
@@ -25,6 +26,14 @@ type Config struct {
|
||||
WalDir string `mapstructure:"wal_dir"`
|
||||
}
|
||||
|
||||
type AWSGWConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
TLSKey string `mapstructure:"tlskey"`
|
||||
TLSCert string `mapstructure:"tlscert"`
|
||||
|
||||
Debug bool `mapstructure:"debug"`
|
||||
}
|
||||
|
||||
type PredastoreConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Bucket string `mapstructure:"bucket"`
|
||||
@@ -37,8 +46,8 @@ type PredastoreConfig struct {
|
||||
// DaemonConfig holds the daemon configuration
|
||||
type DaemonConfig struct {
|
||||
Host string `mapstructure:"host"`
|
||||
SSLKey string `mapstructure:"sslkey"`
|
||||
SSLCert string `mapstructure:"sslcert"`
|
||||
TLSKey string `mapstructure:"tlskey"`
|
||||
TLSCert string `mapstructure:"tlscert"`
|
||||
}
|
||||
|
||||
// NATSConfig holds the NATS configuration
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package gateway
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (gw *GatewayConfig) Account_Request(ctx *fiber.Ctx) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func (gw *GatewayConfig) EC2_Request(ctx *fiber.Ctx) error {
|
||||
|
||||
fmt.Println("HERE!")
|
||||
|
||||
fmt.Println("Body")
|
||||
spew.Dump(ctx.Body())
|
||||
|
||||
fmt.Println("Request POST")
|
||||
spew.Dump(ctx.Request())
|
||||
|
||||
// Headers
|
||||
fmt.Println("Headers")
|
||||
spew.Dump(ctx.GetReqHeaders())
|
||||
|
||||
queryArgs := parseAWSQueryArgs(string(ctx.Body()))
|
||||
|
||||
// Run the action
|
||||
switch queryArgs["Action"] {
|
||||
case "DescribeInstances":
|
||||
return gw.EC2_DescribeInstances(ctx, queryArgs)
|
||||
case "RunInstances":
|
||||
return gw.EC2_RunInstances(ctx, queryArgs)
|
||||
case "CreateKeyPair":
|
||||
return gw.EC2_CreateKeyPair(ctx, queryArgs)
|
||||
default:
|
||||
slog.Warn("EC2 Unsupported Action", "action", queryArgs["Action"])
|
||||
}
|
||||
|
||||
return fiber.NewError(fiber.StatusNotImplemented, "Action not implemented")
|
||||
}
|
||||
|
||||
func (gw *GatewayConfig) EC2_DescribeInstances(ctx *fiber.Ctx, args map[string]string) error {
|
||||
|
||||
slog.Info("EC2 DescribeInstances called")
|
||||
|
||||
// Return a dummy response
|
||||
response := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
|
||||
<requestId>2b7ac2f1-9acd-4d73-b0a1-1b2e5607f9ab</requestId>
|
||||
<reservationSet>
|
||||
<item>
|
||||
<reservationId>r-0f6b3c4d5e6789abc</reservationId>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<groupSet/>
|
||||
<instancesSet>
|
||||
<item>
|
||||
<instanceId>i-0123456789abcdef0</instanceId>
|
||||
<imageId>ami-08d4ac5b634553e16</imageId>
|
||||
<instanceState>
|
||||
<code>16</code>
|
||||
<name>running</name>
|
||||
</instanceState>
|
||||
<privateDnsName>ip-10-0-1-25.ap-southeast-2.compute.internal</privateDnsName>
|
||||
<dnsName>ec2-3-26-14-112.ap-southeast-2.compute.amazonaws.com</dnsName>
|
||||
<reason/>
|
||||
<keyName>my-keypair</keyName>
|
||||
<amiLaunchIndex>0</amiLaunchIndex>
|
||||
<productCodes/>
|
||||
<instanceType>t3.medium</instanceType>
|
||||
<launchTime>2025-02-18T04:12:07.000Z</launchTime>
|
||||
<placement>
|
||||
<availabilityZone>ap-southeast-2a</availabilityZone>
|
||||
<groupName/>
|
||||
<tenancy>default</tenancy>
|
||||
</placement>
|
||||
<monitoring>
|
||||
<state>disabled</state>
|
||||
</monitoring>
|
||||
<privateIpAddress>10.0.1.25</privateIpAddress>
|
||||
<ipAddress>3.26.14.112</ipAddress>
|
||||
<subnetId>subnet-0ab1c2d3e4f567890</subnetId>
|
||||
<vpcId>vpc-0123abcd4567efgh8</vpcId>
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
<item>
|
||||
<groupId>sg-0abc1234def567890</groupId>
|
||||
<groupName>default</groupName>
|
||||
</item>
|
||||
</groupSet>
|
||||
<stateReason/>
|
||||
<architecture>x86_64</architecture>
|
||||
<rootDeviceType>ebs</rootDeviceType>
|
||||
<rootDeviceName>/dev/xvda</rootDeviceName>
|
||||
<blockDeviceMapping>
|
||||
<item>
|
||||
<deviceName>/dev/xvda</deviceName>
|
||||
<ebs>
|
||||
<volumeId>vol-06f5e4d3c2b1a0f98</volumeId>
|
||||
<status>attached</status>
|
||||
<attachTime>2025-02-18T04:12:09.000Z</attachTime>
|
||||
<deleteOnTermination>true</deleteOnTermination>
|
||||
</ebs>
|
||||
</item>
|
||||
</blockDeviceMapping>
|
||||
<virtualizationType>hvm</virtualizationType>
|
||||
<tagSet>
|
||||
<item>
|
||||
<key>Name</key>
|
||||
<value>web-01</value>
|
||||
</item>
|
||||
</tagSet>
|
||||
<hypervisor>xen</hypervisor>
|
||||
<networkInterfaceSet>
|
||||
<item>
|
||||
<networkInterfaceId>eni-0a12b34c56d78ef90</networkInterfaceId>
|
||||
<subnetId>subnet-0ab1c2d3e4f567890</subnetId>
|
||||
<vpcId>vpc-0123abcd4567efgh8</vpcId>
|
||||
<description/>
|
||||
<ownerId>123456789012</ownerId>
|
||||
<status>in-use</status>
|
||||
<macAddress>0a:1b:2c:3d:4e:5f</macAddress>
|
||||
<privateIpAddress>10.0.1.25</privateIpAddress>
|
||||
<privateDnsName>ip-10-0-1-25.ap-southeast-2.compute.internal</privateDnsName>
|
||||
<sourceDestCheck>true</sourceDestCheck>
|
||||
<groupSet>
|
||||
<item>
|
||||
<groupId>sg-0abc1234def567890</groupId>
|
||||
<groupName>default</groupName>
|
||||
</item>
|
||||
</groupSet>
|
||||
<attachment>
|
||||
<attachmentId>eni-attach-0abc123def4567890</attachmentId>
|
||||
<deviceIndex>0</deviceIndex>
|
||||
<status>attached</status>
|
||||
<attachTime>2025-02-18T04:12:07.000Z</attachTime>
|
||||
<deleteOnTermination>true</deleteOnTermination>
|
||||
</attachment>
|
||||
<privateIpAddressesSet>
|
||||
<item>
|
||||
<privateIpAddress>10.0.1.25</privateIpAddress>
|
||||
<privateDnsName>ip-10-0-1-25.ap-southeast-2.compute.internal</privateDnsName>
|
||||
<primary>true</primary>
|
||||
</item>
|
||||
</privateIpAddressesSet>
|
||||
</item>
|
||||
</networkInterfaceSet>
|
||||
</item>
|
||||
</instancesSet>
|
||||
</item>
|
||||
</reservationSet>
|
||||
</DescribeInstancesResponse>
|
||||
`
|
||||
|
||||
return ctx.SendString(response)
|
||||
}
|
||||
|
||||
func (gw *GatewayConfig) EC2_RunInstances(ctx *fiber.Ctx, args map[string]string) error {
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (gw *GatewayConfig) EC2_CreateKeyPair(ctx *fiber.Ctx, args map[string]string) error {
|
||||
slog.Info("EC2 CreateKeyPair called")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/logger"
|
||||
"github.com/mulgadc/predastore/s3"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type GatewayConfig struct {
|
||||
Debug bool `json:"debug"`
|
||||
DisableLogging bool `json:"disable_logging"`
|
||||
}
|
||||
|
||||
var supportedServices = map[string]bool{
|
||||
"ec2": true,
|
||||
"iam": true,
|
||||
"account": true,
|
||||
}
|
||||
|
||||
func (gw *GatewayConfig) SetupRoutes() *fiber.App {
|
||||
|
||||
var logLevel slog.Level
|
||||
|
||||
if gw.Debug {
|
||||
logLevel = slog.LevelDebug
|
||||
} else if gw.DisableLogging {
|
||||
logLevel = slog.LevelError
|
||||
} else {
|
||||
logLevel = slog.LevelInfo
|
||||
}
|
||||
|
||||
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: logLevel,
|
||||
})
|
||||
|
||||
// Create a new logger with the custom handler
|
||||
slogger := slog.New(handler)
|
||||
|
||||
// Set it as the default logger
|
||||
slog.SetDefault(slogger)
|
||||
|
||||
// Configure slog for logging
|
||||
slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
|
||||
app := fiber.New(fiber.Config{
|
||||
|
||||
// Disable the startup banner
|
||||
DisableStartupMessage: gw.DisableLogging,
|
||||
|
||||
// Set the body limit for S3 specs to 5GiB
|
||||
//BodyLimit: 5 * 1024 * 1024 * 1024,
|
||||
|
||||
// Override default error handler
|
||||
ErrorHandler: func(ctx *fiber.Ctx, err error) error {
|
||||
return gw.ErrorHandler(ctx, err)
|
||||
}},
|
||||
)
|
||||
|
||||
if !gw.DisableLogging {
|
||||
app.Use(logger.New())
|
||||
}
|
||||
|
||||
s3 := s3.New(&s3.Config{})
|
||||
|
||||
// TODO: Support env var for config path, and external IAM
|
||||
s3.ConfigPath = "config/awsgw/awsgw.toml"
|
||||
|
||||
err := s3.ReadConfig()
|
||||
|
||||
if err != nil {
|
||||
slog.Error("Error reading config", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add authentication middleware for all requests
|
||||
app.Use(s3.SigV4AuthMiddleware)
|
||||
|
||||
// Define routes
|
||||
app.Get("/*", func(c *fiber.Ctx) error {
|
||||
|
||||
return gw.Request(c)
|
||||
})
|
||||
|
||||
app.Post("/*", func(c *fiber.Ctx) error {
|
||||
|
||||
return gw.Request(c)
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// Note, custom endpoints can be configured via ENV vars to the AWS SDK/CLI tool, with individual endpoints depending the service
|
||||
// AWS_ENDPOINT_URL_EC2=https://localhost:9999/ aws --no-verify-ssl ec2 describe-instances
|
||||
// aws --endpoint-url https://localhost:9999/ --no-verify-ssl eks list-clusters
|
||||
// AWS_ENDPOINT_URL=https://localhost:9999/ aws --no-verify-ssl ec2 describe-instances
|
||||
|
||||
func (gw *GatewayConfig) Request(ctx *fiber.Ctx) error {
|
||||
|
||||
// Route the request to the appropriate endpoint (e.g EC2, IAM, etc)
|
||||
|
||||
// Determine the service from the Authorization header
|
||||
// "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20250930/ap-southeast-2/account/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=adb8183545a9d6f5b626908582b4ca5f9a309137b1d4bcc802f0e79ede7af7bc"
|
||||
|
||||
authHeader := ctx.Get("Authorization")
|
||||
|
||||
parts := strings.Split(authHeader, ", ")
|
||||
if len(parts) != 3 {
|
||||
slog.Debug("Invalid Authorization header format")
|
||||
return errors.New("AccessDenied")
|
||||
}
|
||||
|
||||
// Parse credential
|
||||
creds := strings.Split(strings.TrimPrefix(parts[0], "AWS4-HMAC-SHA256 Credential="), "/")
|
||||
if len(creds) != 5 {
|
||||
slog.Debug("Invalid credential scope")
|
||||
return errors.New("AccessDenied")
|
||||
}
|
||||
|
||||
svc := creds[3]
|
||||
|
||||
if !supportedServices[svc] {
|
||||
slog.Debug("Unsupported service", "service", svc)
|
||||
return errors.New("UnsupportedOperation")
|
||||
}
|
||||
|
||||
slog.Info("Request", "service", svc, "method", ctx.Method(), "path", ctx.Path())
|
||||
|
||||
switch svc {
|
||||
case "ec2":
|
||||
return gw.EC2_Request(ctx)
|
||||
case "account":
|
||||
return gw.Account_Request(ctx)
|
||||
case "iam":
|
||||
return gw.IAM_Request(ctx)
|
||||
default:
|
||||
return errors.New("UnsupportedOperation")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (gw *GatewayConfig) ErrorHandler(ctx *fiber.Ctx, err error) error {
|
||||
// TODO: Support service type specific errors (e.g EC2, S3, etc)
|
||||
// Status code defaults to 500
|
||||
httpCode := fiber.StatusInternalServerError
|
||||
var s3error s3.S3Error
|
||||
var e *fiber.Error
|
||||
|
||||
// Check for specific error types
|
||||
switch {
|
||||
case strings.Contains(err.Error(), "NoSuchBucket") || strings.Contains(err.Error(), "Bucket not found"):
|
||||
// File or bucket not found
|
||||
httpCode = fiber.StatusNotFound
|
||||
s3error.Code = "NoSuchBucket"
|
||||
s3error.Message = "The specified bucket does not exist"
|
||||
|
||||
case strings.Contains(err.Error(), "AccessDenied") || strings.Contains(err.Error(), "Not enough permissions"):
|
||||
// Permission error
|
||||
httpCode = fiber.StatusForbidden
|
||||
s3error.Code = "AccessDenied"
|
||||
s3error.Message = "Access Denied"
|
||||
|
||||
case strings.Contains(err.Error(), "NoSuchObject") || strings.Contains(err.Error(), "not found") ||
|
||||
errors.Is(err, os.ErrNotExist):
|
||||
// File not found
|
||||
httpCode = fiber.StatusNotFound
|
||||
s3error.Code = "NoSuchKey"
|
||||
s3error.Message = "The specified key does not exist"
|
||||
|
||||
case strings.Contains(err.Error(), "Invalid signature") || strings.Contains(err.Error(), "Invalid access key"):
|
||||
// Authentication error
|
||||
httpCode = fiber.StatusForbidden
|
||||
s3error.Code = "SignatureDoesNotMatch"
|
||||
s3error.Message = "The request signature does not match"
|
||||
|
||||
case strings.Contains(err.Error(), "Missing Authorization header"):
|
||||
// Missing auth header
|
||||
httpCode = fiber.StatusForbidden
|
||||
s3error.Code = "AccessDenied"
|
||||
s3error.Message = "Access Denied"
|
||||
|
||||
case strings.Contains(err.Error(), "UnsupportedOperation"):
|
||||
// Unsupported operation
|
||||
httpCode = fiber.StatusBadRequest
|
||||
s3error.Code = "UnsupportedOperation"
|
||||
s3error.Message = "The operation is not supported"
|
||||
ctx.Set("X-Amzn-Errortype", "UnsupportedOperationException")
|
||||
|
||||
case errors.As(err, &e):
|
||||
httpCode = e.Code
|
||||
s3error.Message = e.Message
|
||||
s3error.Code = "InternalError"
|
||||
default:
|
||||
s3error.Code = "InternalError"
|
||||
s3error.Message = err.Error()
|
||||
}
|
||||
|
||||
// Add request ID and host ID
|
||||
|
||||
s3error.RequestId = ctx.GetRespHeader("x-amz-request-id", uuid.NewString())
|
||||
s3error.HostId = ctx.Hostname()
|
||||
|
||||
// Set standard S3 error response headers
|
||||
ctx.Set("Content-Type", "application/xml")
|
||||
|
||||
return ctx.Status(httpCode).XML(s3error)
|
||||
}
|
||||
|
||||
// Parse AWS query arguments (used by some services like EC2/S3)
|
||||
func parseAWSQueryArgs(query string) map[string]string {
|
||||
params := make(map[string]string)
|
||||
pairs := strings.Split(query, "&")
|
||||
for _, pair := range pairs {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) == 2 {
|
||||
params[kv[0]] = kv[1]
|
||||
} else if len(kv) == 1 {
|
||||
params[kv[0]] = ""
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package gateway
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (gw *GatewayConfig) IAM_Request(ctx *fiber.Ctx) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mulgadc/hive/hive/services/awsgw"
|
||||
"github.com/mulgadc/hive/hive/services/hive"
|
||||
"github.com/mulgadc/hive/hive/services/nats"
|
||||
"github.com/mulgadc/hive/hive/services/predastore"
|
||||
@@ -20,17 +21,20 @@ type Service interface {
|
||||
func New(btype string, config interface{}) (Service, error) {
|
||||
|
||||
switch btype {
|
||||
case "nats":
|
||||
return nats.New(config)
|
||||
|
||||
case "predastore":
|
||||
return predastore.New(config)
|
||||
|
||||
case "nats":
|
||||
return nats.New(config)
|
||||
case "viperblock":
|
||||
return viperblockd.New(config)
|
||||
|
||||
case "hive":
|
||||
return hive.New(config)
|
||||
|
||||
case "viperblock":
|
||||
return viperblockd.New(config)
|
||||
case "awsgw":
|
||||
return awsgw.New(config)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package awsgw
|
||||
|
||||
import (
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/mulgadc/hive/hive/config"
|
||||
"github.com/mulgadc/hive/hive/gateway"
|
||||
"github.com/mulgadc/hive/hive/utils"
|
||||
)
|
||||
|
||||
var serviceName = "awsgw"
|
||||
|
||||
type Service struct {
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
func New(cfg interface{}) (svc *Service, err error) {
|
||||
svc = &Service{
|
||||
Config: cfg.(*config.Config),
|
||||
}
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func (svc *Service) Start() (int, error) {
|
||||
utils.WritePidFile(serviceName, os.Getpid())
|
||||
err := launchService(svc.Config)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return os.Getpid(), nil
|
||||
}
|
||||
|
||||
func (svc *Service) Stop() (err error) {
|
||||
err = utils.StopProcess(serviceName)
|
||||
return err
|
||||
}
|
||||
|
||||
func (svc *Service) Status() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (svc *Service) Shutdown() (err error) {
|
||||
return svc.Stop()
|
||||
}
|
||||
|
||||
func (svc *Service) Reload() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func launchService(config *config.Config) (err error) {
|
||||
|
||||
/*
|
||||
d := daemon.NewDaemon(config)
|
||||
slog.Info("Starting AWS-GW daemon ...")
|
||||
err = d.Start()
|
||||
*/
|
||||
|
||||
gw := gateway.GatewayConfig{
|
||||
Debug: config.AWSGW.Debug,
|
||||
DisableLogging: false,
|
||||
}
|
||||
|
||||
app := gw.SetupRoutes()
|
||||
|
||||
if err != nil {
|
||||
slog.Warn("Failed to start AWS-GW daemon: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Fatal(app.ListenTLS(config.AWSGW.Host, config.AWSGW.TLSCert, config.AWSGW.TLSKey))
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
+17
-2
@@ -196,13 +196,28 @@ export HIVE_WAL_DIR=$WAL_DIR
|
||||
#HIVE_CMD="./bin/hive service hive start --config config/hive.toml --base-dir $HIVE_BASE_DIR --wal-dir $WAL_DIR"
|
||||
HIVE_CMD="./bin/hive service hive start"
|
||||
|
||||
start_service "hive" "$HIVE_CMD"
|
||||
|
||||
# 5️⃣ Start AWS Gateway
|
||||
echo ""
|
||||
echo "4️5️⃣ Starting AWS Gateway..."
|
||||
|
||||
# Use the same base directory as Viperblock for consistency
|
||||
export HIVE_CONFIG_PATH=$CONFIG_DIR/hive.toml
|
||||
export HIVE_AWSGW_HOST="0.0.0.0:9999"
|
||||
export HIVE_AWSGW_TLS_CERT=$CONFIG_DIR/server.pem
|
||||
export HIVE_AWSGW_TLS_KEY=$CONFIG_DIR/server.key
|
||||
|
||||
#HIVE_CMD="air -c .air-hive.toml"
|
||||
#HIVE_CMD="./bin/hive service hive start --config config/hive.toml --base-dir $HIVE_BASE_DIR --wal-dir $WAL_DIR"
|
||||
HIVE_CMD="./bin/hive service awsgw start"
|
||||
|
||||
start_service "awsgw" "$HIVE_CMD"
|
||||
|
||||
echo ""
|
||||
echo "🔗 Service endpoints will be:"
|
||||
echo " - NATS: nats://localhost:4222"
|
||||
echo " - Predastore: https://localhost:8443"
|
||||
echo " - Hive Gateway: https://localhost:9999"
|
||||
echo " - AWS Gateway: https://localhost:9999"
|
||||
echo ""
|
||||
echo "📊 Monitor background service logs:"
|
||||
echo " tail -f $LOGS_DIR/*.log"
|
||||
|
||||
Reference in New Issue
Block a user