Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FRR config hot-replace feature #140

Merged
merged 7 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,21 @@ type Config struct {
BPFInterfaces []string `yaml:"bpfInterfaces"`
SkipVRFConfig []string `yaml:"skipVRFConfig"`
ServerASN int `yaml:"serverASN"`

Replacements []Replacement `yaml:"replacements"`
}

type VRFConfig struct {
VNI int `yaml:"vni"`
RT string `yaml:"rt"`
}

type Replacement struct {
Old string `yaml:"old"`
New string `yaml:"new"`
Regex bool `yaml:"regex"`
}

func LoadConfig() (*Config, error) {
config := &Config{}

Expand Down
25 changes: 20 additions & 5 deletions pkg/frr/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"regexp"

"github.com/telekom/das-schiff-network-operator/pkg/config"
"github.com/telekom/das-schiff-network-operator/pkg/healthcheck"
"github.com/telekom/das-schiff-network-operator/pkg/nl"
)
Expand Down Expand Up @@ -33,7 +34,7 @@ type templateConfig struct {
HostRouterID string
}

func (m *Manager) Configure(in Configuration, nm *nl.Manager) (bool, error) {
func (m *Manager) Configure(in Configuration, nm *nl.Manager, nwopCfg *config.Config) (bool, error) {
// Remove permit from VRF and only allow deny rules for mgmt VRFs
for i := range in.VRFs {
if in.VRFs[i].Name != m.mgmtVrf {
Expand All @@ -50,7 +51,7 @@ func (m *Manager) Configure(in Configuration, nm *nl.Manager) (bool, error) {
}
}

config, err := m.renderSubtemplates(in, nm)
frrConfig, err := m.renderSubtemplates(in, nm)
if err != nil {
return false, err
}
Expand All @@ -60,12 +61,13 @@ func (m *Manager) Configure(in Configuration, nm *nl.Manager) (bool, error) {
return false, fmt.Errorf("error reading configuration file: %w", err)
}

targetConfig, err := render(m.configTemplate, config)
targetConfig, err := render(m.configTemplate, frrConfig)
if err != nil {
return false, err
}

targetConfig = fixRouteTargetReload(targetConfig)
targetConfig = applyCfgReplacements(targetConfig, nwopCfg.Replacements)

if !bytes.Equal(currentConfig, targetConfig) {
err = os.WriteFile(m.ConfigPath, targetConfig, frrPermissions)
Expand Down Expand Up @@ -154,8 +156,8 @@ func (m *Manager) renderSubtemplates(in Configuration, nlManager *nl.Manager) (*

// fixRouteTargetReload is a workaround for FRR's inability to reload route-targets if they are configured in a single line.
// This function splits such lines into multiple lines, each containing a single route-target.
func fixRouteTargetReload(config []byte) []byte {
return rtLinesRe.ReplaceAllFunc(config, func(s []byte) []byte {
func fixRouteTargetReload(frrConfig []byte) []byte {
return rtLinesRe.ReplaceAllFunc(frrConfig, func(s []byte) []byte {
parts := rtPartsRe.FindSubmatch(s)
if parts == nil {
return s
Expand All @@ -172,3 +174,16 @@ func fixRouteTargetReload(config []byte) []byte {
return []byte(lines[:len(lines)-1])
})
}

// applyCfgReplacements replaces placeholders in the configuration with the actual values.
func applyCfgReplacements(frrConfig []byte, replacements []config.Replacement) []byte {
for _, replacement := range replacements {
if !replacement.Regex {
frrConfig = bytes.ReplaceAll(frrConfig, []byte(replacement.Old), []byte(replacement.New))
} else {
re := regexp.MustCompile(replacement.Old)
frrConfig = re.ReplaceAll(frrConfig, []byte(replacement.New))
}
}
return frrConfig
}
8 changes: 4 additions & 4 deletions pkg/frr/frr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ var _ = Describe("frr", func() {
It("return error if cannot get underlay IP", func() {
m := &Manager{}
nlMock.EXPECT().AddrList(gomock.Any(), gomock.Any()).Return(nil, errors.New("error listing addresses"))
_, err := m.Configure(Configuration{}, nl.NewManager(nlMock))
_, err := m.Configure(Configuration{}, nl.NewManager(nlMock), &config.Config{})
Expect(err).To(HaveOccurred())
})
It("return error if cannot node's name", func() {
Expand All @@ -148,7 +148,7 @@ var _ = Describe("frr", func() {
nlMock.EXPECT().AddrList(gomock.Any(), gomock.Any()).Return([]netlink.Addr{
{IPNet: netlink.NewIPNet(net.IPv4(0, 0, 0, 0))},
}, nil)
_, err := m.Configure(Configuration{}, nl.NewManager(nlMock))
_, err := m.Configure(Configuration{}, nl.NewManager(nlMock), &config.Config{})
Expect(err).To(HaveOccurred())

if isSet {
Expand All @@ -165,7 +165,7 @@ var _ = Describe("frr", func() {
nlMock.EXPECT().AddrList(gomock.Any(), gomock.Any()).Return([]netlink.Addr{
{IPNet: netlink.NewIPNet(net.IPv4(0, 0, 0, 0))},
}, nil)
_, err = m.Configure(Configuration{}, nl.NewManager(nlMock))
_, err = m.Configure(Configuration{}, nl.NewManager(nlMock), &config.Config{})
Expect(err).To(HaveOccurred())

if isSet {
Expand All @@ -189,7 +189,7 @@ var _ = Describe("frr", func() {
nlMock.EXPECT().AddrList(gomock.Any(), gomock.Any()).Return([]netlink.Addr{
{IPNet: netlink.NewIPNet(net.IPv4(0, 0, 0, 0))},
}, nil)
_, err = m.Configure(Configuration{}, nl.NewManager(nlMock))
_, err = m.Configure(Configuration{}, nl.NewManager(nlMock), &config.Config{})
Expect(err).To(HaveOccurred())

if isSet {
Expand Down
2 changes: 1 addition & 1 deletion pkg/reconciler/layer3.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (r *reconcile) configureFRR(vrfConfigs []frr.VRFConfiguration, reloadTwice
changed, err := r.frrManager.Configure(frr.Configuration{
VRFs: vrfConfigs,
ASN: r.config.ServerASN,
}, r.netlinkManager)
}, r.netlinkManager, r.config)
if err != nil {
r.Logger.Error(err, "error updating FRR configuration")
return fmt.Errorf("error updating FRR configuration: %w", err)
Expand Down