Skip to content

Commit d0f2bdc

Browse files
authored
Merge pull request #24 from skoef/compatFlag
add compatBird213 flag
2 parents 1076ba6 + 180579e commit d0f2bdc

15 files changed

+176
-169
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Configuration section for global options.
9090
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
9191
| configfile | Path to configuration file that will be generated and should be included in the BIRD configuration. Defaults to **/etc/bird/birdwatcher.conf**. |
9292
| reloadcommand | Command to invoke to signal BIRD the configuration should be reloaded. Defaults to **/usr/sbin/birdc configure**. |
93+
| compatbird213 | To use birdwatcher with BIRD 2.13 or earlier, enable this flag. It will remove the function return types from the output |
9394

9495
## **[services]**
9596

birdwatcher/bird.go

+45-12
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,26 @@ package birdwatcher
22

33
import (
44
"bytes"
5+
_ "embed"
56
"errors"
7+
"net"
68
"os"
9+
"text/template"
710
)
811

12+
//go:embed templates/functions.tpl
13+
var functionsTemplate string
14+
15+
// make sure prefixPad can be used in templates
16+
var tplFuncs = template.FuncMap{
17+
"prefixPad": prefixPad,
18+
}
19+
920
var errConfigIdentical = errors.New("configuration file is identical")
1021

