Skip to content

Commit 7038930

Browse files
author
Antoine Belluard
committed
feat(container/serverless): add scaling_option block
1 parent 7c34968 commit 7038930

File tree

8 files changed

+4726
-1
lines changed

8 files changed

+4726
-1
lines changed

docs/data-sources/container.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ In addition to all arguments above, the following attributes are exported:
9191

9292
- `deploy` - Boolean indicating whether the container is on a production environment.
9393

94-
- `sandbox` - (Optional) Execution environment of the container.
94+
- `sandbox` - Execution environment of the container.
95+
96+
- `scaling_option` - Configuration block used to decide when to scale up or down. Possible values:
97+
- `concurrent_requests_threshold` - Scale depending on the number of concurrent requests being processed per container instance.
98+
- `cpu_usage_threshold` - Scale depending on the CPU usage of a container instance.
99+
- `memory_usage_threshold`- Scale depending on the memory usage of a container instance.
95100

96101
- `status` - The container status.
97102

docs/resources/container.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,24 @@ The following arguments are supported:
8484

8585
- `sandbox` - (Optional) Execution environment of the container.
8686

87+
- `scaling_option` - (Optional) Configuration block used to decide when to scale up or down. Possible values:
88+
- `concurrent_requests_threshold` - Scale depending on the number of concurrent requests being processed per container instance.
89+
- `cpu_usage_threshold` - Scale depending on the CPU usage of a container instance.
90+
- `memory_usage_threshold`- Scale depending on the memory usage of a container instance.
91+
92+
Exemple:
93+
```terraform
94+
resource scaleway_container main {
95+
name = "my-container-02"
96+
namespace_id = scaleway_container_namespace.main.id
97+
98+
scaling_option {
99+
concurrent_requests_threshold = 15
100+
}
101+
}
102+
```
103+
Note that **a maximum of one of these parameters may be set**.
104+
87105
- `port` - (Optional) The port to expose the container.
88106
89107
- `deploy` - (Optional) Boolean indicating whether the container is in a production environment.

internal/services/container/container.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ func ResourceContainer() *schema.Resource {
129129
Type: schema.TypeInt,
130130
Optional: true,
131131
Computed: true,
132+
Deprecated: "Use scaling_option.concurrent_requests_threshold instead. This attribute will be removed.",
132133
Description: "The maximum the number of simultaneous requests your container can handle at the same time.",
133134
ValidateFunc: validation.IntAtMost(containerMaxConcurrencyLimit),
134135
},
@@ -170,6 +171,31 @@ func ResourceContainer() *schema.Resource {
170171
Description: "Execution environment of the container.",
171172
ValidateDiagFunc: verify.ValidateEnum[container.ContainerSandbox](),
172173
},
174+
"scaling_option": {
175+
Type: schema.TypeSet,
176+
Optional: true,
177+
Computed: true,
178+
Description: "Configuration used to decide when to scale up or down.",
179+
Elem: &schema.Resource{
180+
Schema: map[string]*schema.Schema{
181+
"concurrent_requests_threshold": {
182+
Type: schema.TypeInt,
183+
Description: "Scale depending on the number of concurrent requests being processed per container instance.",
184+
Optional: true,
185+
},
186+
"cpu_usage_threshold": {
187+
Type: schema.TypeInt,
188+
Description: "Scale depending on the CPU usage of a container instance.",
189+
Optional: true,
190+
},
191+
"memory_usage_threshold": {
192+
Type: schema.TypeInt,
193+
Description: "Scale depending on the memory usage of a container instance.",
194+
Optional: true,
195+
},
196+
},
197+
},
198+
},
173199
// computed
174200
"status": {
175201
Type: schema.TypeString,
@@ -280,6 +306,7 @@ func ResourceContainerRead(ctx context.Context, d *schema.ResourceData, m interf
280306
_ = d.Set("deploy", scw.BoolPtr(*types.ExpandBoolPtr(d.Get("deploy"))))
281307
_ = d.Set("http_option", co.HTTPOption)
282308
_ = d.Set("sandbox", co.Sandbox)
309+
_ = d.Set("scaling_option", flattenScalingOption(co.ScalingOption))
283310
_ = d.Set("region", co.Region.String())
284311

285312
return nil
@@ -375,6 +402,18 @@ func ResourceContainerUpdate(ctx context.Context, d *schema.ResourceData, m inte
375402
req.Sandbox = container.ContainerSandbox(d.Get("sandbox").(string))
376403
}
377404

405+
if d.HasChanges("scaling_option") {
406+
if scalingOption, ok := d.GetOk("scaling_option"); ok {
407+
if scalingOptionReq, err := expandScalingOptions(scalingOption); err != nil {
408+
return diag.FromErr(err)
409+
} else {
410+
req.ScalingOption = scalingOptionReq
411+
}
412+
} else {
413+
return diag.Errorf("unable to fetch scaling_option changes")
414+
}
415+
}
416+
378417
imageHasChanged := d.HasChanges("registry_sha256")
379418
if imageHasChanged {
380419
req.Redeploy = &imageHasChanged

internal/services/container/container_data_source_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,38 @@ func TestAccDataSourceContainer_Basic(t *testing.T) {
4949
},
5050
})
5151
}
52+
53+
func TestAccDataSourceContainer_ScalingOption(t *testing.T) {
54+
tt := acctest.NewTestTools(t)
55+
defer tt.Cleanup()
56+
resource.ParallelTest(t, resource.TestCase{
57+
PreCheck: func() { acctest.PreCheck(t) },
58+
ProviderFactories: tt.ProviderFactories,
59+
CheckDestroy: isNamespaceDestroyed(tt),
60+
Steps: []resource.TestStep{
61+
{
62+
Config: `
63+
resource scaleway_container_namespace main {}
64+
65+
resource scaleway_container main {
66+
namespace_id = scaleway_container_namespace.main.id
67+
deploy = false
68+
}
69+
70+
data scaleway_container main {
71+
namespace_id = scaleway_container_namespace.main.id
72+
container_id = scaleway_container.main.id
73+
}
74+
`,
75+
Check: resource.ComposeTestCheckFunc(
76+
isContainerPresent(tt, "scaleway_container.main"),
77+
// Check default option returned by the API when you don't specify the scaling_option block.
78+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.#", "1"),
79+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.0.concurrent_requests_threshold", "50"),
80+
resource.TestCheckResourceAttr("data.scaleway_container.main", "scaling_option.#", "1"),
81+
resource.TestCheckResourceAttr("data.scaleway_container.main", "scaling_option.0.concurrent_requests_threshold", "50"),
82+
),
83+
},
84+
},
85+
})
86+
}

