Skip to content

Commit

Permalink
Add selectel_dbaas_firewall_v1 resource
Browse files Browse the repository at this point in the history
- Make firewall field for datastore resources deprecated. Because, we
cannot change firewall state during creation of the datastore only after
- Add a new selectel_dbaas_firewall_v1 resource to manage datastore's
firewall
  • Loading branch information
Gogen120 committed May 23, 2024
1 parent 094de56 commit 8f0654e
Show file tree
Hide file tree
Showing 12 changed files with 360 additions and 26 deletions.
21 changes: 0 additions & 21 deletions selectel/dbaas.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,27 +166,6 @@ func resourceDBaaSDatastoreV1InstancesToList(instances []dbaas.Instances) []inte
return flattenedInstances
}

func resourceDBaaSDatastoreV1FirewallOptsFromSet(firewallSet *schema.Set) (dbaas.DatastoreFirewallOpts, error) {
if firewallSet.Len() == 0 {
return dbaas.DatastoreFirewallOpts{IPs: []string{}}, nil
}

var resourceIPsRaw interface{}
var ok bool

resourceFirewallRaw := firewallSet.List()[0].(map[string]interface{})
if resourceIPsRaw, ok = resourceFirewallRaw["ips"]; !ok {
return dbaas.DatastoreFirewallOpts{}, errors.New("firewall.ips value isn't provided")
}
resourceIPRaw := resourceIPsRaw.([]interface{})
var firewall dbaas.DatastoreFirewallOpts
for _, ip := range resourceIPRaw {
firewall.IPs = append(firewall.IPs, ip.(string))
}

return firewall, nil
}

