github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/configdns/zone.go (about) 1 package dns 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 10 "bytes" 11 "encoding/json" 12 "reflect" 13 "strconv" 14 "strings" 15 "sync" 16 ) 17 18 var ( 19 zoneWriteLock sync.Mutex 20 ) 21 22 type ( 23 // Zones contains operations available on Zone resources 24 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html 25 Zones interface { 26 // ListZones retrieves a list of all zones user can access 27 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getzones 28 ListZones(context.Context, ...ZoneListQueryArgs) (*ZoneListResponse, error) 29 // NewZone returns a new ZoneCreate object 30 NewZone(context.Context, ZoneCreate) *ZoneCreate 31 // NewZoneResponse returns a new ZoneResponse object 32 NewZoneResponse(context.Context, string) *ZoneResponse 33 // NewChangeListResponse returns a new ChangeListResponse object 34 NewChangeListResponse(context.Context, string) *ChangeListResponse 35 // NewZoneQueryString returns a new ZoneQueryString object 36 NewZoneQueryString(context.Context, string, string) *ZoneQueryString 37 // GetZone retrieves Zone metadata 38 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getzone 39 GetZone(context.Context, string) (*ZoneResponse, error) 40 //GetChangeList treieves Zone changelist 41 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getchangelist 42 GetChangeList(context.Context, string) (*ChangeListResponse, error) 43 // GetMasterZoneFile retrieves master zone file 44 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getversionmasterzonefile 45 GetMasterZoneFile(context.Context, string) (string, error) 46 // PostMasterZoneFile updates master zone file 47 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postmasterzonefile 48 PostMasterZoneFile(context.Context, string, string) error 49 // CreateZone 50 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postzones 51 CreateZone(context.Context, *ZoneCreate, ZoneQueryString, ...bool) error 52 // SaveChangelist 53 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postchangelists 54 SaveChangelist(context.Context, *ZoneCreate) error 55 // SubmitChangelist submits changelist for the Zone to create default NS SOA records 56 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postchangelistsubmit 57 SubmitChangelist(context.Context, *ZoneCreate) error 58 // UpdateZone updates the Zone 59 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#putzone 60 UpdateZone(context.Context, *ZoneCreate, ZoneQueryString) error 61 // DeleteZone a zone 62 // See: N/A 63 DeleteZone(context.Context, *ZoneCreate, ZoneQueryString) error 64 // ValidateZone validates zone metadata based on type 65 ValidateZone(context.Context, *ZoneCreate) error 66 // GetZoneNames retrieves a list of a zone's record names 67 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getzonerecordsetnames 68 GetZoneNames(context.Context, string) (*ZoneNamesResponse, error) 69 // GetZoneNameTypes retrieves a zone name's record types 70 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getzonerecordsettypes 71 GetZoneNameTypes(context.Context, string, string) (*ZoneNameTypesResponse, error) 72 // CreateBulkZones submits create bulk zone request 73 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postbulkzonecreate 74 CreateBulkZones(context.Context, *BulkZonesCreate, ZoneQueryString) (*BulkZonesResponse, error) 75 // DeleteBulkZones submits delete bulk zone request 76 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postbulkzonedelete 77 DeleteBulkZones(context.Context, *ZoneNameListResponse, ...bool) (*BulkZonesResponse, error) 78 // GetBulkZoneCreateStatus retrieves submit request status 79 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getbulkzonecreatestatus 80 GetBulkZoneCreateStatus(context.Context, string) (*BulkStatusResponse, error) 81 //GetBulkZoneDeleteStatus retrieves submit request status 82 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getbulkzonedeletestatus 83 GetBulkZoneDeleteStatus(context.Context, string) (*BulkStatusResponse, error) 84 // GetBulkZoneCreateResult retrieves create request result 85 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getbulkzonecreateresult 86 GetBulkZoneCreateResult(ctx context.Context, requestid string) (*BulkCreateResultResponse, error) 87 // GetBulkZoneDeleteResult retrieves delete request result 88 // https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getbulkzonedeleteresult 89 GetBulkZoneDeleteResult(context.Context, string) (*BulkDeleteResultResponse, error) 90 } 91 92 // ZoneQueryString contains zone query parameters 93 ZoneQueryString struct { 94 Contract string 95 Group string 96 } 97 98 // ZoneCreate contains zone create request 99 ZoneCreate struct { 100 Zone string `json:"zone"` 101 Type string `json:"type"` 102 Masters []string `json:"masters,omitempty"` 103 Comment string `json:"comment,omitempty"` 104 SignAndServe bool `json:"signAndServe"` 105 SignAndServeAlgorithm string `json:"signAndServeAlgorithm,omitempty"` 106 TsigKey *TSIGKey `json:"tsigKey,omitempty"` 107 Target string `json:"target,omitempty"` 108 EndCustomerID string `json:"endCustomerId,omitempty"` 109 ContractID string `json:"contractId,omitempty"` 110 } 111 112 // ZoneResponse contains zone create response 113 ZoneResponse struct { 114 Zone string `json:"zone,omitempty"` 115 Type string `json:"type,omitempty"` 116 Masters []string `json:"masters,omitempty"` 117 Comment string `json:"comment,omitempty"` 118 SignAndServe bool `json:"signAndServe"` 119 SignAndServeAlgorithm string `json:"signAndServeAlgorithm,omitempty"` 120 TsigKey *TSIGKey `json:"tsigKey,omitempty"` 121 Target string `json:"target,omitempty"` 122 EndCustomerID string `json:"endCustomerId,omitempty"` 123 ContractID string `json:"contractId,omitempty"` 124 AliasCount int64 `json:"aliasCount,omitempty"` 125 ActivationState string `json:"activationState,omitempty"` 126 LastActivationDate string `json:"lastActivationDate,omitempty"` 127 LastModifiedBy string `json:"lastModifiedBy,omitempty"` 128 LastModifiedDate string `json:"lastModifiedDate,omitempty"` 129 VersionId string `json:"versionId,omitempty"` 130 } 131 132 // ZoneListQueryArgs contains parameters for List Zones query 133 ZoneListQueryArgs struct { 134 ContractIDs string 135 Page int 136 PageSize int 137 Search string 138 ShowAll bool 139 SortBy string 140 Types string 141 } 142 143 // ListMetadata contains metadata for List Zones request 144 ListMetadata struct { 145 ContractIDs []string `json:"contractIds"` 146 Page int `json:"page"` 147 PageSize int `json:"pageSize"` 148 ShowAll bool `json:"showAll"` 149 TotalElements int `json:"totalElements"` 150 } //`json:"metadata"` 151 152 // ZoneListResponse contains response for List Zones request 153 ZoneListResponse struct { 154 Metadata *ListMetadata `json:"metadata,omitempty"` 155 Zones []*ZoneResponse `json:"zones,omitempty"` 156 } 157 158 // ChangeListResponse contains metadata about a change list 159 ChangeListResponse struct { 160 Zone string `json:"zone,omitempty"` 161 ChangeTag string `json:"changeTag,omitempty"` 162 ZoneVersionID string `json:"zoneVersionId,omitempty"` 163 LastModifiedDate string `json:"lastModifiedDate,omitempty"` 164 Stale bool `json:"stale,omitempty"` 165 } 166 167 // ZoneNameListResponse contains response with a list of zone's names and aliases 168 ZoneNameListResponse struct { 169 Zones []string `json:"zones"` 170 Aliases []string `json:"aliases,omitempty"` 171 } 172 173 // ZoneNamesResponse contains record set names for zone 174 ZoneNamesResponse struct { 175 Names []string `json:"names"` 176 } 177 178 // ZoneNameTypesResponse contains record set types for zone 179 ZoneNameTypesResponse struct { 180 Types []string `json:"types"` 181 } 182 ) 183 184 var zoneStructMap = map[string]string{ 185 "Zone": "zone", 186 "Type": "type", 187 "Masters": "masters", 188 "Comment": "comment", 189 "SignAndServe": "signAndServe", 190 "SignAndServeAlgorithm": "signAndServeAlgorithm", 191 "TsigKey": "tsigKey", 192 "Target": "target", 193 "EndCustomerID": "endCustomerId", 194 "ContractId": "contractId"} 195 196 // Util to convert struct to http request body, eg. io.reader 197 func convertStructToReqBody(srcstruct interface{}) (io.Reader, error) { 198 199 reqbody, err := json.Marshal(srcstruct) 200 if err != nil { 201 return nil, err 202 } 203 return bytes.NewBuffer(reqbody), nil 204 } 205 206 // List Zones 207 func (p *dns) ListZones(ctx context.Context, queryArgs ...ZoneListQueryArgs) (*ZoneListResponse, error) { 208 209 logger := p.Log(ctx) 210 logger.Debug("ListZones") 211 212 // construct GET url 213 getURL := fmt.Sprintf("/config-dns/v2/zones") 214 if len(queryArgs) > 1 { 215 return nil, fmt.Errorf("ListZones QueryArgs invalid") 216 } 217 218 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 219 if err != nil { 220 return nil, fmt.Errorf("failed to create listzones request: %w", err) 221 } 222 223 q := req.URL.Query() 224 if len(queryArgs) > 0 { 225 if queryArgs[0].Page > 0 { 226 q.Add("page", strconv.Itoa(queryArgs[0].Page)) 227 } 228 if queryArgs[0].PageSize > 0 { 229 q.Add("pageSize", strconv.Itoa(queryArgs[0].PageSize)) 230 } 231 if queryArgs[0].Search != "" { 232 q.Add("search", queryArgs[0].Search) 233 } 234 q.Add("showAll", strconv.FormatBool(queryArgs[0].ShowAll)) 235 if queryArgs[0].SortBy != "" { 236 q.Add("sortBy", queryArgs[0].SortBy) 237 } 238 if queryArgs[0].Types != "" { 239 q.Add("types", queryArgs[0].Types) 240 } 241 if queryArgs[0].ContractIDs != "" { 242 q.Add("contractIds", queryArgs[0].ContractIDs) 243 } 244 req.URL.RawQuery = q.Encode() 245 } 246 247 var zonelist ZoneListResponse 248 resp, err := p.Exec(req, &zonelist) 249 if err != nil { 250 return nil, fmt.Errorf("listzones request failed: %w", err) 251 } 252 253 if resp.StatusCode != http.StatusOK { 254 return nil, p.Error(resp) 255 } 256 257 return &zonelist, nil 258 } 259 260 // NewZone creates a new Zone. Supports subset of fields 261 func (p *dns) NewZone(ctx context.Context, params ZoneCreate) *ZoneCreate { 262 263 logger := p.Log(ctx) 264 logger.Debug("NewZone") 265 266 zone := &ZoneCreate{Zone: params.Zone, 267 Type: params.Type, 268 Masters: params.Masters, 269 TsigKey: params.TsigKey, 270 Target: params.Target, 271 EndCustomerID: params.EndCustomerID, 272 ContractID: params.ContractID, 273 Comment: params.Comment, 274 SignAndServe: params.SignAndServe, 275 SignAndServeAlgorithm: params.SignAndServeAlgorithm} 276 277 logger.Debugf("Created zone: %v", zone) 278 return zone 279 } 280 281 func (p *dns) NewZoneResponse(ctx context.Context, zonename string) *ZoneResponse { 282 283 logger := p.Log(ctx) 284 logger.Debug("NewZoneResponse") 285 286 zone := &ZoneResponse{Zone: zonename} 287 return zone 288 } 289 290 func (p *dns) NewChangeListResponse(ctx context.Context, zone string) *ChangeListResponse { 291 292 logger := p.Log(ctx) 293 logger.Debug("NewChangeListResponse") 294 295 changelist := &ChangeListResponse{Zone: zone} 296 return changelist 297 } 298 299 func (p *dns) NewZoneQueryString(ctx context.Context, contract string, group string) *ZoneQueryString { 300 301 logger := p.Log(ctx) 302 logger.Debug("NewZoneQueryString") 303 304 zonequerystring := &ZoneQueryString{Contract: contract, Group: group} 305 return zonequerystring 306 } 307 308 // GetZone retrieves a DNS Zone for a given hostname 309 func (p *dns) GetZone(ctx context.Context, zonename string) (*ZoneResponse, error) { 310 311 logger := p.Log(ctx) 312 logger.Debug("GetZone") 313 314 var zone ZoneResponse 315 316 getURL := fmt.Sprintf("/config-dns/v2/zones/%s", zonename) 317 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 318 if err != nil { 319 return nil, fmt.Errorf("failed to create GetZone request: %w", err) 320 } 321 322 resp, err := p.Exec(req, &zone) 323 if err != nil { 324 return nil, fmt.Errorf("GetZone request failed: %w", err) 325 } 326 327 if resp.StatusCode != http.StatusOK { 328 return nil, p.Error(resp) 329 } 330 331 return &zone, nil 332 } 333 334 // GetChangeList retrieves a changelist for a zone 335 func (p *dns) GetChangeList(ctx context.Context, zone string) (*ChangeListResponse, error) { 336 337 logger := p.Log(ctx) 338 logger.Debug("GetChangeList") 339 340 var changelist ChangeListResponse 341 getURL := fmt.Sprintf("/config-dns/v2/changelists/%s", zone) 342 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 343 if err != nil { 344 return nil, fmt.Errorf("failed to create GetChangeList request: %w", err) 345 } 346 347 resp, err := p.Exec(req, &changelist) 348 if err != nil { 349 return nil, fmt.Errorf("GetChangeList request failed: %w", err) 350 } 351 352 if resp.StatusCode != http.StatusOK { 353 return nil, p.Error(resp) 354 } 355 356 return &changelist, nil 357 } 358 359 // GetMasterZoneFile retrieves the zone's master file 360 func (p *dns) GetMasterZoneFile(ctx context.Context, zone string) (string, error) { 361 362 logger := p.Log(ctx) 363 logger.Debug("GetMasterZoneFile") 364 365 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/zone-file", zone) 366 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 367 if err != nil { 368 return "", fmt.Errorf("failed to create GetMasterZoneFile request: %w", err) 369 } 370 req.Header.Add("Accept", "text/dns") 371 372 resp, err := p.Exec(req, nil) 373 if err != nil { 374 return "", fmt.Errorf("GetMasterZoneFile request failed: %w", err) 375 } 376 377 if resp.StatusCode != http.StatusOK { 378 return "", p.Error(resp) 379 } 380 381 masterfile, err := ioutil.ReadAll(resp.Body) 382 if err != nil { 383 return "", fmt.Errorf("GetMasterZoneFile request failed: %w", err) 384 } 385 386 return string(masterfile), nil 387 } 388 389 // Update Master Zone file 390 func (p *dns) PostMasterZoneFile(ctx context.Context, zone string, filedata string) error { 391 392 logger := p.Log(ctx) 393 logger.Debug("PostMasterZoneFile") 394 395 mtresp := "" 396 pmzfURL := fmt.Sprintf("/config-dns/v2/zones/%s/zone-file", zone) 397 buf := bytes.NewReader([]byte(filedata)) 398 req, err := http.NewRequestWithContext(ctx, http.MethodPost, pmzfURL, buf) 399 if err != nil { 400 return fmt.Errorf("failed to create PostMasterZoneFile request: %w", err) 401 } 402 403 req.Header.Set("Content-Type", "text/dns") 404 405 resp, err := p.Exec(req, &mtresp) 406 if err != nil { 407 return fmt.Errorf("Create PostMasterZoneFile failed: %w", err) 408 } 409 410 if resp.StatusCode != http.StatusNoContent { 411 return p.Error(resp) 412 } 413 414 return nil 415 } 416 417 // Create a Zone 418 func (p *dns) CreateZone(ctx context.Context, zone *ZoneCreate, zonequerystring ZoneQueryString, clearConn ...bool) error { 419 // This lock will restrict the concurrency of API calls 420 // to 1 save request at a time. This is needed for the Soa.Serial value which 421 // is required to be incremented for every subsequent update to a zone 422 // so we have to save just one request at a time to ensure this is always 423 // incremented properly 424 425 zoneWriteLock.Lock() 426 defer zoneWriteLock.Unlock() 427 428 logger := p.Log(ctx) 429 logger.Debug("Zone Create") 430 431 if err := p.ValidateZone(ctx, zone); err != nil { 432 return err 433 } 434 435 zoneMap := filterZoneCreate(zone) 436 437 var zoneresponse ZoneResponse 438 zoneURL := "/config-dns/v2/zones/?contractId=" + zonequerystring.Contract 439 if len(zonequerystring.Group) > 0 { 440 zoneURL += "&gid=" + zonequerystring.Group 441 } 442 443 reqbody, err := convertStructToReqBody(zoneMap) 444 if err != nil { 445 return fmt.Errorf("failed to generate request body: %w", err) 446 } 447 448 req, err := http.NewRequestWithContext(ctx, http.MethodPost, zoneURL, reqbody) 449 if err != nil { 450 return fmt.Errorf("failed to create Zone Create request: %w", err) 451 } 452 453 resp, err := p.Exec(req, &zoneresponse) 454 if err != nil { 455 return fmt.Errorf("Create Zone request failed: %w", err) 456 } 457 458 if resp.StatusCode != http.StatusCreated { 459 return p.Error(resp) 460 } 461 462 if strings.ToUpper(zone.Type) == "PRIMARY" { 463 // Timing issue with Create immediately followed by SaveChangelist 464 for _, clear := range clearConn { 465 // should only be one entry 466 if clear { 467 logger.Info("Clearing Idle Connections") 468 p.Client().CloseIdleConnections() 469 } 470 } 471 } 472 473 return nil 474 } 475 476 // Create changelist for the Zone. Side effect is to create default NS SOA records 477 func (p *dns) SaveChangelist(ctx context.Context, zone *ZoneCreate) error { 478 // This lock will restrict the concurrency of API calls 479 // to 1 save request at a time. This is needed for the Soa.Serial value which 480 // is required to be incremented for every subsequent update to a zone 481 // so we have to save just one request at a time to ensure this is always 482 // incremented properly 483 484 zoneWriteLock.Lock() 485 defer zoneWriteLock.Unlock() 486 487 logger := p.Log(ctx) 488 logger.Debug("SaveChangeList") 489 490 reqbody, err := convertStructToReqBody("") 491 if err != nil { 492 return fmt.Errorf("failed to generate request body: %w", err) 493 } 494 495 postURL := fmt.Sprintf("/config-dns/v2/changelists/?zone=%s", zone.Zone) 496 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqbody) 497 if err != nil { 498 return fmt.Errorf("failed to create SaveChangeList request: %w", err) 499 } 500 501 resp, err := p.Exec(req, nil) 502 if err != nil { 503 return fmt.Errorf("SaveChangeList request failed: %w", err) 504 } 505 506 if resp.StatusCode != http.StatusCreated { 507 return p.Error(resp) 508 } 509 510 return nil 511 } 512 513 // Save changelist for the Zone to create default NS SOA records 514 func (p *dns) SubmitChangelist(ctx context.Context, zone *ZoneCreate) error { 515 // This lock will restrict the concurrency of API calls 516 // to 1 save request at a time. This is needed for the Soa.Serial value which 517 // is required to be incremented for every subsequent update to a zone 518 // so we have to save just one request at a time to ensure this is always 519 // incremented properly 520 521 zoneWriteLock.Lock() 522 defer zoneWriteLock.Unlock() 523 524 logger := p.Log(ctx) 525 logger.Debug("SubmitChangeList") 526 527 reqbody, err := convertStructToReqBody("") 528 if err != nil { 529 return fmt.Errorf("failed to generate request body: %w", err) 530 } 531 532 postURL := fmt.Sprintf("/config-dns/v2/changelists/%s/submit", zone.Zone) 533 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqbody) 534 if err != nil { 535 return fmt.Errorf("failed to create SubmitChangeList request: %w", err) 536 } 537 538 resp, err := p.Exec(req, nil) 539 if err != nil { 540 return fmt.Errorf("SubmitChangeList request failed: %w", err) 541 } 542 543 if resp.StatusCode != http.StatusNoContent { 544 return p.Error(resp) 545 } 546 547 return nil 548 } 549 550 // Save updates the Zone 551 func (p *dns) UpdateZone(ctx context.Context, zone *ZoneCreate, _ ZoneQueryString) error { 552 // This lock will restrict the concurrency of API calls 553 // to 1 save request at a time. This is needed for the Soa.Serial value which 554 // is required to be incremented for every subsequent update to a zone 555 // so we have to save just one request at a time to ensure this is always 556 // incremented properly 557 558 zoneWriteLock.Lock() 559 defer zoneWriteLock.Unlock() 560 561 logger := p.Log(ctx) 562 logger.Debug("Zone Update") 563 564 if err := p.ValidateZone(ctx, zone); err != nil { 565 return err 566 } 567 568 zoneMap := filterZoneCreate(zone) 569 reqbody, err := convertStructToReqBody(zoneMap) 570 if err != nil { 571 return fmt.Errorf("failed to generate request body: %w", err) 572 } 573 574 putURL := fmt.Sprintf("/config-dns/v2/zones/%s", zone.Zone) 575 req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, reqbody) 576 if err != nil { 577 return fmt.Errorf("failed to create Get Update request: %w", err) 578 } 579 580 var zoneresp ZoneResponse 581 resp, err := p.Exec(req, &zoneresp) 582 if err != nil { 583 return fmt.Errorf("Zone Update request failed: %w", err) 584 } 585 586 if resp.StatusCode != http.StatusOK { 587 return p.Error(resp) 588 } 589 590 return nil 591 592 } 593 594 // Zone Delete. 595 func (p *dns) DeleteZone(ctx context.Context, zone *ZoneCreate, _ ZoneQueryString) error { 596 // remove all the records except for SOA 597 // which is required and save the zone 598 599 zoneWriteLock.Lock() 600 defer zoneWriteLock.Unlock() 601 602 logger := p.Log(ctx) 603 logger.Debug("Zone Delete") 604 605 if zone.Zone == "" { 606 return fmt.Errorf("Zone name missing") 607 } 608 609 deleteURL := fmt.Sprintf("/config-dns/v2/zones/%s", zone.Zone) 610 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, deleteURL, nil) 611 if err != nil { 612 return fmt.Errorf("failed to create Zone Delete request: %w", err) 613 } 614 615 resp, err := p.Exec(req, nil) 616 if err != nil { 617 return fmt.Errorf("Zone Delete request failed: %w", err) 618 } 619 620 if resp.StatusCode == http.StatusNotFound { 621 return nil 622 } 623 624 if resp.StatusCode != http.StatusNoContent { 625 return p.Error(resp) 626 } 627 628 return nil 629 630 } 631 632 func filterZoneCreate(zone *ZoneCreate) map[string]interface{} { 633 634 zoneType := strings.ToUpper(zone.Type) 635 filteredZone := make(map[string]interface{}) 636 zoneElems := reflect.ValueOf(zone).Elem() 637 for i := 0; i < zoneElems.NumField(); i++ { 638 varName := zoneElems.Type().Field(i).Name 639 varLower := zoneStructMap[varName] 640 varValue := zoneElems.Field(i).Interface() 641 switch varName { 642 case "Target": 643 if zoneType == "ALIAS" { 644 filteredZone[varLower] = varValue 645 } 646 case "TsigKey": 647 if zoneType == "SECONDARY" { 648 filteredZone[varLower] = varValue 649 } 650 case "Masters": 651 if zoneType == "SECONDARY" { 652 filteredZone[varLower] = varValue 653 } 654 case "SignAndServe": 655 if zoneType != "ALIAS" { 656 filteredZone[varLower] = varValue 657 } 658 case "SignAndServeAlgorithm": 659 if zoneType != "ALIAS" { 660 filteredZone[varLower] = varValue 661 } 662 default: 663 filteredZone[varLower] = varValue 664 } 665 } 666 667 return filteredZone 668 } 669 670 // Validate ZoneCreate Object 671 func (p *dns) ValidateZone(ctx context.Context, zone *ZoneCreate) error { 672 673 logger := p.Log(ctx) 674 logger.Debug("ValidateZone") 675 676 if len(zone.Zone) == 0 { 677 return fmt.Errorf("Zone name is required") 678 } 679 ztype := strings.ToUpper(zone.Type) 680 if ztype != "PRIMARY" && ztype != "SECONDARY" && ztype != "ALIAS" { 681 return fmt.Errorf("Invalid zone type") 682 } 683 if ztype != "SECONDARY" && zone.TsigKey != nil { 684 return fmt.Errorf("TsigKey is invalid for %s zone type", ztype) 685 } 686 if ztype == "ALIAS" { 687 if len(zone.Target) == 0 { 688 return fmt.Errorf("Target is required for Alias zone type") 689 } 690 if zone.Masters != nil && len(zone.Masters) > 0 { 691 return fmt.Errorf("Masters is invalid for Alias zone type") 692 } 693 if zone.SignAndServe { 694 return fmt.Errorf("SignAndServe is invalid for Alias zone type") 695 } 696 if len(zone.SignAndServeAlgorithm) > 0 { 697 return fmt.Errorf("SignAndServeAlgorithm is invalid for Alias zone type") 698 } 699 return nil 700 } 701 // Primary or Secondary 702 if len(zone.Target) > 0 { 703 return fmt.Errorf("Target is invalid for %s zone type", ztype) 704 } 705 if zone.Masters != nil && len(zone.Masters) > 0 && ztype == "PRIMARY" { 706 return fmt.Errorf("Masters is invalid for Primary zone type") 707 } 708 709 return nil 710 } 711 712 // Get Zone's Names 713 func (p *dns) GetZoneNames(ctx context.Context, zone string) (*ZoneNamesResponse, error) { 714 715 logger := p.Log(ctx) 716 logger.Debug("GetZoneNames") 717 718 var znresponse ZoneNamesResponse 719 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names", zone) 720 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 721 if err != nil { 722 return nil, fmt.Errorf("failed to create GetZoneNames request: %w", err) 723 } 724 725 resp, err := p.Exec(req, &znresponse) 726 if err != nil { 727 return nil, fmt.Errorf("GetZoneNames request failed: %w", err) 728 } 729 730 if resp.StatusCode != http.StatusOK { 731 return nil, p.Error(resp) 732 } 733 734 return &znresponse, nil 735 } 736 737 // Get Zone Name's record types 738 func (p *dns) GetZoneNameTypes(ctx context.Context, zname string, zone string) (*ZoneNameTypesResponse, error) { 739 740 logger := p.Log(ctx) 741 logger.Debug(" GetZoneNameTypes") 742 743 var zntypes ZoneNameTypesResponse 744 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types", zone, zname) 745 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 746 if err != nil { 747 return nil, fmt.Errorf("failed to create GetZoneNameTypes request: %w", err) 748 } 749 750 resp, err := p.Exec(req, &zntypes) 751 if err != nil { 752 return nil, fmt.Errorf("GetZoneNameTypes request failed: %w", err) 753 } 754 755 if resp.StatusCode != http.StatusOK { 756 return nil, p.Error(resp) 757 } 758 759 return &zntypes, nil 760 }