github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/configdns/record.go (about) 1 package dns 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 8 "net" 9 "sync" 10 ) 11 12 // The record types implemented and their fields are as defined here 13 // https://developer.akamai.com/api/luna/config-dns/data.html 14 15 // Records contains operations available on a Record resource 16 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html 17 type Records interface { 18 // RecordToMap returns a map containing record content 19 RecordToMap(context.Context, *RecordBody) map[string]interface{} 20 // Return bare bones tsig key struct 21 NewRecordBody(context.Context, RecordBody) *RecordBody 22 // GetRecordList retrieves recordset list based on type 23 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getzonerecordsets 24 GetRecordList(context.Context, string, string, string) (*RecordSetResponse, error) 25 // GetRdata retrieves record rdata, e.g. target 26 GetRdata(context.Context, string, string, string) ([]string, error) 27 // ProcessRdata 28 ProcessRdata(context.Context, []string, string) []string 29 // ParseRData parses rdata. returning map 30 ParseRData(context.Context, string, []string) map[string]interface{} 31 // GetRecord retrieves a recordset and returns as RecordBody 32 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#getzonerecordset 33 GetRecord(context.Context, string, string, string) (*RecordBody, error) 34 // CreateRecord creates recordset 35 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#postzonerecordset 36 CreateRecord(context.Context, *RecordBody, string, ...bool) error 37 // DeleteRecord removes recordset 38 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#deletezonerecordset 39 DeleteRecord(context.Context, *RecordBody, string, ...bool) error 40 // UpdateRecord replaces the recordset 41 // See: https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html#putzonerecordset 42 UpdateRecord(context.Context, *RecordBody, string, ...bool) error 43 // FullIPv6 is utility method to convert IP to string 44 FullIPv6(context.Context, net.IP) string 45 // PadCoordinates is utility method to convert IP to normalize coordinates 46 PadCoordinates(context.Context, string) string 47 } 48 49 // RecordBody contains request body for dns record 50 type RecordBody struct { 51 Name string `json:"name,omitempty"` 52 RecordType string `json:"type,omitempty"` 53 TTL int `json:"ttl,omitempty"` 54 // Active field no longer used in v2 55 Active bool `json:"active,omitempty"` 56 Target []string `json:"rdata,omitempty"` 57 } 58 59 var ( 60 zoneRecordWriteLock sync.Mutex 61 ) 62 63 // Validate validates RecordBody 64 func (rec *RecordBody) Validate() error { 65 66 if len(rec.Name) < 1 { 67 return fmt.Errorf("Record body is missing Name") 68 } 69 if len(rec.RecordType) < 1 { 70 return fmt.Errorf("Record body is missing RecordType") 71 } 72 if rec.TTL == 0 { 73 return fmt.Errorf("Record body is missing TTL") 74 } 75 if rec.Target == nil || len(rec.Target) < 1 { 76 return fmt.Errorf("Record body is missing Target") 77 } 78 79 return nil 80 } 81 82 func (p *dns) RecordToMap(ctx context.Context, record *RecordBody) map[string]interface{} { 83 84 logger := p.Log(ctx) 85 logger.Debug("RecordToMap") 86 87 if err := record.Validate(); err != nil { 88 logger.Errorf("Record to map failed. %w", err) 89 return nil 90 } 91 92 return map[string]interface{}{ 93 "name": record.Name, 94 "ttl": record.TTL, 95 "recordtype": record.RecordType, 96 // active no longer used 97 "active": record.Active, 98 "target": record.Target, 99 } 100 } 101 102 func (p *dns) NewRecordBody(ctx context.Context, params RecordBody) *RecordBody { 103 104 logger := p.Log(ctx) 105 logger.Debug("NewRecordBody") 106 107 recordbody := &RecordBody{Name: params.Name} 108 return recordbody 109 } 110 111 // Eval option lock arg passed into writable endpoints. Default is true, e.g. lock 112 func localLock(lockArg []bool) bool { 113 114 for _, lock := range lockArg { 115 // should only be one entry 116 return lock 117 } 118 119 return true 120 121 } 122 123 func (p *dns) CreateRecord(ctx context.Context, record *RecordBody, zone string, recLock ...bool) error { 124 // This lock will restrict the concurrency of API calls 125 // to 1 save request at a time. This is needed for the Soa.Serial value which 126 // is required to be incremented for every subsequent update to a zone 127 // so we have to save just one request at a time to ensure this is always 128 // incremented properly 129 130 if localLock(recLock) { 131 zoneRecordWriteLock.Lock() 132 defer zoneRecordWriteLock.Unlock() 133 } 134 135 logger := p.Log(ctx) 136 logger.Debug("CreateRecord") 137 logger.Debugf("DNS Lib Create Record: [%v]", record) 138 if err := record.Validate(); err != nil { 139 logger.Errorf("Record content not valid: %w", err) 140 return fmt.Errorf("Record content not valid. [%w]", err) 141 } 142 143 reqbody, err := convertStructToReqBody(record) 144 if err != nil { 145 return fmt.Errorf("failed to generate request body: %w", err) 146 } 147 148 var rec RecordBody 149 postURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, record.Name, record.RecordType) 150 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqbody) 151 if err != nil { 152 return fmt.Errorf("failed to create CreateRecord request: %w", err) 153 } 154 155 resp, err := p.Exec(req, &rec) 156 if err != nil { 157 return fmt.Errorf("CreateRecord request failed: %w", err) 158 } 159 160 if resp.StatusCode != http.StatusCreated { 161 return p.Error(resp) 162 } 163 164 return nil 165 } 166 167 func (p *dns) UpdateRecord(ctx context.Context, record *RecordBody, zone string, recLock ...bool) error { 168 // This lock will restrict the concurrency of API calls 169 // to 1 save request at a time. This is needed for the Soa.Serial value which 170 // is required to be incremented for every subsequent update to a zone 171 // so we have to save just one request at a time to ensure this is always 172 // incremented properly 173 174 if localLock(recLock) { 175 zoneRecordWriteLock.Lock() 176 defer zoneRecordWriteLock.Unlock() 177 } 178 179 logger := p.Log(ctx) 180 logger.Debug("UpdateRecord") 181 logger.Debugf("DNS Lib Update Record: [%v]", record) 182 if err := record.Validate(); err != nil { 183 logger.Errorf("Record content not valid: %s", err.Error()) 184 return fmt.Errorf("Record content not valid. [%w]", err) 185 } 186 187 reqbody, err := convertStructToReqBody(record) 188 if err != nil { 189 return fmt.Errorf("failed to generate request body: %w", err) 190 } 191 192 var rec RecordBody 193 putURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, record.Name, record.RecordType) 194 req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, reqbody) 195 if err != nil { 196 return fmt.Errorf("failed to create UpdateRecord request: %w", err) 197 } 198 199 resp, err := p.Exec(req, &rec) 200 if err != nil { 201 return fmt.Errorf("UpdateRecord request failed: %w", err) 202 } 203 204 if resp.StatusCode != http.StatusOK { 205 return p.Error(resp) 206 } 207 208 return nil 209 } 210 211 func (p *dns) DeleteRecord(ctx context.Context, record *RecordBody, zone string, recLock ...bool) error { 212 // This lock will restrict the concurrency of API calls 213 // to 1 save request at a time. This is needed for the Soa.Serial value which 214 // is required to be incremented for every subsequent update to a zone 215 // so we have to save just one request at a time to ensure this is always 216 // incremented properly 217 218 if localLock(recLock) { 219 zoneRecordWriteLock.Lock() 220 defer zoneRecordWriteLock.Unlock() 221 } 222 223 logger := p.Log(ctx) 224 logger.Debug("DeleteRecord") 225 226 if err := record.Validate(); err != nil { 227 logger.Errorf("Record content not valid: %w", err) 228 return fmt.Errorf("Record content not valid. [%w]", err) 229 } 230 231 //var mtbody string 232 deleteURL := fmt.Sprintf("/config-dns/v2/zones/%s/names/%s/types/%s", zone, record.Name, record.RecordType) 233 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, deleteURL, nil) 234 if err != nil { 235 return fmt.Errorf("failed to create DeleteRecord request: %w", err) 236 } 237 238 resp, err := p.Exec(req, nil) //, &mtbody) 239 if err != nil { 240 return fmt.Errorf("DeleteRecord request failed: %w", err) 241 } 242 243 if resp.StatusCode != http.StatusNoContent { 244 return p.Error(resp) 245 } 246 247 return nil 248 }