func resourceDBaaSDatastoreV1RestoreOptsFromSet(restoreSet *schema.Set) (*dbaas.Restore, error) {
if restoreSet.Len() == 0 {
return nil, nil
Expand Down
1 change: 1 addition & 0 deletions selectel/dbaas_base_schemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func resourceDBaaSDatastoreV1BaseSchema() map[string]*schema.Schema {
},
},
},
Deprecated: "firewall has been deprecated in favour of using `selectel_dbaas_firewall_v1` resource instead.",
},
"config": {
Type: schema.TypeMap,
Expand Down
1 change: 1 addition & 0 deletions selectel/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ func Provider() *schema.Provider {
"selectel_dbaas_kafka_acl_v1": resourceDBaaSKafkaACLV1(),
"selectel_dbaas_kafka_datastore_v1": resourceDBaaSKafkaDatastoreV1(),
"selectel_dbaas_kafka_topic_v1": resourceDBaaSKafkaTopicV1(),
"selectel_dbaas_firewall_v1": resourceDBaaSFirewallV1(),
"selectel_craas_registry_v1": resourceCRaaSRegistryV1(),
"selectel_craas_token_v1": resourceCRaaSTokenV1(),
"selectel_secretsmanager_secret_v1": resourceSecretsManagerSecretV1(),
Expand Down
182 changes: 182 additions & 0 deletions selectel/resource_selectel_dbaas_firewall_v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package selectel

import (
"context"
"errors"
"log"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/selectel/dbaas-go"
schemas "github.com/terraform-providers/terraform-provider-selectel/selectel/schemas/dbaas"
waiters "github.com/terraform-providers/terraform-provider-selectel/selectel/waiters/dbaas"
)

func resourceDBaaSFirewallV1() *schema.Resource {
return &schema.Resource{
CreateContext: resourceDBaaSFirewallV1Update,
ReadContext: resourceDBaaSFirewallV1Read,
UpdateContext: resourceDBaaSFirewallV1Update,
DeleteContext: resourceDBaaSFirewallV1Delete,
Importer: &schema.ResourceImporter{
StateContext: resourceDBaaSFirewallV1ImportState,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(60 * time.Minute),
Update: schema.DefaultTimeout(60 * time.Minute),
Delete: schema.DefaultTimeout(60 * time.Minute),
},
Schema: schemas.ResourceDBaaSFirewallV1Schema(),
}
}

func resourceDBaaSFirewallV1Update(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
datastoreID := d.Get("datastore_id").(string)

dbaasClient, diagErr := getDBaaSClient(d, meta)
if diagErr != nil {
return diagErr
}

rawIPs := d.Get("ips").([]interface{})
firewallOpts, err := resourceDBaaSDatastoreV1FirewallOptsFromList(rawIPs)
if err != nil {
return diag.FromErr(errParseDatastoreV1Firewall(err))
}

log.Print(msgUpdate(objectDatastore, datastoreID, firewallOpts))
_, err = dbaasClient.FirewallDatastore(ctx, datastoreID, firewallOpts)
if err != nil {
return diag.FromErr(errUpdatingObject(objectDatastore, datastoreID, err))
}

log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", datastoreID)
timeout := d.Timeout(schema.TimeoutUpdate)
err = waiters.WaitForDBaaSDatastoreV1ActiveState(ctx, dbaasClient, datastoreID, timeout)
if err != nil {
return diag.FromErr(errUpdatingObject(objectDatastore, datastoreID, err))
}

return resourceDBaaSFirewallV1Read(ctx, d, meta)
}

func resourceDBaaSFirewallV1Read(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
datastoreID := d.Get("datastore_id").(string)

dbaasClient, diagErr := getDBaaSClient(d, meta)
if diagErr != nil {
return diagErr
}

log.Print(msgGet(objectDatastore, datastoreID))
datastore, err := dbaasClient.Datastore(ctx, datastoreID)
if err != nil {
return diag.FromErr(errGettingObject(objectDatastore, datastoreID, err))
}

checksum, err := firewallChecksum(datastore.Firewall, datastoreID)
if err != nil {
return diag.FromErr(err)
}
d.SetId(checksum)

return nil
}

func resourceDBaaSFirewallV1Delete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
datastoreID := d.Get("datastore_id").(string)

dbaasClient, diagErr := getDBaaSClient(d, meta)
if diagErr != nil {
return diagErr
}

firewallOpts := getEmptyFirewallOpts()

log.Print(msgUpdate(objectDatastore, datastoreID, firewallOpts))
_, err := dbaasClient.FirewallDatastore(ctx, datastoreID, firewallOpts)
if err != nil {
return diag.FromErr(errUpdatingObject(objectDatastore, datastoreID, err))
}

log.Printf("[DEBUG] waiting for datastore %s to become 'ACTIVE'", datastoreID)
timeout := d.Timeout(schema.TimeoutUpdate)
err = waiters.WaitForDBaaSDatastoreV1ActiveState(ctx, dbaasClient, datastoreID, timeout)
if err != nil {
return diag.FromErr(errUpdatingObject(objectDatastore, datastoreID, err))
}

return nil
}

func resourceDBaaSFirewallV1ImportState(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
config := meta.(*Config)
if config.ProjectID == "" {
return nil, errors.New("SEL_PROJECT_ID must be set for the resource import")
}
if config.Region == "" {
return nil, errors.New("SEL_REGION must be set for the resource import")
}

d.Set("project_id", config.ProjectID)
d.Set("region", config.Region)

return []*schema.ResourceData{d}, nil
}

func getEmptyFirewallOpts() dbaas.DatastoreFirewallOpts {
return dbaas.DatastoreFirewallOpts{IPs: []string{}}
}

func resourceDBaaSDatastoreV1FirewallOptsFromSet(firewallSet *schema.Set) (dbaas.DatastoreFirewallOpts, error) {
if firewallSet.Len() == 0 {
return getEmptyFirewallOpts(), nil
}

var resourceIPsRaw interface{}
var ok bool

resourceFirewallRaw := firewallSet.List()[0].(map[string]interface{})
if resourceIPsRaw, ok = resourceFirewallRaw["ips"]; !ok {
return dbaas.DatastoreFirewallOpts{}, errors.New("firewall.ips value isn't provided")
}
resourceIPRaw := resourceIPsRaw.([]interface{})
var firewall dbaas.DatastoreFirewallOpts
for _, ip := range resourceIPRaw {
firewall.IPs = append(firewall.IPs, ip.(string))
}

return firewall, nil
}

func resourceDBaaSDatastoreV1FirewallOptsFromList(rawList []interface{}) (dbaas.DatastoreFirewallOpts, error) {
if len(rawList) == 0 {
return getEmptyFirewallOpts(), nil
}

ipsList := make([]string, len(rawList))
for i := range rawList {
ipsList[i] = rawList[i].(string)
}

var firewall dbaas.DatastoreFirewallOpts
firewall.IPs = append(firewall.IPs, ipsList...)

return firewall, nil
}

func firewallChecksum(firewall []dbaas.Firewall, datastoreID string) (string, error) {
ipsList := make([]string, len(firewall))
for _, rule := range firewall {
ipsList = append(ipsList, rule.IP)
}

ipsList = append(ipsList, datastoreID)
checksum, err := stringListChecksum(ipsList)
if err != nil {
return "", err
}

return checksum, nil
}
76 changes: 76 additions & 0 deletions selectel/resource_selectel_dbaas_firewall_v1_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package selectel

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/selectel/go-selvpcclient/v3/selvpcclient/resell/v2/projects"
)

