Skip to content

Commit 48e0c2c

Browse files
committed
ACL test passed
1 parent 4a978b1 commit 48e0c2c

File tree

3 files changed

+3574
-24
lines changed

3 files changed

+3574
-24
lines changed

internal/services/k8s/acl.go

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import (
44
"context"
55
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
66
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
78
"github.com/scaleway/scaleway-sdk-go/api/k8s/v1"
89
"github.com/scaleway/scaleway-sdk-go/scw"
910
"github.com/scaleway/terraform-provider-scaleway/v2/internal/cdf"
11+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/dsf"
1012
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
1113
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
1214
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
@@ -37,29 +39,38 @@ func ResourceACL() *schema.Resource {
3739
Required: true,
3840
ForceNew: true,
3941
ValidateDiagFunc: verify.IsUUIDorUUIDWithLocality(),
42+
DiffSuppressFunc: dsf.Locality,
4043
Description: "Cluster on which the ACL is applied",
4144
},
42-
"acls": {
45+
"acl_rules": {
4346
Type: schema.TypeList,
44-
Optional: true,
47+
Required: true,
4548
Description: "The list of network rules that manage inbound traffic",
4649
Elem: &schema.Resource{
4750
Schema: map[string]*schema.Schema{
4851
"ip": {
49-
Type: schema.TypeString,
50-
Required: true,
51-
Description: "The IP subnet to be allowed",
52+
Type: schema.TypeString,
53+
Optional: true,
54+
Description: "The IP subnet to be allowed",
55+
ValidateFunc: validation.IsCIDR,
56+
//ExactlyOneOf: []string{"acl_rules.0.scaleway_ranges"},
5257
},
5358
"scaleway_ranges": {
5459
Type: schema.TypeBool,
5560
Optional: true,
5661
Description: "Allow access to cluster from all Scaleway ranges as defined in https://www.scaleway.com/en/docs/console/account/reference-content/scaleway-network-information/#ip-ranges-used-by-scaleway. Only one rule with this field set to true can be added",
62+
//ExactlyOneOf: []string{"acl_rules.0.ip"},
5763
},
5864
"description": {
5965
Type: schema.TypeString,
6066
Optional: true,
6167
Description: "The description of the ACL rule",
6268
},
69+
"id": {
70+
Type: schema.TypeString,
71+
Computed: true,
72+
Description: "The ID of the ACL rule",
73+
},
6374
},
6475
},
6576
},
@@ -71,19 +82,22 @@ func ResourceACL() *schema.Resource {
7182
}
7283

7384
func ResourceACLCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
74-
api, region, err := newAPIWithRegion(d, m)
85+
api, _, err := newAPIWithRegion(d, m)
7586
if err != nil {
7687
return diag.FromErr(err)
7788
}
7889

79-
clusterID := d.Get("cluster_id").(string)
90+
region, clusterID, err := regional.ParseID(d.Get("cluster_id").(string))
91+
if err != nil {
92+
return diag.FromErr(err)
93+
}
8094

8195
_, err = waitCluster(ctx, api, region, locality.ExpandID(clusterID), d.Timeout(schema.TimeoutCreate))
8296
if err != nil {
8397
return diag.FromErr(err)
8498
}
8599

86-
acls, err := expandACL(d.Get("acls").([]interface{}))
100+
acls, err := expandACL(d.Get("acl_rules").([]interface{}))
87101
if err != nil {
88102
return diag.FromErr(err)
89103
}
@@ -99,7 +113,8 @@ func ResourceACLCreate(ctx context.Context, d *schema.ResourceData, m interface{
99113
return diag.FromErr(err)
100114
}
101115

102-
d.SetId(clusterID)
116+
regionalID := regional.NewID(region, clusterID).String()
117+
d.SetId(regionalID)
103118

104119
return ResourceACLRead(ctx, d, m)
105120
}
@@ -129,11 +144,8 @@ func ResourceACLRead(ctx context.Context, d *schema.ResourceData, m interface{})
129144
return diag.FromErr(err)
130145
}
131146

132-
id := regional.NewID(region, clusterID).String()
133-
d.SetId(id)
134-
135147
_ = d.Set("cluster_id", clusterID)
136-
_ = d.Set("acls", flattenACL(acls.Rules))
148+
_ = d.Set("acl_rules", flattenACL(acls.Rules))
137149

138150
return nil
139151
}
@@ -149,8 +161,8 @@ func ResourceACLUpdate(ctx context.Context, d *schema.ResourceData, m interface{
149161
return diag.FromErr(err)
150162
}
151163

152-
if d.HasChange("acls") {
153-
acls, err := expandACL(d.Get("acls").([]interface{}))
164+
if d.HasChange("acl_rules") {
165+
acls, err := expandACL(d.Get("acl_rules").([]interface{}))
154166
if err != nil {
155167
return diag.FromErr(err)
156168
}
@@ -180,10 +192,19 @@ func ResourceACLDelete(ctx context.Context, d *schema.ResourceData, m interface{
180192
return diag.FromErr(err)
181193
}
182194

195+
allIPRange, err := types.ExpandIPNet("0.0.0.0/0")
196+
if err != nil {
197+
return diag.FromErr(err)
198+
}
199+
183200
req := &k8s.SetClusterACLRulesRequest{
184201
Region: region,
185202
ClusterID: clusterID,
186-
ACLs: nil,
203+
ACLs: []*k8s.ACLRuleRequest{
204+
{
205+
IP: &allIPRange,
206+
},
207+
},
187208
}
188209
_, err = api.SetClusterACLRules(req, scw.WithContext(ctx))
189210
if err != nil {
@@ -205,17 +226,17 @@ func expandACL(data []interface{}) ([]*k8s.ACLRuleRequest, error) {
205226
r := rule.(map[string]interface{})
206227
expandedRule := &k8s.ACLRuleRequest{}
207228

208-
if ipRaw, ok := r["ip"]; ok {
229+
if ipRaw, ipSet := r["ip"]; ipSet && ipRaw != "" {
209230
ip, err := types.ExpandIPNet(ipRaw.(string))
210231
if err != nil {
211232
return nil, err
212233
}
213234
expandedRule.IP = &ip
214235
}
215-
if scwRangesRaw, ok := r["scaleway_ranges"]; ok {
216-
expandedRule.ScalewayRanges = scw.BoolPtr(scwRangesRaw.(bool))
236+
if scwRangesRaw, scwRangesSet := r["scaleway_ranges"]; scwRangesSet && scwRangesRaw.(bool) == true {
237+
expandedRule.ScalewayRanges = scw.BoolPtr(true)
217238
}
218-
if descriptionRaw, ok := r["description"]; ok {
239+
if descriptionRaw, descriptionSet := r["description"]; descriptionSet && descriptionRaw.(string) != "" {
219240
expandedRule.Description = descriptionRaw.(string)
220241
}
221242

@@ -232,12 +253,15 @@ func flattenACL(rules []*k8s.ACLRule) interface{} {
232253

233254
flattenedACLs := []map[string]interface{}(nil)
234255
for _, rule := range rules {
235-
flattenedACLs = append(flattenedACLs, map[string]interface{}{
236-
//"id": rule.ID,
237-
"ip": rule.IP,
256+
flattenedRule := map[string]interface{}{
257+
"id": rule.ID,
238258
"scaleway_ranges": rule.ScalewayRanges,
239259
"description": rule.Description,
240-
})
260+
}
261+
if rule.IP != nil {
262+
flattenedRule["ip"] = rule.IP.String()
263+
}
264+
flattenedACLs = append(flattenedACLs, flattenedRule)
241265
}
242266

243267
return flattenedACLs

internal/services/k8s/acl_test.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package k8s_test
2+
3+
import (
4+
"fmt"
5+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
7+
k8sSDK "github.com/scaleway/scaleway-sdk-go/api/k8s/v1"
8+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
9+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/k8s"
10+
"testing"
11+
)
12+
13+
func TestAccACL_Basic(t *testing.T) {
14+
tt := acctest.NewTestTools(t)
15+
defer tt.Cleanup()
16+
17+
clusterName := "k8s-acl-basic"
18+
latestK8sVersion := testAccK8SClusterGetLatestK8SVersion(tt)
19+
20+
resource.ParallelTest(t, resource.TestCase{
21+
PreCheck: func() { acctest.PreCheck(t) },
22+
ProviderFactories: tt.ProviderFactories,
23+
CheckDestroy: testAccCheckK8SClusterDestroy(tt),
24+
Steps: []resource.TestStep{
25+
{
26+
Config: fmt.Sprintf(`
27+
resource "scaleway_vpc_private_network" "acl_basic" {}
28+
29+
resource "scaleway_k8s_cluster" "acl_basic" {
30+
name = "%s"
31+
version = "%s"
32+
cni = "cilium"
33+
delete_additional_resources = true
34+
private_network_id = scaleway_vpc_private_network.acl_basic.id
35+
}
36+
37+
resource "scaleway_k8s_acl" "acl_basic" {
38+
cluster_id = scaleway_k8s_cluster.acl_basic.id
39+
acl_rules {
40+
ip = "1.2.3.4/32"
41+
description = "First rule"
42+
}
43+
}`, clusterName, latestK8sVersion),
44+
Check: resource.ComposeTestCheckFunc(
45+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.#", "1"),
46+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.ip", "1.2.3.4/32"),
47+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.scaleway_ranges", "false"),
48+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.description", "First rule"),
49+
resource.TestCheckResourceAttrSet("scaleway_k8s_acl.acl_basic", "acl_rules.0.id"),
50+
),
51+
},
52+
{
53+
Config: fmt.Sprintf(`
54+
resource "scaleway_vpc_private_network" "acl_basic" {}
55+
56+
resource "scaleway_k8s_cluster" "acl_basic" {
57+
name = "%s"
58+
version = "%s"
59+
cni = "cilium"
60+
delete_additional_resources = true
61+
private_network_id = scaleway_vpc_private_network.acl_basic.id
62+
}
63+
64+
resource "scaleway_k8s_acl" "acl_basic" {
65+
cluster_id = scaleway_k8s_cluster.acl_basic.id
66+
acl_rules {
67+
ip = "1.2.3.4/32"
68+
}
69+
acl_rules {
70+
ip = "5.6.7.0/30"
71+
scaleway_ranges = false
72+
}
73+
}`, clusterName, latestK8sVersion),
74+
Check: resource.ComposeTestCheckFunc(
75+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.#", "2"),
76+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.ip", "1.2.3.4/32"),
77+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.scaleway_ranges", "false"),
78+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.description", ""),
79+
resource.TestCheckResourceAttrSet("scaleway_k8s_acl.acl_basic", "acl_rules.0.id"),
80+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.1.ip", "5.6.7.0/30"),
81+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.1.scaleway_ranges", "false"),
82+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.1.description", ""),
83+
resource.TestCheckResourceAttrSet("scaleway_k8s_acl.acl_basic", "acl_rules.1.id"),
84+
),
85+
},
86+
{
87+
Config: fmt.Sprintf(`
88+
resource "scaleway_vpc_private_network" "acl_basic" {}
89+
90+
resource "scaleway_k8s_cluster" "acl_basic" {
91+
name = "%s"
92+
version = "%s"
93+
cni = "cilium"
94+
delete_additional_resources = true
95+
private_network_id = scaleway_vpc_private_network.acl_basic.id
96+
}
97+
98+
resource "scaleway_k8s_acl" "acl_basic" {
99+
cluster_id = scaleway_k8s_cluster.acl_basic.id
100+
acl_rules {
101+
ip = "1.2.3.4/32"
102+
description = "First rule"
103+
}
104+
acl_rules {
105+
scaleway_ranges = true
106+
description = "Scaleway ranges rule"
107+
}
108+
}`, clusterName, latestK8sVersion),
109+
Check: resource.ComposeTestCheckFunc(
110+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.#", "2"),
111+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.ip", "1.2.3.4/32"),
112+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.scaleway_ranges", "false"),
113+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.0.description", "First rule"),
114+
resource.TestCheckResourceAttrSet("scaleway_k8s_acl.acl_basic", "acl_rules.0.id"),
115+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.1.ip", ""),
116+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.1.scaleway_ranges", "true"),
117+
resource.TestCheckResourceAttr("scaleway_k8s_acl.acl_basic", "acl_rules.1.description", "Scaleway ranges rule"),
118+
resource.TestCheckResourceAttrSet("scaleway_k8s_acl.acl_basic", "acl_rules.1.id"),
119+
),
120+
},
121+
{
122+
Config: fmt.Sprintf(`
123+
resource "scaleway_vpc_private_network" "acl_basic" {}
124+
125+
resource "scaleway_k8s_cluster" "acl_basic" {
126+
name = "%s"
127+
version = "%s"
128+
cni = "cilium"
129+
delete_additional_resources = true
130+
private_network_id = scaleway_vpc_private_network.acl_basic.id
131+
}`, clusterName, latestK8sVersion),
132+
Check: resource.ComposeTestCheckFunc(
133+
testAccCheckK8SClusterAllIPsAllowed(tt, "scaleway_k8s_cluster.acl_basic"),
134+
),
135+
},
136+
},
137+
})
138+
}
139+
140+
func testAccCheckK8SClusterAllIPsAllowed(tt *acctest.TestTools, n string) resource.TestCheckFunc {
141+
return func(s *terraform.State) error {
142+
rs, ok := s.RootModule().Resources[n]
143+
if !ok {
144+
return fmt.Errorf("resource not found: %s", n)
145+
}
146+
147+
k8sAPI, region, clusterID, err := k8s.NewAPIWithRegionAndID(tt.Meta, rs.Primary.ID)
148+
if err != nil {
149+
return err
150+
}
151+
152+
_, err = k8sAPI.WaitForCluster(&k8sSDK.WaitForClusterRequest{
153+
Region: region,
154+
ClusterID: clusterID,
155+
})
156+
if err != nil {
157+
return err
158+
}
159+
160+
acls, err := k8sAPI.ListClusterACLRules(&k8sSDK.ListClusterACLRulesRequest{
161+
Region: region,
162+
ClusterID: clusterID,
163+
})
164+
if err != nil {
165+
return err
166+
}
167+
if acls.TotalCount > 1 {
168+
return fmt.Errorf("unexpected number of ACL rules: %d (expected: 1)", acls.TotalCount)
169+
}
170+
if acls.Rules[0].IP == nil {
171+
return fmt.Errorf("unexpected CL rule: %+v", acls.Rules[0])
172+
}
173+
if acls.Rules[0].IP.String() != "0.0.0.0/0" {
174+
return fmt.Errorf("unexpected IP in ACL rule: %q (expected \"0.0.0.0/0\")", acls.Rules[0].IP.String())
175+
}
176+
177+
return nil
178+
}
179+
}

0 commit comments

Comments
 (0)