Skip to content

Commit cdddac1

Browse files
committed
add some fixes and validation for linode creation with new network interfaces
1 parent 3e45ba9 commit cdddac1

File tree

6 files changed

+103
-33
lines changed

6 files changed

+103
-33
lines changed

internal/controller/linodemachine_controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,10 @@ func (r *LinodeMachineReconciler) reconcilePreflightConfigure(ctx context.Contex
624624
Network: true,
625625
},
626626
}
627+
// LinodeInterfaces does not support network helpers, so we disable it
628+
if machineScope.LinodeMachine.Spec.LinodeInterfaces != nil {
629+
configData.Helpers.Network = false
630+
}
627631

628632
if machineScope.LinodeMachine.Spec.Configuration != nil && machineScope.LinodeMachine.Spec.Configuration.Kernel != "" {
629633
configData.Kernel = machineScope.LinodeMachine.Spec.Configuration.Kernel

internal/controller/linodemachine_controller_helpers.go

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,22 @@ func retryIfTransient(err error, logger logr.Logger) (ctrl.Result, error) {
7676
}
7777
return ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerRetryDelay}, nil
7878
}
79-
logger.Error(err, "unknown Linode API error")
80-
return ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerRetryDelay}, nil
79+
logger.Error(err, "non-retryable Linode API error, skipping requeue")
80+
return ctrl.Result{}, nil
8181
}
8282

