Skip to content

Commit feeea4c

Browse files
Mia-Crossyfodil
authored andcommitted
feat(k8s): read pool's private ips (#3072)
Co-authored-by: Yacine FODIL <[email protected]>
1 parent f728ea2 commit feeea4c

16 files changed

+30992
-23926
lines changed

docs/resources/k8s_pool.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ In addition to all arguments above, the following attributes are exported:
107107
- `status` - The status of the pool.
108108
- `nodes` - (List of) The nodes in the default pool.
109109
- `name` - The name of the node.
110+
- `private_ips` - The list of private IPv4 and IPv6 addresses associated with the node.
111+
- `id` - The ID of the IP address resource.
112+
- `address` - The private IP address.
110113
- `public_ip` - The public IPv4. (Deprecated, Please use the official Kubernetes provider and the kubernetes_nodes data source)
111114
- `public_ip_v6` - The public IPv6. (Deprecated, Please use the official Kubernetes provider and the kubernetes_nodes data source)
112115
- `status` - The status of the node.

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: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func convertNodes(res *k8s.ListNodesResponse) []map[string]interface{} {
8686

8787
for _, node := range res.Nodes {
8888
n := make(map[string]interface{})
89+
n["id"] = node.ID
8990
n["name"] = node.Name
9091
n["status"] = node.Status.String()
9192

@@ -117,3 +118,19 @@ func getNodes(ctx context.Context, k8sAPI *k8s.API, pool *k8s.Pool) ([]map[strin
117118

118119
return convertNodes(nodes), nil
119120
}
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+
135+
return cluster.ProjectID, nil
136+
}

internal/services/k8s/pool.go

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
1414
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
1515
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/zonal"
16+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/ipam"
1617
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
1718
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
1819
)
@@ -192,6 +193,11 @@ func ResourcePool() *schema.Resource {
192193
Computed: true,
193194
Elem: &schema.Resource{
194195
Schema: map[string]*schema.Schema{
196+
"id": {
197+
Type: schema.TypeString,
198+
Computed: true,
199+
Description: "The ID of the node",
200+
},
195201
"name": {
196202
Type: schema.TypeString,
197203
Computed: true,
@@ -214,6 +220,25 @@ func ResourcePool() *schema.Resource {
214220
Description: "The public IPv6 address of the node",
215221
Deprecated: "Please use the official Kubernetes provider and the kubernetes_nodes data source",
216222
},
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+
},
217242
},
218243
},
219244
},
@@ -389,7 +414,6 @@ func ResourceK8SPoolRead(ctx context.Context, d *schema.ResourceData, m interfac
389414
_ = d.Set("container_runtime", pool.ContainerRuntime)
390415
_ = d.Set("created_at", pool.CreatedAt.Format(time.RFC3339))
391416
_ = d.Set("updated_at", pool.UpdatedAt.Format(time.RFC3339))
392-
_ = d.Set("nodes", nodes)
393417
_ = d.Set("status", pool.Status)
394418
_ = d.Set("kubelet_args", flattenKubeletArgs(pool.KubeletArgs))
395419
_ = d.Set("region", region)
@@ -401,7 +425,59 @@ func ResourceK8SPoolRead(ctx context.Context, d *schema.ResourceData, m interfac
401425
_ = d.Set("placement_group_id", zonal.NewID(pool.Zone, *pool.PlacementGroupID).String())
402426
}
403427

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

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

internal/services/k8s/pool_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +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", "nodes.0.private_ips.0.id"),
54+
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.default", "nodes.0.private_ips.0.address"),
5355
),
5456
},
5557
{
@@ -69,6 +71,10 @@ func TestAccPool_Basic(t *testing.T) {
6971
resource.TestCheckResourceAttrSet("scaleway_k8s_pool.minimal", "nodes.0.public_ip"), // Deprecated attributes
7072
testAccCheckK8SPoolServersAreInPrivateNetwork(tt, "scaleway_k8s_cluster.minimal", "scaleway_k8s_pool.default", "scaleway_vpc_private_network.minimal"),
7173
testAccCheckK8SPoolServersAreInPrivateNetwork(tt, "scaleway_k8s_cluster.minimal", "scaleway_k8s_pool.minimal", "scaleway_vpc_private_network.minimal"),
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"),
7278
),
7379
},
7480
{

0 commit comments

Comments
 (0)