Skip to content

Commit f34e4da

Browse files
Felix Wischke (65278)wikkyk
authored andcommitted
add metric annotation support to IPPool objects
1 parent 74cf293 commit f34e4da

File tree

13 files changed

+336
-3
lines changed

13 files changed

+336
-3
lines changed

api/v1alpha1/proxmoxcluster_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ type IPConfigSpec struct {
107107
// Gateway
108108
// +optional
109109
Gateway string `json:"gateway,omitempty"`
110+
111+
// Metric is the route priority applied to the default gateway
112+
// +kubebuilder:default=100
113+
Metric *uint32 `json:"metric"`
110114
}
111115

112116
// SchedulerHints allows to pass the scheduler instructions to (dis)allow over- or enforce underprovisioning of resources.

api/v1alpha1/proxmoxcluster_types_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ func defaultCluster() *ProxmoxCluster {
8383
IPv4Config: &IPConfigSpec{
8484
Addresses: []string{"10.0.0.0/24"},
8585
Prefix: 24,
86+
Gateway: "10.0.0.254",
87+
Metric: func() *uint32 { var a uint32 = 123; return &a }(),
8688
},
8789
DNSServers: []string{"1.2.3.4"},
8890
CloneSpec: &ProxmoxClusterCloneSpec{

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/infrastructure.cluster.x-k8s.io_proxmoxclusters.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,12 +598,19 @@ spec:
598598
gateway:
599599
description: Gateway
600600
type: string
601+
metric:
602+
default: 100
603+
description: Metric is the route priority applied to the default
604+
gateway
605+
format: int32
606+
type: integer
601607
prefix:
602608
description: Prefix is the network prefix to use.
603609
maximum: 128
604610
type: integer
605611
required:
606612
- addresses
613+
- metric
607614
- prefix
608615
type: object
609616
x-kubernetes-validations:
@@ -624,12 +631,19 @@ spec:
624631
gateway:
625632
description: Gateway
626633
type: string
634+
metric:
635+
default: 100
636+
description: Metric is the route priority applied to the default
637+
gateway
638+
format: int32
639+
type: integer
627640
prefix:
628641
description: Prefix is the network prefix to use.
629642
maximum: 128
630643
type: integer
631644
required:
632645
- addresses
646+
- metric
633647
- prefix
634648
type: object
635649
x-kubernetes-validations:

config/crd/bases/infrastructure.cluster.x-k8s.io_proxmoxclustertemplates.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,12 +648,19 @@ spec:
648648
gateway:
649649
description: Gateway
650650
type: string
651+
metric:
652+
default: 100
653+
description: Metric is the route priority applied to the
654+
default gateway
655+
format: int32
656+
type: integer
651657
prefix:
652658
description: Prefix is the network prefix to use.
653659
maximum: 128
654660
type: integer
655661
required:
656662
- addresses
663+
- metric
657664
- prefix
658665
type: object
659666
x-kubernetes-validations:
@@ -674,12 +681,19 @@ spec:
674681
gateway:
675682
description: Gateway
676683
type: string
684+
metric:
685+
default: 100
686+
description: Metric is the route priority applied to the
687+
default gateway
688+
format: int32
689+
type: integer
677690
prefix:
678691
description: Prefix is the network prefix to use.
679692
maximum: 128
680693
type: integer
681694
required:
682695
- addresses
696+
- metric
683697
- prefix
684698
type: object
685699
x-kubernetes-validations:

internal/service/vmservice/bootstrap.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ func getNetworkConfigDataForDevice(ctx context.Context, machineScope *scope.Mach
196196
dns := machineScope.InfraCluster.ProxmoxCluster.Spec.DNSServers
197197
ip := IPAddressWithPrefix(ipAddr.Spec.Address, ipAddr.Spec.Prefix)
198198
gw := ipAddr.Spec.Gateway
199+
metric, err := findIPAddressGatewayMetric(ctx, machineScope, ipAddr)
200+
if err != nil {
201+
return nil, errors.Wrapf(err, "error converting metric annotation, kind=%s, name=%s", ipAddr.Spec.PoolRef.Kind, ipAddr.Spec.PoolRef.Name)
202+
}
199203

200204
cloudinitNetworkConfigData := &cloudinit.NetworkConfigData{
201205
MacAddress: macAddress,
@@ -205,9 +209,11 @@ func getNetworkConfigDataForDevice(ctx context.Context, machineScope *scope.Mach
205209
// If it's an IPv6 address, we must set Gateway6 and IPV6Address instead
206210
if strings.Contains(ip, ":") {
207211
cloudinitNetworkConfigData.Gateway6 = gw
212+
cloudinitNetworkConfigData.Metric6 = metric
208213
cloudinitNetworkConfigData.IPV6Address = ip
209214
} else {
210215
cloudinitNetworkConfigData.Gateway = gw
216+
cloudinitNetworkConfigData.Metric = metric
211217
cloudinitNetworkConfigData.IPAddress = ip
212218
}
213219

@@ -241,6 +247,7 @@ func getDefaultNetworkDevice(ctx context.Context, machineScope *scope.MachineSco
241247
default:
242248
config.IPV6Address = conf.IPV6Address
243249
config.Gateway6 = conf.Gateway6
250+
config.Metric6 = conf.Metric6
244251
}
245252
}
246253

@@ -279,17 +286,28 @@ func getCommonInterfaceConfig(ctx context.Context, machineScope *scope.MachineSc
279286
if err != nil {
280287
return errors.Wrapf(err, "unable to find IPAddress, device=%s", ifname)
281288
}
289+
metric, err := findIPAddressGatewayMetric(ctx, machineScope, ipAddr)
290+
if err != nil {
291+
return errors.Wrapf(err, "error converting metric annotation, kind=%s, name=%s", ipAddr.Spec.PoolRef.Kind, ipAddr.Spec.PoolRef.Name)
292+
}
293+
282294
ciconfig.IPAddress = IPAddressWithPrefix(ipAddr.Spec.Address, ipAddr.Spec.Prefix)
283295
ciconfig.Gateway = ipAddr.Spec.Gateway
296+
ciconfig.Metric = metric
284297
}
285298
if ifconfig.IPv6PoolRef != nil && ciconfig.IPV6Address == "" {
286299
var ifname = fmt.Sprintf("%s-%s", ciconfig.Name, infrav1alpha1.DefaultSuffix+"6")
287300
ipAddr, err := findIPAddress(ctx, machineScope, ifname)
288301
if err != nil {
289302
return errors.Wrapf(err, "unable to find IPAddress, device=%s", ifname)
290303
}
304+
metric, err := findIPAddressGatewayMetric(ctx, machineScope, ipAddr)
305+
if err != nil {
306+
return errors.Wrapf(err, "error converting metric annotation, kind=%s, name=%s", ipAddr.Spec.PoolRef.Kind, ipAddr.Spec.PoolRef.Name)
307+
}
291308
ciconfig.IPV6Address = IPAddressWithPrefix(ipAddr.Spec.Address, ipAddr.Spec.Prefix)
292309
ciconfig.Gateway6 = ipAddr.Spec.Gateway
310+
ciconfig.Metric6 = metric
293311
}
294312

295313
return nil

internal/service/vmservice/ip.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"net/netip"
23+
"strconv"
2324

2425
"github.com/pkg/errors"
2526
corev1 "k8s.io/api/core/v1"
@@ -70,6 +71,23 @@ func findIPAddress(ctx context.Context, machineScope *scope.MachineScope, device
7071
return machineScope.IPAMHelper.GetIPAddress(ctx, key)
7172
}
7273

74+
func findIPAddressGatewayMetric(ctx context.Context, machineScope *scope.MachineScope, ipAddress *ipamv1.IPAddress) (*uint32, error) {
75+
annotations, err := machineScope.IPAMHelper.GetIPPoolAnnotations(ctx, ipAddress)
76+
if err != nil {
77+
return nil, err
78+
}
79+
var rv *uint32
80+
81+
if s, exists := annotations["metric"]; exists {
82+
metric, err := strconv.ParseUint(s, 0, 32)
83+
if err != nil {
84+
return nil, err
85+
}
86+
rv = ptr.To(uint32(metric))
87+
}
88+
return rv, nil
89+
}
90+
7391
func formatIPAddressName(name, device string) string {
7492
return fmt.Sprintf("%s-%s", name, device)
7593
}

pkg/cloudinit/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ var (
3434
// ErrMissingGateway returns an error if required gateway is empty.
3535
ErrMissingGateway = errors.New("gateway is not set")
3636

37+
// ErrConflictingMetrics returns an error if a metric for a route already exists.
38+
ErrConflictingMetrics = errors.New("metric already exists for default gateway")
39+
3740
// ErrMissingMacAddress returns an error if required mac address is empty.
3841
ErrMissingMacAddress = errors.New("mac address is not set")
3942

pkg/cloudinit/network.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,16 @@ const (
8888
routes:
8989
{{- if .Gateway }}
9090
- to: 0.0.0.0/0
91-
metric: {{ if eq .Name "eth0" }}100{{ else }}200{{ end }}
91+
{{- if .Metric }}
92+
metric: {{ .Metric }}
93+
{{- end }}
9294
via: {{ .Gateway }}
9395
{{- end }}
9496
{{- if .Gateway6 }}
9597
- to: '::/0'
96-
metric: {{ if eq .Name "eth0" }}100{{ else }}200{{ end }}
98+
{{- if .Metric6 }}
99+
metric: {{ .Metric6 }}
100+
{{- end }}
97101
via: '{{ .Gateway6 }}'
98102
{{- end }}
99103
{{- else }}
@@ -167,6 +171,11 @@ func (r *NetworkConfig) validate() error {
167171
if len(r.data.NetworkConfigData) == 0 {
168172
return ErrMissingNetworkConfigData
169173
}
174+
metrics := make(map[uint32]*struct {
175+
ipv4 bool
176+
ipv6 bool
177+
})
178+
170179
for i, d := range r.data.NetworkConfigData {
171180
// TODO: refactor this when network configuration is unified
172181
if d.Type != "ethernet" {
@@ -208,6 +217,31 @@ func (r *NetworkConfig) validate() error {
208217
return ErrMissingGateway
209218
}
210219
}
220+
if d.Metric != nil {
221+
if _, exists := metrics[*d.Metric]; !exists {
222+
metrics[*d.Metric] = new(struct {
223+
ipv4 bool
224+
ipv6 bool
225+
})
226+
}
227+
if metrics[*d.Metric].ipv4 {
228+
return ErrConflictingMetrics
229+
}
230+
metrics[*d.Metric].ipv4 = true
231+
}
232+
if d.Metric6 != nil {
233+
if _, exists := metrics[*d.Metric6]; !exists {
234+
metrics[*d.Metric6] = new(struct {
235+
ipv4 bool
236+
ipv6 bool
237+
})
238+
}
239+
240+
if metrics[*d.Metric6].ipv6 {
241+
return ErrConflictingMetrics
242+
}
243+
metrics[*d.Metric6].ipv6 = true
244+
}
211245
}
212246
return nil
213247
}

0 commit comments

Comments
 (0)