Skip to content

Commit

Permalink
feat(core): get raw config for key (#2419)
Browse files Browse the repository at this point in the history
* feat(core): get raw config for key

* rdb instance volume cassette

* cassette re-recorded

---------

Co-authored-by: Jules Castéran <jcasteran@scaleway.com>
  • Loading branch information
Mia-Cross and Codelax committed Mar 21, 2024
1 parent ba2827f commit 635420c
Show file tree
Hide file tree
Showing 6 changed files with 5,422 additions and 535 deletions.
60 changes: 60 additions & 0 deletions scaleway/helpers.go
Expand Up @@ -422,3 +422,63 @@ func findExact[T any](slice []T, finder func(T) bool, searchName string) (T, err

return found, nil
}

func GetKeyInRawConfigMap(rawConfig map[string]cty.Value, key string, ty cty.Type) (interface{}, bool) {
if key == "" {
return rawConfig, false
}
// We split the key into its elements
keys := strings.Split(key, ".")

// We look at the first element's type
if value, ok := rawConfig[keys[0]]; ok {
switch {
case value.Type().IsListType():
// If it's a list and the second element of the key is an index, we look for the value in the list at the given index
if index, err := strconv.Atoi(keys[1]); err == nil {
return GetKeyInRawConfigMap(value.AsValueSlice()[index].AsValueMap(), strings.Join(keys[2:], ""), ty)
}
// If it's a list and the second element of the key is '#', we look for the value in the list's first element
return GetKeyInRawConfigMap(value.AsValueSlice()[0].AsValueMap(), strings.Join(keys[2:], ""), ty)

case value.Type().IsMapType():
// If it's a map, we look for the value in the map
return GetKeyInRawConfigMap(value.AsValueMap(), strings.Join(keys[1:], ""), ty)

case value.Type().IsPrimitiveType():
// If it's a primitive type (bool, string, number), we convert the value to the expected type given as parameter before returning it
switch ty {
case cty.String:
if value.IsNull() {
return nil, false
}
return value.AsString(), true
case cty.Bool:
if value.IsNull() {
return false, false
}
if value.True() {
return true, true
}
return false, true
case cty.Number:
if value.IsNull() {
return nil, false
}
valueInt, _ := value.AsBigFloat().Int64()
return valueInt, true
}
}
}
return nil, false
}

// getRawConfigForKey returns the value for a specific key in the user's raw configuration, which can be useful on resources' update
// The value for the key to look for must be a primitive type (bool, string, number) and the expected type of the value should be passed as the ty parameter
func getRawConfigForKey(d *schema.ResourceData, key string, ty cty.Type) (interface{}, bool) {
rawConfig := d.GetRawConfig()
if rawConfig.IsNull() {
return nil, false
}
return GetKeyInRawConfigMap(rawConfig.AsValueMap(), key, ty)
}
18 changes: 5 additions & 13 deletions scaleway/helpers_rdb.go
Expand Up @@ -355,19 +355,11 @@ func getIPConfigCreate(d *schema.ResourceData, ipFieldName string) (ipamConfig *

// getIPConfigUpdate forces the provider to read the user's config instead of checking the state, because "enable_ipam" is not readable from the API
func getIPConfigUpdate(d *schema.ResourceData, ipFieldName string) (ipamConfig *bool, staticConfig *string) {
if rawConfig := d.GetRawConfig(); !rawConfig.IsNull() {
pnRawConfig := rawConfig.AsValueMap()["private_network"].AsValueSlice()[0].AsValueMap()
if !pnRawConfig["enable_ipam"].IsNull() {
if pnRawConfig["enable_ipam"].False() {
ipamConfig = scw.BoolPtr(false)
} else {
ipamConfig = scw.BoolPtr(true)
}
}
if !pnRawConfig[ipFieldName].IsNull() {
value := pnRawConfig[ipFieldName].AsString()
staticConfig = &value
}
if ipamConfigI, _ := getRawConfigForKey(d, "private_network.#.enable_ipam", cty.Bool); ipamConfigI != nil {
ipamConfig = types.ExpandBoolPtr(ipamConfigI)
}
if staticConfigI, _ := getRawConfigForKey(d, "private_network.#."+ipFieldName, cty.String); staticConfigI != nil {
staticConfig = types.ExpandStringPtr(staticConfigI)
}
return ipamConfig, staticConfig
}
Expand Down
158 changes: 158 additions & 0 deletions scaleway/helpers_test.go
Expand Up @@ -6,9 +6,12 @@ import (
"regexp"
"testing"

"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
"github.com/scaleway/terraform-provider-scaleway/v2/scaleway"
"github.com/stretchr/testify/assert"
)

func testCheckResourceAttrFunc(name string, key string, test func(string) error) resource.TestCheckFunc {
Expand Down Expand Up @@ -85,3 +88,158 @@ func TestStringHashcode_positiveIndex(t *testing.T) {
}
}
}

func TestAcc_GetRawConfigForKey(t *testing.T) {
tt := acctest.NewTestTools(t)
defer tt.Cleanup()

latestEngineVersion := testAccCheckScalewayRdbEngineGetLatestVersion(tt, postgreSQLEngineName)
instanceUnchangedConfig := fmt.Sprintf(`
name = "test-get-raw-config-for-key"
engine = %q
is_ha_cluster = false
user_name = "my_initial_user"
password = "thiZ_is_v&ry_s3cret"`, latestEngineVersion)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckScalewayRdbInstanceDestroy(tt),
testAccCheckScalewayVPCPrivateNetworkDestroy(tt),
),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource scaleway_vpc_private_network pn {}
resource scaleway_rdb_instance main {%s
node_type = "db-dev-s"
disable_backup = false
volume_type = "lssd"
tags = [ "terraform-test", "core", "get-raw-config" ]
private_network {
pn_id = "${scaleway_vpc_private_network.pn.id}"
enable_ipam = true
}
settings = {
work_mem = "4"
max_connections = "200"
}
}
`, instanceUnchangedConfig),
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayRdbExists(tt, "scaleway_rdb_instance.main"),
assertGetRawConfigResults(t, "is_ha_cluster", false, true, cty.Bool),
assertGetRawConfigResults(t, "disable_backup", false, true, cty.Bool),
assertGetRawConfigResults(t, "volume_type", "lssd", true, cty.String),
assertGetRawConfigResults(t, "volume_size_in_gb", nil, false, cty.Number),
assertGetRawConfigResults(t, "tags.0", "terraform-test", true, cty.String),
assertGetRawConfigResults(t, "tags.1", "core", true, cty.String),
assertGetRawConfigResults(t, "tags.2", "get-raw-config", true, cty.String),
assertGetRawConfigResults(t, "tags.3", nil, false, cty.String),
assertGetRawConfigResults(t, "private_network.0.ip_net", nil, false, cty.String),
assertGetRawConfigResults(t, "private_network.0.enable_ipam", true, true, cty.Bool),
assertGetRawConfigResults(t, "private_network.#.enable_ipam", true, true, cty.Bool),
assertGetRawConfigResults(t, "settings.work_mem", "4", true, cty.String),
assertGetRawConfigResults(t, "settings.max_connections", "200", true, cty.String),
assertGetRawConfigResults(t, "settings.not_in_map", nil, false, cty.String),
),
},
{
Config: fmt.Sprintf(`
resource scaleway_vpc_private_network pn {}
resource scaleway_rdb_instance main {%s
node_type = "db-dev-s"
disable_backup = true
volume_type = "bssd"
volume_size_in_gb = 10
tags = [ "terraform-test", "core", "get-raw-config-for-key" ]
private_network {
pn_id = "${scaleway_vpc_private_network.pn.id}"
ip_net = "172.16.32.1/24"
}
settings = {
work_mem = "2"
max_connections = "100"
}
}
`, instanceUnchangedConfig),
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayRdbExists(tt, "scaleway_rdb_instance.main"),
assertGetRawConfigResults(t, "is_ha_cluster", false, true, cty.Bool),
assertGetRawConfigResults(t, "disable_backup", true, true, cty.Bool),
assertGetRawConfigResults(t, "volume_type", "bssd", true, cty.String),
assertGetRawConfigResults(t, "volume_size_in_gb", 10, true, cty.Number),
assertGetRawConfigResults(t, "tags.0", "terraform-test", true, cty.String),
assertGetRawConfigResults(t, "tags.1", "core", true, cty.String),
assertGetRawConfigResults(t, "tags.2", "get-raw-config-for-key", true, cty.String),
assertGetRawConfigResults(t, "tags.3", nil, false, cty.String),
assertGetRawConfigResults(t, "private_network.0.ip_net", "172.16.32.1/24", true, cty.String),
assertGetRawConfigResults(t, "private_network.0.enable_ipam", false, false, cty.Bool),
assertGetRawConfigResults(t, "private_network.#.enable_ipam", false, false, cty.Bool),
assertGetRawConfigResults(t, "private_network.#.not_in_list", false, false, cty.Bool),
assertGetRawConfigResults(t, "settings.work_mem", "2", true, cty.String),
assertGetRawConfigResults(t, "settings.max_connections", "100", true, cty.String),
assertGetRawConfigResults(t, "settings.not_in_map", nil, false, cty.String),
),
},
{
Config: fmt.Sprintf(`
resource scaleway_vpc_private_network pn {}
resource scaleway_rdb_instance main {%s
node_type = "db-gp-s"
tags = [ "terraform-test", "core", "get-raw-config" ]
private_network {
pn_id = "${scaleway_vpc_private_network.pn.id}"
enable_ipam = true
}
settings = {
work_mem = "4"
max_connections = "200"
}
}
`, instanceUnchangedConfig),
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayRdbExists(tt, "scaleway_rdb_instance.main"),
assertGetRawConfigResults(t, "is_ha_cluster", false, true, cty.Bool),
assertGetRawConfigResults(t, "disable_backup", false, false, cty.Bool),
assertGetRawConfigResults(t, "volume_type", "lssd", true, cty.String),
assertGetRawConfigResults(t, "volume_size_in_gb", nil, false, cty.Number),
assertGetRawConfigResults(t, "tags.0", "terraform-test", true, cty.String),
assertGetRawConfigResults(t, "tags.1", "core", true, cty.String),
assertGetRawConfigResults(t, "tags.2", "get-raw-config", true, cty.String),
assertGetRawConfigResults(t, "tags.3", nil, false, cty.String),
assertGetRawConfigResults(t, "private_network.0.ip_net", nil, false, cty.String),
assertGetRawConfigResults(t, "private_network.0.enable_ipam", true, true, cty.Bool),
assertGetRawConfigResults(t, "private_network.#.enable_ipam", true, true, cty.Bool),
assertGetRawConfigResults(t, "settings.work_mem", "4", true, cty.String),
assertGetRawConfigResults(t, "settings.max_connections", "200", true, cty.String),
assertGetRawConfigResults(t, "settings.not_in_map", nil, false, cty.String),
),
},
},
})
}

func assertGetRawConfigResults(t *testing.T, key string, expectedValue any, expectedSet bool, ty cty.Type) resource.TestCheckFunc {
t.Helper()
return func(s *terraform.State) error {
resourceTFName := "scaleway_rdb_instance.main"
rs, ok := s.RootModule().Resources[resourceTFName]
if !ok {
return fmt.Errorf("resource not found: %s", resourceTFName)
}
if rs.Primary.RawConfig.IsNull() {
return nil
}

actualValue, actualSet := scaleway.GetKeyInRawConfigMap(rs.Primary.RawConfig.AsValueMap(), key, ty)
assert.Equal(t, expectedSet, actualSet)
assert.Equal(t, expectedValue, actualValue)

return nil
}
}
5 changes: 2 additions & 3 deletions scaleway/resource_lb_backend.go
Expand Up @@ -5,6 +5,7 @@ import (
"math"
"time"

"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand Down Expand Up @@ -616,9 +617,7 @@ func resourceScalewayLbBackendUpdate(ctx context.Context, d *schema.ResourceData
updateHCRequest.TCPConfig = expandLbHCTCP(d.Get("health_check_tcp"))
}

rawConfig := d.GetRawConfig().AsValueMap()
healthCheckPortValue, healthCheckPortExists := rawConfig["health_check_port"]
healthCheckPortSetByUser := healthCheckPortExists && !healthCheckPortValue.IsNull()
_, healthCheckPortSetByUser := getRawConfigForKey(d, "health_check_port", cty.Number)
if d.HasChange("forward_port") && !healthCheckPortSetByUser {
updateHCRequest.Port = int32(d.Get("forward_port").(int))
}
Expand Down

0 comments on commit 635420c

Please sign in to comment.