Skip to content

Commit 0370427

Browse files
Merge branch 'main' into proj/iam
2 parents 1d31b10 + 72287a0 commit 0370427

21 files changed

+2251
-9
lines changed

account_events.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ const (
122122
ActionFirewallUpdate EventAction = "firewall_update"
123123
ActionFirewallDeviceAdd EventAction = "firewall_device_add"
124124
ActionFirewallDeviceRemove EventAction = "firewall_device_remove"
125+
ActionFirewallRuleSetCreate EventAction = "firewall_ruleset_create"
126+
ActionFirewallRuleSetUpdate EventAction = "firewall_ruleset_update"
127+
ActionFirewallRuleSetDelete EventAction = "firewall_ruleset_delete"
128+
ActionFirewallRuleSetFirewallUpdate EventAction = "firewall_ruleset_firewall_update"
125129
ActionHostReboot EventAction = "host_reboot"
126130
ActionImageDelete EventAction = "image_delete"
127131
ActionImageUpdate EventAction = "image_update"

firewall_rules.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package linodego
22

33
import (
44
"context"
5+
"encoding/json"
56
)
67

78
// NetworkProtocol enum type
@@ -22,13 +23,51 @@ type NetworkAddresses struct {
2223
}
2324

2425
// A FirewallRule is a whitelist of ports, protocols, and addresses for which traffic should be allowed.
26+
// The ipv4/ipv6 address lists may contain Prefix List tokens (for example, "pl::..." or "pl:system:...")
27+
// in addition to literal IP addresses.
2528
type FirewallRule struct {
2629
Action string `json:"action"`
2730
Label string `json:"label"`
2831
Description string `json:"description,omitempty"`
2932
Ports string `json:"ports,omitempty"`
3033
Protocol NetworkProtocol `json:"protocol"`
3134
Addresses NetworkAddresses `json:"addresses"`
35+
36+
// FirewallRule references one `Rule Set` by ID. When provided, this entry
37+
// represents a reference and should be mutually exclusive with ordinary
38+
// rule fields according to the API contract.
39+
RuleSet int `json:"ruleset,omitempty"`
40+
}
41+
42+
// MarshalJSON ensures that when a rule references a Rule Set (RuleSet != 0),
43+
// only the reference shape { "ruleset": <id> } is emitted. Otherwise, the
44+
// ordinary rule fields are emitted without the ruleset key.
45+
func (r FirewallRule) MarshalJSON() ([]byte, error) {
46+
if r.RuleSet != 0 {
47+
type rulesetOnly struct {
48+
RuleSet int `json:"ruleset"`
49+
}
50+
51+
return json.Marshal(rulesetOnly{RuleSet: r.RuleSet})
52+
}
53+
54+
type normal struct {
55+
Action string `json:"action"`
56+
Label string `json:"label"`
57+
Description string `json:"description,omitempty"`
58+
Ports string `json:"ports,omitempty"`
59+
Protocol NetworkProtocol `json:"protocol"`
60+
Addresses NetworkAddresses `json:"addresses"`
61+
}
62+
63+
return json.Marshal(normal{
64+
Action: r.Action,
65+
Label: r.Label,
66+
Description: r.Description,
67+
Ports: r.Ports,
68+
Protocol: r.Protocol,
69+
Addresses: r.Addresses,
70+
})
3271
}
3372

3473
// FirewallRuleSet is a pair of inbound and outbound rules that specify what network traffic should be allowed.
@@ -45,6 +84,12 @@ func (c *Client) GetFirewallRules(ctx context.Context, firewallID int) (*Firewal
4584
return doGETRequest[FirewallRuleSet](ctx, c, e)
4685
}
4786

87+
// GetFirewallRulesExpansion gets the expanded FirewallRuleSet for the given Firewall.
88+
func (c *Client) GetFirewallRulesExpansion(ctx context.Context, firewallID int) (*FirewallRuleSet, error) {
89+
e := formatAPIPath("networking/firewalls/%d/rules/expansion", firewallID)
90+
return doGETRequest[FirewallRuleSet](ctx, c, e)
91+
}
92+
4893
// UpdateFirewallRules updates the FirewallRuleSet for the given Firewall
4994
func (c *Client) UpdateFirewallRules(ctx context.Context, firewallID int, rules FirewallRuleSet) (*FirewallRuleSet, error) {
5095
e := formatAPIPath("networking/firewalls/%d/rules", firewallID)

firewall_rulesets.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package linodego
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"time"
7+
8+
"github.com/linode/linodego/internal/parseabletime"
9+
)
10+
11+
// FirewallRuleSetType represents the type of rules a Rule Set contains.
12+
// Valid values are "inbound" and "outbound".
13+
type FirewallRuleSetType string
14+
15+
const (
16+
FirewallRuleSetTypeInbound FirewallRuleSetType = "inbound"
17+
FirewallRuleSetTypeOutbound FirewallRuleSetType = "outbound"
18+
)
19+
20+
// RuleSet represents the Rule Set resource.
21+
// Note: created/updated/deleted are parsed via UnmarshalJSON into time.Time pointers.
22+
type RuleSet struct {
23+
ID int `json:"id"`
24+
Label string `json:"label"`
25+
Description string `json:"description,omitempty"`
26+
Type FirewallRuleSetType `json:"type"`
27+
Rules []FirewallRule `json:"rules"`
28+
IsServiceDefined bool `json:"is_service_defined"`
29+
Version int `json:"version"`
30+
31+
Created *time.Time `json:"-"`
32+
Updated *time.Time `json:"-"`
33+
Deleted *time.Time `json:"-"`
34+
}
35+
36+
// UnmarshalJSON implements custom timestamp parsing for RuleSet.
37+
func (r *RuleSet) UnmarshalJSON(b []byte) error {
38+
type Mask RuleSet
39+
40+
aux := struct {
41+
*Mask
42+
43+
Created *parseabletime.ParseableTime `json:"created"`
44+
Updated *parseabletime.ParseableTime `json:"updated"`
45+
Deleted *parseabletime.ParseableTime `json:"deleted"`
46+
}{
47+
Mask: (*Mask)(r),
48+
}
49+
50+
if err := json.Unmarshal(b, &aux); err != nil {
51+
return err
52+
}
53+
54+
if aux.Created != nil {
55+
r.Created = (*time.Time)(aux.Created)
56+
}
57+
58+
if aux.Updated != nil {
59+
r.Updated = (*time.Time)(aux.Updated)
60+
}
61+
62+
if aux.Deleted != nil {
63+
r.Deleted = (*time.Time)(aux.Deleted)
64+
}
65+
66+
return nil
67+
}
68+
69+
// RuleSetCreateOptions fields accepted by CreateRuleSet.
70+
type RuleSetCreateOptions struct {
71+
Label string `json:"label"`
72+
Description string `json:"description,omitempty"`
73+
Type FirewallRuleSetType `json:"type"`
74+
Rules []FirewallRule `json:"rules"`
75+
}
76+
77+
// RuleSetUpdateOptions fields accepted by UpdateRuleSet.
78+
// Omit a top-level field to leave it unchanged. If Rules is provided, it
79+
// replaces the entire ordered rules array.
80+
type RuleSetUpdateOptions struct {
81+
Label *string `json:"label,omitempty"`
82+
Description *string `json:"description,omitempty"`
83+
Rules *[]FirewallRule `json:"rules,omitempty"`
84+
}
85+
86+
// ListFirewallRuleSets returns a paginated list of Rule Sets.
87+
// Supports filtering (e.g., by label) via ListOptions.Filter.
88+
func (c *Client) ListFirewallRuleSets(ctx context.Context, opts *ListOptions) ([]RuleSet, error) {
89+
return getPaginatedResults[RuleSet](ctx, c, "networking/firewalls/rulesets", opts)
90+
}
91+
92+
// CreateFirewallRuleSet creates a new Rule Set.
93+
func (c *Client) CreateFirewallRuleSet(ctx context.Context, opts RuleSetCreateOptions) (*RuleSet, error) {
94+
return doPOSTRequest[RuleSet](ctx, c, "networking/firewalls/rulesets", opts)
95+
}
96+
97+
// GetFirewallRuleSet fetches a Rule Set by ID.
98+
func (c *Client) GetFirewallRuleSet(ctx context.Context, rulesetID int) (*RuleSet, error) {
99+
e := formatAPIPath("networking/firewalls/rulesets/%d", rulesetID)
100+
return doGETRequest[RuleSet](ctx, c, e)
101+
}
102+
103+
// UpdateFirewallRuleSet updates a Rule Set by ID.
104+
func (c *Client) UpdateFirewallRuleSet(ctx context.Context, rulesetID int, opts RuleSetUpdateOptions) (*RuleSet, error) {
105+
e := formatAPIPath("networking/firewalls/rulesets/%d", rulesetID)
106+
return doPUTRequest[RuleSet](ctx, c, e, opts)
107+
}
108+
109+
// DeleteFirewallRuleSet deletes a Rule Set by ID.
110+
func (c *Client) DeleteFirewallRuleSet(ctx context.Context, rulesetID int) error {
111+
e := formatAPIPath("networking/firewalls/rulesets/%d", rulesetID)
112+
return doDELETERequest(ctx, c, e)
113+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module github.com/linode/linodego
22

33
require (
4-
github.com/go-resty/resty/v2 v2.17.0
4+
github.com/go-resty/resty/v2 v2.17.1
55
github.com/google/go-cmp v0.7.0
66
github.com/google/go-querystring v1.1.0
77
github.com/jarcoal/httpmock v1.4.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=
4-
github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
3+
github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4=
4+
github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
55
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
66
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
77
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=

instance_configs.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type InstanceConfigDevice struct {
3434

3535
// InstanceConfigDeviceMap contains SDA-SDH InstanceConfigDevice settings
3636
type InstanceConfigDeviceMap struct {
37+
// sda-sdz
3738
SDA *InstanceConfigDevice `json:"sda,omitempty"`
3839
SDB *InstanceConfigDevice `json:"sdb,omitempty"`
3940
SDC *InstanceConfigDevice `json:"sdc,omitempty"`
@@ -42,6 +43,66 @@ type InstanceConfigDeviceMap struct {
4243
SDF *InstanceConfigDevice `json:"sdf,omitempty"`
4344
SDG *InstanceConfigDevice `json:"sdg,omitempty"`
4445
SDH *InstanceConfigDevice `json:"sdh,omitempty"`
46+
SDI *InstanceConfigDevice `json:"sdi,omitempty"`
47+
SDJ *InstanceConfigDevice `json:"sdj,omitempty"`
48+
SDK *InstanceConfigDevice `json:"sdk,omitempty"`
49+
SDL *InstanceConfigDevice `json:"sdl,omitempty"`
50+
SDM *InstanceConfigDevice `json:"sdm,omitempty"`
51+
SDN *InstanceConfigDevice `json:"sdn,omitempty"`
52+
SDO *InstanceConfigDevice `json:"sdo,omitempty"`
53+
SDP *InstanceConfigDevice `json:"sdp,omitempty"`
54+
SDQ *InstanceConfigDevice `json:"sdq,omitempty"`
55+
SDR *InstanceConfigDevice `json:"sdr,omitempty"`
56+
SDS *InstanceConfigDevice `json:"sds,omitempty"`
57+
SDT *InstanceConfigDevice `json:"sdt,omitempty"`
58+
SDU *InstanceConfigDevice `json:"sdu,omitempty"`
59+
SDV *InstanceConfigDevice `json:"sdv,omitempty"`
60+
SDW *InstanceConfigDevice `json:"sdw,omitempty"`
61+
SDX *InstanceConfigDevice `json:"sdx,omitempty"`
62+
SDY *InstanceConfigDevice `json:"sdy,omitempty"`
63+
SDZ *InstanceConfigDevice `json:"sdz,omitempty"`
64+
65+
// sdaa-sdaz
66+
SDAA *InstanceConfigDevice `json:"sdaa,omitempty"`
67+
SDAB *InstanceConfigDevice `json:"sdab,omitempty"`
68+
SDAC *InstanceConfigDevice `json:"sdac,omitempty"`
69+
SDAD *InstanceConfigDevice `json:"sdad,omitempty"`
70+
SDAE *InstanceConfigDevice `json:"sdae,omitempty"`
71+
SDAF *InstanceConfigDevice `json:"sdaf,omitempty"`
72+
SDAG *InstanceConfigDevice `json:"sdag,omitempty"`
73+
SDAH *InstanceConfigDevice `json:"sdah,omitempty"`
74+
SDAI *InstanceConfigDevice `json:"sdai,omitempty"`
75+
SDAJ *InstanceConfigDevice `json:"sdaj,omitempty"`
76+
SDAK *InstanceConfigDevice `json:"sdak,omitempty"`
77+
SDAL *InstanceConfigDevice `json:"sdal,omitempty"`
78+
SDAM *InstanceConfigDevice `json:"sdam,omitempty"`
79+
SDAN *InstanceConfigDevice `json:"sdan,omitempty"`
80+
SDAO *InstanceConfigDevice `json:"sdao,omitempty"`
81+
SDAP *InstanceConfigDevice `json:"sdap,omitempty"`
82+
SDAQ *InstanceConfigDevice `json:"sdaq,omitempty"`
83+
SDAR *InstanceConfigDevice `json:"sdar,omitempty"`
84+
SDAS *InstanceConfigDevice `json:"sdas,omitempty"`
85+
SDAT *InstanceConfigDevice `json:"sdat,omitempty"`
86+
SDAU *InstanceConfigDevice `json:"sdau,omitempty"`
87+
SDAV *InstanceConfigDevice `json:"sdav,omitempty"`
88+
SDAW *InstanceConfigDevice `json:"sdaw,omitempty"`
89+
SDAX *InstanceConfigDevice `json:"sdax,omitempty"`
90+
SDAY *InstanceConfigDevice `json:"sday,omitempty"`
91+
SDAZ *InstanceConfigDevice `json:"sdaz,omitempty"`
92+
93+
// sdba-sdbl
94+
SDBA *InstanceConfigDevice `json:"sdba,omitempty"`
95+
SDBB *InstanceConfigDevice `json:"sdbb,omitempty"`
96+
SDBC *InstanceConfigDevice `json:"sdbc,omitempty"`
97+
SDBD *InstanceConfigDevice `json:"sdbd,omitempty"`
98+
SDBE *InstanceConfigDevice `json:"sdbe,omitempty"`
99+
SDBF *InstanceConfigDevice `json:"sdbf,omitempty"`
100+
SDBG *InstanceConfigDevice `json:"sdbg,omitempty"`
101+
SDBH *InstanceConfigDevice `json:"sdbh,omitempty"`
102+
SDBI *InstanceConfigDevice `json:"sdbi,omitempty"`
103+
SDBJ *InstanceConfigDevice `json:"sdbj,omitempty"`
104+
SDBK *InstanceConfigDevice `json:"sdbk,omitempty"`
105+
SDBL *InstanceConfigDevice `json:"sdbl,omitempty"`
45106
}
46107

47108
// InstanceConfigHelpers are Instance Config options that control Linux distribution specific tweaks

k8s/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/go-openapi/jsonpointer v0.19.6 // indirect
1515
github.com/go-openapi/jsonreference v0.20.2 // indirect
1616
github.com/go-openapi/swag v0.22.3 // indirect
17-
github.com/go-resty/resty/v2 v2.17.0 // indirect
17+
github.com/go-resty/resty/v2 v2.17.1 // indirect
1818
github.com/gogo/protobuf v1.3.2 // indirect
1919
github.com/golang/protobuf v1.5.4 // indirect
2020
github.com/google/gnostic-models v0.6.8 // indirect

k8s/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2Kv
1212
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
1313
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
1414
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
15-
github.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=
16-
github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
15+
github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4=
16+
github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
1717
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
1818
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
1919
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=

prefixlists.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package linodego
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"time"
7+
8+
"github.com/linode/linodego/internal/parseabletime"
9+
)
10+
11+
// PrefixList represents a network prefix list returned by the API.
12+
type PrefixList struct {
13+
ID int `json:"id"`
14+
Name string `json:"name"`
15+
Description string `json:"description"`
16+
Visibility string `json:"visibility"`
17+
SourcePrefixListID *int `json:"source_prefixlist_id"`
18+
IPv4 *[]string `json:"ipv4"`
19+
IPv6 *[]string `json:"ipv6"`
20+
Version int `json:"version"`
21+
22+
Created *time.Time `json:"-"`
23+
Updated *time.Time `json:"-"`
24+
Deleted *time.Time `json:"-"`
25+
}
26+
27+
// UnmarshalJSON implements custom timestamp parsing for PrefixList values.
28+
func (p *PrefixList) UnmarshalJSON(data []byte) error {
29+
type Mask PrefixList
30+
31+
aux := struct {
32+
*Mask
33+
34+
Created *parseabletime.ParseableTime `json:"created"`
35+
Updated *parseabletime.ParseableTime `json:"updated"`
36+
Deleted *parseabletime.ParseableTime `json:"deleted"`
37+
}{
38+
Mask: (*Mask)(p),
39+
}
40+
41+
if err := json.Unmarshal(data, &aux); err != nil {
42+
return err
43+
}
44+
45+
p.Created = (*time.Time)(aux.Created)
46+
p.Updated = (*time.Time)(aux.Updated)
47+
p.Deleted = (*time.Time)(aux.Deleted)
48+
49+
return nil
50+
}
51+
52+
// ListPrefixLists returns a paginated collection of Prefix Lists.
53+
func (c *Client) ListPrefixLists(ctx context.Context, opts *ListOptions) ([]PrefixList, error) {
54+
return getPaginatedResults[PrefixList](ctx, c, "networking/prefixlists", opts)
55+
}
56+
57+
// GetPrefixList fetches a single Prefix List by its ID.
58+
func (c *Client) GetPrefixList(ctx context.Context, id int) (*PrefixList, error) {
59+
endpoint := formatAPIPath("networking/prefixlists/%d", id)
60+
return doGETRequest[PrefixList](ctx, c, endpoint)
61+
}

test/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ require (
2020
github.com/go-openapi/jsonpointer v0.19.6 // indirect
2121
github.com/go-openapi/jsonreference v0.20.2 // indirect
2222
github.com/go-openapi/swag v0.22.3 // indirect
23-
github.com/go-resty/resty/v2 v2.17.0 // indirect
23+
github.com/go-resty/resty/v2 v2.17.1 // indirect
2424
github.com/gogo/protobuf v1.3.2 // indirect
2525
github.com/golang/protobuf v1.5.4 // indirect
2626
github.com/google/gnostic-models v0.6.8 // indirect

0 commit comments

Comments
 (0)