Skip to content

Commit 92c3c50

Browse files
authored
read replica endpoints management (#2371)
1 parent 1e8735c commit 92c3c50

8 files changed

+5501
-1544
lines changed

scaleway/helpers_rdb.go

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ func expandReadReplicaEndpointsSpecDirectAccess(data interface{}) *rdb.ReadRepli
220220
}
221221

222222
// expandReadReplicaEndpointsSpecPrivateNetwork expand read-replica private network endpoints from schema to specs
223-
func expandReadReplicaEndpointsSpecPrivateNetwork(data interface{}) (*rdb.ReadReplicaEndpointSpec, error) {
223+
func expandReadReplicaEndpointsSpecPrivateNetwork(data interface{}, enableIpam bool) (*rdb.ReadReplicaEndpointSpec, error) {
224224
if data == nil || len(data.([]interface{})) == 0 {
225225
return nil, nil
226226
}
@@ -230,26 +230,28 @@ func expandReadReplicaEndpointsSpecPrivateNetwork(data interface{}) (*rdb.ReadRe
230230
rawEndpoint := data.(map[string]interface{})
231231

232232
endpoint := new(rdb.ReadReplicaEndpointSpec)
233-
234-
serviceIP := rawEndpoint["service_ip"].(string)
235233
endpoint.PrivateNetwork = &rdb.ReadReplicaEndpointSpecPrivateNetwork{
236234
PrivateNetworkID: expandID(rawEndpoint["private_network_id"]),
237235
}
238-
if len(serviceIP) > 0 {
239-
ipNet, err := expandIPNet(serviceIP)
240-
if err != nil {
241-
return nil, fmt.Errorf("failed to parse private_network service_ip (%s): %w", rawEndpoint["service_ip"], err)
242-
}
243-
endpoint.PrivateNetwork.ServiceIP = &ipNet
244-
} else {
236+
237+
if enableIpam {
245238
endpoint.PrivateNetwork.IpamConfig = &rdb.ReadReplicaEndpointSpecPrivateNetworkIpamConfig{}
239+
} else {
240+
serviceIP := rawEndpoint["service_ip"].(string)
241+
if len(serviceIP) > 0 {
242+
ipNet, err := expandIPNet(serviceIP)
243+
if err != nil {
244+
return nil, fmt.Errorf("failed to parse private_network service_ip (%s): %w", rawEndpoint["service_ip"], err)
245+
}
246+
endpoint.PrivateNetwork.ServiceIP = &ipNet
247+
}
246248
}
247249

248250
return endpoint, nil
249251
}
250252

251253
// flattenReadReplicaEndpoints flatten read-replica endpoints to directAccess and privateNetwork
252-
func flattenReadReplicaEndpoints(endpoints []*rdb.Endpoint) (directAccess, privateNetwork interface{}) {
254+
func flattenReadReplicaEndpoints(endpoints []*rdb.Endpoint, enableIpam bool) (directAccess, privateNetwork interface{}) {
253255
for _, endpoint := range endpoints {
254256
rawEndpoint := map[string]interface{}{
255257
"endpoint_id": endpoint.ID,
@@ -270,6 +272,7 @@ func flattenReadReplicaEndpoints(endpoints []*rdb.Endpoint) (directAccess, priva
270272
rawEndpoint["private_network_id"] = pnRegionalID
271273
rawEndpoint["service_ip"] = endpoint.PrivateNetwork.ServiceIP.String()
272274
rawEndpoint["zone"] = endpoint.PrivateNetwork.Zone
275+
rawEndpoint["enable_ipam"] = enableIpam
273276
privateNetwork = rawEndpoint
274277
}
275278
}
@@ -322,15 +325,23 @@ func rdbPrivilegeUpgradeV1SchemaType() cty.Type {
322325
})
323326
}
324327

325-
func isIpamEndpoint(instance *rdb.Instance, meta interface{}) (bool, error) {
328+
func isIpamEndpoint(resource interface{}, meta interface{}) (bool, error) {
326329
ipamAPI := ipam.NewAPI(meta.(*Meta).scwClient)
327-
328-
ips, err := ipamAPI.ListIPs(&ipam.ListIPsRequest{
329-
Region: instance.Region,
330-
ResourceID: &instance.ID,
330+
request := &ipam.ListIPsRequest{
331331
ResourceType: "rdb_instance",
332332
IsIPv6: scw.BoolPtr(false),
333-
}, scw.WithAllPages())
333+
}
334+
335+
switch res := resource.(type) {
336+
case *rdb.Instance:
337+
request.Region = res.Region
338+
request.ResourceID = &res.ID
339+
case *rdb.ReadReplica:
340+
request.Region = res.Region
341+
request.ResourceID = &res.InstanceID
342+
}
343+
344+
ips, err := ipamAPI.ListIPs(request, scw.WithAllPages())
334345
if err != nil {
335346
return false, fmt.Errorf("could not list IPs: %w", err)
336347
}

scaleway/resource_rdb_read_replica.go

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66

7-
"github.com/hashicorp/terraform-plugin-log/tflog"
87
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
98
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
109
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -99,8 +98,15 @@ func resourceScalewayRdbReadReplica() *schema.Resource {
9998
Description: "The IP network address within the private subnet",
10099
Optional: true,
101100
Computed: true,
101+
AtLeastOneOf: []string{"private_network.0.enable_ipam"},
102102
ValidateFunc: validation.IsCIDR,
103103
},
104+
"enable_ipam": {
105+
Type: schema.TypeBool,
106+
Optional: true,
107+
AtLeastOneOf: []string{"private_network.0.service_ip"},
108+
Description: "Whether or not the private network endpoint should be configured with IPAM",
109+
},
104110
"zone": {
105111
Type: schema.TypeString,
106112
Description: "Private network zone",
@@ -152,7 +158,11 @@ func resourceScalewayRdbReadReplicaCreate(ctx context.Context, d *schema.Resourc
152158
if directAccess := expandReadReplicaEndpointsSpecDirectAccess(d.Get("direct_access")); directAccess != nil {
153159
endpointSpecs = append(endpointSpecs, directAccess)
154160
}
155-
if pn, err := expandReadReplicaEndpointsSpecPrivateNetwork(d.Get("private_network")); err != nil || pn != nil {
161+
enableIpam := true
162+
if _, ipSet := d.GetOk("private_network.0.service_ip"); ipSet {
163+
enableIpam = false
164+
}
165+
if pn, err := expandReadReplicaEndpointsSpecPrivateNetwork(d.Get("private_network"), enableIpam); err != nil || pn != nil {
156166
if err != nil {
157167
return diag.FromErr(err)
158168
}
@@ -194,7 +204,11 @@ func resourceScalewayRdbReadReplicaRead(ctx context.Context, d *schema.ResourceD
194204
return diag.FromErr(err)
195205
}
196206

197-
directAccess, privateNetwork := flattenReadReplicaEndpoints(rr.Endpoints)
207+
enableIpam, err := isIpamEndpoint(rr, meta)
208+
if err != nil {
209+
return diag.FromErr(err)
210+
}
211+
directAccess, privateNetwork := flattenReadReplicaEndpoints(rr.Endpoints, enableIpam)
198212
_ = d.Set("direct_access", directAccess)
199213
_ = d.Set("private_network", privateNetwork)
200214

@@ -214,7 +228,7 @@ func resourceScalewayRdbReadReplicaUpdate(ctx context.Context, d *schema.Resourc
214228
}
215229

216230
// verify resource is ready
217-
_, err = waitForRDBReadReplica(ctx, rdbAPI, region, ID, d.Timeout(schema.TimeoutRead))
231+
rr, err := waitForRDBReadReplica(ctx, rdbAPI, region, ID, d.Timeout(schema.TimeoutRead))
218232
if err != nil {
219233
if is404Error(err) {
220234
d.SetId("")
@@ -226,35 +240,59 @@ func resourceScalewayRdbReadReplicaUpdate(ctx context.Context, d *schema.Resourc
226240
newEndpoints := []*rdb.ReadReplicaEndpointSpec(nil)
227241

228242
if d.HasChange("direct_access") {
229-
_, directAccessExists := d.GetOk("direct_access")
230-
tflog.Debug(ctx, "direct_access", map[string]interface{}{
231-
"exists": directAccessExists,
232-
})
233-
if !directAccessExists {
234-
err := rdbAPI.DeleteEndpoint(&rdb.DeleteEndpointRequest{
235-
Region: region,
236-
EndpointID: expandID(d.Get("direct_access.0.endpoint_id")),
237-
}, scw.WithContext(ctx))
238-
if err != nil {
239-
return diag.FromErr(err)
243+
// delete old endpoint
244+
for _, e := range rr.Endpoints {
245+
if e.DirectAccess != nil {
246+
err := rdbAPI.DeleteEndpoint(&rdb.DeleteEndpointRequest{
247+
Region: region,
248+
EndpointID: e.ID,
249+
}, scw.WithContext(ctx))
250+
if err != nil {
251+
return diag.FromErr(err)
252+
}
240253
}
241-
} else {
242-
newEndpoints = append(newEndpoints, expandReadReplicaEndpointsSpecDirectAccess(d.Get("direct_access")))
254+
}
255+
// retrieve state
256+
rr, err = waitForRDBReadReplica(ctx, rdbAPI, region, ID, d.Timeout(schema.TimeoutRead))
257+
if err != nil {
258+
return diag.FromErr(err)
259+
}
260+
// create a new one if defined
261+
if directAccess, directAccessExists := d.GetOk("direct_access"); directAccessExists {
262+
newEndpoints = append(newEndpoints, expandReadReplicaEndpointsSpecDirectAccess(directAccess))
243263
}
244264
}
245265

246266
if d.HasChange("private_network") {
247-
_, privateNetworkExists := d.GetOk("private_network")
248-
if !privateNetworkExists {
249-
err := rdbAPI.DeleteEndpoint(&rdb.DeleteEndpointRequest{
250-
Region: region,
251-
EndpointID: expandID(d.Get("private_network.0.endpoint_id")),
252-
}, scw.WithContext(ctx))
253-
if err != nil {
254-
return diag.FromErr(err)
267+
// delete old endpoint
268+
for _, e := range rr.Endpoints {
269+
if e.PrivateNetwork != nil {
270+
err := rdbAPI.DeleteEndpoint(&rdb.DeleteEndpointRequest{
271+
Region: region,
272+
EndpointID: e.ID,
273+
}, scw.WithContext(ctx))
274+
if err != nil {
275+
return diag.FromErr(err)
276+
}
277+
}
278+
}
279+
// retrieve state
280+
_, err = waitForRDBReadReplica(ctx, rdbAPI, region, ID, d.Timeout(schema.TimeoutRead))
281+
if err != nil {
282+
return diag.FromErr(err)
283+
}
284+
// create a new one if defined
285+
if pn, pnExists := d.GetOk("private_network"); pnExists {
286+
// "enable_ipam" is not readable from the API, so we just read the user's config
287+
enableIpam := true
288+
if rawConfig := d.GetRawConfig(); !rawConfig.IsNull() {
289+
pnRawConfig := rawConfig.AsValueMap()["private_network"].AsValueSlice()[0].AsValueMap()
290+
if !pnRawConfig["enable_ipam"].IsNull() && pnRawConfig["enable_ipam"].False() ||
291+
!pnRawConfig["service_ip"].IsNull() {
292+
enableIpam = false
293+
}
255294
}
256-
} else {
257-
pnEndpoint, err := expandReadReplicaEndpointsSpecPrivateNetwork(d.Get("private_network"))
295+
pnEndpoint, err := expandReadReplicaEndpointsSpecPrivateNetwork(pn, enableIpam)
258296
if err != nil {
259297
return diag.FromErr(err)
260298
}

0 commit comments

Comments
 (0)