- Change from fx.Provide to fx.Invoke for health registry registration - CoreModule() already provides *health.Registry - Services should register their database checkers with the existing registry - Use fx.Invoke to register database health checkers instead of providing new registry - Fixes duplicate provider error for *health.Registry - All services now build and should start successfully
225 lines
5.5 KiB
Go
225 lines
5.5 KiB
Go
// Package main provides the entry point for the Authz Service.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"git.dcentral.systems/toolz/goplt/internal/di"
|
|
healthpkg "git.dcentral.systems/toolz/goplt/internal/health"
|
|
"git.dcentral.systems/toolz/goplt/internal/infra/database"
|
|
"git.dcentral.systems/toolz/goplt/pkg/config"
|
|
"git.dcentral.systems/toolz/goplt/pkg/logger"
|
|
"git.dcentral.systems/toolz/goplt/pkg/registry"
|
|
"go.uber.org/fx"
|
|
"go.uber.org/zap"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
// grpcServerWrapper wraps the gRPC server for lifecycle management.
|
|
type grpcServerWrapper struct {
|
|
server *grpc.Server
|
|
listener net.Listener
|
|
port int
|
|
logger logger.Logger
|
|
}
|
|
|
|
func (s *grpcServerWrapper) Start() error {
|
|
s.logger.Info("Starting Authz Service gRPC server",
|
|
zap.Int("port", s.port),
|
|
zap.String("addr", s.listener.Addr().String()),
|
|
)
|
|
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
if err := s.server.Serve(s.listener); err != nil {
|
|
errChan <- err
|
|
}
|
|
}()
|
|
|
|
select {
|
|
case err := <-errChan:
|
|
return fmt.Errorf("gRPC server failed to start: %w", err)
|
|
case <-time.After(100 * time.Millisecond):
|
|
s.logger.Info("Authz Service gRPC server started successfully",
|
|
zap.Int("port", s.port),
|
|
)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (s *grpcServerWrapper) Stop(ctx context.Context) error {
|
|
s.logger.Info("Stopping Authz Service gRPC server")
|
|
|
|
stopped := make(chan struct{})
|
|
go func() {
|
|
s.server.GracefulStop()
|
|
close(stopped)
|
|
}()
|
|
|
|
select {
|
|
case <-stopped:
|
|
s.logger.Info("Authz Service gRPC server stopped gracefully")
|
|
return nil
|
|
case <-ctx.Done():
|
|
s.logger.Warn("Authz Service gRPC server stop timeout, forcing stop")
|
|
s.server.Stop()
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
|
|
func (s *grpcServerWrapper) Port() int {
|
|
return s.port
|
|
}
|
|
|
|
func main() {
|
|
// Create DI container
|
|
// Note: CoreModule() is automatically included by NewContainer()
|
|
container := di.NewContainer(
|
|
// Database for authz service (authz schema)
|
|
fx.Provide(func(cfg config.ConfigProvider, log logger.Logger) (*database.Client, error) {
|
|
dsn := cfg.GetString("database.dsn")
|
|
if dsn == "" {
|
|
return nil, fmt.Errorf("database.dsn is required")
|
|
}
|
|
client, err := database.NewClientWithSchema(dsn, "authz")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Run migrations
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
if err := client.Migrate(ctx); err != nil {
|
|
log.Warn("Failed to run migrations",
|
|
zap.Error(err),
|
|
)
|
|
} else {
|
|
log.Info("Database migrations completed for authz service")
|
|
}
|
|
|
|
return client, nil
|
|
}),
|
|
|
|
// Register database health checker with existing health registry
|
|
fx.Invoke(func(registry *healthpkg.Registry, db *database.Client) {
|
|
registry.Register("database", healthpkg.NewDatabaseChecker(db))
|
|
}),
|
|
|
|
// Provide authz service and gRPC server (defined in authz_service_fx.go)
|
|
provideAuthzService(),
|
|
|
|
// Lifecycle hooks
|
|
fx.Invoke(registerLifecycle),
|
|
)
|
|
|
|
// Create root context
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
|
|
// Handle signals
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
|
|
// Start the application
|
|
if err := container.Start(ctx); err != nil {
|
|
log := logger.GetGlobalLogger()
|
|
if log != nil {
|
|
log.Error("Failed to start Authz Service",
|
|
logger.Error(err),
|
|
)
|
|
} else {
|
|
fmt.Fprintf(os.Stderr, "Failed to start Authz Service: %v\n", err)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Wait for interrupt signal
|
|
<-sigChan
|
|
fmt.Println("\nShutting down Authz Service...")
|
|
|
|
// Create shutdown context with timeout
|
|
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer shutdownCancel()
|
|
|
|
// Stop the application
|
|
if err := container.Stop(shutdownCtx); err != nil {
|
|
log := logger.GetGlobalLogger()
|
|
if log != nil {
|
|
log.Error("Error during Authz Service shutdown",
|
|
logger.Error(err),
|
|
)
|
|
} else {
|
|
fmt.Fprintf(os.Stderr, "Error during shutdown: %v\n", err)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Println("Authz Service stopped successfully")
|
|
}
|
|
|
|
// registerLifecycle registers lifecycle hooks for the service.
|
|
func registerLifecycle(
|
|
lc fx.Lifecycle,
|
|
grpcServer *grpcServerWrapper,
|
|
serviceRegistry registry.ServiceRegistry,
|
|
cfg config.ConfigProvider,
|
|
log logger.Logger,
|
|
) {
|
|
lc.Append(fx.Hook{
|
|
OnStart: func(ctx context.Context) error {
|
|
// Start gRPC server
|
|
if err := grpcServer.Start(); err != nil {
|
|
return fmt.Errorf("failed to start gRPC server: %w", err)
|
|
}
|
|
|
|
// Register with service registry
|
|
serviceID := fmt.Sprintf("authz-service-%d", time.Now().Unix())
|
|
host := cfg.GetString("services.authz.host")
|
|
if host == "" {
|
|
host = "localhost"
|
|
}
|
|
port := grpcServer.Port()
|
|
|
|
instance := ®istry.ServiceInstance{
|
|
ID: serviceID,
|
|
Name: "authz-service",
|
|
Address: host,
|
|
Port: port,
|
|
Tags: []string{"grpc", "authz"},
|
|
Metadata: map[string]string{
|
|
"version": "1.0.0",
|
|
"protocol": "grpc",
|
|
},
|
|
}
|
|
|
|
if err := serviceRegistry.Register(ctx, instance); err != nil {
|
|
log.Warn("Failed to register with service registry",
|
|
zap.Error(err),
|
|
)
|
|
} else {
|
|
log.Info("Registered Authz Service with service registry",
|
|
zap.String("service_id", serviceID),
|
|
zap.String("name", instance.Name),
|
|
zap.Int("port", port),
|
|
)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
OnStop: func(ctx context.Context) error {
|
|
// Stop gRPC server
|
|
if err := grpcServer.Stop(ctx); err != nil {
|
|
return fmt.Errorf("failed to stop gRPC server: %w", err)
|
|
}
|
|
return nil
|
|
},
|
|
})
|
|
}
|