Skip to content

Commit af89512

Browse files
yfodilMonitob
andauthored
feat(lb): add lb acl (#1948)
* feat(lb): add lb acl * tests & doc * fix frontend resource * update cassette * update cassettes * add description --------- Co-authored-by: jaime Bernabe <[email protected]>
1 parent f7c65c0 commit af89512

21 files changed

+9117
-5497
lines changed

docs/resources/lb_acl.md

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
subcategory: "Load Balancers"
3+
page_title: "Scaleway: scaleway_lb_acl"
4+
---
5+
6+
# scaleway_lb_acl
7+
8+
Creates and manages Scaleway Load-Balancer ACLs. For more information, see [the documentation](https://www.scaleway.com/en/developers/api/load-balancer/zoned-api/#path-acls).
9+
10+
## Examples Usage
11+
12+
### Basic
13+
14+
```hcl
15+
resource "scaleway_lb_acl" "acl01" {
16+
frontend_id = scaleway_lb_frontend.frt01.id
17+
name = "acl01"
18+
description = "Exclude well-known IPs"
19+
index = 0
20+
# Allow downstream requests from: 192.168.0.1, 192.168.0.2 or 192.168.10.0/24
21+
action {
22+
type = "allow"
23+
}
24+
match {
25+
ip_subnet = ["192.168.0.1", "192.168.0.2", "192.168.10.0/24"]
26+
}
27+
}
28+
```
29+
30+
## Arguments Reference
31+
32+
The following arguments are supported:
33+
34+
- `frontend_id` - (Required) The load-balancer Frontend ID to attach the ACL to.
35+
36+
- `name` - (Optional) The ACL name. If not provided it will be randomly generated.
37+
38+
- `description` - (Optional) The ACL description.
39+
40+
- `index` - (Required) The Priority of this ACL (ACLs are applied in ascending order, 0 is the first ACL executed).
41+
42+
- `action` - (Required) Action to undertake when an ACL filter matches.
43+
44+
- `type` - (Required) The action type. Possible values are: `allow` or `deny` or `redirect`.
45+
46+
- `redirect` - (Optional) Redirect parameters when using an ACL with `redirect` action.
47+
48+
- `type` - (Optional) The redirect type. Possible values are: `location` or `scheme`.
49+
50+
- `target` - (Optional) An URL can be used in case of a location redirect (e.g. `https://scaleway.com` will redirect to this same URL). A scheme name (e.g. `https`, `http`, `ftp`, `git`) will replace the request's original scheme.
51+
52+
- `code` - (Optional) The HTTP redirect code to use. Valid values are `301`, `302`, `303`, `307` and `308`.
53+
54+
- `match` - (Required) The ACL match rule. At least `ip_subnet` or `http_filter` and `http_filter_value` are required.
55+
56+
- `ip_subnet` - (Optional) A list of IPs or CIDR v4/v6 addresses of the client of the session to match.
57+
58+
- `http_filter` - (Optional) The HTTP filter to match. This filter is supported only if your backend protocol has an HTTP forward protocol.
59+
It extracts the request's URL path, which starts at the first slash and ends before the question mark (without the host part).
60+
Possible values are: `acl_http_filter_none`, `path_begin`, `path_end`, `http_header_match` or `regex`.
61+
62+
- `http_filter_value` - (Optional) A list of possible values to match for the given HTTP filter.
63+
Keep in mind that in the case of `http_header_match` the HTTP header field name is case-insensitive.
64+
65+
- `http_value_option` - (Optional) If you have `http_filter` at `http_header_match`, you can use this field to filter on the HTTP header's value.
66+
67+
- `invert` - (Optional) If set to `true`, the condition will be of type "unless".
68+
69+
## Attributes Reference
70+
71+
In addition to all arguments above, the following attributes are exported:
72+
73+
- `id` - The ID of the load-balancer ACL.
74+
75+
~> **Important:** Load-Balancers ACLs' IDs are [zoned](../guides/regions_and_zones.md#resource-ids), which means they are of the form `{zone}/{id}`, e.g. `fr-par-1/11111111-1111-1111-1111-111111111111`
76+
77+
78+
## Import
79+
80+
Load-Balancer ACL can be imported using the `{zone}/{id}`, e.g.
81+
82+
```bash
83+
$ terraform import scaleway_lb_acl.acl01 fr-par-1/11111111-1111-1111-1111-111111111111
84+
```

docs/resources/lb_frontend.md

+3
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ The following arguments are supported:
201201
- `http_value_option` - (Optional) If you have `http_filter` at `http_header_match`, you can use this field to filter on the HTTP header's value.
202202

203203
- `invert` - (Optional) If set to `true`, the condition will be of type "unless".
204+
205+
- `external_acls` - (Defaults to `false`) A boolean to specify whether to use [lb_acl](../resources/lb_acl.md).
206+
If `external_acls` is set to `true`, `acl` can not be set directly in the lb frontend.
204207

205208
## Attributes Reference
206209

scaleway/helpers_lb.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,17 @@ func flattenLbACL(acl *lbSDK.ACL) interface{} {
6565
func expandLbACL(i interface{}) *lbSDK.ACL {
6666
rawRule := i.(map[string]interface{})
6767
acl := &lbSDK.ACL{
68-
Name: rawRule["name"].(string),
69-
Match: expandLbACLMatch(rawRule["match"]),
70-
Action: expandLbACLAction(rawRule["action"]),
68+
Name: rawRule["name"].(string),
69+
Description: rawRule["description"].(string),
70+
Match: expandLbACLMatch(rawRule["match"]),
71+
Action: expandLbACLAction(rawRule["action"]),
72+
CreatedAt: expandTimePtr(rawRule["created_at"]),
73+
UpdatedAt: expandTimePtr(rawRule["updated_at"]),
7174
}
7275

76+
if rawRule["index"] != nil {
77+
acl.Index = int32(rawRule["index"].(int))
78+
}
7379
// remove http filter values if we do not pass any http filter
7480
if acl.Match.HTTPFilter == "" || acl.Match.HTTPFilter == lbSDK.ACLHTTPFilterACLHTTPFilterNone {
7581
acl.Match.HTTPFilter = lbSDK.ACLHTTPFilterACLHTTPFilterNone

scaleway/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ func Provider(config *ProviderConfig) plugin.ProviderFunc {
126126
"scaleway_k8s_cluster": resourceScalewayK8SCluster(),
127127
"scaleway_k8s_pool": resourceScalewayK8SPool(),
128128
"scaleway_lb": resourceScalewayLb(),
129+
"scaleway_lb_acl": resourceScalewayLbACL(),
129130
"scaleway_lb_ip": resourceScalewayLbIP(),
130131
"scaleway_lb_backend": resourceScalewayLbBackend(),
131132
"scaleway_lb_certificate": resourceScalewayLbCertificate(),

scaleway/resource_lb_acl.go

+267
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
package scaleway
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
9+
lbSDK "github.com/scaleway/scaleway-sdk-go/api/lb/v1"
10+
"github.com/scaleway/scaleway-sdk-go/scw"
11+
)
12+
13+
func resourceScalewayLbACL() *schema.Resource {
14+
return &schema.Resource{
15+
CreateContext: resourceScalewayLbACLCreate,
16+
ReadContext: resourceScalewayLbACLRead,
17+
UpdateContext: resourceScalewayLbACLUpdate,
18+
DeleteContext: resourceScalewayLbACLDelete,
19+
Importer: &schema.ResourceImporter{
20+
StateContext: schema.ImportStatePassthroughContext,
21+
},
22+
Timeouts: &schema.ResourceTimeout{
23+
Default: schema.DefaultTimeout(defaultLbLbTimeout),
24+
},
25+
SchemaVersion: 1,
26+
Schema: map[string]*schema.Schema{
27+
"frontend_id": {
28+
Type: schema.TypeString,
29+
Required: true,
30+
ForceNew: true,
31+
ValidateFunc: validationUUIDorUUIDWithLocality(),
32+
Description: "The frontend ID on which the ACL is applied",
33+
},
34+
"name": {
35+
Type: schema.TypeString,
36+
Optional: true,
37+
Computed: true,
38+
Description: "The ACL name",
39+
},
40+
"description": {
41+
Type: schema.TypeString,
42+
Optional: true,
43+
Description: "Description of the ACL",
44+
},
45+
"index": {
46+
Type: schema.TypeInt,
47+
Description: "The priority of the ACL. (ACLs are applied in ascending order, 0 is the first ACL executed)",
48+
Required: true,
49+
ValidateFunc: validation.IntAtLeast(0),
50+
},
51+
"action": {
52+
Type: schema.TypeList,
53+
Required: true,
54+
Description: "Action to undertake when an ACL filter matches",
55+
MaxItems: 1,
56+
MinItems: 1,
57+
Elem: &schema.Resource{
58+
Schema: map[string]*schema.Schema{
59+
"type": {
60+
Type: schema.TypeString,
61+
Required: true,
62+
ValidateFunc: validation.StringInSlice([]string{
63+
lbSDK.ACLActionTypeAllow.String(),
64+
lbSDK.ACLActionTypeDeny.String(),
65+
lbSDK.ACLActionTypeRedirect.String(),
66+
}, false),
67+
Description: "The action type",
68+
},
69+
"redirect": {
70+
Type: schema.TypeList,
71+
Optional: true,
72+
Description: "Redirect parameters when using an ACL with `redirect` action",
73+
Elem: &schema.Resource{
74+
Schema: map[string]*schema.Schema{
75+
"type": {
76+
Type: schema.TypeString,
77+
Optional: true,
78+
ValidateFunc: validation.StringInSlice([]string{
79+
lbSDK.ACLActionRedirectRedirectTypeLocation.String(),
80+
lbSDK.ACLActionRedirectRedirectTypeScheme.String(),
81+
}, false),
82+
Description: "The redirect type",
83+
},
84+
"target": {
85+
Type: schema.TypeString,
86+
Optional: true,
87+
Description: "An URL can be used in case of a location redirect ",
88+
},
89+
"code": {
90+
Type: schema.TypeInt,
91+
Optional: true,
92+
Description: "The HTTP redirect code to use",
93+
},
94+
},
95+
},
96+
},
97+
},
98+
},
99+
},
100+
"match": {
101+
Type: schema.TypeList,
102+
Optional: true,
103+
MaxItems: 1,
104+
MinItems: 1,
105+
Description: "The ACL match rule",
106+
Elem: &schema.Resource{
107+
Schema: map[string]*schema.Schema{
108+
"ip_subnet": {
109+
Type: schema.TypeList,
110+
Elem: &schema.Schema{
111+
Type: schema.TypeString,
112+
},
113+
Optional: true,
114+
Description: "A list of IPs or CIDR v4/v6 addresses of the client of the session to match",
115+
},
116+
"http_filter": {
117+
Type: schema.TypeString,
118+
Optional: true,
119+
Default: lbSDK.ACLHTTPFilterACLHTTPFilterNone.String(),
120+
ValidateFunc: validation.StringInSlice([]string{
121+
lbSDK.ACLHTTPFilterACLHTTPFilterNone.String(),
122+
lbSDK.ACLHTTPFilterPathBegin.String(),
123+
lbSDK.ACLHTTPFilterPathEnd.String(),
124+
lbSDK.ACLHTTPFilterRegex.String(),
125+
lbSDK.ACLHTTPFilterHTTPHeaderMatch.String(),
126+
}, false),
127+
Description: "The HTTP filter to match",
128+
},
129+
"http_filter_value": {
130+
Type: schema.TypeList,
131+
Optional: true,
132+
Description: "A list of possible values to match for the given HTTP filter",
133+
Elem: &schema.Schema{
134+
Type: schema.TypeString,
135+
},
136+
},
137+
"http_filter_option": {
138+
Type: schema.TypeString,
139+
Optional: true,
140+
Description: "You can use this field with http_header_match acl type to set the header name to filter",
141+
},
142+
"invert": {
143+
Type: schema.TypeBool,
144+
Optional: true,
145+
Description: `If set to true, the condition will be of type "unless"`,
146+
},
147+
},
148+
},
149+
},
150+
"created_at": {
151+
Type: schema.TypeString,
152+
Computed: true,
153+
Description: "Date and time of ACL's creation (RFC 3339 format)",
154+
},
155+
"updated_at": {
156+
Type: schema.TypeString,
157+
Computed: true,
158+
Description: "Date and time of ACL's update (RFC 3339 format)",
159+
},
160+
},
161+
}
162+
}
163+
164+
func resourceScalewayLbACLCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
165+
lbAPI, _, err := lbAPIWithZone(d, meta)
166+
if err != nil {
167+
return diag.FromErr(err)
168+
}
169+
170+
frontZone, frontID, err := parseZonedID(d.Get("frontend_id").(string))
171+
if err != nil {
172+
return diag.FromErr(err)
173+
}
174+
175+
req := &lbSDK.ZonedAPICreateACLRequest{
176+
Zone: frontZone,
177+
FrontendID: frontID,
178+
Name: d.Get("name").(string),
179+
Action: expandLbACLAction(d.Get("action")),
180+
Match: expandLbACLMatch(d.Get("match")),
181+
Index: int32(d.Get("index").(int)),
182+
Description: d.Get("description").(string),
183+
}
184+
185+
res, err := lbAPI.CreateACL(req, scw.WithContext(ctx))
186+
if err != nil {
187+
return diag.FromErr(err)
188+
}
189+
190+
d.SetId(newZonedIDString(frontZone, res.ID))
191+
192+
return resourceScalewayLbACLRead(ctx, d, meta)
193+
}
194+
195+
func resourceScalewayLbACLRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
196+
lbAPI, zone, ID, err := lbAPIWithZoneAndID(meta, d.Id())
197+
if err != nil {
198+
return diag.FromErr(err)
199+
}
200+
201+
acl, err := lbAPI.GetACL(&lbSDK.ZonedAPIGetACLRequest{
202+
Zone: zone,
203+
ACLID: ID,
204+
}, scw.WithContext(ctx))
205+
if err != nil {
206+
if is404Error(err) {
207+
d.SetId("")
208+
return nil
209+
}
210+
return diag.FromErr(err)
211+
}
212+
213+
_ = d.Set("frontend_id", newZonedIDString(zone, acl.Frontend.ID))
214+
_ = d.Set("name", acl.Name)
215+
_ = d.Set("description", acl.Description)
216+
_ = d.Set("index", int(acl.Index))
217+
_ = d.Set("created_at", flattenTime(acl.CreatedAt))
218+
_ = d.Set("updated_at", flattenTime(acl.UpdatedAt))
219+
_ = d.Set("action", flattenLbACLAction(acl.Action))
220+
221+
if acl.Match != nil {
222+
_ = d.Set("match", flattenLbACLMatch(acl.Match))
223+
}
224+
225+
return nil
226+
}
227+
228+
func resourceScalewayLbACLUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
229+
lbAPI, zone, ID, err := lbAPIWithZoneAndID(meta, d.Id())
230+
if err != nil {
231+
return diag.FromErr(err)
232+
}
233+
234+
req := &lbSDK.ZonedAPIUpdateACLRequest{
235+
Zone: zone,
236+
ACLID: ID,
237+
Name: d.Get("name").(string),
238+
Action: expandLbACLAction(d.Get("action")),
239+
Index: int32(d.Get("index").(int)),
240+
Match: expandLbACLMatch(d.Get("match")),
241+
Description: expandUpdatedStringPtr(d.Get("description")),
242+
}
243+
244+
_, err = lbAPI.UpdateACL(req, scw.WithContext(ctx))
245+
if err != nil {
246+
return diag.FromErr(err)
247+
}
248+
249+
return resourceScalewayLbACLRead(ctx, d, meta)
250+
}
251+
252+
func resourceScalewayLbACLDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
253+
lbAPI, zone, ID, err := lbAPIWithZoneAndID(meta, d.Id())
254+
if err != nil {
255+
return diag.FromErr(err)
256+
}
257+
258+
err = lbAPI.DeleteACL(&lbSDK.ZonedAPIDeleteACLRequest{
259+
Zone: zone,
260+
ACLID: ID,
261+
}, scw.WithContext(ctx))
262+
if err != nil {
263+
return diag.FromErr(err)
264+
}
265+
266+
return nil
267+
}

0 commit comments

Comments
 (0)