zone.go raw
1 package dns
2
3 import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "io"
10 "net/http"
11 "net/url"
12 "reflect"
13 "strconv"
14 "strings"
15 "sync"
16 "time"
17
18 "github.com/akamai/AkamaiOPEN-edgegrid-golang/v11/pkg/edgegriderr"
19 "github.com/akamai/AkamaiOPEN-edgegrid-golang/v11/pkg/session"
20 validation "github.com/go-ozzo/ozzo-validation/v4"
21 )
22
23 var (
24 zoneWriteLock sync.Mutex
25 )
26
27 type (
28 // ZoneQueryString contains zone query parameters
29 ZoneQueryString struct {
30 Contract string
31 Group string
32 }
33
34 // ZoneCreate contains zone create request
35 ZoneCreate struct {
36 Zone string `json:"zone"`
37 Type string `json:"type"`
38 Masters []string `json:"masters,omitempty"`
39 Comment string `json:"comment,omitempty"`
40 SignAndServe bool `json:"signAndServe"`
41 SignAndServeAlgorithm string `json:"signAndServeAlgorithm,omitempty"`
42 TSIGKey *TSIGKey `json:"tsigKey,omitempty"`
43 Target string `json:"target,omitempty"`
44 EndCustomerID string `json:"endCustomerId,omitempty"`
45 ContractID string `json:"contractId,omitempty"`
46 OutboundZoneTransfer *OutboundZoneTransfer `json:"outboundZoneTransfer,omitempty"`
47 }
48
49 // OutboundZoneTransfer contains OutboundZoneTransfer request parameters
50 OutboundZoneTransfer struct {
51 ACL []string `json:"ACL"`
52 Enabled bool `json:"enabled"`
53 NotifyTargets []string `json:"notifyTargets"`
54 TSIGKey *TSIGKey `json:"tsigKey,omitempty"`
55 }
56
57 // ZoneResponse contains zone create response
58 ZoneResponse struct {
59 Zone string `json:"zone,omitempty"`
60 Type string `json:"type,omitempty"`
61 Masters []string `json:"masters,omitempty"`
62 Comment string `json:"comment,omitempty"`
63 SignAndServe bool `json:"signAndServe"`
64 SignAndServeAlgorithm string `json:"signAndServeAlgorithm,omitempty"`
65 TSIGKey *TSIGKey `json:"tsigKey,omitempty"`
66 Target string `json:"target,omitempty"`
67 EndCustomerID string `json:"endCustomerId,omitempty"`
68 ContractID string `json:"contractId,omitempty"`
69 AliasCount int64 `json:"aliasCount,omitempty"`
70 ActivationState string `json:"activationState,omitempty"`
71 LastActivationDate string `json:"lastActivationDate,omitempty"`
72 LastModifiedBy string `json:"lastModifiedBy,omitempty"`
73 LastModifiedDate string `json:"lastModifiedDate,omitempty"`
74 VersionID string `json:"versionId,omitempty"`
75 OutboundZoneTransfer *OutboundZoneTransfer `json:"outboundZoneTransfer,omitempty"`
76 }
77
78 // ListMetadata contains metadata for List Zones request
79 ListMetadata struct {
80 ContractIDs []string `json:"contractIds"`
81 Page int `json:"page"`
82 PageSize int `json:"pageSize"`
83 ShowAll bool `json:"showAll"`
84 TotalElements int `json:"totalElements"`
85 }
86
87 // ZoneListResponse contains response for List Zones request
88 ZoneListResponse struct {
89 Metadata *ListMetadata `json:"metadata,omitempty"`
90 Zones []ZoneResponse `json:"zones,omitempty"`
91 }
92
93 // ZoneRequest contains request parameters
94 ZoneRequest struct {
95 Zone string
96 }
97
98 // GetZoneResponse contains the response data from GetZone operation
99 GetZoneResponse ZoneResponse
100
101 // GetChangeListResponse contains metadata about a change list
102 GetChangeListResponse struct {
103 Zone string `json:"zone,omitempty"`
104 ChangeTag string `json:"changeTag,omitempty"`
105 ZoneVersionID string `json:"zoneVersionId,omitempty"`
106 LastModifiedDate string `json:"lastModifiedDate,omitempty"`
107 Stale bool `json:"stale,omitempty"`
108 }
109
110 // ZoneNameListResponse contains response with a list of zone's names and aliases
111 ZoneNameListResponse struct {
112 Zones []string `json:"zones"`
113 Aliases []string `json:"aliases,omitempty"`
114 }
115
116 // GetZoneNamesResponse contains record set names for zone
117 GetZoneNamesResponse struct {
118 Names []string `json:"names"`
119 }
120
121 // GetZoneNameTypesResponse contains record set types for zone
122 GetZoneNameTypesResponse struct {
123 Types []string `json:"types"`
124 }
125 // GetZoneRequest contains request parameters for GetZone
126 GetZoneRequest ZoneRequest
127
128 // GetChangeListRequest contains request parameters for GetChangeList
129 GetChangeListRequest ZoneRequest
130
131 // ListZonesRequest contains request parameters for ListZones
132 ListZonesRequest struct {
133 ContractIDs string
134 Page int
135 PageSize int
136 Search string
137 ShowAll bool
138 SortBy string
139 Types string
140 }
141 // GetMasterZoneFileRequest contains request parameters for GetMasterZoneFile
142 GetMasterZoneFileRequest ZoneRequest
143
144 // PostMasterZoneFileRequest contains request parameters for PostMasterZoneFile
145 PostMasterZoneFileRequest struct {
146 Zone string
147 FileData string
148 }
149 // CreateZoneRequest contains request parameters for CreateZone
150 CreateZoneRequest struct {
151 CreateZone *ZoneCreate
152 ZoneQueryString ZoneQueryString
153 ClearConn []bool
154 }
155 // SaveChangeListRequest contains request parameters for SaveChangelist
156 SaveChangeListRequest ZoneCreate
157
158 // SubmitChangeListRequest contains request parameters for SubmitChangeList
159 SubmitChangeListRequest ZoneCreate
160
161 // UpdateZoneRequest contains request parameters for UpdateZone
162 UpdateZoneRequest struct {
163 CreateZone *ZoneCreate
164 }
165 // GetZoneNamesRequest contains request parameters for GetZoneNames
166 GetZoneNamesRequest ZoneRequest
167
168 // GetZoneNameTypesRequest contains request parameters for GetZoneNameTypes
169 GetZoneNameTypesRequest struct {
170 Zone string
171 ZoneName string
172 }
173
174 // GetZonesDNSSecStatusRequest is used to get the DNSSEC status for one or more zones
175 GetZonesDNSSecStatusRequest struct {
176 Zones []string `json:"zones"`
177 }
178
179 // GetZonesDNSSecStatusResponse represents a list of DNSSEC statuses for DNS zones specified
180 // in the GetZonesDNSSecStatus request
181 GetZonesDNSSecStatusResponse struct {
182 DNSSecStatuses []SecStatus `json:"dnsSecStatuses"`
183 }
184
185 // SecStatus represents the DNSSEC status for a DNS zone
186 SecStatus struct {
187 Zone string `json:"zone"`
188 Alerts []string `json:"alerts"`
189 CurrentRecords SecRecords `json:"currentRecords"`
190 NewRecords *SecRecords `json:"newRecords"`
191 }
192
193 // SecRecords represents a set of DNSSEC records for a DNS zone
194 SecRecords struct {
195 DNSKeyRecord string `json:"dnskeyRecord"`
196 DSRecord string `json:"dsRecord"`
197 ExpectedTTL int64 `json:"expectedTtl"`
198 LastModifiedDate time.Time `json:"lastModifiedDate"`
199 }
200 )
201
202 var (
203 // ErrGetZone is returned when GetZone fails
204 ErrGetZone = errors.New("get zone")
205 // ErrGetChangeList is returned when GetChangeList fails
206 ErrGetChangeList = errors.New("get change list")
207 // ErrGetMasterZoneFile is returned when GetMasterZoneFile fails
208 ErrGetMasterZoneFile = errors.New("get master zone file")
209 // ErrPostMasterZoneFile is returned when PostMasterZoneFile fails
210 ErrPostMasterZoneFile = errors.New("post master zone file")
211 // ErrCreateZone is returned when CreateZone fails
212 ErrCreateZone = errors.New("create zone")
213 // ErrSaveChangeList is returned when SaveChangeList fails
214 ErrSaveChangeList = errors.New("save change list")
215 // ErrSubmitChangeList is returned when SubmitChangeList fails
216 ErrSubmitChangeList = errors.New("submit change list")
217 // ErrGetZoneNames is returned when GetZoneNames fails
218 ErrGetZoneNames = errors.New("get zone names")
219 // ErrGetZoneNameTypes is returned when GetZoneNameTypes fails
220 ErrGetZoneNameTypes = errors.New("get zone name types")
221 )
222
223 // Validate validates GetZoneNameTypesRequest
224 func (r GetZoneNameTypesRequest) Validate() error {
225 return edgegriderr.ParseValidationErrors(validation.Errors{
226 "Zone": validation.Validate(r.Zone, validation.Required),
227 "ZoneName": validation.Validate(r.ZoneName, validation.Required),
228 })
229 }
230
231 // Validate validates GetZoneNamesRequest
232 func (r GetZoneNamesRequest) Validate() error {
233 return edgegriderr.ParseValidationErrors(validation.Errors{
234 "Zone": validation.Validate(r.Zone, validation.Required),
235 })
236 }
237
238 // Validate validates SubmitChangeListRequest
239 func (r SubmitChangeListRequest) Validate() error {
240 return edgegriderr.ParseValidationErrors(validation.Errors{
241 "Zone": validation.Validate(r.Zone, validation.Required),
242 })
243 }
244
245 // Validate validates SaveChangelistRequest
246 func (r SaveChangeListRequest) Validate() error {
247 return edgegriderr.ParseValidationErrors(validation.Errors{
248 "Zone": validation.Validate(r.Zone, validation.Required),
249 })
250 }
251
252 // Validate validates PostMasterZoneFileRequest
253 func (r PostMasterZoneFileRequest) Validate() error {
254 return edgegriderr.ParseValidationErrors(validation.Errors{
255 "Zone": validation.Validate(r.Zone, validation.Required),
256 })
257 }
258
259 // Validate validates CreateZoneRequest
260 func (r CreateZoneRequest) Validate() error {
261 return edgegriderr.ParseValidationErrors(validation.Errors{
262 "ZoneQueryString": validation.Validate(r.ZoneQueryString, validation.Required),
263 })
264 }
265
266 // Validate validates GetZoneRequest
267 func (r GetZoneRequest) Validate() error {
268 return edgegriderr.ParseValidationErrors(validation.Errors{
269 "Zone": validation.Validate(r.Zone, validation.Required),
270 })
271 }
272
273 // Validate validates GetMasterZoneFileRequest
274 func (r GetMasterZoneFileRequest) Validate() error {
275 return edgegriderr.ParseValidationErrors(validation.Errors{
276 "Zone": validation.Validate(r.Zone, validation.Required),
277 })
278 }
279
280 // Validate validates GetChangeListRequest
281 func (r GetChangeListRequest) Validate() error {
282 return edgegriderr.ParseValidationErrors(validation.Errors{
283 "Zone": validation.Validate(r.Zone, validation.Required),
284 })
285 }
286
287 // Validate validates GetZonesDNSSecStatusRequest
288 func (r GetZonesDNSSecStatusRequest) Validate() error {
289 return edgegriderr.ParseValidationErrors(validation.Errors{
290 "Zones": validation.Validate(r.Zones, validation.Required),
291 })
292 }
293
294 var zoneStructMap = map[string]string{
295 "Zone": "zone",
296 "Type": "type",
297 "Masters": "masters",
298 "Comment": "comment",
299 "SignAndServe": "signAndServe",
300 "SignAndServeAlgorithm": "signAndServeAlgorithm",
301 "TSIGKey": "tsigKey",
302 "Target": "target",
303 "EndCustomerID": "endCustomerId",
304 "OutboundZoneTransfer": "outboundZoneTransfer",
305 "ContractID": "contractId"}
306
307 // Util to convert struct to http request body, e.g. io.reader
308 func convertStructToReqBody(srcStruct interface{}) (io.Reader, error) {
309 reqBody, err := json.Marshal(srcStruct)
310 if err != nil {
311 return nil, err
312 }
313 return bytes.NewBuffer(reqBody), nil
314 }
315
316 func (d *dns) ListZones(ctx context.Context, params ListZonesRequest) (*ZoneListResponse, error) {
317 logger := d.Log(ctx)
318 logger.Debug("ListZones")
319
320 getURL := "/config-dns/v2/zones"
321
322 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
323 if err != nil {
324 return nil, fmt.Errorf("failed to create listzones request: %w", err)
325 }
326
327 q := req.URL.Query()
328 if params.Page > 0 {
329 q.Add("page", strconv.Itoa(params.Page))
330 }
331 if params.PageSize > 0 {
332 q.Add("pageSize", strconv.Itoa(params.PageSize))
333 }
334 if params.Search != "" {
335 q.Add("search", params.Search)
336 }
337 q.Add("showAll", strconv.FormatBool(params.ShowAll))
338 if params.SortBy != "" {
339 q.Add("sortBy", params.SortBy)
340 }
341 if params.Types != "" {
342 q.Add("types", params.Types)
343 }
344 if params.ContractIDs != "" {
345 q.Add("contractIds", params.ContractIDs)
346 }
347 req.URL.RawQuery = q.Encode()
348
349 var result ZoneListResponse
350 resp, err := d.Exec(req, &result)
351 if err != nil {
352 return nil, fmt.Errorf("listzones request failed: %w", err)
353 }
354 defer session.CloseResponseBody(resp)
355
356 if resp.StatusCode != http.StatusOK {
357 return nil, d.Error(resp)
358 }
359
360 return &result, nil
361 }
362
363 func (d *dns) GetZone(ctx context.Context, params GetZoneRequest) (*GetZoneResponse, error) {
364 logger := d.Log(ctx)
365 logger.Debug("GetZone")
366
367 if err := params.Validate(); err != nil {
368 return nil, fmt.Errorf("%s: %w: %s", ErrGetZone, ErrStructValidation, err)
369 }
370
371 getURL := fmt.Sprintf("/config-dns/v2/zones/%s", params.Zone)
372 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
373 if err != nil {
374 return nil, fmt.Errorf("failed to create GetZone request: %w", err)
375 }
376
377 var result GetZoneResponse
378 resp, err := d.Exec(req, &result)
379 if err != nil {
380 return nil, fmt.Errorf("GetZone request failed: %w", err)
381 }
382 defer session.CloseResponseBody(resp)
383
384 if resp.StatusCode != http.StatusOK {
385 return nil, d.Error(resp)
386 }
387
388 return &result, nil
389 }
390
391 func (d *dns) GetChangeList(ctx context.Context, params GetChangeListRequest) (*GetChangeListResponse, error) {
392 logger := d.Log(ctx)
393 logger.Debug("GetChangeList")
394
395 if err := params.Validate(); err != nil {
396 return nil, fmt.Errorf("%s: %w: %s", ErrGetChangeList, ErrStructValidation, err)
397 }
398
399 getURL := fmt.Sprintf("/config-dns/v2/changelists/%s", params.Zone)
400 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
401 if err != nil {
402 return nil, fmt.Errorf("failed to create GetChangeList request: %w", err)
403 }
404
405 var result GetChangeListResponse
406 resp, err := d.Exec(req, &result)
407 if err != nil {
408 return nil, fmt.Errorf("GetChangeList request failed: %w", err)
409 }
410 defer session.CloseResponseBody(resp)
411
412 if resp.StatusCode != http.StatusOK {
413 return nil, d.Error(resp)
414 }
415
416 return &result, nil
417 }
418
419 func (d *dns) GetMasterZoneFile(ctx context.Context, params GetMasterZoneFileRequest) (string, error) {
420 logger := d.Log(ctx)
421 logger.Debug("GetMasterZoneFile")
422
423 if err := params.Validate(); err != nil {
424 return "", fmt.Errorf("%s: %w: %s", ErrGetMasterZoneFile, ErrStructValidation, err)
425 }
426
427 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/zone-file", params.Zone)
428 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
429 if err != nil {
430 return "", fmt.Errorf("failed to create GetMasterZoneFile request: %w", err)
431 }
432 req.Header.Add("Accept", "text/dns")
433
434 resp, err := d.Exec(req, nil)
435 if err != nil {
436 return "", fmt.Errorf("GetMasterZoneFile request failed: %w", err)
437 }
438 defer session.CloseResponseBody(resp)
439
440 if resp.StatusCode != http.StatusOK {
441 return "", d.Error(resp)
442 }
443
444 masterFile, err := io.ReadAll(resp.Body)
445 if err != nil {
446 return "", fmt.Errorf("GetMasterZoneFile request failed: %w", err)
447 }
448
449 return string(masterFile), nil
450 }
451
452 func (d *dns) PostMasterZoneFile(ctx context.Context, params PostMasterZoneFileRequest) error {
453 logger := d.Log(ctx)
454 logger.Debug("PostMasterZoneFile")
455
456 if err := params.Validate(); err != nil {
457 return fmt.Errorf("%s: %w: %s", ErrPostMasterZoneFile, ErrStructValidation, err)
458 }
459
460 mtResp := ""
461 pmzfURL := fmt.Sprintf("/config-dns/v2/zones/%s/zone-file", params.Zone)
462 buf := bytes.NewReader([]byte(params.FileData))
463 req, err := http.NewRequestWithContext(ctx, http.MethodPost, pmzfURL, buf)
464 if err != nil {
465 return fmt.Errorf("failed to create PostMasterZoneFile request: %w", err)
466 }
467
468 req.Header.Set("Content-Type", "text/dns")
469
470 resp, err := d.Exec(req, &mtResp)
471 if err != nil {
472 return fmt.Errorf("Create PostMasterZoneFile failed: %w", err)
473 }
474 defer session.CloseResponseBody(resp)
475
476 if resp.StatusCode != http.StatusNoContent {
477 return d.Error(resp)
478 }
479
480 return nil
481 }
482
483 func (d *dns) CreateZone(ctx context.Context, params CreateZoneRequest) error {
484 // This lock will restrict the concurrency of API calls
485 // to 1 save request at a time. This is needed for the Soa.Serial value which
486 // is required to be incremented for every subsequent update to a zone,
487 // so we have to save just one request at a time to ensure this is always
488 // incremented properly
489
490 zoneWriteLock.Lock()
491 defer zoneWriteLock.Unlock()
492
493 logger := d.Log(ctx)
494 logger.Debug("Zone Create")
495
496 if err := params.Validate(); err != nil {
497 return fmt.Errorf("%s: %w: %s", ErrCreateZone, ErrStructValidation, err)
498 }
499
500 if err := ValidateZone(params.CreateZone); err != nil {
501 return err
502 }
503
504 uri, err := url.Parse("/config-dns/v2/zones")
505 if err != nil {
506 return fmt.Errorf("%w: failed to parse uri: %s", ErrCreateZone, err)
507 }
508
509 q := uri.Query()
510 q.Add("contractId", params.ZoneQueryString.Contract)
511 if params.ZoneQueryString.Group != "" {
512 q.Add("gid", params.ZoneQueryString.Group)
513 }
514 uri.RawQuery = q.Encode()
515
516 req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri.String(), nil)
517 if err != nil {
518 return fmt.Errorf("failed to create zone create request: %w", err)
519 }
520
521 zoneMap := filterZoneCreate(params.CreateZone)
522 var zoneResponse ZoneResponse
523 resp, err := d.Exec(req, &zoneResponse, zoneMap)
524 if err != nil {
525 return fmt.Errorf("create zone request failed: %w", err)
526 }
527 defer session.CloseResponseBody(resp)
528
529 if resp.StatusCode != http.StatusCreated {
530 return d.Error(resp)
531 }
532
533 if strings.ToUpper(params.CreateZone.Type) == "PRIMARY" {
534 // Timing issue with Create immediately followed by SaveChangelist
535 for _, shouldClear := range params.ClearConn {
536 // should only be one entry
537 if shouldClear {
538 logger.Info("Clearing Idle Connections")
539 d.Client().CloseIdleConnections()
540 }
541 }
542 }
543
544 return nil
545 }
546
547 func (d *dns) SaveChangeList(ctx context.Context, params SaveChangeListRequest) error {
548 // This lock will restrict the concurrency of API calls
549 // to 1 save request at a time. This is needed for the Soa.Serial value which
550 // is required to be incremented for every subsequent update to a zone
551 // so we have to save just one request at a time to ensure this is always
552 // incremented properly
553
554 zoneWriteLock.Lock()
555 defer zoneWriteLock.Unlock()
556
557 logger := d.Log(ctx)
558 logger.Debug("SaveChangeList")
559
560 if err := params.Validate(); err != nil {
561 return fmt.Errorf("%s: %w: %s", ErrSaveChangeList, ErrStructValidation, err)
562 }
563
564 reqBody, err := convertStructToReqBody("")
565 if err != nil {
566 return fmt.Errorf("failed to generate request body: %w", err)
567 }
568
569 postURL := fmt.Sprintf("/config-dns/v2/changelists?zone=%s", params.Zone)
570 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqBody)
571 if err != nil {
572 return fmt.Errorf("failed to create SaveChangeList request: %w", err)
573 }
574
575 resp, err := d.Exec(req, nil)
576 if err != nil {
577 return fmt.Errorf("SaveChangeList request failed: %w", err)
578 }
579 defer session.CloseResponseBody(resp)
580
581 if resp.StatusCode != http.StatusCreated {
582 return d.Error(resp)
583 }
584
585 return nil
586 }
587
588 func (d *dns) SubmitChangeList(ctx context.Context, params SubmitChangeListRequest) error {
589 // This lock will restrict the concurrency of API calls
590 // to 1 save request at a time. This is needed for the Soa.Serial value which
591 // is required to be incremented for every subsequent update to a zone
592 // so we have to save just one request at a time to ensure this is always
593 // incremented properly
594
595 zoneWriteLock.Lock()
596 defer zoneWriteLock.Unlock()
597
598 logger := d.Log(ctx)
599 logger.Debug("SubmitChangeList")
600
601 if err := params.Validate(); err != nil {
602 return fmt.Errorf("%s: %w: %s", ErrSubmitChangeList, ErrStructValidation, err)
603 }
604
605 reqBody, err := convertStructToReqBody("")
606 if err != nil {
607 return fmt.Errorf("failed to generate request body: %w", err)
608 }
609
610 postURL := fmt.Sprintf("/config-dns/v2/changelists/%s/submit", params.Zone)
611 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqBody)
612 if err != nil {
613 return fmt.Errorf("failed to create SubmitChangeList request: %w", err)
614 }
615
616 resp, err := d.Exec(req, nil)
617 if err != nil {
618 return fmt.Errorf("SubmitChangeList request failed: %w", err)
619 }
620 defer session.CloseResponseBody(resp)
621
622 if resp.StatusCode != http.StatusNoContent {
623 return d.Error(resp)
624 }
625
626 return nil
627 }
628
629 func (d *dns) UpdateZone(ctx context.Context, params UpdateZoneRequest) error {
630 // This lock will restrict the concurrency of API calls
631 // to 1 save request at a time. This is needed for the Soa.Serial value which
632 // is required to be incremented for every subsequent update to a zone
633 // so we have to save just one request at a time to ensure this is always
634 // incremented properly
635
636 zoneWriteLock.Lock()
637 defer zoneWriteLock.Unlock()
638
639 logger := d.Log(ctx)
640 logger.Debug("Zone Update")
641
642 if err := ValidateZone(params.CreateZone); err != nil {
643 return err
644 }
645
646 zoneMap := filterZoneCreate(params.CreateZone)
647
648 putURL := fmt.Sprintf("/config-dns/v2/zones/%s", params.CreateZone.Zone)
649 req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil)
650 if err != nil {
651 return fmt.Errorf("failed to create Get Update request: %w", err)
652 }
653
654 var result ZoneResponse
655 resp, err := d.Exec(req, &result, zoneMap)
656 if err != nil {
657 return fmt.Errorf("zone update request failed: %w", err)
658 }
659 defer session.CloseResponseBody(resp)
660
661 if resp.StatusCode != http.StatusOK {
662 return d.Error(resp)
663 }
664
665 return nil
666 }
667
668 func filterZoneCreate(zone *ZoneCreate) map[string]interface{} {
669 zoneType := strings.ToUpper(zone.Type)
670 filteredZone := make(map[string]interface{})
671 zoneElems := reflect.ValueOf(zone).Elem()
672 for i := 0; i < zoneElems.NumField(); i++ {
673 varName := zoneElems.Type().Field(i).Name
674 varLower := zoneStructMap[varName]
675 varValue := zoneElems.Field(i).Interface()
676 switch varName {
677 case "Target":
678 if zoneType == "ALIAS" {
679 filteredZone[varLower] = varValue
680 }
681 case "TSIGKey":
682 if zoneType == "SECONDARY" {
683 filteredZone[varLower] = varValue
684 }
685 case "Masters":
686 if zoneType == "SECONDARY" {
687 filteredZone[varLower] = varValue
688 }
689 case "SignAndServe":
690 if zoneType != "ALIAS" {
691 filteredZone[varLower] = varValue
692 }
693 case "SignAndServeAlgorithm":
694 if zoneType != "ALIAS" {
695 filteredZone[varLower] = varValue
696 }
697 case "OutboundZoneTransfer":
698 // this is workaround for the check if value is not nil to avoid adding empty entry
699 switch v := varValue.(type) {
700 case *OutboundZoneTransfer:
701 {
702 if v != nil {
703 filteredZone[varLower] = varValue
704 }
705 }
706 default:
707 filteredZone[varLower] = varValue
708 }
709 default:
710 filteredZone[varLower] = varValue
711 }
712 }
713
714 return filteredZone
715 }
716
717 // ValidateZone validates ZoneCreate Object
718 func ValidateZone(zone *ZoneCreate) error {
719 if len(zone.Zone) == 0 {
720 return fmt.Errorf("Zone name is required")
721 }
722 zType := strings.ToUpper(zone.Type)
723 if zType != "PRIMARY" && zType != "SECONDARY" && zType != "ALIAS" {
724 return fmt.Errorf("Invalid zone type")
725 }
726 if zType != "SECONDARY" && zone.TSIGKey != nil {
727 return fmt.Errorf("TsigKey is invalid for %s zone type", zType)
728 }
729 if zType == "ALIAS" {
730 if len(zone.Target) == 0 {
731 return fmt.Errorf("Target is required for Alias zone type")
732 }
733 if len(zone.Masters) > 0 {
734 return fmt.Errorf("Masters is invalid for Alias zone type")
735 }
736 if zone.SignAndServe {
737 return fmt.Errorf("SignAndServe is invalid for Alias zone type")
738 }
739 if len(zone.SignAndServeAlgorithm) > 0 {
740 return fmt.Errorf("SignAndServeAlgorithm is invalid for Alias zone type")
741 }
742 return nil
743 }
744 // Primary or Secondary
745 if len(zone.Target) > 0 {
746 return fmt.Errorf("Target is invalid for %s zone type", zType)
747 }
748 if len(zone.Masters) > 0 && zType == "PRIMARY" {
749 return fmt.Errorf("Masters is invalid for Primary zone type")
750 }
751
752 return nil
753 }
754
755 func (d *dns) GetZoneNames(ctx context.Context, params GetZoneNamesRequest) (*GetZoneNamesResponse, error) {
756 logger := d.Log(ctx)
757 logger.Debug("GetZoneNames")
758
759 if err := params.Validate(); err != nil {
760 return nil, fmt.Errorf("%s: %w: %s", ErrGetZoneNames, ErrStructValidation, err)
761 }
762
763 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names", params.Zone)
764 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
765 if err != nil {
766 return nil, fmt.Errorf("failed to create GetZoneNames request: %w", err)
767 }
768
769 var result GetZoneNamesResponse
770 resp, err := d.Exec(req, &result)
771 if err != nil {
772 return nil, fmt.Errorf("GetZoneNames request failed: %w", err)
773 }
774 defer session.CloseResponseBody(resp)
775
776 if resp.StatusCode != http.StatusOK {
777 return nil, d.Error(resp)
778 }
779
780 return &result, nil
781 }
782
783 func (d *dns) GetZoneNameTypes(ctx context.Context, params GetZoneNameTypesRequest) (*GetZoneNameTypesResponse, error) {
784 logger := d.Log(ctx)
785 logger.Debug(" GetZoneNameTypes")
786
787 if err := params.Validate(); err != nil {
788 return nil, fmt.Errorf("%s: %w: %s", ErrGetZoneNameTypes, ErrStructValidation, err)
789 }
790
791 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types", params.Zone, params.ZoneName)
792 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
793 if err != nil {
794 return nil, fmt.Errorf("failed to create GetZoneNameTypes request: %w", err)
795 }
796
797 var result GetZoneNameTypesResponse
798 resp, err := d.Exec(req, &result)
799 if err != nil {
800 return nil, fmt.Errorf("GetZoneNameTypes request failed: %w", err)
801 }
802 defer session.CloseResponseBody(resp)
803
804 if resp.StatusCode != http.StatusOK {
805 return nil, d.Error(resp)
806 }
807
808 return &result, nil
809 }
810
811 func (d *dns) GetZonesDNSSecStatus(ctx context.Context, params GetZonesDNSSecStatusRequest) (*GetZonesDNSSecStatusResponse, error) {
812 logger := d.Log(ctx)
813 logger.Debug("GetZonesDNSSecStatus")
814
815 if err := params.Validate(); err != nil {
816 return nil, fmt.Errorf("%w: %s", ErrStructValidation, err)
817 }
818
819 req, err := http.NewRequestWithContext(ctx, http.MethodPost, "/config-dns/v2/zones/dns-sec-status", nil)
820 if err != nil {
821 return nil, fmt.Errorf("failed to create GetZonesDNSSecStatus request: %w", err)
822 }
823
824 var result GetZonesDNSSecStatusResponse
825 resp, err := d.Exec(req, &result, params)
826 if err != nil {
827 return nil, fmt.Errorf("GetZonesDNSSecStatus request failed: %w", err)
828 }
829 defer session.CloseResponseBody(resp)
830
831 if resp.StatusCode != http.StatusOK {
832 return nil, d.Error(resp)
833 }
834
835 return &result, nil
836 }
837