Skip to content

Commit 60c8c84

Browse files
authored
refactor: Use metallb APIs instead of unstructured (#1317)
Also refactors the apply logic to return early on anything other than a conflict. Other errors are not necessarily retryable and hence returning early is better practice. I decided to do this while I was thinking about introducing the Cilium load-balancer and looking at using vendored Cilium API types instead of unstructured - doing this for metallb was nice and simple. **What problem does this PR solve?**: **Which issue(s) this PR fixes**: Fixes # **How Has This Been Tested?**: <!-- Please describe the tests that you ran to verify your changes. Provide output from the tests and any manual steps needed to replicate the tests. --> **Special notes for your reviewer**: <!-- Use this to provide any additional information to the reviewers. This may include: - Best way to review the PR. - Where the author wants the most review attention on. - etc. -->
1 parent 87ee7ed commit 60c8c84

27 files changed

+2483
-76
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1beta1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
// BFDProfileSpec defines the desired state of BFDProfile.
24+
type BFDProfileSpec struct {
25+
// The minimum interval that this system is capable of
26+
// receiving control packets in milliseconds.
27+
// Defaults to 300ms.
28+
// +kubebuilder:validation:Maximum:=60000
29+
// +kubebuilder:validation:Minimum:=10
30+
// +optional
31+
ReceiveInterval *uint32 `json:"receiveInterval,omitempty"`
32+
// The minimum transmission interval (less jitter)
33+
// that this system wants to use to send BFD control packets in
34+
// milliseconds. Defaults to 300ms
35+
// +kubebuilder:validation:Maximum:=60000
36+
// +kubebuilder:validation:Minimum:=10
37+
// +optional
38+
TransmitInterval *uint32 `json:"transmitInterval,omitempty"`
39+
// Configures the detection multiplier to determine
40+
// packet loss. The remote transmission interval will be multiplied
41+
// by this value to determine the connection loss detection timer.
42+
// +kubebuilder:validation:Maximum:=255
43+
// +kubebuilder:validation:Minimum:=2
44+
// +optional
45+
DetectMultiplier *uint32 `json:"detectMultiplier,omitempty"`
46+
// Configures the minimal echo receive transmission
47+
// interval that this system is capable of handling in milliseconds.
48+
// Defaults to 50ms
49+
// +kubebuilder:validation:Maximum:=60000
50+
// +kubebuilder:validation:Minimum:=10
51+
// +optional
52+
EchoInterval *uint32 `json:"echoInterval,omitempty"`
53+
// Enables or disables the echo transmission mode.
54+
// This mode is disabled by default, and not supported on multi
55+
// hops setups.
56+
// +optional
57+
EchoMode *bool `json:"echoMode,omitempty"`
58+
// Mark session as passive: a passive session will not
59+
// attempt to start the connection and will wait for control packets
60+
// from peer before it begins replying.
61+
// +optional
62+
PassiveMode *bool `json:"passiveMode,omitempty"`
63+
// For multi hop sessions only: configure the minimum
64+
// expected TTL for an incoming BFD control packet.
65+
// +kubebuilder:validation:Maximum:=254
66+
// +kubebuilder:validation:Minimum:=1
67+
// +optional
68+
MinimumTTL *uint32 `json:"minimumTtl,omitempty"`
69+
}
70+
71+
// BFDProfileStatus defines the observed state of BFDProfile.
72+
type BFDProfileStatus struct {
73+
}
74+
75+
//+kubebuilder:object:root=true
76+
//+kubebuilder:subresource:status
77+
//+kubebuilder:printcolumn:name="Passive Mode",type=boolean,JSONPath=`.spec.passiveMode`
78+
//+kubebuilder:printcolumn:name="Transmit Interval",type=integer,JSONPath=`.spec.transmitInterval`
79+
//+kubebuilder:printcolumn:name="Receive Interval",type=integer,JSONPath=`.spec.receiveInterval`
80+
//+kubebuilder:printcolumn:name="Multiplier",type=integer,JSONPath=`.spec.detectMultiplier`
81+
82+
// BFDProfile represents the settings of the bfd session that can be
83+
// optionally associated with a BGP session.
84+
type BFDProfile struct {
85+
metav1.TypeMeta `json:",inline"`
86+
metav1.ObjectMeta `json:"metadata,omitempty"`
87+
88+
Spec BFDProfileSpec `json:"spec,omitempty"`
89+
Status BFDProfileStatus `json:"status,omitempty"`
90+
}
91+
92+
//+kubebuilder:object:root=true
93+
94+
// BFDProfileList contains a list of BFDProfile.
95+
type BFDProfileList struct {
96+
metav1.TypeMeta `json:",inline"`
97+
metav1.ListMeta `json:"metadata,omitempty"`
98+
Items []BFDProfile `json:"items"`
99+
}
100+
101+
func init() {
102+
SchemeBuilder.Register(&BFDProfile{}, &BFDProfileList{})
103+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1beta1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
24+
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
25+
26+
// BGPAdvertisementSpec defines the desired state of BGPAdvertisement.
27+
type BGPAdvertisementSpec struct {
28+
// The aggregation-length advertisement option lets you “roll up” the /32s into a larger prefix. Defaults to 32. Works for IPv4 addresses.
29+
// +kubebuilder:validation:Minimum=1
30+
// +kubebuilder:default:=32
31+
// +optional
32+
AggregationLength *int32 `json:"aggregationLength,omitempty"`
33+
34+
// The aggregation-length advertisement option lets you “roll up” the /128s into a larger prefix. Defaults to 128. Works for IPv6 addresses.
35+
// +kubebuilder:default:=128
36+
// +optional
37+
AggregationLengthV6 *int32 `json:"aggregationLengthV6,omitempty"`
38+
39+
// The BGP LOCAL_PREF attribute which is used by BGP best path algorithm,
40+
// Path with higher localpref is preferred over one with lower localpref.
41+
// +optional
42+
LocalPref uint32 `json:"localPref,omitempty"`
43+
44+
// The BGP communities to be associated with the announcement. Each item can be a standard community of the
45+
// form 1234:1234, a large community of the form large:1234:1234:1234 or the name of an alias defined in the
46+
// Community CRD.
47+
// +optional
48+
Communities []string `json:"communities,omitempty"`
49+
50+
// The list of IPAddressPools to advertise via this advertisement, selected by name.
51+
// +optional
52+
IPAddressPools []string `json:"ipAddressPools,omitempty"`
53+
54+
// A selector for the IPAddressPools which would get advertised via this advertisement.
55+
// If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools.
56+
// +optional
57+
IPAddressPoolSelectors []metav1.LabelSelector `json:"ipAddressPoolSelectors,omitempty"`
58+
59+
// NodeSelectors allows to limit the nodes to announce as next hops for the LoadBalancer IP. When empty, all the nodes having are announced as next hops.
60+
// +optional
61+
NodeSelectors []metav1.LabelSelector `json:"nodeSelectors,omitempty"`
62+
63+
// Peers limits the bgppeer to advertise the ips of the selected pools to.
64+
// When empty, the loadbalancer IP is announced to all the BGPPeers configured.
65+
// +optional
66+
Peers []string `json:"peers,omitempty"`
67+
}
68+
69+
// BGPAdvertisementStatus defines the observed state of BGPAdvertisement.
70+
type BGPAdvertisementStatus struct {
71+
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
72+
// Important: Run "make" to regenerate code after modifying this file
73+
}
74+
75+
//+kubebuilder:object:root=true
76+
//+kubebuilder:subresource:status
77+
//+kubebuilder:printcolumn:name="IPAddressPools",type=string,JSONPath=`.spec.ipAddressPools`
78+
//+kubebuilder:printcolumn:name="IPAddressPool Selectors",type=string,JSONPath=`.spec.ipAddressPoolSelectors`
79+
//+kubebuilder:printcolumn:name="Peers",type=string,JSONPath=`.spec.peers`
80+
//+kubebuilder:printcolumn:name="Node Selectors",type=string,JSONPath=`.spec.nodeSelectors`,priority=10
81+
82+
// BGPAdvertisement allows to advertise the IPs coming
83+
// from the selected IPAddressPools via BGP, setting the parameters of the
84+
// BGP Advertisement.
85+
type BGPAdvertisement struct {
86+
metav1.TypeMeta `json:",inline"`
87+
metav1.ObjectMeta `json:"metadata,omitempty"`
88+
89+
Spec BGPAdvertisementSpec `json:"spec,omitempty"`
90+
Status BGPAdvertisementStatus `json:"status,omitempty"`
91+
}
92+
93+
//+kubebuilder:object:root=true
94+
95+
// BGPAdvertisementList contains a list of BGPAdvertisement.
96+
type BGPAdvertisementList struct {
97+
metav1.TypeMeta `json:",inline"`
98+
metav1.ListMeta `json:"metadata,omitempty"`
99+
Items []BGPAdvertisement `json:"items"`
100+
}
101+
102+
func init() {
103+
SchemeBuilder.Register(&BGPAdvertisement{}, &BGPAdvertisementList{})
104+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier:Apache-2.0
2+
3+
package v1beta1
4+
5+
import (
6+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/external/go.universe.tf/metallb/api/v1beta2"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
"sigs.k8s.io/controller-runtime/pkg/conversion"
9+
)
10+
11+
// ConvertTo converts this BGPPeer to the Hub version (v1beta2).
12+
func (src *BGPPeer) ConvertTo(dstRaw conversion.Hub) error {
13+
dst := dstRaw.(*v1beta2.BGPPeer)
14+
dst.Spec.ASN = src.Spec.ASN
15+
dst.Spec.MyASN = src.Spec.MyASN
16+
dst.Spec.Address = src.Spec.Address
17+
dst.Spec.SrcAddress = src.Spec.SrcAddress
18+
dst.Spec.Port = src.Spec.Port
19+
dst.Spec.HoldTime = &src.Spec.HoldTime
20+
dst.Spec.KeepaliveTime = &src.Spec.KeepaliveTime
21+
dst.Spec.RouterID = src.Spec.RouterID
22+
dst.Spec.Password = src.Spec.Password
23+
dst.Spec.BFDProfile = src.Spec.BFDProfile
24+
dst.Spec.EBGPMultiHop = src.Spec.EBGPMultiHop
25+
dst.Spec.NodeSelectors = parseNodeSelectors(src.Spec.NodeSelectors)
26+
dst.ObjectMeta = src.ObjectMeta
27+
return nil
28+
}
29+
30+
// ConvertFrom converts from the Hub version (v1beta2) to this version.
31+
func (dst *BGPPeer) ConvertFrom(srcRaw conversion.Hub) error {
32+
src := srcRaw.(*v1beta2.BGPPeer)
33+
var ht metav1.Duration
34+
if src.Spec.HoldTime != nil {
35+
ht = *src.Spec.HoldTime
36+
}
37+
var ka metav1.Duration
38+
if src.Spec.KeepaliveTime != nil {
39+
ka = *src.Spec.KeepaliveTime
40+
}
41+
dst.ObjectMeta = src.ObjectMeta
42+
dst.Spec.MyASN = src.Spec.MyASN
43+
dst.Spec.ASN = src.Spec.ASN
44+
dst.Spec.Address = src.Spec.Address
45+
dst.Spec.SrcAddress = src.Spec.SrcAddress
46+
dst.Spec.Port = src.Spec.Port
47+
dst.Spec.HoldTime = ht
48+
dst.Spec.KeepaliveTime = ka
49+
dst.Spec.RouterID = src.Spec.RouterID
50+
dst.Spec.Password = src.Spec.Password
51+
dst.Spec.BFDProfile = src.Spec.BFDProfile
52+
dst.Spec.EBGPMultiHop = src.Spec.EBGPMultiHop
53+
dst.Spec.NodeSelectors = labelsToLegacySelector(src.Spec.NodeSelectors)
54+
return nil
55+
}
56+
57+
func parseNodeSelectors(selectors []NodeSelector) []metav1.LabelSelector {
58+
var nodeSels []metav1.LabelSelector
59+
for _, sel := range selectors {
60+
nodeSels = append(nodeSels, parseNodeSelector(sel))
61+
}
62+
return nodeSels
63+
}
64+
65+
func parseNodeSelector(ns NodeSelector) metav1.LabelSelector {
66+
if len(ns.MatchLabels)+len(ns.MatchExpressions) == 0 {
67+
return metav1.LabelSelector{}
68+
}
69+
70+
sel := metav1.LabelSelector{
71+
MatchLabels: make(map[string]string),
72+
}
73+
for k, v := range ns.MatchLabels {
74+
sel.MatchLabels[k] = v
75+
}
76+
for _, req := range ns.MatchExpressions {
77+
sel.MatchExpressions = append(sel.MatchExpressions, metav1.LabelSelectorRequirement{
78+
Key: req.Key,
79+
Operator: metav1.LabelSelectorOperator(req.Operator),
80+
Values: req.Values,
81+
})
82+
}
83+
return sel
84+
}
85+
86+
func labelsToLegacySelector(selectors []metav1.LabelSelector) []NodeSelector {
87+
res := []NodeSelector{}
88+
for _, sel := range selectors {
89+
toAdd := NodeSelector{
90+
MatchLabels: make(map[string]string),
91+
MatchExpressions: make([]MatchExpression, 0),
92+
}
93+
for k, v := range sel.MatchLabels {
94+
toAdd.MatchLabels[k] = v
95+
}
96+
for _, e := range sel.MatchExpressions {
97+
m := MatchExpression{
98+
Key: e.Key,
99+
Operator: string(e.Operator),
100+
Values: make([]string, len(e.Values)),
101+
}
102+
copy(m.Values, e.Values)
103+
toAdd.MatchExpressions = append(toAdd.MatchExpressions, m)
104+
}
105+
res = append(res, toAdd)
106+
}
107+
return res
108+
}

0 commit comments

Comments
 (0)