diff --git a/ovsnl/client_linux_integration_test.go b/ovsnl/client_linux_integration_test.go index 4e385d8..14c42d3 100644 --- a/ovsnl/client_linux_integration_test.go +++ b/ovsnl/client_linux_integration_test.go @@ -27,6 +27,7 @@ import ( ) func TestLinuxClientIntegration(t *testing.T) { + c, err := ovsnl.New() if err != nil { if os.IsNotExist(err) { diff --git a/ovsnl/client_linux_test.go b/ovsnl/client_linux_test.go index 16bb7a9..448e303 100644 --- a/ovsnl/client_linux_test.go +++ b/ovsnl/client_linux_test.go @@ -30,12 +30,10 @@ import ( "golang.org/x/sys/unix" ) -// newTestClient creates a test client with a mock genetlink connection func newTestClient(conn *genetlink.Conn) (*Client, error) { c := &Client{} c.c = conn - // Initialize services. families, err := c.c.ListFamilies() if err != nil { return nil, err @@ -45,10 +43,13 @@ func newTestClient(conn *genetlink.Conn) (*Client, error) { return nil, err } - // For testing, we'll skip the aggregator initialization - // since it requires actual kernel conntrack support - c.Agg = nil + // Inject our mock connection directly into the datapath service + if c.Datapath != nil { + c.Datapath.c = c + c.c = conn + } + c.Agg = nil return c, nil } diff --git a/ovsnl/datapath_linux_integration_test.go b/ovsnl/datapath_linux_integration_test.go new file mode 100644 index 0000000..df8bca1 --- /dev/null +++ b/ovsnl/datapath_linux_integration_test.go @@ -0,0 +1,52 @@ +// Copyright 2017 DigitalOcean. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build linux && integration +// +build linux,integration + +package ovsnl + +import ( + "testing" +) + +// TestClientDatapathListIntegration tests datapath listing with real Open vSwitch +func TestClientDatapathListIntegration(t *testing.T) { + // Skip if not running in integration test environment + if testing.Short() { + t.Skip("skipping integration test") + } + + c, err := New() + if err != nil { + t.Skipf("skipping integration test: %v", err) + } + defer c.Close() + + dps, err := c.Datapath.List() + if err != nil { + t.Fatalf("failed to list datapaths: %v", err) + } + + if len(dps) == 0 { + t.Log("no datapaths found (Open vSwitch may not be running)") + return + } + + // Verify we can list datapaths + t.Logf("found %d datapaths", len(dps)) + for _, dp := range dps { + t.Logf("datapath: %s (index: %d)", dp.Name, dp.Index) + } +} diff --git a/ovsnl/datapath_linux_test.go b/ovsnl/datapath_linux_test.go index f5f7694..39f9df3 100644 --- a/ovsnl/datapath_linux_test.go +++ b/ovsnl/datapath_linux_test.go @@ -32,12 +32,17 @@ import ( func TestClientDatapathListShortHeader(t *testing.T) { conn := genltest.Dial(ovsFamilies(func(greq genetlink.Message, nreq netlink.Message) ([]genetlink.Message, error) { - // Not enough data for ovsh.Header. - return []genetlink.Message{ - { - Data: []byte{0xff, 0xff}, - }, - }, nil + + // Check if this is the datapath list command + if greq.Header.Command == ovsh.DpCmdGet { + // Return deliberately short data for datapath list + shortData := []byte{0xff, 0xff} + return []genetlink.Message{ + {Data: shortData}, + }, nil + } + + return []genetlink.Message{}, nil })) c, err := newTestClient(conn) @@ -46,8 +51,9 @@ func TestClientDatapathListShortHeader(t *testing.T) { } defer c.Close() - _, err = c.Datapath.List() - if err == nil { + _, errDatapath := c.Datapath.List() + if errDatapath == nil { + t.Fatalf("expected an error, but none occurred") } @@ -59,12 +65,12 @@ func TestClientDatapathListBadStats(t *testing.T) { // Valid header; not enough data for ovsh.DPStats. return []genetlink.Message{{ Data: append( - // ovsh.Header. + // ovsh.Header (4 bytes). []byte{0xff, 0xff, 0xff, 0xff}, // netlink attributes. mustMarshalAttributes([]netlink.Attribute{{ Type: ovsh.DpAttrStats, - Data: []byte{0xff}, + Data: []byte{0xff}, // Only 1 byte, but sizeofDPStats is 32 bytes }})..., ), }}, nil @@ -89,12 +95,12 @@ func TestClientDatapathListBadMegaflowStats(t *testing.T) { // Valid header; not enough data for ovsh.DPMegaflowStats. return []genetlink.Message{{ Data: append( - // ovsh.Header. + // ovsh.Header (4 bytes). []byte{0xff, 0xff, 0xff, 0xff}, // netlink attributes. mustMarshalAttributes([]netlink.Attribute{{ Type: ovsh.DpAttrMegaflowStats, - Data: []byte{0xff}, + Data: []byte{0xff}, // Only 1 byte, but sizeofDPMegaflowStats is 32 bytes }})..., ), }}, nil @@ -223,14 +229,15 @@ func mustMarshalDatapath(dp Datapath) []byte { // ovsFamilies creates a test handler that returns OVS family messages func ovsFamilies(handler func(genetlink.Message, netlink.Message) ([]genetlink.Message, error)) func(genetlink.Message, netlink.Message) ([]genetlink.Message, error) { return func(greq genetlink.Message, nreq netlink.Message) ([]genetlink.Message, error) { - // Handle family listing requests - if greq.Header.Command == unix.CTRL_CMD_GETFAMILY { + + // Handle family listing requests (CTRL family) + if nreq.Header.Type == unix.GENL_ID_CTRL && greq.Header.Command == unix.CTRL_CMD_GETFAMILY { return familyMessages([]string{ ovsh.DatapathFamily, }), nil } - // Handle actual datapath requests + // Handle actual datapath requests (OVS datapath family) return handler(greq, nreq) } }