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