func TestAccDBaaSFirewallV1Basic(t *testing.T) {
var project projects.Project

projectName := acctest.RandomWithPrefix("tf-acc")
datastoreName := acctest.RandomWithPrefix("tf-acc-ds")
nodeCount := 1

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccSelectelPreCheck(t) },
ProviderFactories: testAccProviders,
CheckDestroy: testAccCheckVPCV2ProjectDestroy,
Steps: []resource.TestStep{
{
Config: testAccDBaaSFirewallV1Basic(projectName, datastoreName, nodeCount),
Check: resource.ComposeTestCheckFunc(
testAccCheckVPCV2ProjectExists("selectel_vpc_project_v2.project_tf_acc_test_1", &project),
resource.TestCheckResourceAttrSet("selectel_dbaas_firewall_v1.firewall_tf_acc_test_1", "datastore_id"),
resource.TestCheckResourceAttr("selectel_dbaas_firewall_v1.firewall_tf_acc_test_1", "ips.#", "2"),
),
},
},
})
}

func testAccDBaaSFirewallV1Basic(projectName, datastoreName string, nodeCount int) string {
return fmt.Sprintf(`
resource "selectel_vpc_project_v2" "project_tf_acc_test_1" {
name = "%s"
}
resource "selectel_vpc_subnet_v2" "subnet_tf_acc_test_1" {
project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}"
region = "ru-3"
}
data "selectel_dbaas_datastore_type_v1" "dt" {
project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}"
region = "ru-3"
filter {
engine = "postgresql"
version = "16"
}
}
resource "selectel_dbaas_datastore_v1" "datastore_tf_acc_test_1" {
name = "%s"
project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}"
region = "ru-3"
type_id = "${data.selectel_dbaas_datastore_type_v1.dt.datastore_types[0].id}"
subnet_id = "${selectel_vpc_subnet_v2.subnet_tf_acc_test_1.subnet_id}"
node_count = "%d"
flavor {
vcpus = 2
ram = 4096
disk = 32
}
}
resource "selectel_dbaas_firewall_v1" "firewall_tf_acc_test_1" {
project_id = "${selectel_vpc_project_v2.project_tf_acc_test_1.id}"
region = "ru-3"
datastore_id = "${selectel_dbaas_datastore_v1.datastore_tf_acc_test_1.id}"
ips = [ "127.0.0.1", "127.0.0.2" ]
}`, projectName, datastoreName, nodeCount)
}
30 changes: 30 additions & 0 deletions selectel/schemas/dbaas/schema_selectel_dbaas_firewall_v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package schemas

import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

func ResourceDBaaSFirewallV1Schema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"project_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"region": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"datastore_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"ips": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
}
}
2 changes: 1 addition & 1 deletion website/docs/r/dbaas_datastore_v1.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ The following arguments are supported:

* `pooler` - (Optional) Pooler configuration for the datastore (only for PostgreSQL datastore). It's a complex value. See description below.

* `firewall` - (Optional) List of the ips to allow access from.
* `firewall` - (Deprecated) Remove this argument as it is no longer in use and will be removed in the next major version of the provider. To manage a list of IP-addresses with access to the datastore, use the [selectel_dbaas_firewall_v1](https://registry.terraform.io/providers/selectel/selectel/latest/docs/resources/dbaas_firewall_v1) resource.

* `restore` - (Optional) Restore parameters for the datastore. It's a complex value. See description below.
Changing this creates a new datastore.
Expand Down
Loading

0 comments on commit 8f0654e

Please sign in to comment.