Skip to content

Commit

Permalink
state, filter: Fix the interpretation of ifaces names
Browse files Browse the repository at this point in the history
In scenarios where an interface has only numeric characters, the
application panics with a failure to extract a string type from a Go
interface (which is actually a float64).

In order to fix the problem, the parsing of the interface name is performed
using a dedicated interface-state structure.
Regular numeric values (integers) are now interpreted as strings and
represented back with double quotes.

Note: this change does not solve the problem of incorrectly representing
valid numeric values like `0xfe` and `1.0` as strings (they are now
represented back as 254 and 1).
This is due to the YAML-to-JSON conversion which does not obey the to
the defined member type.

Signed-off-by: Edward Haas <[email protected]>
  • Loading branch information
EdDev committed Mar 3, 2021
1 parent d5025ef commit 1212087
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 10 deletions.
14 changes: 6 additions & 8 deletions pkg/state/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,11 @@ func filterOutDynamicAttributes(iface map[string]interface{}) {
delete(options, "hello-timer")
}

func filterOutInterfaces(ifaces []interface{}, interfacesFilterGlob glob.Glob) []interface{} {
filteredInterfaces := []interface{}{}
for _, iface := range ifaces {
name := iface.(map[string]interface{})["name"]
if !interfacesFilterGlob.Match(name.(string)) {
filterOutDynamicAttributes(iface.(map[string]interface{}))
func filterOutInterfaces(ifacesState []interfaceState, interfacesFilterGlob glob.Glob) []interfaceState {
filteredInterfaces := []interfaceState{}
for _, iface := range ifacesState {
if !interfacesFilterGlob.Match(iface.Name) {
filterOutDynamicAttributes(iface.Data)
filteredInterfaces = append(filteredInterfaces, iface)
}
}
Expand All @@ -87,8 +86,7 @@ func filterOutInterfaces(ifaces []interface{}, interfacesFilterGlob glob.Glob) [

func filterOut(currentState shared.State, interfacesFilterGlob glob.Glob) (shared.State, error) {
var state rootState
err := yaml.Unmarshal(currentState.Raw, &state)
if err != nil {
if err := yaml.Unmarshal(currentState.Raw, &state); err != nil {
return currentState, err
}

Expand Down
47 changes: 47 additions & 0 deletions pkg/state/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,4 +331,51 @@ interfaces:
})
})

Context("when the interfaces have (only) numeric characters", func() {
BeforeEach(func() {
state = nmstate.NewState(`
interfaces:
- name: eth0
- name: 0
- name: 1101010
`)
filteredState = nmstate.NewState(`
interfaces:
- name: eth0
- name: "1101010"
`)
interfacesFilterGlob = glob.MustCompile("0*")
})

It("should filter out interfaces correctly", func() {
returnedState, err := filterOut(state, interfacesFilterGlob)
Expect(err).NotTo(HaveOccurred())
Expect(returnedState).To(MatchYAML(filteredState))
})
})

Context("when the interfaces have special numeric characters", func() {
BeforeEach(func() {
state = nmstate.NewState(`
interfaces:
- name: eth0
- name: 0.0
- name: 1.0
- name: 0xfe
`)
filteredState = nmstate.NewState(`
interfaces:
- name: eth0
- name: "0"
- name: "254"
`)
interfacesFilterGlob = glob.MustCompile("1*")
})

It("should filter out interfaces correctly but they are not represented correctly", func() {
returnedState, err := filterOut(state, interfacesFilterGlob)
Expect(err).NotTo(HaveOccurred())
Expect(returnedState).To(MatchYAML(filteredState))
})
})
})
40 changes: 38 additions & 2 deletions pkg/state/type.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,47 @@
package state

import (
"encoding/json"
"fmt"

"sigs.k8s.io/yaml"
)

type rootState struct {
Interfaces []interface{} `json:"interfaces"`
Routes *routesState `json:"routes,omitempty"`
Interfaces []interfaceState `json:"interfaces"`
Routes *routesState `json:"routes,omitempty"`
}

type routesState struct {
Config []interface{} `json:"config"`
Running []interface{} `json:"running"`
}

type interfaceState struct {
interfaceFields
Data map[string]interface{}
}

// interfaceFields allows unmarshaling directly into the defined fields
type interfaceFields struct {
Name string `json:"name"`
}

func (i interfaceState) MarshalJSON() (output []byte, err error) {
i.Data["name"] = i.Name
return json.Marshal(i.Data)
}

func (i *interfaceState) UnmarshalJSON(b []byte) error {
if err := yaml.Unmarshal(b, &i.Data); err != nil {
return fmt.Errorf("failed Unmarshaling b: %w", err)
}

var ifaceFields interfaceFields
if err := yaml.Unmarshal(b, &ifaceFields); err != nil {
return fmt.Errorf("failed Unmarshaling raw: %w", err)
}
i.Data["name"] = ifaceFields.Name
i.Name = ifaceFields.Name
return nil
}

0 comments on commit 1212087

Please sign in to comment.