feat: backend service

This commit is contained in:
Ilia Denisov
2026-05-06 10:14:55 +03:00
committed by GitHub
parent 3e2622757e
commit f446c6a2ac
1486 changed files with 49720 additions and 266401 deletions
+166
View File
@@ -0,0 +1,166 @@
package testenv
import (
"context"
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"path/filepath"
"testing"
"time"
"github.com/testcontainers/testcontainers-go"
tcnetwork "github.com/testcontainers/testcontainers-go/network"
"github.com/testcontainers/testcontainers-go/wait"
)
// GatewayContainer wraps a running galaxy/gateway:integration
// container.
type GatewayContainer struct {
Container testcontainers.Container
HTTPHost string
HTTPPort int
HTTPURL string
GRPCHost string
GRPCPort int
GRPCAddr string
// ResponseSignerPublic is the Ed25519 public key the gateway uses
// to sign responses and push events. Tests verify signatures
// against this value.
ResponseSignerPublic ed25519.PublicKey
}
// GatewayOptions tunes a gateway container before it boots.
type GatewayOptions struct {
NetworkAlias string
NetworkName string
BackendHTTPURL string
BackendGRPCURL string
RedisAddr string
GatewayClientID string
Extra map[string]string
}
// StartGateway boots galaxy/gateway:integration with the supplied
// options.
func StartGateway(t *testing.T, opts GatewayOptions) *GatewayContainer {
t.Helper()
EnsureGatewayImage(t)
if opts.NetworkAlias == "" {
opts.NetworkAlias = "gateway"
}
if opts.GatewayClientID == "" {
opts.GatewayClientID = "integration-gateway"
}
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("generate ed25519 key: %v", err)
}
keyDER, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
t.Fatalf("marshal ed25519 key: %v", err)
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})
keyPath := filepath.Join(t.TempDir(), "gateway-signer.pem")
if err := writeFile(keyPath, keyPEM); err != nil {
t.Fatalf("write signer key: %v", err)
}
containerKey := "/etc/galaxy/gateway-signer.pem"
env := map[string]string{
"GATEWAY_PUBLIC_HTTP_ADDR": ":8080",
"GATEWAY_AUTHENTICATED_GRPC_ADDR": ":9090",
"GATEWAY_LOG_LEVEL": "debug",
"GATEWAY_REDIS_MASTER_ADDR": opts.RedisAddr,
"GATEWAY_REDIS_PASSWORD": RedisIntegrationPassword,
"GATEWAY_BACKEND_HTTP_URL": opts.BackendHTTPURL,
"GATEWAY_BACKEND_GRPC_PUSH_URL": opts.BackendGRPCURL,
"GATEWAY_BACKEND_GATEWAY_CLIENT_ID": opts.GatewayClientID,
"GATEWAY_RESPONSE_SIGNER_PRIVATE_KEY_PEM_PATH": containerKey,
// Loosen anti-abuse so happy-path scenarios aren't rate-limited.
// Negative-path edge tests tighten these per-test.
"GATEWAY_PUBLIC_HTTP_ANTI_ABUSE_PUBLIC_AUTH_RATE_LIMIT_REQUESTS": "10000",
"GATEWAY_PUBLIC_HTTP_ANTI_ABUSE_PUBLIC_AUTH_RATE_LIMIT_BURST": "1000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_IP_RATE_LIMIT_REQUESTS": "10000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_IP_RATE_LIMIT_BURST": "1000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_SESSION_RATE_LIMIT_REQUESTS": "10000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_SESSION_RATE_LIMIT_BURST": "1000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_USER_RATE_LIMIT_REQUESTS": "10000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_USER_RATE_LIMIT_BURST": "1000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_MESSAGE_CLASS_RATE_LIMIT_REQUESTS": "10000",
"GATEWAY_AUTHENTICATED_GRPC_ANTI_ABUSE_MESSAGE_CLASS_RATE_LIMIT_BURST": "1000",
}
for k, v := range opts.Extra {
env[k] = v
}
req := testcontainers.ContainerRequest{
Image: GatewayImage,
ExposedPorts: []string{"8080/tcp", "9090/tcp"},
Env: env,
WaitingFor: wait.ForHTTP("/healthz").
WithPort("8080/tcp").
WithStartupTimeout(60 * time.Second),
Files: []testcontainers.ContainerFile{
{
HostFilePath: keyPath,
ContainerFilePath: containerKey,
// 0o444 so the distroless `nonroot` user (uid 65532)
// inside the gateway image can read the integration
// signer key. The key is ephemeral and never leaves
// the test process, so widening the mode is safe.
FileMode: 0o444,
},
},
}
gcr := &testcontainers.GenericContainerRequest{ContainerRequest: req}
if opts.NetworkName != "" {
_ = tcnetwork.WithNetwork([]string{opts.NetworkAlias}, &testcontainers.DockerNetwork{Name: opts.NetworkName}).Customize(gcr)
}
gcr.Started = true
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer cancel()
container, err := testcontainers.GenericContainer(ctx, *gcr)
if err != nil {
t.Fatalf("start gateway container: %v", err)
}
t.Cleanup(func() {
if err := testcontainers.TerminateContainer(container); err != nil {
t.Logf("terminate gateway: %v", err)
}
})
host, err := container.Host(ctx)
if err != nil {
t.Fatalf("gateway host: %v", err)
}
port, err := container.MappedPort(ctx, "8080/tcp")
if err != nil {
t.Fatalf("gateway port: %v", err)
}
grpcPort, err := container.MappedPort(ctx, "9090/tcp")
if err != nil {
t.Fatalf("gateway grpc port: %v", err)
}
return &GatewayContainer{
Container: container,
HTTPHost: host,
HTTPPort: int(port.Num()),
HTTPURL: fmt.Sprintf("http://%s:%d", host, port.Num()),
GRPCHost: host,
GRPCPort: int(grpcPort.Num()),
GRPCAddr: fmt.Sprintf("%s:%d", host, grpcPort.Num()),
ResponseSignerPublic: pub,
}
}
func writeFile(path string, content []byte) error {
return writeFileFn(path, content)
}