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  }