Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6fd4b61
Implement changes for account APIs (#715)
zliang-akamai Apr 22, 2025
245b2f0
Added support for `networking/` and `vpcs/` endpoint changs for VPC L…
ezilber-akamai Apr 23, 2025
724d21a
Add support for firewall template (#726)
zliang-akamai Apr 26, 2025
db8dfbb
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Apr 30, 2025
134c0a4
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai May 5, 2025
75f0190
Adding VPC Enhanced Interfaces (#724)
jriddle-linode May 16, 2025
bc899d5
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai May 16, 2025
e7ea104
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai May 19, 2025
c1db12c
Linode instance changes for Linode Interfaces (#723)
zliang-akamai May 20, 2025
70290fa
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai May 20, 2025
0d06dc8
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai May 21, 2025
d4d1ba1
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai May 30, 2025
625aa9a
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jun 2, 2025
e0764ae
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jun 5, 2025
85b88c3
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jun 5, 2025
c7cde18
Make default firewall IDs pointers (#765)
zliang-akamai Jun 7, 2025
8b04c5d
Firewall Related integration tests (#760)
vshanthe Jun 10, 2025
1cab31f
Linode Interfaces: Consolidate InterfaceGeneration and LinodeInterfac…
lgarber-akamai Jun 11, 2025
517572e
Fix firewall settings tests (#772)
zliang-akamai Jun 24, 2025
b46e5cb
Make interface optional fields pointers (#773)
zliang-akamai Jun 24, 2025
d3eda89
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jun 24, 2025
bf3104f
Rename Label field to VLANLabel to align with API attribute naming
zliang-akamai Jul 1, 2025
2f2f21d
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jul 2, 2025
4954b32
golangci-lint run --fix
zliang-akamai Jul 2, 2025
5811bfe
fix a comment
zliang-akamai Jul 2, 2025
8909b3b
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jul 8, 2025
61afc8e
Nullable default firewall ids (#776)
zliang-akamai Jul 9, 2025
a10839e
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jul 18, 2025
7075370
Add future breaking change notice for ConfigID in VPC IP (#775)
zliang-akamai Jul 24, 2025
905a938
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Jul 28, 2025
40095f4
Make FirewallID in linode interface creation options a double ptr (#792)
zliang-akamai Aug 7, 2025
e657b7d
Merge branch 'main' into proj/vpc-linodes-enhanced-interfaces
zliang-akamai Aug 20, 2025
15d68e3
Make `address` of interface ipv4 optional (#804)
zliang-akamai Aug 21, 2025
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
17 changes: 17 additions & 0 deletions account_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import (
"context"
)

type InterfacesForNewLinodes string

const (
LegacyConfigOnly InterfacesForNewLinodes = "legacy_config_only"
LegacyConfigDefaultButLinodeAllowed InterfacesForNewLinodes = "legacy_config_default_but_linode_allowed"
LinodeDefaultButLegacyConfigAllowed InterfacesForNewLinodes = "linode_default_but_legacy_config_allowed"
LinodeOnly InterfacesForNewLinodes = "linode_only"
)

// AccountSettings are the account wide flags or plans that effect new resources
type AccountSettings struct {
// The default backups enrollment status for all new Linodes for all users on the account. When enabled, backups are mandatory per instance.
Expand All @@ -21,6 +30,10 @@ type AccountSettings struct {
// A string like "disabled", "suspended", or "active" describing the status of this account’s Object Storage service enrollment.
ObjectStorage *string `json:"object_storage"`

// NOTE: Interfaces for new linode setting may not currently be available to all users.
// A new configuration flag defines whether new Linodes can use Linode and/or legacy config interfaces.
InterfacesForNewLinodes InterfacesForNewLinodes `json:"interfaces_for_new_linodes"`

// The slug of the maintenance policy associated with the account.
// NOTE: MaintenancePolicy can only be used with v4beta.
MaintenancePolicy string `json:"maintenance_policy"`
Expand All @@ -34,6 +47,10 @@ type AccountSettingsUpdateOptions struct {
// The default network helper setting for all new Linodes and Linode Configs for all users on the account.
NetworkHelper *bool `json:"network_helper,omitempty"`

// NOTE: Interfaces for new linode setting may not currently be available to all users.
// A new configuration flag defines whether new Linodes can use Linode and/or legacy config interfaces.
InterfacesForNewLinodes *InterfacesForNewLinodes `json:"interfaces_for_new_linodes"`

// The slug of the maintenance policy to set the account to.
// NOTE: MaintenancePolicy can only be used with v4beta.
MaintenancePolicy *string `json:"maintenance_policy,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions firewall_devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type FirewallDeviceType string
const (
FirewallDeviceLinode FirewallDeviceType = "linode"
FirewallDeviceNodeBalancer FirewallDeviceType = "nodebalancer"
FirewallDeviceInterface FirewallDeviceType = "interface"
)

// FirewallDevice represents a device governed by a Firewall
Expand Down
23 changes: 23 additions & 0 deletions firewall_templates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package linodego

import (
"context"
)

type FirewallTemplate struct {
Slug string `json:"slug"`
Rules FirewallRuleSet `json:"rules"`
}

// NOTE: This feature may not currently be available to all users.
// GetFirewallTemplate gets a FirewallTemplate given a slug.
func (c *Client) GetFirewallTemplate(ctx context.Context, slug string) (*FirewallTemplate, error) {
e := formatAPIPath("networking/firewalls/templates/%s", slug)
return doGETRequest[FirewallTemplate](ctx, c, e)
}

// NOTE: This feature may not currently be available to all users.
// ListFirewallTemplates gets all available firewall templates for the account.
func (c *Client) ListFirewallTemplates(ctx context.Context, opts *ListOptions) ([]FirewallTemplate, error) {
return getPaginatedResults[FirewallTemplate](ctx, c, "networking/firewalls/templates", opts)
}
36 changes: 36 additions & 0 deletions firewalls.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Firewall struct {
type DevicesCreationOptions struct {
Linodes []int `json:"linodes,omitempty"`
NodeBalancers []int `json:"nodebalancers,omitempty"`
Interfaces []int `json:"interfaces,omitempty"`
}

// FirewallCreateOptions fields are those accepted by CreateFirewall
Expand All @@ -50,6 +51,31 @@ type FirewallUpdateOptions struct {
Tags *[]string `json:"tags,omitempty"`
}

// FirewallSettings represents the default firewalls for Linodes,
// Linode VPC and public interfaces, and NodeBalancers.
type FirewallSettings struct {
DefaultFirewallIDs DefaultFirewallIDs `json:"default_firewall_ids"`
}

type DefaultFirewallIDs struct {
Linode *int `json:"linode"`
NodeBalancer *int `json:"nodebalancer"`
PublicInterface *int `json:"public_interface"`
VPCInterface *int `json:"vpc_interface"`
}

// FirewallSettingsUpdateOptions is an options struct used when Updating FirewallSettings
type FirewallSettingsUpdateOptions struct {
DefaultFirewallIDs *DefaultFirewallIDsOptions `json:"default_firewall_ids,omitempty"`
}

type DefaultFirewallIDsOptions struct {
Linode **int `json:"linode,omitempty"`
NodeBalancer **int `json:"nodebalancer,omitempty"`
PublicInterface **int `json:"public_interface,omitempty"`
VPCInterface **int `json:"vpc_interface,omitempty"`
}

// GetUpdateOptions converts a Firewall to FirewallUpdateOptions for use in Client.UpdateFirewall.
func (f *Firewall) GetUpdateOptions() FirewallUpdateOptions {
return FirewallUpdateOptions{
Expand Down Expand Up @@ -109,3 +135,13 @@ func (c *Client) DeleteFirewall(ctx context.Context, firewallID int) error {
e := formatAPIPath("networking/firewalls/%d", firewallID)
return doDELETERequest(ctx, c, e)
}

// GetFirewallSettings returns default firewalls for Linodes, Linode VPC and public interfaces, and NodeBalancers.
func (c *Client) GetFirewallSettings(ctx context.Context) (*FirewallSettings, error) {
return doGETRequest[FirewallSettings](ctx, c, "networking/firewalls/settings")
}

// UpdateFirewallSettings updates the default firewalls for Linodes, Linode VPC and public interfaces, and NodeBalancers.
func (c *Client) UpdateFirewallSettings(ctx context.Context, opts FirewallSettingsUpdateOptions) (*FirewallSettings, error) {
return doPUTRequest[FirewallSettings](ctx, c, "networking/firewalls/settings", opts)
}
27 changes: 15 additions & 12 deletions instance_ips.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,18 @@ type InstanceIPv4Response struct {

// InstanceIP represents an Instance IP with additional DNS and networking details
type InstanceIP struct {
Address string `json:"address"`
Gateway string `json:"gateway"`
SubnetMask string `json:"subnet_mask"`
Prefix int `json:"prefix"`
Type InstanceIPType `json:"type"`
Public bool `json:"public"`
RDNS string `json:"rdns"`
LinodeID int `json:"linode_id"`
Region string `json:"region"`
VPCNAT1To1 *InstanceIPNAT1To1 `json:"vpc_nat_1_1"`
Reserved bool `json:"reserved"`
Address string `json:"address"`
Gateway string `json:"gateway"`
SubnetMask string `json:"subnet_mask"`
Prefix int `json:"prefix"`
Type InstanceIPType `json:"type"`
Public bool `json:"public"`
RDNS string `json:"rdns"`
LinodeID int `json:"linode_id"`
InterfaceID *int `json:"interface_id"`
Region string `json:"region"`
VPCNAT1To1 *InstanceIPNAT1To1 `json:"vpc_nat_1_1"`
Reserved bool `json:"reserved"`
}

// VPCIP represents a private IP address in a VPC subnet with additional networking details
Expand All @@ -47,8 +48,10 @@ type VPCIP struct {
NAT1To1 *string `json:"nat_1_1"`
VPCID int `json:"vpc_id"`
SubnetID int `json:"subnet_id"`
ConfigID int `json:"config_id"`
InterfaceID int `json:"interface_id"`

// The type of this field will be made a pointer in the next major release of linodego.
ConfigID int `json:"config_id"`
}

// InstanceIPv6Response contains the IPv6 addresses and ranges for an Instance
Expand Down
121 changes: 104 additions & 17 deletions instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package linodego
import (
"context"
"encoding/json"
"fmt"
"net"
"time"

Expand Down Expand Up @@ -180,23 +181,32 @@ type InstancePasswordResetOptions struct {

// InstanceCreateOptions require only Region and Type
type InstanceCreateOptions struct {
Region string `json:"region"`
Type string `json:"type"`
Label string `json:"label,omitempty"`
RootPass string `json:"root_pass,omitempty"`
AuthorizedKeys []string `json:"authorized_keys,omitempty"`
AuthorizedUsers []string `json:"authorized_users,omitempty"`
StackScriptID int `json:"stackscript_id,omitempty"`
StackScriptData map[string]string `json:"stackscript_data,omitempty"`
BackupID int `json:"backup_id,omitempty"`
Image string `json:"image,omitempty"`
Interfaces []InstanceConfigInterfaceCreateOptions `json:"interfaces,omitempty"`
BackupsEnabled bool `json:"backups_enabled,omitempty"`
PrivateIP bool `json:"private_ip,omitempty"`
Tags []string `json:"tags,omitempty"`
Metadata *InstanceMetadataOptions `json:"metadata,omitempty"`
FirewallID int `json:"firewall_id,omitempty"`
InterfaceGeneration InterfaceGeneration `json:"interface_generation,omitempty"`
Region string `json:"region"`
Type string `json:"type"`
Label string `json:"label,omitempty"`
RootPass string `json:"root_pass,omitempty"`
AuthorizedKeys []string `json:"authorized_keys,omitempty"`
AuthorizedUsers []string `json:"authorized_users,omitempty"`
StackScriptID int `json:"stackscript_id,omitempty"`
StackScriptData map[string]string `json:"stackscript_data,omitempty"`
BackupID int `json:"backup_id,omitempty"`
Image string `json:"image,omitempty"`
BackupsEnabled bool `json:"backups_enabled,omitempty"`
PrivateIP bool `json:"private_ip,omitempty"`
NetworkHelper *bool `json:"network_helper,omitempty"`
Tags []string `json:"tags,omitempty"`
Metadata *InstanceMetadataOptions `json:"metadata,omitempty"`
FirewallID int `json:"firewall_id,omitempty"`
InterfaceGeneration InterfaceGeneration `json:"interface_generation,omitempty"`

// Linode Interfaces to create the new instance with.
// Conflicts with Interfaces.
// NOTE: Linode Interfaces may not currently be available to all users.
LinodeInterfaces []LinodeInterfaceCreateOptions `json:"-"`

// Legacy (config) Interfaces to create the new instance with.
// Conflicts with LinodeInterfaces.
Interfaces []InstanceConfigInterfaceCreateOptions `json:"-"`

// NOTE: Disk encryption may not currently be available to all users.
DiskEncryption InstanceDiskEncryption `json:"disk_encryption,omitempty"`
Expand Down Expand Up @@ -238,6 +248,83 @@ type InstanceUpdateOptions struct {
MaintenancePolicy *string `json:"maintenance_policy,omitempty"`
}

// MarshalJSON contains logic necessary to populate the `interfaces` field of
// InstanceCreateOptions depending on whether Interfaces or LinodeInterfaces
// is specified.
func (i InstanceCreateOptions) MarshalJSON() ([]byte, error) {
type Mask InstanceCreateOptions

resultData := struct {
*Mask

Interfaces any `json:"interfaces,omitempty"`
}{
Mask: (*Mask)(&i),
Interfaces: nil,
}

if i.Interfaces != nil && i.LinodeInterfaces != nil {
return nil, fmt.Errorf("fields Interfaces and LinodeInterfaces cannot be specified together")
}

if i.Interfaces != nil {
resultData.Interfaces = i.Interfaces
}

if i.LinodeInterfaces != nil {
resultData.Interfaces = i.LinodeInterfaces
}

return json.Marshal(resultData)
}

// UnmarshalJSON contains logic necessary to populate the Interfaces field
// depending on the value of interface_generation.
func (i *InstanceCreateOptions) UnmarshalJSON(b []byte) error {
type Mask InstanceCreateOptions

p := struct {
*Mask

GenericInterfaces any `json:"interfaces,omitempty"`
}{
Mask: (*Mask)(i),
}

if err := json.Unmarshal(b, &p); err != nil {
return err
}

if p.GenericInterfaces == nil {
// No interfaces were given - nothing to do here.
return nil
}

if i.InterfaceGeneration == GenerationLinode {
data := struct {
Interfaces []LinodeInterfaceCreateOptions `json:"interfaces"`
}{}

err := json.Unmarshal(b, &data)
i.LinodeInterfaces = data.Interfaces

return err
}

if i.InterfaceGeneration == GenerationLegacyConfig {
data := struct {
Interfaces []InstanceConfigInterfaceCreateOptions `json:"interfaces"`
}{}

err := json.Unmarshal(b, &data)
i.Interfaces = data.Interfaces

return err
}

return fmt.Errorf("cannot unmarshal interfaces without valid value for interface_generation")
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (i *Instance) UnmarshalJSON(b []byte) error {
type Mask Instance
Expand Down
Loading