internal/services/container/container_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,95 @@ func TestAccContainer_Sandbox(t *testing.T) {
400400
})
401401
}
402402

403+
func TestAccContainer_ScalingOption(t *testing.T) {
404+
tt := acctest.NewTestTools(t)
405+
defer tt.Cleanup()
406+
resource.ParallelTest(t, resource.TestCase{
407+
PreCheck: func() { acctest.PreCheck(t) },
408+
ProviderFactories: tt.ProviderFactories,
409+
CheckDestroy: isContainerDestroyed(tt),
410+
Steps: []resource.TestStep{
411+
{
412+
Config: `
413+
resource scaleway_container_namespace main {}
414+
415+
resource scaleway_container main {
416+
namespace_id = scaleway_container_namespace.main.id
417+
deploy = false
418+
}
419+
`,
420+
Check: resource.ComposeTestCheckFunc(
421+
isContainerPresent(tt, "scaleway_container.main"),
422+
// Check default option returned by the API when you don't specify the scaling_option block.
423+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.#", "1"),
424+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.0.concurrent_requests_threshold", "50"),
425+
),
426+
},
427+
{
428+
Config: `
429+
resource scaleway_container_namespace main {}
430+
431+
resource scaleway_container main {
432+
namespace_id = scaleway_container_namespace.main.id
433+
deploy = false
434+
435+
scaling_option {
436+
concurrent_requests_threshold = 15
437+
}
438+
}
439+
`,
440+
Check: resource.ComposeTestCheckFunc(
441+
isContainerPresent(tt, "scaleway_container.main"),
442+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.#", "1"),
443+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.0.concurrent_requests_threshold", "15"),
444+
),
445+
},
446+
{
447+
Config: `
448+
resource scaleway_container_namespace main {}
449+
450+
resource scaleway_container main {
451+
namespace_id = scaleway_container_namespace.main.id
452+
deploy = false
453+
454+
min_scale = 1
455+
456+
scaling_option {
457+
cpu_usage_threshold = 72
458+
}
459+
}
460+
`,
461+
Check: resource.ComposeTestCheckFunc(
462+
isContainerPresent(tt, "scaleway_container.main"),
463+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.#", "1"),
464+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.0.cpu_usage_threshold", "72"),
465+
),
466+
},
467+
468+
{
469+
Config: `
470+
resource scaleway_container_namespace main {}
471+
472+
resource scaleway_container main {
473+
namespace_id = scaleway_container_namespace.main.id
474+
deploy = false
475+
476+
min_scale = 1
477+
478+
scaling_option {
479+
memory_usage_threshold = 66
480+
}
481+
}
482+
`,
483+
Check: resource.ComposeTestCheckFunc(
484+
isContainerPresent(tt, "scaleway_container.main"),
485+
resource.TestCheckResourceAttr("scaleway_container.main", "scaling_option.0.memory_usage_threshold", "66"),
486+
),
487+
},
488+
},
489+
})
490+
}
491+
403492
func isContainerPresent(tt *acctest.TestTools, n string) resource.TestCheckFunc {
404493
return func(state *terraform.State) error {
405494
rs, ok := state.RootModule().Resources[n]

internal/services/container/helpers_container.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package container
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"strings"
78
"time"
89

@@ -115,9 +116,68 @@ func setCreateContainerRequest(d *schema.ResourceData, region scw.Region) (*cont
115116
req.Sandbox = container.ContainerSandbox(sandbox.(string))
116117
}
117118

119+
if scalingOption, ok := d.GetOk("scaling_option"); ok {
120+
if scalingOptionReq, err := expandScalingOptions(scalingOption); err != nil {
121+
return nil, err
122+
} else {
123+
req.ScalingOption = scalingOptionReq
124+
}
125+
}
126+
118127
return req, nil
119128
}
120129

130+
func expandScalingOptions(scalingOptionSchema interface{}) (*container.ContainerScalingOption, error) {
131+
scalingOption, ok := scalingOptionSchema.(*schema.Set)
132+
if !ok {
133+
return &container.ContainerScalingOption{}, nil
134+
}
135+
136+
for _, option := range scalingOption.List() {
137+
rawOption, isRawOption := option.(map[string]interface{})
138+
if !isRawOption {
139+
continue
140+
}
141+
142+
setFields := 0
143+
cso := &container.ContainerScalingOption{}
144+
if concurrentRequestThresold, ok := rawOption["concurrent_requests_threshold"].(int); ok && concurrentRequestThresold != 0 {
145+
cso.ConcurrentRequestsThreshold = scw.Uint32Ptr(uint32(concurrentRequestThresold))
146+
setFields++
147+
}
148+
if cpuUsageThreshold, ok := rawOption["cpu_usage_threshold"].(int); ok && cpuUsageThreshold != 0 {
149+
cso.CPUUsageThreshold = scw.Uint32Ptr(uint32(cpuUsageThreshold))
150+
setFields++
151+
}
152+
if memoryUsageThreshold, ok := rawOption["memory_usage_threshold"].(int); ok && memoryUsageThreshold != 0 {
153+
cso.MemoryUsageThreshold = scw.Uint32Ptr(uint32(memoryUsageThreshold))
154+
setFields++
155+
}
156+
157+
if setFields > 1 {
158+
return &container.ContainerScalingOption{}, fmt.Errorf("a maximum of one scaling option can be set")
159+
}
160+
return cso, nil
161+
}
162+
163+
return &container.ContainerScalingOption{}, nil
164+
}
165+
166+
func flattenScalingOption(scalingOption *container.ContainerScalingOption) interface{} {
167+
if scalingOption == nil {
168+
return nil
169+
}
170+
171+
flattenedScalingOption := []map[string]interface{}(nil)
172+
flattenedScalingOption = append(flattenedScalingOption, map[string]interface{}{
173+
"concurrent_requests_threshold": types.FlattenUint32Ptr(scalingOption.ConcurrentRequestsThreshold),
174+
"cpu_usage_threshold": types.FlattenUint32Ptr(scalingOption.CPUUsageThreshold),
175+
"memory_usage_threshold": types.FlattenUint32Ptr(scalingOption.MemoryUsageThreshold),
176+
})
177+
178+
return flattenedScalingOption
179+
}
180+
121181
func expandContainerSecrets(secretsRawMap interface{}) []*container.Secret {
122182
secretsMap := secretsRawMap.(map[string]interface{})
123183
secrets := make([]*container.Secret, 0, len(secretsMap))

0 commit comments

Comments
 (0)