Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54b7ea3bd5 |
@@ -57,7 +57,11 @@ export function LookupMatchesGrid({
|
|||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<DataGridPrimaryCell
|
<DataGridPrimaryCell
|
||||||
title={row.original.matched_value}
|
title={row.original.matched_value}
|
||||||
subtitle={row.original.match_kind}
|
subtitle={
|
||||||
|
row.original.resolved_ip
|
||||||
|
? `${row.original.match_kind} · via ${row.original.resolved_ip}`
|
||||||
|
: row.original.match_kind
|
||||||
|
}
|
||||||
accent="mono"
|
accent="mono"
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Layers, ListChecks, Radar } from 'lucide-react'
|
import { Globe, Layers, ListChecks, Radar } from 'lucide-react'
|
||||||
|
|
||||||
import { KpiStatGrid, type KpiStatItem } from '@/components/reui-kit'
|
import { KpiStatGrid, type KpiStatItem } from '@/components/reui-kit'
|
||||||
import type { LookupResponse } from '@/types/api'
|
import type { LookupResponse } from '@/types/api'
|
||||||
@@ -10,6 +10,7 @@ import type { LookupResponse } from '@/types/api'
|
|||||||
export function LookupSummaryKpi({ data }: { data: LookupResponse }) {
|
export function LookupSummaryKpi({ data }: { data: LookupResponse }) {
|
||||||
const entryCount = data.matches.filter((m) => m.layer === 'entry').length
|
const entryCount = data.matches.filter((m) => m.layer === 'entry').length
|
||||||
const snapshotCount = data.matches.filter((m) => m.layer === 'snapshot').length
|
const snapshotCount = data.matches.filter((m) => m.layer === 'snapshot').length
|
||||||
|
const resolvedCount = data.resolved_ips?.length ?? 0
|
||||||
|
|
||||||
const items: KpiStatItem[] = [
|
const items: KpiStatItem[] = [
|
||||||
{
|
{
|
||||||
@@ -41,6 +42,17 @@ export function LookupSummaryKpi({ data }: { data: LookupResponse }) {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if (data.query_kind === 'domain') {
|
||||||
|
items.push({
|
||||||
|
id: 'resolved',
|
||||||
|
label: 'DNS IP',
|
||||||
|
value: resolvedCount,
|
||||||
|
hint: resolvedCount > 0 ? data.resolved_ips?.slice(0, 3).join(', ') : 'нет A/AAAA',
|
||||||
|
icon: <Globe aria-hidden />,
|
||||||
|
iconClassName: 'bg-primary text-primary-foreground [&_svg]:text-primary-foreground',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KpiStatGrid
|
<KpiStatGrid
|
||||||
items={items}
|
items={items}
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ export type LookupMatch = {
|
|||||||
community_id?: string | null
|
community_id?: string | null
|
||||||
community?: string
|
community?: string
|
||||||
community_title?: string
|
community_title?: string
|
||||||
|
resolved_ip?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LookupResponse = {
|
export type LookupResponse = {
|
||||||
@@ -178,6 +179,7 @@ export type LookupResponse = {
|
|||||||
matched: boolean
|
matched: boolean
|
||||||
match_count: number
|
match_count: number
|
||||||
matches: LookupMatch[]
|
matches: LookupMatch[]
|
||||||
|
resolved_ips?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Peers ----
|
// ---- Peers ----
|
||||||
|
|||||||
+15
-2
@@ -813,6 +813,11 @@ components:
|
|||||||
community_title:
|
community_title:
|
||||||
type: string
|
type: string
|
||||||
description: Человекочитаемое название community.
|
description: Человекочитаемое название community.
|
||||||
|
resolved_ip:
|
||||||
|
type: string
|
||||||
|
description: |
|
||||||
|
IP, полученный DNS-resolve domain-запроса, из-за которого появился этот матч.
|
||||||
|
Пусто для прямого IP-запроса и для FQDN entry/snapshot без resolve.
|
||||||
|
|
||||||
LookupResponse:
|
LookupResponse:
|
||||||
type: object
|
type: object
|
||||||
@@ -842,6 +847,11 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/LookupMatch"
|
$ref: "#/components/schemas/LookupMatch"
|
||||||
|
resolved_ips:
|
||||||
|
type: array
|
||||||
|
description: IP-адреса после live DNS resolve (только для query_kind=domain; A/AAAA).
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
|
||||||
BgpPeer:
|
BgpPeer:
|
||||||
type: object
|
type: object
|
||||||
@@ -1882,12 +1892,15 @@ paths:
|
|||||||
- **IP** — слой `entry` (`IP_RANGES`, `CIDR.Contains`) и слой `snapshot`
|
- **IP** — слой `entry` (`IP_RANGES`, `CIDR.Contains`) и слой `snapshot`
|
||||||
(все module prefix snapshots, `Prefix.Contains`);
|
(все module prefix snapshots, `Prefix.Contains`);
|
||||||
- **Domain** — слой `entry` (нормализованный FQDN в `DOMAINS`) и слой `snapshot`
|
- **Domain** — слой `entry` (нормализованный FQDN в `DOMAINS`) и слой `snapshot`
|
||||||
(префиксы `source=domain` у matched DOMAINS-модулей, если snapshot есть).
|
(префиксы `source=domain` у matched DOMAINS-модулей, если snapshot есть);
|
||||||
|
затем **live DNS resolve** (A/AAAA через системный резолвер) и проверка
|
||||||
|
каждого полученного IP так же, как для IP-запроса (ranges + все snapshots).
|
||||||
|
|
||||||
Community на матче: `entry.community_id || module.default_community_id` (entry)
|
Community на матче: `entry.community_id || module.default_community_id` (entry)
|
||||||
или `PrefixRow.community_id` (snapshot), с join к справочнику communities.
|
или `PrefixRow.community_id` (snapshot), с join к справочнику communities.
|
||||||
|
|
||||||
Live DoH resolve не выполняется — только уже материализованный snapshot.
|
Поля `resolved_ips` / `resolved_ip` заполняются только для domain-запросов
|
||||||
|
(после успешного DNS). Ошибка DNS не даёт 5xx: FQDN-слой всё равно возвращается.
|
||||||
operationId: lookupMembership
|
operationId: lookupMembership
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: "#/components/parameters/TenantId"
|
- $ref: "#/components/parameters/TenantId"
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (s *Server) handleLookup(w http.ResponseWriter, r *http.Request) {
|
|||||||
writeProblem(w, http.StatusBadRequest, "Bad Request", "query parameter q is required")
|
writeProblem(w, http.StatusBadRequest, "Bad Request", "query parameter q is required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res, err := lookup.Lookup(s.store, a.TenantID, q)
|
res, err := lookup.Lookup(r.Context(), s.store, a.TenantID, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, store.ErrInvalidInput) {
|
if errors.Is(err, store.ErrInvalidInput) {
|
||||||
writeProblem(w, http.StatusBadRequest, "Bad Request", "query must be an IP address or FQDN")
|
writeProblem(w, http.StatusBadRequest, "Bad Request", "query must be an IP address or FQDN")
|
||||||
|
|||||||
+89
-10
@@ -3,7 +3,9 @@
|
|||||||
package lookup
|
package lookup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
@@ -49,20 +51,37 @@ type Match struct {
|
|||||||
CommunityID *string `json:"community_id,omitempty"`
|
CommunityID *string `json:"community_id,omitempty"`
|
||||||
Community string `json:"community,omitempty"`
|
Community string `json:"community,omitempty"`
|
||||||
CommunityTitle string `json:"community_title,omitempty"`
|
CommunityTitle string `json:"community_title,omitempty"`
|
||||||
|
// ResolvedIP is set when the hit came from a DNS-resolved address of a domain query.
|
||||||
|
ResolvedIP string `json:"resolved_ip,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result is the full lookup response payload.
|
// Result is the full lookup response payload.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Query string `json:"query"`
|
Query string `json:"query"`
|
||||||
QueryKind QueryKind `json:"query_kind"`
|
QueryKind QueryKind `json:"query_kind"`
|
||||||
Normalized string `json:"normalized"`
|
Normalized string `json:"normalized"`
|
||||||
Matched bool `json:"matched"`
|
Matched bool `json:"matched"`
|
||||||
MatchCount int `json:"match_count"`
|
MatchCount int `json:"match_count"`
|
||||||
Matches []Match `json:"matches"`
|
Matches []Match `json:"matches"`
|
||||||
|
ResolvedIPs []string `json:"resolved_ips,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DomainResolver resolves a hostname to IP addresses (A/AAAA).
|
||||||
|
type DomainResolver func(ctx context.Context, host string) ([]netip.Addr, error)
|
||||||
|
|
||||||
// Lookup checks whether q (IP or FQDN) is present in tenant lists (entries + snapshots).
|
// Lookup checks whether q (IP or FQDN) is present in tenant lists (entries + snapshots).
|
||||||
func Lookup(st store.Backend, tenantID, q string) (*Result, error) {
|
// For domains, FQDN membership is checked first, then live DNS resolve and IP membership.
|
||||||
|
func Lookup(ctx context.Context, st store.Backend, tenantID, q string) (*Result, error) {
|
||||||
|
return LookupWithResolver(ctx, st, tenantID, q, systemDNSResolver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupWithResolver is like Lookup but uses resolve for domain→IP (tests / alternate DNS).
|
||||||
|
func LookupWithResolver(
|
||||||
|
ctx context.Context,
|
||||||
|
st store.Backend,
|
||||||
|
tenantID, q string,
|
||||||
|
resolve DomainResolver,
|
||||||
|
) (*Result, error) {
|
||||||
raw := strings.TrimSpace(q)
|
raw := strings.TrimSpace(q)
|
||||||
if raw == "" {
|
if raw == "" {
|
||||||
return nil, fmt.Errorf("%w: empty query", store.ErrInvalidInput)
|
return nil, fmt.Errorf("%w: empty query", store.ErrInvalidInput)
|
||||||
@@ -87,7 +106,7 @@ func Lookup(st store.Backend, tenantID, q string) (*Result, error) {
|
|||||||
if addr, err := netip.ParseAddr(raw); err == nil {
|
if addr, err := netip.ParseAddr(raw); err == nil {
|
||||||
out.QueryKind = KindIP
|
out.QueryKind = KindIP
|
||||||
out.Normalized = addr.String()
|
out.Normalized = addr.String()
|
||||||
if err := lookupIP(st, tenantID, addr, out, commByID); err != nil {
|
if err := lookupIP(st, tenantID, addr, out, commByID, ""); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -100,6 +119,12 @@ func Lookup(st store.Backend, tenantID, q string) (*Result, error) {
|
|||||||
if err := lookupDomain(st, tenantID, fqdn, out, commByID); err != nil {
|
if err := lookupDomain(st, tenantID, fqdn, out, commByID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if resolve == nil {
|
||||||
|
resolve = systemDNSResolver
|
||||||
|
}
|
||||||
|
if err := lookupResolvedIPs(ctx, st, tenantID, fqdn, out, commByID, resolve); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out.MatchCount = len(out.Matches)
|
out.MatchCount = len(out.Matches)
|
||||||
@@ -107,7 +132,59 @@ func Lookup(st store.Backend, tenantID, q string) (*Result, error) {
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupIP(st store.Backend, tenantID string, addr netip.Addr, out *Result, commByID map[string]*store.Community) error {
|
func systemDNSResolver(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||||
|
ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return uniqAddrs(ips), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniqAddrs(in []netip.Addr) []netip.Addr {
|
||||||
|
seen := make(map[netip.Addr]struct{}, len(in))
|
||||||
|
out := make([]netip.Addr, 0, len(in))
|
||||||
|
for _, a := range in {
|
||||||
|
a = a.Unmap()
|
||||||
|
if _, ok := seen[a]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[a] = struct{}{}
|
||||||
|
out = append(out, a)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupResolvedIPs(
|
||||||
|
ctx context.Context,
|
||||||
|
st store.Backend,
|
||||||
|
tenantID, fqdn string,
|
||||||
|
out *Result,
|
||||||
|
commByID map[string]*store.Community,
|
||||||
|
resolve DomainResolver,
|
||||||
|
) error {
|
||||||
|
ips, err := resolve(ctx, fqdn)
|
||||||
|
if err != nil {
|
||||||
|
// DNS failure must not hide FQDN-layer matches already collected.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out.ResolvedIPs = make([]string, 0, len(ips))
|
||||||
|
for _, ip := range ips {
|
||||||
|
out.ResolvedIPs = append(out.ResolvedIPs, ip.String())
|
||||||
|
if err := lookupIP(st, tenantID, ip, out, commByID, ip.String()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupIP(
|
||||||
|
st store.Backend,
|
||||||
|
tenantID string,
|
||||||
|
addr netip.Addr,
|
||||||
|
out *Result,
|
||||||
|
commByID map[string]*store.Community,
|
||||||
|
resolvedIP string,
|
||||||
|
) error {
|
||||||
for _, mod := range st.ListModules(tenantID) {
|
for _, mod := range st.ListModules(tenantID) {
|
||||||
if mod == nil {
|
if mod == nil {
|
||||||
continue
|
continue
|
||||||
@@ -137,6 +214,7 @@ func lookupIP(st store.Backend, tenantID string, addr netip.Addr, out *Result, c
|
|||||||
MatchedValue: e.Prefix,
|
MatchedValue: e.Prefix,
|
||||||
EntryID: e.ID,
|
EntryID: e.ID,
|
||||||
CommunityID: resolveCommunityID(e.CommunityID, mod.DefaultCommunityID),
|
CommunityID: resolveCommunityID(e.CommunityID, mod.DefaultCommunityID),
|
||||||
|
ResolvedIP: resolvedIP,
|
||||||
}, commByID))
|
}, commByID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,6 +243,7 @@ func lookupIP(st store.Backend, tenantID string, addr netip.Addr, out *Result, c
|
|||||||
MatchedValue: row.Prefix,
|
MatchedValue: row.Prefix,
|
||||||
Source: row.Source,
|
Source: row.Source,
|
||||||
CommunityID: row.CommunityID,
|
CommunityID: row.CommunityID,
|
||||||
|
ResolvedIP: resolvedIP,
|
||||||
}, commByID))
|
}, commByID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +297,7 @@ func lookupDomain(st store.Backend, tenantID, fqdn string, out *Result, commByID
|
|||||||
}
|
}
|
||||||
out.Matches = append(out.Matches, decorateMatch(Match{
|
out.Matches = append(out.Matches, decorateMatch(Match{
|
||||||
Layer: LayerSnapshot,
|
Layer: LayerSnapshot,
|
||||||
ModuleID: mod.ID,
|
ModuleID: mid,
|
||||||
ModuleName: mod.Name,
|
ModuleName: mod.Name,
|
||||||
ModuleType: mod.Type,
|
ModuleType: mod.Type,
|
||||||
MatchKind: MatchPrefix,
|
MatchKind: MatchPrefix,
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package lookup
|
package lookup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"evobgp/internal/store"
|
"evobgp/internal/store"
|
||||||
@@ -39,7 +41,7 @@ func TestLookupIPEntryAndSnapshot(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := Lookup(m, tenant, "203.0.113.10")
|
res, err := Lookup(context.Background(), m, tenant, "203.0.113.10")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -80,7 +82,7 @@ func TestLookupIPCommunityFallback(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := Lookup(m, tenant, "10.1.2.3")
|
res, err := Lookup(context.Background(), m, tenant, "10.1.2.3")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -134,7 +136,8 @@ func TestLookupDomainEntryAndSnapshot(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := Lookup(m, tenant, "example.com")
|
noDNS := func(context.Context, string) ([]netip.Addr, error) { return nil, nil }
|
||||||
|
res, err := LookupWithResolver(context.Background(), m, tenant, "example.com", noDNS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -165,12 +168,64 @@ func TestLookupDomainEntryAndSnapshot(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLookupDomainResolvedIPAgainstRanges(t *testing.T) {
|
||||||
|
m := store.NewMemory()
|
||||||
|
m.SeedDemo()
|
||||||
|
tenant, _, modIP, _, _ := m.DemoIDs()
|
||||||
|
comms, _ := m.ListCommunities(tenant)
|
||||||
|
cid := comms[0].ID
|
||||||
|
if _, err := m.UpdateModule(tenant, modIP, &store.ModulePatch{DefaultCommunityID: &cid}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := m.CreateIPRangeEntry(tenant, modIP, &store.IPRangeEntry{
|
||||||
|
Prefix: "203.0.113.0/24",
|
||||||
|
CommunityID: &cid,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := m.SetModulePrefixSnapshot(tenant, modIP, "hash-r", []store.PrefixRow{
|
||||||
|
{Prefix: "203.0.113.0/24", CommunityID: &cid, Source: "ip_range"},
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fake := func(_ context.Context, host string) ([]netip.Addr, error) {
|
||||||
|
if host != "google.com" {
|
||||||
|
t.Fatalf("unexpected host %q", host)
|
||||||
|
}
|
||||||
|
return []netip.Addr{netip.MustParseAddr("203.0.113.50")}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := LookupWithResolver(context.Background(), m, tenant, "google.com", fake)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if res.QueryKind != KindDomain {
|
||||||
|
t.Fatalf("kind: %+v", res)
|
||||||
|
}
|
||||||
|
if len(res.ResolvedIPs) != 1 || res.ResolvedIPs[0] != "203.0.113.50" {
|
||||||
|
t.Fatalf("resolved_ips: %+v", res.ResolvedIPs)
|
||||||
|
}
|
||||||
|
if !res.Matched {
|
||||||
|
t.Fatalf("expected IP membership via resolve, got %+v", res)
|
||||||
|
}
|
||||||
|
var viaResolve bool
|
||||||
|
for _, hit := range res.Matches {
|
||||||
|
if hit.ResolvedIP == "203.0.113.50" && hit.MatchedValue == "203.0.113.0/24" {
|
||||||
|
viaResolve = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !viaResolve {
|
||||||
|
t.Fatalf("missing resolved-ip match: %+v", res.Matches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLookupNoMatch(t *testing.T) {
|
func TestLookupNoMatch(t *testing.T) {
|
||||||
m := store.NewMemory()
|
m := store.NewMemory()
|
||||||
m.SeedDemo()
|
m.SeedDemo()
|
||||||
tenant, _, _, _, _ := m.DemoIDs()
|
tenant, _, _, _, _ := m.DemoIDs()
|
||||||
|
|
||||||
res, err := Lookup(m, tenant, "192.0.2.1")
|
res, err := Lookup(context.Background(), m, tenant, "192.0.2.1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -183,16 +238,17 @@ func TestLookupInvalid(t *testing.T) {
|
|||||||
m := store.NewMemory()
|
m := store.NewMemory()
|
||||||
m.SeedDemo()
|
m.SeedDemo()
|
||||||
tenant, _, _, _, _ := m.DemoIDs()
|
tenant, _, _, _, _ := m.DemoIDs()
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
_, err := Lookup(m, tenant, "")
|
_, err := Lookup(ctx, m, tenant, "")
|
||||||
if !errors.Is(err, store.ErrInvalidInput) {
|
if !errors.Is(err, store.ErrInvalidInput) {
|
||||||
t.Fatalf("empty: %v", err)
|
t.Fatalf("empty: %v", err)
|
||||||
}
|
}
|
||||||
_, err = Lookup(m, tenant, "not a host")
|
_, err = Lookup(ctx, m, tenant, "not a host")
|
||||||
if !errors.Is(err, store.ErrInvalidInput) {
|
if !errors.Is(err, store.ErrInvalidInput) {
|
||||||
t.Fatalf("spaces: %v", err)
|
t.Fatalf("spaces: %v", err)
|
||||||
}
|
}
|
||||||
_, err = Lookup(m, tenant, "localhost")
|
_, err = Lookup(ctx, m, tenant, "localhost")
|
||||||
if !errors.Is(err, store.ErrInvalidInput) {
|
if !errors.Is(err, store.ErrInvalidInput) {
|
||||||
t.Fatalf("single label: %v", err)
|
t.Fatalf("single label: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user