github.com/akamai/AkamaiOPEN-edgegrid-golang/v5@v5.0.0/pkg/dns/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  type RecordSets interface {
    20  	// NewRecordSetResponse returns new response object.
    21  	NewRecordSetResponse(context.Context, string) *RecordSetResponse
    22  	// GetRecordsets retrieves recordsets with Query Args. No formatting of arg values.
    23  	//
    24  	// See: See: https://techdocs.akamai.com/edge-dns/reference/get-zones-zone-recordsets
    25  	GetRecordsets(context.Context, string, ...RecordsetQueryArgs) (*RecordSetResponse, error)
    26  	// CreateRecordsets creates multiple recordsets.
    27  	//
    28  	// See: https://techdocs.akamai.com/edge-dns/reference/post-zones-zone-recordsets
    29  	CreateRecordsets(context.Context, *Recordsets, string, ...bool) error
    30  	// UpdateRecordsets replaces list of recordsets.
    31  	//
    32  	// See: https://techdocs.akamai.com/edge-dns/reference/put-zones-zone-recordsets
    33  	UpdateRecordsets(context.Context, *Recordsets, string, ...bool) error
    34  }
    35  
    36  // RecordsetQueryArgs contains query parameters for recordset request
    37  type RecordsetQueryArgs struct {
    38  	Page     int
    39  	PageSize int
    40  	Search   string
    41  	ShowAll  bool
    42  	SortBy   string
    43  	Types    string
    44  }
    45  
    46  // Recordsets Struct. Used for Create and Update Recordsets. Contains a list of Recordset objects
    47  type Recordsets struct {
    48  	Recordsets []Recordset `json:"recordsets"`
    49  }
    50  
    51  // Recordset contains recordset metadata
    52  type Recordset struct {
    53  	Name  string   `json:"name"`
    54  	Type  string   `json:"type"`
    55  	TTL   int      `json:"ttl"`
    56  	Rdata []string `json:"rdata"`
    57  } //`json:"recordsets"`
    58  
    59  // MetadataH contains metadata of RecordSet response
    60  type MetadataH struct {
    61  	LastPage      int  `json:"lastPage"`
    62  	Page          int  `json:"page"`
    63  	PageSize      int  `json:"pageSize"`
    64  	ShowAll       bool `json:"showAll"`
    65  	TotalElements int  `json:"totalElements"`
    66  } //`json:"metadata"`
    67  
    68  // RecordSetResponse contains a response with a list of recordsets
    69  type RecordSetResponse struct {
    70  	Metadata   MetadataH   `json:"metadata"`
    71  	Recordsets []Recordset `json:"recordsets"`
    72  }
    73  
    74  // Validate validates Recordsets
    75  func (rs *Recordsets) Validate() error {
    76  
    77  	if len(rs.Recordsets) < 1 {
    78  		return fmt.Errorf("Request initiated with empty recordsets list")
    79  	}
    80  	for _, rec := range rs.Recordsets {
    81  		err := validation.Errors{
    82  			"Name":  validation.Validate(rec.Name, validation.Required),
    83  			"Type":  validation.Validate(rec.Type, validation.Required),
    84  			"TTL":   validation.Validate(rec.TTL, validation.Required),
    85  			"Rdata": validation.Validate(rec.Rdata, validation.Required),
    86  		}.Filter()
    87  		if err != nil {
    88  			return err
    89  		}
    90  	}
    91  	return nil
    92  }
    93  
    94  func (p *dns) NewRecordSetResponse(_ context.Context, _ string) *RecordSetResponse {
    95  	recordset := &RecordSetResponse{}
    96  	return recordset
    97  }
    98  
    99  func (p *dns) GetRecordsets(ctx context.Context, zone string, queryArgs ...RecordsetQueryArgs) (*RecordSetResponse, error) {
   100  
   101  	logger := p.Log(ctx)
   102  	logger.Debug("GetRecordsets")
   103  
   104  	if len(queryArgs) > 1 {
   105  		return nil, fmt.Errorf("invalid arguments GetRecordsets QueryArgs")
   106  	}
   107  
   108  	var recordsetResp RecordSetResponse
   109  	getURL := fmt.Sprintf("/config-dns/v2/zones/%s/recordsets", zone)
   110  
   111  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   112  	if err != nil {
   113  		return nil, fmt.Errorf("failed to create GetRecordsets request: %w", err)
   114  	}
   115  
   116  	q := req.URL.Query()
   117  	if len(queryArgs) > 0 {
   118  		if queryArgs[0].Page > 0 {
   119  			q.Add("page", strconv.Itoa(queryArgs[0].Page))
   120  		}
   121  		if queryArgs[0].PageSize > 0 {
   122  			q.Add("pageSize", strconv.Itoa(queryArgs[0].PageSize))
   123  		}
   124  		if queryArgs[0].Search != "" {
   125  			q.Add("search", queryArgs[0].Search)
   126  		}
   127  		q.Add("showAll", strconv.FormatBool(queryArgs[0].ShowAll))
   128  		if queryArgs[0].SortBy != "" {
   129  			q.Add("sortBy", queryArgs[0].SortBy)
   130  		}
   131  		if queryArgs[0].Types != "" {
   132  			q.Add("types", queryArgs[0].Types)
   133  		}
   134  		req.URL.RawQuery = q.Encode()
   135  	}
   136  
   137  	resp, err := p.Exec(req, &recordsetResp)
   138  	if err != nil {
   139  		return nil, fmt.Errorf("GetRecordsets request failed: %w", err)
   140  	}
   141  
   142  	if resp.StatusCode != http.StatusOK {
   143  		return nil, p.Error(resp)
   144  	}
   145  
   146  	return &recordsetResp, nil
   147  }
   148  
   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  }