github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/dns/record.go (about)

     1  package dns
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"sync"
     9  )
    10  
    11  // Records contains operations available on a Record resource.
    12  type Records interface {
    13  	// GetRecordList retrieves recordset list based on type.
    14  	//
    15  	// See: https://techdocs.akamai.com/edge-dns/reference/get-zones-zone-recordsets
    16  	GetRecordList(context.Context, string, string, string) (*RecordSetResponse, error)
    17  	// GetRdata retrieves record rdata, e.g. target.
    18  	GetRdata(context.Context, string, string, string) ([]string, error)
    19  	// ProcessRdata process rdata.
    20  	ProcessRdata(context.Context, []string, string) []string
    21  	// ParseRData parses rdata. returning map.
    22  	ParseRData(context.Context, string, []string) map[string]interface{}
    23  	// GetRecord retrieves a recordset and returns as RecordBody.
    24  	//
    25  	// See:  https://techdocs.akamai.com/edge-dns/reference/get-zone-name-type
    26  	GetRecord(context.Context, string, string, string) (*RecordBody, error)
    27  	// CreateRecord creates recordset.
    28  	//
    29  	// See: https://techdocs.akamai.com/edge-dns/reference/post-zones-zone-names-name-types-type
    30  	CreateRecord(context.Context, *RecordBody, string, ...bool) error
    31  	// DeleteRecord removes recordset.
    32  	//
    33  	// See: https://techdocs.akamai.com/edge-dns/reference/delete-zone-name-type
    34  	DeleteRecord(context.Context, *RecordBody, string, ...bool) error
    35  	// UpdateRecord replaces the recordset.
    36  	//
    37  	// See: https://techdocs.akamai.com/edge-dns/reference/put-zones-zone-names-name-types-type
    38  	UpdateRecord(context.Context, *RecordBody, string, ...bool) error
    39  }
    40  
    41  // RecordBody contains request body for dns record
    42  type RecordBody struct {
    43  	Name       string   `json:"name,omitempty"`
    44  	RecordType string   `json:"type,omitempty"`
    45  	TTL        int      `json:"ttl,omitempty"`
    46  	Active     bool     `json:"active,omitempty"`
    47  	Target     []string `json:"rdata,omitempty"`
    48  }
    49  
    50  var (
    51  	zoneRecordWriteLock sync.Mutex
    52  )
    53  
    54  // Validate validates RecordBody
    55  func (rec *RecordBody) Validate() error {
    56  	if len(rec.Name) < 1 {
    57  		return fmt.Errorf("RecordBody is missing Name")
    58  	}
    59  	if len(rec.RecordType) < 1 {
    60  		return fmt.Errorf("RecordBody is missing RecordType")
    61  	}
    62  	if rec.TTL == 0 {
    63  		return fmt.Errorf("RecordBody is missing TTL")
    64  	}
    65  	if rec.Target == nil || len(rec.Target) < 1 {
    66  		return fmt.Errorf("RecordBody is missing Target")
    67  	}
    68  
    69  	return nil
    70  }
    71  
    72  // Eval option lock arg passed into writable endpoints. Default is true, e.g. lock
    73  func localLock(lockArg []bool) bool {
    74  	for _, lock := range lockArg {
    75  		// should only be one entry
    76  		return lock
    77  	}
    78  
    79  	return true
    80  }
    81  
    82  func (d *dns) CreateRecord(ctx context.Context, record *RecordBody, zone string, recLock ...bool) error {
    83  	// This lock will restrict the concurrency of API calls
    84  	// to 1 save request at a time. This is needed for the Soa.Serial value which
    85  	// is required to be incremented for every subsequent update to a zone,
    86  	// so we have to save just one request at a time to ensure this is always
    87  	// incremented properly
    88  
    89  	if localLock(recLock) {
    90  		zoneRecordWriteLock.Lock()
    91  		defer zoneRecordWriteLock.Unlock()
    92  	}
    93  
    94  	logger := d.Log(ctx)
    95  	logger.Debug("CreateRecord")
    96  	logger.Debugf("DNS Lib Create Record: [%v]", record)
    97  	if err := record.Validate(); err != nil {
    98  		logger.Errorf("Record content not valid: %w", err)
    99  		return fmt.Errorf("CreateRecord content not valid. [%w]", err)
   100  	}
   101  
   102  	reqBody, err := convertStructToReqBody(record)
   103  	if err != nil {
   104  		return fmt.Errorf("failed to generate request body: %w", err)
   105  	}
   106  
   107  	postURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, record.Name, record.RecordType)
   108  	req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqBody)
   109  	if err != nil {
   110  		return fmt.Errorf("failed to create CreateRecord request: %w", err)
   111  	}
   112  
   113  	resp, err := d.Exec(req, nil)
   114  	if err != nil {
   115  		return fmt.Errorf("CreateRecord request failed: %w", err)
   116  	}
   117  
   118  	if resp.StatusCode != http.StatusCreated {
   119  		return d.Error(resp)
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  func (d *dns) UpdateRecord(ctx context.Context, record *RecordBody, zone string, recLock ...bool) error {
   126  	// This lock will restrict the concurrency of API calls
   127  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   128  	// is required to be incremented for every subsequent update to a zone
   129  	// so we have to save just one request at a time to ensure this is always
   130  	// incremented properly
   131  
   132  	if localLock(recLock) {
   133  		zoneRecordWriteLock.Lock()
   134  		defer zoneRecordWriteLock.Unlock()
   135  	}
   136  
   137  	logger := d.Log(ctx)
   138  	logger.Debug("UpdateRecord")
   139  	logger.Debugf("DNS Lib Update Record: [%v]", record)
   140  	if err := record.Validate(); err != nil {
   141  		logger.Errorf("Record content not valid: %s", err.Error())
   142  		return fmt.Errorf("UpdateRecord content not valid. [%w]", err)
   143  	}
   144  
   145  	reqBody, err := convertStructToReqBody(record)
   146  	if err != nil {
   147  		return fmt.Errorf("failed to generate request body: %w", err)
   148  	}
   149  
   150  	putURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, record.Name, record.RecordType)
   151  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, reqBody)
   152  	if err != nil {
   153  		return fmt.Errorf("failed to create UpdateRecord request: %w", err)
   154  	}
   155  
   156  	resp, err := d.Exec(req, nil)
   157  	if err != nil {
   158  		return fmt.Errorf("UpdateRecord request failed: %w", err)
   159  	}
   160  
   161  	if resp.StatusCode != http.StatusOK {
   162  		return d.Error(resp)
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  func (d *dns) DeleteRecord(ctx context.Context, record *RecordBody, zone string, recLock ...bool) error {
   169  	// This lock will restrict the concurrency of API calls
   170  	// to 1 save request at a time. This is needed for the Soa.Serial value which
   171  	// is required to be incremented for every subsequent update to a zone
   172  	// so we have to save just one request at a time to ensure this is always
   173  	// incremented properly
   174  
   175  	if localLock(recLock) {
   176  		zoneRecordWriteLock.Lock()
   177  		defer zoneRecordWriteLock.Unlock()
   178  	}
   179  
   180  	logger := d.Log(ctx)
   181  	logger.Debug("DeleteRecord")
   182  
   183  	if err := record.Validate(); err != nil {
   184  		logger.Errorf("Record content not valid: %w", err)
   185  		return fmt.Errorf("DeleteRecord content not valid. [%w]", err)
   186  	}
   187  
   188  	deleteURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, record.Name, record.RecordType)
   189  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, deleteURL, nil)
   190  	if err != nil {
   191  		return fmt.Errorf("failed to create DeleteRecord request: %w", err)
   192  	}
   193  
   194  	resp, err := d.Exec(req, nil)
   195  	if err != nil {
   196  		return fmt.Errorf("DeleteRecord request failed: %w", err)
   197  	}
   198  
   199  	if resp.StatusCode != http.StatusNoContent {
   200  		return d.Error(resp)
   201  	}
   202  
   203  	return nil
   204  }