github.com/nmstate/kubernetes-nmstate@v0.82.0/test/e2e/handler/default_bridged_network_test.go (about) 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 "context" 22 "fmt" 23 "time" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 28 corev1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/types" 30 31 nmstate "github.com/nmstate/kubernetes-nmstate/api/shared" 32 "github.com/nmstate/kubernetes-nmstate/test/e2e/policy" 33 testenv "github.com/nmstate/kubernetes-nmstate/test/env" 34 ) 35 36 func createBridgeOnTheDefaultInterface() nmstate.State { 37 return nmstate.NewState(fmt.Sprintf(`interfaces: 38 - name: brext 39 type: linux-bridge 40 state: up 41 ipv4: 42 dhcp: true 43 enabled: true 44 bridge: 45 options: 46 stp: 47 enabled: false 48 port: 49 - name: %s 50 `, primaryNic)) 51 } 52 53 func resetDefaultInterface() nmstate.State { 54 return nmstate.NewState(fmt.Sprintf(`interfaces: 55 - name: %s 56 type: ethernet 57 state: up 58 ipv4: 59 enabled: true 60 dhcp: true 61 - name: brext 62 type: linux-bridge 63 state: absent 64 `, primaryNic)) 65 } 66 67 var _ = Describe("NodeNetworkConfigurationPolicy default bridged network", func() { 68 var ( 69 DefaultNetwork = "default-network" 70 ) 71 Context("when there is a default interface with dynamic address", func() { 72 addressByNode := map[string]string{} 73 74 BeforeEach(func() { 75 Byf("Check %s is the default route interface and has dynamic address", primaryNic) 76 for _, node := range nodes { 77 defaultRouteNextHopInterface(node).Should(Equal(primaryNic)) 78 Expect(dhcpFlag(node, primaryNic)).Should(BeTrue()) 79 } 80 81 By("Fetching current IP address") 82 for _, node := range nodes { 83 address := "" 84 Eventually(func() string { 85 address = ipv4Address(node, primaryNic) 86 return address 87 }, 15*time.Second, 1*time.Second).ShouldNot(BeEmpty(), fmt.Sprintf("Interface %s has no ipv4 address", primaryNic)) 88 addressByNode[node] = address 89 } 90 }) 91 92 Context("and linux bridge is configured on top of the default interface", func() { 93 BeforeEach(func() { 94 By("Creating the policy") 95 setDesiredStateWithPolicy(DefaultNetwork, createBridgeOnTheDefaultInterface()) 96 97 By("Waiting until the node becomes ready again") 98 waitForNodesReady() 99 100 By("Waiting for policy to be ready") 101 policy.WaitForAvailablePolicy(DefaultNetwork) 102 }) 103 104 AfterEach(func() { 105 Byf("Removing bridge and configuring %s with dhcp", primaryNic) 106 setDesiredStateWithPolicy(DefaultNetwork, resetDefaultInterface()) 107 108 By("Waiting until the node becomes ready again") 109 waitForNodesReady() 110 111 By("Wait for policy to be ready") 112 policy.WaitForAvailablePolicy(DefaultNetwork) 113 114 Byf("Check %s has the default ip address", primaryNic) 115 for _, node := range nodes { 116 Eventually( 117 func() string { 118 return ipv4Address(node, primaryNic) 119 }, 120 30*time.Second, 121 1*time.Second, 122 ).Should(Equal(addressByNode[node]), fmt.Sprintf("Interface %s address is not the original one", primaryNic)) 123 } 124 125 Byf("Check %s is back as the default route interface", primaryNic) 126 for _, node := range nodes { 127 defaultRouteNextHopInterface(node).Should(Equal(primaryNic)) 128 } 129 130 By("Remove the policy") 131 deletePolicy(DefaultNetwork) 132 133 By("Reset desired state at all nodes") 134 resetDesiredStateForNodes() 135 }) 136 137 It("should successfully move default IP address on top of the bridge", func() { 138 checkThatBridgeTookOverTheDefaultIP(nodes, "brext", addressByNode) 139 }) 140 141 It("should keep the default IP address after node reboot", func() { 142 nodeToReboot := nodes[0] 143 144 restartNodeWithoutWaiting(nodeToReboot) 145 146 By("Wait for policy re-reconciled after node reboot") 147 policy.WaitForPolicyTransitionUpdate(DefaultNetwork) 148 policy.WaitForAvailablePolicy(DefaultNetwork) 149 150 Byf("Node %s was rebooted, verifying that bridge took over the default IP", nodeToReboot) 151 checkThatBridgeTookOverTheDefaultIP([]string{nodeToReboot}, "brext", addressByNode) 152 }) 153 }) 154 }) 155 }) 156 157 func nodeReadyConditionStatus(nodeName string) (corev1.ConditionStatus, error) { 158 key := types.NamespacedName{Name: nodeName} 159 node := corev1.Node{} 160 // We use a special context here to ensure that Client.Get does not 161 // get stuck and honor the Eventually timeout and interval values. 162 // It will return a timeout error in case of .Get takes more time than 163 // expected so Eventually will retry after expected interval value. 164 oneSecondTimeoutCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 165 defer cancel() 166 err := testenv.Client.Get(oneSecondTimeoutCtx, key, &node) 167 if err != nil { 168 return "", err 169 } 170 for _, condition := range node.Status.Conditions { 171 if condition.Type == corev1.NodeReady { 172 return condition.Status, nil 173 } 174 } 175 return corev1.ConditionUnknown, nil 176 } 177 178 func waitForNodesReady() { 179 time.Sleep(5 * time.Second) 180 for _, node := range nodes { 181 EventuallyWithOffset(1, func() (corev1.ConditionStatus, error) { 182 return nodeReadyConditionStatus(node) 183 }, 5*time.Minute, 10*time.Second).Should(Equal(corev1.ConditionTrue)) 184 } 185 } 186 187 func checkThatBridgeTookOverTheDefaultIP(nodesToCheck []string, bridgeName string, addressByNode map[string]string) { 188 By("Verifying that the bridge obtained node's default IP") 189 for _, node := range nodesToCheck { 190 Eventually( 191 func() string { 192 return ipv4Address(node, bridgeName) 193 }, 194 15*time.Second, 195 1*time.Second, 196 ).Should(Equal(addressByNode[node]), fmt.Sprintf("Interface %s has not take over the %s address", bridgeName, primaryNic)) 197 } 198 199 By("Verify that next-hop-interface for default route is the bridge") 200 for _, node := range nodesToCheck { 201 defaultRouteNextHopInterface(node).Should(Equal(bridgeName)) 202 203 By("Verify that VLAN configuration is done properly") 204 hasVlans(node, primaryNic, 2, 4094).Should(Succeed()) 205 getVLANFlagsEventually(node, bridgeName, 1).Should(ConsistOf("PVID", Or(Equal("Egress Untagged"), Equal("untagged")))) 206 } 207 }