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
5 changes: 5 additions & 0 deletions firewall_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ type FirewallRuleSet struct {
InboundPolicy string `json:"inbound_policy"`
Outbound []FirewallRule `json:"outbound"`
OutboundPolicy string `json:"outbound_policy"`

// Version is the firewall rules version, starting from 1. The version
// number increments when the rules change. Include the version when updating
// rules to avoid conflicts.
Version int `json:"version,omitempty"`
}

// GetFirewallRules gets the FirewallRuleSet for the given Firewall.
Expand Down
1 change: 1 addition & 0 deletions firewalls.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Firewall struct {
Status FirewallStatus `json:"status"`
Tags []string `json:"tags,omitempty"`
Rules FirewallRuleSet `json:"rules"`
Version int `json:"version"`
Created *time.Time `json:"-"`
Updated *time.Time `json:"-"`
}
Expand Down
10 changes: 10 additions & 0 deletions lke_clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ type LKECluster struct {
SubnetID int `json:"subnet_id"`
VpcID int `json:"vpc_id"`
StackType LKEClusterStackType `json:"stack_type"`

// RuleSetIDs contains the IDs of the service-managed firewall rulesets
// automatically created for LKE Enterprise clusters.
RuleSetIDs *LKEClusterRuleSetIDs `json:"ruleset_ids,omitempty"`
}

// LKEClusterRuleSetIDs contains the inbound and outbound ruleset IDs for an LKE-E cluster.
type LKEClusterRuleSetIDs struct {
Inbound int `json:"inbound"`
Outbound int `json:"outbound"`
}

// LKEClusterCreateOptions fields are those accepted by CreateLKECluster
Expand Down
17 changes: 17 additions & 0 deletions lke_node_pools.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ type LKENodePoolTaint struct {
Effect LKENodePoolTaintEffect `json:"effect"`
}

// LKENodePoolIsolation controls network isolation for nodes in the pool.
type LKENodePoolIsolation struct {
PublicIPv4 bool `json:"public_ipv4"`
PublicIPv6 bool `json:"public_ipv6"`
}

// LKENodePoolLabels represents Kubernetes labels to add to an LKENodePool
type LKENodePoolLabels map[string]string

