Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7fb2779
init
yec-akamai Jun 8, 2026
23fd365
address comments for omitzero
yec-akamai Jun 16, 2026
54740a7
Refactor vpcCreateOptionsCheck to use createOpts
mawilk90 Jun 12, 2026
04c6cc4
Update fixtures
mawilk90 Jun 12, 2026
57a5d2c
Create int tests for VPC and VPC Subnet with RDMA type
mawilk90 Jun 12, 2026
f3c7279
Add assertion for regular VPC type
mawilk90 Jun 12, 2026
de55c0c
Update vpcCreateOptionsCheck to use require
mawilk90 Jun 25, 2026
f2cb55d
Update error msg in TestVPC_Update_Invalid
mawilk90 Jun 26, 2026
0b4a3bd
Update fixtures for TestVPC_List tests
mawilk90 Jun 26, 2026
96d0b8e
Update err msg for invalid label in VPC and Subnet tests
mawilk90 Jun 26, 2026
a2453fd
Add negative TCs for RDMA VPC with IPv6
mawilk90 Jun 26, 2026
acc5ac4
Create int test for Linode instance with RDMA interfaces
mawilk90 Jun 29, 2026
9f57dc0
Refactor createInstanceWithLinodeInterfaces to use LinodeInstanceInte…
mawilk90 Jun 30, 2026
bc18d1f
Refactor TestInstance_CreateWithRDMAVPCInterfaces to use createInstan…
mawilk90 Jun 30, 2026
5a31809
Small refactor & fixture update
mawilk90 Jun 30, 2026
c9872fa
Linter
mawilk90 Jul 7, 2026
8adc5a6
Comment out TestInstance_CreateWithRDMAVPCInterfaces due to infra cha…
mawilk90 Jul 7, 2026
951445c
Update comment for TestVPC_WithRDMATypeIPv6_Fail
mawilk90 Jul 7, 2026
763321a
Update fixtures for TestVPC_CreateGet_smoke and TestVPC_Subnet_Create
mawilk90 Jul 8, 2026
fe27290
Update fixtures and TestVPC_IPv4Ranges to return 4 vals in createVPC
mawilk90 Jul 9, 2026
c5f6f4f
Update TestInstance_CreateWithRDMAVPCInterfaces
mawilk90 Jul 13, 2026
85c8f35
Address Copilot remarks
mawilk90 Jul 14, 2026
8a11e62
Uncomment TestVPC_WithRDMATypeAndIPv6_Fail as IPv6 is blocked already
mawilk90 Jul 14, 2026
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
855 changes: 585 additions & 270 deletions test/integration/fixtures/TestInstance_CreateWithLinodeInterfaces.yaml

Large diffs are not rendered by default.

385 changes: 53 additions & 332 deletions test/integration/fixtures/TestVPC_CreateGet.yaml

Large diffs are not rendered by default.

538 changes: 306 additions & 232 deletions test/integration/fixtures/TestVPC_Create_Invalid.yaml

Large diffs are not rendered by default.

552 changes: 314 additions & 238 deletions test/integration/fixtures/TestVPC_List.yaml

Large diffs are not rendered by default.

618 changes: 348 additions & 270 deletions test/integration/fixtures/TestVPC_ListAllIPAddresses.yaml

Large diffs are not rendered by default.

611 changes: 343 additions & 268 deletions test/integration/fixtures/TestVPC_ListAllIPv6Addresses.yaml

Large diffs are not rendered by default.

616 changes: 347 additions & 269 deletions test/integration/fixtures/TestVPC_ListIPAddresses.yaml

Large diffs are not rendered by default.

604 changes: 340 additions & 264 deletions test/integration/fixtures/TestVPC_ListIPv6Addresses.yaml

Large diffs are not rendered by default.

369 changes: 45 additions & 324 deletions test/integration/fixtures/TestVPC_Subnet_Create.yaml

Large diffs are not rendered by default.

391 changes: 54 additions & 337 deletions test/integration/fixtures/TestVPC_Subnet_Create_Invalid_data.yaml

Large diffs are not rendered by default.

562 changes: 319 additions & 243 deletions test/integration/fixtures/TestVPC_Subnet_Update_Invalid_Label.yaml

Large diffs are not rendered by default.

556 changes: 316 additions & 240 deletions test/integration/fixtures/TestVPC_Update.yaml

Large diffs are not rendered by default.

557 changes: 316 additions & 241 deletions test/integration/fixtures/TestVPC_Update_Invalid.yaml

Large diffs are not rendered by default.

