Skip to content

Commit dd4f9f0

Browse files
authored
Add E2E tests and example for VRFs (#1189)
Create E2E tests and add an example for VRFs to make sure that the feature works. Reported-at: #847 Signed-off-by: Andreas Karis <[email protected]>
1 parent eefd3b0 commit dd4f9f0

File tree

6 files changed

+159
-5
lines changed

6 files changed

+159
-5
lines changed

docs/examples.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ toc_sticky: true
8383
{% include_absolute 'examples/vlan.yaml' %}
8484
```
8585

86+
## VRF
87+
88+
```yaml
89+
{% include_absolute 'examples/vrf.yaml' %}
90+
```
91+
8692
## Worker selector
8793

8894
```yaml

docs/examples/vrf.yaml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
apiVersion: nmstate.io/v1
2+
kind: NodeNetworkConfigurationPolicy
3+
metadata:
4+
name: vrf
5+
spec:
6+
desiredState:
7+
interfaces:
8+
- name: vrf102
9+
state: up
10+
type: vrf
11+
vrf:
12+
port:
13+
- eth1
14+
route-table-id: 102
15+
- ipv4:
16+
address:
17+
- ip: 192.0.2.251
18+
prefix-length: 24
19+
dhcp: false
20+
enabled: true
21+
name: eth1
22+
state: up
23+
type: ethernet
24+
routes:
25+
config:
26+
- destination: 198.51.100.0/24
27+
metric: 150
28+
next-hop-address: 192.0.2.1
29+
next-hop-interface: eth1
30+
table-id: 102
31+
nodeSelector:
32+
kubernetes.io/hostname: node01

test/e2e/handler/states.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,28 @@ func vlanAbsent(iface, vlanID string) nmstate.State {
215215
`, iface, vlanID, iface, vlanID))
216216
}
217217

218+
// vrfUp creates VRF vrfID and adds the interfaces from ifaceList to that VRF.
219+
func vrfUp(vrfID string, ifaceList ...string) nmstate.State {
220+
ifaces := strings.Join(ifaceList, ",")
221+
return nmstate.NewState(fmt.Sprintf(`interfaces:
222+
- name: vrf%s
223+
type: vrf
224+
state: up
225+
vrf:
226+
port: [%s]
227+
route-table-id: %[1]s
228+
`, vrfID, ifaces))
229+
}
230+
231+
// vrfAbsent removes VRF vrfID.
232+
func vrfAbsent(vrfID string) nmstate.State {
233+
return nmstate.NewState(fmt.Sprintf(`interfaces:
234+
- name: vrf%s
235+
type: vrf
236+
state: absent
237+
`, vrfID))
238+
}
239+
218240
func interfaceAbsent(iface string) nmstate.State {
219241
return nmstate.NewState(fmt.Sprintf(`interfaces:
220242
- name: %s

test/e2e/handler/static_addr_and_route_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,17 @@ import (
2626
nmstate "github.com/nmstate/kubernetes-nmstate/api/shared"
2727
)
2828

29+
// ipV4AddrAndRoute creates an IP address and routes for the default routing table.
2930
func ipV4AddrAndRoute(firstSecondaryNic, ipAddress, destIPAddress, prefixLen, nextHopIPAddress string) nmstate.State {
31+
return ipV4AddrAndRouteWithTableID(firstSecondaryNic, ipAddress, destIPAddress, prefixLen, nextHopIPAddress, "")
32+
}
33+
34+
// ipV4AddrAndRouteWithTableID creates an IP address and routes for a given routing table ID.
35+
func ipV4AddrAndRouteWithTableID(firstSecondaryNic, ipAddress, destIPAddress, prefixLen, nextHopIPAddress,
36+
tableID string) nmstate.State {
37+
if tableID == "" {
38+
tableID = "254"
39+
}
3040
return nmstate.NewState(fmt.Sprintf(`interfaces:
3141
- name: %s
3242
type: ethernet
@@ -43,8 +53,8 @@ routes:
4353
metric: 150
4454
next-hop-address: %s
4555
next-hop-interface: %s
46-
table-id: 254
47-
`, firstSecondaryNic, ipAddress, prefixLen, destIPAddress, nextHopIPAddress, firstSecondaryNic))
56+
table-id: %s
57+
`, firstSecondaryNic, ipAddress, prefixLen, destIPAddress, nextHopIPAddress, firstSecondaryNic, tableID))
4858
}
4959

5060
func ipV4AddrAndRouteAbsent(firstSecondaryNic string) nmstate.State {

test/e2e/handler/utils.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,7 @@ func updateDesiredStateWithCaptureAtNodeAndWait(node string, desiredState nmstat
196196
policy.WaitForAvailableTestPolicy()
197197
}
198198

199-
// TODO: After we implement policy delete (it will cleanUp desiredState) we have
200-
// to remove this
199+
// TODO: After we implement policy delete (it will cleanUp desiredState) we have to remove this.
201200
func resetDesiredStateForNodes() {
202201
By("Resetting nics state primary up and secondaries disable ipv4 and ipv6")
203202
updateDesiredState(resetPrimaryAndSecondaryNICs())
@@ -399,6 +398,13 @@ func vlanForNodeInterfaceEventually(node, iface string) AsyncAssertion {
399398
}, ReadTimeout, ReadInterval)
400399
}
401400

401+
// vrfForNodeInterfaceEventually asserts that VRF with vrfID is eventually created.
402+
func vrfForNodeInterfaceEventually(node, vrfID string) AsyncAssertion {
403+
return Eventually(func() string {
404+
return vrf(node, vrfID)
405+
}, ReadTimeout, ReadInterval)
406+
}
407+
402408
func interfacesForNode(node string) AsyncAssertion {
403409
return Eventually(func() []interface{} {
404410
var currentStateYaml nmstate.State
@@ -612,9 +618,19 @@ func routeDest(node, destIP string) string {
612618
return gjson.ParseBytes(currentStateJSON(node)).Get(path).String()
613619
}
614620

621+
// routeNextHopInterfaceWithTableID checks if a route with destIP exists in the default routing table.
615622
func routeNextHopInterface(node, destIP string) AsyncAssertion {
623+
return routeNextHopInterfaceWithTableID(node, destIP, "")
624+
}
625+
626+
// routeNextHopInterfaceWithTableID checks if a route with destIP exists in table tableID. If tableID is the empty
627+
// string, use the default table-id (254).
628+
func routeNextHopInterfaceWithTableID(node, destIP, tableID string) AsyncAssertion {
629+
if tableID == "" {
630+
tableID = "254"
631+
}
616632
return Eventually(func() string {
617-
path := fmt.Sprintf("routes.running.#(destination==%q).next-hop-interface", destIP)
633+
path := fmt.Sprintf("routes.running.#(table-id==%s)#|#(destination==%q).next-hop-interface", tableID, destIP)
618634
return gjson.ParseBytes(currentStateJSON(node)).Get(path).String()
619635
}, 15*time.Second, 1*time.Second)
620636
}
@@ -624,6 +640,12 @@ func vlan(node, iface string) string {
624640
return gjson.ParseBytes(currentStateJSON(node)).Get(vlanFilter).String()
625641
}
626642

643+
// vrf verifies if the VRF with vrfID was created on node.
644+
func vrf(node, vrfID string) string {
645+
vrfFilter := fmt.Sprintf("interfaces.#(name==vrf%s).vrf.route-table-id", vrfID)
646+
return gjson.ParseBytes(currentStateJSON(node)).Get(vrfFilter).String()
647+
}
648+
627649
func kubectlAndCheck(command ...string) {
628650
out, err := cmd.Kubectl(command...)
629651
Expect(err).ShouldNot(HaveOccurred(), out)

test/e2e/handler/vrf_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
Copyright The Kubernetes NMState Authors.
3+
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package handler
19+
20+
import (
21+
. "github.com/onsi/ginkgo/v2"
22+
. "github.com/onsi/gomega"
23+
)
24+
25+
var _ = Describe("NodeNetworkState", func() {
26+
var (
27+
node string
28+
)
29+
30+
BeforeEach(func() {
31+
node = nodes[0]
32+
})
33+
34+
Context("when VRF configured", func() {
35+
var (
36+
vrfID = "102"
37+
ipAddress = "192.0.2.251"
38+
destIPAddress = "198.51.100.0/24"
39+
prefixLen = "24"
40+
nextHopIPAddress = "192.0.2.1"
41+
)
42+
43+
BeforeEach(func() {
44+
updateDesiredStateAtNodeAndWait(node, vrfUp(vrfID, firstSecondaryNic))
45+
updateDesiredStateAtNodeAndWait(
46+
node,
47+
ipV4AddrAndRouteWithTableID(firstSecondaryNic, ipAddress, destIPAddress, prefixLen, nextHopIPAddress, vrfID),
48+
)
49+
})
50+
51+
AfterEach(func() {
52+
updateDesiredStateAtNodeAndWait(node, vrfAbsent(vrfID))
53+
resetDesiredStateForNodes()
54+
})
55+
56+
It("should have the VRF interface configured", func() {
57+
vrfForNodeInterfaceEventually(node, vrfID).Should(Equal(vrfID))
58+
ipAddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(Equal(ipAddress))
59+
routeNextHopInterfaceWithTableID(node, destIPAddress, vrfID).Should(Equal(firstSecondaryNic))
60+
})
61+
})
62+
})

0 commit comments

Comments
 (0)