feat: implement AS holder name resolution and enhance ASEntry structure. Add ASHolderName function to retrieve organization names from RIPEstat, update ASEntry model to include ASN name, prefix count, and resolution timestamp. Modify database interactions and API responses to support new fields, improving ASN metadata handling.
CI / changes (push) Successful in 5s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 24s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m9s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m3s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 16s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m0s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 1m37s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m25s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m29s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m25s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m21s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m24s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m31s

This commit is contained in:
Denozordec
2026-04-06 01:49:31 +07:00
parent 80965adc0c
commit c958d5af0d
13 changed files with 233 additions and 16 deletions
+62 -10
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"strings"
"time"
"evobgp/internal/store"
@@ -164,7 +165,8 @@ func (p *Postgres) ListASEntries(tenantID, moduleID string) ([]*store.ASEntry, e
}
ctx := context.Background()
rows, err := p.pool.Query(ctx, `
SELECT id::text, asn, community_id::text FROM module_as_entry WHERE module_id=$1`, moduleID)
SELECT id::text, asn, community_id::text, asn_name, prefix_count, asn_resolved_at
FROM module_as_entry WHERE module_id=$1 ORDER BY asn`, moduleID)
if err != nil {
return nil, err
}
@@ -173,11 +175,18 @@ func (p *Postgres) ListASEntries(tenantID, moduleID string) ([]*store.ASEntry, e
for rows.Next() {
var e store.ASEntry
e.ModuleID = moduleID
var comm *string
if err := rows.Scan(&e.ID, &e.ASN, &comm); err != nil {
var comm, asnName *string
var pc *int64
var at *time.Time
if err := rows.Scan(&e.ID, &e.ASN, &comm, &asnName, &pc, &at); err != nil {
continue
}
e.CommunityID = strOrNil(comm)
if asnName != nil {
e.ASNName = *asnName
}
e.PrefixCount = pc
e.ASNResolvedAt = at
out = append(out, &e)
}
return out, nil
@@ -209,14 +218,22 @@ func (p *Postgres) CreateASEntry(tenantID, moduleID string, in *store.ASEntry) (
func (p *Postgres) getASEntry(ctx context.Context, moduleID, id string) (*store.ASEntry, error) {
var e store.ASEntry
e.ModuleID = moduleID
var comm *string
var comm, asnName *string
var pc *int64
var at *time.Time
err := p.pool.QueryRow(ctx, `
SELECT id::text, asn, community_id::text FROM module_as_entry WHERE id=$1 AND module_id=$2`, id, moduleID).Scan(
&e.ID, &e.ASN, &comm)
SELECT id::text, asn, community_id::text, asn_name, prefix_count, asn_resolved_at
FROM module_as_entry WHERE id=$1 AND module_id=$2`, id, moduleID).Scan(
&e.ID, &e.ASN, &comm, &asnName, &pc, &at)
if err != nil {
return nil, err
}
e.CommunityID = strOrNil(comm)
if asnName != nil {
e.ASNName = *asnName
}
e.PrefixCount = pc
e.ASNResolvedAt = at
return &e, nil
}
@@ -231,6 +248,7 @@ func (p *Postgres) UpdateASEntry(tenantID, moduleID, entryID string, patch *stor
}
return nil, err
}
prevASN := cur.ASN
if patch.ASN != nil {
cur.ASN = *patch.ASN
}
@@ -245,17 +263,51 @@ func (p *Postgres) UpdateASEntry(tenantID, moduleID, entryID string, patch *stor
if !store.ValidASN(cur.ASN) {
return nil, store.ErrInvalidInput
}
clearResolve := patch.ASN != nil && cur.ASN != prevASN
ctx := context.Background()
_, err = p.pool.Exec(ctx, `
UPDATE module_as_entry SET asn=$3, community_id=NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid), updated_at=now()
WHERE id=$1 AND module_id=$2`,
entryID, moduleID, cur.ASN, uuidOrNilPtr(cur.CommunityID))
if clearResolve {
_, err = p.pool.Exec(ctx, `
UPDATE module_as_entry SET asn=$3, community_id=NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid),
asn_name=NULL, prefix_count=NULL, asn_resolved_at=NULL, updated_at=now()
WHERE id=$1 AND module_id=$2`,
entryID, moduleID, cur.ASN, uuidOrNilPtr(cur.CommunityID))
} else {
_, err = p.pool.Exec(ctx, `
UPDATE module_as_entry SET asn=$3, community_id=NULLIF($4::uuid, '00000000-0000-0000-0000-000000000000'::uuid), updated_at=now()
WHERE id=$1 AND module_id=$2`,
entryID, moduleID, cur.ASN, uuidOrNilPtr(cur.CommunityID))
}
if err != nil {
return nil, err
}
return p.getASEntry(ctx, moduleID, entryID)
}
func (p *Postgres) UpdateASEntryResolveMeta(tenantID, moduleID, entryID string, asnName string, prefixCount int64, resolvedAt time.Time) error {
if _, err := p.GetModule(tenantID, moduleID); err != nil {
return err
}
ctx := context.Background()
var nameArg any
sn := strings.TrimSpace(asnName)
if sn == "" {
nameArg = nil
} else {
nameArg = sn
}
tag, err := p.pool.Exec(ctx, `
UPDATE module_as_entry SET asn_name=$3, prefix_count=$4, asn_resolved_at=$5, updated_at=now()
WHERE id=$1 AND module_id=$2`,
entryID, moduleID, nameArg, prefixCount, resolvedAt.UTC())
if err != nil {
return err
}
if tag.RowsAffected() == 0 {
return store.ErrNotFound
}
return nil
}
func (p *Postgres) DeleteASEntry(tenantID, moduleID, entryID string) error {
if _, err := p.GetModule(tenantID, moduleID); err != nil {
return err