domain_utils.go raw
1 package domain
2
3 import (
4 "time"
5
6 "github.com/scaleway/scaleway-sdk-go/errors"
7 "github.com/scaleway/scaleway-sdk-go/internal/async"
8 "github.com/scaleway/scaleway-sdk-go/scw"
9 )
10
11 const (
12 defaultRetryInterval = 15 * time.Second
13 defaultTimeout = 5 * time.Minute
14 )
15
16 const (
17 // ErrCodeNoSuchDNSZone for service response error code
18 //
19 // The specified dns zone does not exist.
20 ErrCodeNoSuchDNSZone = "NoSuchDNSZone"
21 ErrCodeNoSuchDNSRecord = "NoSuchDNSRecord"
22 )
23
24 // WaitForDNSZoneRequest is used by WaitForDNSZone method.
25 type WaitForDNSZoneRequest struct {
26 DNSZone string
27 DNSZones []string
28 Timeout *time.Duration
29 RetryInterval *time.Duration
30 }
31
32 func (s *API) WaitForDNSZone(
33 req *WaitForDNSZoneRequest,
34 opts ...scw.RequestOption,
35 ) (*DNSZone, error) {
36 timeout := defaultTimeout
37 if req.Timeout != nil {
38 timeout = *req.Timeout
39 }
40 retryInterval := defaultRetryInterval
41 if req.RetryInterval != nil {
42 retryInterval = *req.RetryInterval
43 }
44
45 terminalStatus := map[DNSZoneStatus]struct{}{
46 DNSZoneStatusActive: {},
47 DNSZoneStatusLocked: {},
48 DNSZoneStatusError: {},
49 }
50
51 dnsZone, err := async.WaitSync(&async.WaitSyncConfig{
52 Get: func() (any, bool, error) {
53 listReq := &ListDNSZonesRequest{
54 DNSZones: req.DNSZones,
55 }
56
57 if req.DNSZone != "" {
58 listReq.DNSZone = &req.DNSZone
59 }
60
61 // listing dnsZone zones and take the first one
62 DNSZones, err := s.ListDNSZones(listReq, opts...)
63 if err != nil {
64 return nil, false, err
65 }
66
67 if len(DNSZones.DNSZones) == 0 {
68 return nil, true, errors.New(ErrCodeNoSuchDNSZone)
69 }
70
71 zone := DNSZones.DNSZones[0]
72
73 _, isTerminal := terminalStatus[zone.Status]
74
75 return zone, isTerminal, nil
76 },
77 Timeout: timeout,
78 IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
79 })
80 if err != nil {
81 return nil, errors.Wrap(err, "waiting for DNS failed")
82 }
83
84 return dnsZone.(*DNSZone), nil
85 }
86
87 // WaitForDNSRecordExistRequest is used by WaitForDNSRecordExist method.
88 type WaitForDNSRecordExistRequest struct {
89 DNSZone string
90 RecordName string
91 RecordType RecordType
92 Timeout *time.Duration
93 RetryInterval *time.Duration
94 }
95
96 func (s *API) WaitForDNSRecordExist(
97 req *WaitForDNSRecordExistRequest,
98 opts ...scw.RequestOption,
99 ) (*Record, error) {
100 timeout := defaultTimeout
101 if req.Timeout != nil {
102 timeout = *req.Timeout
103 }
104 retryInterval := defaultRetryInterval
105 if req.RetryInterval != nil {
106 retryInterval = *req.RetryInterval
107 }
108
109 dns, err := async.WaitSync(&async.WaitSyncConfig{
110 Get: func() (any, bool, error) {
111 // listing dns zone records and take the first one
112 DNSRecords, err := s.ListDNSZoneRecords(&ListDNSZoneRecordsRequest{
113 Name: req.RecordName,
114 Type: req.RecordType,
115 DNSZone: req.DNSZone,
116 }, opts...)
117 if err != nil {
118 return nil, false, err
119 }
120
121 if DNSRecords.TotalCount == 0 {
122 return nil, false, errors.New(ErrCodeNoSuchDNSRecord)
123 }
124
125 record := DNSRecords.Records[0]
126
127 return record, true, nil
128 },
129 Timeout: timeout,
130 IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
131 })
132 if err != nil {
133 return nil, errors.Wrap(err, "check for DNS Record exist failed")
134 }
135
136 return dns.(*Record), nil
137 }
138
139 // WaitForOrderDomainRequest is used by WaitForOrderDomain method.
140 type WaitForOrderDomainRequest struct {
141 Domain string
142 Timeout *time.Duration
143 RetryInterval *time.Duration
144 }
145
146 // WaitForOrderDomain waits until the domain reaches a terminal status.
147 func (s *RegistrarAPI) WaitForOrderDomain(
148 req *WaitForOrderDomainRequest,
149 opts ...scw.RequestOption,
150 ) (*Domain, error) {
151 timeout := defaultTimeout
152 if req.Timeout != nil {
153 timeout = *req.Timeout
154 }
155 retryInterval := defaultRetryInterval
156 if req.RetryInterval != nil {
157 retryInterval = *req.RetryInterval
158 }
159
160 // Terminal statuses indicating success or error.
161 terminalStatuses := map[DomainStatus]struct{}{
162 DomainStatusActive: {},
163 DomainStatusExpired: {},
164 DomainStatusLocked: {},
165 DomainStatusCreateError: {},
166 DomainStatusRenewError: {},
167 DomainStatusXferError: {},
168 }
169
170 var lastStatus DomainStatus
171
172 domain, err := async.WaitSync(&async.WaitSyncConfig{
173 Get: func() (any, bool, error) {
174 resp, err := s.GetDomain(&RegistrarAPIGetDomainRequest{
175 Domain: req.Domain,
176 }, opts...)
177 if err != nil {
178 return nil, false, err
179 }
180
181 lastStatus = resp.Status
182
183 if _, isTerminal := terminalStatuses[resp.Status]; isTerminal {
184 return resp, true, nil
185 }
186 return resp, false, nil
187 },
188 Timeout: timeout,
189 IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
190 })
191 if err != nil {
192 return nil, errors.Wrap(err, "waiting for domain %s failed, last known status: %s", req.Domain, lastStatus)
193 }
194
195 return domain.(*Domain), nil
196 }
197
198 // WaitForAutoRenewStatusRequest defines the parameters for waiting on the auto‑renew feature.
199 type WaitForAutoRenewStatusRequest struct {
200 Domain string // The domain to wait for.
201 Timeout *time.Duration // Optional timeout.
202 RetryInterval *time.Duration // Optional retry interval.
203 }
204
205 // WaitForAutoRenewStatus polls the domain until its auto‑renew feature reaches a terminal state
206 // (either "enabled" or "disabled"). It uses GetDomain() to fetch the current status.
207 func (s *RegistrarAPI) WaitForAutoRenewStatus(req *WaitForAutoRenewStatusRequest, opts ...scw.RequestOption) (*Domain, error) {
208 // Use default timeout and retry interval if not provided.
209 timeout := defaultTimeout
210 if req.Timeout != nil {
211 timeout = *req.Timeout
212 }
213 retryInterval := defaultRetryInterval
214 if req.RetryInterval != nil {
215 retryInterval = *req.RetryInterval
216 }
217
218 // Terminal statuses for auto_renew: enabled or disabled.
219 terminalStatuses := map[DomainFeatureStatus]struct{}{
220 DomainFeatureStatusEnabled: {},
221 DomainFeatureStatusDisabled: {},
222 }
223
224 var lastStatus DomainFeatureStatus
225
226 domainResult, err := async.WaitSync(&async.WaitSyncConfig{
227 Get: func() (any, bool, error) {
228 resp, err := s.GetDomain(&RegistrarAPIGetDomainRequest{
229 Domain: req.Domain,
230 }, opts...)
231 if err != nil {
232 return nil, false, err
233 }
234
235 lastStatus = resp.AutoRenewStatus
236 if _, isTerminal := terminalStatuses[resp.AutoRenewStatus]; isTerminal {
237 return resp, true, nil
238 }
239 return resp, false, nil
240 },
241 Timeout: timeout,
242 IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
243 })
244 if err != nil {
245 return nil, errors.Wrap(err, "waiting for auto_renew to reach a terminal state for domain %s failed, last known status: %s", req.Domain, lastStatus)
246 }
247 return domainResult.(*Domain), nil
248 }
249
250 // WaitForDNSSECStatusRequest defines the parameters for waiting on the DNSSEC feature.
251 type WaitForDNSSECStatusRequest struct {
252 Domain string // The domain to wait for.
253 Timeout *time.Duration // Optional timeout.
254 RetryInterval *time.Duration // Optional retry interval.
255 }
256
257 // WaitForDNSSECStatus polls the domain until its DNSSEC feature reaches a terminal state
258 // (either "enabled" or "disabled"). It uses GetDomain() to fetch the current status.
259 func (s *RegistrarAPI) WaitForDNSSECStatus(req *WaitForDNSSECStatusRequest, opts ...scw.RequestOption) (*Domain, error) {
260 // Use default timeout and retry interval if not provided.
261 timeout := defaultTimeout
262 if req.Timeout != nil {
263 timeout = *req.Timeout
264 }
265 retryInterval := defaultRetryInterval
266 if req.RetryInterval != nil {
267 retryInterval = *req.RetryInterval
268 }
269
270 // Terminal statuses for DNSSEC: enabled or disabled.
271 terminalStatuses := map[DomainFeatureStatus]struct{}{
272 DomainFeatureStatusEnabled: {},
273 DomainFeatureStatusDisabled: {},
274 }
275
276 var lastStatus DomainFeatureStatus
277
278 domainResult, err := async.WaitSync(&async.WaitSyncConfig{
279 Get: func() (any, bool, error) {
280 // Retrieve the domain.
281 resp, err := s.GetDomain(&RegistrarAPIGetDomainRequest{
282 Domain: req.Domain,
283 }, opts...)
284 if err != nil {
285 return nil, false, err
286 }
287
288 // Check the current DNSSEC status.
289 lastStatus = resp.Dnssec.Status
290 if _, isTerminal := terminalStatuses[resp.Dnssec.Status]; isTerminal {
291 return resp, true, nil
292 }
293 return resp, false, nil
294 },
295 Timeout: timeout,
296 IntervalStrategy: async.LinearIntervalStrategy(retryInterval),
297 })
298 if err != nil {
299 return nil, errors.Wrap(err, "waiting for dnssec to reach a terminal state for domain %s failed, last known status: %s", req.Domain, lastStatus)
300 }
301 return domainResult.(*Domain), nil
302 }
303