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  }