github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/cloudlets/loadbalancer_version.go (about) 1 package cloudlets 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 "strings" 10 "time" 11 12 "github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/edgegriderr" 13 14 validation "github.com/go-ozzo/ozzo-validation/v4" 15 ) 16 17 type ( 18 // LoadBalancerVersions is a cloudlets LoadBalancer version API interface. 19 LoadBalancerVersions interface { 20 // CreateLoadBalancerVersion creates load balancer version. 21 // 22 // See: https://techdocs.akamai.com/cloudlets/v2/reference/post-origin-versions 23 CreateLoadBalancerVersion(context.Context, CreateLoadBalancerVersionRequest) (*LoadBalancerVersion, error) 24 25 // GetLoadBalancerVersion gets specific load balancer version by originID and version. 26 // 27 // See: https://techdocs.akamai.com/cloudlets/v2/reference/get-origin-version 28 GetLoadBalancerVersion(context.Context, GetLoadBalancerVersionRequest) (*LoadBalancerVersion, error) 29 30 // UpdateLoadBalancerVersion updates specific load balancer version by originID and version. 31 // 32 // See: https://techdocs.akamai.com/cloudlets/v2/reference/put-origin-version 33 UpdateLoadBalancerVersion(context.Context, UpdateLoadBalancerVersionRequest) (*LoadBalancerVersion, error) 34 35 // ListLoadBalancerVersions lists all versions of Origin with type APPLICATION_LOAD_BALANCER. 36 // 37 // See: https://techdocs.akamai.com/cloudlets/v2/reference/get-origin-versions 38 ListLoadBalancerVersions(context.Context, ListLoadBalancerVersionsRequest) ([]LoadBalancerVersion, error) 39 } 40 41 // DataCenter represents the dataCenter field of load balancer version 42 DataCenter struct { 43 City string `json:"city,omitempty"` 44 CloudServerHostHeaderOverride bool `json:"cloudServerHostHeaderOverride,omitempty"` 45 CloudService bool `json:"cloudService"` 46 Continent string `json:"continent"` 47 Country string `json:"country"` 48 Hostname string `json:"hostname,omitempty"` 49 Latitude *float64 `json:"latitude"` 50 LivenessHosts []string `json:"livenessHosts,omitempty"` 51 Longitude *float64 `json:"longitude"` 52 OriginID string `json:"originId"` 53 Percent *float64 `json:"percent"` 54 StateOrProvince *string `json:"stateOrProvince,omitempty"` 55 } 56 57 // LivenessSettings represents the livenessSettings field of load balancer version 58 LivenessSettings struct { 59 HostHeader string `json:"hostHeader,omitempty"` 60 AdditionalHeaders map[string]string `json:"additionalHeaders,omitempty"` 61 Interval int `json:"interval,omitempty"` 62 Path string `json:"path,omitempty"` 63 PeerCertificateVerification bool `json:"peerCertificateVerification,omitempty"` 64 Port int `json:"port"` 65 Protocol string `json:"protocol"` 66 RequestString string `json:"requestString,omitempty"` 67 ResponseString string `json:"responseString,omitempty"` 68 Status3xxFailure bool `json:"status3xxFailure,omitempty"` 69 Status4xxFailure bool `json:"status4xxFailure,omitempty"` 70 Status5xxFailure bool `json:"status5xxFailure,omitempty"` 71 Timeout float64 `json:"timeout,omitempty"` 72 } 73 74 // BalancingType is a type for BalancingType field 75 BalancingType string 76 77 // LoadBalancerVersion describes the body of the create and update load balancer version request 78 LoadBalancerVersion struct { 79 BalancingType BalancingType `json:"balancingType,omitempty"` 80 CreatedBy string `json:"createdBy,omitempty"` 81 CreatedDate string `json:"createdDate,omitempty"` 82 DataCenters []DataCenter `json:"dataCenters,omitempty"` 83 Deleted bool `json:"deleted"` 84 Description string `json:"description,omitempty"` 85 Immutable bool `json:"immutable"` 86 LastModifiedBy string `json:"lastModifiedBy,omitempty"` 87 LastModifiedDate string `json:"lastModifiedDate,omitempty"` 88 LivenessSettings *LivenessSettings `json:"livenessSettings,omitempty"` 89 OriginID string `json:"originID,omitempty"` 90 Version int64 `json:"version,omitempty"` 91 Warnings []Warning `json:"warnings,omitempty"` 92 } 93 94 // CreateLoadBalancerVersionRequest describes the parameters needed to create load balancer version 95 CreateLoadBalancerVersionRequest struct { 96 OriginID string 97 LoadBalancerVersion LoadBalancerVersion 98 } 99 100 // GetLoadBalancerVersionRequest describes the parameters needed to get load balancer version 101 GetLoadBalancerVersionRequest struct { 102 OriginID string 103 Version int64 104 ShouldValidate bool 105 } 106 107 // UpdateLoadBalancerVersionRequest describes the parameters needed to update load balancer version 108 UpdateLoadBalancerVersionRequest struct { 109 OriginID string 110 ShouldValidate bool 111 Version int64 112 LoadBalancerVersion LoadBalancerVersion 113 } 114 115 // ListLoadBalancerVersionsRequest describes the parameters needed to list load balancer versions 116 ListLoadBalancerVersionsRequest struct { 117 OriginID string 118 } 119 ) 120 121 const ( 122 // BalancingTypeWeighted represents weighted balancing type for load balancer version 123 BalancingTypeWeighted BalancingType = "WEIGHTED" 124 // BalancingTypePerformance represents performance balancing type for load balancer version 125 BalancingTypePerformance BalancingType = "PERFORMANCE" 126 ) 127 128 var ( 129 // ErrCreateLoadBalancerVersion is returned when CreateLoadBalancerVersion fails 130 ErrCreateLoadBalancerVersion = errors.New("create origin version") 131 // ErrGetLoadBalancerVersion is returned when GetLoadBalancerVersion fails 132 ErrGetLoadBalancerVersion = errors.New("get origin version") 133 // ErrUpdateLoadBalancerVersion is returned when UpdateLoadBalancerVersion fails 134 ErrUpdateLoadBalancerVersion = errors.New("update origin version") 135 // ErrListLoadBalancerVersions is returned when ListLoadBalancerVersions fails 136 ErrListLoadBalancerVersions = errors.New("list origin versions") 137 ) 138 139 // Validate validates DataCenter 140 func (v DataCenter) Validate() error { 141 return validation.Errors{ 142 "Continent": validation.Validate(v.Continent, validation.Required, validation.In("AF", "AS", "EU", "NA", "OC", "OT", "SA").Error( 143 fmt.Sprintf("value '%s' is invalid. Must be one of: 'AF', 'AS', 'EU', 'NA', 'OC', 'OT' or 'SA'", (&v).Continent))), 144 "Country": validation.Validate(v.Country, validation.Required, validation.Length(2, 2)), 145 "Hostname": validation.Validate(v.Hostname, validation.Length(0, 256)), 146 "Latitude": validation.Validate(v.Latitude, validation.NotNil, validation.Min(-180.0), validation.Max(180.0)), 147 "Longitude": validation.Validate(v.Longitude, validation.NotNil, validation.Min(-180.0), validation.Max(180.0)), 148 "OriginID": validation.Validate(v.OriginID, validation.Required, validation.Length(1, 128)), 149 "Percent": validation.Validate(v.Percent, validation.NotNil, validation.Min(0.0), validation.Max(100.0)), 150 }.Filter() 151 } 152 153 // generateHostHeaderRules generates case insensitive validation rules for host header 154 // its required because schema requires that host header value contains at least 1 char 155 // but it doesnt put such requirement on other headers, so these two cases need to be considered separately 156 func generateHostHeaderRules(headers map[string]string) []*validation.KeyRules { 157 var hostRules []*validation.KeyRules 158 159 for k := range headers { 160 if strings.ToLower(k) == "host" { 161 hostRules = append(hostRules, validation.Key(k, validation.Length(1, 256))) 162 } 163 } 164 return hostRules 165 } 166 167 // Validate validates LivenessSettings 168 func (v LivenessSettings) Validate() error { 169 return validation.Errors{ 170 "HostHeader": validation.Validate(v.HostHeader, validation.Length(1, 256)), 171 "AdditionalHeaders": validation.Validate(v.AdditionalHeaders, validation.Map(generateHostHeaderRules(v.AdditionalHeaders)...).AllowExtraKeys()), 172 "Interval": validation.Validate(v.Interval, validation.Min(10), validation.Max(3600)), 173 "Path": validation.Validate(v.Path, 174 validation.When(v.Protocol == "HTTP" || v.Protocol == "HTTPS", validation.Required, validation.Length(1, 256)), 175 ), 176 "Port": validation.Validate(v.Port, validation.Required, validation.Min(1), validation.Max(65535)), 177 "Protocol": validation.Validate(v.Protocol, validation.Required, validation.In("HTTP", "HTTPS", "TCP", "TCPS").Error( 178 fmt.Sprintf("value '%s' is invalid. Must be one of: 'HTTP', 'HTTPS', 'TCP' or 'TCPS'", (&v).Protocol))), 179 "RequestString": validation.Validate(v.RequestString, 180 validation.When(v.Protocol == "TCP" || v.Protocol == "TCPS", validation.Required), 181 ), 182 "ResponseString": validation.Validate(v.ResponseString, 183 validation.When(v.Protocol == "TCP" || v.Protocol == "TCPS", validation.Required), 184 ), 185 "Timeout": validation.Validate(v.Timeout, validation.Min(0.001), validation.Max(60.0)), 186 }.Filter() 187 } 188 189 // Validate validates Warning 190 func (v Warning) Validate() error { 191 return validation.Errors{ 192 "Detail": validation.Validate(v.Detail, validation.Required), 193 "JSONPointer": validation.Validate(v.JSONPointer, validation.Length(0, 128)), 194 "Title": validation.Validate(v.Title, validation.Required), 195 "Type": validation.Validate(v.Type, validation.Required), 196 } 197 } 198 199 // Validate validates LoadBalancerVersion 200 func (v LoadBalancerVersion) Validate() error { 201 return validation.Errors{ 202 "BalancingType": validation.Validate(v.BalancingType, validation.In(BalancingTypeWeighted, BalancingTypePerformance).Error( 203 fmt.Sprintf("value '%s' is invalid. Must be one of: 'WEIGHTED', 'PERFORMANCE' or '' (empty)", (&v).BalancingType))), 204 "CreatedDate": validation.Validate(v.CreatedDate, validation.Date(time.RFC3339)), 205 "DataCenters": validation.Validate(v.DataCenters, validation.Length(1, 199)), 206 "LastModifiedDate": validation.Validate(v.LastModifiedDate, validation.Date(time.RFC3339)), 207 "LivenessSettings": validation.Validate(v.LivenessSettings), 208 "OriginID": validation.Validate(v.OriginID, validation.Length(2, 62)), 209 "Version": validation.Validate(v.Version, validation.Min(0)), 210 "Warnings": validation.Validate(v.Warnings), 211 }.Filter() 212 } 213 214 // Validate validates CreateLoadBalancerVersionRequest 215 func (v CreateLoadBalancerVersionRequest) Validate() error { 216 errs := validation.Errors{ 217 "OriginID": validation.Validate(v.OriginID, validation.Length(2, 62)), 218 "LoadBalancerVersion": validation.Validate(v.LoadBalancerVersion), 219 } 220 return edgegriderr.ParseValidationErrors(errs) 221 } 222 223 // Validate validates GetLoadBalancerVersionRequest 224 func (v GetLoadBalancerVersionRequest) Validate() error { 225 errs := validation.Errors{ 226 "OriginID": validation.Validate(v.OriginID, validation.Length(2, 62)), 227 "Version": validation.Validate(v.Version, validation.Min(0)), 228 } 229 return edgegriderr.ParseValidationErrors(errs) 230 } 231 232 // Validate validates UpdateLoadBalancerVersionRequest 233 func (v UpdateLoadBalancerVersionRequest) Validate() error { 234 errs := validation.Errors{ 235 "OriginID": validation.Validate(v.OriginID, validation.Length(2, 62)), 236 "Version": validation.Validate(v.Version, validation.Min(0)), 237 "LoadBalancerVersion": validation.Validate(v.LoadBalancerVersion), 238 } 239 return edgegriderr.ParseValidationErrors(errs) 240 } 241 242 // Validate validates ListLoadBalancerVersionsRequest 243 func (v ListLoadBalancerVersionsRequest) Validate() error { 244 errs := validation.Errors{ 245 "OriginID": validation.Validate(v.OriginID, validation.Required, validation.Length(2, 62)), 246 } 247 return edgegriderr.ParseValidationErrors(errs) 248 } 249 250 func (c *cloudlets) CreateLoadBalancerVersion(ctx context.Context, params CreateLoadBalancerVersionRequest) (*LoadBalancerVersion, error) { 251 logger := c.Log(ctx) 252 logger.Debug("CreateLoadBalancerVersion") 253 254 if err := params.Validate(); err != nil { 255 return nil, fmt.Errorf("%s: %w:\n%s", ErrCreateLoadBalancerVersion, ErrStructValidation, err) 256 } 257 258 uri, err := url.Parse(fmt.Sprintf("/cloudlets/api/v2/origins/%s/versions", params.OriginID)) 259 if err != nil { 260 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrCreateLoadBalancerVersion, err) 261 } 262 263 req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri.String(), nil) 264 if err != nil { 265 return nil, fmt.Errorf("%w: failed to create request: %s", ErrCreateLoadBalancerVersion, err) 266 } 267 268 var result LoadBalancerVersion 269 resp, err := c.Exec(req, &result, params.LoadBalancerVersion) 270 if err != nil { 271 return nil, fmt.Errorf("%w: request failed: %s", ErrCreateLoadBalancerVersion, err) 272 } 273 274 if resp.StatusCode != http.StatusCreated { 275 return nil, fmt.Errorf("%s: %w", ErrCreateLoadBalancerVersion, c.Error(resp)) 276 } 277 278 return &result, nil 279 } 280 281 func (c *cloudlets) GetLoadBalancerVersion(ctx context.Context, params GetLoadBalancerVersionRequest) (*LoadBalancerVersion, error) { 282 logger := c.Log(ctx) 283 logger.Debug("GetLoadBalancerVersion") 284 285 if err := params.Validate(); err != nil { 286 return nil, fmt.Errorf("%s: %w:\n%s", ErrGetLoadBalancerVersion, ErrStructValidation, err) 287 } 288 289 uri, err := url.Parse(fmt.Sprintf("/cloudlets/api/v2/origins/%s/versions/%d", params.OriginID, params.Version)) 290 if err != nil { 291 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrGetLoadBalancerVersion, err) 292 } 293 294 if params.ShouldValidate { 295 q := uri.Query() 296 q.Add("validate", "true") 297 uri.RawQuery = q.Encode() 298 } 299 300 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil) 301 if err != nil { 302 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetLoadBalancerVersion, err) 303 } 304 305 var result LoadBalancerVersion 306 resp, err := c.Exec(req, &result) 307 if err != nil { 308 return nil, fmt.Errorf("%w: request failed: %s", ErrGetLoadBalancerVersion, err) 309 } 310 311 if resp.StatusCode != http.StatusOK { 312 return nil, fmt.Errorf("%s: %w", ErrGetLoadBalancerVersion, c.Error(resp)) 313 } 314 315 return &result, nil 316 } 317 318 func (c *cloudlets) UpdateLoadBalancerVersion(ctx context.Context, params UpdateLoadBalancerVersionRequest) (*LoadBalancerVersion, error) { 319 logger := c.Log(ctx) 320 logger.Debug("UpdateLoadBalancerVersion") 321 322 if err := params.Validate(); err != nil { 323 return nil, fmt.Errorf("%s: %w:\n%s", ErrUpdateLoadBalancerVersion, ErrStructValidation, err) 324 } 325 326 uri, err := url.Parse(fmt.Sprintf("/cloudlets/api/v2/origins/%s/versions/%d", params.OriginID, params.Version)) 327 if err != nil { 328 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrUpdateLoadBalancerVersion, err) 329 } 330 331 if params.ShouldValidate { 332 q := uri.Query() 333 q.Add("validate", "true") 334 uri.RawQuery = q.Encode() 335 } 336 337 req, err := http.NewRequestWithContext(ctx, http.MethodPut, uri.String(), nil) 338 if err != nil { 339 return nil, fmt.Errorf("%w: failed to create request: %s", ErrUpdateLoadBalancerVersion, err) 340 } 341 342 var result LoadBalancerVersion 343 resp, err := c.Exec(req, &result, params.LoadBalancerVersion) 344 if err != nil { 345 return nil, fmt.Errorf("%w: request failed: %s", ErrUpdateLoadBalancerVersion, err) 346 } 347 348 if resp.StatusCode != http.StatusOK { 349 return nil, fmt.Errorf("%s: %w", ErrUpdateLoadBalancerVersion, c.Error(resp)) 350 } 351 352 return &result, nil 353 } 354 355 func (c *cloudlets) ListLoadBalancerVersions(ctx context.Context, params ListLoadBalancerVersionsRequest) ([]LoadBalancerVersion, error) { 356 logger := c.Log(ctx) 357 logger.Debug("ListLoadBalancerVersions") 358 359 if err := params.Validate(); err != nil { 360 return nil, fmt.Errorf("%s: %w:\n%s", ErrListLoadBalancerVersions, ErrStructValidation, err) 361 } 362 363 uri, err := url.Parse(fmt.Sprintf("/cloudlets/api/v2/origins/%s/versions?includeModel=true", params.OriginID)) 364 if err != nil { 365 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrListLoadBalancerVersions, err) 366 } 367 368 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil) 369 if err != nil { 370 return nil, fmt.Errorf("%w: failed to create request: %s", ErrListLoadBalancerVersions, err) 371 } 372 373 var result []LoadBalancerVersion 374 resp, err := c.Exec(req, &result) 375 if err != nil { 376 return nil, fmt.Errorf("%w: request failed: %s", ErrListLoadBalancerVersions, err) 377 } 378 379 if resp.StatusCode != http.StatusOK { 380 return nil, fmt.Errorf("%s: %w", ErrListLoadBalancerVersions, c.Error(resp)) 381 } 382 383 return result, nil 384 }