Skip to content

Commit

Permalink
Support Shared Private Networks (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwindower authored Oct 22, 2020
1 parent 58ea11d commit 7ce566a
Show file tree
Hide file tree
Showing 40 changed files with 1,146 additions and 209 deletions.
5 changes: 1 addition & 4 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ on:
pull_request:
branches:
- master
types:
- assigned
- edited

env:
GCS_BUCKET: images.metal-pod.io
Expand All @@ -29,7 +26,7 @@ jobs:
run: make
- name: Build and push docker image
run: |
export GITHUB_TAG_NAME=${GITHUB_REF##*/}
export GITHUB_TAG_NAME=${GITHUB_HEAD_REF##*/}
docker login -u metalstackci -p ${{ secrets.DOCKER_HUB_TOKEN }}
make docker-build
make docker-push
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ go 1.15

require (
github.com/coreos/go-systemd/v22 v22.1.0
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/google/go-cmp v0.5.1
github.com/google/go-cmp v0.5.2
github.com/magiconair/properties v1.8.3 // indirect
github.com/metal-stack/metal-go v0.10.0
github.com/metal-stack/metal-lib v0.6.3
github.com/metal-stack/v v1.0.2
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
Expand All @@ -22,6 +23,5 @@ require (
go.uber.org/zap v1.16.0
golang.org/x/sys v0.0.0-20200915084602-288bc346aa39 // indirect
gopkg.in/ini.v1 v1.61.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
)
547 changes: 547 additions & 0 deletions go.sum

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions internal/netconf/chrony.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package netconf
import (
"fmt"

mn "github.com/metal-stack/metal-lib/pkg/net"
"github.com/metal-stack/metal-networker/pkg/exec"
)

Expand All @@ -26,11 +27,11 @@ func (c ChronyServiceEnabler) Enable() error {
}

func getDefaultRouteVRFName(kb KnowledgeBase) (string, error) {
networks := kb.GetNetworks(Public)
networks := kb.GetNetworks(mn.External)
for _, network := range networks {
for _, prefix := range network.Destinationprefixes {
if prefix == AllZerosCIDR {
vrf := fmt.Sprintf("vrf%d", network.Vrf)
vrf := fmt.Sprintf("vrf%d", *network.Vrf)
return vrf, nil
}
}
Expand Down
10 changes: 7 additions & 3 deletions internal/netconf/chrony_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@ package netconf
import (
"testing"

"github.com/metal-stack/metal-go/api/models"
mn "github.com/metal-stack/metal-lib/pkg/net"
"github.com/stretchr/testify/assert"
)

func TestChronyServiceEnabler_Enable(t *testing.T) {
assert := assert.New(t)

network := Network{Private: false, Underlay: false, Destinationprefixes: []string{AllZerosCIDR}, Vrf: 104009}
vrf := int64(104009)
external := mn.External
network := models.V1MachineNetwork{Networktype: &external, Destinationprefixes: []string{AllZerosCIDR}, Vrf: &vrf}
tests := []struct {
kb KnowledgeBase
vrf string
isErrorExpected bool
}{
{kb: KnowledgeBase{Networks: []Network{network}},
{kb: KnowledgeBase{Networks: []models.V1MachineNetwork{network}},
vrf: "vrf104009",
isErrorExpected: false},
{kb: KnowledgeBase{Networks: []Network{}},
{kb: KnowledgeBase{Networks: []models.V1MachineNetwork{}},
vrf: "",
isErrorExpected: true},
}
Expand Down
10 changes: 4 additions & 6 deletions internal/netconf/droptailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@ func NewDroptailerServiceApplier(kb KnowledgeBase, v net.Validator) (net.Applier
}

func getTenantVRFName(kb KnowledgeBase) (string, error) {
networks := kb.GetNetworks(Private)
for _, network := range networks {
if network.Vrf != 0 {
vrf := fmt.Sprintf("vrf%d", network.Vrf)
return vrf, nil
}
primary := kb.getPrivatePrimaryNetwork()
if primary.Vrf != nil && *primary.Vrf != 0 {
vrf := fmt.Sprintf("vrf%d", *primary.Vrf)
return vrf, nil
}

return "", fmt.Errorf("there is no private tenant network")
Expand Down
10 changes: 5 additions & 5 deletions internal/netconf/firewall_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type FirewallControllerData struct {
Comment string
DefaultRouteVrf string
ServiceIP string
PrivateVrfID int
PrivateVrfID int64
}

// NewFirewallControllerServiceApplier constructs a new instance of this type.
Expand All @@ -27,16 +27,16 @@ func NewFirewallControllerServiceApplier(kb KnowledgeBase, v net.Validator) (net
return nil, err
}

if len(kb.getPrivateNetwork().Ips) == 0 {
if len(kb.getPrivatePrimaryNetwork().Ips) == 0 {
return nil, fmt.Errorf("no private IP found useable for the firewall controller")
}
serviceIP := kb.getPrivateNetwork().Ips[0]
privateVrfID := kb.getPrivateNetwork().Vrf
serviceIP := kb.getPrivatePrimaryNetwork().Ips[0]
privateVrfID := kb.getPrivatePrimaryNetwork().Vrf
data := FirewallControllerData{
Comment: versionHeader(kb.Machineuuid),
DefaultRouteVrf: defaultRouteVrf,
ServiceIP: serviceIP,
PrivateVrfID: privateVrfID,
PrivateVrfID: *privateVrfID,
}

return net.NewNetworkApplier(data, v, nil), nil
Expand Down
72 changes: 46 additions & 26 deletions internal/netconf/frr.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"strconv"
"strings"

"github.com/metal-stack/metal-go/api/models"
mn "github.com/metal-stack/metal-lib/pkg/net"
"github.com/metal-stack/metal-networker/pkg/exec"
"github.com/metal-stack/metal-networker/pkg/net"
)
Expand Down Expand Up @@ -63,7 +65,7 @@ func NewFrrConfigApplier(kind BareMetalType, kb KnowledgeBase, tmpFile string) n
VRFs: assembleVRFs(kb),
}
case Machine:
net := kb.getPrivateNetwork()
net := kb.getPrivatePrimaryNetwork()
data = MachineFRRData{
CommonFRRData: newCommonFRRData(net, kb),
}
Expand All @@ -76,9 +78,9 @@ func NewFrrConfigApplier(kind BareMetalType, kb KnowledgeBase, tmpFile string) n
return net.NewNetworkApplier(data, validator, nil)
}

func newCommonFRRData(net Network, kb KnowledgeBase) CommonFRRData {
func newCommonFRRData(net models.V1MachineNetwork, kb KnowledgeBase) CommonFRRData {
return CommonFRRData{FRRVersion: FRRVersion, Hostname: kb.Hostname, Comment: versionHeader(kb.Machineuuid),
ASN: net.Asn, RouterID: net.Ips[0]}
ASN: *net.Asn, RouterID: net.Ips[0]}
}

// Validate can be used to run validation on FRR configuration using vtysh.
Expand All @@ -89,7 +91,7 @@ func (v FRRValidator) Validate() error {
return exec.NewVerboseCmd("bash", "-c", vtysh, v.path).Run()
}

func getDestinationPrefixes(networks []Network) []string {
func getDestinationPrefixes(networks []models.V1MachineNetwork) []string {
var result []string
for _, network := range networks {
result = append(result, network.Destinationprefixes...)
Expand All @@ -98,7 +100,7 @@ func getDestinationPrefixes(networks []Network) []string {
return result
}

func getPrefixes(networks ...Network) []string {
func getPrefixes(networks ...models.V1MachineNetwork) []string {
var result []string
for _, network := range networks {
result = append(result, network.Prefixes...)
Expand All @@ -110,30 +112,48 @@ func getPrefixes(networks ...Network) []string {
func assembleVRFs(kb KnowledgeBase) []VRF {
var result []VRF

networks := kb.GetNetworks(Private, Public)
privatePrimary := kb.getPrivatePrimaryNetwork()
networks := kb.GetNetworks(mn.PrivatePrimaryUnshared, mn.PrivatePrimaryShared, mn.PrivateSecondaryShared, mn.External)

for _, network := range networks {
var targets []Network

var targets []models.V1MachineNetwork
var prefixes []string

if network.Private {
// reach out from private into to public networks
targets = kb.GetNetworks(Public)
prefixes = getDestinationPrefixes(targets)
} else {
if network.Networktype == nil {
continue
}
nt := *network.Networktype
switch nt {
case mn.PrivatePrimaryUnshared:
fallthrough
case mn.PrivatePrimaryShared:
// reach out from private primary network into public networks
publicTargets := kb.GetNetworks(mn.External)
prefixes = getDestinationPrefixes(publicTargets)
targets = append(targets, publicTargets...)

// reach out from private primary network into shared private networks
privateSharedTargets := kb.GetNetworks(mn.PrivateSecondaryShared)
prefixes = append(prefixes, getPrefixes(privateSharedTargets...)...)
targets = append(targets, privateSharedTargets...)
case mn.PrivateSecondaryShared:
// reach out from private shared networks into private primary network
targets = []models.V1MachineNetwork{privatePrimary}
prefixes = getPrefixes(append(targets, network)...)
case mn.External:
// reach out from public into private and other public networks
targets = kb.GetNetworks(Private)
targets = []models.V1MachineNetwork{privatePrimary}
prefixes = getPrefixes(append(targets, network)...)
}

vrfName := "vrf" + strconv.Itoa(network.Vrf)
prefixLists := assembleIPPrefixListsFor(vrfName, prefixes, IPPrefixListSeqSeed, kb)
shared := (nt == mn.PrivatePrimaryShared || nt == mn.PrivateSecondaryShared)
vrfID := network.Vrf
vrfName := "vrf" + strconv.Itoa(int(*vrfID))
prefixLists := assembleIPPrefixListsFor(vrfName, prefixes, IPPrefixListSeqSeed, kb, shared)
vrf := VRF{
Identity: Identity{
ID: network.Vrf,
ID: int(*network.Vrf),
},
VNI: network.Vrf,
VNI: int(*network.Vrf),
ImportVRFNames: vrfNamesOf(targets...),
IPPrefixLists: prefixLists,
RouteMaps: assembleRouteMapsFor(vrfName, prefixLists),
Expand Down Expand Up @@ -200,11 +220,11 @@ func routeMapName(vrfName string) string {
return vrfName + "-import-map"
}

func vrfNamesOf(networks ...Network) []string {
func vrfNamesOf(networks ...models.V1MachineNetwork) []string {
var result []string

for _, n := range networks {
vrf := fmt.Sprintf("vrf%d", n.Vrf)
vrf := fmt.Sprintf("vrf%d", *n.Vrf)
result = append(result, vrf)
}

Expand All @@ -224,10 +244,10 @@ func buildIPPrefixListSpecs(seq int, prefix string) []string {
return result
}

func assembleIPPrefixListsFor(vrfName string, prefixes []string, seed int, kb KnowledgeBase) []IPPrefixList {
func assembleIPPrefixListsFor(vrfName string, prefixes []string, seed int, kb KnowledgeBase, shared bool) []IPPrefixList {
var result []IPPrefixList

private := kb.getPrivateNetwork()
private := kb.getPrivatePrimaryNetwork()

for _, prefix := range prefixes {
if len(prefix) == 0 {
Expand All @@ -237,7 +257,7 @@ func assembleIPPrefixListsFor(vrfName string, prefixes []string, seed int, kb Kn
specs := buildIPPrefixListSpecs(seed, prefix)

for _, spec := range specs {
name := namePrefixList(vrfName, private, prefix)
name := namePrefixList(vrfName, private, prefix, shared)
prefixList := IPPrefixList{
Name: name,
Spec: spec,
Expand All @@ -251,11 +271,11 @@ func assembleIPPrefixListsFor(vrfName string, prefixes []string, seed int, kb Kn
return result
}

func namePrefixList(vrfName string, private Network, prefix string) string {
func namePrefixList(vrfName string, private models.V1MachineNetwork, prefix string, shared bool) string {
name := vrfName + "-import-prefixes"

for _, privatePrefix := range private.Prefixes {
if privatePrefix == prefix {
if privatePrefix == prefix && !shared {
// tenant private network ip addresses must not be visible in the public VRFs to avoid blown up routing tables
name += IPPrefixListNoExportSuffix
}
Expand Down
35 changes: 23 additions & 12 deletions internal/netconf/frr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,50 @@ import (
)

func TestFrrConfigApplier(t *testing.T) {
assert := assert.New(t)
tests := []struct {
name string
input string
expectedOutput string
configuratorType BareMetalType
tpl string
}{
{
name: "firewall of a shared private network",
input: "testdata/firewall_shared.yaml",
expectedOutput: "testdata/frr.conf.firewall_shared",
configuratorType: Firewall,
tpl: TplFirewallFRR,
},
{
name: "standard firewall with private primary unshared network, private secondary shared network, internet and mpls",
input: "testdata/firewall.yaml",
expectedOutput: "testdata/frr.conf.firewall",
configuratorType: Firewall,
tpl: TplFirewallFRR,
},
{
name: "standard machine",
input: "testdata/machine.yaml",
expectedOutput: "testdata/frr.conf.machine",
configuratorType: Machine,
tpl: TplMachineFRR,
},
}
for _, t := range tests {
expected, err := ioutil.ReadFile(t.expectedOutput)
assert.NoError(err)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
expected, err := ioutil.ReadFile(test.expectedOutput)
assert.NoError(t, err)

kb := NewKnowledgeBase(t.input)
assert.NoError(err)
a := NewFrrConfigApplier(t.configuratorType, kb, "")
b := bytes.Buffer{}
kb := NewKnowledgeBase(test.input)
assert.NoError(t, err)
a := NewFrrConfigApplier(test.configuratorType, kb, "")
b := bytes.Buffer{}

tpl := mustParseTpl(t.tpl)
err = a.Render(&b, *tpl)
assert.NoError(err)
assert.Equal(string(expected), b.String())
tpl := mustParseTpl(test.tpl)
err = a.Render(&b, *tpl)
assert.NoError(t, err)
assert.Equal(t, string(expected), b.String())
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/netconf/hosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type (

// NewHostsApplier creates a new hosts applier.
func NewHostsApplier(kb KnowledgeBase, tmpFile string) net.Applier {
data := HostsData{Hostname: kb.Hostname, Comment: versionHeader(kb.Machineuuid), IP: kb.getPrivateNetwork().Ips[0]}
data := HostsData{Hostname: kb.Hostname, Comment: versionHeader(kb.Machineuuid), IP: kb.getPrivatePrimaryNetwork().Ips[0]}
validator := HostsValidator{tmpFile}

return net.NewNetworkApplier(data, validator, nil)
Expand Down
Loading

0 comments on commit 7ce566a

Please sign in to comment.