diff --git a/client/api_test.go b/client/api_test.go index bb6724b8..db4c30a7 100644 --- a/client/api_test.go +++ b/client/api_test.go @@ -9,6 +9,14 @@ import ( "github.com/ovn-org/libovsdb/model" "github.com/ovn-org/libovsdb/ovsdb" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + trueVal = true + falseVal = false + one = 1 + six = 6 ) func TestAPIListSimple(t *testing.T) { @@ -222,25 +230,25 @@ func TestAPIListFields(t *testing.T) { UUID: aUUID0, Name: "lsp0", ExternalIds: map[string]string{"foo": "bar"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, &testLogicalSwitchPort{ UUID: aUUID1, Name: "magiclsp1", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID2, Name: "lsp2", ExternalIds: map[string]string{"unique": "id"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID3, Name: "magiclsp2", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, } lspcache := map[string]model.Model{} @@ -624,22 +632,22 @@ func TestAPIMutate(t *testing.T) { Name: "lsp0", Type: "someType", ExternalIds: map[string]string{"foo": "bar"}, - Enabled: []bool{true}, - Tag: []int{1}, + Enabled: &trueVal, + Tag: &one, }, aUUID1: &testLogicalSwitchPort{ UUID: aUUID1, Name: "lsp1", Type: "someType", ExternalIds: map[string]string{"foo": "baz"}, - Tag: []int{1}, + Tag: &one, }, aUUID2: &testLogicalSwitchPort{ UUID: aUUID2, Name: "lsp2", Type: "someOtherType", ExternalIds: map[string]string{"foo": "baz"}, - Tag: []int{1}, + Tag: &one, }, } testData := cache.Data{ @@ -648,7 +656,6 @@ func TestAPIMutate(t *testing.T) { tcache := apiTestCache(t, testData) testObj := testLogicalSwitchPort{} - test := []struct { name string condition func(API) ConditionalAPI @@ -667,16 +674,16 @@ func TestAPIMutate(t *testing.T) { }, mutations: []model.Mutation{ { - Field: &testObj.Tag, + Field: &testObj.Addresses, Mutator: ovsdb.MutateOperationInsert, - Value: []int{5}, + Value: []string{"1.1.1.1"}, }, }, result: []ovsdb.Operation{ { Op: ovsdb.OperationMutate, Table: "Logical_Switch_Port", - Mutations: []ovsdb.Mutation{{Column: "tag", Mutator: ovsdb.MutateOperationInsert, Value: testOvsSet(t, []int{5})}}, + Mutations: []ovsdb.Mutation{{Column: "addresses", Mutator: ovsdb.MutateOperationInsert, Value: testOvsSet(t, []string{"1.1.1.1"})}}, Where: []ovsdb.Condition{{Column: "_uuid", Function: ovsdb.ConditionEqual, Value: ovsdb.UUID{GoUUID: aUUID0}}}, }, }, @@ -777,9 +784,9 @@ func TestAPIMutate(t *testing.T) { cond := tt.condition(api) ops, err := cond.Mutate(&testObj, tt.mutations...) if tt.err { - assert.NotNil(t, err) + require.Error(t, err) } else { - assert.Nil(t, err) + require.Nil(t, err) assert.ElementsMatchf(t, tt.result, ops, "ovsdb.Operations should match") } }) @@ -793,23 +800,23 @@ func TestAPIUpdate(t *testing.T) { Name: "lsp0", Type: "someType", ExternalIds: map[string]string{"foo": "bar"}, - Enabled: []bool{true}, - Tag: []int{1}, + Enabled: &trueVal, + Tag: &one, }, aUUID1: &testLogicalSwitchPort{ UUID: aUUID1, Name: "lsp1", Type: "someType", ExternalIds: map[string]string{"foo": "baz"}, - Tag: []int{1}, - Enabled: []bool{true}, + Tag: &one, + Enabled: &trueVal, }, aUUID2: &testLogicalSwitchPort{ UUID: aUUID2, Name: "lsp2", Type: "someOtherType", ExternalIds: map[string]string{"foo": "baz"}, - Tag: []int{1}, + Tag: &one, }, } testData := cache.Data{ @@ -836,7 +843,7 @@ func TestAPIUpdate(t *testing.T) { }, prepare: func(t *testLogicalSwitchPort) { t.Type = "somethingElse" - t.Tag = []int{6} + t.Tag = &six }, result: []ovsdb.Operation{ { @@ -857,7 +864,7 @@ func TestAPIUpdate(t *testing.T) { }, prepare: func(t *testLogicalSwitchPort) { t.Type = "somethingElse" - t.Tag = []int{6} + t.Tag = &six }, result: []ovsdb.Operation{ { @@ -874,7 +881,7 @@ func TestAPIUpdate(t *testing.T) { condition: func(a API) ConditionalAPI { t := testLogicalSwitchPort{ Type: "sometype", - Enabled: []bool{true}, + Enabled: &trueVal, } return a.Where(&t, model.Condition{ Field: &t.Type, @@ -883,7 +890,7 @@ func TestAPIUpdate(t *testing.T) { }) }, prepare: func(t *testLogicalSwitchPort) { - t.Tag = []int{6} + t.Tag = &six }, result: []ovsdb.Operation{ { @@ -908,11 +915,11 @@ func TestAPIUpdate(t *testing.T) { model.Condition{ Field: &t.Enabled, Function: ovsdb.ConditionIncludes, - Value: []bool{true}, + Value: &trueVal, }) }, prepare: func(t *testLogicalSwitchPort) { - t.Tag = []int{6} + t.Tag = &six }, result: []ovsdb.Operation{ { @@ -925,7 +932,7 @@ func TestAPIUpdate(t *testing.T) { Op: ovsdb.OperationUpdate, Table: "Logical_Switch_Port", Row: tagRow, - Where: []ovsdb.Condition{{Column: "enabled", Function: ovsdb.ConditionIncludes, Value: testOvsSet(t, []bool{true})}}, + Where: []ovsdb.Condition{{Column: "enabled", Function: ovsdb.ConditionIncludes, Value: testOvsSet(t, &trueVal)}}, }, }, err: false, @@ -943,11 +950,11 @@ func TestAPIUpdate(t *testing.T) { model.Condition{ Field: &t.Enabled, Function: ovsdb.ConditionIncludes, - Value: []bool{true}, + Value: &trueVal, }) }, prepare: func(t *testLogicalSwitchPort) { - t.Tag = []int{6} + t.Tag = &six }, result: []ovsdb.Operation{ { @@ -956,7 +963,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, []bool{true})}, + {Column: "enabled", Function: ovsdb.ConditionIncludes, Value: testOvsSet(t, &trueVal)}, }, }, }, @@ -967,7 +974,7 @@ func TestAPIUpdate(t *testing.T) { condition: func(a API) ConditionalAPI { t := testLogicalSwitchPort{ Type: "sometype", - Enabled: []bool{true}, + Enabled: &trueVal, } return a.Where(&t, model.Condition{ Field: &t.Type, @@ -976,7 +983,7 @@ func TestAPIUpdate(t *testing.T) { }) }, prepare: func(t *testLogicalSwitchPort) { - t.Tag = []int{6} + t.Tag = &six }, result: []ovsdb.Operation{ { @@ -992,12 +999,12 @@ func TestAPIUpdate(t *testing.T) { name: "select multiple by predicate change multiple field", condition: func(a API) ConditionalAPI { return a.WhereCache(func(t *testLogicalSwitchPort) bool { - return t.Enabled != nil && t.Enabled[0] == true + return t.Enabled != nil && *t.Enabled == true }) }, prepare: func(t *testLogicalSwitchPort) { t.Type = "somethingElse" - t.Tag = []int{6} + t.Tag = &six }, result: []ovsdb.Operation{ { @@ -1041,23 +1048,23 @@ func TestAPIDelete(t *testing.T) { Name: "lsp0", Type: "someType", ExternalIds: map[string]string{"foo": "bar"}, - Enabled: []bool{true}, - Tag: []int{1}, + Enabled: &trueVal, + Tag: &one, }, aUUID1: &testLogicalSwitchPort{ UUID: aUUID1, Name: "lsp1", Type: "someType", ExternalIds: map[string]string{"foo": "baz"}, - Tag: []int{1}, - Enabled: []bool{true}, + Tag: &one, + Enabled: &trueVal, }, aUUID2: &testLogicalSwitchPort{ UUID: aUUID2, Name: "lsp2", Type: "someOtherType", ExternalIds: map[string]string{"foo": "baz"}, - Tag: []int{1}, + Tag: &one, }, } testData := cache.Data{ @@ -1107,7 +1114,7 @@ func TestAPIDelete(t *testing.T) { name: "select by field equality", condition: func(a API) ConditionalAPI { t := testLogicalSwitchPort{ - Enabled: []bool{true}, + Enabled: &trueVal, } return a.Where(&t, model.Condition{ Field: &t.Type, @@ -1128,7 +1135,7 @@ func TestAPIDelete(t *testing.T) { name: "select any by field ", condition: func(a API) ConditionalAPI { t := testLogicalSwitchPort{ - Enabled: []bool{true}, + Enabled: &trueVal, } return a.Where(&t, model.Condition{ @@ -1159,7 +1166,7 @@ func TestAPIDelete(t *testing.T) { name: "select all by field ", condition: func(a API) ConditionalAPI { t := testLogicalSwitchPort{ - Enabled: []bool{true}, + Enabled: &trueVal, } return a.WhereAll(&t, model.Condition{ @@ -1188,7 +1195,7 @@ func TestAPIDelete(t *testing.T) { name: "select multiple by predicate", condition: func(a API) ConditionalAPI { return a.WhereCache(func(t *testLogicalSwitchPort) bool { - return t.Enabled != nil && t.Enabled[0] == true + return t.Enabled != nil && *t.Enabled == true }) }, result: []ovsdb.Operation{ diff --git a/client/api_test_model.go b/client/api_test_model.go index a28a6035..b90541ac 100644 --- a/client/api_test_model.go +++ b/client/api_test_model.go @@ -131,21 +131,21 @@ func (*testLogicalSwitch) Table() string { //LogicalSwitchPort struct defines an object in Logical_Switch_Port table type testLogicalSwitchPort struct { UUID string `ovsdb:"_uuid"` - Up []bool `ovsdb:"up"` - Dhcpv4Options []string `ovsdb:"dhcpv4_options"` + Up *bool `ovsdb:"up"` + Dhcpv4Options *string `ovsdb:"dhcpv4_options"` Name string `ovsdb:"name"` - DynamicAddresses []string `ovsdb:"dynamic_addresses"` - HaChassisGroup []string `ovsdb:"ha_chassis_group"` + DynamicAddresses *string `ovsdb:"dynamic_addresses"` + HaChassisGroup *string `ovsdb:"ha_chassis_group"` Options map[string]string `ovsdb:"options"` - Enabled []bool `ovsdb:"enabled"` + Enabled *bool `ovsdb:"enabled"` Addresses []string `ovsdb:"addresses"` - Dhcpv6Options []string `ovsdb:"dhcpv6_options"` - TagRequest []int `ovsdb:"tag_request"` - Tag []int `ovsdb:"tag"` + Dhcpv6Options *string `ovsdb:"dhcpv6_options"` + TagRequest *int `ovsdb:"tag_request"` + Tag *int `ovsdb:"tag"` PortSecurity []string `ovsdb:"port_security"` ExternalIds map[string]string `ovsdb:"external_ids"` Type string `ovsdb:"type"` - ParentName []string `ovsdb:"parent_name"` + ParentName *string `ovsdb:"parent_name"` } // Table returns the table name. It's part of the Model interface diff --git a/client/client_test.go b/client/client_test.go index ebd93fea..b8edbf9e 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -40,26 +40,26 @@ const ( // Bridge defines an object in Bridge table type Bridge struct { UUID string `ovsdb:"_uuid"` - AutoAttach []string `ovsdb:"auto_attach"` + AutoAttach *string `ovsdb:"auto_attach"` Controller []string `ovsdb:"controller"` - DatapathID []string `ovsdb:"datapath_id"` + DatapathID *string `ovsdb:"datapath_id"` DatapathType string `ovsdb:"datapath_type"` DatapathVersion string `ovsdb:"datapath_version"` ExternalIDs map[string]string `ovsdb:"external_ids"` - FailMode []BridgeFailMode `ovsdb:"fail_mode"` - FloodVLANs []int `ovsdb:"flood_vlans"` + FailMode *BridgeFailMode `ovsdb:"fail_mode"` + FloodVLANs [4096]int `ovsdb:"flood_vlans"` FlowTables map[int]string `ovsdb:"flow_tables"` - IPFIX []string `ovsdb:"ipfix"` + IPFIX *string `ovsdb:"ipfix"` McastSnoopingEnable bool `ovsdb:"mcast_snooping_enable"` Mirrors []string `ovsdb:"mirrors"` Name string `ovsdb:"name"` - Netflow []string `ovsdb:"netflow"` + Netflow *string `ovsdb:"netflow"` OtherConfig map[string]string `ovsdb:"other_config"` Ports []string `ovsdb:"ports"` Protocols []BridgeProtocols `ovsdb:"protocols"` RSTPEnable bool `ovsdb:"rstp_enable"` RSTPStatus map[string]string `ovsdb:"rstp_status"` - Sflow []string `ovsdb:"sflow"` + Sflow *string `ovsdb:"sflow"` Status map[string]string `ovsdb:"status"` STPEnable bool `ovsdb:"stp_enable"` } @@ -71,19 +71,19 @@ type OpenvSwitch struct { CurCfg int `ovsdb:"cur_cfg"` DatapathTypes []string `ovsdb:"datapath_types"` Datapaths map[string]string `ovsdb:"datapaths"` - DbVersion []string `ovsdb:"db_version"` + DbVersion *string `ovsdb:"db_version"` DpdkInitialized bool `ovsdb:"dpdk_initialized"` - DpdkVersion []string `ovsdb:"dpdk_version"` + DpdkVersion *string `ovsdb:"dpdk_version"` ExternalIDs map[string]string `ovsdb:"external_ids"` IfaceTypes []string `ovsdb:"iface_types"` ManagerOptions []string `ovsdb:"manager_options"` NextCfg int `ovsdb:"next_cfg"` OtherConfig map[string]string `ovsdb:"other_config"` - OVSVersion []string `ovsdb:"ovs_version"` - SSL []string `ovsdb:"ssl"` + OVSVersion *string `ovsdb:"ovs_version"` + SSL *string `ovsdb:"ssl"` Statistics map[string]string `ovsdb:"statistics"` - SystemType []string `ovsdb:"system_type"` - SystemVersion []string `ovsdb:"system_version"` + SystemType *string `ovsdb:"system_type"` + SystemVersion *string `ovsdb:"system_version"` } var defDB, _ = model.NewDBModel("Open_vSwitch", diff --git a/client/condition_test.go b/client/condition_test.go index 7c44efb2..7ce98730 100644 --- a/client/condition_test.go +++ b/client/condition_test.go @@ -16,25 +16,25 @@ func TestEqualityConditional(t *testing.T) { UUID: aUUID0, Name: "lsp0", ExternalIds: map[string]string{"foo": "bar"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, &testLogicalSwitchPort{ UUID: aUUID1, Name: "lsp1", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID2, Name: "lsp2", ExternalIds: map[string]string{"unique": "id"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID3, Name: "lsp3", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, } lspcache := map[string]model.Model{} @@ -156,25 +156,25 @@ func TestPredicateConditional(t *testing.T) { UUID: aUUID0, Name: "lsp0", ExternalIds: map[string]string{"foo": "bar"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, &testLogicalSwitchPort{ UUID: aUUID1, Name: "lsp1", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID2, Name: "lsp2", ExternalIds: map[string]string{"unique": "id"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID3, Name: "lsp3", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, } lspcache := map[string]model.Model{} @@ -214,7 +214,7 @@ func TestPredicateConditional(t *testing.T) { { name: "by random field", predicate: func(lsp *testLogicalSwitchPort) bool { - return lsp.Enabled[0] == false + return lsp.Enabled != nil && *lsp.Enabled == false }, condition: [][]ovsdb.Condition{ { @@ -230,8 +230,8 @@ func TestPredicateConditional(t *testing.T) { Value: ovsdb.UUID{GoUUID: aUUID2}, }}}, matches: map[model.Model]bool{ - &testLogicalSwitchPort{UUID: aUUID1, Enabled: []bool{true}}: false, - &testLogicalSwitchPort{UUID: aUUID1, Enabled: []bool{false}}: true, + &testLogicalSwitchPort{UUID: aUUID1, Enabled: &trueVal}: false, + &testLogicalSwitchPort{UUID: aUUID1, Enabled: &falseVal}: true, }, }, } @@ -265,25 +265,25 @@ func TestExplicitConditional(t *testing.T) { UUID: aUUID0, Name: "lsp0", ExternalIds: map[string]string{"foo": "bar"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, &testLogicalSwitchPort{ UUID: aUUID1, Name: "lsp1", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID2, Name: "lsp2", ExternalIds: map[string]string{"unique": "id"}, - Enabled: []bool{false}, + Enabled: &falseVal, }, &testLogicalSwitchPort{ UUID: aUUID3, Name: "lsp3", ExternalIds: map[string]string{"foo": "baz"}, - Enabled: []bool{true}, + Enabled: &trueVal, }, } lspcache := map[string]model.Model{} @@ -362,7 +362,7 @@ func TestExplicitConditional(t *testing.T) { { Field: &testObj.Enabled, Function: ovsdb.ConditionEqual, - Value: []bool{true}, + Value: &trueVal, }, }, result: [][]ovsdb.Condition{ @@ -370,7 +370,7 @@ func TestExplicitConditional(t *testing.T) { { Column: "enabled", Function: ovsdb.ConditionEqual, - Value: testOvsSet(t, []bool{true}), + Value: testOvsSet(t, &trueVal), }}}, }, { @@ -379,7 +379,7 @@ func TestExplicitConditional(t *testing.T) { { Field: &testObj.Enabled, Function: ovsdb.ConditionEqual, - Value: []bool{true}, + Value: &trueVal, }, { Field: &testObj.Name, @@ -392,7 +392,7 @@ func TestExplicitConditional(t *testing.T) { { Column: "enabled", Function: ovsdb.ConditionEqual, - Value: testOvsSet(t, []bool{true}), + Value: testOvsSet(t, &trueVal), }}, { { @@ -407,7 +407,7 @@ func TestExplicitConditional(t *testing.T) { { Field: &testObj.Enabled, Function: ovsdb.ConditionEqual, - Value: []bool{true}, + Value: &trueVal, }, { Field: &testObj.Name, @@ -419,7 +419,7 @@ func TestExplicitConditional(t *testing.T) { { Column: "enabled", Function: ovsdb.ConditionEqual, - Value: testOvsSet(t, []bool{true}), + Value: testOvsSet(t, &trueVal), }, { Column: "name", diff --git a/mapper/info.go b/mapper/info.go index fd1ceb1e..7268da4a 100644 --- a/mapper/info.go +++ b/mapper/info.go @@ -38,12 +38,40 @@ func (i *Info) SetField(column string, value interface{}) error { return fmt.Errorf("column %s not found in orm info", column) } fieldValue := reflect.ValueOf(i.obj).Elem().FieldByName(fieldName) - + v := reflect.ValueOf(value) if !fieldValue.Type().AssignableTo(reflect.TypeOf(value)) { - return fmt.Errorf("column %s: native value %v (%s) is not assignable to field %s (%s)", - column, value, reflect.TypeOf(value), fieldName, fieldValue.Type()) + if fieldValue.Kind() == reflect.Ptr { + if v.Kind() == reflect.Ptr && fieldValue.Type().AssignableTo(reflect.TypeOf(v.Elem())) { + v = v.Elem() + } else { + schema := i.table.Column(column) + native, err := ovsdb.OvsToNative(schema, value) + if err != nil { + return err + } + v = reflect.ValueOf(native) + } + } + if v.Type().ConvertibleTo(fieldValue.Type()) { + // handle enum + v = v.Convert(fieldValue.Type()) + } else if fieldValue.Kind() == reflect.Slice { + // handle set of enums + if !v.Type().Elem().ConvertibleTo(fieldValue.Type().Elem()) { + return fmt.Errorf("column %s: element %v (%s) is not convertible to field %s element (%s)", + column, value, reflect.TypeOf(value), fieldName, fieldValue.Type()) + } + nv := reflect.Zero(fieldValue.Type()) + for i := 0; i < v.Len(); i++ { + nv = reflect.Append(nv, v.Index(i).Convert(fieldValue.Type().Elem())) + } + v = nv + } else { + return fmt.Errorf("column %s: native value %v (%s) is not assignable or convertible to field %s (%s)", + column, value, reflect.TypeOf(value), fieldName, fieldValue.Type()) + } } - fieldValue.Set(reflect.ValueOf(value)) + fieldValue.Set(v) return nil } @@ -133,7 +161,14 @@ func NewInfo(table *ovsdb.TableSchema, obj interface{}) (*Info, error) { // Perform schema-based type checking expType := ovsdb.NativeType(column) - if expType != field.Type { + // check for slice of enums + if expType.Kind() == reflect.Slice && expType.Elem().Kind() == reflect.String { + // it's a slice of enums + } else if expType.Kind() == reflect.String && field.Type.Kind() == reflect.String { + // it's an enum + } else if expType.Kind() == reflect.Ptr && expType.Elem().Kind() == reflect.String && field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.String { + // it's a pointer to an enum + } else if expType != field.Type { return nil, &ErrMapper{ objType: objType.String(), field: field.Name, diff --git a/mapper/info_test.go b/mapper/info_test.go index b50633f3..63c5a05c 100644 --- a/mapper/info_test.go +++ b/mapper/info_test.go @@ -10,25 +10,57 @@ import ( ) var sampleTable = []byte(`{ - "columns": { + "columns": { "aString": { - "type": "string" + "type": "string" }, "aInteger": { - "type": "integer" + "type": "integer" }, "aSet": { - "type": { - "key": "string", - "max": "unlimited", - "min": 0 - } + "type": { + "key": "string", + "max": "unlimited", + "min": 0 + } }, "aMap": { - "type": { - "key": "string", - "value": "string" - } + "type": { + "key": "string", + "value": "string" + } + }, + "aEnum": { + "type": { + "key": { + "enum": [ + "set", + [ + "enum1", + "enum2", + "enum3" + ] + ], + "type": "string" + } + } + }, + "aEnumSet": { + "type": { + "key": { + "enum": [ + "set", + [ + "enum1", + "enum2", + "enum3" + ] + ], + "type": "string" + }, + "max": "unlimited", + "min": 0 + } } } }`) @@ -73,11 +105,19 @@ func TestNewMapperInfo(t *testing.T) { } func TestMapperInfoSet(t *testing.T) { + type enum string + const ( + enum1 enum = "one" + enum2 enum = "two" + enum3 enum = "three" + ) 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"` + Omap map[string]string `ovsdb:"aMap"` + Oenum enum `ovsdb:"aEnum"` + OenumSet []enum `ovsdb:"aEnumSet"` } type test struct { @@ -127,13 +167,29 @@ func TestMapperInfoSet(t *testing.T) { err: false, }, { - name: "un-assignable", + name: "unassignable", table: sampleTable, obj: &obj{}, field: []string{"foo"}, column: "aMap", err: true, }, + { + name: "enum", + table: sampleTable, + obj: &obj{}, + field: enum1, + column: "aEnum", + err: false, + }, + { + name: "enumSet", + table: sampleTable, + obj: &obj{}, + field: []enum{enum2, enum3}, + column: "aEnumSet", + err: false, + }, } for _, tt := range tests { t.Run(fmt.Sprintf("SetField_%s", tt.name), func(t *testing.T) { diff --git a/mapper/mapper_test.go b/mapper/mapper_test.go index 6bfafde6..826c62f2 100644 --- a/mapper/mapper_test.go +++ b/mapper/mapper_test.go @@ -33,7 +33,7 @@ var ( } aFloat = 42.00 - aFloatSet = []float64{ + aFloatSet = [10]float64{ 3.0, 2.0, 42.0, @@ -77,7 +77,8 @@ var testSchema = []byte(`{ "refType": "weak", "type": "uuid" }, - "min": 0 + "min": 0, + "max": "unlimited" } }, "aUUID": { @@ -187,12 +188,12 @@ func TestMapperGetData(t *testing.T) { type ormTestType struct { AString string `ovsdb:"aString"` ASet []string `ovsdb:"aSet"` - ASingleSet []string `ovsdb:"aSingleSet"` + ASingleSet *string `ovsdb:"aSingleSet"` AUUIDSet []string `ovsdb:"aUUIDSet"` AUUID string `ovsdb:"aUUID"` AIntSet []int `ovsdb:"aIntSet"` AFloat float64 `ovsdb:"aFloat"` - AFloatSet []float64 `ovsdb:"aFloatSet"` + AFloatSet [10]float64 `ovsdb:"aFloatSet"` YetAnotherStringSet []string `ovsdb:"aEmptySet"` AEnum string `ovsdb:"aEnum"` AMap map[string]string `ovsdb:"aMap"` @@ -202,7 +203,7 @@ func TestMapperGetData(t *testing.T) { var expected = ormTestType{ AString: aString, ASet: aSet, - ASingleSet: []string{aString}, + ASingleSet: &aString, AUUIDSet: aUUIDSet, AUUID: aUUID0, AIntSet: aIntSet, @@ -304,7 +305,7 @@ func TestMapperNewRow(t *testing.T) { }, { name: "aFloatSet", objInput: &struct { - MyFloatSet []float64 `ovsdb:"aFloatSet"` + MyFloatSet [10]float64 `ovsdb:"aFloatSet"` }{ MyFloatSet: aFloatSet, }, @@ -880,7 +881,8 @@ func TestMapperMutation(t *testing.T) { "set": { "type": { "key": "string", - "min": 0 + "min": 0, + "max": "unlimited" } }, "map": { diff --git a/modelgen/table.go b/modelgen/table.go index c31dd5f7..4122fd9b 100644 --- a/modelgen/table.go +++ b/modelgen/table.go @@ -51,11 +51,11 @@ func NewTableTemplate() *template.Template { {{ if index . "Enums" }} type ( {{ range index . "Enums" }} -{{ .Alias }} = {{ .Type }} +{{ .Alias }} {{ .Type }} {{- end }} ) -const ( +var ( {{ range index . "Enums" }} {{- $e := . }} {{- range .Sets }} @@ -167,6 +167,28 @@ func fieldType(tableName, columnName string, column *ovsdb.ColumnSchema, enumTyp return fmt.Sprintf("map[%s]%s", AtomicType(column.TypeObj.Key.Type), AtomicType(column.TypeObj.Value.Type)) case ovsdb.TypeSet: + // optional with max 1 element + if column.TypeObj.Min() == 0 && column.TypeObj.Max() == 1 { + if enumTypes && FieldEnum(tableName, columnName, column) != nil { + return fmt.Sprintf("*%s", enumName(tableName, columnName)) + } + return fmt.Sprintf("*%s", AtomicType(column.TypeObj.Key.Type)) + } + // required, max 1 element + if column.TypeObj.Min() == 1 && column.TypeObj.Max() == 1 { + if enumTypes && FieldEnum(tableName, columnName, column) != nil { + return enumName(tableName, columnName) + } + 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 6c9d4b29..7d2982c9 100644 --- a/modelgen/table_test.go +++ b/modelgen/table_test.go @@ -54,11 +54,11 @@ func TestNewTableTemplate(t *testing.T) { package test type ( - AtomicTableEventType = string - AtomicTableProtocol = string + AtomicTableEventType string + AtomicTableProtocol string ) -const ( +var ( AtomicTableEventTypeEmptyLbBackends AtomicTableEventType = "empty_lb_backends" AtomicTableProtocolTCP AtomicTableProtocol = "tcp" AtomicTableProtocolUDP AtomicTableProtocol = "udp" @@ -67,12 +67,12 @@ const ( // AtomicTable defines an object in atomicTable table type AtomicTable struct { - UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` - EventType AtomicTableEventType ` + "`" + `ovsdb:"event_type"` + "`" + ` - Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` - Int int ` + "`" + `ovsdb:"int"` + "`" + ` - Protocol []AtomicTableProtocol ` + "`" + `ovsdb:"protocol"` + "`" + ` - Str string ` + "`" + `ovsdb:"str"` + "`" + ` + UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` + EventType AtomicTableEventType ` + "`" + `ovsdb:"event_type"` + "`" + ` + Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` + Int int ` + "`" + `ovsdb:"int"` + "`" + ` + Protocol *AtomicTableProtocol ` + "`" + `ovsdb:"protocol"` + "`" + ` + Str string ` + "`" + `ovsdb:"str"` + "`" + ` } `, }, @@ -88,12 +88,12 @@ package test // AtomicTable defines an object in atomicTable table type AtomicTable struct { - UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` - EventType string ` + "`" + `ovsdb:"event_type"` + "`" + ` - Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` - Int int ` + "`" + `ovsdb:"int"` + "`" + ` - Protocol []string ` + "`" + `ovsdb:"protocol"` + "`" + ` - Str string ` + "`" + `ovsdb:"str"` + "`" + ` + UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` + EventType string ` + "`" + `ovsdb:"event_type"` + "`" + ` + Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` + Int int ` + "`" + `ovsdb:"int"` + "`" + ` + Protocol *string ` + "`" + `ovsdb:"protocol"` + "`" + ` + Str string ` + "`" + `ovsdb:"str"` + "`" + ` } `, }, @@ -114,11 +114,11 @@ type AtomicTable struct { package test type ( - AtomicTableEventType = string - AtomicTableProtocol = string + AtomicTableEventType string + AtomicTableProtocol string ) -const ( +var ( AtomicTableEventTypeEmptyLbBackends AtomicTableEventType = "empty_lb_backends" AtomicTableProtocolTCP AtomicTableProtocol = "tcp" AtomicTableProtocolUDP AtomicTableProtocol = "udp" @@ -127,18 +127,18 @@ const ( // AtomicTable defines an object in atomicTable table type AtomicTable struct { - UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` - EventType AtomicTableEventType ` + "`" + `ovsdb:"event_type"` + "`" + ` - Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` - Int int ` + "`" + `ovsdb:"int"` + "`" + ` - Protocol []AtomicTableProtocol ` + "`" + `ovsdb:"protocol"` + "`" + ` - Str string ` + "`" + `ovsdb:"str"` + "`" + ` + UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` + EventType AtomicTableEventType ` + "`" + `ovsdb:"event_type"` + "`" + ` + Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` + Int int ` + "`" + `ovsdb:"int"` + "`" + ` + Protocol *AtomicTableProtocol ` + "`" + `ovsdb:"protocol"` + "`" + ` + Str string ` + "`" + `ovsdb:"str"` + "`" + ` OtherUUID string OtherEventType string OtherFloat float64 OtherInt int - OtherProtocol []string + OtherProtocol *string OtherStr string } `, @@ -163,11 +163,11 @@ func {{ index . "TestName" }} () string { package test type ( - AtomicTableEventType = string - AtomicTableProtocol = string + AtomicTableEventType string + AtomicTableProtocol string ) -const ( +var ( AtomicTableEventTypeEmptyLbBackends AtomicTableEventType = "empty_lb_backends" AtomicTableProtocolTCP AtomicTableProtocol = "tcp" AtomicTableProtocolUDP AtomicTableProtocol = "udp" @@ -176,12 +176,12 @@ const ( // AtomicTable defines an object in atomicTable table type AtomicTable struct { - UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` - EventType AtomicTableEventType ` + "`" + `ovsdb:"event_type"` + "`" + ` - Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` - Int int ` + "`" + `ovsdb:"int"` + "`" + ` - Protocol []AtomicTableProtocol ` + "`" + `ovsdb:"protocol"` + "`" + ` - Str string ` + "`" + `ovsdb:"str"` + "`" + ` + UUID string ` + "`" + `ovsdb:"_uuid"` + "`" + ` + EventType AtomicTableEventType ` + "`" + `ovsdb:"event_type"` + "`" + ` + Float float64 ` + "`" + `ovsdb:"float"` + "`" + ` + Int int ` + "`" + `ovsdb:"int"` + "`" + ` + Protocol *AtomicTableProtocol ` + "`" + `ovsdb:"protocol"` + "`" + ` + Str string ` + "`" + `ovsdb:"str"` + "`" + ` } func TestFunc() string { diff --git a/ovsdb/bindings.go b/ovsdb/bindings.go index e0a714c0..46a9c670 100644 --- a/ovsdb/bindings.go +++ b/ovsdb/bindings.go @@ -20,7 +20,7 @@ type ErrWrongType struct { } func (e *ErrWrongType) Error() string { - return fmt.Sprintf("Wrong Type (%s): expected %s but got %s (%s)", + return fmt.Sprintf("Wrong Type (%s): expected %s but got %+v (%s)", e.from, e.expected, e.got, reflect.TypeOf(e.got)) } @@ -54,7 +54,7 @@ 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 +// 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 @@ -70,6 +70,18 @@ func NativeType(column *ColumnSchema) reflect.Type { return reflect.MapOf(keyType, valueType) case TypeSet: keyType := NativeTypeFromAtomic(column.TypeObj.Key.Type) + // optional type + if column.TypeObj.Min() == 0 && column.TypeObj.Max() == 1 { + return reflect.PtrTo(keyType) + } + // non-optional type with max 1 + 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)) @@ -81,6 +93,12 @@ func OvsToNativeAtomic(basicType string, ovsElem interface{}) (interface{}, erro switch basicType { case TypeReal, TypeString, TypeBoolean: naType := NativeTypeFromAtomic(basicType) + if reflect.TypeOf(ovsElem).Kind() == reflect.Ptr { + if reflect.ValueOf(ovsElem).IsNil() { + return reflect.Zero(naType).Interface(), nil + } + ovsElem = reflect.ValueOf(ovsElem).Elem().Interface() + } if reflect.TypeOf(ovsElem) != naType { return nil, NewErrWrongType("OvsToNativeAtomic", naType.String(), ovsElem) } @@ -115,30 +133,76 @@ func OvsToNative(column *ColumnSchema, ovsElem interface{}) (interface{}, error) // The inner slice is []interface{} // We need to convert it to the real type os slice var nativeSet reflect.Value + switch naType.Kind() { + 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 { + return reflect.Zero(naType).Interface(), nil + } + native, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsSet.GoSet[0]) + if err != nil { + return nil, err + } + pv := reflect.New(naType.Elem()) + pv.Elem().Set(reflect.ValueOf(native)) + return pv.Interface(), nil + default: + native, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsElem) + if err != nil { + return nil, err + } + pv := reflect.New(naType.Elem()) + 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: + switch ovsSet := ovsElem.(type) { + case OvsSet: + nativeSet = reflect.MakeSlice(naType, 0, len(ovsSet.GoSet)) + for _, v := range ovsSet.GoSet { + nv, err := OvsToNativeAtomic(column.TypeObj.Key.Type, v) + if err != nil { + return nil, err + } + nativeSet = reflect.Append(nativeSet, reflect.ValueOf(nv)) + } - // RFC says that for a set of exactly one, an atomic type an be sent - switch ovsSet := ovsElem.(type) { - case OvsSet: - nativeSet = reflect.MakeSlice(naType, 0, len(ovsSet.GoSet)) - for _, v := range ovsSet.GoSet { - nv, err := OvsToNativeAtomic(column.TypeObj.Key.Type, v) + default: + nativeSet = reflect.MakeSlice(naType, 0, 1) + nv, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsElem) if err != nil { return nil, err } + nativeSet = reflect.Append(nativeSet, reflect.ValueOf(nv)) } - + return nativeSet.Interface(), nil default: - nativeSet = reflect.MakeSlice(naType, 0, 1) - nv, err := OvsToNativeAtomic(column.TypeObj.Key.Type, ovsElem) - if err != nil { - return nil, err - } - - nativeSet = reflect.Append(nativeSet, reflect.ValueOf(nv)) + return nil, fmt.Errorf("native type was not slice, array or pointer. got %d", naType.Kind()) } - return nativeSet.Interface(), nil - case TypeMap: naType := NativeType(column) ovsMap, ok := ovsElem.(OvsMap) @@ -182,11 +246,28 @@ func NativeToOvsAtomic(basicType string, nativeElem interface{}) (interface{}, e // NativeToOvs transforms an native type to a ovs type based on the column type information func NativeToOvs(column *ColumnSchema, rawElem interface{}) (interface{}, error) { naType := NativeType(column) - - if t := reflect.TypeOf(rawElem); t != naType { + rawElemType := reflect.TypeOf(rawElem) + if column.Type == TypeEnum && rawElemType != strType && rawElemType.Kind() == reflect.String { + // cast type alias back to string + rawElem = rawElem.(string) + rawElemType = reflect.TypeOf(rawElem) + } + if column.Type == TypeSet && len(column.TypeObj.Key.Enum) > 0 && rawElemType.Elem() != strType && rawElemType.Elem().Kind() == reflect.String { + // convert a set of enums where the type is an alias, to a set of strings + tmp := []string{} + v := reflect.ValueOf(rawElem) + for i := 0; i < v.Len(); i++ { + // convert value to string first, because the String method won't panic + // but will instead return a string of which isn't what we want + vi := v.Index(i).Convert(strType) + tmp = append(tmp, vi.String()) + } + rawElem = tmp + rawElemType = reflect.TypeOf(rawElem) + } + if t := rawElemType; t != naType { return nil, NewErrWrongType("NativeToOvs", naType.String(), rawElem) } - switch column.Type { case TypeInteger, TypeReal, TypeString, TypeBoolean, TypeEnum: return rawElem, nil @@ -354,11 +435,16 @@ func isDefaultBaseValue(elem interface{}, etype ExtendedType) bool { if !value.IsValid() { return true } - + if reflect.TypeOf(elem).Kind() == reflect.Ptr { + return reflect.ValueOf(elem).IsZero() + } switch etype { case TypeUUID: return elem.(string) == "00000000-0000-0000-0000-000000000000" || elem.(string) == "" || isNamed(elem.(string)) case TypeMap, TypeSet: + if value.Kind() == reflect.Array { + return value.Len() == 0 + } return value.IsNil() || value.Len() == 0 case TypeString: return elem.(string) == "" diff --git a/ovsdb/bindings_test.go b/ovsdb/bindings_test.go index 50c90f7a..8d135f1f 100644 --- a/ovsdb/bindings_test.go +++ b/ovsdb/bindings_test.go @@ -87,6 +87,8 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { "key3": {GoUUID: aUUID2}, }) + singleStringSet, _ := NewOvsSet([]string{"foo"}) + tests := []struct { name string schema []byte @@ -164,7 +166,8 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { "refType": "weak", "type": "uuid" }, - "min": 0 + "min": 0, + "max": "unlimited" } }`), input: uss, @@ -180,7 +183,8 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { "refType": "weak", "type": "uuid" }, - "min": 0 + "min": 0, + "max": "unlimited" } }`), input: UUID{GoUUID: aUUID0}, @@ -356,6 +360,36 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { native: aUUIDMap, ovs: um, }, + { + name: "String set with min 0 max 1", + schema: []byte(`{ + "type":{ + "key": { + "type": "string" + }, + "min": 0, + "max": 1 + } + }`), + input: singleStringSet, + native: &aString, + ovs: singleStringSet, + }, + { + name: "A string with min 0 max 1", + schema: []byte(`{ + "type":{ + "key": { + "type": "string" + }, + "min": 0, + "max": 1 + } + }`), + input: aString, + native: &aString, + ovs: singleStringSet, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -364,9 +398,9 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { require.NoError(t, err) native, err := OvsToNative(&column, tt.input) - require.NoErrorf(t, err, "failed to convert %+v: %s", tt, err) + require.NoError(t, err) - require.Equalf(t, native, tt.native, + require.Equalf(t, tt.native, native, "fail to convert ovs2native. input: %v(%s). expected %v(%s). got %v (%s)", tt.input, reflect.TypeOf(tt.input), tt.native, reflect.TypeOf(tt.native), @@ -376,7 +410,7 @@ func TestOvsToNativeAndNativeToOvs(t *testing.T) { ovs, err := NativeToOvs(&column, native) require.NoErrorf(t, err, "failed to convert %s: %s", tt, err) - assert.Equalf(t, ovs, tt.ovs, + assert.Equalf(t, tt.ovs, ovs, "fail to convert native2ovs. native: %v(%s). expected %v(%s). got %v (%s)", native, reflect.TypeOf(native), tt.ovs, reflect.TypeOf(tt.ovs), diff --git a/ovsdb/notation_test.go b/ovsdb/notation_test.go index 9d8aebcf..ff183f4a 100644 --- a/ovsdb/notation_test.go +++ b/ovsdb/notation_test.go @@ -86,11 +86,10 @@ func TestValidateOvsSet(t *testing.T) { t.Error("Expected: ", expected, "Got", string(data)) } // Negative condition test - integer := 5 - _, err = NewOvsSet(&integer) + oSet, err = NewOvsSet(struct{ foo string }{}) if err == nil { t.Error("OvsSet must fail for anything other than Slices and atomic types") - t.Error("Expected: ", expected, "Got", string(data)) + t.Error("Got", oSet) } } diff --git a/ovsdb/set.go b/ovsdb/set.go index 3f42e578..60628a23 100644 --- a/ovsdb/set.go +++ b/ovsdb/set.go @@ -19,7 +19,12 @@ type OvsSet struct { // NewOvsSet creates a new OVSDB style set from a Go interface (object) func NewOvsSet(obj interface{}) (OvsSet, error) { - v := reflect.ValueOf(obj) + var v reflect.Value + if reflect.TypeOf(obj).Kind() == reflect.Ptr { + v = reflect.ValueOf(obj).Elem() + } else { + v = reflect.ValueOf(obj) + } ovsSet := make([]interface{}, 0) switch v.Kind() { case reflect.Slice, reflect.Array: @@ -31,10 +36,14 @@ func NewOvsSet(obj interface{}) (OvsSet, error) { reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.Bool: ovsSet = append(ovsSet, v.Interface()) - case reflect.ValueOf(UUID{}).Kind(): - ovsSet = append(ovsSet, v.Interface()) + case reflect.Struct: + if v.Type() == reflect.TypeOf(UUID{}) { + ovsSet = append(ovsSet, v.Interface()) + } else { + return OvsSet{}, fmt.Errorf("ovsset supports only go slice/string/numbers/uuid or pointers to those types") + } default: - return OvsSet{}, fmt.Errorf("ovsset supports only go slice/string/numbers/uuid types") + return OvsSet{}, fmt.Errorf("ovsset supports only go slice/string/numbers/uuid or pointers to those types") } return OvsSet{ovsSet}, nil } diff --git a/test/ovs/ovs_integration_test.go b/test/ovs/ovs_integration_test.go index f2583d5f..34d993ea 100644 --- a/test/ovs/ovs_integration_test.go +++ b/test/ovs/ovs_integration_test.go @@ -55,8 +55,8 @@ func (suite *OVSIntegrationSuite) SetupSuite() { suite.resource, err = suite.pool.RunWithOptions(options, hostConfig) require.NoError(suite.T(), err) - // set expiry to 30 seconds so containers are cleaned up on test panic - err = suite.resource.Expire(30) + // set expiry to 60 seconds so containers are cleaned up on test panic + err = suite.resource.Expire(60) require.NoError(suite.T(), err) // let the container start before we attempt connection @@ -104,14 +104,22 @@ func TestOVSIntegrationTestSuite(t *testing.T) { suite.Run(t, new(OVSIntegrationSuite)) } +type BridgeFailMode string + +var ( + BridgeFailModeStandalone BridgeFailMode = "standalone" + BridgeFailModeSecure BridgeFailMode = "secure" +) + // bridgeType is the simplified ORM model of the Bridge table type bridgeType struct { - UUID string `ovsdb:"_uuid"` - Name string `ovsdb:"name"` - OtherConfig map[string]string `ovsdb:"other_config"` - ExternalIds map[string]string `ovsdb:"external_ids"` - Ports []string `ovsdb:"ports"` - Status map[string]string `ovsdb:"status"` + UUID string `ovsdb:"_uuid"` + Name string `ovsdb:"name"` + OtherConfig map[string]string `ovsdb:"other_config"` + ExternalIds map[string]string `ovsdb:"external_ids"` + Ports []string `ovsdb:"ports"` + Status map[string]string `ovsdb:"status"` + BridgeFailMode *BridgeFailMode `ovsdb:"fail_mode"` } // ovsType is the simplified ORM model of the Bridge table