github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/gtm/property.go (about)

     1  package gtm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/edgegriderr"
     9  	validation "github.com/go-ozzo/ozzo-validation/v4"
    10  )
    11  
    12  // Properties contains operations available on a Property resource.
    13  type Properties interface {
    14  	// ListProperties retrieves all Properties for the provided domainName.
    15  	//
    16  	// See: https://techdocs.akamai.com/gtm/reference/get-properties
    17  	ListProperties(context.Context, string) ([]*Property, error)
    18  	// GetProperty retrieves a Property with the given domain and property names.
    19  	//
    20  	// See: https://techdocs.akamai.com/gtm/reference/get-property
    21  	GetProperty(context.Context, string, string) (*Property, error)
    22  	// CreateProperty creates property.
    23  	//
    24  	// See: https://techdocs.akamai.com/gtm/reference/put-property
    25  	CreateProperty(context.Context, *Property, string) (*PropertyResponse, error)
    26  	// DeleteProperty is a method applied to a property object resulting in removal.
    27  	//
    28  	// See: https://techdocs.akamai.com/gtm/reference/delete-property
    29  	DeleteProperty(context.Context, *Property, string) (*ResponseStatus, error)
    30  	// UpdateProperty is a method applied to a property object resulting in an update.
    31  	//
    32  	// See: https://techdocs.akamai.com/gtm/reference/put-property
    33  	UpdateProperty(context.Context, *Property, string) (*ResponseStatus, error)
    34  }
    35  
    36  // TrafficTarget struct contains information about where to direct data center traffic
    37  type TrafficTarget struct {
    38  	DatacenterID int      `json:"datacenterId"`
    39  	Enabled      bool     `json:"enabled"`
    40  	Weight       float64  `json:"weight,omitempty"`
    41  	Servers      []string `json:"servers,omitempty"`
    42  	Name         string   `json:"name,omitempty"`
    43  	HandoutCName string   `json:"handoutCName,omitempty"`
    44  	Precedence   *int     `json:"precedence,omitempty"`
    45  }
    46  
    47  // HTTPHeader struct contains HTTP headers to send if the testObjectProtocol is http or https
    48  type HTTPHeader struct {
    49  	Name  string `json:"name"`
    50  	Value string `json:"value"`
    51  }
    52  
    53  // LivenessTest contains configuration of liveness tests to determine whether your servers respond to requests
    54  type LivenessTest struct {
    55  	Name                          string        `json:"name"`
    56  	ErrorPenalty                  float64       `json:"errorPenalty,omitempty"`
    57  	PeerCertificateVerification   bool          `json:"peerCertificateVerification"`
    58  	TestInterval                  int           `json:"testInterval,omitempty"`
    59  	TestObject                    string        `json:"testObject,omitempty"`
    60  	Links                         []*Link       `json:"links,omitempty"`
    61  	RequestString                 string        `json:"requestString,omitempty"`
    62  	ResponseString                string        `json:"responseString,omitempty"`
    63  	HTTPError3xx                  bool          `json:"httpError3xx"`
    64  	HTTPError4xx                  bool          `json:"httpError4xx"`
    65  	HTTPError5xx                  bool          `json:"httpError5xx"`
    66  	HTTPMethod                    *string       `json:"httpMethod"`
    67  	HTTPRequestBody               *string       `json:"httpRequestBody"`
    68  	Disabled                      bool          `json:"disabled"`
    69  	TestObjectProtocol            string        `json:"testObjectProtocol,omitempty"`
    70  	TestObjectPassword            string        `json:"testObjectPassword,omitempty"`
    71  	TestObjectPort                int           `json:"testObjectPort,omitempty"`
    72  	SSLClientPrivateKey           string        `json:"sslClientPrivateKey,omitempty"`
    73  	SSLClientCertificate          string        `json:"sslClientCertificate,omitempty"`
    74  	Pre2023SecurityPosture        bool          `json:"pre2023SecurityPosture"`
    75  	DisableNonstandardPortWarning bool          `json:"disableNonstandardPortWarning"`
    76  	HTTPHeaders                   []*HTTPHeader `json:"httpHeaders,omitempty"`
    77  	TestObjectUsername            string        `json:"testObjectUsername,omitempty"`
    78  	TestTimeout                   float32       `json:"testTimeout,omitempty"`
    79  	TimeoutPenalty                float64       `json:"timeoutPenalty,omitempty"`
    80  	AnswersRequired               bool          `json:"answersRequired"`
    81  	ResourceType                  string        `json:"resourceType,omitempty"`
    82  	RecursionRequested            bool          `json:"recursionRequested"`
    83  	AlternateCACertificates       []string      `json:"alternateCACertificates"`
    84  }
    85  
    86  // StaticRRSet contains static recordset
    87  type StaticRRSet struct {
    88  	Type  string   `json:"type"`
    89  	TTL   int      `json:"ttl"`
    90  	Rdata []string `json:"rdata"`
    91  }
    92  
    93  // Property represents a GTM property
    94  type Property struct {
    95  	Name                      string           `json:"name"`
    96  	Type                      string           `json:"type"`
    97  	IPv6                      bool             `json:"ipv6"`
    98  	ScoreAggregationType      string           `json:"scoreAggregationType"`
    99  	StickinessBonusPercentage int              `json:"stickinessBonusPercentage,omitempty"`
   100  	StickinessBonusConstant   int              `json:"stickinessBonusConstant,omitempty"`
   101  	HealthThreshold           float64          `json:"healthThreshold,omitempty"`
   102  	UseComputedTargets        bool             `json:"useComputedTargets"`
   103  	BackupIP                  string           `json:"backupIp,omitempty"`
   104  	BalanceByDownloadScore    bool             `json:"balanceByDownloadScore"`
   105  	StaticTTL                 int              `json:"staticTTL,omitempty"`
   106  	StaticRRSets              []*StaticRRSet   `json:"staticRRSets,omitempty"`
   107  	LastModified              string           `json:"lastModified"`
   108  	UnreachableThreshold      float64          `json:"unreachableThreshold,omitempty"`
   109  	MinLiveFraction           float64          `json:"minLiveFraction,omitempty"`
   110  	HealthMultiplier          float64          `json:"healthMultiplier,omitempty"`
   111  	DynamicTTL                int              `json:"dynamicTTL,omitempty"`
   112  	MaxUnreachablePenalty     int              `json:"maxUnreachablePenalty,omitempty"`
   113  	MapName                   string           `json:"mapName,omitempty"`
   114  	HandoutLimit              int              `json:"handoutLimit"`
   115  	HandoutMode               string           `json:"handoutMode"`
   116  	FailoverDelay             int              `json:"failoverDelay,omitempty"`
   117  	BackupCName               string           `json:"backupCName,omitempty"`
   118  	FailbackDelay             int              `json:"failbackDelay,omitempty"`
   119  	LoadImbalancePercentage   float64          `json:"loadImbalancePercentage,omitempty"`
   120  	HealthMax                 float64          `json:"healthMax,omitempty"`
   121  	GhostDemandReporting      bool             `json:"ghostDemandReporting"`
   122  	Comments                  string           `json:"comments,omitempty"`
   123  	CName                     string           `json:"cname,omitempty"`
   124  	WeightedHashBitsForIPv4   int              `json:"weightedHashBitsForIPv4,omitempty"`
   125  	WeightedHashBitsForIPv6   int              `json:"weightedHashBitsForIPv6,omitempty"`
   126  	TrafficTargets            []*TrafficTarget `json:"trafficTargets,omitempty"`
   127  	Links                     []*Link          `json:"links,omitempty"`
   128  	LivenessTests             []*LivenessTest  `json:"livenessTests,omitempty"`
   129  }
   130  
   131  // PropertyList contains a list of property items
   132  type PropertyList struct {
   133  	PropertyItems []*Property `json:"items"`
   134  }
   135  
   136  // Validate validates Property
   137  func (p *Property) Validate() error {
   138  	return edgegriderr.ParseValidationErrors(validation.Errors{
   139  		"Name":                  validation.Validate(p.Name, validation.Required),
   140  		"Type":                  validation.Validate(p.Type, validation.Required),
   141  		"ScoreAggregationTypes": validation.Validate(p.ScoreAggregationType, validation.Required),
   142  		"HandoutMode":           validation.Validate(p.HandoutMode, validation.Required),
   143  		"TrafficTargets":        validation.Validate(p.TrafficTargets, validation.When(p.Type == "ranked-failover", validation.By(validateRankedFailoverTrafficTargets))),
   144  	})
   145  }
   146  
   147  // validateRankedFailoverTrafficTargets validates traffic targets when property type is 'ranked-failover'
   148  func validateRankedFailoverTrafficTargets(value interface{}) error {
   149  	tt := value.([]*TrafficTarget)
   150  	if len(tt) == 0 {
   151  		return fmt.Errorf("no traffic targets are enabled")
   152  	}
   153  	precedenceCounter := map[int]int{}
   154  	minPrecedence := 256
   155  	for _, t := range tt {
   156  		if t.Precedence == nil {
   157  			precedenceCounter[0]++
   158  			minPrecedence = 0
   159  		} else {
   160  			if *t.Precedence > 255 || *t.Precedence < 0 {
   161  				return fmt.Errorf("'Precedence' value has to be between 0 and 255")
   162  			}
   163  			precedenceCounter[*t.Precedence]++
   164  			if *t.Precedence < minPrecedence {
   165  				minPrecedence = *t.Precedence
   166  			}
   167  		}
   168  	}
   169  	if precedenceCounter[minPrecedence] > 1 {
   170  		return fmt.Errorf("property cannot have multiple primary traffic targets (targets with lowest precedence)")
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  func (g *gtm) ListProperties(ctx context.Context, domainName string) ([]*Property, error) {
   177  	logger := g.Log(ctx)
   178  	logger.Debug("ListProperties")
   179  
   180  	getURL := fmt.Sprintf("/config-gtm/v1/domains/%s/properties", domainName)
   181  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   182  	if err != nil {
   183  		return nil, fmt.Errorf("failed to create ListProperties request: %w", err)
   184  	}
   185  	setVersionHeader(req, schemaVersion)
   186  
   187  	var result PropertyList
   188  	resp, err := g.Exec(req, &result)
   189  	if err != nil {
   190  		return nil, fmt.Errorf("ListProperties request failed: %w", err)
   191  	}
   192  
   193  	if resp.StatusCode != http.StatusOK {
   194  		return nil, g.Error(resp)
   195  	}
   196  
   197  	return result.PropertyItems, nil
   198  }
   199  
   200  func (g *gtm) GetProperty(ctx context.Context, propertyName, domainName string) (*Property, error) {
   201  	logger := g.Log(ctx)
   202  	logger.Debug("GetProperty")
   203  
   204  	getURL := fmt.Sprintf("/config-gtm/v1/domains/%s/properties/%s", domainName, propertyName)
   205  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
   206  	if err != nil {
   207  		return nil, fmt.Errorf("failed to create GetProperty request: %w", err)
   208  	}
   209  	setVersionHeader(req, schemaVersion)
   210  
   211  	var result Property
   212  	resp, err := g.Exec(req, &result)
   213  	if err != nil {
   214  		return nil, fmt.Errorf("GetProperty request failed: %w", err)
   215  	}
   216  
   217  	if resp.StatusCode != http.StatusOK {
   218  		return nil, g.Error(resp)
   219  	}
   220  
   221  	return &result, nil
   222  }
   223  
   224  func (g *gtm) CreateProperty(ctx context.Context, property *Property, domainName string) (*PropertyResponse, error) {
   225  	logger := g.Log(ctx)
   226  	logger.Debug("CreateProperty")
   227  
   228  	return property.save(ctx, g, domainName)
   229  }
   230  
   231  func (g *gtm) UpdateProperty(ctx context.Context, property *Property, domainName string) (*ResponseStatus, error) {
   232  	logger := g.Log(ctx)
   233  	logger.Debug("UpdateProperty")
   234  
   235  	stat, err := property.save(ctx, g, domainName)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	return stat.Status, err
   240  }
   241  
   242  // Save Property updates method
   243  func (p *Property) save(ctx context.Context, g *gtm, domainName string) (*PropertyResponse, error) {
   244  
   245  	if err := p.Validate(); err != nil {
   246  		return nil, fmt.Errorf("property validation failed. %w", err)
   247  	}
   248  
   249  	putURL := fmt.Sprintf("/config-gtm/v1/domains/%s/properties/%s", domainName, p.Name)
   250  	req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil)
   251  	if err != nil {
   252  		return nil, fmt.Errorf("failed to create Property request: %w", err)
   253  	}
   254  	setVersionHeader(req, schemaVersion)
   255  
   256  	var result PropertyResponse
   257  	resp, err := g.Exec(req, &result, p)
   258  	if err != nil {
   259  		return nil, fmt.Errorf("property request failed: %w", err)
   260  	}
   261  
   262  	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
   263  		return nil, g.Error(resp)
   264  	}
   265  
   266  	return &result, nil
   267  }
   268  
   269  func (g *gtm) DeleteProperty(ctx context.Context, property *Property, domainName string) (*ResponseStatus, error) {
   270  	logger := g.Log(ctx)
   271  	logger.Debug("DeleteProperty")
   272  
   273  	if err := property.Validate(); err != nil {
   274  		return nil, fmt.Errorf("DeleteProperty validation failed. %w", err)
   275  	}
   276  
   277  	delURL := fmt.Sprintf("/config-gtm/v1/domains/%s/properties/%s", domainName, property.Name)
   278  	req, err := http.NewRequestWithContext(ctx, http.MethodDelete, delURL, nil)
   279  	if err != nil {
   280  		return nil, fmt.Errorf("failed to create Property request: %w", err)
   281  	}
   282  	setVersionHeader(req, schemaVersion)
   283  
   284  	var result ResponseBody
   285  	resp, err := g.Exec(req, &result)
   286  	if err != nil {
   287  		return nil, fmt.Errorf("DeleteProperty request failed: %w", err)
   288  	}
   289  
   290  	if resp.StatusCode != http.StatusOK {
   291  		return nil, g.Error(resp)
   292  	}
   293  
   294  	return result.Status, nil
   295  }