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 }