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 }