256 changes: 200 additions & 56 deletions test/integration/instance_interfaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,113 @@ package integration
import (
"context"
"testing"
"time"

"github.com/linode/linodego/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func createInstanceWithLinodeInterfaces(
t *testing.T,
client *linodego.Client,
enableCloudFirewall bool,
interfaces []linodego.LinodeInterfaceCreateOptions,
interfaces []linodego.LinodeInstanceInterfaceCreateOptions,
modifiers ...instanceModifier,
) (*linodego.Instance, func(), error) {
if t != nil {
t.Helper()
}
t.Helper()

createOpts := linodego.InstanceCreateOptions{
Label: "go-test-intf-" + randLabel(),
RootPass: randPassword(),
Region: getRegionsWithCaps(t, client, []linodego.RegionCapability{linodego.CapabilityLinodeInterfaces})[0],
Type: "g6-nanode-1",
Image: "linode/debian12",
Booted: linodego.Pointer(false),
InterfaceGeneration: linodego.GenerationLinode,
LinodeInterfaces: interfaces,
Label: "go-test-intf-" + randLabel(),
RootPass: randPassword(),
Region: getRegionsWithCaps(t, client, []linodego.RegionCapability{linodego.CapabilityLinodeInterfaces})[0],
Type: "g6-nanode-1",
Image: "linode/debian12",
Booted: linodego.Pointer(false),
InterfaceGeneration: linodego.GenerationLinode,
LinodeInstanceInterfaces: interfaces,
}

if enableCloudFirewall {
for i := range createOpts.LinodeInterfaces {
createOpts.LinodeInterfaces[i].FirewallID = linodego.Pointer(firewallID)
for i := range createOpts.LinodeInstanceInterfaces {
createOpts.LinodeInstanceInterfaces[i].FirewallID = linodego.Pointer(firewallID)
}
}

for _, modifier := range modifiers {
modifier(client, &createOpts)
}
instance, err := client.CreateInstance(context.Background(), createOpts)
require.NoErrorf(t, err, "Error creating test instance: %s", err)

teardown := func() {
if err := client.DeleteInstance(context.Background(), instance.ID); err != nil {
if t != nil {
t.Errorf("error deleting test Instance: %s", err)
}
// Use a fresh, independent context
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second)
defer cancel()

// Wait for the instance to be deleted
p, err := client.NewEventPoller(
context.Background(), instance.ID, linodego.EntityLinode, linodego.ActionLinodeDelete,
)
require.NoErrorf(t, err, "Error creating event poller for instance deletion: %s", err)

err = client.DeleteInstance(context.Background(), instance.ID)
require.NoErrorf(t, err, "Error deleting test instance: %s", err)

// NOTE: The linode_delete event is sometimes reported as "failed" by the
// API even though the instance is actually deleted successfully.
// Rather than treating that as fatal, we log it and fall back to
// GetInstance returning a 404 as the authoritative check that the instance
// was actually deleted.
event, err := p.WaitForFinished(ctx)
if err != nil {
t.Logf("Warning: Error waiting for instance deletion event (instance deletion will still be verified directly): %s", err)
} else if event.Action != linodego.ActionLinodeDelete {
t.Errorf("Expected event action %s, got %s", linodego.ActionLinodeDelete, event.Action)
}

_, err = client.GetInstance(context.Background(), instance.ID)
require.Truef(t, linodego.IsNotFound(err), "Expected instance to be deleted (404), got: %v", err)
}
return instance, teardown, err
}

func setupInstanceWithLinodeInterfaces(
t *testing.T,
fixturesYaml string,
EnableCloudFirewall bool,
interfaces []linodego.LinodeInterfaceCreateOptions,
modifiers ...instanceModifier,
) (*linodego.Client, *linodego.Instance, func(), error) {
if t != nil {
t.Helper()
}
client, fixtureTeardown := createTestClient(t, fixturesYaml)
func createVPCWithSubnetAndType(t *testing.T, client *linodego.Client, region string, vpcType linodego.VPCType) (
*linodego.VPC,
*linodego.VPCSubnet,
func(),
error,
) {
vpc, subnet, teardown, err := createVPCWithSubnet(
t,
client,
func(c *linodego.Client, opts *linodego.VPCCreateOptions) {
opts.Region = region
opts.VPCType = vpcType
},
)
return vpc, subnet, teardown, err
}

instance, teardownInstance, err := createInstanceWithLinodeInterfaces(t, client, EnableCloudFirewall, interfaces, modifiers...)
if err != nil {
t.Errorf("failed to create test instance: %s", err)
}
func prepareMultipleRDMAInterfaces(amount int, subnet *linodego.VPCSubnet) []linodego.LinodeInstanceInterfaceCreateOptions {
interfaces := make([]linodego.LinodeInstanceInterfaceCreateOptions, 0)

teardown := func() {
teardownInstance()
fixtureTeardown()
for i := 1; i <= amount; i++ {
interfaces = append(interfaces, linodego.LinodeInstanceInterfaceCreateOptions{
LinodeInterfaceCreateOptions: linodego.LinodeInterfaceCreateOptions{
FirewallID: linodego.Pointer(-1),
},
RDMAVPC: &linodego.RDMAVPCInterfaceCreateOptions{
SubnetID: subnet.ID,
IPv4: linodego.RDMAVPCInterfaceIPv4Options{
Addresses: []linodego.RDMAVPCInterfaceIPv4AddressOptions{
{Address: "auto", Primary: linodego.Pointer(true)},
},
},
},
})
}

return client, instance, teardown, err
return interfaces
}

func TestInstance_CreateWithLinodeInterfaces(
Expand All @@ -97,31 +135,35 @@ func TestInstance_CreateWithLinodeInterfaces(
t,
client,
true,
[]linodego.LinodeInterfaceCreateOptions{
[]linodego.LinodeInstanceInterfaceCreateOptions{
{
FirewallID: linodego.Pointer(firewallID),
Public: &linodego.PublicInterfaceCreateOptions{
IPv4: &linodego.PublicInterfaceIPv4CreateOptions{
Addresses: []linodego.PublicInterfaceIPv4AddressCreateOptions{
{
Address: linodego.Pointer("auto"),
Primary: linodego.Pointer(true),
LinodeInterfaceCreateOptions: linodego.LinodeInterfaceCreateOptions{
FirewallID: linodego.Pointer(firewallID),
Public: &linodego.PublicInterfaceCreateOptions{
IPv4: &linodego.PublicInterfaceIPv4CreateOptions{
Addresses: []linodego.PublicInterfaceIPv4AddressCreateOptions{
{
Address: linodego.Pointer("auto"),
Primary: linodego.Pointer(true),
},
},
},
IPv6: &linodego.PublicInterfaceIPv6CreateOptions{},
},
IPv6: &linodego.PublicInterfaceIPv6CreateOptions{},
},
},
{
FirewallID: linodego.Pointer(firewallID),
VPC: &linodego.VPCInterfaceCreateOptions{
SubnetID: vpcSubnet.ID,
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{
{
Address: linodego.Pointer("auto"),
Primary: linodego.Pointer(true),
NAT1To1Address: linodego.Pointer("auto"),
LinodeInterfaceCreateOptions: linodego.LinodeInterfaceCreateOptions{
FirewallID: linodego.Pointer(firewallID),
VPC: &linodego.VPCInterfaceCreateOptions{
SubnetID: vpcSubnet.ID,
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{
{
Address: linodego.Pointer("auto"),
Primary: linodego.Pointer(true),
NAT1To1Address: linodego.Pointer("auto"),
},
},
},
},
Expand All @@ -141,3 +183,105 @@ func TestInstance_CreateWithLinodeInterfaces(
t.Errorf("Expected a valid instance ID, got 0")
}
}

func TestInstance_CreateWithRDMAVPCInterfaces(t *testing.T) {
t.Skip("Skipping test because Linode with RDMA interfaces requires manual infra changes at the moment")

client, fixtureTeardown := createTestClient(t, "fixtures/TestInstance_CreateWithRDMAVPCInterfaces")
t.Cleanup(fixtureTeardown)

// GPUDirect RDMA capability not available for now
// testRegion := getRegionsWithCaps(t, client, []linodego.RegionCapability{linodego.CapabilityVPCs, linodego.CapabilityGPUDirectRDMA})[0]
testRegion := getRegionsWithCaps(t, client, []linodego.RegionCapability{linodego.CapabilityVPCs})[0]
interfaceCreateOptions := make([]linodego.LinodeInstanceInterfaceCreateOptions, 0)

// CREATE
_, vpcSubnet, vpcTeardown, err := createVPCWithSubnetAndType(t, client, testRegion, linodego.VPCTypeRegular)
require.NoErrorf(t, err, "Error creating Regular VPC with subnet: %s", err)
t.Cleanup(vpcTeardown)

_, vpcSubnetRDMA, vpcRDMATeardown, err := createVPCWithSubnetAndType(t, client, testRegion, linodego.VPCTypeRDMA)
require.NoErrorf(t, err, "Error creating RDMA VPC with subnet: %s", err)
t.Cleanup(vpcRDMATeardown)

_, vpcSubnetRDMAUpdate, vpcRDMAUpdateTeardown, err := createVPCWithSubnetAndType(t, client, testRegion, linodego.VPCTypeRDMA)
require.NoErrorf(t, err, "Error creating RDMA VPC with subnet: %s", err)
t.Cleanup(vpcRDMAUpdateTeardown)

// Include RDMA VPC interfaces
multiRDMAInterfaces := prepareMultipleRDMAInterfaces(8, vpcSubnetRDMA)
interfaceCreateOptions = append(interfaceCreateOptions, multiRDMAInterfaces...)

// Include (at least one) regular interface
interfaceCreateOptions = append(interfaceCreateOptions, linodego.LinodeInstanceInterfaceCreateOptions{
LinodeInterfaceCreateOptions: linodego.LinodeInterfaceCreateOptions{
VPC: &linodego.VPCInterfaceCreateOptions{
SubnetID: vpcSubnet.ID,
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{
{
Address: linodego.Pointer("auto"),
Primary: linodego.Pointer(true),
},
},
},
},
},
})

instance, instanceTeardown, err := createInstanceWithLinodeInterfaces(
t,
client,
false,
interfaceCreateOptions,
func(c *linodego.Client, opts *linodego.InstanceCreateOptions) {
opts.Label = "go-test-rdma-" + randLabel()
opts.RootPass = randPassword()
opts.Image = "linode/ubuntu24.04"
opts.Region = testRegion
// opts.Type = linodego.InstanceRDMAType
// opts.HostID = linodego.InstanceRDMAHostID
opts.InterfaceGeneration = linodego.GenerationLinode
opts.LinodeInstanceInterfaces = interfaceCreateOptions
},
)
require.NoErrorf(t, err, "Error creating instance with RDMA interfaces: %s", err)
t.Cleanup(instanceTeardown)

instance, err = client.WaitForInstanceStatus(
waitContext(t, 180*time.Second),
instance.ID,
linodego.InstanceOffline,
)
require.NoErrorf(t, err, "Error waiting for instance to be running: %s", err)

// READ
allInterfaces, err := client.ListInterfaces(context.Background(), instance.ID, nil)
require.NoErrorf(t, err, "Error listing interfaces for RDMA instance: %s", err)
assert.Equal(t, len(interfaceCreateOptions), len(allInterfaces), "Expected %d interfaces, got %d", len(interfaceCreateOptions), len(allInterfaces))

basicRDMAInterface := allInterfaces[0]
require.NotNil(t, basicRDMAInterface.RDMAVPC, "Expected interface to have RDMAVPC field populated")

// UPDATE
updateOpts := linodego.LinodeInterfaceUpdateOptions{
RDMAVPC: &linodego.RDMAVPCInterfaceUpdateOptions{
SubnetID: vpcSubnetRDMAUpdate.ID,
},
}
updatedRDMAInterface, err := client.UpdateInterface(context.Background(), instance.ID, basicRDMAInterface.ID, updateOpts)
require.NoErrorf(t, err, "Error updating RDMA interface: %s", err)
require.NotNil(t, updatedRDMAInterface.RDMAVPC, "Expected updated interface to have RDMAVPC field populated")
assert.Equal(t, basicRDMAInterface.ID, updatedRDMAInterface.ID, "Expected RDMA interface ID to remain the same after update")
assert.Equal(t, vpcSubnetRDMAUpdate.ID, updatedRDMAInterface.RDMAVPC.SubnetID, "Expected RDMA interface to be updated")

// DELETE
err = client.DeleteInterface(context.Background(), instance.ID, basicRDMAInterface.ID)
require.Error(t, err, "Expected error deleting RDMA interface from RDMA instance")

var e *linodego.Error
require.ErrorAsf(t, err, &e, "Expected error to be of type *linodego.Error, got: %T", err)
assert.Equal(t, 400, e.Code, "Expected error code 400, got: %d", e.Code)
expectedErrorMessage := "RDMA VPC Interfaces cannot be deleted"
assert.Contains(t, e.Message, expectedErrorMessage, "Expected error message to contain: %s, got: %s", expectedErrorMessage, e.Message)
}
2 changes: 1 addition & 1 deletion test/integration/lke_clusters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestLKECluster_Enterprise_BYOVPC_smoke(t *testing.T) {
require.Greater(t, len(regions), 0, "Error getting regions with required capabilities")

region := regions[0]
vpc, vpcTeardown, err := createVPC(t, client, []vpcModifier{func(l *linodego.Client, options *linodego.VPCCreateOptions) {
vpc, _, vpcTeardown, err := createVPC(t, client, []vpcModifier{func(l *linodego.Client, options *linodego.VPCCreateOptions) {
options.Region = region
options.IPv6 = []linodego.VPCCreateOptionsIPv6{
{
Expand Down
Loading
Loading