github.com/akamai/AkamaiOPEN-edgegrid-golang/v4@v4.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  //
    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  }