Expand All @@ -80,6 +86,8 @@ type LKENodePool struct {
// NOTE: Disk encryption may not currently be available to all users.
DiskEncryption InstanceDiskEncryption `json:"disk_encryption,omitempty"`

Isolation *LKENodePoolIsolation `json:"isolation,omitempty"`

// K8sVersion and UpdateStrategy are only for LKE Enterprise to support node pool upgrades.
// It may not currently be available to all users and is under v4beta.
K8sVersion *string `json:"k8s_version,omitempty"`
Expand All @@ -99,6 +107,10 @@ type LKENodePoolCreateOptions struct {
Autoscaler *LKENodePoolAutoscaler `json:"autoscaler,omitempty"`
FirewallID *int `json:"firewall_id,omitempty"`

// NOTE: Disk encryption may not currently be available to all users.
DiskEncryption InstanceDiskEncryption `json:"disk_encryption,omitempty"`
Isolation *LKENodePoolIsolation `json:"isolation,omitempty"`

// K8sVersion and UpdateStrategy only works for LKE Enterprise to support node pool upgrades.
// It may not currently be available to all users and is under v4beta.
K8sVersion *string `json:"k8s_version,omitempty"`
Expand All @@ -120,6 +132,8 @@ type LKENodePoolUpdateOptions struct {
// It may not currently be available to all users and is under v4beta.
K8sVersion *string `json:"k8s_version,omitempty"`
UpdateStrategy *LKENodePoolUpdateStrategy `json:"update_strategy,omitempty"`

Isolation *LKENodePoolIsolation `json:"isolation,omitempty"`
}

// GetCreateOptions converts a LKENodePool to LKENodePoolCreateOptions for
Expand All @@ -135,6 +149,8 @@ func (l LKENodePool) GetCreateOptions() (o LKENodePoolCreateOptions) {
o.UpdateStrategy = l.UpdateStrategy
o.Label = l.Label
o.FirewallID = l.FirewallID
o.DiskEncryption = l.DiskEncryption
o.Isolation = l.Isolation

return o
}
Expand All @@ -150,6 +166,7 @@ func (l LKENodePool) GetUpdateOptions() (o LKENodePoolUpdateOptions) {
o.UpdateStrategy = l.UpdateStrategy
o.Label = l.Label
o.FirewallID = l.FirewallID
o.Isolation = l.Isolation

return o
}
Expand Down
26 changes: 26 additions & 0 deletions prefixlists.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package linodego
import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/linode/linodego/internal/parseabletime"
Expand Down Expand Up @@ -59,3 +60,28 @@ func (c *Client) GetPrefixList(ctx context.Context, id int) (*PrefixList, error)
endpoint := formatAPIPath("networking/prefixlists/%d", id)
return doGETRequest[PrefixList](ctx, c, endpoint)
}

// GetPrefixListByName finds a Prefix List by its name (e.g., "pl:system:object-storage:us-iad").
// Returns nil and an error if no matching prefix list is found.
func (c *Client) GetPrefixListByName(ctx context.Context, name string) (*PrefixList, error) {
f := Filter{}
f.AddField(Eq, "name", name)

fJSON, err := f.MarshalJSON()
if err != nil {
return nil, err
}

opts := ListOptions{Filter: string(fJSON)}

lists, err := c.ListPrefixLists(ctx, &opts)
if err != nil {
return nil, err
}

if len(lists) == 0 {
return nil, fmt.Errorf("prefix list with name %q not found", name)
}

return &lists[0], nil
}
4 changes: 4 additions & 0 deletions test/unit/firewall_rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func TestFirewallRule_Get(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, firewallRule)

assert.Equal(t, 1, firewallRule.Version)
assert.Equal(t, "DROP", firewallRule.InboundPolicy)
assert.Equal(t, 1, len(firewallRule.Inbound))
assert.Equal(t, "ACCEPT", firewallRule.Inbound[0].Action)
Expand Down Expand Up @@ -119,6 +120,7 @@ func TestFirewallRule_Update(t *testing.T) {
assert.NoError(t, err)
assert.NotNil(t, firewallRule)

assert.Equal(t, 1, firewallRule.Version)
assert.Equal(t, "DROP", firewallRule.InboundPolicy)
assert.Equal(t, 1, len(firewallRule.Inbound))
assert.Equal(t, "ACCEPT", firewallRule.Inbound[0].Action)
Expand Down Expand Up @@ -147,6 +149,7 @@ func TestFirewallRule_GetExpansion(t *testing.T) {
outboundIPv6 := []string{"pl::vpcs:<current>"}

mockResponse := linodego.FirewallRuleSet{
Version: 2,
Inbound: []linodego.FirewallRule{
{
Action: "ACCEPT",
Expand Down Expand Up @@ -187,6 +190,7 @@ func TestFirewallRule_GetExpansion(t *testing.T) {
firewallRuleSet, err := base.Client.GetFirewallRulesExpansion(context.Background(), firewallID)
assert.NoError(t, err)
assert.NotNil(t, firewallRuleSet)
assert.Equal(t, 2, firewallRuleSet.Version)

if assert.Len(t, firewallRuleSet.Inbound, 1) {
assert.Equal(t, "ACCEPT", firewallRuleSet.Inbound[0].Action)
Expand Down
8 changes: 8 additions & 0 deletions test/unit/firewalls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ func TestFirewall_List(t *testing.T) {
assert.Equal(t, 123, firewall.ID)
assert.Equal(t, "firewall123", firewall.Label)
assert.Equal(t, linodego.FirewallStatus("enabled"), firewall.Status)
assert.Equal(t, 2, firewall.Version)

assert.Equal(t, "DROP", firewall.Rules.InboundPolicy)
assert.Equal(t, 1, firewall.Rules.Version)
assert.Len(t, firewall.Rules.Inbound, 1)

inboundRule := firewall.Rules.Inbound[0]
Expand Down Expand Up @@ -112,11 +114,13 @@ func TestFirewall_Create(t *testing.T) {
assert.Equal(t, 123, firewall.ID)
assert.Equal(t, "firewall123", firewall.Label)
assert.Equal(t, linodego.FirewallStatus("enabled"), firewall.Status)
assert.Equal(t, 1, firewall.Version)
assert.ElementsMatch(t, []string{"example tag", "another example"}, firewall.Tags)

assert.NotNil(t, firewall.Rules)
assert.Equal(t, "DROP", firewall.Rules.InboundPolicy)
assert.Equal(t, "DROP", firewall.Rules.OutboundPolicy)
assert.Equal(t, 1, firewall.Rules.Version)

assert.Len(t, firewall.Rules.Inbound, 1)
inboundRule := firewall.Rules.Inbound[0]
Expand Down Expand Up @@ -158,13 +162,15 @@ func TestFirewall_Get(t *testing.T) {
assert.Equal(t, 123, firewall.ID)
assert.Equal(t, "firewall123", firewall.Label)
assert.Equal(t, linodego.FirewallStatus("enabled"), firewall.Status)
assert.Equal(t, 2, firewall.Version)
assert.Equal(t, "2018-01-01T00:01:01Z", firewall.Created.Format(time.RFC3339))
assert.Equal(t, "2018-01-02T00:01:01Z", firewall.Updated.Format(time.RFC3339))
assert.ElementsMatch(t, []string{"example tag", "another example"}, firewall.Tags)

assert.NotNil(t, firewall.Rules)
assert.Equal(t, "DROP", firewall.Rules.InboundPolicy)
assert.Equal(t, "DROP", firewall.Rules.OutboundPolicy)
assert.Equal(t, 1, firewall.Rules.Version)

assert.Len(t, firewall.Rules.Inbound, 1)
inboundRule := firewall.Rules.Inbound[0]
Expand Down Expand Up @@ -212,13 +218,15 @@ func TestFirewall_Update(t *testing.T) {
assert.Equal(t, 123, firewall.ID)
assert.Equal(t, "firewall123", firewall.Label)
assert.Equal(t, linodego.FirewallStatus("enabled"), firewall.Status)
assert.Equal(t, 3, firewall.Version)
assert.Equal(t, "2018-01-01T00:01:01Z", firewall.Created.Format(time.RFC3339))
assert.Equal(t, "2018-01-02T00:01:01Z", firewall.Updated.Format(time.RFC3339))
assert.ElementsMatch(t, []string{"updated tag", "another updated tag"}, firewall.Tags)

assert.NotNil(t, firewall.Rules)
assert.Equal(t, "DROP", firewall.Rules.InboundPolicy)
assert.Equal(t, "DROP", firewall.Rules.OutboundPolicy)
assert.Equal(t, 1, firewall.Rules.Version)

assert.Len(t, firewall.Rules.Inbound, 1)
inboundRule := firewall.Rules.Inbound[0]
Expand Down
3 changes: 2 additions & 1 deletion test/unit/fixtures/firewall_create.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@
"example tag",
"another example"
],
"updated": "2018-01-02T00:01:01"
"updated": "2018-01-02T00:01:01",
"version": 1
}
3 changes: 2 additions & 1 deletion test/unit/fixtures/firewall_get.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@
"example tag",
"another example"
],
"updated": "2018-01-02T00:01:01"
"updated": "2018-01-02T00:01:01",
"version": 2
}
3 changes: 2 additions & 1 deletion test/unit/fixtures/firewall_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
"example tag",
"another example"
],
"updated": "2018-01-02T00:01:01"
"updated": "2018-01-02T00:01:01",
"version": 2
}
],
"page": 1,
Expand Down
3 changes: 2 additions & 1 deletion test/unit/fixtures/firewall_update.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@
"updated tag",
"another updated tag"
],
"updated": "2018-01-02T00:01:01"
"updated": "2018-01-02T00:01:01",
"version": 3
}
18 changes: 18 additions & 0 deletions test/unit/fixtures/lke_cluster_enterprise_create.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": 3010,
"label": "enterprise-cluster",
"region": "us-east",
"status": "ready",
"tier": "enterprise",
"subnet_id": 2010,
"vpc_id": 1010,
"stack_type": "ipv4-ipv6",
"control_plane": {
"high_availability": true,
"audit_logs_enabled": true
},
"ruleset_ids": {
"inbound": 4010,
"outbound": 4011
}
}
54 changes: 54 additions & 0 deletions test/unit/lke_clusters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,60 @@ func TestLKECluster_Create(t *testing.T) {
assert.Equal(t, false, cluster.ControlPlane.AuditLogsEnabled)
}

func TestLKECluster_Create_Enterprise_RuleSetIDs(t *testing.T) {
fixtureData, err := fixtures.GetFixture("lke_cluster_enterprise_create")
assert.NoError(t, err)

var base ClientBaseCase
base.SetUp(t)
defer base.TearDown(t)

createOptions := linodego.LKEClusterCreateOptions{
Label: "enterprise-cluster",
Region: "us-east",
K8sVersion: "1.31",
Tier: "enterprise",
SubnetID: linodego.Pointer(2010),
VpcID: linodego.Pointer(1010),
StackType: linodego.Pointer(linodego.LKEClusterDualStack),
ControlPlane: &linodego.LKEClusterControlPlaneOptions{
HighAvailability: linodego.Pointer(true),
AuditLogsEnabled: linodego.Pointer(true),
},
}

base.MockPost("lke/clusters", fixtureData)

cluster, err := base.Client.CreateLKECluster(context.Background(), createOptions)
assert.NoError(t, err)
assert.Equal(t, 3010, cluster.ID)
assert.Equal(t, "enterprise", cluster.Tier)
assert.Equal(t, 2010, cluster.SubnetID)
assert.Equal(t, 1010, cluster.VpcID)
assert.Equal(t, linodego.LKEClusterDualStack, cluster.StackType)

// Validate ruleset_ids deserialization
assert.NotNil(t, cluster.RuleSetIDs, "RuleSetIDs should not be nil for enterprise clusters")
assert.Equal(t, 4010, cluster.RuleSetIDs.Inbound)
assert.Equal(t, 4011, cluster.RuleSetIDs.Outbound)
}

func TestLKECluster_Get_NoRuleSetIDs(t *testing.T) {
// Standard clusters do not return ruleset_ids; the field should be nil
fixtureData, err := fixtures.GetFixture("lke_cluster_get")
assert.NoError(t, err)

var base ClientBaseCase
base.SetUp(t)
defer base.TearDown(t)

base.MockGet("lke/clusters/123", fixtureData)

cluster, err := base.Client.GetLKECluster(context.Background(), 123)
assert.NoError(t, err)
assert.Nil(t, cluster.RuleSetIDs, "RuleSetIDs should be nil for standard clusters")
}

func TestLKECluster_Update(t *testing.T) {
fixtureData, err := fixtures.GetFixture("lke_cluster_update")
assert.NoError(t, err)
Expand Down
Loading