github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/configdns/recordsets.go (about)

     1  package dns
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	validation "github.com/go-ozzo/ozzo-validation/v4"
     9  
    10  	"strconv"
    11  	"sync"
    12  )
    13  
    14  var (
    15  	zoneRecordsetsWriteLock sync.Mutex
    16  )
    17  
    18  // RecordSets contains operations available on a recordsets
    19  // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html
    20  type RecordSets interface {
    21  	// NewRecordSetResponse returns new response object
    22  	NewRecordSetResponse(context.Context, string) *RecordSetResponse
    23  	// GetRecordsets retrieves recordsets with Query Args. No formatting of arg values
    24  	// See: See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getzonerecordsets
    25  	GetRecordsets(context.Context, string, ...RecordsetQueryArgs) (*RecordSetResponse, error)
    26  	// CreateRecordsets creates multiple recordsets
    27  	// See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postzonerecordsets
    28  	CreateRecordsets(context.Context, *Recordsets, string, ...bool) error
    29  	// UpdateRecordsets sreplaces list of recordsets
    30  	// See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#putzonerecordsets
    31  	UpdateRecordsets(context.Context, *Recordsets, string, ...bool) error
    32  }
    33  
    34  // RecordsetQueryArgs contains query parameters for recordset request
    35  type RecordsetQueryArgs struct {
    36  	Page     int
    37  	PageSize int
    38  	Search   string
    39  	ShowAll  bool
    40  	SortBy   string
    41  	Types    string
    42  }
    43  
    44  // Recordsets Struct. Used for Create and Update Recordsets. Contains a list of Recordset objects
    45  type Recordsets struct {
    46  	Recordsets []Recordset `json:"recordsets"`
    47  }
    48  
    49  // Recordset contains recordset metadata
    50  type Recordset struct {
    51  	Name  string   `json:"name"`
    52  	Type  string   `json:"type"`
    53  	TTL   int      `json:"ttl"`
    54  	Rdata []string `json:"rdata"`
    55  } //`json:"recordsets"`
    56  
    57  // MetadataH contains metadata of RecordSet response
    58  type MetadataH struct {
    59  	LastPage      int  `json:"lastPage"`
    60  	Page          int  `json:"page"`
    61  	PageSize      int  `json:"pageSize"`
    62  	ShowAll       bool `json:"showAll"`
    63  	TotalElements int  `json:"totalElements"`
    64  } //`json:"metadata"`
    65  
    66  // RecordSetResponse contains a response with a list of recordsets
    67  type RecordSetResponse struct {
    68  	Metadata   MetadataH   `json:"metadata"`
    69  	Recordsets []Recordset `json:"recordsets"`
    70  }
    71  
    72  // Validate validates Recordsets
    73  func (rs *Recordsets) Validate() error {
    74  
    75  	if len(rs.Recordsets) < 1 {
    76  		return fmt.Errorf("Request initiated with empty recordsets list")
    77  	}
    78  	for _, rec := range rs.Recordsets {
    79  		err := validation.Errors{
    80  			"Name":  validation.Validate(rec.Name, validation.Required),
    81  			"Type":  validation.Validate(rec.Type, validation.Required),
    82  			"TTL":   validation.Validate(rec.TTL, validation.Required),
    83  			"Rdata": validation.Validate(rec.Rdata, validation.Required),
    84  		}.Filter()
    85  		if err != nil {
    86  			return err
    87  		}
    88  	}
    89  	return nil
    90  }
    91  
    92  func (p *dns) NewRecordSetResponse(_ context.Context, _ string) *RecordSetResponse {
    93  	recordset := &RecordSetResponse{}
    94  	return recordset
    95  }
    96  
    97  // Get RecordSets with Query Args. No formatting of arg values!
    98  func (p *dns) GetRecordsets(ctx context.Context, zone string, queryArgs ...RecordsetQueryArgs) (*RecordSetResponse, error) {
    99  
   100  	logger := p.Log(ctx)
   101  	logger.Debug("GetRecordsets")
   102  
   103  	if len(queryArgs) > 1 {
   104  		return nil, fmt.Errorf("invalid arguments GetRecordsets QueryArgs")
   105  	}
   106  
   107  	var recordsetResp RecordSetResponse
   108  	getURL := fmt.Sprintf("/config-dns/v2/zones/%s/recordsets", zone)
   109  
   110  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   111  	if err != nil {
   112  		return nil, fmt.Errorf("failed to create GetRecordsets request: %w", err)
   113  	}
   114  
   115  	q := req.URL.Query()
   116  	if len(queryArgs) > 0 {
   117  		if queryArgs[0].Page > 0 {
   118  			q.Add("page", strconv.Itoa(queryArgs[0].Page))
   119  		}
   120  		if queryArgs[0].PageSize > 0 {
   121  			q.Add("pageSize", strconv.Itoa(queryArgs[0].PageSize))
   122  		}
   123  		if queryArgs[0].Search != "" {
   124  			q.Add("search", queryArgs[0].Search)
   125  		}
   126  		q.Add("showAll", strconv.FormatBool(queryArgs[0].ShowAll))
   127  		if queryArgs[0].SortBy != "" {
   128  			q.Add("sortBy", queryArgs[0].SortBy)
   129  		}
   130  		if queryArgs[0].Types != "" {
   131  			q.Add("types", queryArgs[0].Types)
   132  		}
   133  		req.URL.RawQuery = q.Encode()
   134  	}
   135  
   136  	resp, err := p.Exec(req, &recordsetResp)
   137  	if err != nil {
   138  		return nil, fmt.Errorf("GetRecordsets request failed: %w", err)
   139  	}
   140  
   141  	if resp.StatusCode != http.StatusOK {
   142  		return nil, p.Error(resp)
   143  	}
   144  
   145  	return &recordsetResp, nil
   146  }
   147  
   148  // Create Recordstes
   149  func (p *dns) CreateRecordsets(ctx context.Context, recordsets *Recordsets, zone string, recLock ...bool) error {
   150  	// This lock will restrict the concurrency of API calls
   151  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   152  	// is required to be incremented for every subsequent update to a zone
   153  	// so we have to save just one request at a time to ensure this is always
   154  	// incremented properly
   155  
   156  	if localLock(recLock) {
   157  		zoneRecordsetsWriteLock.Lock()
   158  		defer zoneRecordsetsWriteLock.Unlock()
   159  	}
   160  
   161  	logger := p.Log(ctx)
   162  	logger.Debug("CreateRecordsets")
   163  
   164  	if err := recordsets.Validate(); err != nil {
   165  		return err
   166  	}
   167  
   168  	reqbody, err := convertStructToReqBody(recordsets)
   169  	if err != nil {
   170  		return fmt.Errorf("failed to generate request body: %w", err)
   171  	}
   172  
   173  	postURL := fmt.Sprintf("/config-dns/v2/zones/%s/recordsets", zone)
   174  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqbody)
   175  	if err != nil {
   176  		return fmt.Errorf("failed to create CreateRecordsets request: %w", err)
   177  	}
   178  
   179  	resp, err := p.Exec(req, nil)
   180  	if err != nil {
   181  		return fmt.Errorf("CreateRecordsets request failed: %w", err)
   182  	}
   183  
   184  	if resp.StatusCode != http.StatusNoContent {
   185  		return p.Error(resp)
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  func (p *dns) UpdateRecordsets(ctx context.Context, recordsets *Recordsets, zone string, recLock ...bool) error {
   192  	// This lock will restrict the concurrency of API calls
   193  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   194  	// is required to be incremented for every subsequent update to a zone
   195  	// so we have to save just one request at a time to ensure this is always
   196  	// incremented properly
   197  
   198  	if localLock(recLock) {
   199  		zoneRecordsetsWriteLock.Lock()
   200  		defer zoneRecordsetsWriteLock.Unlock()
   201  	}
   202  
   203  	logger := p.Log(ctx)
   204  	logger.Debug("UpdateRecordsets")
   205  
   206  	if err := recordsets.Validate(); err != nil {
   207  		return err
   208  	}
   209  
   210  	reqbody, err := convertStructToReqBody(recordsets)
   211  	if err != nil {
   212  		return fmt.Errorf("failed to generate request body: %w", err)
   213  	}
   214  
   215  	putURL := fmt.Sprintf("/config-dns/v2/zones/%s/recordsets", zone)
   216  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, reqbody)
   217  	if err != nil {
   218  		return fmt.Errorf("failed to create UpdateRecordsets request: %w", err)
   219  	}
   220  
   221  	resp, err := p.Exec(req, nil)
   222  	if err != nil {
   223  		return fmt.Errorf("UpdateRecordsets request failed: %w", err)
   224  	}
   225  
   226  	if resp.StatusCode != http.StatusNoContent {
   227  		return p.Error(resp)
   228  	}
   229  
   230  	return nil
   231  }