feat: add database-backed IP allocation with fallback to IPAM service
Add FindNextAvailableIP repository method to query ip_allocations table and find next available IP address within gateway VPN CIDR range. Query existing allocations from database and build used IP map. Iterate through CIDR range starting at offset to find first unused address. Update Enroll service method to call FindNextAvailableIP first with fallback to IPAM service Allocate method on error. Add netip and errors imports to repository
This commit is contained in:
@@ -2,6 +2,8 @@ package device
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -9,6 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
|
FindNextAvailableIP(ctx context.Context, gatewayID uuid.UUID, vpnCIDR string, startOffset int) (string, error)
|
||||||
Enroll(ctx context.Context, userID uuid.UUID, gatewayID uuid.UUID, input EnrollRequest, assignedIP string, dnsServers []string, allowedIPs []string) (EnrollmentResponse, error)
|
Enroll(ctx context.Context, userID uuid.UUID, gatewayID uuid.UUID, input EnrollRequest, assignedIP string, dnsServers []string, allowedIPs []string) (EnrollmentResponse, error)
|
||||||
ListByUser(ctx context.Context, userID uuid.UUID) ([]Device, error)
|
ListByUser(ctx context.Context, userID uuid.UUID) ([]Device, error)
|
||||||
ListAll(ctx context.Context) ([]Device, error)
|
ListAll(ctx context.Context) ([]Device, error)
|
||||||
@@ -26,6 +29,49 @@ func NewPGRepository(db *pgxpool.Pool) *PGRepository {
|
|||||||
return &PGRepository{db: db}
|
return &PGRepository{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *PGRepository) FindNextAvailableIP(ctx context.Context, gatewayID uuid.UUID, vpnCIDR string, startOffset int) (string, error) {
|
||||||
|
prefix, err := netip.ParsePrefix(vpnCIDR)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := r.db.Query(ctx, `
|
||||||
|
select host(address)
|
||||||
|
from ip_allocations
|
||||||
|
where gateway_id = $1
|
||||||
|
`, gatewayID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
used := map[string]struct{}{}
|
||||||
|
for rows.Next() {
|
||||||
|
var address string
|
||||||
|
if err := rows.Scan(&address); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
used[address] = struct{}{}
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
address := prefix.Addr().Next()
|
||||||
|
for i := 1; i < startOffset; i++ {
|
||||||
|
address = address.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
for prefix.Contains(address) {
|
||||||
|
if _, exists := used[address.String()]; !exists {
|
||||||
|
return address.String() + "/32", nil
|
||||||
|
}
|
||||||
|
address = address.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("no available ip addresses for gateway")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *PGRepository) Enroll(ctx context.Context, userID uuid.UUID, gatewayID uuid.UUID, input EnrollRequest, assignedIP string, dnsServers []string, allowedIPs []string) (EnrollmentResponse, error) {
|
func (r *PGRepository) Enroll(ctx context.Context, userID uuid.UUID, gatewayID uuid.UUID, input EnrollRequest, assignedIP string, dnsServers []string, allowedIPs []string) (EnrollmentResponse, error) {
|
||||||
tx, err := r.db.Begin(ctx)
|
tx, err := r.db.Begin(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -33,9 +33,12 @@ func (s *Service) Enroll(ctx context.Context, userID uuid.UUID, input EnrollRequ
|
|||||||
return EnrollmentResponse{}, err
|
return EnrollmentResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
assignedIP, err := s.ipamService.Allocate(selectedGateway.VPNCIDR, 10)
|
assignedIP, err := s.repo.FindNextAvailableIP(ctx, selectedGateway.ID, selectedGateway.VPNCIDR, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return EnrollmentResponse{}, err
|
assignedIP, err = s.ipamService.Allocate(selectedGateway.VPNCIDR, 10)
|
||||||
|
if err != nil {
|
||||||
|
return EnrollmentResponse{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destinations, err := s.policyService.ResolveDestinations(ctx, userID, nil)
|
destinations, err := s.policyService.ResolveDestinations(ctx, userID, nil)
|
||||||
|
|||||||
Reference in New Issue
Block a user