diff --git a/pkg/config/config.go b/pkg/config/config.go index af8bc8bb..c7054888 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -18,6 +18,8 @@ type Config struct { BPFInterfaces []string `yaml:"bpfInterfaces"` SkipVRFConfig []string `yaml:"skipVRFConfig"` ServerASN int `yaml:"serverASN"` + + Replacements []Replacement `yaml:"replacements"` } type VRFConfig struct { @@ -25,6 +27,12 @@ type VRFConfig struct { 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{} diff --git a/pkg/frr/configure.go b/pkg/frr/configure.go index e6902267..d5d8782c 100644 --- a/pkg/frr/configure.go +++ b/pkg/frr/configure.go @@ -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" ) @@ -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 { @@ -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 } @@ -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) @@ -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 @@ -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 +} diff --git a/pkg/frr/frr_test.go b/pkg/frr/frr_test.go index 88808721..f04eb7b1 100644 --- a/pkg/frr/frr_test.go +++ b/pkg/frr/frr_test.go @@ -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() { @@ -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 { @@ -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 { @@ -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 { diff --git a/pkg/reconciler/layer3.go b/pkg/reconciler/layer3.go index bf50b417..f66e91db 100644 --- a/pkg/reconciler/layer3.go +++ b/pkg/reconciler/layer3.go @@ -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)