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