Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(redis): add redis acl #1256

Merged
merged 5 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/resources/redis_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ resource "scaleway_redis_cluster" "main" {
tags = [ "test", "redis" ]
cluster_size = 1
tls_enabled = "true"

acl {
ip = "0.0.0.0/0"
description = "Allow all"
}
}
```

Expand Down Expand Up @@ -56,6 +61,13 @@ The following arguments are supported:

- `project_id` - (Defaults to [provider](../index.md) `project_id`) The ID of the project the Redis Cluster is associated with.

- `acl` - (Optional) List of acl rules, this is cluster's authorized IPs.

The `acl` block supports:

- `ip` - (Required) The ip range to whitelist in [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation)
- `description` - (Optional) A text describing this rule. Default description: `Allow IP`

## Attributes Reference

In addition to all arguments above, the following attributes are exported:
Expand Down
33 changes: 33 additions & 0 deletions scaleway/helpers_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package scaleway

import (
"context"
"fmt"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -53,3 +54,35 @@ func waitForRedisCluster(ctx context.Context, api *redis.API, zone scw.Zone, id
RetryInterval: &retryInterval,
}, scw.WithContext(ctx))
}

func expandRedisACLSpecs(i interface{}) ([]*redis.ACLRuleSpec, error) {
rules := []*redis.ACLRuleSpec(nil)

for _, aclRule := range i.([]interface{}) {
rawRule := aclRule.(map[string]interface{})
rule := &redis.ACLRuleSpec{}
if ruleDescription, hasDescription := rawRule["description"]; hasDescription {
rule.Description = ruleDescription.(string)
}
ip, err := expandIPNet(rawRule["ip"].(string))
if err != nil {
return nil, fmt.Errorf("failed to validate acl ip (%s): %w", rawRule["ip"].(string), err)
}
rule.IP = ip
rules = append(rules, rule)
}

return rules, nil
}

func flattenRedisACLs(aclRules []*redis.ACLRule) interface{} {
flat := []map[string]interface{}(nil)
for _, acl := range aclRules {
flat = append(flat, map[string]interface{}{
"id": acl.ID,
"ip": acl.IP.String(),
"description": flattenStringPtr(acl.Description),
})
}
return flat
}
58 changes: 58 additions & 0 deletions scaleway/resource_redis_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ func resourceScalewayRedisCluster() *schema.Resource {
Computed: true,
Description: "The date and time of the last update of the Redis cluster",
},
"acl": {
Type: schema.TypeList,
Description: "List of acl rules.",
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Description: "ID of the rule (UUID format).",
Computed: true,
},
"ip": {
Type: schema.TypeString,
Description: "IPv4 network address of the rule (IP network in a CIDR format).",
Required: true,
},
"description": {
Type: schema.TypeString,
Description: "Description of the rule.",
Optional: true,
Computed: true,
},
},
},
},
// Common
"zone": zoneSchema(),
"project_id": projectIDSchema(),
Expand Down Expand Up @@ -116,6 +141,14 @@ func resourceScalewayRedisClusterCreate(ctx context.Context, d *schema.ResourceD
if tlsEnabledExist {
createReq.TLSEnabled = tlsEnabled.(bool)
}
aclRules, aclRulesExist := d.GetOk("acl")
if aclRulesExist {
rules, err := expandRedisACLSpecs(aclRules)
if err != nil {
return diag.FromErr(err)
}
createReq.ACLRules = rules
}

res, err := redisAPI.CreateCluster(createReq, scw.WithContext(ctx))
if err != nil {
Expand Down Expand Up @@ -161,6 +194,7 @@ func resourceScalewayRedisClusterRead(ctx context.Context, d *schema.ResourceDat
_ = d.Set("cluster_size", cluster.ClusterSize)
_ = d.Set("created_at", cluster.CreatedAt.Format(time.RFC3339))
_ = d.Set("updated_at", cluster.UpdatedAt.Format(time.RFC3339))
_ = d.Set("acl", flattenRedisACLs(cluster.ACLRules))

if len(cluster.Tags) > 0 {
_ = d.Set("tags", cluster.Tags)
Expand Down Expand Up @@ -192,6 +226,12 @@ func resourceScalewayRedisClusterUpdate(ctx context.Context, d *schema.ResourceD
if d.HasChange("tags") {
req.Tags = expandStrings(d.Get("tags"))
}
if d.HasChange("acl") {
diagnostics := resourceScalewayRedisClusterUpdateACL(ctx, d, redisAPI, zone, ID)
if diagnostics != nil {
return diagnostics
}
}

_, err = waitForRedisCluster(ctx, redisAPI, zone, ID, d.Timeout(schema.TimeoutUpdate))
if err != nil {
Expand Down Expand Up @@ -249,6 +289,24 @@ func resourceScalewayRedisClusterUpdate(ctx context.Context, d *schema.ResourceD
return resourceScalewayRedisClusterRead(ctx, d, meta)
}

func resourceScalewayRedisClusterUpdateACL(ctx context.Context, d *schema.ResourceData, redisAPI *redis.API, zone scw.Zone, clusterID string) diag.Diagnostics {
rules, err := expandRedisACLSpecs(d.Get("acl"))
if err != nil {
return diag.FromErr(err)
}

_, err = redisAPI.SetACLRules(&redis.SetACLRulesRequest{
Zone: zone,
ClusterID: clusterID,
ACLRules: rules,
}, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceScalewayRedisClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
redisAPI, zone, ID, err := redisAPIWithZoneAndID(meta, d.Id())
if err != nil {
Expand Down
71 changes: 71 additions & 0 deletions scaleway/resource_redis_cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,77 @@ func TestAccScalewayRedisCluster_Migrate(t *testing.T) {
})
}

func TestAccScalewayRedisCluster_ACL(t *testing.T) {
tt := NewTestTools(t)
defer tt.Cleanup()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: testAccCheckScalewayRedisClusterDestroy(tt),
Steps: []resource.TestStep{
{
Config: `
resource "scaleway_redis_cluster" "main" {
name = "test_redis_acl"
version = "6.2.6"
node_type = "MDB-BETA-M"
user_name = "my_initial_user"
password = "thiZ_is_v&ry_s3cret"
acl {
ip = "0.0.0.0/0"
description = "An acl description"
}
acl {
ip = "192.168.10.0/24"
description = "A second acl description"
}
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayRedisExists(tt, "scaleway_redis_cluster.main"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "name", "test_redis_acl"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "version", "6.2.6"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "node_type", "MDB-BETA-M"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "user_name", "my_initial_user"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "password", "thiZ_is_v&ry_s3cret"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "acl.0.ip", "0.0.0.0/0"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "acl.0.description", "An acl description"),
resource.TestCheckResourceAttrSet("scaleway_redis_cluster.main", "acl.0.id"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "acl.1.ip", "192.168.10.0/24"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "acl.1.description", "A second acl description"),
resource.TestCheckResourceAttrSet("scaleway_redis_cluster.main", "acl.1.id"),
),
},
{
Config: `
resource "scaleway_redis_cluster" "main" {
name = "test_redis_acl"
version = "6.2.6"
node_type = "MDB-BETA-M"
user_name = "my_initial_user"
password = "thiZ_is_v&ry_s3cret"
acl {
ip = "192.168.11.0/24"
description = "Another acl description"
}
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayRedisExists(tt, "scaleway_redis_cluster.main"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "name", "test_redis_acl"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "version", "6.2.6"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "node_type", "MDB-BETA-M"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "user_name", "my_initial_user"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "password", "thiZ_is_v&ry_s3cret"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "acl.0.ip", "192.168.11.0/24"),
resource.TestCheckResourceAttr("scaleway_redis_cluster.main", "acl.0.description", "Another acl description"),
resource.TestCheckResourceAttrSet("scaleway_redis_cluster.main", "acl.0.id"),
),
},
},
})
}

func testAccCheckScalewayRedisClusterDestroy(tt *TestTools) resource.TestCheckFunc {
return func(state *terraform.State) error {
for _, rs := range state.RootModule().Resources {
Expand Down
Loading