8383
func fillCreateConfig(createConfig *linodego.InstanceCreateOptions, machineScope *scope.MachineScope) {
8484
if machineScope.LinodeMachine.Spec.PrivateIP != nil {
8585
createConfig.PrivateIP = *machineScope.LinodeMachine.Spec.PrivateIP
8686
} else {
87-
createConfig.PrivateIP = true
87+
if machineScope.LinodeMachine.Spec.LinodeInterfaces == nil {
88+
// Supported only for legacy network interfaces.
89+
createConfig.PrivateIP = true
90+
} else {
91+
// Network Helper is not supported for the new network interfaces.
92+
createConfig.NetworkHelper = ptr.To(false)
93+
createConfig.InterfaceGeneration = linodego.GenerationLinode
94+
}
8895
}
8996

9097
if createConfig.Tags == nil {
@@ -638,12 +645,21 @@ func getVPCLinodeInterfaceConfig(ctx context.Context, machineScope *scope.Machin
638645
}
639646

640647
// Check if a VPC interface already exists
641-
for i, netInterface := range linodeInterfaces {
648+
for iface, netInterface := range linodeInterfaces {
642649
if netInterface.VPC != nil {
643-
linodeInterfaces[i].VPC.SubnetID = subnetID
650+
linodeInterfaces[iface].VPC.SubnetID = subnetID
644651
// If IPv6 range config is not empty, add it to the interface configuration
645652
if !isVPCInterfaceIPv6ConfigEmpty(ipv6Config) {
646-
linodeInterfaces[i].VPC.IPv6 = ipv6Config
653+
linodeInterfaces[iface].VPC.IPv6 = ipv6Config
654+
}
655+
if netInterface.VPC.IPv4 == nil {
656+
linodeInterfaces[iface].VPC.IPv4 = &linodego.VPCInterfaceIPv4CreateOptions{
657+
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{{
658+
Primary: ptr.To(true),
659+
NAT1To1Address: ptr.To("auto"),
660+
Address: "auto",
661+
}},
662+
}
647663
}
648664
return nil, nil //nolint:nilnil // it is important we don't return an interface if a VPC interface already exists
649665
}
@@ -656,7 +672,8 @@ func getVPCLinodeInterfaceConfig(ctx context.Context, machineScope *scope.Machin
656672
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
657673
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{{
658674
Primary: ptr.To(true),
659-
NAT1To1Address: ptr.To("any"),
675+
NAT1To1Address: ptr.To("auto"),
676+
Address: "auto",
660677
}},
661678
},
662679
},
@@ -667,6 +684,8 @@ func getVPCLinodeInterfaceConfig(ctx context.Context, machineScope *scope.Machin
667684
vpcIntfCreateOpts.VPC.IPv6 = ipv6Config
668685
}
669686

687+
logger.Info("Creating LinodeInterfaceCreateOptions", "VPC", *vpcIntfCreateOpts)
688+
670689
return vpcIntfCreateOpts, nil
671690
}
672691

@@ -738,7 +757,7 @@ func getVPCLinodeInterfaceConfigFromDirectID(ctx context.Context, machineScope *
738757
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
739758
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{{
740759
Primary: ptr.To(true),
741-
NAT1To1Address: ptr.To("any"),
760+
NAT1To1Address: ptr.To("auto"),
742761
}},
743762
},
744763
},
@@ -905,7 +924,6 @@ func getVPCInterfaceIPv6Config(machineScope *scope.MachineScope, numIPv6RangesIn
905924

906925
// Unfortunately, this is necessary since DeepCopy can't be generated for linodego.LinodeInterfaceCreateOptions
907926
// so here we manually create the options for Linode interfaces.
908-
// /
909927
//
910928
//nolint:gocognit,cyclop,gocritic,nestif,nolintlint // Also, unfortunately, this cannot be made any reasonably simpler with how complicated the linodego struct is
911929
func constructLinodeInterfaceCreateOpts(createOpts []infrav1alpha2.LinodeInterfaceCreateOptions) []linodego.LinodeInterfaceCreateOptions {
@@ -946,7 +964,8 @@ func constructLinodeInterfaceCreateOpts(createOpts []infrav1alpha2.LinodeInterfa
946964
ipv4Addrs = []linodego.VPCInterfaceIPv4AddressCreateOptions{
947965
{
948966
Primary: ptr.To(true),
949-
NAT1To1Address: ptr.To("any"),
967+
NAT1To1Address: ptr.To("auto"),
968+
Address: "auto", // Default to auto-assigned address
950969
},
951970
}
952971
}
@@ -1021,37 +1040,39 @@ func constructLinodeInterfaceCreateOpts(createOpts []infrav1alpha2.LinodeInterfa
10211040
return linodeInterfaces
10221041
}
10231042

1043+
// for converting LinodeMachineSpec to linodego.InstanceCreateOptions. Any defaulting should be done in fillCreateConfig instead
10241044
func linodeMachineSpecToInstanceCreateConfig(machineSpec infrav1alpha2.LinodeMachineSpec, machineTags []string) *linodego.InstanceCreateOptions {
1025-
interfaces := make([]linodego.InstanceConfigInterfaceCreateOptions, len(machineSpec.Interfaces))
1026-
for idx, iface := range machineSpec.Interfaces {
1027-
interfaces[idx] = linodego.InstanceConfigInterfaceCreateOptions{
1028-
IPAMAddress: iface.IPAMAddress,
1029-
Label: iface.Label,
1030-
Purpose: iface.Purpose,
1031-
Primary: iface.Primary,
1032-
SubnetID: iface.SubnetID,
1033-
IPRanges: iface.IPRanges,
1034-
}
1035-
}
1036-
privateIP := false
1037-
if machineSpec.PrivateIP != nil {
1038-
privateIP = *machineSpec.PrivateIP
1039-
}
10401045
instCreateOpts := &linodego.InstanceCreateOptions{
10411046
Region: machineSpec.Region,
10421047
Type: machineSpec.Type,
10431048
AuthorizedKeys: machineSpec.AuthorizedKeys,
10441049
AuthorizedUsers: machineSpec.AuthorizedUsers,
10451050
RootPass: machineSpec.RootPass,
10461051
Image: machineSpec.Image,
1047-
Interfaces: interfaces,
1048-
PrivateIP: privateIP,
10491052
Tags: machineTags,
10501053
FirewallID: machineSpec.FirewallID,
10511054
DiskEncryption: linodego.InstanceDiskEncryption(machineSpec.DiskEncryption),
10521055
}
1056+
1057+
if machineSpec.PrivateIP != nil {
1058+
instCreateOpts.PrivateIP = *machineSpec.PrivateIP
1059+
}
1060+
10531061
if len(machineSpec.LinodeInterfaces) > 0 {
10541062
instCreateOpts.LinodeInterfaces = constructLinodeInterfaceCreateOpts(machineSpec.LinodeInterfaces)
1063+
} else {
1064+
interfaces := make([]linodego.InstanceConfigInterfaceCreateOptions, len(machineSpec.Interfaces))
1065+
for idx, iface := range machineSpec.Interfaces {
1066+
interfaces[idx] = linodego.InstanceConfigInterfaceCreateOptions{
1067+
IPAMAddress: iface.IPAMAddress,
1068+
Label: iface.Label,
1069+
Purpose: iface.Purpose,
1070+
Primary: iface.Primary,
1071+
SubnetID: iface.SubnetID,
1072+
IPRanges: iface.IPRanges,
1073+
}
1074+
}
1075+
instCreateOpts.Interfaces = interfaces
10551076
}
10561077

10571078
return instCreateOpts
@@ -1478,6 +1499,14 @@ func configureFirewall(ctx context.Context, machineScope *scope.MachineScope, cr
14781499
}
14791500

14801501
createConfig.FirewallID = fwID
1502+
1503+
// If using LinodeInterfaces that needs to know about the firewall ID
1504+
if machineScope.LinodeMachine.Spec.LinodeInterfaces != nil {
1505+
for i := range createConfig.LinodeInterfaces {
1506+
createConfig.LinodeInterfaces[i].FirewallID = ptr.To(fwID)
1507+
}
1508+
}
1509+
14811510
return nil
14821511
}
14831512

internal/controller/linodemachine_controller_helpers_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ func validateInterfaceExpectations(
436436
require.Equal(t, expectSubnetID, linodeIface.VPC.SubnetID)
437437
require.NotNil(t, linodeIface.VPC.IPv4)
438438
require.NotNil(t, linodeIface.VPC.IPv4.Addresses[0].NAT1To1Address)
439-
require.Equal(t, "any", *linodeIface.VPC.IPv4.Addresses[0].NAT1To1Address)
439+
require.Equal(t, "auto", *linodeIface.VPC.IPv4.Addresses[0].NAT1To1Address)
440440
} else {
441441
require.Nil(t, linodeIface)
442442
}

internal/controller/linodemachine_controller_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,8 +2567,9 @@ var _ = Describe("machine in VPC with new network interfaces", Label("machine",
25672567
SubnetID: 1,
25682568
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
25692569
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{{
2570-
NAT1To1Address: ptr.To("any"),
2570+
NAT1To1Address: ptr.To("auto"),
25712571
Primary: ptr.To(true),
2572+
Address: "auto",
25722573
}},
25732574
},
25742575
IPv6: nil,
@@ -2640,8 +2641,9 @@ var _ = Describe("machine in VPC with new network interfaces", Label("machine",
26402641
SubnetID: 1,
26412642
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
26422643
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{{
2643-
NAT1To1Address: ptr.To("any"),
2644+
NAT1To1Address: ptr.To("auto"),
26442645
Primary: ptr.To(true),
2646+
Address: "auto",
26452647
}},
26462648
},
26472649
IPv6: &linodego.VPCInterfaceIPv6CreateOptions{
@@ -2727,8 +2729,9 @@ var _ = Describe("machine in VPC with new network interfaces", Label("machine",
27272729
SubnetID: 27,
27282730
IPv4: &linodego.VPCInterfaceIPv4CreateOptions{
27292731
Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{{
2730-
NAT1To1Address: ptr.To("any"),
2732+
NAT1To1Address: ptr.To("auto"),
27312733
Primary: ptr.To(true),
2734+
Address: "auto",
27322735
}},
27332736
},
27342737
IPv6: &linodego.VPCInterfaceIPv6CreateOptions{SLAAC: nil, Ranges: nil, IsPublic: false},

internal/webhook/v1alpha2/linodemachine_webhook.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func (r *linodeMachineValidator) ValidateDelete(ctx context.Context, obj runtime
125125
return nil, nil
126126
}
127127

128+
//nolint:cyclop // as simple as it gets
128129
func (r *linodeMachineValidator) validateLinodeMachineSpec(ctx context.Context, linodeclient clients.LinodeClient, spec infrav1alpha2.LinodeMachineSpec, skipAPIValidation bool) field.ErrorList {
129130
var errs field.ErrorList
130131

@@ -156,6 +157,30 @@ func (r *linodeMachineValidator) validateLinodeMachineSpec(ctx context.Context,
156157
})
157158
}
158159

160+
if spec.LinodeInterfaces != nil && spec.Interfaces != nil {
161+
errs = append(errs, &field.Error{
162+
Field: "spec.linodeInterfaces/spec.interfaces",
163+
Type: field.ErrorTypeInvalid,
164+
Detail: "Cannot specify both LinodeInterfaces and Interfaces",
165+
})
166+
}
167+
168+
if spec.LinodeInterfaces != nil && spec.PrivateIP != nil && *spec.PrivateIP != false {
169+
errs = append(errs, &field.Error{
170+
Field: "spec.linodeInterfaces/spec.privateIP",
171+
Type: field.ErrorTypeInvalid,
172+
Detail: "Linode Interfaces do not support private IPs",
173+
})
174+
}
175+
176+
if spec.LinodeInterfaces != nil && spec.NetworkHelper != nil && *spec.NetworkHelper != false {
177+
errs = append(errs, &field.Error{
178+
Field: "spec.linodeInterfaces/spec.networkHelper",
179+
Type: field.ErrorTypeInvalid,
180+
Detail: "Linode Interfaces do not support network helper (enabled by default), it must be explicitly set to false",
181+
})
182+
}
183+
159184
if spec.FirewallID != 0 && spec.FirewallRef != nil {
160185
errs = append(errs, &field.Error{
161186
Field: "spec.firewallID/spec.firewallRef",

templates/infra/linodeMachineTemplate.yaml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ spec:
1414
kind: LinodeFirewall
1515
name: ${CLUSTER_NAME}
1616
# diskEncryption: disabled
17+
#
18+
# linodeInterfaces can't be used for the control plane
19+
# if using the default NodeBalancer for the associated
20+
# LinodeCluster's .Spec.Network.LoadBalancerType
21+
# because we need a private IP to add the machine to the
22+
# NodeBalancer
1723
interfaces:
1824
- purpose: public
1925
authorizedKeys:
@@ -35,8 +41,11 @@ spec:
3541
kind: LinodeFirewall
3642
name: ${CLUSTER_NAME}
3743
# diskEncryption: disabled
38-
interfaces:
39-
- purpose: public
44+
linodeInterfaces:
45+
- public:
46+
ipv4:
47+
addresses:
48+
- address: "auto"
4049
authorizedKeys:
4150
# uncomment to include your ssh key in linode provisioning
4251
# - ${LINODE_SSH_PUBKEY}

0 commit comments

Comments
 (0)