Skip to content

Commit ff7432d

Browse files
committed
feat: make discovery work
1 parent 6e3d534 commit ff7432d

File tree

6 files changed

+200
-8
lines changed

6 files changed

+200
-8
lines changed

api/v1alpha1/dnszonediscovery_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,3 @@ type DNSZoneDiscoveryList struct {
7878
func init() {
7979
SchemeBuilder.Register(&DNSZoneDiscovery{}, &DNSZoneDiscoveryList{})
8080
}
81-

cmd/discovery/main.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SPDX-License-Identifier: AGPL-3.0-only
2+
package main
3+
4+
import (
5+
"context"
6+
"encoding/json"
7+
"flag"
8+
"fmt"
9+
"os"
10+
"time"
11+
12+
"go.miloapis.com/dns-operator/internal/discovery"
13+
)
14+
15+
func main() {
16+
domainFlag := flag.String("domain", "", "Zone/domain to discover (e.g. example.com)")
17+
timeoutFlag := flag.Duration("timeout", 15*time.Second, "Timeout for DNS discovery")
18+
flag.Parse()
19+
20+
fmt.Println("discovery CLI starting")
21+
domain := *domainFlag
22+
if domain == "" {
23+
// Accept positional arg as fallback: cmd/discovery <domain>
24+
if flag.NArg() > 0 {
25+
domain = flag.Arg(0)
26+
}
27+
}
28+
if domain == "" {
29+
fmt.Fprintln(os.Stderr, "error: domain is required\nUsage: discovery -domain example.com [-timeout 10s]")
30+
flag.PrintDefaults()
31+
os.Exit(2)
32+
}
33+
34+
fmt.Printf("target domain: %s\n", domain)
35+
ctx, cancel := context.WithTimeout(context.Background(), *timeoutFlag)
36+
defer cancel()
37+
38+
start := time.Now()
39+
type result struct {
40+
sets []interface{}
41+
err error
42+
}
43+
resCh := make(chan result, 1)
44+
go func() {
45+
rs, err := discovery.DiscoverZoneRecords(context.Background(), domain)
46+
asAny := make([]interface{}, len(rs))
47+
for i := range rs {
48+
asAny[i] = rs[i]
49+
}
50+
resCh <- result{sets: asAny, err: err}
51+
}()
52+
53+
var (
54+
recordSetsAny []interface{}
55+
err error
56+
)
57+
select {
58+
case r := <-resCh:
59+
recordSetsAny, err = r.sets, r.err
60+
case <-ctx.Done():
61+
elapsed := time.Since(start)
62+
fmt.Fprintf(os.Stderr, "discovery timed out for %q after %s\n", domain, elapsed)
63+
os.Exit(1)
64+
}
65+
elapsed := time.Since(start)
66+
if err != nil {
67+
fmt.Fprintf(os.Stderr, "discovery failed for %q after %s: %v\n", domain, elapsed, err)
68+
os.Exit(1)
69+
}
70+
71+
fmt.Printf("discovery succeeded for %q in %s\n", domain, elapsed)
72+
fmt.Printf("discovered record set count: %d\n", len(recordSetsAny))
73+
74+
enc := json.NewEncoder(os.Stdout)
75+
enc.SetIndent("", " ")
76+
if err := enc.Encode(recordSetsAny); err != nil {
77+
fmt.Fprintf(os.Stderr, "failed to encode results: %v\n", err)
78+
os.Exit(1)
79+
}
80+
}

discovery.yaml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
apiVersion: dns.networking.miloapis.com/v1alpha1
2+
kind: DNSZoneDiscovery
3+
metadata:
4+
annotations:
5+
kubectl.kubernetes.io/last-applied-configuration: |
6+
{"apiVersion":"dns.networking.miloapis.com/v1alpha1","kind":"DNSZoneDiscovery","metadata":{"annotations":{},"labels":{"app.kubernetes.io/managed-by":"kustomize","app.kubernetes.io/name":"dns-operator"},"name":"www-example-com","namespace":"default"},"spec":{"dnsZoneRef":{"name":"example-com"}}}
7+
creationTimestamp: "2025-11-18T01:56:04Z"
8+
generation: 1
9+
labels:
10+
app.kubernetes.io/managed-by: kustomize
11+
app.kubernetes.io/name: dns-operator
12+
name: www-example-com
13+
namespace: default
14+
resourceVersion: "2310"
15+
uid: 3585b686-a326-4c6b-88d2-abc699c18724
16+
spec:
17+
dnsZoneRef:
18+
name: example-com
19+
status:
20+
conditions:
21+
- lastTransitionTime: "2025-11-18T01:56:04Z"
22+
message: discovery accepted
23+
observedGeneration: 1
24+
reason: Accepted
25+
status: "True"
26+
type: Accepted
27+
- lastTransitionTime: "2025-11-18T01:56:04Z"
28+
message: zone records discovered
29+
observedGeneration: 1
30+
reason: Discovered
31+
status: "True"
32+
type: Discovered
33+
recordSets:
34+
- recordType: MX
35+
records:
36+
- mx:
37+
exchange: .
38+
preference: 0
39+
name: '@'
40+
ttl: 27906
41+
- recordType: A
42+
records:
43+
- a:
44+
content: 23.192.228.84
45+
name: '@'
46+
ttl: 293
47+
- a:
48+
content: 23.220.75.245
49+
name: '@'
50+
ttl: 293
51+
- a:
52+
content: 23.215.0.138
53+
name: '@'
54+
ttl: 293
55+
- a:
56+
content: 23.192.228.80
57+
name: '@'
58+
ttl: 293
59+
- a:
60+
content: 23.215.0.136
61+
name: '@'
62+
ttl: 293
63+
- a:
64+
content: 23.220.75.232
65+
name: '@'
66+
ttl: 293
67+
- recordType: AAAA
68+
records:
69+
- aaaa:
70+
content: 2600:1406:5e00:6::17ce:bc12
71+
name: '@'
72+
ttl: 15
73+
- aaaa:
74+
content: 2600:1406:5e00:6::17ce:bc1b
75+
name: '@'
76+
ttl: 15
77+
- aaaa:
78+
content: 2600:1406:bc00:53::b81e:94ce
79+
name: '@'
80+
ttl: 15
81+
- aaaa:
82+
content: 2600:1408:ec00:36::1736:7f24
83+
name: '@'
84+
ttl: 15
85+
- aaaa:
86+
content: 2600:1406:bc00:53::b81e:94c8
87+
name: '@'
88+
ttl: 15
89+
- aaaa:
90+
content: 2600:1408:ec00:36::1736:7f31
91+
name: '@'
92+
ttl: 15
93+
- recordType: TXT
94+
records:
95+
- name: '@'
96+
ttl: 27495
97+
txt:
98+
content: v=spf1 -all
99+
- name: '@'
100+
ttl: 27495
101+
txt:
102+
content: _k2n1y4vw3qtb4skdx9e7dxt97qrmmq9

internal/controller/dnszonediscovery_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (r *DNSZoneDiscoveryReplicator) Reconcile(ctx context.Context, req mcreconc
7878
// Mark Accepted true if not already
7979
if getCondStatus(dzd.Status.Conditions, CondAccepted) != metav1.ConditionTrue {
8080
base := dzd.DeepCopy()
81-
if setCond(&dzd.Status.Conditions, CondAccepted, ReasonAccepted, "discovery accepted", metav1.ConditionTrue, dzd.Generation) {
81+
if setCond(&dzd.Status.Conditions, CondAccepted, ReasonAccepted, "Discovery Accepted", metav1.ConditionTrue, dzd.Generation) {
8282
if err := upstreamCluster.GetClient().Status().Patch(ctx, &dzd, client.MergeFrom(base)); err != nil {
8383
return ctrl.Result{}, err
8484
}
@@ -97,7 +97,7 @@ func (r *DNSZoneDiscoveryReplicator) Reconcile(ctx context.Context, req mcreconc
9797

9898
base := dzd.DeepCopy()
9999
dzd.Status.RecordSets = recordSets
100-
_ = setCond(&dzd.Status.Conditions, CondDiscovered, ReasonDiscovered, "zone records discovered", metav1.ConditionTrue, dzd.Generation)
100+
_ = setCond(&dzd.Status.Conditions, CondDiscovered, ReasonDiscovered, "Zone Records Discovered", metav1.ConditionTrue, dzd.Generation)
101101
if err := upstreamCluster.GetClient().Status().Patch(ctx, &dzd, client.MergeFrom(base)); err != nil {
102102
return ctrl.Result{}, err
103103
}

internal/discovery/resolver.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
// DiscoverZoneRecords performs best-effort discovery of common RR types for the given domain
1414
// and returns RecordSets grouped by RecordType, with typed fields populated where available.
1515
func DiscoverZoneRecords(ctx context.Context, domain string) ([]dnsv1alpha1.DiscoveredRecordSet, error) {
16+
fmt.Printf("starting discovery for domain=%q\n", domain)
1617
// Exclude NS and SOA per requirements.
1718
options := dnsx.DefaultOptions
1819
qtypes := []uint16{
@@ -30,19 +31,29 @@ func DiscoverZoneRecords(ctx context.Context, domain string) ([]dnsv1alpha1.Disc
3031
options.QuestionTypes = qtypes
3132
options.QueryAll = true
3233

34+
fmt.Println("creating dnsx client")
3335
client, err := dnsx.New(options)
3436
if err != nil {
3537
return nil, err
3638
}
39+
fmt.Println("querying multiple record types")
3740
resp, err := client.QueryMultiple(domain)
38-
if err != nil || resp == nil || resp.RawResp == nil {
41+
if err != nil {
3942
return nil, err
4043
}
41-
// print all records
42-
fmt.Printf("All records: %+v\n", resp.RawResp)
44+
if resp == nil {
45+
return nil, fmt.Errorf("dnsx returned nil response")
46+
}
47+
// Print quick summary for debugging
48+
fmt.Printf("resolver status=%s answers=%d types=%v\n", resp.StatusCode, len(resp.AllRecords), qtypes)
4349

50+
// Build RRs from textual records since QueryMultiple does not populate RawResp
4451
typeToRRs := make(map[uint16][]dns.RR)
45-
for _, rr := range resp.RawResp.Answer {
52+
for _, rec := range resp.AllRecords {
53+
rr, perr := dns.NewRR(rec)
54+
if perr != nil || rr == nil {
55+
continue
56+
}
4657
rt := rr.Header().Rrtype
4758
typeToRRs[rt] = append(typeToRRs[rt], rr)
4859
}

test/e2e/chainsaw-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ spec:
323323
metadata:
324324
name: example-com
325325
status:
326-
recordCount: 2
326+
recordCount: 3
327327

328328
- name: Delete DNSZone and confirm accounting configmap is deleted
329329
try:

0 commit comments

Comments
 (0)