Skip to content

Commit 21dc4f5

Browse files
authored
feat(container-cron): add new resource container cron (#1329)
1 parent 3c553ab commit 21dc4f5

14 files changed

+2587
-267
lines changed

docs/resources/container.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ In addition to all above arguments, the following attributes are exported:
9797

9898
## Import
9999

100-
Container can be imported using the container_name, e.g.,
100+
Container can be imported using the `{region}/{id}`, e.g.
101101

102102
```bash
103103
$ terraform import scaleway_container.main fr-par/11111111-1111-1111-1111-111111111111

docs/resources/container_cron.md

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
page_title: "Scaleway: scaleway_container_cron"
3+
description: |-
4+
Manages Scaleway Containers Triggers.
5+
---
6+
7+
# scaleway_container_cron
8+
9+
Creates and manages Scaleway Container Triggers. For the moment, the feature is limited to CRON Schedule (time-based).
10+
11+
For more information consult
12+
the [documentation](https://www.scaleway.com/en/docs/compute/containers/api-cli/cont-uploading-with-serverless-framework/#configuring-events)
13+
.
14+
15+
For more details about the limitation
16+
check [containers-limitations](https://www.scaleway.com/en/docs/compute/containers/reference-content/containers-limitations/)
17+
.
18+
19+
You can check also
20+
our [containers cron api documentation](https://developers.scaleway.com/en/products/containers/api/#crons-942bf4).
21+
22+
## Example Usage
23+
24+
```hcl
25+
resource scaleway_container_namespace main {
26+
}
27+
28+
resource scaleway_container main {
29+
name = "my-container-with-cron-tf"
30+
namespace_id = scaleway_container_namespace.main.id
31+
}
32+
33+
resource scaleway_container_cron main {
34+
container_id = scaleway_container.main.id
35+
schedule = "5 4 1 * *" #cron at 04:05 on day-of-month 1
36+
args = jsonencode(
37+
{
38+
address = {
39+
city = "Paris"
40+
country = "FR"
41+
}
42+
age = 23
43+
firstName = "John"
44+
isAlive = true
45+
lastName = "Smith"
46+
# minScale: 1
47+
# memoryLimit: 256
48+
# maxScale: 2
49+
# timeout: 20000
50+
# Local environment variables - used only in given function
51+
}
52+
)
53+
}
54+
```
55+
56+
## Arguments Reference
57+
58+
The following arguments are required:
59+
60+
- `schedule` - (Required) Cron format string, e.g. @hourly, as schedule time of its jobs to be created and
61+
executed.
62+
- `container_id` - (Required) The container ID to link with your cron.
63+
- `args` - (Required) The key-value mapping to define arguments that will be passed to your container’s event object
64+
during
65+
66+
## Attributes Reference
67+
68+
In addition to all above arguments, the following attributes are exported:
69+
70+
- `region` - (Defaults to [provider](../index.md#region) `region`) The [region](../guides/regions_and_zones.md#regions)
71+
in where the job was created.
72+
- `status` - The cron status.
73+
74+
## Import
75+
76+
Container Cron can be imported using the `{region}/{id}`, e.g.
77+
78+
```bash
79+
$ terraform import scaleway_container_cron.main fr-par/11111111-1111-1111-1111-111111111111
80+
```

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ require (
1111
github.com/hashicorp/go-retryablehttp v0.7.1
1212
github.com/hashicorp/terraform-plugin-log v0.4.0
1313
github.com/hashicorp/terraform-plugin-sdk/v2 v2.17.0
14-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220624101230-756b7ec05a7f
14+
github.com/robfig/cron/v3 v3.0.1
15+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220624140600-38685b7aadbb
1516
github.com/stretchr/testify v1.7.1
1617
)
1718

go.sum

+4-4
Original file line numberDiff line numberDiff line change
@@ -249,11 +249,11 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
249249
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
250250
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
251251
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
252+
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
253+
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
252254
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
253-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220616135310-b11a2a9a6c76 h1:FjeyXcr7eAD4GkstU4ZrpM+GJq8t0zo5h1BwknvJKB8=
254-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220616135310-b11a2a9a6c76/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
255-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220624101230-756b7ec05a7f h1:wLBnkc5V6oq/u9ep7FIpUHzl2vgaURY+Dr2Mp/7Rv/c=
256-
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220624101230-756b7ec05a7f/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
255+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220624140600-38685b7aadbb h1:47x0la4woy6fwiU5xcahypU6NCcfnprcrW39rWJCuDI=
256+
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9.0.20220624140600-38685b7aadbb/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
257257
github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdkkZBH4=
258258
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
259259
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=

scaleway/helpers_container.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111

1212
const (
1313
defaultContainerNamespaceTimeout = 5 * time.Minute
14+
defaultContainerCronTimeout = 5 * time.Minute
15+
defaultContainerTimeout = 5 * time.Minute
1416
defaultContainerRetryInterval = 5 * time.Second
1517
)
1618

@@ -100,18 +102,50 @@ func setCreateContainerRequest(d *schema.ResourceData, region scw.Region) (*cont
100102
return req, nil
101103
}
102104

103-
func waitForContainerNamespace(ctx context.Context, containerAPI *container.API, region scw.Region, id string, timeout time.Duration) (*container.Namespace, error) {
105+
func waitForContainerNamespace(ctx context.Context, containerAPI *container.API, region scw.Region, namespaceID string, timeout time.Duration) (*container.Namespace, error) {
104106
retryInterval := defaultContainerRetryInterval
105107
if DefaultWaitRetryInterval != nil {
106108
retryInterval = *DefaultWaitRetryInterval
107109
}
108110

109111
ns, err := containerAPI.WaitForNamespace(&container.WaitForNamespaceRequest{
110112
Region: region,
111-
NamespaceID: id,
113+
NamespaceID: namespaceID,
112114
RetryInterval: &retryInterval,
113115
Timeout: scw.TimeDurationPtr(timeout),
114116
}, scw.WithContext(ctx))
115117

116118
return ns, err
117119
}
120+
121+
func waitForContainerCron(ctx context.Context, api *container.API, cronID string, region scw.Region, timeout time.Duration) (*container.Cron, error) {
122+
retryInterval := defaultContainerRetryInterval
123+
if DefaultWaitRetryInterval != nil {
124+
retryInterval = *DefaultWaitRetryInterval
125+
}
126+
127+
request := container.WaitForCronRequest{
128+
CronID: cronID,
129+
Region: region,
130+
Timeout: scw.TimeDurationPtr(timeout),
131+
RetryInterval: &retryInterval,
132+
}
133+
134+
return api.WaitForCron(&request, scw.WithContext(ctx))
135+
}
136+
137+
func waitForContainer(ctx context.Context, api *container.API, containerID string, region scw.Region, timeout time.Duration) (*container.Container, error) {
138+
retryInterval := defaultContainerRetryInterval
139+
if DefaultWaitRetryInterval != nil {
140+
retryInterval = *DefaultWaitRetryInterval
141+
}
142+
143+
request := container.WaitForContainerRequest{
144+
ContainerID: containerID,
145+
Region: region,
146+
Timeout: scw.TimeDurationPtr(timeout),
147+
RetryInterval: &retryInterval,
148+
}
149+
150+
return api.WaitForContainer(&request, scw.WithContext(ctx))
151+
}

scaleway/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
8484
"scaleway_apple_silicon_server": resourceScalewayAppleSiliconServer(),
8585
"scaleway_baremetal_server": resourceScalewayBaremetalServer(),
8686
"scaleway_container_namespace": resourceScalewayContainerNamespace(),
87+
"scaleway_container_cron": resourceScalewayContainerCron(),
8788
"scaleway_domain_record": resourceScalewayDomainRecord(),
8889
"scaleway_domain_zone": resourceScalewayDomainZone(),
8990
"scaleway_function": resourceScalewayFunction(),

scaleway/resource_container.go

+29-60
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ func resourceScalewayContainer() *schema.Resource {
2424
StateContext: schema.ImportStatePassthroughContext,
2525
},
2626
Timeouts: &schema.ResourceTimeout{
27-
Default: schema.DefaultTimeout(defaultContainerNamespaceTimeout),
27+
Create: schema.DefaultTimeout(defaultContainerTimeout),
28+
Read: schema.DefaultTimeout(defaultContainerTimeout),
29+
Update: schema.DefaultTimeout(defaultContainerTimeout),
30+
Delete: schema.DefaultTimeout(defaultContainerTimeout),
31+
Default: schema.DefaultTimeout(defaultContainerTimeout),
2832
},
2933
SchemaVersion: 0,
3034
Schema: map[string]*schema.Schema{
@@ -167,14 +171,9 @@ func resourceScalewayContainerCreate(ctx context.Context, d *schema.ResourceData
167171
if region.String() == "" {
168172
region = scw.RegionFrPar
169173
}
170-
namespaceID := d.Get("namespace_id")
174+
namespaceID := expandID(d.Get("namespace_id").(string))
171175
// verify name space state
172-
_, err = api.WaitForNamespace(&container.WaitForNamespaceRequest{
173-
NamespaceID: expandID(namespaceID),
174-
Region: region,
175-
Timeout: scw.TimeDurationPtr(defaultRegistryNamespaceTimeout),
176-
RetryInterval: DefaultWaitRetryInterval,
177-
}, scw.WithContext(ctx))
176+
_, err = waitForContainerNamespace(ctx, api, region, namespaceID, d.Timeout(schema.TimeoutCreate))
178177
if err != nil {
179178
return diag.Errorf("unexpected namespace error: %s", err)
180179
}
@@ -192,10 +191,7 @@ func resourceScalewayContainerCreate(ctx context.Context, d *schema.ResourceData
192191
// check if container should be deployed
193192
shouldDeploy := d.Get("deploy")
194193
if *expandBoolPtr(shouldDeploy) {
195-
_, err := api.WaitForContainer(&container.WaitForContainerRequest{
196-
ContainerID: res.ID,
197-
Region: region,
198-
}, scw.WithContext(ctx))
194+
_, err = waitForContainer(ctx, api, res.ID, region, d.Timeout(schema.TimeoutCreate))
199195
if err != nil {
200196
return diag.Errorf("unexpected waiting container error: %s", err)
201197
}
@@ -209,6 +205,11 @@ func resourceScalewayContainerCreate(ctx context.Context, d *schema.ResourceData
209205
if err != nil {
210206
return diag.FromErr(err)
211207
}
208+
209+
_, err = waitForContainer(ctx, api, res.ID, region, d.Timeout(schema.TimeoutCreate))
210+
if err != nil {
211+
return diag.Errorf("unexpected waiting container error: %s", err)
212+
}
212213
}
213214

214215
d.SetId(newRegionalIDString(region, res.ID))
@@ -222,11 +223,12 @@ func resourceScalewayContainerRead(ctx context.Context, d *schema.ResourceData,
222223
return diag.FromErr(err)
223224
}
224225

225-
co, err := api.WaitForContainer(&container.WaitForContainerRequest{
226-
ContainerID: containerID,
227-
Region: region,
228-
}, scw.WithContext(ctx))
226+
co, err := waitForContainer(ctx, api, containerID, region, d.Timeout(schema.TimeoutCreate))
229227
if err != nil {
228+
if is404Error(err) {
229+
d.SetId("")
230+
return nil
231+
}
230232
return diag.Errorf("unexpected waiting container error: %s", err)
231233
}
232234

@@ -262,52 +264,17 @@ func resourceScalewayContainerUpdate(ctx context.Context, d *schema.ResourceData
262264

263265
namespaceID := d.Get("namespace_id")
264266
// verify name space state
265-
_, err = api.WaitForNamespace(&container.WaitForNamespaceRequest{
266-
NamespaceID: expandID(namespaceID),
267-
Region: region,
268-
}, scw.WithContext(ctx))
267+
_, err = waitForContainerNamespace(ctx, api, region, expandID(namespaceID), d.Timeout(schema.TimeoutUpdate))
269268
if err != nil {
270269
return diag.Errorf("unexpected namespace error: %s", err)
271270
}
272271

273272
// check for container state
274-
_, err = api.WaitForContainer(&container.WaitForContainerRequest{
275-
ContainerID: containerID,
276-
Region: region,
277-
}, scw.WithContext(ctx))
273+
_, err = waitForContainer(ctx, api, containerID, region, d.Timeout(schema.TimeoutUpdate))
278274
if err != nil {
279275
return diag.Errorf("unexpected waiting container error: %s", err)
280276
}
281277

282-
// Warning or Errors can be collected as warnings
283-
var diags diag.Diagnostics
284-
285-
// check triggers associated
286-
triggers, err := api.ListCrons(&container.ListCronsRequest{
287-
Region: region,
288-
ContainerID: containerID,
289-
}, scw.WithContext(ctx))
290-
if err != nil {
291-
return diag.FromErr(err)
292-
}
293-
294-
// wait for triggers state
295-
for _, c := range triggers.Crons {
296-
_, err := api.WaitForCron(&container.WaitForCronRequest{
297-
CronID: c.ID,
298-
Region: region,
299-
Timeout: scw.TimeDurationPtr(defaultContainerNamespaceTimeout),
300-
RetryInterval: DefaultWaitRetryInterval,
301-
}, scw.WithContext(ctx))
302-
if err != nil {
303-
diags = append(diags, diag.Diagnostic{
304-
Severity: diag.Warning,
305-
Summary: "Warning waiting cron job",
306-
Detail: err.Error(),
307-
})
308-
}
309-
}
310-
311278
// update container
312279
req := &container.UpdateContainerRequest{
313280
Region: region,
@@ -368,12 +335,17 @@ func resourceScalewayContainerUpdate(ctx context.Context, d *schema.ResourceData
368335
req.Redeploy = expandBoolPtr(d.Get("deploy"))
369336
}
370337

371-
_, err = api.UpdateContainer(req, scw.WithContext(ctx))
338+
con, err := api.UpdateContainer(req, scw.WithContext(ctx))
372339
if err != nil {
373340
return diag.FromErr(err)
374341
}
375342

376-
return append(diags, resourceScalewayContainerRead(ctx, d, meta)...)
343+
_, err = waitForContainer(ctx, api, con.ID, region, d.Timeout(schema.TimeoutUpdate))
344+
if err != nil {
345+
return diag.FromErr(err)
346+
}
347+
348+
return resourceScalewayContainerRead(ctx, d, meta)
377349
}
378350

379351
func resourceScalewayContainerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -383,12 +355,9 @@ func resourceScalewayContainerDelete(ctx context.Context, d *schema.ResourceData
383355
}
384356

385357
// check for container state
386-
_, err = api.WaitForContainer(&container.WaitForContainerRequest{
387-
ContainerID: containerID,
388-
Region: region,
389-
}, scw.WithContext(ctx))
358+
_, err = waitForContainer(ctx, api, containerID, region, d.Timeout(schema.TimeoutUpdate))
390359
if err != nil {
391-
return diag.Errorf("unexpected waiting container error: %s", err)
360+
return diag.FromErr(err)
392361
}
393362

394363
// delete container

0 commit comments

Comments
 (0)