Ruleset prefix list isolation support#2290
Conversation
6878459 to
ddba969
Compare
ddba969 to
3a48f0f
Compare
There was a problem hiding this comment.
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_firewallto 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.", |
There was a problem hiding this comment.
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.
| Description: "The unique ID of this Object Storage key.", | |
| Description: "The unique ID of this Firewall.", |
| // 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) |
There was a problem hiding this comment.
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).
| * `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. | ||
|
|
There was a problem hiding this comment.
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.
| * `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. |
| 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), | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| 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), |
There was a problem hiding this comment.
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.
| "visibility": schema.StringAttribute{ | ||
| Description: "The visibility of the Prefix List (e.g. system, user).", | ||
| Computed: true, | ||
| }, |
There was a problem hiding this comment.
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.
| * `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed. | ||
|
|
There was a problem hiding this comment.
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.
| * `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed. |
| * `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed. | ||
|
|
There was a problem hiding this comment.
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.
| * `version` - The version number of the Firewall's rule configuration. This is incremented each time the Firewall's rules are changed. |
| * `version` - The version number of the Firewall's rule configuration. | ||
|
|
There was a problem hiding this comment.
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.
| * `version` - The version number of the Firewall's rule configuration. |
| 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 { |
There was a problem hiding this comment.
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).
📝 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
LKEClusterRuleSetIDs(Inbound/Outbound int)discoverClusterRulesets()inlinode_lke_clusterLKECluster.RuleSetIDsLKENodePoolIsolation(PublicIPv4/PublicIPv6 bool)isolationblock onlinode_lke_cluster+linode_lke_node_poolLKENodePoolCreateOptions.DiskEncryptiondisk_encryptionon pool createLKENodePoolCreateOptions.Isolationisolationon pool createLKENodePoolUpdateOptions.Isolationisolationon pool updateClient.ListFirewallRuleSets()discoverClusterRulesets()Firewall Ruleset References
New
inbound_ruleset/outbound_rulesetattributes onlinode_firewallaccept 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/ipv6rule address types fromcidrtypes.IPv4PrefixType/IPv6PrefixTypetotypes.StringTypeso that prefix list tokens (e.g.pl::subnets:123,pl:system:ps:managed:container:registry) are accepted alongside CIDRs.LKE-E Cluster
ruleset_idsNew computed
ruleset_idsblock onlinode_lke_clusterexposesinboundandoutboundfirewall ruleset IDs. Discovered viaListFirewallRuleSetsmatching thelke{id}-inbound/lke{id}-outboundlabel convention, since the cluster API does not return these natively.Node Pool Isolation
New
isolationblock on bothlinode_lke_cluster(SDKv2 pools) andlinode_lke_node_pool(framework resource) withpublic_ipv4/public_ipv6booleans.Node Pool Disk Encryption
disk_encryptionchanged from computed-only to optional+computed with validation (enabled/disabled). ForceNew on the framework resource.Zero-Pool Enterprise Clusters
linode_lke_clusternow supports creating enterprise-tier clusters with zero inline node pools. The node-ready wait is skipped andcustomDiffValidateOptionalCountguards against nil pool lists.Files Changed
linode/lke/resource.godiscoverClusterRulesets()helper, zero-pool wait logic, isolation/disk_encryption on pool createlinode/lke/schema_resource.goruleset_idscomputed block,isolationblock,disk_encryptionoptional+validatedlinode/lke/cluster.goNodePoolSpecgetsDiskEncryption/Isolation, reconcile/expand/flatten updatedlinode/lkenodepool/framework_models.goNodePoolIsolationModel, flatten/create/update/copy for isolation + disk_encryptionlinode/lkenodepool/framework_resource_schema.goisolationblock,disk_encryptionoptional withRequiresReplacelinode/firewall/framework_schema_resource.goinbound_ruleset/outbound_rulesetattributes, ipv4/ipv6 →types.StringTypelinode/firewall/framework_schema_datasource.gotypes.StringTypelinode/firewall/framework_models.goseparateRulesetRefs(), ruleset expand/flatten,InboundRuleSet/OutboundRuleSetfieldslinode/firewall/firewall_helpers_unit_test.go✔️ How to Test
What are the steps to reproduce the issue or verify the changes?
tier = "enterprise"and zero inline poolslinode_lke_cluster.main.ruleset_ids[0].inbound/.outboundin alinode_firewallresource'sinbound_ruleset/outbound_rulesetipv4 = ["pl::subnets:123"])linode_lke_node_poolwithdisk_encryption = "enabled"andisolation { public_ipv4 = false; public_ipv6 = true }tofu applycompletes andtofu planshows no driftHow do I run the relevant unit tests?