Skip to content

Commit c494bbe

Browse files
authored
[bugfix] Correcting the ensure dns records logic to cleanup the right stale records. (#1083)
Co-authored-by: bren-ak <bren-ak@users.noreply.github.com>
1 parent f4e2145 commit c494bbe

4 files changed

Lines changed: 202 additions & 35 deletions

File tree

cloud/services/domains.go

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func EnsureDNSEntries(ctx context.Context, cscope *scope.ClusterScope, operation
5252
}
5353

5454
if cscope.LinodeCluster.Spec.Network.DNSProvider == "akamai" {
55-
if err := deleteStaleAkamaiEntries(ctx, cscope); err != nil {
55+
if err := deleteStaleAkamaiEntries(ctx, cscope, dnsEntries); err != nil {
5656
return err
5757
}
5858
for _, dnsEntry := range dnsEntries {
@@ -69,29 +69,6 @@ func EnsureDNSEntries(ctx context.Context, cscope *scope.ClusterScope, operation
6969
return nil
7070
}
7171

72-
func getMachineIPs(cscope *scope.ClusterScope) (ipv4IPs, ipv6IPs []string, err error) {
73-
for _, eachMachine := range cscope.LinodeMachines.Items {
74-
if !eachMachine.Status.Ready {
75-
continue
76-
}
77-
for _, IPs := range eachMachine.Status.Addresses {
78-
if IPs.Type != v1beta2.MachineExternalIP {
79-
continue
80-
}
81-
addr, err := netip.ParseAddr(IPs.Address)
82-
if err != nil {
83-
return nil, nil, fmt.Errorf("not a valid IP %w", err)
84-
}
85-
if addr.Is4() {
86-
ipv4IPs = append(ipv4IPs, IPs.Address)
87-
} else {
88-
ipv6IPs = append(ipv6IPs, IPs.Address)
89-
}
90-
}
91-
}
92-
return ipv4IPs, ipv6IPs, nil
93-
}
94-
9572
func resetAkamaiRecord(ctx context.Context, cscope *scope.ClusterScope, recordResponse *dns.GetRecordResponse, machineIPList []string, rootDomain string) error {
9673
freshEntries := make([]string, 0)
9774
for _, ip := range recordResponse.Target {
@@ -121,12 +98,8 @@ func resetAkamaiRecord(ctx context.Context, cscope *scope.ClusterScope, recordRe
12198
})
12299
}
123100

124-
func deleteStaleAkamaiEntries(ctx context.Context, cscope *scope.ClusterScope) error {
125-
ipv4IPs, ipv6IPs, err := getMachineIPs(cscope)
126-
if err != nil {
127-
return err
128-
}
129-
101+
func deleteStaleAkamaiEntries(ctx context.Context, cscope *scope.ClusterScope, dnsEntries []DNSOptions) error {
102+
ipv4IPs, ipv6IPs := getDNSMachineIPs(dnsEntries)
130103
rootDomain := cscope.LinodeCluster.Spec.Network.DNSRootDomain
131104
fqdn := getSubDomain(cscope) + "." + rootDomain
132105

@@ -165,12 +138,20 @@ func deleteStaleAkamaiEntries(ctx context.Context, cscope *scope.ClusterScope) e
165138
return nil
166139
}
167140

168-
func deleteStaleLinodeEntries(ctx context.Context, cscope *scope.ClusterScope, domainRecords []linodego.DomainRecord, domainID int) error {
169-
ipv4IPs, ipv6IPs, err := getMachineIPs(cscope)
170-
if err != nil {
171-
return err
141+
func getDNSMachineIPs(dnsEntries []DNSOptions) (ipv4IPs, ipv6IPs []string) {
142+
for _, entry := range dnsEntries {
143+
if entry.DNSRecordType == linodego.RecordTypeA {
144+
ipv4IPs = append(ipv4IPs, entry.Target)
145+
}
146+
if entry.DNSRecordType == linodego.RecordTypeAAAA {
147+
ipv6IPs = append(ipv6IPs, entry.Target)
148+
}
172149
}
150+
return ipv4IPs, ipv6IPs
151+
}
173152

153+
func deleteStaleLinodeEntries(ctx context.Context, cscope *scope.ClusterScope, domainRecords []linodego.DomainRecord, domainID int, dnsEntries []DNSOptions) error {
154+
ipv4IPs, ipv6IPs := getDNSMachineIPs(dnsEntries)
174155
if len(domainRecords) > 0 {
175156
for _, record := range domainRecords {
176157
if record.Type == linodego.RecordTypeTXT {
@@ -207,7 +188,7 @@ func EnsureLinodeDNSEntries(ctx context.Context, cscope *scope.ClusterScope, ope
207188
return err
208189
}
209190

210-
if err := deleteStaleLinodeEntries(ctx, cscope, domainRecords, domainID); err != nil {
191+
if err := deleteStaleLinodeEntries(ctx, cscope, domainRecords, domainID, dnsEntries); err != nil {
211192
return err
212193
}
213194

cloud/services/domains_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package services
33
import (
44
"context"
55
"fmt"
6+
"strings"
67
"testing"
78

89
"github.com/akamai/AkamaiOPEN-edgegrid-golang/v12/pkg/dns"
@@ -1121,6 +1122,136 @@ func TestAddIPToDNS(t *testing.T) {
11211122
}
11221123
}
11231124

1125+
func TestEnsureLinodeDNSDeletesRecordForDeletingCAPIMachine(t *testing.T) {
1126+
t.Parallel()
1127+
1128+
clusterScope := &scope.ClusterScope{
1129+
Cluster: &clusterv1.Cluster{
1130+
ObjectMeta: metav1.ObjectMeta{
1131+
Name: "test-cluster",
1132+
UID: "test-uid",
1133+
},
1134+
},
1135+
LinodeCluster: &infrav1alpha2.LinodeCluster{
1136+
ObjectMeta: metav1.ObjectMeta{
1137+
Name: "test-cluster",
1138+
UID: "test-uid",
1139+
},
1140+
Spec: infrav1alpha2.LinodeClusterSpec{
1141+
Network: infrav1alpha2.NetworkSpec{
1142+
LoadBalancerType: "dns",
1143+
DNSRootDomain: "lkedevs.net",
1144+
DNSUniqueIdentifier: "test-hash",
1145+
},
1146+
},
1147+
},
1148+
LinodeMachines: infrav1alpha2.LinodeMachineList{
1149+
Items: []infrav1alpha2.LinodeMachine{
1150+
{
1151+
ObjectMeta: metav1.ObjectMeta{
1152+
Name: "deleting-machine",
1153+
OwnerReferences: []metav1.OwnerReference{{
1154+
APIVersion: "cluster.x-k8s.io/v1beta1",
1155+
Kind: "Machine",
1156+
Name: "deleting-machine",
1157+
UID: "deleting-machine-uid",
1158+
}},
1159+
},
1160+
Status: infrav1alpha2.LinodeMachineStatus{
1161+
Addresses: []clusterv1.MachineAddress{{
1162+
Type: clusterv1.MachineExternalIP,
1163+
Address: "10.10.10.10",
1164+
}},
1165+
},
1166+
},
1167+
{
1168+
ObjectMeta: metav1.ObjectMeta{
1169+
Name: "active-machine",
1170+
OwnerReferences: []metav1.OwnerReference{{
1171+
APIVersion: "cluster.x-k8s.io/v1beta1",
1172+
Kind: "Machine",
1173+
Name: "active-machine",
1174+
UID: "active-machine-uid",
1175+
}},
1176+
},
1177+
Status: infrav1alpha2.LinodeMachineStatus{
1178+
Addresses: []clusterv1.MachineAddress{{
1179+
Type: clusterv1.MachineExternalIP,
1180+
Address: "10.20.20.20",
1181+
}},
1182+
},
1183+
},
1184+
},
1185+
},
1186+
}
1187+
1188+
ctrl := gomock.NewController(t)
1189+
defer ctrl.Finish()
1190+
1191+
mockDNSClient := mock.NewMockLinodeClient(ctrl)
1192+
clusterScope.LinodeDomainsClient = mockDNSClient
1193+
mockDNSClient.EXPECT().ListDomains(gomock.Any(), gomock.Any()).Return([]linodego.Domain{{
1194+
ID: 1,
1195+
Domain: "lkedevs.net",
1196+
}}, nil)
1197+
mockDNSClient.EXPECT().ListDomainRecords(gomock.Any(), 1, gomock.Any()).DoAndReturn(
1198+
func(_ context.Context, _ int, opts *linodego.ListOptions) ([]linodego.DomainRecord, error) {
1199+
if strings.Contains(opts.Filter, `"name":"test-cluster-test-hash"`) && !strings.Contains(opts.Filter, `"target"`) {
1200+
return []linodego.DomainRecord{
1201+
{
1202+
ID: 100,
1203+
Type: linodego.RecordTypeA,
1204+
Name: "test-cluster-test-hash",
1205+
Target: "10.10.10.10",
1206+
},
1207+
{
1208+
ID: 101,
1209+
Type: linodego.RecordTypeA,
1210+
Name: "test-cluster-test-hash",
1211+
Target: "10.20.20.20",
1212+
},
1213+
{
1214+
ID: 102,
1215+
Type: linodego.RecordTypeTXT,
1216+
Name: "test-cluster-test-hash",
1217+
Target: "test-cluster",
1218+
},
1219+
}, nil
1220+
}
1221+
return []linodego.DomainRecord{{ID: 101}}, nil
1222+
}).AnyTimes()
1223+
mockDNSClient.EXPECT().DeleteDomainRecord(gomock.Any(), 1, 100).Return(nil)
1224+
1225+
mockK8sClient := mock.NewMockK8sClient(ctrl)
1226+
clusterScope.Client = mockK8sClient
1227+
mockK8sClient.EXPECT().Scheme().Return(nil).AnyTimes()
1228+
mockK8sClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(
1229+
func(_ context.Context, key client.ObjectKey, obj client.Object, _ ...client.GetOption) error {
1230+
machine, ok := obj.(*clusterv1.Machine)
1231+
if !ok {
1232+
return nil
1233+
}
1234+
1235+
machine.Name = key.Name
1236+
machine.Namespace = key.Namespace
1237+
switch key.Name {
1238+
case "deleting-machine":
1239+
deletionTime := metav1.Now()
1240+
machine.DeletionTimestamp = &deletionTime
1241+
machine.UID = "deleting-machine-uid"
1242+
case "active-machine":
1243+
machine.UID = "active-machine-uid"
1244+
machine.Status.Conditions = []metav1.Condition{{
1245+
Type: clusterv1.ReadyCondition,
1246+
Status: metav1.ConditionTrue,
1247+
}}
1248+
}
1249+
return nil
1250+
}).AnyTimes()
1251+
1252+
require.NoError(t, EnsureDNSEntries(t.Context(), clusterScope, "create"))
1253+
}
1254+
11241255
func TestDeleteIPFromDNS(t *testing.T) {
11251256
t.Parallel()
11261257
tests := []struct {

internal/controller/linodecluster_controller.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ import (
3838
"sigs.k8s.io/controller-runtime/pkg/client"
3939
crcontroller "sigs.k8s.io/controller-runtime/pkg/controller"
4040
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
41+
"sigs.k8s.io/controller-runtime/pkg/event"
4142
"sigs.k8s.io/controller-runtime/pkg/handler"
43+
"sigs.k8s.io/controller-runtime/pkg/predicate"
4244

4345
infrav1alpha2 "github.com/linode/cluster-api-provider-linode/api/v1alpha2"
4446
"github.com/linode/cluster-api-provider-linode/cloud/scope"
@@ -533,6 +535,22 @@ func (r *LinodeClusterReconciler) SetupWithManager(mgr ctrl.Manager, options crc
533535
),
534536
builder.WithPredicates(predicates.ClusterPausedTransitionsOrInfrastructureProvisioned(mgr.GetScheme(), mgr.GetLogger())),
535537
).
538+
Watches(
539+
&clusterv1.Machine{},
540+
handler.EnqueueRequestsFromMapFunc(machineToLinodeCluster(r.TracedClient(), mgr.GetLogger())),
541+
builder.WithPredicates(predicate.Funcs{
542+
CreateFunc: func(e event.CreateEvent) bool { return false },
543+
UpdateFunc: func(e event.UpdateEvent) bool {
544+
if e.ObjectOld == nil || e.ObjectNew == nil {
545+
return false
546+
}
547+
_, isControlPlane := e.ObjectNew.GetLabels()[clusterv1.MachineControlPlaneLabel]
548+
return isControlPlane
549+
},
550+
DeleteFunc: func(event.DeleteEvent) bool { return false },
551+
GenericFunc: func(event.GenericEvent) bool { return false },
552+
}),
553+
).
536554
Watches(
537555
&infrav1alpha2.LinodeMachine{},
538556
handler.EnqueueRequestsFromMapFunc(linodeMachineToLinodeCluster(r.TracedClient(), mgr.GetLogger())),

internal/controller/linodecluster_controller_helpers.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,43 @@ func buildPortCombosForIP(ip string, apiServerLBPort int, additionalPorts []infr
143143
return results
144144
}
145145

146+
func machineToLinodeCluster(tracedClient client.Client, logger logr.Logger) handler.MapFunc {
147+
logger = logger.WithName("LinodeClusterReconciler").WithName("MachineToLinodeCluster")
148+
149+
return func(ctx context.Context, o client.Object) []ctrl.Request {
150+
ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultMappingTimeout)
151+
defer cancel()
152+
153+
machine, ok := o.(*clusterv1.Machine)
154+
if !ok {
155+
logger.Info("Failed to cast object to Machine")
156+
return nil
157+
}
158+
159+
linodeCluster := infrav1alpha2.LinodeCluster{}
160+
if err := tracedClient.Get(
161+
ctx,
162+
types.NamespacedName{
163+
Name: machine.Labels[clusterv1.ClusterNameLabel],
164+
Namespace: machine.Namespace,
165+
},
166+
&linodeCluster); err != nil {
167+
logger.Info("Failed to get LinodeCluster")
168+
return nil
169+
}
170+
171+
result := make([]ctrl.Request, 0, 1)
172+
result = append(result, ctrl.Request{
173+
NamespacedName: client.ObjectKey{
174+
Namespace: linodeCluster.Namespace,
175+
Name: linodeCluster.Name,
176+
},
177+
})
178+
179+
return result
180+
}
181+
}
182+
146183
func linodeMachineToLinodeCluster(tracedClient client.Client, logger logr.Logger) handler.MapFunc {
147184
logger = logger.WithName("LinodeClusterReconciler").WithName("linodeMachineToLinodeCluster")
148185

0 commit comments

Comments
 (0)