Skip to content

Ruleset prefix list isolation support#2290

Open
aweingarten wants to merge 8 commits intolinode:devfrom
aweingarten:ruleset_prefix_list_isolation_support
Open

Ruleset prefix list isolation support#2290
aweingarten wants to merge 8 commits intolinode:devfrom
aweingarten:ruleset_prefix_list_isolation_support

Conversation

@aweingarten
Copy link
Copy Markdown

@aweingarten aweingarten commented Mar 4, 2026

📝 Description

What does this PR do and why is this change necessary?

Adds LKE Enterprise provisioning support across three areas: firewall rulesets/prefix lists, node pool isolation & disk encryption, and zero-pool cluster creation.

Dependencies

The following linodego additions are required in PR

Type Addition Used by
Struct LKEClusterRuleSetIDs (Inbound/Outbound int) discoverClusterRulesets() in linode_lke_cluster
Field LKECluster.RuleSetIDs Deserialization (API does not yet populate)
Struct LKENodePoolIsolation (PublicIPv4/PublicIPv6 bool) isolation block on linode_lke_cluster + linode_lke_node_pool
Field LKENodePoolCreateOptions.DiskEncryption disk_encryption on pool create
Field LKENodePoolCreateOptions.Isolation isolation on pool create
Field LKENodePoolUpdateOptions.Isolation isolation on pool update
Method Client.ListFirewallRuleSets() Discovering LKE-E rulesets by label in discoverClusterRulesets()

Firewall Ruleset References

New inbound_ruleset / outbound_ruleset attributes on linode_firewall accept a list of ruleset IDs. These are prepended before inline rules in the API payload and separated back out on read. This lets users wire LKE-E service-managed rulesets (control-plane, registry, DNS, etc.) directly into a firewall resource.

Prefix List Support

Changed ipv4/ipv6 rule address types from cidrtypes.IPv4PrefixType/IPv6PrefixType to types.StringType so that prefix list tokens (e.g. pl::subnets:123, pl:system:ps:managed:container:registry) are accepted alongside CIDRs.

LKE-E Cluster ruleset_ids

New computed ruleset_ids block on linode_lke_cluster exposes inbound and outbound firewall ruleset IDs. Discovered via ListFirewallRuleSets matching the lke{id}-inbound/lke{id}-outbound label convention, since the cluster API does not return these natively.

Node Pool Isolation

New isolation block on both linode_lke_cluster (SDKv2 pools) and linode_lke_node_pool (framework resource) with public_ipv4 / public_ipv6 booleans.

Node Pool Disk Encryption

disk_encryption changed from computed-only to optional+computed with validation (enabled/disabled). ForceNew on the framework resource.

Zero-Pool Enterprise Clusters

linode_lke_cluster now supports creating enterprise-tier clusters with zero inline node pools. The node-ready wait is skipped and customDiffValidateOptionalCount guards against nil pool lists.

Files Changed

File Change
linode/lke/resource.go discoverClusterRulesets() helper, zero-pool wait logic, isolation/disk_encryption on pool create
linode/lke/schema_resource.go ruleset_ids computed block, isolation block, disk_encryption optional+validated
linode/lke/cluster.go NodePoolSpec gets DiskEncryption/Isolation, reconcile/expand/flatten updated
linode/lkenodepool/framework_models.go NodePoolIsolationModel, flatten/create/update/copy for isolation + disk_encryption
linode/lkenodepool/framework_resource_schema.go isolation block, disk_encryption optional with RequiresReplace
linode/firewall/framework_schema_resource.go inbound_ruleset/outbound_ruleset attributes, ipv4/ipv6 → types.StringType
linode/firewall/framework_schema_datasource.go ipv4/ipv6 → types.StringType
linode/firewall/framework_models.go separateRulesetRefs(), ruleset expand/flatten, InboundRuleSet/OutboundRuleSet fields
linode/firewall/firewall_helpers_unit_test.go 5 new tests for separateRulesetRefs, ExpandFirewallRuleSet, FlattenFirewallRules

✔️ How to Test