11-
func updateBirdConfig(filename string, prefixes PrefixCollection) error {
22+
func updateBirdConfig(config Config, prefixes PrefixCollection) error {
1223
// write config to temp file
13-
tmpFilename := filename + ".tmp"
24+
tmpFilename := config.ConfigFile + ".tmp"
1425
// make sure we don't keep tmp file around when something goes wrong
1526
defer func(x string) {
1627
if _, err := os.Stat(x); !os.IsNotExist(err) {
@@ -19,20 +30,20 @@ func updateBirdConfig(filename string, prefixes PrefixCollection) error {
1930
}
2031
}(tmpFilename)
2132

22-
if err := writeBirdConfig(tmpFilename, prefixes); err != nil {
33+
if err := writeBirdConfig(tmpFilename, prefixes, config.CompatBird213); err != nil {
2334
return err
2435
}
2536

2637
// compare new file with original config file
27-
if compareFiles(tmpFilename, filename) {
38+
if compareFiles(tmpFilename, config.ConfigFile) {
2839
return errConfigIdentical
2940
}
3041

3142
// move tmp file to right place
32-
return os.Rename(tmpFilename, filename)
43+
return os.Rename(tmpFilename, config.ConfigFile)
3344
}
3445

35-
func writeBirdConfig(filename string, prefixes PrefixCollection) error {
46+
func writeBirdConfig(filename string, prefixes PrefixCollection, compatBird213 bool) error {
3647
var err error
3748

3849
// open file
@@ -41,20 +52,42 @@ func writeBirdConfig(filename string, prefixes PrefixCollection) error {
4152
return err
4253
}
4354

44-
// prepare content with a header
45-
output := "# DO NOT EDIT MANUALLY\n"
55+
tmpl := template.Must(template.New("func").Funcs(tplFuncs).Parse(functionsTemplate))
4656

47-
// append marshalled prefixsets
48-
for _, p := range prefixes {
49-
output += p.Marshal()
57+
tplBody := struct {
58+
Collections PrefixCollection
59+
CompatBird213 bool
60+
}{
61+
Collections: prefixes,
62+
CompatBird213: compatBird213,
63+
}
64+
65+
var buf bytes.Buffer
66+
if err := tmpl.Execute(&buf, tplBody); err != nil {
67+
return err
5068
}
5169

5270
// write data to file
53-
_, err = f.WriteString(output)
71+
_, err = f.Write(buf.Bytes())
5472

5573
return err
5674
}
5775

76+
// prefixPad is a helper function for the template
77+
// basically returns CIDR notations per IPNet, each suffixed with a , except for
78+
// the last entry
79+
func prefixPad(x []net.IPNet) []string {
80+
pp := make([]string, len(x))
81+
for i, p := range x {
82+
pp[i] = p.String()
83+
if i < len(x)-1 {
84+
pp[i] += ","
85+
}
86+
}
87+
88+
return pp
89+
}
90+
5891
func compareFiles(fileA, fileB string) bool {
5992
data, err := os.ReadFile(fileA)
6093
if err != nil {

birdwatcher/bird_test.go

+89-28
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package birdwatcher
33
import (
44
"net"
55
"os"
6+
"strings"
67
"testing"
78

89
"github.com/stretchr/testify/assert"
@@ -12,44 +13,104 @@ import (
1213
func TestWriteBirdConfig(t *testing.T) {
1314
t.Parallel()
1415

15-
// open tempfile
16-
tmpFile, err := os.CreateTemp("", "bird_test")
17-
require.NoError(t, err)
18-
defer os.Remove(tmpFile.Name())
16+
t.Run("empty config", func(t *testing.T) {
17+
t.Parallel()
1918

20-
prefixes := make(PrefixCollection)
21-
prefixes["match_route"] = NewPrefixSet("match_route")
19+
// open tempfile
20+
tmpFile, err := os.CreateTemp("", "bird_test")
21+
require.NoError(t, err)
22+
defer os.Remove(tmpFile.Name())
2223

23-
// write bird config with empty prefix list
24-
err = writeBirdConfig(tmpFile.Name(), prefixes)
25-
require.NoError(t, err)
24+
prefixes := make(PrefixCollection)
25+
prefixes["match_route"] = NewPrefixSet("match_route")
2626

27-
// read data from temp file and compare it to file fixture
28-
data, err := os.ReadFile(tmpFile.Name())
29-
require.NoError(t, err)
27+
// write bird config with empty prefix list
28+
err = writeBirdConfig(tmpFile.Name(), prefixes, false)
29+
require.NoError(t, err)
3030

31-
fixture, err := os.ReadFile("testdata/bird/config_empty")
32-
require.NoError(t, err)
31+
// read data from temp file and compare it to file fixture
32+
data, err := os.ReadFile(tmpFile.Name())
33+
require.NoError(t, err)
3334

34-
assert.Equal(t, fixture, data)
35+
fixture, err := os.ReadFile("testdata/bird/config_empty")
36+
require.NoError(t, err)
3537

36-
for _, pref := range []string{"1.2.3.4/32", "2.3.4.5/26", "3.4.5.6/24", "4.5.6.7/21"} {
37-
_, prf, _ := net.ParseCIDR(pref)
38-
prefixes["match_route"].Add(*prf)
39-
}
38+
assert.Equal(t, string(fixture), string(data))
39+
})
4040

41-
// write bird config to it
42-
err = writeBirdConfig(tmpFile.Name(), prefixes)
43-
require.NoError(t, err)
41+
t.Run("one prefixset", func(t *testing.T) {
42+
t.Parallel()
4443

45-
// read data from temp file and compare it to file fixture
46-
data, err = os.ReadFile(tmpFile.Name())
47-
require.NoError(t, err)
44+
// open tempfile
45+
tmpFile, err := os.CreateTemp("", "bird_test")
46+
require.NoError(t, err)
47+
defer os.Remove(tmpFile.Name())
4848

49-
fixture, err = os.ReadFile("testdata/bird/config")
50-
require.NoError(t, err)
49+
prefixes := make(PrefixCollection)
50+
prefixes["match_route"] = NewPrefixSet("match_route")
51+
52+
for _, pref := range []string{"1.2.3.4/32", "2.3.4.5/26", "3.4.5.6/24", "4.5.6.7/21"} {
53+
_, prf, _ := net.ParseCIDR(pref)
54+
prefixes["match_route"].Add(*prf)
55+
}
56+
57+
// write bird config to it
58+
err = writeBirdConfig(tmpFile.Name(), prefixes, false)
59+
require.NoError(t, err)
60+
61+
// read data from temp file and compare it to file fixture
62+
data, err := os.ReadFile(tmpFile.Name())
63+
require.NoError(t, err)
64+
65+
fixture, err := os.ReadFile("testdata/bird/config")
66+
require.NoError(t, err)
67+
68+
assert.Equal(t, string(fixture), string(data))
69+
})
70+
71+
t.Run("one prefix, compat", func(t *testing.T) {
72+
t.Parallel()
73+
74+
// open tempfile
75+
tmpFile, err := os.CreateTemp("", "bird_test")
76+
require.NoError(t, err)
77+
defer os.Remove(tmpFile.Name())
78+
79+
prefixes := make(PrefixCollection)
80+
81+
prefixes["other_function"] = NewPrefixSet("other_function")
82+
for _, pref := range []string{"5.6.7.8/32", "6.7.8.9/26", "7.8.9.10/24"} {
83+
_, prf, _ := net.ParseCIDR(pref)
84+
prefixes["other_function"].Add(*prf)
85+
}
86+
87+
// write bird config to it
88+
err = writeBirdConfig(tmpFile.Name(), prefixes, true)
89+
require.NoError(t, err)
90+
91+
// read data from temp file and compare it to file fixture
92+
data, err := os.ReadFile(tmpFile.Name())
93+
require.NoError(t, err)
94+
95+
fixture, err := os.ReadFile("testdata/bird/config_compat")
96+
require.NoError(t, err)
97+
98+
assert.Equal(t, string(fixture), string(data))
99+
})
100+
}
101+
102+
func TestPrefixPad(t *testing.T) {
103+
t.Parallel()
104+
105+
prefixes := make([]net.IPNet, 4)
106+
107+
for i, pref := range []string{"1.2.3.0/24", "2.3.4.0/24", "3.4.5.0/24", "3.4.5.0/26"} {
108+
_, prf, _ := net.ParseCIDR(pref)
109+
prefixes[i] = *prf
110+
}
51111

52-
assert.Equal(t, fixture, data)
112+
padded := prefixPad(prefixes)
113+
assert.Equal(t, "1.2.3.0/24,2.3.4.0/24,3.4.5.0/24,3.4.5.0/26", strings.Join(padded, ""))
53114
}
54115

55116
func TestBirdCompareFiles(t *testing.T) {

birdwatcher/config.go

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
type Config struct {
1515
ConfigFile string
1616
ReloadCommand string
17+
CompatBird213 bool
1718
Prometheus PrometheusConfig
1819
Services map[string]*ServiceCheck
1920
}

birdwatcher/config_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ func TestConfig(t *testing.T) {
130130

131131
assert.Equal(t, "/etc/birdwatcher.conf", testConf.ConfigFile)
132132
assert.Equal(t, "/sbin/birdc configure", testConf.ReloadCommand)
133+
assert.True(t, testConf.CompatBird213)
134+
133135
assert.True(t, testConf.Prometheus.Enabled)
134136
assert.Equal(t, 1234, testConf.Prometheus.Port)
135137
assert.Equal(t, "/something", testConf.Prometheus.Path)

birdwatcher/healthcheck.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func NewHealthCheck(c Config) HealthCheck {
4949

5050
// Start starts the process of health checking the services and handling
5151
// Actions that come from them
52-
func (h *HealthCheck) Start(services []*ServiceCheck, ready chan bool, status *chan string) {
52+
func (h *HealthCheck) Start(services []*ServiceCheck, ready chan<- bool, status *chan string) {
5353
// copy reference to services
5454
h.services = services
5555
// create channel for service check to push there events on
@@ -157,7 +157,7 @@ func (h *HealthCheck) applyConfig(config Config, prefixes PrefixCollection) erro
157157
})
158158

159159
// update bird config
160-
err := updateBirdConfig(config.ConfigFile, prefixes)
160+
err := updateBirdConfig(config, prefixes)
161161
if err != nil {
162162
// if config did not change, we should still reload if we don't know the
163163
// state of BIRD

birdwatcher/prefixset.go

+10-46
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,10 @@ import (
55
// use embed for embedding the function template
66
_ "embed"
77
"net"
8-
"text/template"
98

109
log "github.com/sirupsen/logrus"
1110
)
1211

13-
//go:embed templates/function.tpl
14-
var functionTemplate string
15-
16-
var tplFuncs = template.FuncMap{
17-
"prefpad": prefixPad,
18-
}
19-
2012
// PrefixCollection represents prefixsets per function name
2113
type PrefixCollection map[string]*PrefixSet
2214

@@ -31,6 +23,16 @@ func NewPrefixSet(functionName string) *PrefixSet {
3123
return &PrefixSet{functionName: functionName}
3224
}
3325

26+
// FunctionName returns the function name
27+
func (p PrefixSet) FunctionName() string {
28+
return p.functionName
29+
}
30+
31+
// Prefixes returns the prefixes
32+
func (p PrefixSet) Prefixes() []net.IPNet {
33+
return p.prefixes
34+
}
35+
3436
// Add adds a prefix to the PrefixSet if it wasn't already in it
3537
func (p *PrefixSet) Add(prefix net.IPNet) {
3638
pLog := log.WithFields(log.Fields{
@@ -72,41 +74,3 @@ func (p *PrefixSet) Remove(prefix net.IPNet) {
7274

7375
pLog.Warn("prefix not found in prefix set, skipping")
7476
}
75-
76-
// Marshal returns the BIRD function for this prefixset
77-
func (p PrefixSet) Marshal() string {
78-
// init template
79-
tmpl := template.Must(template.New("func").Funcs(tplFuncs).Parse(functionTemplate))
80-
81-
// init template body
82-
tplBody := struct {
83-
FunctionName string
84-
Prefixes []net.IPNet
85-
}{
86-
FunctionName: p.functionName,
87-
Prefixes: p.prefixes,
88-
}
89-
90-
// execute template and return output
91-
var buf bytes.Buffer
92-
if err := tmpl.Execute(&buf, tplBody); err != nil {
93-
log.WithError(err).Error("could not parse template body")
94-
}
95-
96-
return buf.String()
97-
}
98-
99-
// prefixPad is a helper function for the template
100-
// basically returns CIDR notations per IPNet, each suffixed with a , except for
101-
// the last entry
102-
func prefixPad(x []net.IPNet) []string {
103-
pp := make([]string, len(x))
104-
for i, p := range x {
105-
pp[i] = p.String()
106-
if i < len(x)-1 {
107-
pp[i] += ","
108-
}
109-
}
110-
111-
return pp
112-
}

0 commit comments

Comments
 (0)