Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/data-sources/lke_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ In addition to all arguments above, the following attributes are exported:

* `label` - The label of the Node Pool.

* `firewall_id` - The ID of the firewall associated with the Node Pool.

* `type` - The linode type for all of the nodes in the Node Pool. See all node types [here](https://api.linode.com/v4/linode/types).

* `count` - The number of nodes in the Node Pool.
Expand Down
20 changes: 20 additions & 0 deletions docs/resources/lke_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,24 @@ resource "linode_lke_cluster" "my-cluster" {
}
```

Creating an LKE cluster with existing firewall on a node pool:

```terraform
resource "linode_lke_cluster" "my-cluster" {
label = "my-cluster"
k8s_version = "1.32"
region = "us-central"
tags = ["prod"]

pool {
type = "g6-standard-2"
count = 2
label = "db-pool"
firewall_id = 12345 # id of the firewall to associate with this node pool
}
}
```

Creating an LKE cluster with node pool labels:

```terraform
Expand Down Expand Up @@ -187,6 +205,8 @@ The following arguments are supported in the `pool` specification block:

* `label` - (Optional) A label for the Node Pool. If not provided, it defaults to empty string.

* `firewall_id` - (Optional) The ID of the firewall to associate with this node pool. If not provided, default firewall will be associated.

* `labels` - (Optional) A map of key/value pairs to apply to all nodes in the pool. Labels are used to identify and organize Kubernetes resources within your cluster.

* `tags` - (Optional) A set of tags applied to this node pool. Tags can be used to flag node pools as externally managed. See [Externally Managed Node Pools](#externally-managed-node-pools) for more details.
Expand Down
13 changes: 13 additions & 0 deletions docs/resources/lke_node_pool.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ resource "linode_lke_node_pool" "my-pool" {
}
```

Creating a basic LKE Node with firewall:

```terraform
resource "linode_lke_node_pool" "my-pool" {
cluster_id = 150003
type = "g6-standard-2"
firewall_id = 12345
node_count = 3
}
```

Creating an LKE Node Pool with autoscaler:

```terraform
Expand Down Expand Up @@ -104,6 +115,8 @@ The following arguments are supported:

* `label` - (Optional) A label for the Node Pool. If not provided, it defaults to empty string.

* `firewall_id` - (Optional) The ID of the firewall to associate with this node pool. If not provided, default firewall will be associated.

* `labels` - (Optional) A map attribute containing key-value pairs to be added as labels to nodes in the node pool. Labels help classify your nodes and to easily select subsets of objects. To learn more, review [Add Labels and Taints to your LKE Node Pools](https://www.linode.com/docs/products/compute/kubernetes/guides/deploy-and-manage-cluster-with-the-linode-api/#add-labels-and-taints-to-your-lke-node-pools).

* `k8s_version` - (Optional) The k8s version of the nodes in this node pool. For LKE enterprise only and may not currently available to all users even under v4beta.
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ go 1.24.0

toolchain go1.24.1

replace github.com/linode/linodego => ../linodego/
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local module replacement should not be committed to the repository. This replace directive points to a local filesystem path that won't exist in other development environments or CI/CD pipelines. Remove this line before merging.

Suggested change
replace github.com/linode/linodego => ../linodego/

Copilot uses AI. Check for mistakes.

require (
github.com/aws/aws-sdk-go-v2 v1.39.5
github.com/aws/aws-sdk-go-v2/config v1.31.6
Expand Down Expand Up @@ -102,7 +104,7 @@ require (
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/zclconf/go-cty v1.17.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/oauth2 v0.32.0 // indirect
golang.org/x/oauth2 v0.33.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/term v0.36.0 // indirect
golang.org/x/text v0.30.0 // indirect
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/linode/linodego v1.61.0 h1:9g20NWl+/SbhDFj6X5EOZXtM2hBm1Mx8I9h8+F3l1LM=
github.com/linode/linodego v1.61.0/go.mod h1:64o30geLNwR0NeYh5HM/WrVCBXcSqkKnRK3x9xoRuJI=
github.com/linode/linodego/k8s v1.25.2 h1:PY6S0sAD3xANVvM9WY38bz9GqMTjIbytC8IJJ9Cv23o=
github.com/linode/linodego/k8s v1.25.2/go.mod h1:DC1XCSRZRGsmaa/ggpDPSDUmOM6aK1bhSIP6+f9Cwhc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
Expand Down Expand Up @@ -303,8 +301,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
25 changes: 25 additions & 0 deletions linode/lke/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type NodePoolSpec struct {
K8sVersion *string
UpdateStrategy *string
Label *string
FirewallID *int
}

type NodePoolUpdates struct {
Expand Down Expand Up @@ -65,6 +66,10 @@ func ReconcileLKENodePoolSpecs(
createOpts.Label = spec.Label
}

if spec.FirewallID != nil && *spec.FirewallID != 0 {
createOpts.FirewallID = spec.FirewallID
}

if spec.Taints != nil {
createOpts.Taints = expandNodePoolTaints(spec.Taints)
}
Expand Down Expand Up @@ -147,6 +152,13 @@ func ReconcileLKENodePoolSpecs(
updateOpts.Label = &empty
}

if (newSpec.FirewallID != nil && oldSpec.FirewallID != nil && *newSpec.FirewallID != *oldSpec.FirewallID) ||
(newSpec.FirewallID != nil && oldSpec.FirewallID == nil) {
updateOpts.FirewallID = newSpec.FirewallID
} else if newSpec.FirewallID == nil && oldSpec.FirewallID != nil {
updateOpts.FirewallID = nil
}

if enterprise {
if newSpec.K8sVersion != nil && oldSpec.K8sVersion != nil &&
*newSpec.K8sVersion != *oldSpec.K8sVersion {
Expand Down Expand Up @@ -432,6 +444,12 @@ func matchPoolsWithSchema(ctx context.Context, pools []linodego.LKENodePool, dec
}
}

if declaredFirewallID, ok := declaredPool["firewall_id"].(int); ok && declaredFirewallID != 0 {
if apiPool.FirewallID == nil || declaredFirewallID != *apiPool.FirewallID {
continue
}
}

// Pair the API pool with the declared pool
result[i] = apiPool
delete(apiPools, apiPool.ID)
Expand Down Expand Up @@ -496,9 +514,15 @@ func expandLinodeLKENodePoolSpecs(pool []interface{}, preserveNoTarget bool) (po
labelPtr = &v
}

var firewallIdPtr *int
if v, ok := specMap["firewall_id"].(int); ok && v != 0 {
firewallIdPtr = &v
}

poolSpecs = append(poolSpecs, NodePoolSpec{
ID: specMap["id"].(int),
Label: labelPtr,
FirewallID: firewallIdPtr,
Type: specMap["type"].(string),
Tags: helper.ExpandStringSet(specMap["tags"].(*schema.Set)),
Taints: helper.ExpandObjectSet(specMap["taint"].(*schema.Set)),
Expand Down Expand Up @@ -551,6 +575,7 @@ func flattenLKENodePools(pools []linodego.LKENodePool) []map[string]interface{}
"autoscaler": autoscaler,
"k8s_version": pool.K8sVersion,
"update_strategy": pool.UpdateStrategy,
"firewall_id": pool.FirewallID,
}
}
return flattened
Expand Down
4 changes: 4 additions & 0 deletions linode/lke/framework_datasource_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ var frameworkDataSourceSchema = schema.Schema{
Description: "The label of the Node Pool.",
Optional: true,
},
"firewall_id": schema.Int64Attribute{
Computed: true,
Description: "The ID of the firewall associated with the Node Pool.",
},
"count": schema.Int64Attribute{
Computed: true,
Description: "The number of nodes in the Node Pool.",
Expand Down
6 changes: 6 additions & 0 deletions linode/lke/framework_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type LKENodePool struct {
K8sVersion types.String `tfsdk:"k8s_version"`
UpdateStrategy types.String `tfsdk:"update_strategy"`
Label types.String `tfsdk:"label"`
FirewallId types.Int64 `tfsdk:"firewall_id"`
}

type LKENodePoolDisk struct {
Expand Down Expand Up @@ -143,6 +144,11 @@ func (data *LKEDataModel) parseLKEAttributes(
}
pool.Label = label

if p.FirewallID != nil {
firewallId := types.Int64Value(int64(*p.FirewallID))
pool.FirewallId = firewallId
}

if p.UpdateStrategy != nil {
pool.UpdateStrategy = types.StringValue(string(*p.UpdateStrategy))
}
Expand Down
1 change: 1 addition & 0 deletions linode/lke/framework_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ func TestAccResourceLKECluster_poolUpdates(t *testing.T) {
{
Config: tmpl.LabelledPools(t, clusterName, k8sVersionLatest, testRegion, "test"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceClusterName, "pool.0.firewall_id", "12345"),
resource.TestCheckResourceAttr(resourceClusterName, "pool.0.label", "test"),
),
},
Expand Down
6 changes: 6 additions & 0 deletions linode/lke/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,14 @@ func createResource(ctx context.Context, d *schema.ResourceData, meta interface{
label = linodego.Pointer(poolSpec["label"].(string))
}

var firewallId *int
if poolSpec["firewall_id"] != 0 {
firewallId = linodego.Pointer(poolSpec["firewall_id"].(int))
}

createOpts.NodePools = append(createOpts.NodePools, linodego.LKENodePoolCreateOptions{
Label: label,
FirewallID: firewallId,
Type: poolSpec["type"].(string),
Tags: helper.ExpandStringSet(poolSpec["tags"].(*schema.Set)),
Taints: expandNodePoolTaints(helper.ExpandObjectSet(poolSpec["taint"].(*schema.Set))),
Expand Down
5 changes: 5 additions & 0 deletions linode/lke/schema_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ var resourceSchema = map[string]*schema.Schema{
Description: "A Linode Type for all of the nodes in the Node Pool.",
Required: true,
},
"firewall_id": {
Type: schema.TypeInt,
Description: "The ID of the Firewall to attach to nodes in this node pool.",
Optional: true,
},
"labels": {
Type: schema.TypeMap,
Elem: &schema.Schema{
Expand Down
13 changes: 13 additions & 0 deletions linode/lkenodepool/framework_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type NodePoolModel struct {
K8sVersion types.String `tfsdk:"k8s_version"`
UpdateStrategy types.String `tfsdk:"update_strategy"`
Label types.String `tfsdk:"label"`
FirewallID types.Int64 `tfsdk:"firewall_id"`
}

type NodePoolAutoscalerModel struct {
Expand Down Expand Up @@ -101,6 +102,7 @@ func (pool *NodePoolModel) FlattenLKENodePool(
pool.Type = helper.KeepOrUpdateString(pool.Type, p.Type, preserveKnown)
pool.DiskEncryption = helper.KeepOrUpdateString(pool.DiskEncryption, string(p.DiskEncryption), preserveKnown)
pool.Label = helper.KeepOrUpdateStringPointer(pool.Label, p.Label, preserveKnown)
pool.FirewallID = helper.KeepOrUpdateIntPointer(pool.FirewallID, p.FirewallID, preserveKnown)
pool.Tags = helper.KeepOrUpdateStringSet(pool.Tags, p.Tags, preserveKnown, diags)
if diags.HasError() {
return
Expand Down Expand Up @@ -147,6 +149,10 @@ func (pool *NodePoolModel) SetNodePoolCreateOptions(
)
p.Type = pool.Type.ValueString()
p.Label = pool.Label.ValueStringPointer()
if !pool.FirewallID.IsNull() && pool.FirewallID.ValueInt64() != 0 {
firewall_id := int(pool.FirewallID.ValueInt64())
p.FirewallID = &firewall_id
}

if !pool.Tags.IsNull() {
diags.Append(pool.Tags.ElementsAs(ctx, &p.Tags, false)...)
Expand Down Expand Up @@ -198,6 +204,12 @@ func (pool *NodePoolModel) SetNodePoolUpdateOptions(
shouldUpdate = true
}

if state.FirewallID != pool.FirewallID {
firewall_id := int(pool.FirewallID.ValueInt64())
p.FirewallID = &firewall_id
shouldUpdate = true
}

if !state.Tags.Equal(pool.Tags) {
if !pool.Tags.IsNull() {
diags.Append(pool.Tags.ElementsAs(ctx, &p.Tags, false)...)
Expand Down Expand Up @@ -341,6 +353,7 @@ func (data *NodePoolModel) CopyFrom(other NodePoolModel, preserveKnown bool) {
data.K8sVersion = helper.KeepOrUpdateValue(data.K8sVersion, other.K8sVersion, preserveKnown)
data.UpdateStrategy = helper.KeepOrUpdateValue(data.UpdateStrategy, other.UpdateStrategy, preserveKnown)
data.Label = helper.KeepOrUpdateValue(data.Label, other.Label, preserveKnown)
data.FirewallID = helper.KeepOrUpdateValue(data.FirewallID, other.FirewallID, preserveKnown)

if !preserveKnown {
data.Autoscaler = other.Autoscaler
Expand Down
19 changes: 12 additions & 7 deletions linode/lkenodepool/framework_models_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import (

func TestParseNodePool(t *testing.T) {
poolLabelName := "test-pool"
poolFirewallId := 12345
lkeNodePool := linodego.LKENodePool{
ID: 123,
Count: 3,
Label: &poolLabelName,
Type: "g6-standard-2",
FirewallID: &poolFirewallId,
DiskEncryption: linodego.InstanceDiskEncryptionEnabled,
Disks: []linodego.LKENodePoolDisk{
{Size: 50, Type: "ssd"},
Expand Down Expand Up @@ -48,6 +50,7 @@ func TestParseNodePool(t *testing.T) {
assert.Equal(t, int64(3), nodePoolModel.Count.ValueInt64())
assert.Equal(t, "g6-standard-2", nodePoolModel.Type.ValueString())
assert.Equal(t, "enabled", nodePoolModel.DiskEncryption.ValueString())
assert.Equal(t, int64(12345), nodePoolModel.FirewallID.ValueInt64())
assert.Len(t, nodePoolModel.Nodes.Elements(), 3)

tags := make([]string, len(nodePoolModel.Tags.Elements()))
Expand Down Expand Up @@ -77,7 +80,7 @@ func TestSetNodePoolCreateOptions(t *testing.T) {
assert.Equal(t, "test-pool", *createOpts.Label)
assert.Contains(t, createOpts.Tags, "production")
assert.Contains(t, createOpts.Tags, "web-server")

assert.Equal(t, 12345, *createOpts.FirewallID)
assert.True(t, createOpts.Autoscaler.Enabled)
assert.Equal(t, 1, createOpts.Autoscaler.Min)
assert.Equal(t, 5, createOpts.Autoscaler.Max)
Expand All @@ -99,6 +102,7 @@ func TestSetNodePoolUpdateOptions(t *testing.T) {
assert.True(t, shouldUpdate)
assert.Equal(t, 3, updateOpts.Count)
assert.Equal(t, "test-pool", *updateOpts.Label)
assert.Equal(t, 12345, *updateOpts.FirewallID)
assert.Contains(t, *updateOpts.Tags, "production")
assert.Contains(t, *updateOpts.Tags, "web-server")

Expand All @@ -119,12 +123,13 @@ func createNodePoolModel() *NodePoolModel {
})

nodePoolModel := NodePoolModel{
ClusterID: types.Int64Value(1),
Count: types.Int64Value(3),
Label: types.StringValue("test-pool"),
Type: types.StringValue("g6-standard-2"),
Nodes: *nodes,
Tags: tags,
ClusterID: types.Int64Value(1),
Count: types.Int64Value(3),
Label: types.StringValue("test-pool"),
FirewallID: types.Int64Value(12345),
Type: types.StringValue("g6-standard-2"),
Nodes: *nodes,
Tags: tags,
Autoscaler: []NodePoolAutoscalerModel{
{
Min: types.Int64Value(1),
Expand Down
5 changes: 5 additions & 0 deletions linode/lkenodepool/framework_resource_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ var resourceSchema = schema.Schema{
Default: stringdefault.StaticString(""),
Computed: true,
},
"firewall_id": schema.Int64Attribute{
Description: "The ID of the Firewall to attach to nodes in this node pool.",
Optional: true,
Computed: true,
},
"node_count": schema.Int64Attribute{
Validators: []validator.Int64{
int64validator.AtLeast(1),
Expand Down
Loading