Skip to content

Commit 6dc2ff3

Browse files
committed
move private_ips as a node attribute + handle 403
1 parent f2ebe6d commit 6dc2ff3

File tree

5 files changed

+96
-55
lines changed

5 files changed

+96
-55
lines changed

docs/resources/k8s_pool.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ In addition to all arguments above, the following attributes are exported:
110110
- `public_ip` - The public IPv4. (Deprecated, Please use the official Kubernetes provider and the kubernetes_nodes data source)
111111
- `public_ip_v6` - The public IPv6. (Deprecated, Please use the official Kubernetes provider and the kubernetes_nodes data source)
112112
- `status` - The status of the node.
113+
- `private_ips` - The list of private IPv4 and IPv6 addresses associated with the node.
114+
- `id` - The ID of the IP address resource.
115+
- `address` - The private IP address.
113116
- `created_at` - The creation date of the pool.
114117
- `updated_at` - The last update date of the pool.
115118
- `version` - The version of the pool.
116119
- `current_size` - The size of the pool at the time the terraform state was updated.
117-
- `private_ips` - The list of private IPv4 addresses associated with the resource.
118-
- `id` - The ID of the IPv4 address resource.
119-
- `address` - The private IPv4 address.
120120

121121
## Zone
122122

internal/services/ipam/helpers.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type GetResourcePrivateIPsOptions struct {
7474
ResourceID *string
7575
ResourceName *string
7676
PrivateNetworkID *string
77+
ProjectID *string
7778
}
7879

7980
// GetResourcePrivateIPs fetches the private IP addresses of a resource in a private network.
@@ -100,6 +101,11 @@ func GetResourcePrivateIPs(ctx context.Context, m interface{}, region scw.Region
100101
if opts.ResourceType != nil {
101102
req.ResourceType = *opts.ResourceType
102103
}
104+
105+
// Project ID needs to be specified in order to force the IPAM API to check IAM permissions and send a 403 response code if not authorized
106+
if opts.ProjectID != nil {
107+
req.ProjectID = opts.ProjectID
108+
}
103109
}
104110

105111
resp, err := ipamAPI.ListIPs(req, scw.WithContext(ctx))

