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 }