Skip to content

Commit b3b8aa0

Browse files
Changed FRR configuration behavior
Signed-off-by: Patryk Strusiewicz-Surmacki <[email protected]>
1 parent 28a37d8 commit b3b8aa0

File tree

3 files changed

+62
-44
lines changed

3 files changed

+62
-44
lines changed

pkg/frr/configure.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (m *Manager) Configure(in Configuration, nm *nl.Manager, nwopCfg *config.Co
4343
for j := range in.VRFs[i].Import {
4444
for k := range in.VRFs[i].Import[j].Items {
4545
if in.VRFs[i].Import[j].Items[k].Action != "deny" {
46-
return false, fmt.Errorf("only deny rules are allowed in import prefix-lists of mgmt VRFs")
46+
return false, &ConfigurationError{fmt.Errorf("only deny rules are allowed in import prefix-lists of mgmt VRFs")}
4747
}
4848
// Swap deny to permit, this will be a prefix-list called from a deny route-map
4949
in.VRFs[i].Import[j].Items[k].Action = "permit"
@@ -58,7 +58,7 @@ func (m *Manager) Configure(in Configuration, nm *nl.Manager, nwopCfg *config.Co
5858

5959
currentConfig, err := os.ReadFile(m.ConfigPath)
6060
if err != nil {
61-
return false, fmt.Errorf("error reading configuration file: %w", err)
61+
return false, &ConfigurationError{fmt.Errorf("error reading configuration file: %w", err)}
6262
}
6363

6464
targetConfig, err := render(m.configTemplate, frrConfig)
@@ -72,7 +72,7 @@ func (m *Manager) Configure(in Configuration, nm *nl.Manager, nwopCfg *config.Co
7272
if !bytes.Equal(currentConfig, targetConfig) {
7373
err = os.WriteFile(m.ConfigPath, targetConfig, frrPermissions)
7474
if err != nil {
75-
return false, fmt.Errorf("error writing configuration file: %w", err)
75+
return false, &ConfigurationError{fmt.Errorf("error writing configuration file: %w", err)}
7676
}
7777

7878
return true, nil
@@ -187,3 +187,11 @@ func applyCfgReplacements(frrConfig []byte, replacements []config.Replacement) [
187187
}
188188
return frrConfig
189189
}
190+
191+
type ConfigurationError struct {
192+
err error
193+
}
194+
195+
func (r *ConfigurationError) Error() string {
196+
return fmt.Sprintf("FRR configuration error: %s", r.err.Error())
197+
}

pkg/reconciler/layer3.go

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package reconciler
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"net"
78
"sort"
@@ -65,6 +66,15 @@ func (r *reconcile) reconcileLayer3(l3vnis []networkv1alpha1.VRFRouteConfigurati
6566
return allConfigs[i].VNI < allConfigs[j].VNI
6667
})
6768

69+
// Create FRR configuration and reload it
70+
err = r.configureFRR(allConfigs)
71+
if err != nil {
72+
if !errors.Is(err, &frr.ConfigurationError{}) {
73+
return err
74+
}
75+
r.Logger.Error(err, "failed to configure FRR")
76+
}
77+
6878
created, deletedVRF, err := r.reconcileL3Netlink(l3Configs)
6979
if err != nil {
7080
r.Logger.Error(err, "error reconciling Netlink")
@@ -75,54 +85,40 @@ func (r *reconcile) reconcileLayer3(l3vnis []networkv1alpha1.VRFRouteConfigurati
7585
if err != nil {
7686
return err
7787
}
78-
reloadTwice := deletedVRF || deletedTaas
7988

80-
// We wait here for two seconds to let FRR settle after updating netlink devices
81-
time.Sleep(defaultSleep)
82-
83-
err = r.configureFRR(allConfigs, reloadTwice)
84-
if err != nil {
85-
return err
89+
// When a BGP VRF is deleted there is a leftover running configuration after reload
90+
// A second reload fixes this.
91+
if deletedVRF || deletedTaas {
92+
if err := r.reloadFRR(); err != nil {
93+
return fmt.Errorf("failed to reload FRR: %w", err)
94+
}
8695
}
8796

88-
// Make sure that all created netlink VRFs are up after FRR reload
97+
// We wait here for two seconds to let FRR settle after updating netlink devices
8998
time.Sleep(defaultSleep)
99+
90100
for _, info := range created {
91101
if err := r.netlinkManager.UpL3(info); err != nil {
92-
r.Logger.Error(err, "error setting L3 to state UP")
93-
return fmt.Errorf("error setting L3 to state UP: %w", err)
102+
r.Logger.Error(err, "error setting L3 to state UP", "interface", info)
94103
}
95104
}
96105
return nil
97106
}
98107

99-
func (r *reconcile) configureFRR(vrfConfigs []frr.VRFConfiguration, reloadTwice bool) error {
108+
func (r *reconcile) configureFRR(vrfConfigs []frr.VRFConfiguration) error {
100109
changed, err := r.frrManager.Configure(frr.Configuration{
101110
VRFs: vrfConfigs,
102111
ASN: r.config.ServerASN,
103112
}, r.netlinkManager, r.config)
104113
if err != nil {
105-
r.Logger.Error(err, "error updating FRR configuration")
106114
return fmt.Errorf("error updating FRR configuration: %w", err)
107115
}
108116

109-
if changed || r.dirtyFRRConfig {
117+
if changed {
110118
err := r.reloadFRR()
111119
if err != nil {
112-
r.dirtyFRRConfig = true
113120
return err
114121
}
115-
116-
// When a BGP VRF is deleted there is a leftover running configuration after reload
117-
// A second reload fixes this.
118-
if reloadTwice {
119-
err := r.reloadFRR()
120-
if err != nil {
121-
r.dirtyFRRConfig = true
122-
return err
123-
}
124-
}
125-
r.dirtyFRRConfig = false
126122
}
127123
return nil
128124
}
@@ -249,20 +245,12 @@ func (r *reconcile) reconcileL3Netlink(vrfConfigs []frr.VRFConfiguration) ([]nl.
249245
}
250246

251247
// Check for VRFs that are configured on the host but no longer in Kubernetes
252-
toDelete := []nl.VRFInformation{}
253-
for i := range existing {
254-
stillExists := false
255-
for j := range vrfConfigs {
256-
if vrfConfigs[j].Name == existing[i].Name && vrfConfigs[j].VNI == existing[i].VNI {
257-
stillExists = true
258-
existing[i].MTU = vrfConfigs[j].MTU
259-
break
260-
}
261-
}
262-
if !stillExists || existing[i].MarkForDelete {
263-
toDelete = append(toDelete, existing[i])
264-
} else if err := r.reconcileExisting(existing[i]); err != nil {
265-
r.Logger.Error(err, "error reconciling existing VRF", "vrf", existing[i].Name, "vni", strconv.Itoa(existing[i].VNI))
248+
preexisting, toDelete := r.gatherInterfacesInfo(vrfConfigs, existing)
249+
250+
// Make sure that all previously configured L3 interfaces are up
251+
for _, info := range preexisting {
252+
if err := r.netlinkManager.UpL3(info); err != nil {
253+
r.Logger.Error(err, "failed to set L3 up", "interface", info.Name)
266254
}
267255
}
268256

@@ -289,6 +277,30 @@ func (r *reconcile) reconcileL3Netlink(vrfConfigs []frr.VRFConfiguration) ([]nl.
289277
return toCreate, len(toDelete) > 0, nil
290278
}
291279

280+
func (r *reconcile) gatherInterfacesInfo(vrfConfigs []frr.VRFConfiguration, existing []nl.VRFInformation) (preexisting, toDelete []nl.VRFInformation) {
281+
// Check for VRFs that are configured on the host but no longer in Kubernetes
282+
for i := range existing {
283+
stillExists := false
284+
for j := range vrfConfigs {
285+
if vrfConfigs[j].Name == existing[i].Name && vrfConfigs[j].VNI == existing[i].VNI {
286+
stillExists = true
287+
existing[i].MTU = vrfConfigs[j].MTU
288+
if !existing[i].MarkForDelete {
289+
preexisting = append(preexisting, existing[i])
290+
}
291+
break
292+
}
293+
}
294+
if !stillExists || existing[i].MarkForDelete {
295+
toDelete = append(toDelete, existing[i])
296+
} else if err := r.reconcileExisting(existing[i]); err != nil {
297+
r.Logger.Error(err, "error reconciling existing VRF", "vrf", existing[i].Name, "vni", strconv.Itoa(existing[i].VNI))
298+
}
299+
}
300+
301+
return preexisting, toDelete
302+
}
303+
292304
func (r *reconcile) reconcileTaasNetlink(vrfConfigs []frr.VRFConfiguration) (bool, error) {
293305
existing, err := r.netlinkManager.ListTaas()
294306
if err != nil {

pkg/reconciler/reconciler.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ type Reconciler struct {
2828
healthChecker *healthcheck.HealthChecker
2929

3030
debouncer *debounce.Debouncer
31-
32-
dirtyFRRConfig bool
3331
}
3432

3533
type reconcile struct {

0 commit comments

Comments
 (0)