github.com/akamai/AkamaiOPEN-edgegrid-golang/v2@v2.17.0/pkg/configgtm/domain.go (about) 1 package gtm 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 8 "reflect" 9 "strings" 10 "unicode" 11 ) 12 13 // 14 // Support gtm domains thru Edgegrid 15 // Based on 1.4 Schema 16 // 17 18 // Domains contains operations available on a Domain resource 19 // See: https://developer.akamai.com/api/web_performance/global_traffic_management/v1.html 20 type Domains interface { 21 // Retrieve map of null fields 22 NullFieldMap(context.Context, *Domain) (*NullFieldMapStruct, error) 23 // NewDomain is a utility function that creates a new Domain object. 24 NewDomain(context.Context, string, string) *Domain 25 // GetStatus retrieves current status for the given domainname. 26 // See: https://developer.akamai.com/api/web_performance/global_traffic_management/v1.html#getcurrentstatus 27 GetDomainStatus(context.Context, string) (*ResponseStatus, error) 28 // ListDomains retrieves all Domains. 29 // See: https://developer.akamai.com/api/web_performance/global_traffic_management/v1.html#getdomains 30 ListDomains(context.Context) ([]*DomainItem, error) 31 // GetDomain retrieves a Domain with the given domainname. 32 // See: https://developer.akamai.com/api/web_performance/global_traffic_management/v1.html#getdomain 33 GetDomain(context.Context, string) (*Domain, error) 34 // CreateDomain creates domain 35 // See: https://developer.akamai.com/api/web_performance/global_traffic_management/v1.html#postdomains 36 CreateDomain(context.Context, *Domain, map[string]string) (*DomainResponse, error) 37 // Delete is a method applied to a domain object resulting in removal. 38 // See: ** Not Supported by API ** 39 DeleteDomain(context.Context, *Domain) (*ResponseStatus, error) 40 // Update is a method applied to a domain object resulting in an update. 41 // See: https://developer.akamai.com/api/web_performance/global_traffic_management/v1.html#postdomains 42 UpdateDomain(context.Context, *Domain, map[string]string) (*ResponseStatus, error) 43 } 44 45 // The Domain data structure represents a GTM domain 46 type Domain struct { 47 Name string `json:"name"` 48 Type string `json:"type"` 49 AsMaps []*AsMap `json:"asMaps,omitempty"` 50 Resources []*Resource `json:"resources,omitempty"` 51 DefaultUnreachableThreshold float32 `json:"defaultUnreachableThreshold,omitempty"` 52 EmailNotificationList []string `json:"emailNotificationList,omitempty"` 53 MinPingableRegionFraction float32 `json:"minPingableRegionFraction,omitempty"` 54 DefaultTimeoutPenalty int `json:"defaultTimeoutPenalty,omitempty"` 55 Datacenters []*Datacenter `json:"datacenters,omitempty"` 56 ServermonitorLivenessCount int `json:"servermonitorLivenessCount,omitempty"` 57 RoundRobinPrefix string `json:"roundRobinPrefix,omitempty"` 58 ServermonitorLoadCount int `json:"servermonitorLoadCount,omitempty"` 59 PingInterval int `json:"pingInterval,omitempty"` 60 MaxTTL int64 `json:"maxTTL,omitempty"` 61 LoadImbalancePercentage float64 `json:"loadImbalancePercentage,omitempty"` 62 DefaultHealthMax float64 `json:"defaultHealthMax,omitempty"` 63 LastModified string `json:"lastModified,omitempty"` 64 Status *ResponseStatus `json:"status,omitempty"` 65 MapUpdateInterval int `json:"mapUpdateInterval,omitempty"` 66 MaxProperties int `json:"maxProperties,omitempty"` 67 MaxResources int `json:"maxResources,omitempty"` 68 DefaultSslClientPrivateKey string `json:"defaultSslClientPrivateKey,omitempty"` 69 DefaultErrorPenalty int `json:"defaultErrorPenalty,omitempty"` 70 Links []*Link `json:"links,omitempty"` 71 Properties []*Property `json:"properties,omitempty"` 72 MaxTestTimeout float64 `json:"maxTestTimeout,omitempty"` 73 CnameCoalescingEnabled bool `json:"cnameCoalescingEnabled"` 74 DefaultHealthMultiplier float64 `json:"defaultHealthMultiplier,omitempty"` 75 ServermonitorPool string `json:"servermonitorPool,omitempty"` 76 LoadFeedback bool `json:"loadFeedback"` 77 MinTTL int64 `json:"minTTL,omitempty"` 78 GeographicMaps []*GeoMap `json:"geographicMaps,omitempty"` 79 CidrMaps []*CidrMap `json:"cidrMaps,omitempty"` 80 DefaultMaxUnreachablePenalty int `json:"defaultMaxUnreachablePenalty"` 81 DefaultHealthThreshold float64 `json:"defaultHealthThreshold,omitempty"` 82 LastModifiedBy string `json:"lastModifiedBy,omitempty"` 83 ModificationComments string `json:"modificationComments,omitempty"` 84 MinTestInterval int `json:"minTestInterval,omitempty"` 85 PingPacketSize int `json:"pingPacketSize,omitempty"` 86 DefaultSslClientCertificate string `json:"defaultSslClientCertificate,omitempty"` 87 EndUserMappingEnabled bool `json:"endUserMappingEnabled"` 88 } 89 90 // DomainsList contains a list of domain items 91 type DomainsList struct { 92 DomainItems []*DomainItem `json:"items"` 93 } 94 95 // DomainItem is a DomainsList item 96 type DomainItem struct { 97 AcgId string `json:"acgId"` 98 LastModified string `json:"lastModified"` 99 Links []*Link `json:"links"` 100 Name string `json:"name"` 101 Status string `json:"status"` 102 } 103 104 // Validate validates Domain 105 func (dom *Domain) Validate() error { 106 107 if len(dom.Name) < 1 { 108 return fmt.Errorf("Domain is missing Name") 109 } 110 if len(dom.Type) < 1 { 111 return fmt.Errorf("Domain is missing Type") 112 } 113 114 return nil 115 } 116 117 // NewDomain is a utility function that creates a new Domain object. 118 func (p *gtm) NewDomain(ctx context.Context, domainName, domainType string) *Domain { 119 120 logger := p.Log(ctx) 121 logger.Debug("NewDomain") 122 123 domain := &Domain{} 124 domain.Name = domainName 125 domain.Type = domainType 126 return domain 127 } 128 129 // GetStatus retrieves current status for the given domainname. 130 func (p *gtm) GetDomainStatus(ctx context.Context, domainName string) (*ResponseStatus, error) { 131 132 logger := p.Log(ctx) 133 logger.Debug("GetDomainStatus") 134 135 var stat ResponseStatus 136 getURL := fmt.Sprintf("/config-gtm/v1/domains/%s/status/current", domainName) 137 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 138 if err != nil { 139 return nil, fmt.Errorf("failed to create GetDomain request: %w", err) 140 } 141 setVersionHeader(req, schemaVersion) 142 resp, err := p.Exec(req, &stat) 143 if err != nil { 144 return nil, fmt.Errorf("GetDomain request failed: %w", err) 145 } 146 147 if resp.StatusCode != http.StatusOK { 148 return nil, p.Error(resp) 149 } 150 151 return &stat, nil 152 } 153 154 // ListDomains retrieves all Domains. 155 func (p *gtm) ListDomains(ctx context.Context) ([]*DomainItem, error) { 156 157 logger := p.Log(ctx) 158 logger.Debug("ListDomains") 159 160 var domains DomainsList 161 getURL := fmt.Sprintf("/config-gtm/v1/domains") 162 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 163 if err != nil { 164 return nil, fmt.Errorf("failed to create ListDomains request: %w", err) 165 } 166 setVersionHeader(req, schemaVersion) 167 resp, err := p.Exec(req, &domains) 168 if err != nil { 169 return nil, fmt.Errorf("ListDomains request failed: %w", err) 170 } 171 172 if resp.StatusCode != http.StatusOK { 173 return nil, p.Error(resp) 174 } 175 176 return domains.DomainItems, nil 177 } 178 179 // GetDomain retrieves a Domain with the given domainname. 180 func (p *gtm) GetDomain(ctx context.Context, domainName string) (*Domain, error) { 181 182 logger := p.Log(ctx) 183 logger.Debug("GetDomain") 184 185 var domain Domain 186 getURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domainName) 187 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 188 if err != nil { 189 return nil, fmt.Errorf("failed to create GetDomain request: %w", err) 190 } 191 setVersionHeader(req, schemaVersion) 192 resp, err := p.Exec(req, &domain) 193 if err != nil { 194 return nil, fmt.Errorf("GetDomain request failed: %w", err) 195 } 196 197 if resp.StatusCode != http.StatusOK { 198 return nil, p.Error(resp) 199 } 200 201 return &domain, nil 202 } 203 204 // save method; Create or Update 205 func (dom *Domain) save(_ context.Context, p *gtm, queryArgs map[string]string, req *http.Request) (*DomainResponse, error) { 206 207 // set schema version 208 setVersionHeader(req, schemaVersion) 209 210 // Look for optional args 211 if len(queryArgs) > 0 { 212 q := req.URL.Query() 213 if val, ok := queryArgs["contractId"]; ok { 214 q.Add("contractId", strings.TrimPrefix(val, "ctr_")) 215 } 216 if val, ok := queryArgs["gid"]; ok { 217 q.Add("gid", strings.TrimPrefix(val, "grp_")) 218 } 219 req.URL.RawQuery = q.Encode() 220 } 221 222 var dresp DomainResponse 223 resp, err := p.Exec(req, &dresp, dom) 224 if err != nil { 225 return nil, fmt.Errorf("Domain request failed: %w", err) 226 } 227 228 if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { 229 return nil, p.Error(resp) 230 } 231 232 return &dresp, nil 233 234 } 235 236 // CreateDomain is a method applied to a domain object resulting in creation. 237 func (p *gtm) CreateDomain(ctx context.Context, domain *Domain, queryArgs map[string]string) (*DomainResponse, error) { 238 239 logger := p.Log(ctx) 240 logger.Debug("CreateDomain") 241 242 if err := domain.Validate(); err != nil { 243 logger.Errorf("Domain validation failed. %w", err) 244 return nil, fmt.Errorf("Domain validation failed. %w", err) 245 } 246 247 postURL := fmt.Sprintf("/config-gtm/v1/domains/") 248 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, nil) 249 if err != nil { 250 return nil, fmt.Errorf("failed to create CreateDomain request: %w", err) 251 } 252 253 return domain.save(ctx, p, queryArgs, req) 254 255 } 256 257 // UpdateDomain is a method applied to a domain object resulting in an update. 258 func (p *gtm) UpdateDomain(ctx context.Context, domain *Domain, queryArgs map[string]string) (*ResponseStatus, error) { 259 260 logger := p.Log(ctx) 261 logger.Debug("UpdateDomain") 262 263 if err := domain.Validate(); err != nil { 264 logger.Errorf("Domain validation failed. %w", err) 265 return nil, fmt.Errorf("Domain validation failed. %w", err) 266 } 267 268 putURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domain.Name) 269 req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil) 270 if err != nil { 271 return nil, fmt.Errorf("failed to create UpdateDomain request: %w", err) 272 } 273 274 stat, err := domain.save(ctx, p, queryArgs, req) 275 if err != nil { 276 return nil, err 277 } 278 return stat.Status, err 279 } 280 281 // DeleteDomain is a method applied to a domain object resulting in removal. 282 func (p *gtm) DeleteDomain(ctx context.Context, domain *Domain) (*ResponseStatus, error) { 283 284 logger := p.Log(ctx) 285 logger.Debug("DeleteDomain") 286 287 delURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domain.Name) 288 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, delURL, nil) 289 if err != nil { 290 return nil, fmt.Errorf("failed to create DeleteDomain request: %w", err) 291 } 292 293 var responseBody ResponseBody 294 setVersionHeader(req, schemaVersion) 295 296 resp, err := p.Exec(req, &responseBody) 297 if err != nil { 298 return nil, fmt.Errorf("Delete Domain request failed: %w", err) 299 } 300 301 if resp.StatusCode != http.StatusOK { 302 return nil, p.Error(resp) 303 } 304 305 return responseBody.Status, nil 306 } 307 308 // NullPerObjectAttributeStruct represents core and child null object attributes 309 type NullPerObjectAttributeStruct struct { 310 CoreObjectFields map[string]string 311 ChildObjectFields map[string]interface{} // NullObjectAttributeStruct 312 } 313 314 // NullFieldMapStruct returned null Objects structure 315 type NullFieldMapStruct struct { 316 Domain NullPerObjectAttributeStruct // entry is domain 317 Properties map[string]NullPerObjectAttributeStruct // entries are properties 318 Datacenters map[string]NullPerObjectAttributeStruct // entries are datacenters 319 Resources map[string]NullPerObjectAttributeStruct // entries are resources 320 CidrMaps map[string]NullPerObjectAttributeStruct // entries are cidrmaps 321 GeoMaps map[string]NullPerObjectAttributeStruct // entries are geomaps 322 AsMaps map[string]NullPerObjectAttributeStruct // entries are asmaps 323 } 324 325 // ObjectMap represents ObjectMap datatype 326 type ObjectMap map[string]interface{} 327 328 // NullFieldMap retrieves map of null fields 329 func (p *gtm) NullFieldMap(ctx context.Context, domain *Domain) (*NullFieldMapStruct, error) { 330 331 logger := p.Log(ctx) 332 logger.Debug("NullFieldMap") 333 334 if err := domain.Validate(); err != nil { 335 logger.Errorf("Domain validation failed. %w", err) 336 return nil, fmt.Errorf("Domain validation failed. %w", err) 337 } 338 339 var nullFieldMap = &NullFieldMapStruct{} 340 var domFields = NullPerObjectAttributeStruct{} 341 domainMap := make(map[string]string) 342 var objMap ObjectMap 343 344 getURL := fmt.Sprintf("/config-gtm/v1/domains/%s", domain.Name) 345 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 346 if err != nil { 347 return nil, fmt.Errorf("failed to create GetDomain request: %w", err) 348 } 349 setVersionHeader(req, schemaVersion) 350 resp, err := p.Exec(req, &objMap) 351 if err != nil { 352 return nil, fmt.Errorf("GetDomain request failed: %w", err) 353 } 354 355 if resp.StatusCode != http.StatusOK { 356 return nil, p.Error(resp) 357 } 358 359 for i, d := range objMap { 360 objval := fmt.Sprint(d) 361 if fmt.Sprintf("%T", d) == "<nil>" { 362 if objval == "<nil>" { 363 domainMap[makeFirstCharUpperCase(i)] = "" 364 } 365 continue 366 } 367 list, ok := d.([]interface{}) 368 if !ok { 369 continue 370 } 371 372 switch i { 373 case "properties": 374 nullFieldMap.Properties = processObjectList(list) 375 case "datacenters": 376 nullFieldMap.Datacenters = processObjectList(list) 377 case "resources": 378 nullFieldMap.Resources = processObjectList(list) 379 case "cidrMaps": 380 nullFieldMap.CidrMaps = processObjectList(list) 381 case "geographicMaps": 382 nullFieldMap.GeoMaps = processObjectList(list) 383 case "asMaps": 384 nullFieldMap.AsMaps = processObjectList(list) 385 } 386 } 387 388 domFields.CoreObjectFields = domainMap 389 nullFieldMap.Domain = domFields 390 391 return nullFieldMap, nil 392 393 } 394 395 func makeFirstCharUpperCase(origString string) string { 396 397 a := []rune(origString) 398 a[0] = unicode.ToUpper(a[0]) 399 // hack 400 if origString == "cname" { 401 a[1] = unicode.ToUpper(a[1]) 402 } 403 return string(a) 404 } 405 406 func processObjectList(objectList []interface{}) map[string]NullPerObjectAttributeStruct { 407 408 nullObjectsList := make(map[string]NullPerObjectAttributeStruct) 409 for _, obj := range objectList { 410 nullObjectFields := NullPerObjectAttributeStruct{} 411 objectName := "" 412 objectDCID := "" 413 objectMap := make(map[string]string) 414 objectChildList := make(map[string]interface{}) 415 for objf, objd := range obj.(map[string]interface{}) { 416 objval := fmt.Sprint(objd) 417 switch fmt.Sprintf("%T", objd) { 418 case "<nil>": 419 if objval == "<nil>" { 420 objectMap[makeFirstCharUpperCase(objf)] = "" 421 } 422 case "map[string]interface {}": 423 // include null stand alone struct elements in core 424 for moname, movalue := range objd.(map[string]interface{}) { 425 if fmt.Sprintf("%T", movalue) == "<nil>" { 426 objectMap[makeFirstCharUpperCase(moname)] = "" 427 } 428 } 429 case "[]interface {}": 430 iSlice := objd.([]interface{}) 431 if len(iSlice) > 0 && reflect.TypeOf(iSlice[0]).Kind() != reflect.String && reflect.TypeOf(iSlice[0]).Kind() != reflect.Int64 && reflect.TypeOf(iSlice[0]).Kind() != reflect.Float64 && reflect.TypeOf(iSlice[0]).Kind() != reflect.Int32 { 432 objectChildList[makeFirstCharUpperCase(objf)] = processObjectList(objd.([]interface{})) 433 } 434 default: 435 if objf == "name" { 436 objectName = objval 437 } 438 if objf == "datacenterId" { 439 objectDCID = objval 440 } 441 } 442 } 443 nullObjectFields.CoreObjectFields = objectMap 444 nullObjectFields.ChildObjectFields = objectChildList 445 446 if objectDCID == "" { 447 if objectName != "" { 448 nullObjectsList[objectName] = nullObjectFields 449 } else { 450 nullObjectsList["unknown"] = nullObjectFields // TODO: What if mnore than one? 451 } 452 } else { 453 nullObjectsList[objectDCID] = nullObjectFields 454 } 455 } 456 457 return nullObjectsList 458 459 }