github.com/nmstate/kubernetes-nmstate@v0.82.0/test/e2e/handler/static_addr_and_route_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  	"fmt"
    22  
    23  	. "github.com/onsi/ginkgo/v2"
    24  	. "github.com/onsi/gomega"
    25  
    26  	nmstate "github.com/nmstate/kubernetes-nmstate/api/shared"
    27  )
    28  
    29  // ipV4AddrAndRoute creates an IP address and routes for the default routing table.
    30  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  	}
    40  	return nmstate.NewState(fmt.Sprintf(`interfaces:
    41    - name: %s
    42      type: ethernet
    43      state: up
    44      ipv4:
    45        address:
    46        - ip: %s
    47          prefix-length: %s
    48        dhcp: false
    49        enabled: true
    50  routes:
    51      config:
    52      - destination: %s
    53        metric: 150
    54        next-hop-address: %s
    55        next-hop-interface: %s
    56        table-id: %s
    57  `, firstSecondaryNic, ipAddress, prefixLen, destIPAddress, nextHopIPAddress, firstSecondaryNic, tableID))
    58  }
    59  
    60  func ipV4AddrAndRouteAbsent(firstSecondaryNic string) nmstate.State {
    61  	return nmstate.NewState(fmt.Sprintf(`interfaces:
    62    - name: %s
    63      type: ethernet
    64      state: up
    65      ipv4:
    66        enabled: false
    67  routes:
    68      config:
    69      - next-hop-interface: %s
    70        state: absent
    71  `, firstSecondaryNic, firstSecondaryNic))
    72  }
    73  
    74  func ipV4AddrAndRouteAndBridgeAbsent(firstSecondaryNic, bridgeName string) nmstate.State {
    75  	return nmstate.NewState(fmt.Sprintf(`interfaces:
    76    - name: %s
    77      type: ethernet
    78      state: up
    79      ipv4:
    80        enabled: false
    81    - name: %s
    82      type: linux-bridge
    83      state: absent
    84  routes:
    85      config:
    86      - next-hop-interface: %s
    87        state: absent
    88  `, firstSecondaryNic, bridgeName, bridgeName))
    89  }
    90  
    91  func ipV6Addr(firstSecondaryNic, ipAddressV6, prefixLenV6 string) nmstate.State {
    92  	return nmstate.NewState(fmt.Sprintf(`interfaces:
    93    - name: %s
    94      type: ethernet
    95      state: up
    96      ipv6:
    97        address:
    98        - ip: %s
    99          prefix-length: %s
   100        dhcp: false
   101        enabled: true
   102  `, firstSecondaryNic, ipAddressV6, prefixLenV6))
   103  }
   104  
   105  func ipV6AddrAbsent(firstSecondaryNic string) nmstate.State {
   106  	return nmstate.NewState(fmt.Sprintf(`interfaces:
   107    - name: %s
   108      type: ethernet
   109      state: up
   110      ipv6:
   111        enabled: false
   112  `, firstSecondaryNic))
   113  }
   114  
   115  func ipV6AddrAndRoute(firstSecondaryNic, ipAddressV6, destIPAddressV6, prefixLenV6, nextHopIPAddressV6 string) nmstate.State {
   116  	return nmstate.NewState(fmt.Sprintf(`interfaces:
   117    - name: %s
   118      type: ethernet
   119      state: up
   120      ipv6:
   121        address:
   122        - ip: %s
   123          prefix-length: %s
   124        dhcp: false
   125        enabled: true
   126  routes:
   127      config:
   128      - destination: %s
   129        metric: 150
   130        next-hop-address: %s
   131        next-hop-interface: %s
   132        table-id: 254
   133  `, firstSecondaryNic, ipAddressV6, prefixLenV6, destIPAddressV6, nextHopIPAddressV6, firstSecondaryNic))
   134  }
   135  
   136  func ipV6AddrAndRouteAbsent(firstSecondaryNic string) nmstate.State {
   137  	return nmstate.NewState(fmt.Sprintf(`interfaces:
   138    - name: %s
   139      type: ethernet
   140      state: up
   141      ipv6:
   142        enabled: false
   143  routes:
   144      config:
   145      - next-hop-interface: %s
   146        state: absent
   147  `, firstSecondaryNic, firstSecondaryNic))
   148  }
   149  
   150  var _ = Describe("Static addresses and routes", func() {
   151  	Context("when desiredState is configured", func() {
   152  		var (
   153  			node               string
   154  			ipAddress          = "192.0.2.251"
   155  			destIPAddress      = "198.51.100.0/24"
   156  			prefixLen          = "24"
   157  			nextHopIPAddress   = "192.0.2.1"
   158  			ipAddressV6        = "2001:db8::1:1"
   159  			prefixLenV6        = "64"
   160  			destIPAddressV6    = "2001:dc8::/64"
   161  			nextHopIPAddressV6 = "2001:db8::1:2"
   162  			bridgeName         = "brext"
   163  		)
   164  		BeforeEach(func() {
   165  			node = nodes[0]
   166  		})
   167  		Context("with static V4 address", func() {
   168  			BeforeEach(func() {
   169  
   170  				updateDesiredStateAtNodeAndWait(node, ifaceUpWithStaticIP(firstSecondaryNic, ipAddress, prefixLen))
   171  
   172  			})
   173  			AfterEach(func() {
   174  				updateDesiredStateAndWait(ifaceUpWithStaticIPAbsent(firstSecondaryNic))
   175  				ipAddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(BeEmpty())
   176  				resetDesiredStateForNodes()
   177  			})
   178  			It("should have the static V4 address", func() {
   179  				ipAddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(Equal(ipAddress))
   180  			})
   181  		})
   182  
   183  		Context("with static V4 address and route", func() {
   184  			BeforeEach(func() {
   185  				updateDesiredStateAtNodeAndWait(
   186  					node,
   187  					ipV4AddrAndRoute(firstSecondaryNic, ipAddress, destIPAddress, prefixLen, nextHopIPAddress),
   188  				)
   189  			})
   190  			AfterEach(func() {
   191  				updateDesiredStateAndWait(ipV4AddrAndRouteAbsent(firstSecondaryNic))
   192  				ipAddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(BeEmpty())
   193  				routeDestForNodeInterfaceEventually(node, destIPAddress).ShouldNot(Equal(firstSecondaryNic))
   194  				resetDesiredStateForNodes()
   195  			})
   196  			It("should have the static V4 address and route  at currentState", func() {
   197  				ipAddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(Equal(ipAddress))
   198  				routeNextHopInterface(node, destIPAddress).Should(Equal(firstSecondaryNic))
   199  			})
   200  		})
   201  
   202  		Context("with bridge taking over the static networking of the slave", func() {
   203  			BeforeEach(func() {
   204  				updateDesiredStateAtNodeAndWait(
   205  					node,
   206  					ipV4AddrAndRoute(firstSecondaryNic, ipAddress, destIPAddress, prefixLen, nextHopIPAddress),
   207  				)
   208  				ipAddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(Equal(ipAddress))
   209  				routeNextHopInterface(node, destIPAddress).Should(Equal(firstSecondaryNic))
   210  				// The policy has to be removed since it is not possible to update capture of an existing policy
   211  				deletePolicy(TestPolicy)
   212  
   213  				capture := map[string]string{
   214  					"gw":                     fmt.Sprintf(`routes.running.destination==%q`, destIPAddress),
   215  					"secondary-iface":        `interfaces.name==capture.gw.routes.running.0.next-hop-interface`,
   216  					"secondary-iface-routes": `routes.running.next-hop-interface==capture.secondary-iface.interfaces.0.name`,
   217  					"bridge-routes": fmt.Sprintf(
   218  						`capture.secondary-iface-routes | routes.running.next-hop-interface:="%s"`, bridgeName,
   219  					),
   220  				}
   221  				updateDesiredStateWithCaptureAtNodeAndWait(node, bridgeOnTheSecondaryInterfaceState(), capture)
   222  				deletePolicy(TestPolicy)
   223  			})
   224  			AfterEach(func() {
   225  				updateDesiredStateAndWait(ipV4AddrAndRouteAndBridgeAbsent(firstSecondaryNic, bridgeName))
   226  				ipAddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(BeEmpty())
   227  				routeDestForNodeInterfaceEventually(node, destIPAddress).ShouldNot(Equal(firstSecondaryNic))
   228  				routeDestForNodeInterfaceEventually(node, destIPAddress).ShouldNot(Equal(bridgeName))
   229  				resetDesiredStateForNodes()
   230  			})
   231  			It("should have the bridge and the routes created", func() {
   232  				routeNextHopInterface(node, destIPAddress).Should(Equal(bridgeName))
   233  			})
   234  		})
   235  
   236  		Context("with static V6 address", func() {
   237  			BeforeEach(func() {
   238  				updateDesiredStateAtNodeAndWait(node, ipV6Addr(firstSecondaryNic, ipAddressV6, prefixLenV6))
   239  			})
   240  			AfterEach(func() {
   241  				updateDesiredStateAndWait(ipV6AddrAbsent(firstSecondaryNic))
   242  				ipV6AddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(BeEmpty())
   243  				resetDesiredStateForNodes()
   244  			})
   245  			It("should have the static V6 address", func() {
   246  				ipV6AddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(Equal(ipAddressV6))
   247  			})
   248  		})
   249  
   250  		Context("with static V6 address and route", func() {
   251  			BeforeEach(func() {
   252  				updateDesiredStateAtNodeAndWait(
   253  					node,
   254  					ipV6AddrAndRoute(firstSecondaryNic, ipAddressV6, destIPAddressV6, prefixLenV6, nextHopIPAddressV6),
   255  				)
   256  			})
   257  			AfterEach(func() {
   258  				updateDesiredStateAndWait(ipV6AddrAndRouteAbsent(firstSecondaryNic))
   259  				ipV6AddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(BeEmpty())
   260  				routeDestForNodeInterfaceEventually(node, destIPAddressV6).ShouldNot(Equal(firstSecondaryNic))
   261  				resetDesiredStateForNodes()
   262  			})
   263  			It("should have the static V6 address and route  at currentState", func() {
   264  				ipV6AddressForNodeInterfaceEventually(node, firstSecondaryNic).Should(Equal(ipAddressV6))
   265  				routeNextHopInterface(node, destIPAddressV6).Should(Equal(firstSecondaryNic))
   266  			})
   267  		})
   268  	})
   269  })