Skip to content

Commit 09997db

Browse files
committed
Add referential integrity integration tests
While these tests could have been purely server tests, setting them up as integration tests against both the test server and the real OVSDB server allows us to contrast the implemented behavior. Also provides coverage for how the references are updated in the database as well as for how referential integrity updates are sent back to the client. Signed-off-by: Jaime Caamaño Ruiz <[email protected]>
1 parent 1c3007d commit 09997db

File tree

3 files changed

+780
-2
lines changed

3 files changed

+780
-2
lines changed

server/server_integration_test.go

+346
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import (
1010
"testing"
1111
"time"
1212

13+
"github.com/google/uuid"
1314
"github.com/ovn-org/libovsdb/cache"
1415
"github.com/ovn-org/libovsdb/client"
1516
"github.com/ovn-org/libovsdb/database/inmemory"
1617
"github.com/ovn-org/libovsdb/model"
1718
"github.com/ovn-org/libovsdb/ovsdb"
19+
"github.com/ovn-org/libovsdb/test"
1820
"github.com/stretchr/testify/assert"
1921
"github.com/stretchr/testify/require"
2022

@@ -516,3 +518,347 @@ func TestMultipleOpsSameRow(t *testing.T) {
516518
require.Equal(t, map[string]string{"key1": "value1", "keyA": "valueA"}, br.ExternalIds)
517519
require.Nil(t, br.DatapathID)
518520
}
521+
522+
func TestReferentialIntegrity(t *testing.T) {
523+
// UUIDs to use throughout the tests
524+
ovsUUID := uuid.New().String()
525+
bridgeUUID := uuid.New().String()
526+
port1UUID := uuid.New().String()
527+
port2UUID := uuid.New().String()
528+
mirrorUUID := uuid.New().String()
529+
530+
// the test adds an additional op to initialOps to set a reference to
531+
// the bridge in OVS table
532+
// the test deletes expectModels at the end
533+
tests := []struct {
534+
name string
535+
initialOps []ovsdb.Operation
536+
testOps func(client.Client) ([]ovsdb.Operation, error)
537+
expectModels []model.Model
538+
dontExpectModels []model.Model
539+
expectErr bool
540+
}{
541+
{
542+
name: "strong reference is garbage collected",
543+
initialOps: []ovsdb.Operation{
544+
{
545+
Op: ovsdb.OperationInsert,
546+
Table: "Bridge",
547+
UUID: bridgeUUID,
548+
Row: ovsdb.Row{
549+
"name": bridgeUUID,
550+
"ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}}},
551+
"mirrors": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: mirrorUUID}}},
552+
},
553+
},
554+
{
555+
Op: ovsdb.OperationInsert,
556+
Table: "Port",
557+
UUID: port1UUID,
558+
Row: ovsdb.Row{
559+
"name": port1UUID,
560+
},
561+
},
562+
{
563+
Op: ovsdb.OperationInsert,
564+
Table: "Mirror",
565+
UUID: mirrorUUID,
566+
Row: ovsdb.Row{
567+
"name": mirrorUUID,
568+
"select_src_port": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}}},
569+
},
570+
},
571+
},
572+
testOps: func(c client.Client) ([]ovsdb.Operation, error) {
573+
// remove the mirror reference
574+
b := &test.BridgeType{UUID: bridgeUUID}
575+
return c.Where(b).Update(b, &b.Mirrors)
576+
},
577+
expectModels: []model.Model{
578+
&test.BridgeType{UUID: bridgeUUID, Name: bridgeUUID, Ports: []string{port1UUID}},
579+
&test.PortType{UUID: port1UUID, Name: port1UUID},
580+
},
581+
dontExpectModels: []model.Model{
582+
// mirror should have been garbage collected
583+
&test.MirrorType{UUID: mirrorUUID},
584+
},
585+
},
586+
{
587+
name: "adding non-root row that is not strongly reference is a noop",
588+
initialOps: []ovsdb.Operation{
589+
{
590+
Op: ovsdb.OperationInsert,
591+
Table: "Bridge",
592+
UUID: bridgeUUID,
593+
Row: ovsdb.Row{
594+
"name": bridgeUUID,
595+
},
596+
},
597+
},
598+
testOps: func(c client.Client) ([]ovsdb.Operation, error) {
599+
// add a mirror
600+
m := &test.MirrorType{UUID: mirrorUUID, Name: mirrorUUID}
601+
return c.Create(m)
602+
},
603+
expectModels: []model.Model{
604+
&test.BridgeType{UUID: bridgeUUID, Name: bridgeUUID},
605+
},
606+
dontExpectModels: []model.Model{
607+
// mirror should have not been added as is not referenced from anywhere
608+
&test.MirrorType{UUID: mirrorUUID},
609+
},
610+
},
611+
{
612+
name: "adding non-existent strong reference fails",
613+
initialOps: []ovsdb.Operation{
614+
{
615+
Op: ovsdb.OperationInsert,
616+
Table: "Bridge",
617+
UUID: bridgeUUID,
618+
Row: ovsdb.Row{
619+
"name": bridgeUUID,
620+
},
621+
},
622+
},
623+
testOps: func(c client.Client) ([]ovsdb.Operation, error) {
624+
// add a mirror
625+
b := &test.BridgeType{UUID: bridgeUUID, Mirrors: []string{mirrorUUID}}
626+
return c.Where(b).Update(b, &b.Mirrors)
627+
},
628+
expectModels: []model.Model{
629+
&test.BridgeType{UUID: bridgeUUID, Name: bridgeUUID},
630+
},
631+
expectErr: true,
632+
},
633+
{
634+
name: "weak reference is garbage collected",
635+
initialOps: []ovsdb.Operation{
636+
{
637+
Op: ovsdb.OperationInsert,
638+
Table: "Bridge",
639+
UUID: bridgeUUID,
640+
Row: ovsdb.Row{
641+
"name": bridgeUUID,
642+
"ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}, ovsdb.UUID{GoUUID: port2UUID}}},
643+
"mirrors": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: mirrorUUID}}},
644+
},
645+
},
646+
{
647+
Op: ovsdb.OperationInsert,
648+
Table: "Port",
649+
UUID: port1UUID,
650+
Row: ovsdb.Row{
651+
"name": port1UUID,
652+
},
653+
},
654+
{
655+
Op: ovsdb.OperationInsert,
656+
Table: "Port",
657+
UUID: port2UUID,
658+
Row: ovsdb.Row{
659+
"name": port2UUID,
660+
},
661+
},
662+
{
663+
Op: ovsdb.OperationInsert,
664+
Table: "Mirror",
665+
UUID: mirrorUUID,
666+
Row: ovsdb.Row{
667+
"name": mirrorUUID,
668+
"select_src_port": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}, ovsdb.UUID{GoUUID: port2UUID}}},
669+
},
670+
},
671+
},
672+
testOps: func(c client.Client) ([]ovsdb.Operation, error) {
673+
// remove port1
674+
p := &test.PortType{UUID: port1UUID}
675+
ops, err := c.Where(p).Delete()
676+
if err != nil {
677+
return nil, err
678+
}
679+
b := &test.BridgeType{UUID: bridgeUUID, Ports: []string{port2UUID}}
680+
op, err := c.Where(b).Update(b, &b.Ports)
681+
if err != nil {
682+
return nil, err
683+
}
684+
return append(ops, op...), nil
685+
},
686+
expectModels: []model.Model{
687+
&test.BridgeType{UUID: bridgeUUID, Name: bridgeUUID, Ports: []string{port2UUID}, Mirrors: []string{mirrorUUID}},
688+
&test.PortType{UUID: port2UUID, Name: port2UUID},
689+
// mirror reference to port1 should have been garbage collected
690+
&test.MirrorType{UUID: mirrorUUID, Name: mirrorUUID, SelectSrcPort: []string{port2UUID}},
691+
},
692+
dontExpectModels: []model.Model{
693+
&test.PortType{UUID: port1UUID},
694+
},
695+
},
696+
{
697+
name: "adding a weak reference to a non-existent row is a noop",
698+
initialOps: []ovsdb.Operation{
699+
{
700+
Op: ovsdb.OperationInsert,
701+
Table: "Bridge",
702+
UUID: bridgeUUID,
703+
Row: ovsdb.Row{
704+
"name": bridgeUUID,
705+
"ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}}},
706+
"mirrors": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: mirrorUUID}}},
707+
},
708+
},
709+
{
710+
Op: ovsdb.OperationInsert,
711+
Table: "Port",
712+
UUID: port1UUID,
713+
Row: ovsdb.Row{
714+
"name": port1UUID,
715+
},
716+
},
717+
{
718+
Op: ovsdb.OperationInsert,
719+
Table: "Mirror",
720+
UUID: mirrorUUID,
721+
Row: ovsdb.Row{
722+
"name": mirrorUUID,
723+
"select_src_port": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}}},
724+
},
725+
},
726+
},
727+
testOps: func(c client.Client) ([]ovsdb.Operation, error) {
728+
// add reference to non-existent port2
729+
m := &test.MirrorType{UUID: mirrorUUID, SelectSrcPort: []string{port1UUID, port2UUID}}
730+
return c.Where(m).Update(m, &m.SelectSrcPort)
731+
},
732+
expectModels: []model.Model{
733+
&test.BridgeType{UUID: bridgeUUID, Name: bridgeUUID, Ports: []string{port1UUID}, Mirrors: []string{mirrorUUID}},
734+
&test.PortType{UUID: port1UUID, Name: port1UUID},
735+
// mirror reference to port2 should have been garbage collected resulting in noop
736+
&test.MirrorType{UUID: mirrorUUID, Name: mirrorUUID, SelectSrcPort: []string{port1UUID}},
737+
},
738+
},
739+
{
740+
name: "garbage collecting a weak reference on a column lowering it below the min length fails",
741+
initialOps: []ovsdb.Operation{
742+
{
743+
Op: ovsdb.OperationInsert,
744+
Table: "Bridge",
745+
UUID: bridgeUUID,
746+
Row: ovsdb.Row{
747+
"name": bridgeUUID,
748+
"ports": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}}},
749+
"mirrors": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: mirrorUUID}}},
750+
},
751+
},
752+
{
753+
Op: ovsdb.OperationInsert,
754+
Table: "Port",
755+
UUID: port1UUID,
756+
Row: ovsdb.Row{
757+
"name": port1UUID,
758+
},
759+
},
760+
{
761+
Op: ovsdb.OperationInsert,
762+
Table: "Mirror",
763+
UUID: mirrorUUID,
764+
Row: ovsdb.Row{
765+
"name": mirrorUUID,
766+
"select_src_port": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: port1UUID}}},
767+
},
768+
},
769+
},
770+
testOps: func(c client.Client) ([]ovsdb.Operation, error) {
771+
// remove port 1
772+
return c.Where(&test.PortType{UUID: port1UUID}).Delete()
773+
},
774+
expectModels: []model.Model{
775+
&test.BridgeType{UUID: bridgeUUID, Name: bridgeUUID, Ports: []string{port1UUID}, Mirrors: []string{mirrorUUID}},
776+
&test.PortType{UUID: port1UUID, Name: port1UUID},
777+
&test.MirrorType{UUID: mirrorUUID, Name: mirrorUUID, SelectSrcPort: []string{port1UUID}},
778+
},
779+
expectErr: true,
780+
},
781+
}
782+
783+
for _, tt := range tests {
784+
t.Run(tt.name, func(t *testing.T) {
785+
c, close := buildTestServerAndClient(t)
786+
defer close()
787+
_, err := c.MonitorAll(context.Background())
788+
require.NoError(t, err)
789+
790+
// add the bridge reference to the initial ops
791+
ops := append(tt.initialOps, ovsdb.Operation{
792+
Op: ovsdb.OperationInsert,
793+
Table: "Open_vSwitch",
794+
UUID: ovsUUID,
795+
Row: ovsdb.Row{
796+
"bridges": ovsdb.OvsSet{GoSet: []interface{}{ovsdb.UUID{GoUUID: bridgeUUID}}},
797+
},
798+
})
799+
800+
results, err := c.Transact(context.Background(), ops...)
801+
require.NoError(t, err)
802+
require.Len(t, results, len(ops))
803+
804+
errors, err := ovsdb.CheckOperationResults(results, ops)
805+
require.Nil(t, errors)
806+
require.NoError(t, err)
807+
808+
ops, err = tt.testOps(c)
809+
require.NoError(t, err)
810+
811+
results, err = c.Transact(context.Background(), ops...)
812+
require.NoError(t, err)
813+
814+
errors, err = ovsdb.CheckOperationResults(results, ops)
815+
require.Nil(t, errors)
816+
if tt.expectErr {
817+
require.Error(t, err)
818+
} else {
819+
require.NoError(t, err)
820+
}
821+
822+
for _, m := range tt.expectModels {
823+
actual := model.Clone(m)
824+
err := c.Get(context.Background(), actual)
825+
require.NoError(t, err, "when expecting model %v", m)
826+
require.Equal(t, m, actual)
827+
}
828+
829+
for _, m := range tt.dontExpectModels {
830+
err := c.Get(context.Background(), m)
831+
require.ErrorIs(t, err, client.ErrNotFound, "when not expecting model %v", m)
832+
}
833+
834+
ops = []ovsdb.Operation{}
835+
for _, m := range tt.expectModels {
836+
op, err := c.Where(m).Delete()
837+
require.NoError(t, err)
838+
require.Len(t, op, 1)
839+
ops = append(ops, op...)
840+
}
841+
842+
// remove the bridge reference
843+
ops = append(ops, ovsdb.Operation{
844+
Op: ovsdb.OperationDelete,
845+
Table: "Open_vSwitch",
846+
Where: []ovsdb.Condition{
847+
{
848+
Column: "_uuid",
849+
Function: ovsdb.ConditionEqual,
850+
Value: ovsdb.UUID{GoUUID: ovsUUID},
851+
},
852+
},
853+
})
854+
855+
results, err = c.Transact(context.Background(), ops...)
856+
require.NoError(t, err)
857+
require.Len(t, results, len(ops))
858+
859+
errors, err = ovsdb.CheckOperationResults(results, ops)
860+
require.Nil(t, errors)
861+
require.NoError(t, err)
862+
})
863+
}
864+
}

0 commit comments

Comments
 (0)