diff --git a/cache/cache_test.go b/cache/cache_test.go index abca11d5..5be139f4 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -10,6 +10,7 @@ import ( "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" "github.com/ovn-org/libovsdb/test" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/ovn-org/libovsdb/updates" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -2037,7 +2038,8 @@ func BenchmarkPopulate2UpdateArray(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { for i := 0; i < numRows; i++ { - updatedRow := ovsdb.Row(map[string]interface{}{"array": ovsdb.OvsSet{GoSet: updateSet}}) + ovsSet := testhelpers.MakeOvsSet(b, ovsdb.TypeString, updateSet) + updatedRow := ovsdb.Row(map[string]interface{}{"array": ovsSet}) err := tc.Populate2(ovsdb.TableUpdates2{ "Open_vSwitch": { "foo": &ovsdb.RowUpdate2{ @@ -2714,7 +2716,7 @@ func BenchmarkPopulate2SingleModify(b *testing.B) { UUID string `ovsdb:"_uuid"` Set []string `ovsdb:"set"` } - aFooSet, _ := ovsdb.NewOvsSet([]string{"foo"}) + aFooSet, _ := ovsdb.NewOvsSet(ovsdb.TypeString, []string{"foo"}) base := &testDBModel{Set: []string{}} for i := 0; i < 57000; i++ { base.Set = append(base.Set, fmt.Sprintf("foo%d", i)) diff --git a/client/api_test.go b/client/api_test.go index 7c768109..90664f55 100644 --- a/client/api_test.go +++ b/client/api_test.go @@ -12,6 +12,7 @@ import ( "github.com/ovn-org/libovsdb/cache" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -914,7 +915,7 @@ func TestAPIMutate(t *testing.T) { { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testOvsSet(t, []string{"1.1.1.1"})}}, + Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"1.1.1.1"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID0}}}, }, }, @@ -940,19 +941,19 @@ func TestAPIMutate(t *testing.T) { { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testOvsSet(t, []string{"2.2.2.2"})}}, + Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"2.2.2.2"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID0}}}, }, { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testOvsSet(t, []string{"2.2.2.2"})}}, + Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"2.2.2.2"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID1}}}, }, { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testOvsSet(t, []string{"2.2.2.2"})}}, + Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"2.2.2.2"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID2}}}, }, }, @@ -976,7 +977,7 @@ func TestAPIMutate(t *testing.T) { { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationDelete, Value: testOvsSet(t, []string{"foo"})}}, + Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationDelete, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"foo"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID2}}}, }, }, @@ -1000,7 +1001,7 @@ func TestAPIMutate(t *testing.T) { { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationDelete, Value: testOvsSet(t, []string{"foo"})}}, + Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationDelete, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"foo"})}}, Where: []ovsdb.Condition{{Column: "name", Function: ovsdb.ConditionEqual, Value: "foo"}}, }, }, @@ -1024,7 +1025,7 @@ func TestAPIMutate(t *testing.T) { { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationInsert, Value: testOvsMap(t, map[string]string{"bar": "baz"})}}, + Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationInsert, Value: testhelpers.MakeOvsMap(t, map[string]string{"bar": "baz"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID2}}}, }, }, @@ -1048,13 +1049,13 @@ func TestAPIMutate(t *testing.T) { { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationInsert, Value: testOvsMap(t, map[string]string{"bar": "baz"})}}, + Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationInsert, Value: testhelpers.MakeOvsMap(t, map[string]string{"bar": "baz"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID0}}}, }, { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationInsert, Value: testOvsMap(t, map[string]string{"bar": "baz"})}}, + Mutations: []ovsdb.Mutation{{Column: "external_ids", Mutator: ovsdb.MutateOperationInsert, Value: testhelpers.MakeOvsMap(t, map[string]string{"bar": "baz"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID1}}}, }, }, @@ -1136,10 +1137,10 @@ func TestAPIUpdate(t *testing.T) { tcache := apiTestCache(t, testData) testObj := testLogicalSwitchPort{} - testRow := ovsdb.Row(map[string]interface{}{"type": "somethingElse", "tag": testOvsSet(t, []int{6})}) - tagRow := ovsdb.Row(map[string]interface{}{"tag": testOvsSet(t, []int{6})}) + testRow := ovsdb.Row(map[string]interface{}{"type": "somethingElse", "tag": testhelpers.MakeOvsSet(t, ovsdb.TypeInteger, []int{6})}) + tagRow := ovsdb.Row(map[string]interface{}{"tag": testhelpers.MakeOvsSet(t, ovsdb.TypeInteger, []int{6})}) var nilInt *int - testNilRow := ovsdb.Row(map[string]interface{}{"type": "somethingElse", "tag": testOvsSet(t, nilInt)}) + testNilRow := ovsdb.Row(map[string]interface{}{"type": "somethingElse", "tag": testhelpers.MakeOvsSet(t, ovsdb.TypeInteger, nilInt)}) typeRow := ovsdb.Row(map[string]interface{}{"type": "somethingElse"}) fields := []interface{}{&testObj.Tag, &testObj.Type} @@ -1350,7 +1351,7 @@ func TestAPIUpdate(t *testing.T) { Row: tagRow, Where: []ovsdb.Condition{ {Column: "type", Function: ovsdb.ConditionEqual, Value: "sometype"}, - {Column: "enabled", Function: ovsdb.ConditionIncludes, Value: testOvsSet(t, &trueVal)}, + {Column: "enabled", Function: ovsdb.ConditionIncludes, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeBoolean, &trueVal)}, }, }, }, @@ -1403,7 +1404,7 @@ func TestAPIUpdate(t *testing.T) { Op: ovsdb.OperationUpdate, Table: "Logical_Switch_Port", Row: tagRow, - Where: []ovsdb.Condition{{Column: "tag", Function: ovsdb.ConditionNotEqual, Value: testOvsSet(t, &one)}}, + Where: []ovsdb.Condition{{Column: "tag", Function: ovsdb.ConditionNotEqual, Value: testhelpers.MakeOvsSet(t, ovsdb.TypeInteger, &one)}}, }, }, err: false, @@ -1944,7 +1945,7 @@ func TestAPIWait(t *testing.T) { { Column: "up", Function: ovsdb.ConditionNotEqual, - Value: ovsdb.OvsSet{GoSet: []interface{}{true}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeBoolean, []interface{}{true}), }, }, Until: string(ovsdb.WaitConditionNotEqual), diff --git a/client/client_test.go b/client/client_test.go index 91eff98a..c5766c42 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -59,7 +59,7 @@ type Bridge struct { DatapathVersion string `ovsdb:"datapath_version"` ExternalIDs map[string]string `ovsdb:"external_ids"` FailMode *BridgeFailMode `ovsdb:"fail_mode"` - FloodVLANs [4096]int `ovsdb:"flood_vlans"` + FloodVLANs []int `ovsdb:"flood_vlans"` FlowTables map[int]string `ovsdb:"flow_tables"` IPFIX *string `ovsdb:"ipfix"` McastSnoopingEnable bool `ovsdb:"mcast_snooping_enable"` @@ -488,18 +488,6 @@ var schema = `{ } }` -func testOvsSet(t *testing.T, set interface{}) ovsdb.OvsSet { - oSet, err := ovsdb.NewOvsSet(set) - assert.Nil(t, err) - return oSet -} - -func testOvsMap(t *testing.T, set interface{}) ovsdb.OvsMap { - oMap, err := ovsdb.NewOvsMap(set) - assert.Nil(t, err) - return oMap -} - func updateBenchmark(ovs *ovsdbClient, updates []byte, b *testing.B) { for n := 0; n < b.N; n++ { params := []json.RawMessage{[]byte(`{"databaseName":"Open_vSwitch","id":"v1"}`), updates} diff --git a/client/condition_test.go b/client/condition_test.go index 5bd63b33..05f53ecb 100644 --- a/client/condition_test.go +++ b/client/condition_test.go @@ -7,6 +7,7 @@ import ( "github.com/ovn-org/libovsdb/cache" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/stretchr/testify/assert" ) @@ -330,7 +331,7 @@ func TestExplicitConditionalWithNoCache(t *testing.T) { { Column: "external_ids", Function: ovsdb.ConditionIncludes, - Value: testOvsMap(t, map[string]string{"foo": "baz"}), + Value: testhelpers.MakeOvsMap(t, map[string]string{"foo": "baz"}), }}}, }, { @@ -347,7 +348,7 @@ func TestExplicitConditionalWithNoCache(t *testing.T) { { Column: "enabled", Function: ovsdb.ConditionEqual, - Value: testOvsSet(t, &trueVal), + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeBoolean, &trueVal), }}}, }, { @@ -369,7 +370,7 @@ func TestExplicitConditionalWithNoCache(t *testing.T) { { Column: "enabled", Function: ovsdb.ConditionEqual, - Value: testOvsSet(t, &trueVal), + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeBoolean, &trueVal), }}, { { @@ -396,7 +397,7 @@ func TestExplicitConditionalWithNoCache(t *testing.T) { { Column: "enabled", Function: ovsdb.ConditionEqual, - Value: testOvsSet(t, &trueVal), + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeBoolean, &trueVal), }, { Column: "name", diff --git a/database/transaction_test.go b/database/transaction_test.go index 3a6a5aac..a6faec29 100644 --- a/database/transaction_test.go +++ b/database/transaction_test.go @@ -11,6 +11,7 @@ import ( "github.com/ovn-org/libovsdb/mapper" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" + "github.com/ovn-org/libovsdb/test/helpers" . "github.com/ovn-org/libovsdb/test" ) @@ -286,6 +287,7 @@ func TestMutateOp(t *testing.T) { "baz": "quux", "waldo": "fred", }, + FloodVLANs: []int{1, 2, 3}, } bridgeInfo, err := dbModel.NewModelInfo(&bridge) require.NoError(t, err) @@ -330,7 +332,8 @@ func TestMutateOp(t *testing.T) { err = db.Commit("Open_vSwitch", uuid.New(), *gotUpdate) require.NoError(t, err) - bridgeSet, err := ovsdb.NewOvsSet([]ovsdb.UUID{{GoUUID: bridgeUUID}}) + bridgesSchema := info.Metadata.TableSchema.Column("bridges") + bridgeSet, err := ovsdb.NewOvsSet(bridgesSchema.TypeObj.Key.Type, []ovsdb.UUID{{GoUUID: bridgeUUID}}) assert.Nil(t, err) assert.Equal(t, ovsdb.TableUpdates2{ "Open_vSwitch": ovsdb.TableUpdate2{ @@ -351,7 +354,8 @@ func TestMutateOp(t *testing.T) { }, }, getTableUpdates(*gotUpdate)) - keyDelete, err := ovsdb.NewOvsSet([]string{"foo"}) + extIDSchema := bridgeInfo.Metadata.TableSchema.Column("external_ids") + keyDelete, err := ovsdb.NewOvsSet(extIDSchema.TypeObj.Key.Type, []string{"foo"}) assert.Nil(t, err) keyValueDelete, err := ovsdb.NewOvsMap(map[string]string{"baz": "quux"}) assert.Nil(t, err) @@ -380,6 +384,21 @@ func TestMutateOp(t *testing.T) { assert.Equal(t, diffExternalIds, gotModify["external_ids"]) assert.Equal(t, oldExternalIds, gotOld["external_ids"]) assert.Equal(t, newExternalIds, gotNew["external_ids"]) + + // Test that attempting to mutate a set to exceed its allowed size results in an error + floodVLANsSchema := bridgeInfo.Metadata.TableSchema.Column("flood_vlans") + keyInsert, err := ovsdb.NewOvsSet(floodVLANsSchema.TypeObj.Key.Type, []int{33}) + assert.Nil(t, err) + gotResult, gotUpdate, err = transaction.Mutate( + "Bridge", + []ovsdb.Condition{ + ovsdb.NewCondition("_uuid", ovsdb.ConditionEqual, ovsdb.UUID{GoUUID: bridgeUUID}), + }, + []ovsdb.Mutation{ + *ovsdb.NewMutation("flood_vlans", ovsdb.MutateOperationInsert, keyInsert), + }, + ) + assert.Error(t, err) } func TestOvsdbServerInsert(t *testing.T) { @@ -476,12 +495,14 @@ func TestOvsdbServerUpdate(t *testing.T) { err = db.Commit("Open_vSwitch", uuid.New(), *updates) assert.NoError(t, err) - halloween, _ := ovsdb.NewOvsSet([]string{"halloween"}) - emptySet, _ := ovsdb.NewOvsSet([]string{}) + halloween := testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"halloween"}) + emptySet := testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{}) + floodVlanSet := testhelpers.MakeOvsSet(t, ovsdb.TypeInteger, []int{1, 2, 3, 4, 5, 6, 7}) tests := []struct { - name string - row ovsdb.Row - expected *ovsdb.RowUpdate2 + name string + row ovsdb.Row + expected *ovsdb.RowUpdate2 + expectErr bool }{ { "update single field", @@ -491,6 +512,13 @@ func TestOvsdbServerUpdate(t *testing.T) { "datapath_type": "waldo", }, }, + false, + }, + { + "update single field with too-large array", + ovsdb.Row{"flood_vlans": floodVlanSet}, + nil, + true, }, { "update single optional field, with direct value", @@ -500,6 +528,7 @@ func TestOvsdbServerUpdate(t *testing.T) { "datapath_id": halloween, }, }, + false, }, { "update single optional field, with set", @@ -509,6 +538,7 @@ func TestOvsdbServerUpdate(t *testing.T) { "datapath_id": halloween, }, }, + false, }, { "unset single optional field", @@ -518,6 +548,7 @@ func TestOvsdbServerUpdate(t *testing.T) { "datapath_id": emptySet, }, }, + false, }, } for _, tt := range tests { @@ -532,14 +563,18 @@ func TestOvsdbServerUpdate(t *testing.T) { } res, updates := transaction.Update(&op) errs, err := ovsdb.CheckOperationResults([]ovsdb.OperationResult{res}, []ovsdb.Operation{{Op: "update"}}) - require.NoErrorf(t, err, "%+v", errs) - - bridge.UUID = bridgeUUID - row, err := db.Get("Open_vSwitch", "Bridge", bridgeUUID) - assert.NoError(t, err) - br := row.(*BridgeType) - assert.NotEqual(t, br, bridgeRow) - assert.Equal(t, tt.expected.Modify, getTableUpdates(*updates)["Bridge"][bridgeUUID].Modify) + if tt.expectErr { + require.Error(t, err) + } else { + require.NoErrorf(t, err, "%+v", errs) + + bridge.UUID = bridgeUUID + row, err := db.Get("Open_vSwitch", "Bridge", bridgeUUID) + assert.NoError(t, err) + br := row.(*BridgeType) + assert.NotEqual(t, br, bridgeRow) + assert.Equal(t, tt.expected.Modify, getTableUpdates(*updates)["Bridge"][bridgeUUID].Modify) + } }) } } @@ -572,7 +607,7 @@ func TestMultipleOps(t *testing.T) { ovsdb.NewCondition("_uuid", ovsdb.ConditionEqual, ovsdb.UUID{GoUUID: bridgeUUID}), }, Row: ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "port1"}, ovsdb.UUID{GoUUID: "port10"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "port1"}, {GoUUID: "port10"}}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key1": "value1", "key10": "value10"}}, }, } @@ -598,7 +633,7 @@ func TestMultipleOps(t *testing.T) { Op: ovsdb.OperationMutate, Mutations: []ovsdb.Mutation{ *ovsdb.NewMutation("external_ids", ovsdb.MutateOperationInsert, ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"keyA": "valueA"}}), - *ovsdb.NewMutation("ports", ovsdb.MutateOperationDelete, ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "port1"}, ovsdb.UUID{GoUUID: "port10"}}}), + *ovsdb.NewMutation("ports", ovsdb.MutateOperationDelete, testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "port1"}, {GoUUID: "port10"}})), }, } ops = append(ops, op) @@ -611,7 +646,7 @@ func TestMultipleOps(t *testing.T) { Op: ovsdb.OperationMutate, Mutations: []ovsdb.Mutation{ *ovsdb.NewMutation("external_ids", ovsdb.MutateOperationDelete, ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key10": "value10"}}), - *ovsdb.NewMutation("ports", ovsdb.MutateOperationInsert, ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "port1"}}}), + *ovsdb.NewMutation("ports", ovsdb.MutateOperationInsert, testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "port1"}})), }, } ops = append(ops, op2) @@ -628,19 +663,19 @@ func TestMultipleOps(t *testing.T) { bridgeUUID: &ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"keyA": "valueA", "key10": "value10"}}, - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "port10"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "port10"}}), }, Old: &ovsdb.Row{ "_uuid": ovsdb.UUID{GoUUID: bridgeUUID}, "name": "a_bridge_to_nowhere", "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key1": "value1", "key10": "value10"}}, - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "port1"}, ovsdb.UUID{GoUUID: "port10"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "port1"}, {GoUUID: "port10"}}), }, New: &ovsdb.Row{ "_uuid": ovsdb.UUID{GoUUID: bridgeUUID}, "name": "a_bridge_to_nowhere", "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key1": "value1", "keyA": "valueA"}}, - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "port1"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "port1"}}), }, }, }, diff --git a/mapper/info.go b/mapper/info.go index 8ac436c7..f6ac01c9 100644 --- a/mapper/info.go +++ b/mapper/info.go @@ -66,6 +66,15 @@ func (i *Info) SetField(column string, value interface{}) error { return fmt.Errorf("column %s: native value %v (%s) is not assignable to field %s (%s)", column, value, reflect.TypeOf(value), fieldName, fieldValue.Type()) } + + colSchema := i.Metadata.TableSchema.Column(column) + if colSchema == nil { + return fmt.Errorf("SetField: column %s schema not found", column) + } + if err := ovsdb.ValidateColumnConstraints(colSchema, value); err != nil { + return fmt.Errorf("SetField: column %s failed validation: %v", column, err) + } + fieldValue.Set(reflect.ValueOf(value)) return nil } diff --git a/mapper/info_test.go b/mapper/info_test.go index cf5ad960..1c89febf 100644 --- a/mapper/info_test.go +++ b/mapper/info_test.go @@ -24,6 +24,13 @@ var sampleTable = []byte(`{ "min": 0 } }, + "aLimitedSet": { + "type": { + "key": "string", + "max": 3, + "min": 0 + } + }, "aMap": { "type": { "key": "string", @@ -75,10 +82,11 @@ func TestNewMapperInfo(t *testing.T) { func TestMapperInfoSet(t *testing.T) { type obj struct { - Ostring string `ovsdb:"aString"` - Oint int `ovsdb:"aInteger"` - Oset []string `ovsdb:"aSet"` - Omap map[string]string `ovsdb:"aMap"` + Ostring string `ovsdb:"aString"` + Oint int `ovsdb:"aInteger"` + Oset []string `ovsdb:"aSet"` + OLimitedSet []string `ovsdb:"aLimitedSet"` + Omap map[string]string `ovsdb:"aMap"` } type test struct { @@ -106,6 +114,22 @@ func TestMapperInfoSet(t *testing.T) { column: "aSet", err: false, }, + { + name: "limited set under max", + table: sampleTable, + obj: &obj{}, + field: []string{"foo", "bar"}, + column: "aLimitedSet", + err: false, + }, + { + name: "limited set over max", + table: sampleTable, + obj: &obj{}, + field: []string{"foo", "bar", "baz", "qux"}, + column: "aLimitedSet", + err: true, + }, { name: "map", table: sampleTable, diff --git a/mapper/mapper.go b/mapper/mapper.go index 5ca7a412..70c2c99f 100644 --- a/mapper/mapper.go +++ b/mapper/mapper.go @@ -118,6 +118,9 @@ func (m Mapper) NewRow(data *Info, fields ...interface{}) (ovsdb.Row, error) { if len(fields) == 0 && ovsdb.IsDefaultValue(column, nativeElem) { continue } + if err := ovsdb.ValidateColumnConstraints(column, nativeElem); err != nil { + return nil, fmt.Errorf("column %s assignment failed: %w", column, err) + } ovsElem, err := ovsdb.NativeToOvs(column, nativeElem) if err != nil { return nil, fmt.Errorf("table %s, column %s: failed to generate ovs element. %s", data.Metadata.TableName, name, err.Error()) @@ -247,7 +250,7 @@ func (m Mapper) NewMutation(data *Info, column string, mutator ovsdb.Mutator, va // keys (rfc7047 5.1). Handle this special case here. if mutator == "delete" && columnSchema.Type == ovsdb.TypeMap && reflect.TypeOf(value).Kind() != reflect.Map { // It's OK to cast the value to a list of elements because validation has passed - ovsSet, err := ovsdb.NewOvsSet(value) + ovsSet, err := ovsdb.NewOvsSet(columnSchema.TypeObj.Key.Type, value) if err != nil { return nil, err } diff --git a/mapper/mapper_test.go b/mapper/mapper_test.go index fe208d0c..6dd073c9 100644 --- a/mapper/mapper_test.go +++ b/mapper/mapper_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/ovn-org/libovsdb/ovsdb" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/stretchr/testify/assert" ) @@ -32,12 +33,14 @@ var ( } aFloat = 42.00 - aFloatSet = [10]float64{ + aFloatSet = []float64{ 3.0, 2.0, 42.0, } + aFloatSetTooBig = []float64{1.0, 2.0, 3.0, 4.0, 5.0, 6.0} + aMap = map[string]string{ "key1": "value1", "key2": "value2", @@ -113,7 +116,7 @@ var testSchema = []byte(`{ "type": "real" }, "min": 0, - "max": 10 + "max": 5 } }, "aEmptySet": { @@ -156,7 +159,7 @@ var testSchema = []byte(`{ func getOvsTestRow(t *testing.T) ovsdb.Row { ovsRow := ovsdb.NewRow() ovsRow["aString"] = aString - ovsRow["aSet"] = testOvsSet(t, aSet) + ovsRow["aSet"] = testhelpers.MakeOvsSet(t, ovsdb.TypeString, aSet) // Set's can hold the value if they have len == 1 ovsRow["aSingleSet"] = aString @@ -164,21 +167,21 @@ func getOvsTestRow(t *testing.T) ovsdb.Row { for _, u := range aUUIDSet { us = append(us, ovsdb.UUID{GoUUID: u}) } - ovsRow["aUUIDSet"] = testOvsSet(t, us) + ovsRow["aUUIDSet"] = testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, us) ovsRow["aUUID"] = ovsdb.UUID{GoUUID: aUUID0} - ovsRow["aIntSet"] = testOvsSet(t, aIntSet) + ovsRow["aIntSet"] = testhelpers.MakeOvsSet(t, ovsdb.TypeInteger, aIntSet) ovsRow["aFloat"] = aFloat - ovsRow["aFloatSet"] = testOvsSet(t, aFloatSet) + ovsRow["aFloatSet"] = testhelpers.MakeOvsSet(t, ovsdb.TypeReal, aFloatSet) - ovsRow["aEmptySet"] = testOvsSet(t, []string{}) + ovsRow["aEmptySet"] = testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{}) ovsRow["aEnum"] = aEnum - ovsRow["aMap"] = testOvsMap(t, aMap) + ovsRow["aMap"] = testhelpers.MakeOvsMap(t, aMap) return ovsRow } @@ -192,7 +195,7 @@ func TestMapperGetData(t *testing.T) { AUUID string `ovsdb:"aUUID"` AIntSet []int `ovsdb:"aIntSet"` AFloat float64 `ovsdb:"aFloat"` - AFloatSet [10]float64 `ovsdb:"aFloatSet"` + AFloatSet []float64 `ovsdb:"aFloatSet"` YetAnotherStringSet []string `ovsdb:"aEmptySet"` AEnum string `ovsdb:"aEnum"` AMap map[string]string `ovsdb:"aMap"` @@ -214,28 +217,49 @@ func TestMapperGetData(t *testing.T) { NonTagged: "something", } - ovsRow := getOvsTestRow(t) /* Code under test */ var schema ovsdb.DatabaseSchema if err := json.Unmarshal(testSchema, &schema); err != nil { t.Error(err) } - mapper := NewMapper(schema) - test := ormTestType{ - NonTagged: "something", - } - testInfo, err := NewInfo("TestTable", schema.Table("TestTable"), &test) - assert.NoError(t, err) - - err = mapper.GetRowData(&ovsRow, testInfo) - assert.NoError(t, err) - /*End code under test*/ + tests := []struct { + name string + setup func() ovsdb.Row + expectErr bool + }{{ + name: "basic", + setup: func() ovsdb.Row { + return getOvsTestRow(t) + }, + }, { + name: "too big array", + setup: func() ovsdb.Row { + testRow := getOvsTestRow(t) + testRow["aFloatSet"] = test.MakeOvsSet(t, ovsdb.TypeReal, aFloatSetTooBig) + return testRow + }, + expectErr: true, + }} + for _, test := range tests { + t.Run(fmt.Sprintf("GetData: %s", test.name), func(t *testing.T) { + mapper := NewMapper(schema) + tt := ormTestType{ + NonTagged: "something", + } + testInfo, err := NewInfo("TestTable", schema.Table("TestTable"), &tt) + assert.NoError(t, err) - if err != nil { - t.Error(err) + ovsRow := test.setup() + err = mapper.GetRowData(&ovsRow, testInfo) + if test.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, expected, tt) + } + }) } - assert.Equal(t, expected, test) } func TestMapperNewRow(t *testing.T) { @@ -264,7 +288,7 @@ func TestMapperNewRow(t *testing.T) { }{ SomeSet: aSet, }, - expectedRow: ovsdb.Row(map[string]interface{}{"aSet": testOvsSet(t, aSet)}), + expectedRow: ovsdb.Row(map[string]interface{}{"aSet": testhelpers.MakeOvsSet(t, ovsdb.TypeString, aSet)}), }, { name: "emptySet with no column specification", objInput: &struct { @@ -288,7 +312,7 @@ func TestMapperNewRow(t *testing.T) { }{ MyUUIDSet: []string{aUUID0, aUUID1}, }, - expectedRow: ovsdb.Row(map[string]interface{}{"aUUIDSet": testOvsSet(t, []ovsdb.UUID{{GoUUID: aUUID0}, {GoUUID: aUUID1}})}), + expectedRow: ovsdb.Row(map[string]interface{}{"aUUIDSet": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: aUUID0}, {GoUUID: aUUID1}})}), }, { name: "aIntSet", objInput: &struct { @@ -296,7 +320,7 @@ func TestMapperNewRow(t *testing.T) { }{ MyIntSet: []int{0, 42}, }, - expectedRow: ovsdb.Row(map[string]interface{}{"aIntSet": testOvsSet(t, []int{0, 42})}), + expectedRow: ovsdb.Row(map[string]interface{}{"aIntSet": testhelpers.MakeOvsSet(t, ovsdb.TypeInteger, []int{0, 42})}), }, { name: "aFloat", objInput: &struct { @@ -308,11 +332,19 @@ func TestMapperNewRow(t *testing.T) { }, { name: "aFloatSet", objInput: &struct { - MyFloatSet [10]float64 `ovsdb:"aFloatSet"` + MyFloatSet []float64 `ovsdb:"aFloatSet"` }{ MyFloatSet: aFloatSet, }, - expectedRow: ovsdb.Row(map[string]interface{}{"aFloatSet": testOvsSet(t, aFloatSet)}), + expectedRow: ovsdb.Row(map[string]interface{}{"aFloatSet": testhelpers.MakeOvsSet(t, ovsdb.TypeReal, aFloatSet)}), + }, { + name: "aFloatSet too big", + objInput: &struct { + MyFloatSet []float64 `ovsdb:"aFloatSet"` + }{ + MyFloatSet: aFloatSetTooBig, + }, + shoulderr: true, }, { name: "Enum", objInput: &struct { @@ -338,7 +370,7 @@ func TestMapperNewRow(t *testing.T) { }{ MyMap: aMap, }, - expectedRow: ovsdb.Row(map[string]interface{}{"aMap": testOvsMap(t, aMap)}), + expectedRow: ovsdb.Row(map[string]interface{}{"aMap": testhelpers.MakeOvsMap(t, aMap)}), }, } for _, test := range tests { @@ -400,7 +432,7 @@ func TestMapperNewRowFields(t *testing.T) { prepare: func(o *obj) { }, fields: []interface{}{&testObj.MySet}, - expectedRow: ovsdb.Row(map[string]interface{}{"aSet": testOvsSet(t, []string{})}), + expectedRow: ovsdb.Row(map[string]interface{}{"aSet": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{})}), }, { name: "empty maps", prepare: func(o *obj) { @@ -413,7 +445,7 @@ func TestMapperNewRowFields(t *testing.T) { o.MyString = "foo" }, fields: []interface{}{&testObj.MyMap}, - expectedRow: ovsdb.Row(map[string]interface{}{"aMap": testOvsMap(t, map[string]string{})}), + expectedRow: ovsdb.Row(map[string]interface{}{"aMap": testhelpers.MakeOvsMap(t, map[string]string{})}), }, { name: "Complex object with field selection", prepare: func(o *obj) { @@ -423,7 +455,7 @@ func TestMapperNewRowFields(t *testing.T) { o.MyFloat = aFloat }, fields: []interface{}{&testObj.MyMap, &testObj.MySet}, - expectedRow: ovsdb.Row(map[string]interface{}{"aMap": testOvsMap(t, aMap), "aSet": testOvsSet(t, aSet)}), + expectedRow: ovsdb.Row(map[string]interface{}{"aMap": testhelpers.MakeOvsMap(t, aMap), "aSet": testhelpers.MakeOvsSet(t, ovsdb.TypeString, aSet)}), }, } @@ -988,7 +1020,7 @@ func TestMapperMutation(t *testing.T) { obj: testType{}, mutator: ovsdb.MutateOperationInsert, value: []string{"foo"}, - expected: ovsdb.NewMutation("set", ovsdb.MutateOperationInsert, testOvsSet(t, []string{"foo"})), + expected: ovsdb.NewMutation("set", ovsdb.MutateOperationInsert, testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"foo"})), err: false, }, { @@ -997,7 +1029,7 @@ func TestMapperMutation(t *testing.T) { obj: testType{}, mutator: ovsdb.MutateOperationDelete, value: []string{"foo"}, - expected: ovsdb.NewMutation("set", ovsdb.MutateOperationDelete, testOvsSet(t, []string{"foo"})), + expected: ovsdb.NewMutation("set", ovsdb.MutateOperationDelete, testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"foo"})), err: false, }, { @@ -1006,7 +1038,7 @@ func TestMapperMutation(t *testing.T) { obj: testType{}, mutator: ovsdb.MutateOperationDelete, value: []string{"foo", "bar"}, - expected: ovsdb.NewMutation("map", ovsdb.MutateOperationDelete, testOvsSet(t, []string{"foo", "bar"})), + expected: ovsdb.NewMutation("map", ovsdb.MutateOperationDelete, testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"foo", "bar"})), err: false, }, { @@ -1015,7 +1047,7 @@ func TestMapperMutation(t *testing.T) { obj: testType{}, mutator: ovsdb.MutateOperationDelete, value: map[string]string{"foo": "bar"}, - expected: ovsdb.NewMutation("map", ovsdb.MutateOperationDelete, testOvsMap(t, map[string]string{"foo": "bar"})), + expected: ovsdb.NewMutation("map", ovsdb.MutateOperationDelete, testhelpers.MakeOvsMap(t, map[string]string{"foo": "bar"})), err: false, }, { @@ -1024,7 +1056,7 @@ func TestMapperMutation(t *testing.T) { obj: testType{}, mutator: ovsdb.MutateOperationInsert, value: map[string]string{"foo": "bar"}, - expected: ovsdb.NewMutation("map", ovsdb.MutateOperationInsert, testOvsMap(t, map[string]string{"foo": "bar"})), + expected: ovsdb.NewMutation("map", ovsdb.MutateOperationInsert, testhelpers.MakeOvsMap(t, map[string]string{"foo": "bar"})), err: false, }, } @@ -1048,15 +1080,3 @@ func TestMapperMutation(t *testing.T) { }) } } - -func testOvsSet(t *testing.T, set interface{}) ovsdb.OvsSet { - oSet, err := ovsdb.NewOvsSet(set) - assert.Nil(t, err) - return oSet -} - -func testOvsMap(t *testing.T, set interface{}) ovsdb.OvsMap { - oMap, err := ovsdb.NewOvsMap(set) - assert.Nil(t, err) - return oMap -} diff --git a/modelgen/table.go b/modelgen/table.go index fa2dcb76..2c0b301e 100644 --- a/modelgen/table.go +++ b/modelgen/table.go @@ -356,13 +356,6 @@ func fieldType(tableName, columnName string, column *ovsdb.ColumnSchema, enumTyp } return AtomicType(column.TypeObj.Key.Type) } - // use array for columns with max > 1 - if column.TypeObj.Max() > 1 { - if enumTypes && FieldEnum(tableName, columnName, column) != nil { - return fmt.Sprintf("[%d]%s", column.TypeObj.Max(), enumName(tableName, columnName)) - } - return fmt.Sprintf("[%d]%s", column.TypeObj.Max(), AtomicType(column.TypeObj.Key.Type)) - } // use a slice if enumTypes && FieldEnum(tableName, columnName, column) != nil { return fmt.Sprintf("[]%s", enumName(tableName, columnName)) diff --git a/modelgen/table_test.go b/modelgen/table_test.go index ef0d113e..6dfae2e9 100644 --- a/modelgen/table_test.go +++ b/modelgen/table_test.go @@ -850,7 +850,7 @@ func buildTestBridge() *vswitchd.Bridge { DatapathVersion: *buildRandStr(), ExternalIDs: map[string]string{*buildRandStr(): *buildRandStr(), *buildRandStr(): *buildRandStr()}, FailMode: &vswitchd.BridgeFailModeSecure, - FloodVLANs: [4096]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + FloodVLANs: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, FlowTables: map[int]string{1: *buildRandStr(), 2: *buildRandStr()}, IPFIX: buildRandStr(), McastSnoopingEnable: false, @@ -888,8 +888,6 @@ func buildTestInterface() *vswitchd.Interface { ExternalIDs: map[string]string{*buildRandStr(): *buildRandStr(), *buildRandStr(): *buildRandStr()}, Ifindex: &aInt, IngressPolicingBurst: aInt, - IngressPolicingKpktsBurst: aInt, - IngressPolicingKpktsRate: aInt, IngressPolicingRate: aInt, LACPCurrent: &aBool, LinkResets: &aInt, diff --git a/ovsdb/bindings.go b/ovsdb/bindings.go index 4c675858..e0239f4a 100644 --- a/ovsdb/bindings.go +++ b/ovsdb/bindings.go @@ -52,12 +52,13 @@ func NativeTypeFromAtomic(basicType string) reflect.Type { } } -//NativeType returns the reflect.Type that can hold the value of a column -//OVS Type to Native Type convertions: -// OVS sets -> go slices, arrays or a go native type depending on the key -// OVS uuid -> go strings -// OVS map -> go map -// OVS enum -> go native type depending on the type of the enum key +// NativeType returns the reflect.Type that can hold the value of a column +// OVS Type to Native Type convertions: +// +// OVS sets -> go slices, arrays or a go native type depending on the key +// OVS uuid -> go strings +// OVS map -> go map +// OVS enum -> go native type depending on the type of the enum key func NativeType(column *ColumnSchema) reflect.Type { switch column.Type { case TypeInteger, TypeReal, TypeBoolean, TypeUUID, TypeString: @@ -78,10 +79,6 @@ func NativeType(column *ColumnSchema) reflect.Type { if column.TypeObj.Min() == 1 && column.TypeObj.Max() == 1 { return keyType } - // max is > 1, use an array - if column.TypeObj.Max() > 1 { - return reflect.ArrayOf(column.TypeObj.Max(), keyType) - } return reflect.SliceOf(keyType) default: panic(fmt.Errorf("unknown extended type %s", column.Type)) @@ -120,15 +117,17 @@ func OvsToNativeSlice(baseType string, ovsElem interface{}) (interface{}, error) var nativeSet reflect.Value switch ovsSet := ovsElem.(type) { case OvsSet: - nativeSet = reflect.MakeSlice(reflect.SliceOf(naType), 0, len(ovsSet.GoSet)) - for _, v := range ovsSet.GoSet { + nativeSet = reflect.MakeSlice(reflect.SliceOf(naType), 0, ovsSet.Len()) + if err := ovsSet.Range(func(i int, v interface{}) (bool, error) { nv, err := OvsToNativeAtomic(baseType, v) if err != nil { - return nil, err + return true, err } nativeSet = reflect.Append(nativeSet, reflect.ValueOf(nv)) + return false, nil + }); err != nil { + return nil, err } - default: nativeSet = reflect.MakeSlice(reflect.SliceOf(naType), 0, 1) nv, err := OvsToNativeAtomic(baseType, ovsElem) @@ -156,13 +155,12 @@ func OvsToNative(column *ColumnSchema, ovsElem interface{}) (interface{}, error) case reflect.Ptr: switch ovsSet := ovsElem.(type) { case OvsSet: - if len(ovsSet.GoSet) > 1 { - return nil, fmt.Errorf("expected a slice of len =< 1, but got a slice with %d elements", len(ovsSet.GoSet)) - } - if len(ovsSet.GoSet) == 0 { + if ovsSet.Len() > 1 { + return nil, fmt.Errorf("expected a slice of len =< 1, but got a slice with %d elements", ovsSet.Len()) + } else if ovsSet.Len() == 0 { return reflect.Zero(naType).Interface(), nil } - native, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsSet.GoSet[0]) + native, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsSet.goSet[0]) if err != nil { return nil, err } @@ -178,29 +176,10 @@ func OvsToNative(column *ColumnSchema, ovsElem interface{}) (interface{}, error) pv.Elem().Set(reflect.ValueOf(native)) return pv.Interface(), nil } - case reflect.Array: - array := reflect.New(reflect.ArrayOf(column.TypeObj.Max(), naType.Elem())).Elem() - switch ovsSet := ovsElem.(type) { - case OvsSet: - for i, v := range ovsSet.GoSet { - nv, err := OvsToNativeAtomic(column.TypeObj.Key.Type, v) - if err != nil { - return nil, err - } - array.Index(i).Set(reflect.ValueOf(nv)) - } - default: - nv, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsElem) - if err != nil { - return nil, err - } - array.Index(0).Set(reflect.ValueOf(nv)) - } - return array.Interface(), nil case reflect.Slice: return OvsToNativeSlice(column.TypeObj.Key.Type, ovsElem) default: - return nil, fmt.Errorf("native type was not slice, array or pointer. got %d", naType.Kind()) + return nil, fmt.Errorf("native type was not slice or pointer. got %d", naType.Kind()) } case TypeMap: naType := NativeType(column) @@ -255,33 +234,7 @@ func NativeToOvs(column *ColumnSchema, rawElem interface{}) (interface{}, error) case TypeUUID: return UUID{GoUUID: rawElem.(string)}, nil case TypeSet: - var ovsSet OvsSet - if column.TypeObj.Key.Type == TypeUUID { - ovsSlice := []interface{}{} - if _, ok := rawElem.([]string); ok { - for _, v := range rawElem.([]string) { - uuid := UUID{GoUUID: v} - ovsSlice = append(ovsSlice, uuid) - } - } else if _, ok := rawElem.(*string); ok { - v := rawElem.(*string) - if v != nil { - uuid := UUID{GoUUID: *v} - ovsSlice = append(ovsSlice, uuid) - } - } else { - return nil, fmt.Errorf("uuid slice was neither []string or *string") - } - ovsSet = OvsSet{GoSet: ovsSlice} - - } else { - var err error - ovsSet, err = NewOvsSet(rawElem) - if err != nil { - return nil, err - } - } - return ovsSet, nil + return NewOvsSet(column.TypeObj.Key.Type, rawElem) case TypeMap: nativeMapVal := reflect.ValueOf(rawElem) ovsMap := make(map[interface{}]interface{}, nativeMapVal.Len()) @@ -447,3 +400,21 @@ func isDefaultBaseValue(elem interface{}, etype ExtendedType) bool { return false } } + +// ValidateColumnConstraints validates the native value against any constraints +// of a given column. +func ValidateColumnConstraints(column *ColumnSchema, nativeValue interface{}) error { + switch column.Type { + case TypeSet, TypeEnum: + // Validate set length requirements + newVal := reflect.ValueOf(nativeValue) + maxVal := column.TypeObj.Max() + minVal := column.TypeObj.Min() + if maxVal > 1 && newVal.Len() > maxVal { + return fmt.Errorf("slice would overflow (%d elements but %d allowed)", newVal.Len(), maxVal) + } else if minVal > 0 && newVal.Len() < minVal { + return fmt.Errorf("slice would underflow (%d elements but %d required)", newVal.Len(), minVal) + } + } + return nil +} diff --git a/ovsdb/bindings_test.go b/ovsdb/bindings_test.go index afac2d06..4d26822d 100644 --- a/ovsdb/bindings_test.go +++ b/ovsdb/bindings_test.go @@ -20,8 +20,6 @@ var ( aUUID2 = "2f77b348-9768-4866-b761-89d5177ecda2" aUUID3 = "2f77b348-9768-4866-b761-89d5177ecda3" - aSingleUUIDSet, _ = NewOvsSet(UUID{GoUUID: aUUID0}) - aUUIDSet = []string{ aUUID0, aUUID1, @@ -60,26 +58,27 @@ var ( ) func TestOvsToNativeAndNativeToOvs(t *testing.T) { - s, _ := NewOvsSet(aSet) - s1, _ := NewOvsSet([]string{aString}) + s, _ := NewOvsSet(TypeString, aSet) + s1, _ := NewOvsSet(TypeString, []string{aString}) us := make([]UUID, 0) for _, u := range aUUIDSet { us = append(us, UUID{GoUUID: u}) } - uss, _ := NewOvsSet(us) + uss, _ := NewOvsSet(TypeUUID, us) us1 := []UUID{{GoUUID: aUUID0}} - uss1, _ := NewOvsSet(us1) + uss1, _ := NewOvsSet(TypeUUID, us1) - is, _ := NewOvsSet(aIntSet) - fs, _ := NewOvsSet(aFloatSet) + is, _ := NewOvsSet(TypeInteger, aIntSet) + fs, _ := NewOvsSet(TypeReal, aFloatSet) - sis, _ := NewOvsSet([]int{aInt}) - sfs, _ := NewOvsSet([]float64{aFloat}) + sis, _ := NewOvsSet(TypeInteger, []int{aInt}) + sfs, _ := NewOvsSet(TypeReal, []float64{aFloat}) - es, _ := NewOvsSet(aEmptySet) - ens, _ := NewOvsSet(aEnumSet) + eso, _ := NewOvsSet(TypeString, aEmptySet) + esu, _ := NewOvsSet(TypeString, aEmptySet) + ens, _ := NewOvsSet(TypeString, aEnumSet) m, _ := NewOvsMap(aMap) @@ -89,7 +88,8 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { "key3": {GoUUID: aUUID2}, }) - singleStringSet, _ := NewOvsSet([]string{"foo"}) + singleStringSet, _ := NewOvsSet(TypeString, []string{"foo"}) + singleUUIDSet, _ := NewOvsSet(TypeUUID, UUID{GoUUID: aUUID0}) tests := []struct { name string @@ -284,9 +284,9 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { "max": "unlimited" } }`), - input: es, + input: esu, native: aEmptySet, - ovs: es, + ovs: esu, }, { // Enum @@ -390,9 +390,9 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { "max": 1 } }`), - input: aSingleUUIDSet, + input: singleUUIDSet, native: &aUUID0, - ovs: aSingleUUIDSet, + ovs: singleUUIDSet, }, { name: "null UUID set with min 0 max 1", @@ -407,9 +407,9 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { "max": 1 } }`), - input: es, + input: eso, native: (*string)(nil), - ovs: es, + ovs: eso, }, { name: "A string with min 0 max 1", @@ -457,7 +457,7 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { } func TestOvsToNativeErr(t *testing.T) { - as, _ := NewOvsSet([]string{"foo"}) + as, _ := NewOvsSet(TypeString, []string{"foo"}) s, _ := NewOvsMap(map[string]string{"foo": "bar"}) diff --git a/ovsdb/condition_test.go b/ovsdb/condition_test.go index 4eae9cb8..7b572ca7 100644 --- a/ovsdb/condition_test.go +++ b/ovsdb/condition_test.go @@ -9,6 +9,11 @@ import ( ) func TestConditionMarshalUnmarshalJSON(t *testing.T) { + stringSet, err := NewOvsSet(TypeString, []interface{}{"foo", "bar", "baz"}) + assert.NoError(t, err) + uuidSet, err := NewOvsSet(TypeUUID, []interface{}{UUID{GoUUID: "foo"}, UUID{GoUUID: "bar"}}) + assert.NoError(t, err) + tests := []struct { name string condition Condition @@ -71,7 +76,7 @@ func TestConditionMarshalUnmarshalJSON(t *testing.T) { }, { "test set", - Condition{"foo", ConditionExcludes, OvsSet{GoSet: []interface{}{"foo", "bar", "baz"}}}, + Condition{"foo", ConditionExcludes, stringSet}, `[ "foo", "excludes", ["set",["foo", "bar", "baz"]] ]`, false, }, @@ -83,7 +88,7 @@ func TestConditionMarshalUnmarshalJSON(t *testing.T) { }, { "test uuid set", - Condition{"foo", ConditionExcludes, OvsSet{GoSet: []interface{}{UUID{GoUUID: "foo"}, UUID{GoUUID: "bar"}}}}, + Condition{"foo", ConditionExcludes, uuidSet}, `[ "foo", "excludes", ["set",[["named-uuid", "foo"], ["named-uuid", "bar"]]] ]`, false, }, diff --git a/ovsdb/encoding_test.go b/ovsdb/encoding_test.go index 2ea6c60b..ceee3ec1 100644 --- a/ovsdb/encoding_test.go +++ b/ovsdb/encoding_test.go @@ -63,108 +63,116 @@ func TestSet(t *testing.T) { var y *string tests := []struct { name string + setType string input interface{} expected string }{ { "empty set", + TypeString, []string{}, `["set",[]]`, }, { "string", + TypeString, `aa`, `"aa"`, }, { "bool", + TypeBoolean, false, `false`, }, { "float 64", + TypeReal, float64(10), `10`, }, { "float", + TypeReal, 10.2, `10.2`, }, { "string slice", + TypeString, []string{`aa`}, `"aa"`, }, - { - "string array", - [1]string{`aa`}, - `"aa"`, - }, { "string slice with multiple elements", + TypeString, []string{`aa`, `bb`}, `["set",["aa","bb"]]`, }, - { - "string array multiple elements", - [2]string{`aa`, `bb`}, - `["set",["aa","bb"]]`, - }, { "float slice", + TypeString, []float64{10.2, 15.4}, `["set",[10.2,15.4]]`, }, { "empty uuid", + TypeUUID, []UUID{}, `["set",[]]`, }, { "uuid", + TypeUUID, UUID{GoUUID: `aa`}, `["named-uuid","aa"]`, }, { "uuid slice single element", + TypeUUID, []UUID{{GoUUID: `aa`}}, `["named-uuid","aa"]`, }, { "uuid slice multiple elements", + TypeUUID, []UUID{{GoUUID: `aa`}, {GoUUID: `bb`}}, `["set",[["named-uuid","aa"],["named-uuid","bb"]]]`, }, { "valid uuid", + TypeUUID, validUUID0, fmt.Sprintf(`["uuid","%v"]`, validUUIDStr0), }, { "valid uuid set single element", + TypeUUID, []UUID{validUUID0}, fmt.Sprintf(`["uuid","%v"]`, validUUIDStr0), }, { "valid uuid set multiple elements", + TypeUUID, []UUID{validUUID0, validUUID1}, fmt.Sprintf(`["set",[["uuid","%v"],["uuid","%v"]]]`, validUUIDStr0, validUUIDStr1), }, { name: "nil pointer of valid *int type", + setType: TypeInteger, input: x, expected: `["set",[]]`, }, { name: "nil pointer of valid *string type", + setType: TypeString, input: y, expected: `["set",[]]`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - set, err := NewOvsSet(tt.input) + set, err := NewOvsSet(tt.setType, tt.input) assert.Nil(t, err) jsonStr, err := json.Marshal(set) assert.Nil(t, err) @@ -173,7 +181,7 @@ func TestSet(t *testing.T) { var res OvsSet err = json.Unmarshal(jsonStr, &res) assert.Nil(t, err) - assert.Equal(t, set.GoSet, res.GoSet, "they should have the same elements\n") + assert.Equal(t, set.goSet, res.goSet, "they should have the same elements\n") }) } } diff --git a/ovsdb/error.go b/ovsdb/error.go index 0803894e..964b3563 100644 --- a/ovsdb/error.go +++ b/ovsdb/error.go @@ -153,7 +153,7 @@ func NewConstraintViolation(details string) *ConstraintViolation { } // Error implements the error interface -func (e *ConstraintViolation) Error() string { +func (e ConstraintViolation) Error() string { msg := constraintViolation if e.details != "" { msg += ": " + e.details diff --git a/ovsdb/mutation_test.go b/ovsdb/mutation_test.go index a70c835b..8237f923 100644 --- a/ovsdb/mutation_test.go +++ b/ovsdb/mutation_test.go @@ -9,6 +9,11 @@ import ( ) func TestMutationMarshalUnmarshalJSON(t *testing.T) { + stringSet, err := NewOvsSet(TypeString, []interface{}{"foo", "bar", "baz"}) + assert.NoError(t, err) + uuidSet, err := NewOvsSet(TypeUUID, []interface{}{UUID{GoUUID: "foo"}, UUID{GoUUID: "bar"}}) + assert.NoError(t, err) + tests := []struct { name string mutation Mutation @@ -65,7 +70,7 @@ func TestMutationMarshalUnmarshalJSON(t *testing.T) { }, { "test set", - Mutation{"foo", MutateOperationInsert, OvsSet{GoSet: []interface{}{"foo", "bar", "baz"}}}, + Mutation{"foo", MutateOperationInsert, stringSet}, `[ "foo", "insert", ["set",["foo", "bar", "baz"]] ]`, false, }, @@ -77,7 +82,7 @@ func TestMutationMarshalUnmarshalJSON(t *testing.T) { }, { "test uuid set", - Mutation{"foo", MutateOperationInsert, OvsSet{GoSet: []interface{}{UUID{GoUUID: "foo"}, UUID{GoUUID: "bar"}}}}, + Mutation{"foo", MutateOperationInsert, uuidSet}, `[ "foo", "insert", ["set",[["named-uuid", "foo"], ["named-uuid", "bar"]]] ]`, false, }, diff --git a/ovsdb/notation_test.go b/ovsdb/notation_test.go index ff183f4a..32537ee4 100644 --- a/ovsdb/notation_test.go +++ b/ovsdb/notation_test.go @@ -73,7 +73,7 @@ func TestOpRowsSerialization(t *testing.T) { func TestValidateOvsSet(t *testing.T) { goSlice := []int{1, 2, 3, 4} - oSet, err := NewOvsSet(goSlice) + oSet, err := NewOvsSet(TypeInteger, goSlice) if err != nil { t.Error("Error creating OvsSet ", err) } @@ -86,7 +86,7 @@ func TestValidateOvsSet(t *testing.T) { t.Error("Expected: ", expected, "Got", string(data)) } // Negative condition test - oSet, err = NewOvsSet(struct{ foo string }{}) + oSet, err = NewOvsSet(TypeString, struct{ foo string }{}) if err == nil { t.Error("OvsSet must fail for anything other than Slices and atomic types") t.Error("Got", oSet) @@ -194,6 +194,13 @@ func TestOperationsMarshalUnmarshalJSON(t *testing.T) { } func TestOvsSliceToGoNotation(t *testing.T) { + emptySet, err := NewOvsSet(TypeString, []interface{}{}) + assert.NoError(t, err) + stringSet, err := NewOvsSet(TypeString, []interface{}{"foo", "bar", "baz"}) + assert.NoError(t, err) + uuidSet, err := NewOvsSet(TypeUUID, []interface{}{UUID{GoUUID: "foo"}, UUID{GoUUID: "bar"}}) + assert.NoError(t, err) + tests := []struct { name string value interface{} @@ -209,19 +216,19 @@ func TestOvsSliceToGoNotation(t *testing.T) { { "empty set", []interface{}{"set", []interface{}{}}, - OvsSet{GoSet: []interface{}{}}, + emptySet, false, }, { "set", []interface{}{"set", []interface{}{"foo", "bar", "baz"}}, - OvsSet{GoSet: []interface{}{"foo", "bar", "baz"}}, + stringSet, false, }, { "uuid set", []interface{}{"set", []interface{}{[]interface{}{"named-uuid", "foo"}, []interface{}{"named-uuid", "bar"}}}, - OvsSet{GoSet: []interface{}{UUID{GoUUID: "foo"}, UUID{GoUUID: "bar"}}}, + uuidSet, false, }, { diff --git a/ovsdb/schema.go b/ovsdb/schema.go index cf80aa50..12b916cf 100644 --- a/ovsdb/schema.go +++ b/ovsdb/schema.go @@ -363,7 +363,7 @@ func (b BaseType) MarshalJSON() ([]byte, error) { RefType: b.refType, } if len(b.Enum) > 0 { - set, err := NewOvsSet(b.Enum) + set, err := NewOvsSet(b.Type, b.Enum) if err != nil { return nil, err } diff --git a/ovsdb/set.go b/ovsdb/set.go index d5fab1ec..b1c7f16d 100644 --- a/ovsdb/set.go +++ b/ovsdb/set.go @@ -12,32 +12,62 @@ import ( // a 2-element JSON array that represents a database set value. The // first element of the array must be the string "set", and the // second element must be an array of zero or more s giving the -// values in the set. All of the s must have the same type. +// values in the set. All of the s must have the same type, and all +// values must be unique within the set. type OvsSet struct { - GoSet []interface{} + goSet []interface{} +} + +func getUUID(val interface{}) (UUID, error) { + uuid, ok := val.(UUID) + if ok { + return uuid, nil + } + str, ok := val.(string) + if ok { + return UUID{GoUUID: str}, nil + } + return UUID{}, fmt.Errorf("expected UUID or string but got %T", val) } // NewOvsSet creates a new OVSDB style set from a Go interface (object) -func NewOvsSet(obj interface{}) (OvsSet, error) { +func NewOvsSet(keyType string, obj interface{}) (OvsSet, error) { ovsSet := make([]interface{}, 0) var v reflect.Value if reflect.TypeOf(obj).Kind() == reflect.Ptr { v = reflect.ValueOf(obj).Elem() if v.Kind() == reflect.Invalid { // must be a nil pointer, so just return an empty set - return OvsSet{ovsSet}, nil + return OvsSet{goSet: ovsSet}, nil } } else { v = reflect.ValueOf(obj) } switch v.Kind() { - case reflect.Slice, reflect.Array: + case reflect.Slice: for i := 0; i < v.Len(); i++ { - ovsSet = append(ovsSet, v.Index(i).Interface()) + if keyType == TypeUUID { + uuid, err := getUUID(v.Index(i).Interface()) + if err != nil { + return OvsSet{}, err + } + ovsSet = append(ovsSet, uuid) + } else { + ovsSet = append(ovsSet, v.Index(i).Interface()) + } + } + case reflect.String: + if keyType == TypeUUID { + uuid, err := getUUID(v.Interface()) + if err != nil { + return OvsSet{}, err + } + ovsSet = append(ovsSet, uuid) + } else { + ovsSet = append(ovsSet, v.Interface()) } - case reflect.String, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.Bool: ovsSet = append(ovsSet, v.Interface()) @@ -50,18 +80,18 @@ func NewOvsSet(obj interface{}) (OvsSet, error) { default: return OvsSet{}, fmt.Errorf("ovsset supports only go slice/string/numbers/uuid or pointers to those types") } - return OvsSet{ovsSet}, nil + return OvsSet{goSet: ovsSet}, nil } // MarshalJSON wil marshal an OVSDB style Set in to a JSON byte array func (o OvsSet) MarshalJSON() ([]byte, error) { - switch l := len(o.GoSet); { + switch l := len(o.goSet); { case l == 1: - return json.Marshal(o.GoSet[0]) + return json.Marshal(o.goSet[0]) case l > 0: var oSet []interface{} oSet = append(oSet, "set") - oSet = append(oSet, o.GoSet) + oSet = append(oSet, o.goSet) return json.Marshal(oSet) } return []byte("[\"set\",[]]"), nil @@ -69,11 +99,11 @@ func (o OvsSet) MarshalJSON() ([]byte, error) { // UnmarshalJSON will unmarshal a JSON byte array to an OVSDB style Set func (o *OvsSet) UnmarshalJSON(b []byte) (err error) { - o.GoSet = make([]interface{}, 0) + o.goSet = make([]interface{}, 0) addToSet := func(o *OvsSet, v interface{}) error { goVal, err := ovsSliceToGoNotation(v) if err == nil { - o.GoSet = append(o.GoSet, goVal) + o.goSet = append(o.goSet, goVal) } return err } @@ -107,3 +137,93 @@ func (o *OvsSet) UnmarshalJSON(b []byte) (err error) { return addToSet(o, inter) } } + +func (o *OvsSet) Append(newVal ...interface{}) error { + o.goSet = append(o.goSet, newVal...) + return nil +} + +func (o *OvsSet) Len() int { + return len(o.goSet) +} + +func (o *OvsSet) Replace(idx int, newVal interface{}) error { + if idx > len(o.goSet)-1 { + return fmt.Errorf("attempted to access element %d beyond end of array (length %d)", idx, len(o.goSet)) + } + o.goSet[idx] = newVal + return nil +} + +// HasElementType matches the given value's type with the set's element type. +// It returns true if the set has at least one element, and that element is +// of the given type, otherwise false. +func (o *OvsSet) HasElementType(checkVal interface{}) bool { + if len(o.goSet) == 0 { + return false + } + return reflect.ValueOf(checkVal).Type() == reflect.ValueOf(o.goSet[0]).Type() +} + +// Range iterates over elements of the set and calls the given function for +// each element. The function should return true if iteration should terminate, +// a value to return to the caller of Range(), and/or an error (which also +// terminates iteration). +func (o *OvsSet) Range(elemFn func(int, interface{}) (bool, error)) error { + for i, v := range o.goSet { + done, err := elemFn(i, v) + if err != nil { + return err + } else if done { + return nil + } + } + return nil +} + +func SetDifference(a, b OvsSet) (OvsSet, bool) { + if a.Len() == 0 && b.Len() == 0 { + return a, false + } else if a.Len() == 0 && b.Len() != 0 { + return b, b.Len() != 0 + } else if b.Len() == 0 && a.Len() != 0 { + return a, a.Len() != 0 + } + + // From https://docs.openvswitch.org/en/latest/ref/ovsdb-server.7/#update2-notification + // The difference between two sets are all elements that only belong to one + // of the sets. + difference := make(map[interface{}]struct{}, b.Len()) + for i := 0; i < b.Len(); i++ { + // supossedly we are working with comparable atomic types with no + // pointers so we can use the values as map key + difference[b.goSet[i]] = struct{}{} + } + j := a.Len() + for i := 0; i < j; { + vi := a.goSet[i] + if _, ok := difference[vi]; ok { + // this value of 'a' is in 'b', so remove it from 'a'; to do that, + // overwrite it with the last value and re-evaluate + a.goSet[i] = a.goSet[j-1] + // decrease where the last 'a' value is at + j-- + // remove from 'b' values + delete(difference, vi) + } else { + // this value of 'a' is not in 'b', evaluate the next value + i++ + } + } + // trim the slice to the actual values held + a.goSet = a.goSet[:j] + for item := range difference { + a.goSet = append(a.goSet, item) + } + + if a.Len() == 0 { + return a, false + } + + return a, true +} diff --git a/ovsdb/set_test.go b/ovsdb/set_test.go index 12857a87..99221218 100644 --- a/ovsdb/set_test.go +++ b/ovsdb/set_test.go @@ -19,8 +19,8 @@ var testUUIDs = []string{ "7e191fdb-228d-4bf3-9db4-883c8705ac7e", } -func benchmarkSetMarshalJSON(s interface{}, b *testing.B) { - testSet, err := NewOvsSet(s) +func benchmarkSetMarshalJSON(keyType string, s interface{}, b *testing.B) { + testSet, err := NewOvsSet(keyType, s) if err != nil { b.Fatal(err) } @@ -31,60 +31,60 @@ func benchmarkSetMarshalJSON(s interface{}, b *testing.B) { } } } -func BenchmarkSetMarshalJSONString1(b *testing.B) { benchmarkSetMarshalJSON("foo", b) } +func BenchmarkSetMarshalJSONString1(b *testing.B) { benchmarkSetMarshalJSON(TypeString, "foo", b) } func BenchmarkSetMarshalJSONString2(b *testing.B) { - benchmarkSetMarshalJSON([]string{"foo", "bar"}, b) + benchmarkSetMarshalJSON(TypeString, []string{"foo", "bar"}, b) } func BenchmarkSetMarshalJSONString3(b *testing.B) { - benchmarkSetMarshalJSON([]string{"foo", "bar", "baz"}, b) + benchmarkSetMarshalJSON(TypeString, []string{"foo", "bar", "baz"}, b) } func BenchmarkSetMarshalJSONString5(b *testing.B) { - benchmarkSetMarshalJSON([]string{"foo", "bar", "baz", "quux", "foofoo"}, b) + benchmarkSetMarshalJSON(TypeString, []string{"foo", "bar", "baz", "quux", "foofoo"}, b) } func BenchmarkSetMarshalJSONString8(b *testing.B) { - benchmarkSetMarshalJSON([]string{"foo", "bar", "baz", "quux", "foofoo", "foobar", "foobaz", "fooquux"}, b) + benchmarkSetMarshalJSON(TypeString, []string{"foo", "bar", "baz", "quux", "foofoo", "foobar", "foobaz", "fooquux"}, b) } -func BenchmarkSetMarshalJSONInt1(b *testing.B) { benchmarkSetMarshalJSON(1, b) } +func BenchmarkSetMarshalJSONInt1(b *testing.B) { benchmarkSetMarshalJSON(TypeInteger, 1, b) } func BenchmarkSetMarshalJSONInt2(b *testing.B) { - benchmarkSetMarshalJSON([]int{1, 2}, b) + benchmarkSetMarshalJSON(TypeInteger, []int{1, 2}, b) } func BenchmarkSetMarshalJSONInt3(b *testing.B) { - benchmarkSetMarshalJSON([]int{1, 2, 3}, b) + benchmarkSetMarshalJSON(TypeInteger, []int{1, 2, 3}, b) } func BenchmarkSetMarshalJSONInt5(b *testing.B) { - benchmarkSetMarshalJSON([]int{1, 2, 3, 4, 5}, b) + benchmarkSetMarshalJSON(TypeInteger, []int{1, 2, 3, 4, 5}, b) } func BenchmarkSetMarshalJSONInt8(b *testing.B) { - benchmarkSetMarshalJSON([]int{1, 2, 3, 4, 5, 6, 7, 8}, b) + benchmarkSetMarshalJSON(TypeInteger, []int{1, 2, 3, 4, 5, 6, 7, 8}, b) } -func BenchmarkSetMarshalJSONFloat1(b *testing.B) { benchmarkSetMarshalJSON(1.0, b) } +func BenchmarkSetMarshalJSONFloat1(b *testing.B) { benchmarkSetMarshalJSON(TypeReal, 1.0, b) } func BenchmarkSetMarshalJSONFloat2(b *testing.B) { - benchmarkSetMarshalJSON([]int{1.0, 2.0}, b) + benchmarkSetMarshalJSON(TypeReal, []int{1.0, 2.0}, b) } func BenchmarkSetMarshalJSONFloat3(b *testing.B) { - benchmarkSetMarshalJSON([]int{1.0, 2.0, 3.0}, b) + benchmarkSetMarshalJSON(TypeReal, []int{1.0, 2.0, 3.0}, b) } func BenchmarkSetMarshalJSONFloat5(b *testing.B) { - benchmarkSetMarshalJSON([]int{1.0, 2.0, 3.0, 4.0, 5.0}, b) + benchmarkSetMarshalJSON(TypeReal, []int{1.0, 2.0, 3.0, 4.0, 5.0}, b) } func BenchmarkSetMarshalJSONFloat8(b *testing.B) { - benchmarkSetMarshalJSON([]int{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, b) + benchmarkSetMarshalJSON(TypeReal, []int{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0}, b) } -func BenchmarkSetMarshalJSONUUID1(b *testing.B) { benchmarkSetMarshalJSON(testUUIDs[0], b) } +func BenchmarkSetMarshalJSONUUID1(b *testing.B) { benchmarkSetMarshalJSON(TypeUUID, testUUIDs[0], b) } func BenchmarkSetMarshalJSONUUID2(b *testing.B) { - benchmarkSetMarshalJSON(testUUIDs[0:2], b) + benchmarkSetMarshalJSON(TypeUUID, testUUIDs[0:2], b) } func BenchmarkSetMarshalJSONUUID3(b *testing.B) { - benchmarkSetMarshalJSON(testUUIDs[0:3], b) + benchmarkSetMarshalJSON(TypeUUID, testUUIDs[0:3], b) } func BenchmarkSetMarshalJSONUUID5(b *testing.B) { - benchmarkSetMarshalJSON(testUUIDs[0:5], b) + benchmarkSetMarshalJSON(TypeUUID, testUUIDs[0:5], b) } func BenchmarkSetMarshalJSONUUID8(b *testing.B) { - benchmarkSetMarshalJSON(testUUIDs, b) + benchmarkSetMarshalJSON(TypeUUID, testUUIDs, b) } func benchmarkSetUnmarshalJSON(data []byte, b *testing.B) { diff --git a/server/server.go b/server/server.go index b82fa1c9..e7801072 100644 --- a/server/server.go +++ b/server/server.go @@ -193,18 +193,30 @@ func (o *OvsdbServer) Transact(client *rpc2.Client, args []json.RawMessage, repl op.UUIDName = newUUID } for i, condition := range op.Where { - op.Where[i].Value = expandNamedUUID(condition.Value, namedUUID) + op.Where[i].Value, err = expandNamedUUID(condition.Value, namedUUID) + if err != nil { + return err + } } for i, mutation := range op.Mutations { - op.Mutations[i].Value = expandNamedUUID(mutation.Value, namedUUID) + op.Mutations[i].Value, err = expandNamedUUID(mutation.Value, namedUUID) + if err != nil { + return err + } } for _, row := range op.Rows { for k, v := range row { - row[k] = expandNamedUUID(v, namedUUID) + row[k], err = expandNamedUUID(v, namedUUID) + if err != nil { + return err + } } } for k, v := range op.Row { - op.Row[k] = expandNamedUUID(v, namedUUID) + op.Row[k], err = expandNamedUUID(v, namedUUID) + if err != nil { + return err + } } ops = append(ops, op) } @@ -420,21 +432,29 @@ func (o *OvsdbServer) processMonitors(id uuid.UUID, update database.Update) { o.monitorMutex.RUnlock() } -func expandNamedUUID(value interface{}, namedUUID map[string]ovsdb.UUID) interface{} { +func expandNamedUUID(value interface{}, namedUUID map[string]ovsdb.UUID) (interface{}, error) { if uuid, ok := value.(ovsdb.UUID); ok { if newUUID, ok := namedUUID[uuid.GoUUID]; ok { - return newUUID + return newUUID, nil } } if set, ok := value.(ovsdb.OvsSet); ok { - for i, s := range set.GoSet { - if _, ok := s.(ovsdb.UUID); !ok { - return value - } - uuid := s.(ovsdb.UUID) + if !set.HasElementType(ovsdb.UUID{}) { + // Not a UUID set; nothing to expand. Return original value. + return value, nil + } + if err := set.Range(func(i int, val interface{}) (bool, error) { + uuid := val.(ovsdb.UUID) if newUUID, ok := namedUUID[uuid.GoUUID]; ok { - set.GoSet[i] = newUUID + // Replace named UUID with the real one + if err := set.Replace(i, newUUID); err != nil { + return true, err + } } + // Continue to next element + return false, nil + }); err != nil { + return nil, err } } if m, ok := value.(ovsdb.OvsMap); ok { @@ -452,5 +472,5 @@ func expandNamedUUID(value interface{}, namedUUID map[string]ovsdb.UUID) interfa } } } - return value + return value, nil } diff --git a/server/server_test.go b/server/server_test.go index 9776c470..37245f69 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -8,6 +8,7 @@ import ( "github.com/ovn-org/libovsdb/database" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,14 +33,14 @@ func TestExpandNamedUUID(t *testing.T) { { "set", map[string]ovsdb.UUID{"foo": {GoUUID: testUUID}}, - ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "foo"}}}, - ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: testUUID}}}, + testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []interface{}{ovsdb.UUID{GoUUID: "foo"}}), + testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []interface{}{ovsdb.UUID{GoUUID: testUUID}}), }, { "set multiple", map[string]ovsdb.UUID{"foo": {GoUUID: testUUID}, "bar": {GoUUID: testUUID1}}, - ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "foo"}, ovsdb.UUID{GoUUID: "bar"}, ovsdb.UUID{GoUUID: "baz"}}}, - ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: testUUID}, ovsdb.UUID{GoUUID: testUUID1}, ovsdb.UUID{GoUUID: "baz"}}}, + testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []interface{}{ovsdb.UUID{GoUUID: "foo"}, ovsdb.UUID{GoUUID: "bar"}, ovsdb.UUID{GoUUID: "baz"}}), + testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []interface{}{ovsdb.UUID{GoUUID: testUUID}, ovsdb.UUID{GoUUID: testUUID1}, ovsdb.UUID{GoUUID: "baz"}}), }, { "map key", @@ -62,7 +63,8 @@ func TestExpandNamedUUID(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := expandNamedUUID(tt.value, tt.namedUUIDs) + got, err := expandNamedUUID(tt.value, tt.namedUUIDs) + assert.NoError(t, err) assert.Equal(t, tt.expected, got) }) } diff --git a/test/helpers/helpers.go b/test/helpers/helpers.go new file mode 100644 index 00000000..66bc0933 --- /dev/null +++ b/test/helpers/helpers.go @@ -0,0 +1,19 @@ +package testhelpers + +import ( + "github.com/stretchr/testify/assert" + + "github.com/ovn-org/libovsdb/ovsdb" +) + +func MakeOvsSet(t assert.TestingT, keyType string, set interface{}) ovsdb.OvsSet { + oSet, err := ovsdb.NewOvsSet(keyType, set) + assert.Nil(t, err) + return oSet +} + +func MakeOvsMap(t assert.TestingT, m interface{}) ovsdb.OvsMap { + oMap, err := ovsdb.NewOvsMap(m) + assert.Nil(t, err) + return oMap +} diff --git a/test/ovs/ovs_integration_test.go b/test/ovs/ovs_integration_test.go index 0a37073e..9b7265c2 100644 --- a/test/ovs/ovs_integration_test.go +++ b/test/ovs/ovs_integration_test.go @@ -16,6 +16,7 @@ import ( "github.com/ovn-org/libovsdb/client" "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -1013,7 +1014,7 @@ func (suite *OVSIntegrationSuite) TestMultipleOpsSameRow() { UUIDName: port1UUID, Row: ovsdb.Row{ "name": port1UUID, - "interfaces": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: iface1UUID}}}, + "interfaces": testhelpers.MakeOvsSet(suite.T(), ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: iface1UUID}}), }, }, } @@ -1040,7 +1041,7 @@ func (suite *OVSIntegrationSuite) TestMultipleOpsSameRow() { UUIDName: port10UUID, Row: ovsdb.Row{ "name": port10UUID, - "interfaces": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: iface10UUID}}}, + "interfaces": testhelpers.MakeOvsSet(suite.T(), ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: iface10UUID}}), }, }, } diff --git a/test/test_data.go b/test/test_data.go index 6be6b8b2..aee27649 100644 --- a/test/test_data.go +++ b/test/test_data.go @@ -80,6 +80,17 @@ const schema = ` "min": 0, "max": "unlimited" } + }, + "flood_vlans": { + "type": { + "key": { + "type": "integer", + "minInteger": 0, + "maxInteger": 4095 + }, + "min": 0, + "max": 3 + } } }, "indexes": [ @@ -142,6 +153,7 @@ type BridgeType struct { ExternalIds map[string]string `ovsdb:"external_ids"` Ports []string `ovsdb:"ports"` Status map[string]string `ovsdb:"status"` + FloodVLANs []int `ovsdb:"flood_vlans"` } // OvsType is the simplified ORM model of the Bridge table diff --git a/updates/merge.go b/updates/merge.go index 903490c1..b9da6aa9 100644 --- a/updates/merge.go +++ b/updates/merge.go @@ -105,8 +105,7 @@ func mergeModifyRow(ts *ovsdb.TableSchema, o, a, b *ovsdb.Row) *ovsdb.Row { if ts.Column(k).TypeObj.Max() != 1 { // set difference is a fully transitive operation so we dont // need to do anything special to merge two differences - result, changed = setDifference(aSet.GoSet, bSet.GoSet) - result = ovsdb.OvsSet{GoSet: result.([]interface{})} + result, changed = ovsdb.SetDifference(aSet, bSet) } case ovsdb.OvsMap: aMap := aMod[k].(ovsdb.OvsMap) diff --git a/updates/merge_test.go b/updates/merge_test.go index 54039f97..a9f4190b 100644 --- a/updates/merge_test.go +++ b/updates/merge_test.go @@ -5,6 +5,7 @@ import ( "github.com/ovn-org/libovsdb/ovsdb" "github.com/ovn-org/libovsdb/test" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -724,10 +725,10 @@ func Test_merge(t *testing.T) { }, New: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -743,14 +744,14 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, New: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -769,10 +770,10 @@ func Test_merge(t *testing.T) { }, New: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -792,14 +793,14 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), }, New: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -815,14 +816,14 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, New: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), }, }, }, @@ -843,14 +844,14 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), }, New: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -865,13 +866,13 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, New: &ovsdb.Row{ "name": "bridge", }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{}), }, }, }, @@ -887,13 +888,13 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), }, New: &ovsdb.Row{ "name": "bridge", }, Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{}), }, }, }, @@ -912,13 +913,13 @@ func Test_merge(t *testing.T) { }, rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port2"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port2"}), }, New: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port3"}), }, Modify: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port2", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port2", "port3"}), }, }, }, @@ -933,13 +934,13 @@ func Test_merge(t *testing.T) { }, rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port3"}), }, New: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port2"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port2"}), }, Modify: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port2", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port2", "port3"}), }, }, }, @@ -1011,21 +1012,21 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port2"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port2"}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key": "value", "key1": "value1", "key2": "value2"}}, - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), }, New: &ovsdb.Row{ "name": "bridge2", - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port3"}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key": "value1", "key1": "value1", "key3": "value3"}}, - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, Modify: &ovsdb.Row{ "name": "bridge2", - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port2", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port2", "port3"}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key": "value1", "key2": "value2", "key3": "value3"}}, - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -1045,19 +1046,19 @@ func Test_merge(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge2", - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port3"}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key": "value1", "key1": "value1", "key3": "value3"}}, }, New: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port1", "port2"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port1", "port2"}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key": "value", "key1": "value1", "key2": "value2"}}, }, Modify: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{"port2", "port3"}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"port2", "port3"}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{"key": "value", "key2": "value2", "key3": "value3"}}, - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), }, }, }, diff --git a/updates/updates_test.go b/updates/updates_test.go index 0977428e..3a2ae7d6 100644 --- a/updates/updates_test.go +++ b/updates/updates_test.go @@ -6,6 +6,7 @@ import ( "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" "github.com/ovn-org/libovsdb/test" + "github.com/ovn-org/libovsdb/test/helpers" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -667,12 +668,12 @@ func TestUpdates_AddOperation(t *testing.T) { { Column: "ports", Mutator: ovsdb.MutateOperationInsert, - Value: ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid1"}, ovsdb.UUID{GoUUID: "uuid3"}}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid1"}, {GoUUID: "uuid3"}}), }, { Column: "ports", Mutator: ovsdb.MutateOperationDelete, - Value: ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid3"}, ovsdb.UUID{GoUUID: "uuid1"}}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid3"}, {GoUUID: "uuid1"}}), }, }, }, @@ -694,14 +695,14 @@ func TestUpdates_AddOperation(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid1"}, ovsdb.UUID{GoUUID: "uuid2"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid1"}, {GoUUID: "uuid2"}}), }, New: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid2"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid2"}}), }, Modify: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid1"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid1"}}), }, }, }, @@ -735,7 +736,7 @@ func TestUpdates_AddOperation(t *testing.T) { { Column: "external_ids", Mutator: ovsdb.MutateOperationDelete, - Value: ovsdb.OvsSet{GoSet: []interface{}{"key"}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{"key"}), }, { Column: "external_ids", @@ -761,7 +762,7 @@ func TestUpdates_AddOperation(t *testing.T) { { Column: "ports", Mutator: ovsdb.MutateOperationInsert, - Value: ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid"}}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid"}}), }, }, }, @@ -798,11 +799,11 @@ func TestUpdates_AddOperation(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ New: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid"}}), }, Insert: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid"}}), }, }, }, @@ -825,7 +826,7 @@ func TestUpdates_AddOperation(t *testing.T) { { Column: "ports", Mutator: ovsdb.MutateOperationInsert, - Value: ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid"}}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid"}}), }, }, }, @@ -876,11 +877,11 @@ func TestUpdates_AddOperation(t *testing.T) { }, New: &ovsdb.Row{ "name": "bridge2", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid"}}), }, Modify: &ovsdb.Row{ "name": "bridge2", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid"}}), }, }, }, @@ -979,7 +980,7 @@ func TestUpdates_AddOperation(t *testing.T) { { Column: "ports", Mutator: ovsdb.MutateOperationInsert, - Value: ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid-2"}}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid-2"}}), }, }, }, @@ -996,11 +997,11 @@ func TestUpdates_AddOperation(t *testing.T) { rowUpdate2: &ovsdb.RowUpdate2{ Old: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid-1"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid-1"}}), }, Delete: &ovsdb.Row{ "name": "bridge", - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid-1"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid-1"}}), }, }, }, @@ -1020,7 +1021,7 @@ func TestUpdates_AddOperation(t *testing.T) { { Column: "ports", Mutator: ovsdb.MutateOperationInsert, - Value: ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid-2"}}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid-2"}}), }, }, }, @@ -1039,7 +1040,7 @@ func TestUpdates_AddOperation(t *testing.T) { { Column: "ports", Mutator: ovsdb.MutateOperationInsert, - Value: ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "uuid-2"}}}, + Value: testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "uuid-2"}}), }, }, }, @@ -1357,7 +1358,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, ru2: ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "foo"}, ovsdb.UUID{GoUUID: "bar"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "foo"}, {GoUUID: "bar"}}), }, }, }, @@ -1377,7 +1378,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, rowUpdate2: &ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: "foo"}, ovsdb.UUID{GoUUID: "bar"}}}, + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeUUID, []ovsdb.UUID{{GoUUID: "foo"}, {GoUUID: "bar"}}), }, }, }, @@ -1437,7 +1438,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, ru2: ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -1457,7 +1458,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, rowUpdate2: &ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -1476,7 +1477,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, ru2: ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -1495,7 +1496,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, rowUpdate2: &ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{newDatapathID}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{newDatapathID}), }, }, }, @@ -1515,7 +1516,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, ru2: ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{}), }, }, }, @@ -1534,7 +1535,7 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { }, rowUpdate2: &ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{}), }, }, }, @@ -1558,8 +1559,8 @@ func TestModelUpdates_AddRowUpdate2(t *testing.T) { ru2: ovsdb.RowUpdate2{ Modify: &ovsdb.Row{ "datapath_type": "type", - "datapath_id": ovsdb.OvsSet{GoSet: []interface{}{oldDatapathID}}, - "ports": ovsdb.OvsSet{GoSet: []interface{}{}}, + "datapath_id": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{oldDatapathID}), + "ports": testhelpers.MakeOvsSet(t, ovsdb.TypeString, []string{}), "external_ids": ovsdb.OvsMap{GoMap: map[interface{}]interface{}{}}, }, },