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