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 }