Skip to content

Commit b898094

Browse files
authored
Merge pull request #368 from kbst/scaleway-nat
Default Scaleway to private nodes and NAT
2 parents da92976 + eb57e22 commit b898094

7 files changed

Lines changed: 65 additions & 10 deletions

File tree

DEVIATIONS.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ When a divergence is resolved, remove its entry from this file.
5757
5858
> **Divergence — `google/cluster`:** Required File Layout — `provider.tf` contains the data source `data.google_client_config.default`, which is used by both `kubeconfig.tf` and the kubernetes provider alias block in the same file; per the data source placement rule, a data source used by multiple resources belongs in `data_sources.tf`, not co-located with a provider block. Planned resolution: move the data source to `data_sources.tf`.
5959
60-
> **Divergence — `scaleway/cluster`:** Networking — nodes are assigned public IPs by default (`public_ip_disabled` defaults to `false` in `node_pool.tf`), violating the rule that nodes MUST be configured with private IPs only by default. Planned resolution: flip the default to `true` and add any additional resources that need to be created; if these additioanl resources are only required in one or the other case make them conditional.
61-
6260
> **Divergence — `scaleway/cluster/node-pool`:** Tagging and Labelling — the node-pool module exposes no configuration attribute for Kubernetes node labels; node labels can only be approximated by encoding them as Scaleway tag strings, with no guarantee that they are applied to the Kubernetes node object's `.metadata.labels`, violating the rule that node labels MUST be applied to both the cloud API and the Kubernetes node object. Planned resolution: add a dedicated node label attribute once the Scaleway provider exposes first-class Kubernetes node label support.
6361
6462
> **Divergence — `scaleway/cluster`:** Cluster API Authentication — `scaleway_k8s_cluster.current.kubeconfig[0].token` is a long-lived static admin token issued by the cluster resource; Scaleway does not expose a control to disable static credential issuance on the cluster resource, so this cannot currently be resolved, but it diverges from the rule that static long-lived credentials must be disabled where the provider exposes such a control. Planned resolution: revisit when the Scaleway provider exposes a control to disable static token issuance.

scaleway/cluster/network.tf

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,49 @@ resource "scaleway_vpc_private_network" "current" {
2222
}
2323
}
2424
}
25+
26+
# When nodes have public IPs disabled (the default), a Public Gateway is
27+
# required in each node zone so that private nodes can reach the internet
28+
# through NAT (masquerade). One gateway per zone is created conditionally.
29+
locals {
30+
# The set of zones that need a gateway: non-empty only when public IPs are
31+
# disabled on the default node pool. Extra node pools share the same
32+
# private network and therefore benefit from the same gateways automatically.
33+
gateway_zones = try(coalesce(local.cfg.default_node_pool.public_ip_disabled, null), true) ? toset(coalesce(local.cfg.default_node_pool.zones, [])) : toset([])
34+
}
35+
36+
resource "scaleway_vpc_public_gateway_ip" "current" {
37+
for_each = local.gateway_zones
38+
39+
project_id = local.cfg.project_id
40+
zone = each.value
41+
42+
tags = concat(module.cluster_metadata.tags, try(coalesce(local.cfg.extra_tags, null), []))
43+
}
44+
45+
resource "scaleway_vpc_public_gateway" "current" {
46+
for_each = local.gateway_zones
47+
48+
name = "${module.cluster_metadata.name}-${each.value}"
49+
project_id = local.cfg.project_id
50+
zone = each.value
51+
52+
type = try(coalesce(local.cfg.public_gateway_type, null), "VPC-GW-S")
53+
ip_id = scaleway_vpc_public_gateway_ip.current[each.value].id
54+
55+
tags = concat(module.cluster_metadata.tags, try(coalesce(local.cfg.extra_tags, null), []))
56+
}
57+
58+
resource "scaleway_vpc_gateway_network" "current" {
59+
for_each = local.gateway_zones
60+
61+
gateway_id = scaleway_vpc_public_gateway.current[each.value].id
62+
private_network_id = scaleway_vpc_private_network.current.id
63+
zone = each.value
64+
65+
enable_masquerade = true
66+
67+
ipam_config {
68+
push_default_route = true
69+
}
70+
}

scaleway/cluster/node-pool/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ resource "scaleway_k8s_pool" "current" {
3939
root_volume_type = local.cfg.root_volume_type
4040
root_volume_size_in_gb = local.cfg.root_volume_size_in_gb
4141

42-
public_ip_disabled = try(coalesce(local.cfg.public_ip_disabled, null), false)
42+
public_ip_disabled = try(coalesce(local.cfg.public_ip_disabled, null), true)
4343

4444
wait_for_pool_ready = try(coalesce(local.cfg.wait_for_pool_ready, null), true)
4545

scaleway/cluster/node_pool.tf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module "node_pool" {
2020
root_volume_type = try(local.cfg.default_node_pool.root_volume_type, null)
2121
root_volume_size_in_gb = try(local.cfg.default_node_pool.root_volume_size_in_gb, null)
2222

23-
public_ip_disabled = try(coalesce(local.cfg.default_node_pool.public_ip_disabled, null), false)
23+
public_ip_disabled = try(coalesce(local.cfg.default_node_pool.public_ip_disabled, null), true)
2424

2525
upgrade_policy = try(local.cfg.default_node_pool.upgrade_policy, null)
2626

@@ -31,4 +31,10 @@ module "node_pool" {
3131
}
3232
}
3333
configuration_base_key = terraform.workspace
34+
35+
# Scaleway rejects public_ip_disabled = true on a pool unless a Public
36+
# Gateway is already attached to the private network and advertising a
37+
# default route. Wait for all gateway networks to be fully operational
38+
# before creating or updating any node pool.
39+
depends_on = [scaleway_vpc_gateway_network.current]
3440
}

scaleway/cluster/variables.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ variable "configuration" {
4646
# Private network
4747
private_network_subnet = optional(string)
4848

49+
# Public Gateway — only provisioned when public_ip_disabled is true (the default).
50+
# Sets the gateway type for all per-zone public gateways created for NAT egress.
51+
# Run `scw vpc public-gateway list-types` to see available types.
52+
public_gateway_type = optional(string)
53+
4954
# Extra tags added to all cloud resources in addition to the metadata tags
5055
extra_tags = optional(list(string))
5156

tests/scw_zero_cluster.tf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ module "scw_zero" {
77
name_prefix = "kbstacctest"
88
base_domain = "infra.serverwolken.de"
99

10-
region = "fr-par"
10+
region = "nl-ams"
1111

12-
cluster_version = "1.32"
12+
cluster_version = "1.34"
1313
cni = "cilium"
1414

1515
delete_additional_resources = false
1616

1717
default_node_pool = {
18-
node_type = "GP1-XS"
19-
zones = ["fr-par-1", "fr-par-2", "fr-par-3"]
18+
node_type = "PLAY2-MICRO"
19+
zones = ["nl-ams-1", "nl-ams-2", "nl-ams-3"]
2020
size = 1
2121
min_size = 1
2222
max_size = 2

tests/scw_zero_node_pools.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ module "scw_zero_node_pool" {
99
apps = {
1010
name = "test1"
1111

12-
node_type = "GP1-XS"
13-
zones = ["fr-par-1", "fr-par-2", "fr-par-3"]
12+
node_type = "PLAY2-MICRO"
13+
zones = ["nl-ams-1", "nl-ams-2", "nl-ams-3"]
1414
size = 1
1515
min_size = 1
1616
max_size = 2

0 commit comments

Comments
 (0)