github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/dns/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  	Zones interface {
    25  		// ListZones retrieves a list of all zones user can access.
    26  		//
    27  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zones
    28  		ListZones(context.Context, ...ZoneListQueryArgs) (*ZoneListResponse, error)
    29  		// GetZone retrieves Zone metadata.
    30  		//
    31  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zone
    32  		GetZone(context.Context, string) (*ZoneResponse, error)
    33  		//GetChangeList retrieves Zone changelist.
    34  		//
    35  		// See: https://techdocs.akamai.com/edge-dns/reference/get-changelists-zone
    36  		GetChangeList(context.Context, string) (*ChangeListResponse, error)
    37  		// GetMasterZoneFile retrieves master zone file.
    38  		//
    39  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zones-zone-zone-file
    40  		GetMasterZoneFile(context.Context, string) (string, error)
    41  		// PostMasterZoneFile updates master zone file.
    42  		//
    43  		// See: https://techdocs.akamai.com/edge-dns/reference/post-zones-zone-zone-file
    44  		PostMasterZoneFile(context.Context, string, string) error
    45  		// CreateZone creates new zone.
    46  		//
    47  		// See: https://techdocs.akamai.com/edge-dns/reference/post-zone
    48  		CreateZone(context.Context, *ZoneCreate, ZoneQueryString, ...bool) error
    49  		// SaveChangelist creates a new Change List based on the most recent version of a zone.
    50  		//
    51  		// See: https://techdocs.akamai.com/edge-dns/reference/post-changelists
    52  		SaveChangelist(context.Context, *ZoneCreate) error
    53  		// SubmitChangelist submits changelist for the Zone to create default NS SOA records.
    54  		//
    55  		// See: https://techdocs.akamai.com/edge-dns/reference/post-changelists-zone-submit
    56  		SubmitChangelist(context.Context, *ZoneCreate) error
    57  		// UpdateZone updates zone.
    58  		//
    59  		// See: https://techdocs.akamai.com/edge-dns/reference/put-zone
    60  		UpdateZone(context.Context, *ZoneCreate, ZoneQueryString) error
    61  		// GetZoneNames retrieves a list of a zone's record names.
    62  		//
    63  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zone-names
    64  		GetZoneNames(context.Context, string) (*ZoneNamesResponse, error)
    65  		// GetZoneNameTypes retrieves a zone name's record types.
    66  		//
    67  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zone-name-types
    68  		GetZoneNameTypes(context.Context, string, string) (*ZoneNameTypesResponse, error)
    69  		// CreateBulkZones submits create bulk zone request.
    70  		//
    71  		// See: https://techdocs.akamai.com/edge-dns/reference/post-zones-create-requests
    72  		CreateBulkZones(context.Context, *BulkZonesCreate, ZoneQueryString) (*BulkZonesResponse, error)
    73  		// DeleteBulkZones submits delete bulk zone request.
    74  		//
    75  		// See: https://techdocs.akamai.com/edge-dns/reference/post-zones-delete-requests
    76  		DeleteBulkZones(context.Context, *ZoneNameListResponse, ...bool) (*BulkZonesResponse, error)
    77  		// GetBulkZoneCreateStatus retrieves submit request status.
    78  		//
    79  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zones-create-requests-requestid
    80  		GetBulkZoneCreateStatus(context.Context, string) (*BulkStatusResponse, error)
    81  		//GetBulkZoneDeleteStatus retrieves submit request status.
    82  		//
    83  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zones-delete-requests-requestid
    84  		GetBulkZoneDeleteStatus(context.Context, string) (*BulkStatusResponse, error)
    85  		// GetBulkZoneCreateResult retrieves create request result.
    86  		//
    87  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zones-create-requests-requestid-result
    88  		GetBulkZoneCreateResult(ctx context.Context, requestid string) (*BulkCreateResultResponse, error)
    89  		// GetBulkZoneDeleteResult retrieves delete request result.
    90  		//
    91  		// See: https://techdocs.akamai.com/edge-dns/reference/get-zones-delete-requests-requestid-result
    92  		GetBulkZoneDeleteResult(context.Context, string) (*BulkDeleteResultResponse, error)
    93  	}
    94  
    95  	// ZoneQueryString contains zone query parameters
    96  	ZoneQueryString struct {
    97  		Contract string
    98  		Group    string
    99  	}
   100  
   101  	// ZoneCreate contains zone create request
   102  	ZoneCreate struct {
   103  		Zone                  string   `json:"zone"`
   104  		Type                  string   `json:"type"`
   105  		Masters               []string `json:"masters,omitempty"`
   106  		Comment               string   `json:"comment,omitempty"`
   107  		SignAndServe          bool     `json:"signAndServe"`
   108  		SignAndServeAlgorithm string   `json:"signAndServeAlgorithm,omitempty"`
   109  		TSIGKey               *TSIGKey `json:"tsigKey,omitempty"`
   110  		Target                string   `json:"target,omitempty"`
   111  		EndCustomerID         string   `json:"endCustomerId,omitempty"`
   112  		ContractID            string   `json:"contractId,omitempty"`
   113  	}
   114  
   115  	// ZoneResponse contains zone create response
   116  	ZoneResponse struct {
   117  		Zone                  string   `json:"zone,omitempty"`
   118  		Type                  string   `json:"type,omitempty"`
   119  		Masters               []string `json:"masters,omitempty"`
   120  		Comment               string   `json:"comment,omitempty"`
   121  		SignAndServe          bool     `json:"signAndServe"`
   122  		SignAndServeAlgorithm string   `json:"signAndServeAlgorithm,omitempty"`
   123  		TSIGKey               *TSIGKey `json:"tsigKey,omitempty"`
   124  		Target                string   `json:"target,omitempty"`
   125  		EndCustomerID         string   `json:"endCustomerId,omitempty"`
   126  		ContractID            string   `json:"contractId,omitempty"`
   127  		AliasCount            int64    `json:"aliasCount,omitempty"`
   128  		ActivationState       string   `json:"activationState,omitempty"`
   129  		LastActivationDate    string   `json:"lastActivationDate,omitempty"`
   130  		LastModifiedBy        string   `json:"lastModifiedBy,omitempty"`
   131  		LastModifiedDate      string   `json:"lastModifiedDate,omitempty"`
   132  		VersionID             string   `json:"versionId,omitempty"`
   133  	}
   134  
   135  	// ZoneListQueryArgs contains parameters for List Zones query
   136  	ZoneListQueryArgs struct {
   137  		ContractIDs string
   138  		Page        int
   139  		PageSize    int
   140  		Search      string
   141  		ShowAll     bool
   142  		SortBy      string
   143  		Types       string
   144  	}
   145  
   146  	// ListMetadata contains metadata for List Zones request
   147  	ListMetadata struct {
   148  		ContractIDs   []string `json:"contractIds"`
   149  		Page          int      `json:"page"`
   150  		PageSize      int      `json:"pageSize"`
   151  		ShowAll       bool     `json:"showAll"`
   152  		TotalElements int      `json:"totalElements"`
   153  	}
   154  
   155  	// ZoneListResponse contains response for List Zones request
   156  	ZoneListResponse struct {
   157  		Metadata *ListMetadata   `json:"metadata,omitempty"`
   158  		Zones    []*ZoneResponse `json:"zones,omitempty"`
   159  	}
   160  
   161  	// ChangeListResponse contains metadata about a change list
   162  	ChangeListResponse struct {
   163  		Zone             string `json:"zone,omitempty"`
   164  		ChangeTag        string `json:"changeTag,omitempty"`
   165  		ZoneVersionID    string `json:"zoneVersionId,omitempty"`
   166  		LastModifiedDate string `json:"lastModifiedDate,omitempty"`
   167  		Stale            bool   `json:"stale,omitempty"`
   168  	}
   169  
   170  	// ZoneNameListResponse contains response with a list of zone's names and aliases
   171  	ZoneNameListResponse struct {
   172  		Zones   []string `json:"zones"`
   173  		Aliases []string `json:"aliases,omitempty"`
   174  	}
   175  
   176  	// ZoneNamesResponse contains record set names for zone
   177  	ZoneNamesResponse struct {
   178  		Names []string `json:"names"`
   179  	}
   180  
   181  	// ZoneNameTypesResponse contains record set types for zone
   182  	ZoneNameTypesResponse struct {
   183  		Types []string `json:"types"`
   184  	}
   185  )
   186  
   187  var zoneStructMap = map[string]string{
   188  	"Zone":                  "zone",
   189  	"Type":                  "type",
   190  	"Masters":               "masters",
   191  	"Comment":               "comment",
   192  	"SignAndServe":          "signAndServe",
   193  	"SignAndServeAlgorithm": "signAndServeAlgorithm",
   194  	"TSIGKey":               "tsigKey",
   195  	"Target":                "target",
   196  	"EndCustomerID":         "endCustomerId",
   197  	"ContractId":            "contractId"}
   198  
   199  // Util to convert struct to http request body, eg. io.reader
   200  func convertStructToReqBody(srcStruct interface{}) (io.Reader, error) {
   201  	reqBody, err := json.Marshal(srcStruct)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	return bytes.NewBuffer(reqBody), nil
   206  }
   207  
   208  func (d *dns) ListZones(ctx context.Context, queryArgs ...ZoneListQueryArgs) (*ZoneListResponse, error) {
   209  	logger := d.Log(ctx)
   210  	logger.Debug("ListZones")
   211  
   212  	getURL := fmt.Sprintf("/config-dns/v2/zones")
   213  	if len(queryArgs) > 1 {
   214  		return nil, fmt.Errorf("ListZones QueryArgs invalid")
   215  	}
   216  
   217  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   218  	if err != nil {
   219  		return nil, fmt.Errorf("failed to create listzones request: %w", err)
   220  	}
   221  
   222  	q := req.URL.Query()
   223  	if len(queryArgs) > 0 {
   224  		if queryArgs[0].Page > 0 {
   225  			q.Add("page", strconv.Itoa(queryArgs[0].Page))
   226  		}
   227  		if queryArgs[0].PageSize > 0 {
   228  			q.Add("pageSize", strconv.Itoa(queryArgs[0].PageSize))
   229  		}
   230  		if queryArgs[0].Search != "" {
   231  			q.Add("search", queryArgs[0].Search)
   232  		}
   233  		q.Add("showAll", strconv.FormatBool(queryArgs[0].ShowAll))
   234  		if queryArgs[0].SortBy != "" {
   235  			q.Add("sortBy", queryArgs[0].SortBy)
   236  		}
   237  		if queryArgs[0].Types != "" {
   238  			q.Add("types", queryArgs[0].Types)
   239  		}
   240  		if queryArgs[0].ContractIDs != "" {
   241  			q.Add("contractIds", queryArgs[0].ContractIDs)
   242  		}
   243  		req.URL.RawQuery = q.Encode()
   244  	}
   245  
   246  	var result ZoneListResponse
   247  	resp, err := d.Exec(req, &result)
   248  	if err != nil {
   249  		return nil, fmt.Errorf("listzones request failed: %w", err)
   250  	}
   251  
   252  	if resp.StatusCode != http.StatusOK {
   253  		return nil, d.Error(resp)
   254  	}
   255  
   256  	return &result, nil
   257  }
   258  
   259  func (d *dns) GetZone(ctx context.Context, zoneName string) (*ZoneResponse, error) {
   260  	logger := d.Log(ctx)
   261  	logger.Debug("GetZone")
   262  
   263  	getURL := fmt.Sprintf("/config-dns/v2/zones/%s", zoneName)
   264  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   265  	if err != nil {
   266  		return nil, fmt.Errorf("failed to create GetZone request: %w", err)
   267  	}
   268  
   269  	var result ZoneResponse
   270  	resp, err := d.Exec(req, &result)
   271  	if err != nil {
   272  		return nil, fmt.Errorf("GetZone request failed: %w", err)
   273  	}
   274  
   275  	if resp.StatusCode != http.StatusOK {
   276  		return nil, d.Error(resp)
   277  	}
   278  
   279  	return &result, nil
   280  }
   281  
   282  func (d *dns) GetChangeList(ctx context.Context, zone string) (*ChangeListResponse, error) {
   283  	logger := d.Log(ctx)
   284  	logger.Debug("GetChangeList")
   285  
   286  	getURL := fmt.Sprintf("/config-dns/v2/changelists/%s", zone)
   287  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   288  	if err != nil {
   289  		return nil, fmt.Errorf("failed to create GetChangeList request: %w", err)
   290  	}
   291  
   292  	var result ChangeListResponse
   293  	resp, err := d.Exec(req, &result)
   294  	if err != nil {
   295  		return nil, fmt.Errorf("GetChangeList request failed: %w", err)
   296  	}
   297  
   298  	if resp.StatusCode != http.StatusOK {
   299  		return nil, d.Error(resp)
   300  	}
   301  
   302  	return &result, nil
   303  }
   304  
   305  func (d *dns) GetMasterZoneFile(ctx context.Context, zone string) (string, error) {
   306  	logger := d.Log(ctx)
   307  	logger.Debug("GetMasterZoneFile")
   308  
   309  	getURL := fmt.Sprintf("/config-dns/v2/zones/%s/zone-file", zone)
   310  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   311  	if err != nil {
   312  		return "", fmt.Errorf("failed to create GetMasterZoneFile request: %w", err)
   313  	}
   314  	req.Header.Add("Accept", "text/dns")
   315  
   316  	resp, err := d.Exec(req, nil)
   317  	if err != nil {
   318  		return "", fmt.Errorf("GetMasterZoneFile request failed: %w", err)
   319  	}
   320  
   321  	if resp.StatusCode != http.StatusOK {
   322  		return "", d.Error(resp)
   323  	}
   324  
   325  	masterFile, err := ioutil.ReadAll(resp.Body)
   326  	if err != nil {
   327  		return "", fmt.Errorf("GetMasterZoneFile request failed: %w", err)
   328  	}
   329  
   330  	return string(masterFile), nil
   331  }
   332  
   333  func (d *dns) PostMasterZoneFile(ctx context.Context, zone string, fileData string) error {
   334  	logger := d.Log(ctx)
   335  	logger.Debug("PostMasterZoneFile")
   336  
   337  	mtResp := ""
   338  	pmzfURL := fmt.Sprintf("/config-dns/v2/zones/%s/zone-file", zone)
   339  	buf := bytes.NewReader([]byte(fileData))
   340  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, pmzfURL, buf)
   341  	if err != nil {
   342  		return fmt.Errorf("failed to create PostMasterZoneFile request: %w", err)
   343  	}
   344  
   345  	req.Header.Set("Content-Type", "text/dns")
   346  
   347  	resp, err := d.Exec(req, &mtResp)
   348  	if err != nil {
   349  		return fmt.Errorf("Create PostMasterZoneFile failed: %w", err)
   350  	}
   351  
   352  	if resp.StatusCode != http.StatusNoContent {
   353  		return d.Error(resp)
   354  	}
   355  
   356  	return nil
   357  }
   358  
   359  func (d *dns) CreateZone(ctx context.Context, zone *ZoneCreate, zoneQueryString ZoneQueryString, clearConn ...bool) error {
   360  	// This lock will restrict the concurrency of API calls
   361  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   362  	// is required to be incremented for every subsequent update to a zone,
   363  	// so we have to save just one request at a time to ensure this is always
   364  	// incremented properly
   365  
   366  	zoneWriteLock.Lock()
   367  	defer zoneWriteLock.Unlock()
   368  
   369  	logger := d.Log(ctx)
   370  	logger.Debug("Zone Create")
   371  
   372  	if err := ValidateZone(zone); err != nil {
   373  		return err
   374  	}
   375  
   376  	zoneMap := filterZoneCreate(zone)
   377  
   378  	var zoneResponse ZoneResponse
   379  	zoneURL := "/config-dns/v2/zones/?contractId=" + zoneQueryString.Contract
   380  	if len(zoneQueryString.Group) > 0 {
   381  		zoneURL += "&gid=" + zoneQueryString.Group
   382  	}
   383  
   384  	reqBody, err := convertStructToReqBody(zoneMap)
   385  	if err != nil {
   386  		return fmt.Errorf("failed to generate request body: %w", err)
   387  	}
   388  
   389  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, zoneURL, reqBody)
   390  	if err != nil {
   391  		return fmt.Errorf("failed to create Zone Create request: %w", err)
   392  	}
   393  
   394  	resp, err := d.Exec(req, &zoneResponse)
   395  	if err != nil {
   396  		return fmt.Errorf("Create Zone request failed: %w", err)
   397  	}
   398  
   399  	if resp.StatusCode != http.StatusCreated {
   400  		return d.Error(resp)
   401  	}
   402  
   403  	if strings.ToUpper(zone.Type) == "PRIMARY" {
   404  		// Timing issue with Create immediately followed by SaveChangelist
   405  		for _, clear := range clearConn {
   406  			// should only be one entry
   407  			if clear {
   408  				logger.Info("Clearing Idle Connections")
   409  				d.Client().CloseIdleConnections()
   410  			}
   411  		}
   412  	}
   413  
   414  	return nil
   415  }
   416  
   417  func (d *dns) SaveChangelist(ctx context.Context, zone *ZoneCreate) error {
   418  	// This lock will restrict the concurrency of API calls
   419  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   420  	// is required to be incremented for every subsequent update to a zone
   421  	// so we have to save just one request at a time to ensure this is always
   422  	// incremented properly
   423  
   424  	zoneWriteLock.Lock()
   425  	defer zoneWriteLock.Unlock()
   426  
   427  	logger := d.Log(ctx)
   428  	logger.Debug("SaveChangeList")
   429  
   430  	reqBody, err := convertStructToReqBody("")
   431  	if err != nil {
   432  		return fmt.Errorf("failed to generate request body: %w", err)
   433  	}
   434  
   435  	postURL := fmt.Sprintf("/config-dns/v2/changelists/?zone=%s", zone.Zone)
   436  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqBody)
   437  	if err != nil {
   438  		return fmt.Errorf("failed to create SaveChangeList request: %w", err)
   439  	}
   440  
   441  	resp, err := d.Exec(req, nil)
   442  	if err != nil {
   443  		return fmt.Errorf("SaveChangeList request failed: %w", err)
   444  	}
   445  
   446  	if resp.StatusCode != http.StatusCreated {
   447  		return d.Error(resp)
   448  	}
   449  
   450  	return nil
   451  }
   452  
   453  func (d *dns) SubmitChangelist(ctx context.Context, zone *ZoneCreate) error {
   454  	// This lock will restrict the concurrency of API calls
   455  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   456  	// is required to be incremented for every subsequent update to a zone
   457  	// so we have to save just one request at a time to ensure this is always
   458  	// incremented properly
   459  
   460  	zoneWriteLock.Lock()
   461  	defer zoneWriteLock.Unlock()
   462  
   463  	logger := d.Log(ctx)
   464  	logger.Debug("SubmitChangeList")
   465  
   466  	reqBody, err := convertStructToReqBody("")
   467  	if err != nil {
   468  		return fmt.Errorf("failed to generate request body: %w", err)
   469  	}
   470  
   471  	postURL := fmt.Sprintf("/config-dns/v2/changelists/%s/submit", zone.Zone)
   472  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqBody)
   473  	if err != nil {
   474  		return fmt.Errorf("failed to create SubmitChangeList request: %w", err)
   475  	}
   476  
   477  	resp, err := d.Exec(req, nil)
   478  	if err != nil {
   479  		return fmt.Errorf("SubmitChangeList request failed: %w", err)
   480  	}
   481  
   482  	if resp.StatusCode != http.StatusNoContent {
   483  		return d.Error(resp)
   484  	}
   485  
   486  	return nil
   487  }
   488  
   489  func (d *dns) UpdateZone(ctx context.Context, zone *ZoneCreate, _ ZoneQueryString) error {
   490  	// This lock will restrict the concurrency of API calls
   491  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   492  	// is required to be incremented for every subsequent update to a zone
   493  	// so we have to save just one request at a time to ensure this is always
   494  	// incremented properly
   495  
   496  	zoneWriteLock.Lock()
   497  	defer zoneWriteLock.Unlock()
   498  
   499  	logger := d.Log(ctx)
   500  	logger.Debug("Zone Update")
   501  
   502  	if err := ValidateZone(zone); err != nil {
   503  		return err
   504  	}
   505  
   506  	zoneMap := filterZoneCreate(zone)
   507  	reqBody, err := convertStructToReqBody(zoneMap)
   508  	if err != nil {
   509  		return fmt.Errorf("failed to generate request body: %w", err)
   510  	}
   511  
   512  	putURL := fmt.Sprintf("/config-dns/v2/zones/%s", zone.Zone)
   513  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, reqBody)
   514  	if err != nil {
   515  		return fmt.Errorf("failed to create Get Update request: %w", err)
   516  	}
   517  
   518  	var result ZoneResponse
   519  	resp, err := d.Exec(req, &result)
   520  	if err != nil {
   521  		return fmt.Errorf("Zone Update request failed: %w", err)
   522  	}
   523  
   524  	if resp.StatusCode != http.StatusOK {
   525  		return d.Error(resp)
   526  	}
   527  
   528  	return nil
   529  }
   530  
   531  func filterZoneCreate(zone *ZoneCreate) map[string]interface{} {
   532  	zoneType := strings.ToUpper(zone.Type)
   533  	filteredZone := make(map[string]interface{})
   534  	zoneElems := reflect.ValueOf(zone).Elem()
   535  	for i := 0; i < zoneElems.NumField(); i++ {
   536  		varName := zoneElems.Type().Field(i).Name
   537  		varLower := zoneStructMap[varName]
   538  		varValue := zoneElems.Field(i).Interface()
   539  		switch varName {
   540  		case "Target":
   541  			if zoneType == "ALIAS" {
   542  				filteredZone[varLower] = varValue
   543  			}
   544  		case "TsigKey":
   545  			if zoneType == "SECONDARY" {
   546  				filteredZone[varLower] = varValue
   547  			}
   548  		case "Masters":
   549  			if zoneType == "SECONDARY" {
   550  				filteredZone[varLower] = varValue
   551  			}
   552  		case "SignAndServe":
   553  			if zoneType != "ALIAS" {
   554  				filteredZone[varLower] = varValue
   555  			}
   556  		case "SignAndServeAlgorithm":
   557  			if zoneType != "ALIAS" {
   558  				filteredZone[varLower] = varValue
   559  			}
   560  		default:
   561  			filteredZone[varLower] = varValue
   562  		}
   563  	}
   564  
   565  	return filteredZone
   566  }
   567  
   568  // ValidateZone validates ZoneCreate Object
   569  func ValidateZone(zone *ZoneCreate) error {
   570  	if len(zone.Zone) == 0 {
   571  		return fmt.Errorf("Zone name is required")
   572  	}
   573  	zType := strings.ToUpper(zone.Type)
   574  	if zType != "PRIMARY" && zType != "SECONDARY" && zType != "ALIAS" {
   575  		return fmt.Errorf("Invalid zone type")
   576  	}
   577  	if zType != "SECONDARY" && zone.TSIGKey != nil {
   578  		return fmt.Errorf("TsigKey is invalid for %s zone type", zType)
   579  	}
   580  	if zType == "ALIAS" {
   581  		if len(zone.Target) == 0 {
   582  			return fmt.Errorf("Target is required for Alias zone type")
   583  		}
   584  		if zone.Masters != nil && len(zone.Masters) > 0 {
   585  			return fmt.Errorf("Masters is invalid for Alias zone type")
   586  		}
   587  		if zone.SignAndServe {
   588  			return fmt.Errorf("SignAndServe is invalid for Alias zone type")
   589  		}
   590  		if len(zone.SignAndServeAlgorithm) > 0 {
   591  			return fmt.Errorf("SignAndServeAlgorithm is invalid for Alias zone type")
   592  		}
   593  		return nil
   594  	}
   595  	// Primary or Secondary
   596  	if len(zone.Target) > 0 {
   597  		return fmt.Errorf("Target is invalid for %s zone type", zType)
   598  	}
   599  	if zone.Masters != nil && len(zone.Masters) > 0 && zType == "PRIMARY" {
   600  		return fmt.Errorf("Masters is invalid for Primary zone type")
   601  	}
   602  
   603  	return nil
   604  }
   605  
   606  func (d *dns) GetZoneNames(ctx context.Context, zone string) (*ZoneNamesResponse, error) {
   607  	logger := d.Log(ctx)
   608  	logger.Debug("GetZoneNames")
   609  
   610  	getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names", zone)
   611  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   612  	if err != nil {
   613  		return nil, fmt.Errorf("failed to create GetZoneNames request: %w", err)
   614  	}
   615  
   616  	var result ZoneNamesResponse
   617  	resp, err := d.Exec(req, &result)
   618  	if err != nil {
   619  		return nil, fmt.Errorf("GetZoneNames request failed: %w", err)
   620  	}
   621  
   622  	if resp.StatusCode != http.StatusOK {
   623  		return nil, d.Error(resp)
   624  	}
   625  
   626  	return &result, nil
   627  }
   628  
   629  func (d *dns) GetZoneNameTypes(ctx context.Context, zName, zone string) (*ZoneNameTypesResponse, error) {
   630  	logger := d.Log(ctx)
   631  	logger.Debug(" GetZoneNameTypes")
   632  
   633  	getURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types", zone, zName)
   634  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   635  	if err != nil {
   636  		return nil, fmt.Errorf("failed to create GetZoneNameTypes request: %w", err)
   637  	}
   638  
   639  	var result ZoneNameTypesResponse
   640  	resp, err := d.Exec(req, &result)
   641  	if err != nil {
   642  		return nil, fmt.Errorf("GetZoneNameTypes request failed: %w", err)
   643  	}
   644  
   645  	if resp.StatusCode != http.StatusOK {
   646  		return nil, d.Error(resp)
   647  	}
   648  
   649  	return &result, nil
   650  }