What are the steps to reproduce the issue or verify the changes?

  1. Create an LKE-E cluster with tier = "enterprise" and zero inline pools
  2. Reference linode_lke_cluster.main.ruleset_ids[0].inbound / .outbound in a linode_firewall resource's inbound_ruleset / outbound_ruleset
  3. Add outbound rules using prefix list tokens (e.g. ipv4 = ["pl::subnets:123"])
  4. Create a linode_lke_node_pool with disk_encryption = "enabled" and isolation { public_ipv4 = false; public_ipv6 = true }
  5. Verify tofu apply completes and tofu plan shows no drift

How do I run the relevant unit tests?

# Firewall helpers (separateRulesetRefs, ExpandFirewallRuleSet, FlattenFirewallRules)
go test ./linode/firewall/ -run 'TestSeparateRulesetRefs|TestExpandFirewallRuleSet|TestFlattenFirewallRules_PrefixList' -v

# linodego LKE cluster tests
cd ../linodego && go test ./test/unit/ -run 'TestLKECluster_Create_Enterprise|TestLKECluster_Get_NoRuleSetIDs' -v

@aweingarten aweingarten requested a review from a team as a code owner March 4, 2026 14:12
@aweingarten aweingarten requested review from mawilk90 and psnoch-akamai and removed request for a team March 4, 2026 14:12
@aweingarten aweingarten force-pushed the ruleset_prefix_list_isolation_support branch 6 times, most recently from 6878459 to ddba969 Compare March 10, 2026 18:07
@aweingarten aweingarten force-pushed the ruleset_prefix_list_isolation_support branch from ddba969 to 3a48f0f Compare March 10, 2026 18:17
@zliang-akamai zliang-akamai requested a review from a team as a code owner April 12, 2026 07:05
@zliang-akamai zliang-akamai requested review from ckulinsk and removed request for a team April 12, 2026 07:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds Terraform provider support for LKE Enterprise firewall ruleset/prefix list integrations, plus LKE node pool isolation & disk encryption, and introduces new firewall ruleset/prefix list data sources/resources.

Changes:

  • Add Firewall Rule Set resources/data sources and a “rules expansion” data source; allow linode_firewall to reference rule sets and accept prefix list tokens in rule addresses.
  • Add Prefix List data sources (linode_prefix_list, linode_prefix_lists).
  • Extend LKE cluster/node pool functionality for enterprise features (ruleset discovery, isolation, disk encryption, zero-pool cluster create) and add control plane metrics_enabled.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
