Skip to content

Commit 68c5e39

Browse files
authored
Merge pull request #23 from philips-software/feature/improve-autoscalers
Improve autoscalers
2 parents 512984c + 5f73613 commit 68c5e39

File tree

7 files changed

+115
-47
lines changed

7 files changed

+115
-47
lines changed

docs/resources/metrics_autoscaler.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ The app upscales at 90% CPU utilization and downscales again when CPU usage fall
1111

1212
```hcl
1313
resource "hsdp_metrics_autoscaler" "myapp_autoscaler" {
14-
instance_id = cloudfoundry_service_instance.metrics.id
14+
metrics_instance_id = cloudfoundry_service_instance.metrics.id
1515
app_name = cloudfoundry_app.myapp.name
1616
1717
enabled = true
18-
min = 1
19-
max = 10
18+
min_instances = 1
19+
max_instances = 10
2020
2121
threshold_cpu {
2222
enabled = true
@@ -32,7 +32,7 @@ resource "hsdp_metrics_autoscaler" "myapp_autoscaler" {
3232
enabled = false
3333
}
3434
35-
threshold_http_traffic {
35+
threshold_http_rate {
3636
enabled = false
3737
}
3838
}
@@ -42,7 +42,7 @@ resource "hsdp_metrics_autoscaler" "myapp_autoscaler" {
4242

4343
The following arguments are supported:
4444

45-
* `instance_id` - (Required) The Metrics service instance UUID running in the space where the app is hosted.
45+
* `metrics_instance_id` - (Required) The Metrics service instance UUID running in the space where the app is hosted.
4646
* `app_name` - (Required) The CF app name to apply this autoscaler settings for.
4747
* `min` - (Optional) Minimum number of app instances. Default: 1
4848
* `max` - (Optional) Maximum number of app instances. Default: 10

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ module github.com/philips-software/terraform-provider-hsdp
22

33
require (
44
github.com/aws/aws-sdk-go v1.30.12 // indirect
5+
github.com/cenkalti/backoff/v4 v4.1.0 // indirect
56
github.com/google/go-querystring v1.0.0 // indirect
67
github.com/hashicorp/go-retryablehttp v0.6.6
78
github.com/hashicorp/terraform-plugin-sdk/v2 v2.2.0
8-
github.com/philips-software/go-hsdp-api v0.24.1-0.20201116153039-dc27531a282b
9+
github.com/pelletier/go-toml v1.8.0 // indirect
10+
github.com/philips-software/go-hsdp-api v0.26.1-0.20201215053631-69d660526250
911
github.com/pkg/errors v0.9.1
1012
github.com/stretchr/testify v1.5.1
1113
)

go.sum

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1U
6666
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
6767
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
6868
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
69+
github.com/cenkalti/backoff v1.1.0 h1:QnvVp8ikKCDWOsFheytRCoYWYPO/ObCTBGxT19Hc+yE=
70+
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
71+
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
72+
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
6973
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
7074
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
7175
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -113,6 +117,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87
113117
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
114118
github.com/go-playground/validator/v10 v10.0.1 h1:QgDDZpXlR/L3atIL2PbFt0TpazbtN7N6PxTGcgcyEUg=
115119
github.com/go-playground/validator/v10 v10.0.1/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
120+
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
121+
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
116122
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
117123
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
118124
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
@@ -294,6 +300,8 @@ github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMF
294300
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
295301
github.com/philips-software/go-hsdp-api v0.24.1-0.20201116153039-dc27531a282b h1:VzUZHPFe//m09TTyELVJV73jpGhVax6hOa1Ixf46+aA=
296302
github.com/philips-software/go-hsdp-api v0.24.1-0.20201116153039-dc27531a282b/go.mod h1:HzmFohwDfs+IiUbFjUVPm0EHqCOO+i9AO4qGjwRPRpo=
303+
github.com/philips-software/go-hsdp-api v0.26.1-0.20201215053631-69d660526250 h1:EVE4vtazB9ua2RrMikIgyHl4Wm7ATVGyLoKmaQchUCs=
304+
github.com/philips-software/go-hsdp-api v0.26.1-0.20201215053631-69d660526250/go.mod h1:HaEaF0bYLfcP2P5NKoVCevPZDnNQVr0Y9t/DzWvEGYM=
297305
github.com/philips-software/go-hsdp-signer v1.3.0 h1:Si1voDE/GHzthmxpasPdntbu8aUW6EYJfI6gHVf7BCc=
298306
github.com/philips-software/go-hsdp-signer v1.3.0/go.mod h1:/QehZ/+Aks2t1TFpjhF/7ZSB8PJIIJHzLc03rOqwLw0=
299307
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -355,6 +363,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
355363
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
356364
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
357365
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
366+
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o=
367+
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
358368
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
359369
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
360370
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -445,6 +455,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
445455
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
446456
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
447457
golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
458+
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
448459
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
449460
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
450461
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -461,6 +472,8 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
461472
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
462473
golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o=
463474
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
475+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
476+
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
464477
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
465478
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
466479
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

hsdp/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (c *Config) setupS3CredsClient() {
111111
if c.Environment != "" && c.Region != "" {
112112
ac, err := config.New(config.WithRegion(c.Region), config.WithEnv(c.Environment))
113113
if err == nil {
114-
if url, err := ac.Service("s3creds").GetString("url"); err == nil && c.S3CredsURL == "" {
114+
if url := ac.Service("s3creds").URL; c.S3CredsURL == "" {
115115
c.S3CredsURL = url
116116
}
117117
}

hsdp/data_source_config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,13 @@ func dataSourceConfigRead(ctx context.Context, d *schema.ResourceData, meta inte
6060
return diag.FromErr(err)
6161
}
6262
d.SetId("data" + region + environment + service)
63-
if url, err := c.Service(service).GetString("url"); err == nil {
63+
if url := c.Service(service).URL; url != "" {
6464
_ = d.Set("url", url)
6565
}
66-
if host, err := c.Service(service).GetString("host"); err == nil {
66+
if host := c.Service(service).Host; host != "" {
6767
_ = d.Set("host", host)
6868
}
69-
if domain, err := c.Service(service).GetString("domain"); err == nil {
69+
if domain := c.Service(service).Domain; domain != "" {
7070
_ = d.Set("domain", domain)
7171
}
7272
return diags

hsdp/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ var (
1313
ErrMissingClientPassword = errors.New("missing client password")
1414
ErrInvalidResponse = errors.New("invalid response received")
1515
ErrResourceNotFound = errors.New("resource not found")
16+
ErrIntermittent = errors.New("intermittent error detected")
1617
)

hsdp/resource_metrics_autoscaler.go

Lines changed: 89 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ package hsdp
33
import (
44
"context"
55
"fmt"
6+
"github.com/cenkalti/backoff/v4"
67
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
78
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
810
"github.com/philips-software/go-hsdp-api/console"
11+
"net/http"
12+
"strings"
913
"time"
1014
)
1115

@@ -34,21 +38,23 @@ func resourceMetricsAutoscaler() *schema.Resource {
3438
ForceNew: true,
3539
},
3640
"max_instances": {
37-
Type: schema.TypeInt,
38-
Optional: true,
39-
Default: 10,
41+
Type: schema.TypeInt,
42+
Optional: true,
43+
Default: 10,
44+
ValidateFunc: validation.IntBetween(0, 1000000000),
4045
},
4146
"min_instances": {
42-
Type: schema.TypeInt,
43-
Optional: true,
44-
Default: 1,
47+
Type: schema.TypeInt,
48+
Optional: true,
49+
Default: 1,
50+
ValidateFunc: validation.IntBetween(0, 1000000000),
4551
},
4652
"enabled": {
4753
Type: schema.TypeBool,
4854
Optional: true,
4955
Default: false,
5056
},
51-
"threshold_http_latency": &schema.Schema{
57+
"threshold_http_latency": {
5258
Type: schema.TypeSet,
5359
Required: true,
5460
MaxItems: 1,
@@ -59,19 +65,21 @@ func resourceMetricsAutoscaler() *schema.Resource {
5965
Required: true,
6066
},
6167
"max": {
62-
Type: schema.TypeFloat,
63-
Optional: true,
64-
Default: 10000,
68+
Type: schema.TypeFloat,
69+
Optional: true,
70+
Default: 10000,
71+
ValidateFunc: validation.FloatBetween(1, 1000000),
6572
},
6673
"min": {
67-
Type: schema.TypeFloat,
68-
Optional: true,
69-
Default: 10,
74+
Type: schema.TypeFloat,
75+
Optional: true,
76+
Default: 10,
77+
ValidateFunc: validation.FloatBetween(1, 1000000),
7078
},
7179
},
7280
},
7381
},
74-
"threshold_http_rate": &schema.Schema{
82+
"threshold_http_rate": {
7583
Type: schema.TypeSet,
7684
Required: true,
7785
MaxItems: 1,
@@ -82,19 +90,21 @@ func resourceMetricsAutoscaler() *schema.Resource {
8290
Required: true,
8391
},
8492
"max": {
85-
Type: schema.TypeFloat,
86-
Optional: true,
87-
Default: 6000000,
93+
Type: schema.TypeFloat,
94+
Optional: true,
95+
Default: 6000000,
96+
ValidateFunc: validation.FloatBetween(1, 6000000),
8897
},
8998
"min": {
90-
Type: schema.TypeFloat,
91-
Optional: true,
92-
Default: 300,
99+
Type: schema.TypeFloat,
100+
Optional: true,
101+
Default: 300,
102+
ValidateFunc: validation.FloatBetween(1, 6000000),
93103
},
94104
},
95105
},
96106
},
97-
"threshold_memory": &schema.Schema{
107+
"threshold_memory": {
98108
Type: schema.TypeSet,
99109
Required: true,
100110
MaxItems: 1,
@@ -105,19 +115,21 @@ func resourceMetricsAutoscaler() *schema.Resource {
105115
Required: true,
106116
},
107117
"max": {
108-
Type: schema.TypeFloat,
109-
Optional: true,
110-
Default: 100,
118+
Type: schema.TypeFloat,
119+
Optional: true,
120+
Default: 100,
121+
ValidateFunc: validation.FloatBetween(0, 100),
111122
},
112123
"min": {
113-
Type: schema.TypeFloat,
114-
Optional: true,
115-
Default: 20,
124+
Type: schema.TypeFloat,
125+
Optional: true,
126+
Default: 20,
127+
ValidateFunc: validation.FloatBetween(0, 100),
116128
},
117129
},
118130
},
119131
},
120-
"threshold_cpu": &schema.Schema{
132+
"threshold_cpu": {
121133
Type: schema.TypeSet,
122134
Required: true,
123135
MaxItems: 1,
@@ -129,14 +141,16 @@ func resourceMetricsAutoscaler() *schema.Resource {
129141
Default: false,
130142
},
131143
"max": {
132-
Type: schema.TypeFloat,
133-
Optional: true,
134-
Default: 100,
144+
Type: schema.TypeFloat,
145+
Optional: true,
146+
Default: 100,
147+
ValidateFunc: validation.FloatBetween(0, 100),
135148
},
136149
"min": {
137-
Type: schema.TypeFloat,
138-
Optional: true,
139-
Default: 5,
150+
Type: schema.TypeFloat,
151+
Optional: true,
152+
Default: 5,
153+
ValidateFunc: validation.FloatBetween(0, 100),
140154
},
141155
},
142156
},
@@ -158,7 +172,7 @@ func resourceMetricsAutoscalerDelete(ctx context.Context, d *schema.ResourceData
158172
instanceID := d.Get("metrics_instance_id").(string)
159173
app.Name = d.Get("app_name").(string)
160174
app.Enabled = false
161-
result, _, err := client.Metrics.UpdateApplicationAutoscaler(instanceID, app)
175+
result, err := updateWithRetry(client, instanceID, app)
162176
if err != nil {
163177
return diag.FromErr(err)
164178
}
@@ -185,7 +199,7 @@ func resourceMetricsAutoscalerRead(ctx context.Context, d *schema.ResourceData,
185199
instanceID := d.Get("metrics_instance_id").(string)
186200
name := d.Get("app_name").(string)
187201

188-
app, _, err := client.Metrics.GetApplicationAutoscaler(instanceID, name)
202+
app, err := getWitRetry(client, instanceID, name)
189203
if err != nil {
190204
return diag.FromErr(err)
191205
}
@@ -244,7 +258,7 @@ func resourceMetricsAutoscalerCreate(ctx context.Context, d *schema.ResourceData
244258
}
245259
}
246260
}
247-
created, _, err := client.Metrics.UpdateApplicationAutoscaler(instanceID, app)
261+
created, err := updateWithRetry(client, instanceID, app)
248262
if err != nil {
249263
return diag.FromErr(err)
250264
}
@@ -254,3 +268,41 @@ func resourceMetricsAutoscalerCreate(ctx context.Context, d *schema.ResourceData
254268
d.SetId(instanceID + created.Name)
255269
return diags
256270
}
271+
272+
func updateWithRetry(client *console.Client, instanceID string, app console.Application) (*console.Application, error) {
273+
var created *console.Application
274+
operation := func() error {
275+
var err error
276+
var resp *console.Response
277+
created, resp, err = client.Metrics.UpdateApplicationAutoscaler(instanceID, app)
278+
return checkForIntermittentErrors(resp, err)
279+
}
280+
err := backoff.Retry(operation, backoff.NewExponentialBackOff())
281+
return created, err
282+
}
283+
284+
func getWitRetry(client *console.Client, instanceID string, name string) (*console.Application, error) {
285+
var app *console.Application
286+
operation := func() error {
287+
var err error
288+
var resp *console.Response
289+
app, resp, err = client.Metrics.GetApplicationAutoscaler(instanceID, name)
290+
return checkForIntermittentErrors(resp, err)
291+
}
292+
err := backoff.Retry(operation, backoff.NewExponentialBackOff())
293+
return app, err
294+
}
295+
296+
func checkForIntermittentErrors(resp *console.Response, err error) error {
297+
if resp == nil || resp.StatusCode > 500 {
298+
return err
299+
}
300+
if resp.StatusCode == http.StatusInternalServerError {
301+
return backoff.Permanent(fmt.Errorf("console: %s %w", resp.Error.Message, err))
302+
}
303+
if resp.StatusCode == http.StatusBadRequest &&
304+
strings.Contains(resp.Error.Message, "invalid character") {
305+
return ErrIntermittent
306+
}
307+
return backoff.Permanent(err)
308+
}

0 commit comments

Comments
 (0)