From 09067b48a7814f384b2c20fa7a62ada3f5cd3ccf Mon Sep 17 00:00:00 2001 From: Radim Hrazdil <32546791+rhrazdil@users.noreply.github.com> Date: Wed, 16 Feb 2022 16:44:22 +0100 Subject: [PATCH] Fix issue with Route next-hop-interface being interpreted as float64 (#987) * unit test filtering of ifaces with numeric names Signed-off-by: Radim Hrazdil * Fix next-hop-interface with numeric names being interpreted as float Signed-off-by: Radim Hrazdil --- pkg/state/filter.go | 19 ++++++--- pkg/state/filter_test.go | 84 ++++++++++++++++++++++++++++++++++++++++ pkg/state/type.go | 37 ++++++++++++++++-- 3 files changed, 131 insertions(+), 9 deletions(-) diff --git a/pkg/state/filter.go b/pkg/state/filter.go index bb471eeb64..ed82632db5 100644 --- a/pkg/state/filter.go +++ b/pkg/state/filter.go @@ -78,11 +78,11 @@ func FilterOut(currentState shared.State, deviceInfo DeviceInfoer) (shared.State return filterOut(currentState, devStates) } -func filterOutRoutes(routes []interface{}, filteredInterfaces []interfaceState) []interface{} { - filteredRoutes := []interface{}{} +func filterOutRoutes(routes []routeState, filteredInterfaces []interfaceState) []routeState { + filteredRoutes := []routeState{} for _, route := range routes { - name := route.(map[string]interface{})["next-hop-interface"] - if isInInterfaces(name.(string), filteredInterfaces) { + name := route.NextHopInterface + if isInInterfaces(name, filteredInterfaces) { filteredRoutes = append(filteredRoutes, route) } } @@ -150,7 +150,6 @@ func filterOut(currentState shared.State, deviceStates map[string]networkmanager if err := yaml.Unmarshal(currentState.Raw, &state); err != nil { return currentState, err } - if err := normalizeInterfacesNames(currentState.Raw, &state); err != nil { return currentState, err } @@ -176,8 +175,18 @@ func normalizeInterfacesNames(rawState []byte, state *rootState) error { if err := goyaml.Unmarshal(rawState, &stateForNormalization); err != nil { return err } + for i, iface := range stateForNormalization.Interfaces { state.Interfaces[i].Name = iface.Name } + + if stateForNormalization.Routes != nil { + for i, route := range stateForNormalization.Routes.Config { + state.Routes.Config[i].NextHopInterface = route.NextHopInterface + } + for i, route := range stateForNormalization.Routes.Running { + state.Routes.Running[i].NextHopInterface = route.NextHopInterface + } + } return nil } diff --git a/pkg/state/filter_test.go b/pkg/state/filter_test.go index 8fc294e7d4..28a0351513 100644 --- a/pkg/state/filter_test.go +++ b/pkg/state/filter_test.go @@ -384,4 +384,88 @@ dns-resolver: Expect(returnedState).To(MatchYAML(state)) }) }) + + Context("when the interfaces have numeric characters", func() { + BeforeEach(func() { + ifaceStates = map[string]networkmanager.DeviceState{ + "0": networkmanager.DeviceStateUnmanaged, + "1101010": networkmanager.DeviceStateUnmanaged, + "0.0": networkmanager.DeviceStateUnmanaged, + "1.0": networkmanager.DeviceStateUnmanaged, + "0xfe": networkmanager.DeviceStateUnmanaged, + "60.e+02": networkmanager.DeviceStateUnmanaged, + "10e+02": networkmanager.DeviceStateUnmanaged, + "70e+02": networkmanager.DeviceStateUnmanaged, + "94475496822e234": networkmanager.DeviceStateUnmanaged, + } + state = nmstate.NewState(`interfaces: + - name: eth0 + type: ethernet + - name: '0' + type: veth + - name: '1101010' + type: veth + - name: '0.0' + type: veth + - name: '1.0' + type: veth + - name: '0xfe' + type: veth + - name: '60.e+02' + type: veth + - name: 10e+02 + type: veth + - name: 70e+02 + type: veth + - name: 94475496822e234 + type: veth +routes: + config: [] + running: + - destination: fd10:244::8c40/128 + metric: 1024 + next-hop-address: 10.21.21.10 + next-hop-interface: eth0 + table-id: 254 + - destination: fd10:244::8c40/128 + metric: 1024 + next-hop-address: 10.21.21.10 + next-hop-interface: 94475496822e234 + table-id: 254 + - destination: fd10:244::8c40/128 + metric: 1024 + next-hop-address: 10.21.21.10 + next-hop-interface: '94475496822e234' + table-id: 254 + - destination: fd10:244::8c40/128 + metric: 1024 + next-hop-address: 10.21.21.10 + next-hop-interface: 70e+02 + table-id: 254 + - destination: fd10:244::8c40/128 + metric: 1024 + next-hop-address: 10.21.21.10 + next-hop-interface: 60.e+02 + table-id: 254 +`) + filteredState = nmstate.NewState(`interfaces: +- name: eth0 + type: ethernet +routes: + config: [] + running: + - destination: fd10:244::8c40/128 + metric: 1024 + next-hop-address: 10.21.21.10 + next-hop-interface: eth0 + table-id: 254 +`) + }) + + It("should filter out interfaces correctly", func() { + returnedState, err := filterOut(state, ifaceStates) + Expect(err).NotTo(HaveOccurred()) + Expect(returnedState).To(MatchYAML(filteredState)) + }) + }) }) diff --git a/pkg/state/type.go b/pkg/state/type.go index 2a6990c35e..97f2755a4c 100644 --- a/pkg/state/type.go +++ b/pkg/state/type.go @@ -26,13 +26,18 @@ import ( type rootState struct { Interfaces []interfaceState `json:"interfaces" yaml:"interfaces"` - Routes *routesState `json:"routes,omitempty" yaml:"routes,omitempty"` + Routes *routes `json:"routes,omitempty" yaml:"routes,omitempty"` DNSResolver *dnsResolver `json:"dns-resolver,omitempty" yaml:"dns-resolver,omitempty"` } -type routesState struct { - Config []interface{} `json:"config" yaml:"config"` - Running []interface{} `json:"running" yaml:"running"` +type routes struct { + Config []routeState `json:"config" yaml:"config"` + Running []routeState `json:"running" yaml:"running"` +} + +type routeState struct { + routeFields `yaml:",inline"` + Data map[string]interface{} } type interfaceState struct { @@ -55,6 +60,11 @@ type interfaceFields struct { Name string `json:"name" yaml:"name"` } +// routeFields allows unmarshaling directly into the defined fields +type routeFields struct { + NextHopInterface string `json:"next-hop-interface" yaml:"next-hop-interface"` +} + func (i interfaceState) MarshalJSON() (output []byte, err error) { i.Data["name"] = i.Name return json.Marshal(i.Data) @@ -73,3 +83,22 @@ func (i *interfaceState) UnmarshalJSON(b []byte) error { i.interfaceFields = ifaceFields return nil } + +func (r routeState) MarshalJSON() (output []byte, err error) { + r.Data["next-hop-interface"] = r.NextHopInterface + return json.Marshal(r.Data) +} + +func (r *routeState) UnmarshalJSON(b []byte) error { + if err := yaml.Unmarshal(b, &r.Data); err != nil { + return fmt.Errorf("failed Unmarshaling b: %w", err) + } + + var fields routeFields + if err := yaml.Unmarshal(b, &fields); err != nil { + return fmt.Errorf("failed Unmarchaling raw: %w", err) + } + r.Data["next-hop-interface"] = fields.NextHopInterface + r.routeFields = fields + return nil +}