@@ -2,36 +2,41 @@ package services
2
2
3
3
import (
4
4
"context"
5
- "crypto/sha256"
6
- "encoding/hex"
7
5
"encoding/json"
8
- "errors"
9
6
"fmt"
10
- "time"
7
+ "net/netip"
8
+ "sync"
11
9
12
10
"github.com/linode/linodego"
11
+ "sigs.k8s.io/cluster-api/api/v1beta1"
13
12
kutil "sigs.k8s.io/cluster-api/util"
14
13
15
14
"github.com/linode/cluster-api-provider-linode/cloud/scope"
16
15
rutil "github.com/linode/cluster-api-provider-linode/util/reconciler"
17
16
)
18
17
19
- var ipTypeToRecordTypeMapper = map [string ]linodego.DomainRecordType {"IPv4" : "A" , "IPv6" : "AAAA" }
18
+ type DNSEntries struct {
19
+ options []DNSOptions
20
+ mux sync.RWMutex
21
+ }
20
22
21
- // AddIPToDNS creates the A and TXT record for the machine
22
- func AddIPToDNS ( ctx context. Context , mscope * scope. MachineScope ) error {
23
- dnsTTLSec := rutil . DefaultDNSTTLSec
24
- if mscope . LinodeCluster . Spec . Network . DNSTTLSec != 0 {
25
- dnsTTLSec = mscope . LinodeCluster . Spec . Network . DNSTTLSec
26
- }
23
+ type DNSOptions struct {
24
+ Hostname string
25
+ Target string
26
+ DNSRecordType linodego. DomainRecordType
27
+ DNSTTLSec int
28
+ }
27
29
30
+ // EnsureDNSEntries ensures the domainrecord is created, updated, or deleted based on operation passed
31
+ func EnsureDNSEntries (ctx context.Context , mscope * scope.MachineScope , operation string ) error {
28
32
// Check if instance is a control plane node
29
33
if ! kutil .IsControlPlaneMachine (mscope .Machine ) {
30
34
return nil
31
35
}
32
36
33
37
// Get the public IP that was assigned
34
- publicIPs , err := getMachinePublicIPs (ctx , mscope )
38
+ var dnss DNSEntries
39
+ dnsEntries , err := dnss .getDNSEntriesToEnsure (mscope )
35
40
if err != nil {
36
41
return err
37
42
}
@@ -41,90 +46,53 @@ func AddIPToDNS(ctx context.Context, mscope *scope.MachineScope) error {
41
46
if err != nil {
42
47
return err
43
48
}
44
- domainHostname := mscope .LinodeCluster .ObjectMeta .Name + "-" + mscope .LinodeCluster .Spec .Network .DNSUniqueIdentifier
45
49
46
- // Create/Update the A record for this IP and name combo
47
- for ipType , publicIP := range publicIPs {
48
- if err := CreateUpdateDomainRecord (ctx , mscope , domainHostname , publicIP , dnsTTLSec , domainID , ipTypeToRecordTypeMapper [ipType ]); err != nil {
50
+ for _ , dnsEntry := range dnsEntries {
51
+ if operation == "delete" {
52
+ if err := DeleteDomainRecord (ctx , mscope , domainID , dnsEntry ); err != nil {
53
+ return err
54
+ }
55
+ continue
56
+ }
57
+ if err := CreateUpdateDomainRecord (ctx , mscope , domainID , dnsEntry ); err != nil {
49
58
return err
50
59
}
51
60
}
52
61
53
- // Create/Update the TXT record for this IP and name combo
54
- txtRecordValueString := createSHA256HashOfString (mscope .LinodeMachine .Name )
55
- if err := CreateUpdateDomainRecord (ctx , mscope , domainHostname , "owner:" + txtRecordValueString , dnsTTLSec , domainID , "TXT" ); err != nil {
56
- return err
57
- }
58
-
59
62
return nil
60
63
}
61
64
62
- // DeleteIPFromDNS deletes the A and TXT record for the machine
63
- func DeleteIPFromDNS (ctx context.Context , mscope * scope.MachineScope ) error {
65
+ // getDNSEntriesToEnsure return DNS entries to create/delete
66
+ func (d * DNSEntries ) getDNSEntriesToEnsure (mscope * scope.MachineScope ) ([]DNSOptions , error ) {
67
+ d .mux .Lock ()
68
+ defer d .mux .Unlock ()
64
69
dnsTTLSec := rutil .DefaultDNSTTLSec
65
70
if mscope .LinodeCluster .Spec .Network .DNSTTLSec != 0 {
66
71
dnsTTLSec = mscope .LinodeCluster .Spec .Network .DNSTTLSec
67
72
}
68
73
69
- // Check if instance is a control plane node
70
- if ! kutil .IsControlPlaneMachine (mscope .Machine ) {
71
- return nil
72
- }
73
-
74
- // Get the public IP that was assigned
75
- publicIPs , err := getMachinePublicIPs (ctx , mscope )
76
- if err != nil {
77
- return err
78
- }
79
-
80
- // Get domainID from domain name
81
- domainID , err := GetDomainID (ctx , mscope )
82
- if err != nil {
83
- return err
74
+ if mscope .LinodeMachine .Status .Addresses == nil {
75
+ return nil , fmt .Errorf ("no addresses available on the LinodeMachine resource" )
84
76
}
85
77
domainHostname := mscope .LinodeCluster .ObjectMeta .Name + "-" + mscope .LinodeCluster .Spec .Network .DNSUniqueIdentifier
86
78
87
- // Delete A record
88
- for ipType , publicIP := range publicIPs {
89
- if err := DeleteDomainRecord ( ctx , mscope , domainHostname , publicIP , dnsTTLSec , domainID , ipTypeToRecordTypeMapper [ ipType ]); err != nil {
90
- return err
79
+ for _ , IPs := range mscope . LinodeMachine . Status . Addresses {
80
+ recordType := linodego . RecordTypeA
81
+ if IPs . Type != v1beta1 . MachineExternalIP {
82
+ continue
91
83
}
84
+ addr , err := netip .ParseAddr (IPs .Address )
85
+ if err != nil {
86
+ return nil , fmt .Errorf ("not a valid IP %w" , err )
87
+ }
88
+ if ! addr .Is4 () {
89
+ recordType = linodego .RecordTypeAAAA
90
+ }
91
+ d .options = append (d .options , DNSOptions {domainHostname , IPs .Address , recordType , dnsTTLSec })
92
92
}
93
+ d .options = append (d .options , DNSOptions {domainHostname , mscope .LinodeMachine .Name , linodego .RecordTypeTXT , dnsTTLSec })
93
94
94
- // Delete TXT record
95
- txtRecordValueString := createSHA256HashOfString (mscope .LinodeMachine .Name )
96
- if err := DeleteDomainRecord (ctx , mscope , domainHostname , "owner:" + txtRecordValueString , dnsTTLSec , domainID , "TXT" ); err != nil {
97
- return err
98
- }
99
-
100
- // Wait for TTL to expire
101
- time .Sleep (time .Duration (dnsTTLSec ) * time .Second )
102
-
103
- return nil
104
- }
105
-
106
- // getMachinePublicIPs gets the machines public IP
107
- func getMachinePublicIPs (ctx context.Context , mscope * scope.MachineScope ) (map [string ]string , error ) {
108
- // Verify instance id is not nil
109
- if mscope .LinodeMachine .Spec .InstanceID == nil {
110
- return nil , errors .New ("instance ID is nil. cant get machine's public ip" )
111
- }
112
-
113
- // Get the public IP that was assigned
114
- addresses , err := mscope .LinodeClient .GetInstanceIPAddresses (ctx , * mscope .LinodeMachine .Spec .InstanceID )
115
- if err != nil {
116
- return nil , err
117
- }
118
-
119
- if len (addresses .IPv4 .Public ) == 0 || addresses .IPv6 == nil {
120
- return nil , errors .New ("no public address" )
121
- }
122
-
123
- if addresses .IPv6 .SLAAC == nil {
124
- return nil , errors .New ("no SLAAC address" )
125
- }
126
-
127
- return map [string ]string {"IPv4" : addresses .IPv4 .Public [0 ].Address , "IPv6" : addresses .IPv6 .SLAAC .Address }, nil
95
+ return d .options , nil
128
96
}
129
97
130
98
// GetDomainID gets the domains linode id
@@ -145,9 +113,9 @@ func GetDomainID(ctx context.Context, mscope *scope.MachineScope) (int, error) {
145
113
return domains [0 ].ID , nil
146
114
}
147
115
148
- func CreateUpdateDomainRecord (ctx context.Context , mscope * scope.MachineScope , hostname , target string , ttl , domainID int , recordType linodego. DomainRecordType ) error {
116
+ func CreateUpdateDomainRecord (ctx context.Context , mscope * scope.MachineScope , domainID int , dnsEntry DNSOptions ) error {
149
117
// Check if domain record exists for this IP and name combo
150
- filter , err := json .Marshal (map [string ]interface {}{"name" : hostname , "target" : target , "type" : recordType })
118
+ filter , err := json .Marshal (map [string ]interface {}{"name" : dnsEntry . Hostname , "target" : dnsEntry . Target , "type" : dnsEntry . DNSRecordType })
151
119
if err != nil {
152
120
return err
153
121
}
@@ -159,31 +127,31 @@ func CreateUpdateDomainRecord(ctx context.Context, mscope *scope.MachineScope, h
159
127
160
128
// If record doesnt exist, create it
161
129
if len (domainRecords ) == 0 {
162
- if err := CreateDomainRecord (ctx , mscope , hostname , target , ttl , domainID , recordType ); err != nil {
130
+ if err := CreateDomainRecord (ctx , mscope , domainID , dnsEntry ); err != nil {
163
131
return err
164
132
}
165
133
return nil
166
134
}
167
135
168
136
// If record exists, update it
169
- if len (domainRecords ) != 0 && recordType != "TXT" {
170
- isOwner , err := IsDomainRecordOwner (ctx , mscope , hostname , target , domainID )
137
+ if len (domainRecords ) != 0 && dnsEntry . DNSRecordType != linodego . RecordTypeTXT {
138
+ isOwner , err := IsDomainRecordOwner (ctx , mscope , dnsEntry . Hostname , domainID )
171
139
if err != nil {
172
140
return err
173
141
}
174
142
if ! isOwner {
175
143
return fmt .Errorf ("the domain record is not owned by this entity. wont update" )
176
144
}
177
145
}
178
- if err := UpdateDomainRecord (ctx , mscope , hostname , target , ttl , domainID , domainRecords [0 ].ID , recordType ); err != nil {
146
+ if err := UpdateDomainRecord (ctx , mscope , domainID , domainRecords [0 ].ID , dnsEntry ); err != nil {
179
147
return err
180
148
}
181
149
return nil
182
150
}
183
151
184
- func DeleteDomainRecord (ctx context.Context , mscope * scope.MachineScope , hostname , target string , ttl , domainID int , recordType linodego. DomainRecordType ) error {
152
+ func DeleteDomainRecord (ctx context.Context , mscope * scope.MachineScope , domainID int , dnsEntry DNSOptions ) error {
185
153
// Check if domain record exists for this IP and name combo
186
- filter , err := json .Marshal (map [string ]interface {}{"name" : hostname , "target" : target , "type" : recordType })
154
+ filter , err := json .Marshal (map [string ]interface {}{"name" : dnsEntry . Hostname , "target" : dnsEntry . Target , "type" : dnsEntry . DNSRecordType })
187
155
if err != nil {
188
156
return err
189
157
}
@@ -198,12 +166,11 @@ func DeleteDomainRecord(ctx context.Context, mscope *scope.MachineScope, hostnam
198
166
return nil
199
167
}
200
168
201
- // If record is A type, verify ownership
202
- if recordType != "TXT" {
203
- txtRecordValueString := createSHA256HashOfString (mscope .LinodeMachine .Name )
204
- isOwner , ownerErr := IsDomainRecordOwner (ctx , mscope , hostname , "owner:" + txtRecordValueString , domainID )
205
- if ownerErr != nil {
206
- return ownerErr
169
+ // If record is A/AAAA type, verify ownership
170
+ if dnsEntry .DNSRecordType != linodego .RecordTypeTXT {
171
+ isOwner , err := IsDomainRecordOwner (ctx , mscope , dnsEntry .Hostname , domainID )
172
+ if err != nil {
173
+ return err
207
174
}
208
175
if ! isOwner {
209
176
return fmt .Errorf ("the domain record is not owned by this entity. wont delete" )
@@ -217,12 +184,12 @@ func DeleteDomainRecord(ctx context.Context, mscope *scope.MachineScope, hostnam
217
184
return nil
218
185
}
219
186
220
- func CreateDomainRecord (ctx context.Context , mscope * scope.MachineScope , hostname , target string , ttl , domainID int , recordType linodego. DomainRecordType ) error {
187
+ func CreateDomainRecord (ctx context.Context , mscope * scope.MachineScope , domainID int , dnsEntries DNSOptions ) error {
221
188
recordReq := linodego.DomainRecordCreateOptions {
222
- Type : recordType ,
223
- Name : hostname ,
224
- Target : target ,
225
- TTLSec : ttl ,
189
+ Type : dnsEntries . DNSRecordType ,
190
+ Name : dnsEntries . Hostname ,
191
+ Target : dnsEntries . Target ,
192
+ TTLSec : dnsEntries . DNSTTLSec ,
226
193
}
227
194
228
195
if _ , err := mscope .LinodeDomainsClient .CreateDomainRecord (ctx , domainID , recordReq ); err != nil {
@@ -231,12 +198,12 @@ func CreateDomainRecord(ctx context.Context, mscope *scope.MachineScope, hostnam
231
198
return nil
232
199
}
233
200
234
- func UpdateDomainRecord (ctx context.Context , mscope * scope.MachineScope , hostname , target string , ttl , domainID , domainRecordID int , recordType linodego. DomainRecordType ) error {
201
+ func UpdateDomainRecord (ctx context.Context , mscope * scope.MachineScope , domainID , domainRecordID int , dnsEntries DNSOptions ) error {
235
202
recordReq := linodego.DomainRecordUpdateOptions {
236
- Type : recordType ,
237
- Name : hostname ,
238
- Target : target ,
239
- TTLSec : ttl ,
203
+ Type : dnsEntries . DNSRecordType ,
204
+ Name : dnsEntries . Hostname ,
205
+ Target : dnsEntries . Target ,
206
+ TTLSec : dnsEntries . DNSTTLSec ,
240
207
}
241
208
242
209
if _ , err := mscope .LinodeDomainsClient .UpdateDomainRecord (ctx , domainID , domainRecordID , recordReq ); err != nil {
@@ -245,9 +212,9 @@ func UpdateDomainRecord(ctx context.Context, mscope *scope.MachineScope, hostnam
245
212
return nil
246
213
}
247
214
248
- func IsDomainRecordOwner (ctx context.Context , mscope * scope.MachineScope , hostname , target string , domainID int ) (bool , error ) {
215
+ func IsDomainRecordOwner (ctx context.Context , mscope * scope.MachineScope , hostname string , domainID int ) (bool , error ) {
249
216
// Check if domain record exists
250
- filter , err := json .Marshal (map [string ]interface {}{"name" : hostname , "target" : target , "type" : "TXT" })
217
+ filter , err := json .Marshal (map [string ]interface {}{"name" : hostname , "target" : mscope . LinodeMachine . Name , "type" : linodego . RecordTypeTXT })
251
218
if err != nil {
252
219
return false , err
253
220
}
@@ -259,14 +226,8 @@ func IsDomainRecordOwner(ctx context.Context, mscope *scope.MachineScope, hostna
259
226
260
227
// If record exists, update it
261
228
if len (domainRecords ) == 0 {
262
- return false , fmt .Errorf ("no txt record %s found with value %s for machine %s" , hostname , target , mscope .LinodeMachine .Name )
229
+ return false , fmt .Errorf ("no txt record %s found with value %s for machine %s" , hostname , mscope . LinodeMachine . Name , mscope .LinodeMachine .Name )
263
230
}
264
231
265
232
return true , nil
266
233
}
267
-
268
- func createSHA256HashOfString (stringToConvert string ) string {
269
- machineNameHash := sha256 .New ()
270
- machineNameHash .Write ([]byte (stringToConvert ))
271
- return hex .EncodeToString (machineNameHash .Sum (nil ))
272
- }
0 commit comments