internal/services/k8s/helpers_k8s.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,18 @@ func getNodes(ctx context.Context, k8sAPI *k8s.API, pool *k8s.Pool) ([]map[strin
118118

119119
return convertNodes(nodes), nil
120120
}
121+
122+
func getClusterProjectID(ctx context.Context, k8sAPI *k8s.API, pool *k8s.Pool) (string, error) {
123+
cluster, err := k8sAPI.GetCluster(&k8s.GetClusterRequest{
124+
Region: pool.Region,
125+
ClusterID: pool.ClusterID,
126+
}, scw.WithContext(ctx))
127+
if err != nil {
128+
return "", fmt.Errorf("get pool project ID: error getting cluster %s", pool.ClusterID)
129+
}
130+
131+
if cluster.ProjectID == "" {
132+
return "", fmt.Errorf("no project ID found for cluster %s", pool.ClusterID)
133+
}
134+
return cluster.ProjectID, nil
135+
}

internal/services/k8s/pool.go

Lines changed: 66 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,25 @@ func ResourcePool() *schema.Resource {
220220
Description: "The public IPv6 address of the node",
221221
Deprecated: "Please use the official Kubernetes provider and the kubernetes_nodes data source",
222222
},
223+
"private_ips": {
224+
Type: schema.TypeList,
225+
Computed: true,
226+
Description: "List of private IPv4 and IPv6 addresses associated with the node",
227+
Elem: &schema.Resource{
228+
Schema: map[string]*schema.Schema{
229+
"id": {
230+
Type: schema.TypeString,
231+
Computed: true,
232+
Description: "The ID of the IP address resource",
233+
},
234+
"address": {
235+
Type: schema.TypeString,
236+
Computed: true,
237+
Description: "The private IP address",
238+
},
239+
},
240+
},
241+
},
223242
},
224243
},
225244
},
@@ -228,25 +247,6 @@ func ResourcePool() *schema.Resource {
228247
Computed: true,
229248
Description: "The status of the pool",
230249
},
231-
"private_ips": {
232-
Type: schema.TypeList,
233-
Computed: true,
234-
Description: "List of private IPv4 addresses associated with the resource",
235-
Elem: &schema.Resource{
236-
Schema: map[string]*schema.Schema{
237-
"id": {
238-
Type: schema.TypeString,
239-
Computed: true,
240-
Description: "The ID of the IPv4 address resource",
241-
},
242-
"address": {
243-
Type: schema.TypeString,
244-
Computed: true,
245-
Description: "The private IPv4 address",
246-
},
247-
},
248-
},
249-
},
250250
},
251251
}
252252
}
@@ -414,7 +414,6 @@ func ResourceK8SPoolRead(ctx context.Context, d *schema.ResourceData, m interfac
414414
_ = d.Set("container_runtime", pool.ContainerRuntime)
415415
_ = d.Set("created_at", pool.CreatedAt.Format(time.RFC3339))
416416
_ = d.Set("updated_at", pool.UpdatedAt.Format(time.RFC3339))
417-
_ = d.Set("nodes", nodes)
418417
_ = d.Set("status", pool.Status)
419418
_ = d.Set("kubelet_args", flattenKubeletArgs(pool.KubeletArgs))
420419
_ = d.Set("region", region)
@@ -426,36 +425,57 @@ func ResourceK8SPoolRead(ctx context.Context, d *schema.ResourceData, m interfac
426425
_ = d.Set("placement_group_id", zonal.NewID(pool.Zone, *pool.PlacementGroupID).String())
427426
}
428427

429-
var allPrivateIPs []map[string]interface{}
430-
431-
for _, nodeMap := range nodes {
432-
nodeNameInterface, ok := nodeMap["name"]
433-
if !ok {
434-
continue
435-
}
436-
437-
nodeName, ok := nodeNameInterface.(string)
438-
if !ok {
439-
continue
440-
}
441-
442-
opts := &ipam.GetResourcePrivateIPsOptions{
443-
ResourceName: &nodeName,
444-
}
445-
446-
privateIPs, err := ipam.GetResourcePrivateIPs(ctx, m, region, opts)
447-
if err != nil {
448-
return diag.FromErr(err)
449-
}
450-
451-
if privateIPs != nil {
452-
allPrivateIPs = append(allPrivateIPs, privateIPs...)
428+
// Get nodes' private IPs
429+
diags := diag.Diagnostics{}
430+
projectID, err := getClusterProjectID(ctx, k8sAPI, pool)
431+
if err != nil {
432+
diags = append(diags, diag.Diagnostic{
433+
Severity: diag.Warning,
434+
Summary: "Unable to get nodes private IPs",
435+
Detail: err.Error(),
436+
})
437+
} else {
438+
for i, nodeMap := range nodes {
439+
nodeNameInterface, ok := nodeMap["name"]
440+
if !ok {
441+
continue
442+
}
443+
444+
nodeName, ok := nodeNameInterface.(string)
445+
if !ok {
446+
continue
447+
}
448+
449+
opts := &ipam.GetResourcePrivateIPsOptions{
450+
ResourceName: &nodeName,
451+
ProjectID: &projectID,
452+
}
453+
454+
privateIPs, err := ipam.GetResourcePrivateIPs(ctx, m, region, opts)
455+
if err != nil {
456+
if httperrors.Is403(err) {
457+
diags = append(diags, diag.Diagnostic{
458+
Severity: diag.Warning,
459+
Summary: "Unauthorized to read nodes' private IPs, please check your IAM permissions",
460+
Detail: err.Error(),
461+
})
462+
break
463+
} else {
464+
diags = append(diags, diag.Diagnostic{
465+
Severity: diag.Warning,
466+
Summary: "Unable to get nodes private IPs from IPAM API",
467+
Detail: err.Error(),
468+
})
469+
}
470+
}
471+
472+
nodes[i]["private_ips"] = privateIPs
453473
}
454474
}
455475

456-
_ = d.Set("private_ips", allPrivateIPs)
476+
_ = d.Set("nodes", nodes)
457477

458-
return nil
478+
return diags
459479
}
460480

461481
func ResourceK8SPoolUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {

internal/services/k8s/pool_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ func TestAccPool_Basic(t *testing.T) {
5050
resource.TestCheckResourceAttr("scaleway_k8s_pool.default", "tags.1", "scaleway_k8s_cluster"),
5151
resource.TestCheckResourceAttr("scaleway_k8s_pool.default", "tags.2", "default"),
5252
testAccCheckK8SPoolServersAreInPrivateNetwork(tt, "scaleway_k8s_cluster.minimal", "scaleway_k8s_pool.default", "scaleway_vpc_private_network.minimal"),
53-
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "private_ips.0.id"),
54-
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "private_ips.0.address"),
53+
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "nodes.0.private_ips.0.id"),
54+
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "nodes.0.private_ips.0.address"),
5555
),
5656
},
5757
{
@@ -71,10 +71,10 @@ func TestAccPool_Basic(t *testing.T) {
7171
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.minimal", "nodes.0.public_ip"), // Deprecated attributes
7272
testAccCheckK8SPoolServersAreInPrivateNetwork(tt, "scaleway_k8s_cluster.minimal", "scaleway_k8s_pool.default", "scaleway_vpc_private_network.minimal"),
7373
testAccCheckK8SPoolServersAreInPrivateNetwork(tt, "scaleway_k8s_cluster.minimal", "scaleway_k8s_pool.minimal", "scaleway_vpc_private_network.minimal"),
74-
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "private_ips.0.id"),
75-
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "private_ips.0.address"),
76-
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "private_ips.1.id"),
77-
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "private_ips.1.address"),
74+
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "nodes.0.private_ips.0.id"),
75+
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "nodes.0.private_ips.0.address"),
76+
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "nodes.0.private_ips.1.id"),
77+
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "nodes.0.private_ips.1.address"),
7878
),
7979
},
8080
{

0 commit comments

Comments
 (0)