linode/prefixlists/framework_schema.go New prefix lists list data source schema.
linode/prefixlists/framework_models.go New prefix lists list data source model.
linode/prefixlists/framework_datasource.go New prefix lists list data source read logic.
linode/prefixlist/framework_schema.go New single prefix list data source schema.
linode/prefixlist/framework_models.go New single prefix list model + flattening.
linode/prefixlist/framework_datasource.go New single prefix list data source read logic.
linode/lkenodepool/framework_resource_schema.go Adds isolation block and makes disk_encryption optional+computed.
linode/lkenodepool/framework_models.go Adds node pool isolation wiring + disk encryption on create.
linode/lkeclusters/framework_models.go Adds metrics_enabled control plane field.
linode/lkeclusters/framework_datasource_schema.go Exposes metrics_enabled in LKE clusters data source.
linode/lke/schema_resource.go Adds ruleset_ids, isolation, disk encryption, and metrics_enabled to SDKv2 LKE cluster resource schema.
linode/lke/resource.go Adds ruleset discovery, zero-pool wait behavior, and pool create options for isolation/disk encryption.
linode/lke/framework_models.go Adds metrics_enabled control plane field to framework LKE model.
linode/lke/framework_datasource_schema.go Exposes metrics_enabled in framework LKE data source schema.
linode/lke/cluster.go Expands/flattens isolation + disk encryption in SDKv2 cluster pool specs.
linode/framework_provider.go Registers new firewall ruleset/prefix list resources/data sources.
linode/firewallrulesexpansion/framework_schema.go New data source schema for resolved firewall rules.
linode/firewallrulesexpansion/framework_models.go New data source model for resolved firewall rules.
linode/firewallrulesexpansion/framework_datasource.go New data source read logic for firewall rules expansion.
linode/firewallrulesets/framework_schema.go New firewall rulesets list data source schema.
linode/firewallrulesets/framework_models.go New firewall rulesets list data source model.
linode/firewallrulesets/framework_datasource.go New firewall rulesets list data source read logic.
linode/firewallruleset/framework_schema_resource.go New firewall ruleset resource schema.
linode/firewallruleset/framework_schema_datasource.go New firewall ruleset data source schema.
linode/firewallruleset/framework_resource.go New firewall ruleset CRUD implementation.
linode/firewallruleset/framework_models.go New firewall ruleset models + expand/flatten.
linode/firewallruleset/framework_datasource.go New firewall ruleset single data source read logic.
linode/firewall/framework_schema_resource.go Adds ruleset reference attributes and allows prefix list tokens via string IPv4/IPv6 lists.
linode/firewall/framework_schema_datasource.go Allows prefix list tokens in rule address lists for data sources.
linode/firewall/framework_models.go Implements ruleset ref expand/flatten separation + prefix list string handling.
linode/firewall/firewall_helpers_unit_test.go Adds unit tests for ruleset separation and prefix list tokens.
go.sum Dependency checksum updates.
go.mod Updates Go/deps and adds a local replace for linodego.
docs/resources/lke_node_pool.md Documents node pool isolation + disk encryption.
docs/resources/lke_cluster.md Documents enterprise zero-pool, ruleset_ids, isolation, disk encryption.
docs/resources/firewall.md Documents ruleset refs and prefix list tokens (also adds version).
docs/resources/firewall_ruleset.md Adds firewall ruleset resource docs.
docs/data-sources/prefix_lists.md Adds prefix lists list data source docs.
docs/data-sources/prefix_list.md Adds single prefix list data source docs.
docs/data-sources/lke_node_pool.md Documents node pool isolation fields.
docs/data-sources/lke_cluster.md Documents cluster ruleset_ids and pool isolation fields.
docs/data-sources/firewalls.md Documents firewall list data source changes (adds version).
docs/data-sources/firewall.md Documents firewall data source changes (adds rulesets + version).
docs/data-sources/firewall_rulesets.md Adds firewall rulesets list data source docs.
docs/data-sources/firewall_ruleset.md Adds firewall ruleset data source docs.
docs/data-sources/firewall_rules_expansion.md Adds firewall rules expansion data source docs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ElementType: types.Int64Type,
},
"id": schema.StringAttribute{
Description: "The unique ID of this Object Storage key.",
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

The id attribute description appears to be copy/pasted ("Object Storage key") and doesn’t match this resource. Update it to refer to the Firewall ID to avoid confusing users.

Suggested change
Description: "The unique ID of this Object Storage key.",
Description: "The unique ID of this Firewall.",

Copilot uses AI. Check for mistakes.
Comment on lines +265 to +279
// Flatten inbound ruleset IDs
inboundRSList, newDiags := types.ListValueFrom(ctx, types.Int64Type, inboundRulesetIDs)
diags.Append(newDiags...)
if diags.HasError() {
return
}
data.InboundRuleSet = helper.KeepOrUpdateValue(data.InboundRuleSet, inboundRSList, preserveKnown)

// Flatten outbound ruleset IDs
outboundRSList, newDiags := types.ListValueFrom(ctx, types.Int64Type, outboundRulesetIDs)
diags.Append(newDiags...)
if diags.HasError() {
return
}
data.OutboundRuleSet = helper.KeepOrUpdateValue(data.OutboundRuleSet, outboundRSList, preserveKnown)
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

When there are no ruleset references, types.ListValueFrom will produce an empty list, which can cause perpetual diffs vs a null config value for these optional attributes. Consider setting inbound_ruleset/outbound_ruleset to types.ListNull(types.Int64Type) when no ruleset IDs are present (and only emitting an empty list when the user explicitly configured one).

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +45
* `inbound_ruleset` - A list of Firewall Rule Set IDs referenced as inbound rules.

* `inbound_policy` - The default behavior for inbound traffic. (`ACCEPT`, `DROP`)

* [`outbound`](#inbound-and-outbound) - A firewall rule that specifies what outbound network traffic is allowed.

* `outbound_ruleset` - A list of Firewall Rule Set IDs referenced as outbound rules.

Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

This data source documentation now claims inbound_ruleset/outbound_ruleset are exported, but the linode_firewall data source schema/model doesn’t include these attributes and the flatten logic doesn’t separate ruleset refs from inline rules. Either implement these attributes for the data source or remove them from the docs to avoid misleading users.

Suggested change
* `inbound_ruleset` - A list of Firewall Rule Set IDs referenced as inbound rules.
* `inbound_policy` - The default behavior for inbound traffic. (`ACCEPT`, `DROP`)
* [`outbound`](#inbound-and-outbound) - A firewall rule that specifies what outbound network traffic is allowed.
* `outbound_ruleset` - A list of Firewall Rule Set IDs referenced as outbound rules.
* `inbound_policy` - The default behavior for inbound traffic. (`ACCEPT`, `DROP`)
* [`outbound`](#inbound-and-outbound) - A firewall rule that specifies what outbound network traffic is allowed.

Copilot uses AI. Check for mistakes.
Comment thread linode/lke/resource.go
Comment on lines +259 to +265
if isoList, ok := poolSpec["isolation"].([]any); ok && len(isoList) > 0 {
if isoMap, ok := isoList[0].(map[string]any); ok {
poolCreateOpts.Isolation = &linodego.LKENodePoolIsolation{
PublicIPv4: isoMap["public_ipv4"].(bool),
PublicIPv6: isoMap["public_ipv6"].(bool),
}
}
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

isoMap["public_ipv4"].(bool) / isoMap["public_ipv6"].(bool) can panic if the keys are unset or unknown, and it also forces a default false value when users omit these fields (docs indicate defaults should be true/API-default). Please safely read these values (type-check + default to true or omit the Isolation field unless explicitly set) to avoid crashes and unintended isolation settings.

Copilot uses AI. Check for mistakes.
Comment thread linode/lke/cluster.go
Comment on lines +535 to +539
if isoList, ok := specMap["isolation"].([]any); ok && len(isoList) > 0 {
if isoMap, ok := isoList[0].(map[string]any); ok {
isolation = &linodego.LKENodePoolIsolation{
PublicIPv4: isoMap["public_ipv4"].(bool),
PublicIPv6: isoMap["public_ipv6"].(bool),
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

Same issue as in createResource: isoMap["public_ipv4"].(bool) / isoMap["public_ipv6"].(bool) can panic when unset/unknown and will default to false if the user didn’t specify values. Use safe extraction and default-to-true (or omit Isolation unless explicitly configured) to prevent unintended API payloads.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +26
"visibility": schema.StringAttribute{
Description: "The visibility of the Prefix List (e.g. system, user).",
Computed: true,
},
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

The visibility description lists example values (system, user), but the accompanying docs in this PR describe visibility as (account, restricted). Please align the schema description with the actual API values to avoid confusion.

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +128
* `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed.

Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

This doc now lists a version attribute, but the provider’s linode_firewall resource schema/model doesn’t expose a version field. Either add a version attribute in the resource implementation (and populate it from the API) or remove this line from the docs to avoid promising an attribute that isn’t available.

Suggested change
* `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed.

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +57
* `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed.

Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

This data source doc now includes a version attribute, but the linode_firewall data source schema/model does not currently export version. Please either implement and populate this attribute in the data source or remove it from the documentation.

Suggested change
* `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed.

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +93
* `version` - The version number of the Firewall's rule configuration.

Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

This list data source doc now claims each firewall exports a version, but the linode_firewalls data source implementation doesn’t currently include that attribute. Either add version to the data source schema/model or drop this from the docs to prevent confusion.

Suggested change
* `version` - The version number of the Firewall's rule configuration.

Copilot uses AI. Check for mistakes.
Comment thread linode/lke/resource.go
Comment on lines +655 to +667
func discoverClusterRulesets(
ctx context.Context, client linodego.Client, clusterID int,
) (*linodego.LKEClusterRuleSetIDs, diag.Diagnostics) {
inboundLabel := fmt.Sprintf("lke%d-inbound", clusterID)
outboundLabel := fmt.Sprintf("lke%d-outbound", clusterID)

tflog.Debug(ctx, "Discovering LKE-E rulesets", map[string]any{
"inbound_label": inboundLabel,
"outbound_label": outboundLabel,
})

rulesets, err := client.ListFirewallRuleSets(ctx, &linodego.ListOptions{})
if err != nil {
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

discoverClusterRulesets accepts client linodego.Client, but callers pass *linodego.Client (e.g., meta.(*helper.ProviderMeta).Client), which will not compile. Change the parameter type to *linodego.Client (and consider using API filtering on label to avoid listing all rulesets on every read).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants