diff --git a/go.mod b/go.mod index 5944e61cbd8..0a89450c031 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,13 @@ require ( github.com/Microsoft/go-winio v0.6.2 github.com/Microsoft/hcsshim v0.12.7 github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e + github.com/brianvoe/gofakeit/v7 v7.0.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 github.com/containerd/containerd v1.7.22 github.com/containernetworking/cni v1.2.3 github.com/containernetworking/plugins v1.6.0 - github.com/docker/docker v27.3.1+incompatible + github.com/docker/docker v27.4.0+incompatible github.com/emicklei/go-restful/v3 v3.12.1 github.com/evanphx/json-patch/v5 v5.9.0 github.com/go-logr/stdr v1.2.2 @@ -43,6 +44,7 @@ require ( golang.org/x/mod v0.22.0 golang.org/x/sys v0.28.0 golang.org/x/time v0.8.0 + golang.org/x/tools v0.28.0 google.golang.org/grpc v1.68.1 google.golang.org/protobuf v1.35.2 gopkg.in/k8snetworkplumbingwg/multus-cni.v4 v4.1.4 @@ -251,14 +253,13 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect gocv.io/x/gocv v0.39.0 // indirect - golang.org/x/crypto v0.29.0 // indirect - golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect - golang.org/x/net v0.31.0 // indirect + golang.org/x/crypto v0.30.0 // indirect + golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/term v0.26.0 // indirect - golang.org/x/text v0.20.0 // indirect - golang.org/x/tools v0.27.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20240812133136-8ffd90a71988 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect diff --git a/go.sum b/go.sum index 7073689b168..b0793d06cfa 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,8 @@ github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e h1:KCjb01YiNo github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e/go.mod h1:f7vw6ObmmNcyFQLhZX9eUGBJGpnwTJFDvVjqZxIxHWY= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/brianvoe/gofakeit/v7 v7.0.0 h1:y2MKKQ5qnErs2DaGg/O9MfKN0nEOaLf69lSF6ztfnCI= +github.com/brianvoe/gofakeit/v7 v7.0.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= @@ -109,8 +111,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= -github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.4.0+incompatible h1:I9z7sQ5qyzO0BfAb9IMOawRkAGxhYsidKiTMcm0DU+A= +github.com/docker/docker v27.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -749,12 +751,12 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= -golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= +golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -822,8 +824,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -844,8 +846,8 @@ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -931,8 +933,8 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -950,8 +952,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= @@ -986,8 +988,8 @@ golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= -golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/mocks/pkg/ovs/interface.go b/mocks/pkg/ovs/interface.go index 98cbc0553c9..9623d1d0a8b 100644 --- a/mocks/pkg/ovs/interface.go +++ b/mocks/pkg/ovs/interface.go @@ -2144,9 +2144,9 @@ func (m *MockLogicalRouterStaticRoute) EXPECT() *MockLogicalRouterStaticRouteMoc } // AddLogicalRouterStaticRoute mocks base method. -func (m *MockLogicalRouterStaticRoute) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, nexthops ...string) error { +func (m *MockLogicalRouterStaticRoute) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, externalIDs map[string]string, nexthops ...string) error { m.ctrl.T.Helper() - varargs := []any{lrName, routeTable, policy, ipPrefix, bfdID} + varargs := []any{lrName, routeTable, policy, ipPrefix, bfdID, externalIDs} for _, a := range nexthops { varargs = append(varargs, a) } @@ -2156,9 +2156,9 @@ func (m *MockLogicalRouterStaticRoute) AddLogicalRouterStaticRoute(lrName, route } // AddLogicalRouterStaticRoute indicates an expected call of AddLogicalRouterStaticRoute. -func (mr *MockLogicalRouterStaticRouteMockRecorder) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, bfdID any, nexthops ...any) *gomock.Call { +func (mr *MockLogicalRouterStaticRouteMockRecorder) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, bfdID, externalIDs any, nexthops ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{lrName, routeTable, policy, ipPrefix, bfdID}, nexthops...) + varargs := append([]any{lrName, routeTable, policy, ipPrefix, bfdID, externalIDs}, nexthops...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddLogicalRouterStaticRoute", reflect.TypeOf((*MockLogicalRouterStaticRoute)(nil).AddLogicalRouterStaticRoute), varargs...) } @@ -2732,9 +2732,9 @@ func (mr *MockNbClientMockRecorder) AddLogicalRouterPolicy(lrName, priority, mat } // AddLogicalRouterStaticRoute mocks base method. -func (m *MockNbClient) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, nexthops ...string) error { +func (m *MockNbClient) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, externalIDs map[string]string, nexthops ...string) error { m.ctrl.T.Helper() - varargs := []any{lrName, routeTable, policy, ipPrefix, bfdID} + varargs := []any{lrName, routeTable, policy, ipPrefix, bfdID, externalIDs} for _, a := range nexthops { varargs = append(varargs, a) } @@ -2744,9 +2744,9 @@ func (m *MockNbClient) AddLogicalRouterStaticRoute(lrName, routeTable, policy, i } // AddLogicalRouterStaticRoute indicates an expected call of AddLogicalRouterStaticRoute. -func (mr *MockNbClientMockRecorder) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, bfdID any, nexthops ...any) *gomock.Call { +func (mr *MockNbClientMockRecorder) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, bfdID, externalIDs any, nexthops ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]any{lrName, routeTable, policy, ipPrefix, bfdID}, nexthops...) + varargs := append([]any{lrName, routeTable, policy, ipPrefix, bfdID, externalIDs}, nexthops...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddLogicalRouterStaticRoute", reflect.TypeOf((*MockNbClient)(nil).AddLogicalRouterStaticRoute), varargs...) } diff --git a/pkg/apis/kubeovn/v1/condition_test.go b/pkg/apis/kubeovn/v1/condition_test.go new file mode 100644 index 00000000000..488705001e2 --- /dev/null +++ b/pkg/apis/kubeovn/v1/condition_test.go @@ -0,0 +1,172 @@ +package v1 + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + + "github.com/stretchr/testify/require" +) + +func TestSetCondition(t *testing.T) { + tests := []struct { + name string + conditions Conditions + ctype ConditionType + status corev1.ConditionStatus + reason string + message string + generation int64 + expctedLen int + }{ + { + name: "add to nil conditions", + conditions: nil, + ctype: "Foo", + status: corev1.ConditionTrue, + reason: "insert", + message: "foo", + generation: 1, + expctedLen: 1, + }, + { + name: "insert a new condition", + conditions: Conditions{{Type: "Foo", Status: corev1.ConditionTrue}}, + ctype: "Bar", + status: corev1.ConditionTrue, + reason: "insert", + message: "bar", + generation: 2, + expctedLen: 2, + }, + { + name: "update an existing condition", + conditions: Conditions{{Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1}}, + ctype: "Foo", + status: corev1.ConditionFalse, + reason: "update", + message: "bar", + generation: 2, + expctedLen: 1, + }, + { + name: "no op", + conditions: Conditions{{Type: "Foo", Status: corev1.ConditionTrue, Reason: "noop", Message: "foo", ObservedGeneration: 1}}, + ctype: "Foo", + status: corev1.ConditionTrue, + reason: "noop", + message: "foo", + generation: 1, + expctedLen: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.conditions.SetCondition(tt.ctype, tt.status, tt.reason, tt.message, 1) + require.Len(t, tt.conditions, tt.expctedLen) + }) + } +} + +func TestRemoveCondition(t *testing.T) { + tests := []struct { + name string + conditions Conditions + ctype ConditionType + expctedLen int + }{ + { + name: "remove from a nil conditions", + conditions: nil, + ctype: "Foo", + expctedLen: 0, + }, + { + name: "remove from an empty conditions", + conditions: Conditions{}, + ctype: "Foo", + expctedLen: 0, + }, + { + name: "remove an existing condition", + conditions: Conditions{{ + Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1, + }, { + Type: "Bar", Status: corev1.ConditionFalse, ObservedGeneration: 2, + }}, + ctype: "Foo", + expctedLen: 1, + }, + { + name: "remove the only condition", + conditions: Conditions{{Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1}}, + ctype: "Foo", + expctedLen: 0, + }, + { + name: "remove a non-existent condition", + conditions: Conditions{{Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1}}, + ctype: "Bar", + expctedLen: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.conditions.RemoveCondition(tt.ctype) + require.Len(t, tt.conditions, tt.expctedLen) + }) + } +} + +func TestGetCondition(t *testing.T) { + tests := []struct { + name string + conditions Conditions + ctype ConditionType + expcted *Condition + }{ + { + name: "get from a nil conditions", + conditions: nil, + ctype: "Foo", + expcted: nil, + }, + { + name: "get from an empty conditions", + conditions: Conditions{}, + ctype: "Foo", + expcted: nil, + }, + { + name: "get an existing condition", + conditions: Conditions{{ + Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1, + }, { + Type: "Bar", Status: corev1.ConditionFalse, ObservedGeneration: 2, + }}, + ctype: "Foo", + expcted: &Condition{Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1}, + }, + { + name: "get the only condition", + conditions: Conditions{{Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1}}, + ctype: "Foo", + expcted: &Condition{Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1}, + }, + { + name: "get a non-existent condition", + conditions: Conditions{{Type: "Foo", Status: corev1.ConditionTrue, ObservedGeneration: 1}}, + ctype: "Bar", + expcted: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := tt.conditions.GetCondition(tt.ctype) + require.Equal(t, tt.expcted, c) + }) + } +} diff --git a/pkg/apis/kubeovn/v1/register_test.go b/pkg/apis/kubeovn/v1/register_test.go new file mode 100644 index 00000000000..fe95ade154b --- /dev/null +++ b/pkg/apis/kubeovn/v1/register_test.go @@ -0,0 +1,102 @@ +package v1 + +import ( + "go/types" + "path" + "reflect" + "runtime" + "strings" + "testing" + + "golang.org/x/tools/go/packages" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/utils/set" + + "github.com/stretchr/testify/require" + + "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn" +) + +func TestKind(t *testing.T) { + gk := Kind("foo") + require.Equal(t, schema.GroupKind{Group: kubeovn.GroupName, Kind: "foo"}, gk) +} + +func TestResource(t *testing.T) { + gr := Resource("foo") + require.Equal(t, schema.GroupResource{Group: kubeovn.GroupName, Resource: "foo"}, gr) +} + +func getPackagePath(t *testing.T) string { + t.Helper() + + pc, _, _, _ := runtime.Caller(1) + funcName := runtime.FuncForPC(pc).Name() + base := path.Base(funcName) + index := strings.LastIndexByte(base, '.') + require.Greater(t, index, 0) + + return path.Join(path.Dir(funcName), base[:index]) +} + +func TestAddResources(t *testing.T) { + pkgPath := getPackagePath(t) + + config := &packages.Config{ + Mode: packages.NeedTypes | packages.NeedTypesInfo, + } + pkgs, err := packages.Load(config, pkgPath) + require.NoError(t, err, "failed to load package %q", pkgPath) + require.Len(t, pkgs, 1, "expected exactly one package, got %d", len(pkgs)) + + scheme := k8sruntime.NewScheme() + err = addKnownTypes(scheme) + require.NoError(t, err) + + addedTypes := set.New[string]() + for name, vt := range scheme.KnownTypes(SchemeGroupVersion) { + if vt.PkgPath() != pkgPath { + continue + } + require.Implements(t, (*k8sruntime.Object)(nil), reflect.New(vt).Interface()) + addedTypes.Insert(name) + } + + scope := pkgs[0].Types.Scope() + require.NotNil(t, scope) + typeMetaType := reflect.TypeFor[metav1.TypeMeta]() + objMetaType := reflect.TypeFor[metav1.ObjectMeta]() + listMetaType := reflect.TypeFor[metav1.ListMeta]() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + if !obj.Exported() { + continue + } + + st, ok := obj.Type().Underlying().(*types.Struct) + if !ok { + continue + } + + var hasTypeMeta, hasObjMeta, hasListMeta bool + for i := 0; i < st.NumFields(); i++ { + v := st.Field(i) + if !v.Embedded() || !v.Exported() { + continue + } + switch v.Name() { + case typeMetaType.Name(): + hasTypeMeta = true + case objMetaType.Name(): + hasObjMeta = true + case listMetaType.Name(): + hasListMeta = true + } + } + if hasTypeMeta && (hasObjMeta || hasListMeta) { + require.True(t, addedTypes.Has(name), "type %q not registered", name) + } + } +} diff --git a/pkg/apis/kubeovn/v1/zz_generated.deepcopy_test.go b/pkg/apis/kubeovn/v1/zz_generated.deepcopy_test.go new file mode 100644 index 00000000000..fd911b60351 --- /dev/null +++ b/pkg/apis/kubeovn/v1/zz_generated.deepcopy_test.go @@ -0,0 +1,80 @@ +package v1 + +import ( + "testing" + + runtime "k8s.io/apimachinery/pkg/runtime" + + "github.com/brianvoe/gofakeit/v7" + "github.com/stretchr/testify/require" +) + +type DeepCopy[T any] interface { + DeepCopy() T +} + +type DeepCopyObject[T any] interface { + DeepCopy[T] + runtime.Object + DeepCopyObject() runtime.Object +} + +func deepCopyTestHelper[T any](t *testing.T, in DeepCopy[T]) { + t.Helper() + require.Equal(t, in, in.DeepCopy()) +} + +func deepCopyObjectTestHelper[T any](t *testing.T, in DeepCopyObject[T]) { + t.Helper() + + err := gofakeit.Struct(in) + require.NoError(t, err) + + deepCopyTestHelper(t, in) + require.Equal(t, in.(runtime.Object), in.DeepCopyObject()) +} + +func TestDeepCopyObject(t *testing.T) { + deepCopyObjectTestHelper(t, &IP{}) + deepCopyObjectTestHelper(t, &IPList{}) + deepCopyObjectTestHelper(t, &IPPool{}) + deepCopyObjectTestHelper(t, &IPPoolList{}) + deepCopyObjectTestHelper(t, &IptablesDnatRule{}) + deepCopyObjectTestHelper(t, &IptablesDnatRuleList{}) + deepCopyObjectTestHelper(t, &IptablesEIP{}) + deepCopyObjectTestHelper(t, &IptablesEIPList{}) + deepCopyObjectTestHelper(t, &IptablesFIPRule{}) + deepCopyObjectTestHelper(t, &IptablesFIPRuleList{}) + deepCopyObjectTestHelper(t, &IptablesSnatRule{}) + deepCopyObjectTestHelper(t, &IptablesSnatRuleList{}) + deepCopyObjectTestHelper(t, &OvnDnatRule{}) + deepCopyObjectTestHelper(t, &OvnDnatRuleList{}) + deepCopyObjectTestHelper(t, &OvnEip{}) + deepCopyObjectTestHelper(t, &OvnEipList{}) + deepCopyObjectTestHelper(t, &OvnFip{}) + deepCopyObjectTestHelper(t, &OvnFipList{}) + deepCopyObjectTestHelper(t, &OvnSnatRule{}) + deepCopyObjectTestHelper(t, &OvnSnatRuleList{}) + deepCopyObjectTestHelper(t, &ProviderNetwork{}) + deepCopyObjectTestHelper(t, &ProviderNetworkList{}) + deepCopyObjectTestHelper(t, &QoSPolicy{}) + deepCopyObjectTestHelper(t, &QoSPolicyList{}) + deepCopyObjectTestHelper(t, &SecurityGroup{}) + deepCopyObjectTestHelper(t, &SecurityGroupList{}) + deepCopyObjectTestHelper(t, &Subnet{}) + deepCopyObjectTestHelper(t, &SubnetList{}) + deepCopyObjectTestHelper(t, &SwitchLBRule{}) + deepCopyObjectTestHelper(t, &SwitchLBRuleList{}) + deepCopyObjectTestHelper(t, &Vip{}) + deepCopyObjectTestHelper(t, &VipList{}) + deepCopyObjectTestHelper(t, &Vlan{}) + deepCopyObjectTestHelper(t, &VlanList{}) + deepCopyObjectTestHelper(t, &Vpc{}) + deepCopyObjectTestHelper(t, &VpcList{}) + deepCopyObjectTestHelper(t, &VpcDns{}) + deepCopyObjectTestHelper(t, &VpcDnsList{}) + deepCopyObjectTestHelper(t, &VpcEgressGateway{}) + deepCopyObjectTestHelper(t, &VpcEgressGatewayList{}) + deepCopyObjectTestHelper(t, &VpcNatGateway{}) + deepCopyObjectTestHelper(t, &VpcNatGatewayList{}) +} diff --git a/pkg/controller/vpc.go b/pkg/controller/vpc.go index fbe72de33bc..668b4c14420 100644 --- a/pkg/controller/vpc.go +++ b/pkg/controller/vpc.go @@ -434,7 +434,7 @@ func (c *Controller) handleAddOrUpdateVpc(key string) error { if item.BfdID != "" { klog.Infof("vpc %s add static ecmp route: %+v", vpc.Name, item) if err = c.OVNNbClient.AddLogicalRouterStaticRoute( - vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, &item.BfdID, item.NextHopIP, + vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, &item.BfdID, nil, item.NextHopIP, ); err != nil { klog.Errorf("failed to add bfd static route to vpc %s , %v", vpc.Name, err) return err @@ -442,7 +442,7 @@ func (c *Controller) handleAddOrUpdateVpc(key string) error { } else { klog.Infof("vpc %s add static route: %+v", vpc.Name, item) if err = c.OVNNbClient.AddLogicalRouterStaticRoute( - vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, nil, item.NextHopIP, + vpc.Name, item.RouteTable, convertPolicy(item.Policy), item.CIDR, nil, nil, item.NextHopIP, ); err != nil { klog.Errorf("failed to add normal static route to vpc %s , %v", vpc.Name, err) return err @@ -805,7 +805,7 @@ func (c *Controller) addStaticRouteToVpc(name string, route *kubeovnv1.StaticRou if route.BfdID != "" { klog.Infof("vpc %s add static ecmp route: %+v", name, route) if err := c.OVNNbClient.AddLogicalRouterStaticRoute( - name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, &route.BfdID, route.NextHopIP, + name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, &route.BfdID, nil, route.NextHopIP, ); err != nil { klog.Errorf("failed to add bfd static route to vpc %s , %v", name, err) return err @@ -813,7 +813,7 @@ func (c *Controller) addStaticRouteToVpc(name string, route *kubeovnv1.StaticRou } else { klog.Infof("vpc %s add static route: %+v", name, route) if err := c.OVNNbClient.AddLogicalRouterStaticRoute( - name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, nil, route.NextHopIP, + name, route.RouteTable, convertPolicy(route.Policy), route.CIDR, nil, nil, route.NextHopIP, ); err != nil { klog.Errorf("failed to add normal static route to vpc %s , %v", name, err) return err diff --git a/pkg/controller/vpc_egress_gateway.go b/pkg/controller/vpc_egress_gateway.go index a29616edc19..a6538d27856 100644 --- a/pkg/controller/vpc_egress_gateway.go +++ b/pkg/controller/vpc_egress_gateway.go @@ -627,7 +627,7 @@ func (c *Controller) reconcileVpcEgressGatewayOVNRoutes(gw *kubeovnv1.VpcEgressG } for nextHop, sources := range expected { for _, src := range sources.UnsortedList() { - if err = c.OVNNbClient.AddLogicalRouterStaticRoute(lrName, util.MainRouteTable, ovnnb.LogicalRouterStaticRoutePolicySrcIP, src, bfdMap[nextHop], nextHop); err != nil { + if err = c.OVNNbClient.AddLogicalRouterStaticRoute(lrName, util.MainRouteTable, ovnnb.LogicalRouterStaticRoutePolicySrcIP, src, bfdMap[nextHop], externalIDs, nextHop); err != nil { klog.Error(err) return err } diff --git a/pkg/ovs/interface.go b/pkg/ovs/interface.go index 67a230b9a16..d07636d4069 100644 --- a/pkg/ovs/interface.go +++ b/pkg/ovs/interface.go @@ -176,7 +176,7 @@ type AddressSet interface { } type LogicalRouterStaticRoute interface { - AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, nexthops ...string) error + AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, externalIDs map[string]string, nexthops ...string) error UpdateLogicalRouterStaticRoute(route *ovnnb.LogicalRouterStaticRoute, fields ...interface{}) error ClearLogicalRouterStaticRoute(lrName string) error DeleteLogicalRouterStaticRoute(lrName string, routeTable, policy *string, ipPrefix, nextHop string) error diff --git a/pkg/ovs/ovn-nb-bfd_test.go b/pkg/ovs/ovn-nb-bfd_test.go index 1642a1e0237..7cf975915dd 100644 --- a/pkg/ovs/ovn-nb-bfd_test.go +++ b/pkg/ovs/ovn-nb-bfd_test.go @@ -3,10 +3,12 @@ package ovs import ( "testing" - "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb" - + "github.com/google/uuid" "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/require" + + "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb" ) func (suite *OvnClientTestSuite) testCreateBFD() { @@ -111,6 +113,55 @@ func (suite *OvnClientTestSuite) testListBFD() { }) } +func (suite *OvnClientTestSuite) testFindBFD() { + t := suite.T() + t.Parallel() + + nbClient := suite.ovnNBClient + failedNbClient := suite.failedOvnNBClient + lrpName := "test-find-bfd" + dstIP1 := "192.168.124.101" + dstIP2 := "192.168.124.102" + minRx1, minTx1, detectMult1 := 101, 102, 19 + minRx2, minTx2, detectMult2 := 201, 202, 29 + + bfd1, err := nbClient.CreateBFD(lrpName, dstIP1, minRx1, minTx1, detectMult1, map[string]string{"k1": "v1", "k2": "v2"}) + require.NoError(t, err) + + bfd2, err := nbClient.CreateBFD(lrpName, dstIP2, minRx2, minTx2, detectMult2, map[string]string{"k2": "v2"}) + require.NoError(t, err) + + t.Run("find BFD", func(t *testing.T) { + bfds, err := nbClient.FindBFD(map[string]string{"k1": "v1"}) + require.NoError(t, err) + require.Len(t, bfds, 1) + require.Equal(t, bfd1.UUID, bfds[0].UUID) + }) + + t.Run("find multiple BFDs", func(t *testing.T) { + bfds, err := nbClient.FindBFD(map[string]string{"k2": "v2"}) + require.NoError(t, err) + require.Len(t, bfds, 2) + require.ElementsMatch(t, []string{bfds[0].UUID, bfds[1].UUID}, []string{bfd1.UUID, bfd2.UUID}) + }) + + t.Run("find non-existent BFD", func(t *testing.T) { + t.Parallel() + + bfds, err := nbClient.FindBFD(map[string]string{"k3": "v3"}) + require.NoError(t, err) + require.Empty(t, bfds) + }) + + t.Run("closed server find non-existent BFD", func(t *testing.T) { + t.Parallel() + + bfds, err := failedNbClient.FindBFD(map[string]string{"k1": "v1"}) + require.NoError(t, err) + require.Empty(t, bfds) + }) +} + func (suite *OvnClientTestSuite) testDeleteBFD() { t := suite.T() t.Parallel() @@ -118,6 +169,56 @@ func (suite *OvnClientTestSuite) testDeleteBFD() { nbClient := suite.ovnNBClient failedNbClient := suite.failedOvnNBClient lrpName := "test-del-bfd" + dstIP1 := "192.168.124.103" + dstIP2 := "192.168.124.104" + minRx1, minTx1, detectMult1 := 101, 102, 19 + minRx2, minTx2, detectMult2 := 201, 202, 29 + + bfd1, err := nbClient.CreateBFD(lrpName, dstIP1, minRx1, minTx1, detectMult1, nil) + require.NoError(t, err) + + bfd2, err := nbClient.CreateBFD(lrpName, dstIP2, minRx2, minTx2, detectMult2, nil) + require.NoError(t, err) + + t.Run("delete BFD", func(t *testing.T) { + err = nbClient.DeleteBFD(bfd1.UUID) + require.NoError(t, err) + + bfdList, err := nbClient.ListBFDs(lrpName, dstIP1) + require.NoError(t, err) + require.Len(t, bfdList, 0) + + bfdList, err = nbClient.ListBFDs(lrpName, dstIP2) + require.NoError(t, err) + require.Len(t, bfdList, 1) + require.Equal(t, bfd2.UUID, bfdList[0].UUID) + }) + + t.Run("delete non-existent BFD", func(t *testing.T) { + t.Parallel() + + err := nbClient.DeleteBFD(uuid.New().String()) + require.NoError(t, err) + }) + + t.Run("closed server delete non-existent BFD", func(t *testing.T) { + t.Parallel() + + _, err := failedNbClient.CreateBFD(lrpName, dstIP1, minRx1, minTx1, detectMult1, nil) + require.Error(t, err) + err = failedNbClient.DeleteBFD(uuid.New().String()) + // cache db should be empty + require.NoError(t, err) + }) +} + +func (suite *OvnClientTestSuite) testDeleteBFDByDstIP() { + t := suite.T() + t.Parallel() + + nbClient := suite.ovnNBClient + failedNbClient := suite.failedOvnNBClient + lrpName := "test-del-bfd-by-dst-ip" dstIP1 := "192.168.124.4" dstIP2 := "192.168.124.5" minRx1, minTx1, detectMult1 := 101, 102, 19 @@ -129,7 +230,7 @@ func (suite *OvnClientTestSuite) testDeleteBFD() { bfd2, err := nbClient.CreateBFD(lrpName, dstIP2, minRx2, minTx2, detectMult2, nil) require.NoError(t, err) - t.Run("delete BFD", func(t *testing.T) { + t.Run("delete BFD by dst ip", func(t *testing.T) { err = nbClient.DeleteBFDByDstIP(lrpName, dstIP1) require.NoError(t, err) @@ -143,7 +244,7 @@ func (suite *OvnClientTestSuite) testDeleteBFD() { require.Equal(t, bfd2.UUID, bfdList[0].UUID) }) - t.Run("delete multiple BFDs", func(t *testing.T) { + t.Run("delete multiple BFDs by dst ip", func(t *testing.T) { err = nbClient.DeleteBFDByDstIP(lrpName, "") require.NoError(t, err) @@ -152,14 +253,14 @@ func (suite *OvnClientTestSuite) testDeleteBFD() { require.Len(t, bfdList, 0) }) - t.Run("delete non-existent BFD", func(t *testing.T) { + t.Run("delete non-existent BFD by dst ip", func(t *testing.T) { t.Parallel() err := nbClient.DeleteBFDByDstIP(lrpName, "192.168.124.17") require.NoError(t, err) }) - t.Run("closed server delete non-existent BFD", func(t *testing.T) { + t.Run("closed server delete non-existent BFD by dst ip", func(t *testing.T) { t.Parallel() _, err := failedNbClient.CreateBFD(lrpName, dstIP1, minRx1, minTx1, detectMult1, nil) diff --git a/pkg/ovs/ovn-nb-logical_router_policy_test.go b/pkg/ovs/ovn-nb-logical_router_policy_test.go index e404a1a4858..8ec011e0e3b 100644 --- a/pkg/ovs/ovn-nb-logical_router_policy_test.go +++ b/pkg/ovs/ovn-nb-logical_router_policy_test.go @@ -528,6 +528,41 @@ func (suite *OvnClientTestSuite) testNewLogicalRouterPolicy() { require.Equal(t, expect, policy) } +func (suite *OvnClientTestSuite) testUpdateLogicalRouterPolicy() { + t := suite.T() + t.Parallel() + + nbClient := suite.ovnNBClient + lrName := "test-update-lr-policy-lr" + priority := 10000 + match := "ip4.dst == 1.1.1.1" + action := ovnnb.LogicalRouterPolicyActionAllow + + err := nbClient.CreateLogicalRouter(lrName) + require.NoError(t, err) + + err = nbClient.AddLogicalRouterPolicy(lrName, priority, match, action, nil, nil, nil) + require.NoError(t, err) + + policies, err := nbClient.GetLogicalRouterPolicy(lrName, priority, match, true) + require.NoError(t, err) + require.Len(t, policies, 1) + require.NotNil(t, policies[0]) + + policy := policies[0] + policy.Action = ovnnb.LogicalRouterPolicyActionDrop + err = nbClient.UpdateLogicalRouterPolicy(policy, &policy.Action) + require.NoError(t, err) + + policies, err = nbClient.GetLogicalRouterPolicy(lrName, priority, match, true) + require.NoError(t, err) + require.Len(t, policies, 1) + require.NotNil(t, policies[0]) + require.Equal(t, policy.UUID, policies[0].UUID) + require.Equal(t, ovnnb.LogicalRouterPolicyActionDrop, policies[0].Action) + require.Equal(t, policy, policies[0]) +} + func (suite *OvnClientTestSuite) testPolicyFilter() { t := suite.T() t.Parallel() diff --git a/pkg/ovs/ovn-nb-logical_router_route.go b/pkg/ovs/ovn-nb-logical_router_route.go index 6138c1ce89a..0e9d1bd0721 100644 --- a/pkg/ovs/ovn-nb-logical_router_route.go +++ b/pkg/ovs/ovn-nb-logical_router_route.go @@ -70,7 +70,7 @@ func (c *OVNNbClient) CreateLogicalRouterStaticRoutes(lrName string, routes ...* } // AddLogicalRouterStaticRoute add a logical router static route -func (c *OVNNbClient) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, nexthops ...string) error { +func (c *OVNNbClient) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix string, bfdID *string, externalIDs map[string]string, nexthops ...string) error { if len(policy) == 0 { policy = ovnnb.LogicalRouterStaticRoutePolicyDstIP } @@ -96,7 +96,7 @@ func (c *OVNNbClient) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ip var toAdd []*ovnnb.LogicalRouterStaticRoute for _, nexthop := range nexthops { if !existing.Has(nexthop) { - route, err := c.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, bfdID) + route, err := c.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, bfdID, externalIDs) if err != nil { klog.Error(err) return err @@ -104,7 +104,9 @@ func (c *OVNNbClient) AddLogicalRouterStaticRoute(lrName, routeTable, policy, ip toAdd = append(toAdd, route) } } - klog.Infof("logical router %s del static routes: %v", lrName, toDel) + if len(toDel) != 0 { + klog.Infof("logical router %s del static routes: %v", lrName, toDel) + } ops, err := c.LogicalRouterUpdateStaticRouteOp(lrName, toDel, ovsdb.MutateOperationDelete) if err != nil { klog.Error(err) @@ -366,7 +368,7 @@ func (c *OVNNbClient) LogicalRouterStaticRouteExists(lrName, routeTable, policy, } // newLogicalRouterStaticRoute return logical router static route with basic information -func (c *OVNNbClient) newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop string, bfdID *string, options ...func(route *ovnnb.LogicalRouterStaticRoute)) (*ovnnb.LogicalRouterStaticRoute, error) { +func (c *OVNNbClient) newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop string, bfdID *string, externalIDs map[string]string, options ...func(route *ovnnb.LogicalRouterStaticRoute)) (*ovnnb.LogicalRouterStaticRoute, error) { if len(lrName) == 0 { return nil, errors.New("the logical router name is required") } @@ -387,11 +389,12 @@ func (c *OVNNbClient) newLogicalRouterStaticRoute(lrName, routeTable, policy, ip } route := &ovnnb.LogicalRouterStaticRoute{ - UUID: ovsclient.NamedUUID(), - Policy: &policy, - IPPrefix: ipPrefix, - Nexthop: nexthop, - RouteTable: routeTable, + UUID: ovsclient.NamedUUID(), + Policy: &policy, + IPPrefix: ipPrefix, + Nexthop: nexthop, + RouteTable: routeTable, + ExternalIDs: externalIDs, } for _, option := range options { option(route) diff --git a/pkg/ovs/ovn-nb-logical_router_route_test.go b/pkg/ovs/ovn-nb-logical_router_route_test.go index 505b1e0b4d3..cb78fb4f187 100644 --- a/pkg/ovs/ovn-nb-logical_router_route_test.go +++ b/pkg/ovs/ovn-nb-logical_router_route_test.go @@ -26,7 +26,7 @@ func (suite *OvnClientTestSuite) testCreateLogicalRouterStaticRoutes() { require.NoError(t, err) for i, ipPrefix := range ipPrefixes { - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthops[i], nil) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthops[i], nil, nil) require.NoError(t, err) routes = append(routes, route) @@ -75,7 +75,7 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { t.Run("create route", func(t *testing.T) { for i := range ipPrefixes { - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefixes[i], nil, nexthops[i]) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefixes[i], nil, nil, nexthops[i]) require.NoError(t, err) } @@ -92,7 +92,7 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { t.Run("create route for non-exist logical router", func(t *testing.T) { for i := range ipPrefixes { - err = nbClient.AddLogicalRouterStaticRoute("non-exist-lrName", routeTable, "", ipPrefixes[i], nil, nexthops[i]) + err = nbClient.AddLogicalRouterStaticRoute("non-exist-lrName", routeTable, "", ipPrefixes[i], nil, nil, nexthops[i]) require.ErrorContains(t, err, "not found logical router") } }) @@ -100,7 +100,7 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { t.Run("update route", func(t *testing.T) { updatedNexthops := [...]string{"192.168.30.254", "fd00:100:64::fe"} for i := range ipPrefixes { - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefixes[i], nil, updatedNexthops[i]) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefixes[i], nil, nil, updatedNexthops[i]) require.NoError(t, err) } @@ -124,7 +124,7 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { nexthops := []string{"192.168.50.1", "192.168.60.1"} t.Run("create route", func(t *testing.T) { - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthops...) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthops...) require.NoError(t, err) lr, err := nbClient.GetLogicalRouter(lrName, false) @@ -138,7 +138,7 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { }) t.Run("update route", func(t *testing.T) { - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthops...) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthops...) require.NoError(t, err) }) }) @@ -148,7 +148,7 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { ipPrefix := "192.168.40.0/24" nexthops := []string{"192.168.50.1", "192.168.60.1"} - err = failedNbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthops...) + err = failedNbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthops...) require.Error(t, err) }) @@ -160,14 +160,14 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { existingBFDID := "test-existing-bfd-id" newBFDID := "test-new-bfd-id" - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, &existingBFDID, nexthops...) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, &existingBFDID, nil, nexthops...) require.NoError(t, err) initialRoutes, err := nbClient.ListLogicalRouterStaticRoutes(lrName, &routeTable, &policy, ipPrefix, nil) require.NoError(t, err) require.Len(t, initialRoutes, 1) - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, &newBFDID, nexthops...) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, &newBFDID, nil, nexthops...) require.NoError(t, err) finalRoutes, err := nbClient.ListLogicalRouterStaticRoutes(lrName, &routeTable, &policy, ipPrefix, nil) @@ -176,6 +176,115 @@ func (suite *OvnClientTestSuite) testAddLogicalRouterStaticRoute() { }) } +func (suite *OvnClientTestSuite) testDeleteLogicalRouterStaticRouteByUUID() { + t := suite.T() + t.Parallel() + + nbClient := suite.ovnNBClient + lrName := "test-del-lr-route-by-uuid" + routeTable := util.MainRouteTable + policy := ovnnb.LogicalRouterStaticRoutePolicyDstIP + ipPrefix := "1.1.1.0/24" + nexthop := "192.168.0.1" + + err := nbClient.CreateLogicalRouter(lrName) + require.NoError(t, err) + + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthop) + require.NoError(t, err) + + route, err := nbClient.GetLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, true) + require.NoError(t, err) + require.NotNil(t, route) + + err = nbClient.DeleteLogicalRouterStaticRouteByUUID(lrName, route.UUID) + require.NoError(t, err) + + route, err = nbClient.GetLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, true) + require.NoError(t, err) + require.Nil(t, route) +} + +func (suite *OvnClientTestSuite) testDeleteLogicalRouterStaticRouteByExternalIDs() { + t := suite.T() + t.Parallel() + + nbClient := suite.ovnNBClient + lrName := "test-del-lr-route-by-ext-ids" + routeTable := util.MainRouteTable + policy := ovnnb.LogicalRouterStaticRoutePolicyDstIP + nexthop := "192.168.0.1" + + err := nbClient.CreateLogicalRouter(lrName) + require.NoError(t, err) + + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, "1.1.0.0/24", nil, map[string]string{"k1": "v1"}, nexthop) + require.NoError(t, err) + + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, "1.1.1.0/24", nil, map[string]string{"k1": "v1", "k2": "v2"}, nexthop) + require.NoError(t, err) + + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, "1.1.2.0/24", nil, map[string]string{"k1": "v1", "k3": "v3"}, nexthop) + require.NoError(t, err) + + routes, err := nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k1": "v1"}) + require.NoError(t, err) + require.Len(t, routes, 3) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k2": "v2"}) + require.NoError(t, err) + require.Len(t, routes, 1) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k3": "v3"}) + require.NoError(t, err) + require.Len(t, routes, 1) + + err = nbClient.DeleteLogicalRouterStaticRouteByExternalIDs(lrName, map[string]string{"foo": "bar"}) + require.NoError(t, err) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k1": "v1"}) + require.NoError(t, err) + require.Len(t, routes, 3) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k2": "v2"}) + require.NoError(t, err) + require.Len(t, routes, 1) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k3": "v3"}) + require.NoError(t, err) + require.Len(t, routes, 1) + + err = nbClient.DeleteLogicalRouterStaticRouteByExternalIDs(lrName, map[string]string{"k2": "v2"}) + require.NoError(t, err) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k1": "v1"}) + require.NoError(t, err) + require.Len(t, routes, 2) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k2": "v2"}) + require.NoError(t, err) + require.Len(t, routes, 0) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k3": "v3"}) + require.NoError(t, err) + require.Len(t, routes, 1) + + err = nbClient.DeleteLogicalRouterStaticRouteByExternalIDs(lrName, map[string]string{"k1": "v1"}) + require.NoError(t, err) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k1": "v1"}) + require.NoError(t, err) + require.Len(t, routes, 0) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k2": "v2"}) + require.NoError(t, err) + require.Len(t, routes, 0) + + routes, err = nbClient.ListLogicalRouterStaticRoutes(lrName, nil, nil, "", map[string]string{"k3": "v3"}) + require.NoError(t, err) + require.Len(t, routes, 0) +} + func (suite *OvnClientTestSuite) testDeleteLogicalRouterStaticRoute() { t := suite.T() t.Parallel() @@ -192,7 +301,7 @@ func (suite *OvnClientTestSuite) testDeleteLogicalRouterStaticRoute() { ipPrefix := "192.168.30.0/24" nexthop := "192.168.30.1" - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthop) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthop) require.NoError(t, err) lr, err := nbClient.GetLogicalRouter(lrName, false) @@ -231,7 +340,7 @@ func (suite *OvnClientTestSuite) testDeleteLogicalRouterStaticRoute() { ipPrefix := "192.168.40.0/24" nexthops := []string{"192.168.50.1", "192.168.60.1"} - err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthops...) + err = nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthops...) require.NoError(t, err) lr, err := nbClient.GetLogicalRouter(lrName, false) @@ -286,7 +395,7 @@ func (suite *OvnClientTestSuite) testClearLogicalRouterStaticRoute() { require.NoError(t, err) for i, ipPrefix := range ipPrefixes { - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthops[i], nil) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthops[i], nil, nil) require.NoError(t, err) routes = append(routes, route) @@ -333,7 +442,7 @@ func (suite *OvnClientTestSuite) testGetLogicalRouterStaticRoute() { ipPrefix := "192.168.30.0/24" nexthop := "192.168.30.1" - err := nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthop) + err := nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthop) require.NoError(t, err) t.Run("found route", func(t *testing.T) { @@ -363,7 +472,7 @@ func (suite *OvnClientTestSuite) testGetLogicalRouterStaticRoute() { ipPrefix := "192.168.40.0/24" nexthop := "192.168.40.1" - err := nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthop) + err := nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthop) require.NoError(t, err) t.Run("found route", func(t *testing.T) { @@ -404,7 +513,7 @@ func (suite *OvnClientTestSuite) testListLogicalRouterStaticRoutes() { } for _, data := range testData { - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, data.ipPrefix, data.nexthop, nil) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, data.ipPrefix, data.nexthop, nil, nil) require.NoError(t, err) route.ExternalIDs = data.externalIDs routes = append(routes, route) @@ -475,7 +584,7 @@ func (suite *OvnClientTestSuite) testNewLogicalRouterStaticRoute() { BFD: nil, } - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil, nil) require.NoError(t, err) expect.UUID = route.UUID require.Equal(t, expect, route) @@ -492,7 +601,7 @@ func (suite *OvnClientTestSuite) testNewLogicalRouterStaticRoute() { } } - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil, customOption) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil, nil, customOption) require.NoError(t, err) require.Equal(t, "true", route.Options["ecmp_symmetric_reply"]) require.Equal(t, "redirect", route.Options["method"]) @@ -514,7 +623,7 @@ func (suite *OvnClientTestSuite) testNewLogicalRouterStaticRoute() { route.Options["test"] = "value" } - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, &bfdID, option1, option2) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, &bfdID, nil, option1, option2) require.NoError(t, err) require.Equal(t, "value1", route.ExternalIDs["key1"]) require.Equal(t, "value", route.Options["test"]) @@ -526,12 +635,12 @@ func (suite *OvnClientTestSuite) testNewLogicalRouterStaticRoute() { ipPrefix := "192.168.120.0/24" nexthop := "192.168.120.1" - route1, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, "", ipPrefix, nexthop, nil) + route1, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, "", ipPrefix, nexthop, nil, nil) require.NoError(t, err) err = nbClient.CreateLogicalRouterStaticRoutes(lrName, route1) require.NoError(t, err) - route2, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, "", ipPrefix, nexthop, nil) + route2, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, "", ipPrefix, nexthop, nil, nil) require.Nil(t, route2) require.NoError(t, err) }) @@ -542,7 +651,7 @@ func (suite *OvnClientTestSuite) testNewLogicalRouterStaticRoute() { nexthop := "192.168.130.1" bfdID := "test-bfd-2" - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, &bfdID) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, &bfdID, nil) require.NoError(t, err) require.Equal(t, "true", route.Options[util.StaticRouteBfdEcmp]) require.Equal(t, &bfdID, route.BFD) @@ -554,7 +663,7 @@ func (suite *OvnClientTestSuite) testNewLogicalRouterStaticRoute() { nexthop := "192.168.130.1" bfdID := "test-bfd-2" - route, err := nbClient.newLogicalRouterStaticRoute("", routeTable, policy, ipPrefix, nexthop, &bfdID) + route, err := nbClient.newLogicalRouterStaticRoute("", routeTable, policy, ipPrefix, nexthop, &bfdID, nil) require.ErrorContains(t, err, "the logical router name is required") require.Nil(t, route) }) @@ -581,7 +690,7 @@ func (suite *OvnClientTestSuite) testListLogicalRouterStaticRoutesByOption() { bfdIDValue := "bfd-id" bfdID = &bfdIDValue } - route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthops[i], bfdID) + route, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthops[i], bfdID, nil) require.NoError(t, err) routes = append(routes, route) @@ -619,7 +728,7 @@ func (suite *OvnClientTestSuite) testUpdateLogicalRouterStaticRoute() { }) t.Run("update route fields", func(t *testing.T) { - err := nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nexthop) + err := nbClient.AddLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nil, nil, nexthop) require.NoError(t, err) route, err := nbClient.GetLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, false) @@ -662,9 +771,9 @@ func (suite *OvnClientTestSuite) testGetLogicalRouterStaticRouteEdgeCases() { ipPrefix := "192.168.2.0/24" nexthop := "192.168.2.1" - route1, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil) + route1, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil, nil) require.NoError(t, err) - route2, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil) + route2, err := nbClient.newLogicalRouterStaticRoute(lrName, routeTable, policy, ipPrefix, nexthop, nil, nil) require.NoError(t, err) err = nbClient.CreateLogicalRouterStaticRoutes(lrName, route1, route2) @@ -685,7 +794,7 @@ func (suite *OvnClientTestSuite) testGetLogicalRouterStaticRouteEdgeCases() { ipPrefix := "192.168.4.0/24" nexthop := "192.168.4.1" - err := nbClient.AddLogicalRouterStaticRoute(lrName, "", policy, ipPrefix, nil, nexthop) + err := nbClient.AddLogicalRouterStaticRoute(lrName, "", policy, ipPrefix, nil, nil, nexthop) require.NoError(t, err) route, err := nbClient.GetLogicalRouterStaticRoute(lrName, "", policy, ipPrefix, nexthop, false) diff --git a/pkg/ovs/ovn-nb-suite_test.go b/pkg/ovs/ovn-nb-suite_test.go index 054d39f8607..08883685d42 100644 --- a/pkg/ovs/ovn-nb-suite_test.go +++ b/pkg/ovs/ovn-nb-suite_test.go @@ -17,9 +17,10 @@ import ( "github.com/ovn-org/libovsdb/ovsdb" "github.com/ovn-org/libovsdb/ovsdb/serverdb" "github.com/ovn-org/libovsdb/server" + "k8s.io/klog/v2" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "k8s.io/klog/v2" "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnnb" "github.com/kubeovn/kube-ovn/pkg/ovsdb/ovnsb" @@ -443,10 +444,18 @@ func (suite *OvnClientTestSuite) Test_ListBFD() { suite.testListBFD() } +func (suite *OvnClientTestSuite) Test_FindBFD() { + suite.testFindBFD() +} + func (suite *OvnClientTestSuite) Test_DeleteBFD() { suite.testDeleteBFD() } +func (suite *OvnClientTestSuite) Test_DeleteBFDByDstIP() { + suite.testDeleteBFDByDstIP() +} + func (suite *OvnClientTestSuite) Test_ListDownBFDs() { suite.testListDownBFDs() } @@ -839,6 +848,10 @@ func (suite *OvnClientTestSuite) Test_NewLogicalRouterPolicy() { suite.testNewLogicalRouterPolicy() } +func (suite *OvnClientTestSuite) Test_UpdateLogicalRouterPolicy() { + suite.testUpdateLogicalRouterPolicy() +} + func (suite *OvnClientTestSuite) Test_PolicyFilter() { suite.testPolicyFilter() } @@ -901,6 +914,14 @@ func (suite *OvnClientTestSuite) Test_AddLogicalRouterStaticRoute() { suite.testAddLogicalRouterStaticRoute() } +func (suite *OvnClientTestSuite) TestDeleteLogicalRouterStaticRouteByUUID() { + suite.testDeleteLogicalRouterStaticRouteByUUID() +} + +func (suite *OvnClientTestSuite) TestDeleteLogicalRouterStaticRouteByExternalIDs() { + suite.testDeleteLogicalRouterStaticRouteByExternalIDs() +} + func (suite *OvnClientTestSuite) Test_DeleteLogicalRouterStaticRoute() { suite.testDeleteLogicalRouterStaticRoute() } diff --git a/pkg/util/hash_test.go b/pkg/util/hash_test.go new file mode 100644 index 00000000000..c0ab50439d2 --- /dev/null +++ b/pkg/util/hash_test.go @@ -0,0 +1,73 @@ +package util + +import "testing" + +func TestSha256Hash(t *testing.T) { + tests := []struct { + name string + input []byte + output string + }{ + { + name: "Empty input", + input: []byte(""), + output: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + }, + { + name: "Non empty input", + input: []byte("hello"), + output: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := Sha256Hash(tt.input) + if got != tt.output { + t.Errorf("got %v, but want %v", got, tt.output) + } + }) + } +} + +func TestSha256HashObject(t *testing.T) { + tests := []struct { + name string + arg any + wantErr bool + hash string + }{ + { + name: "nil", + arg: nil, + hash: "74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b", + }, + { + name: "string slice", + arg: []string{"hello", "world"}, + hash: "94bedb26fb1cb9547b5b77902e89522f313c7f7fe2e9f0175cfb0a244878ee07", + }, + { + name: "string map", + arg: map[string]string{"hello": "world"}, + hash: "93a23971a914e5eacbf0a8d25154cda309c3c1c72fbb9914d47c60f3cb681588", + }, + { + name: "unsupported type", + arg: make(chan struct{}), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hash, err := Sha256HashObject(tt.arg) + if (err != nil) != tt.wantErr { + t.Errorf("got error = %#v, but wantErr = %v", err, tt.wantErr) + } + if hash != tt.hash { + t.Errorf("got hash %v, but want %v", hash, tt.hash) + } + }) + } +} diff --git a/pkg/util/k8s_test.go b/pkg/util/k8s_test.go index 024e0ff8fa9..5f5ba293155 100644 --- a/pkg/util/k8s_test.go +++ b/pkg/util/k8s_test.go @@ -3,6 +3,8 @@ package util import ( "context" "errors" + "fmt" + "math/rand/v2" "net" "net/http" "net/http/httptest" @@ -10,12 +12,19 @@ import ( "testing" "time" + "github.com/google/uuid" + nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/fake" clientv1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/klog/v2" + "k8s.io/utils/ptr" + + kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" ) func TestDialTCP(t *testing.T) { @@ -526,3 +535,336 @@ func TestGetTruncatedUID(t *testing.T) { uid := "12345678-1234-1234-1234-123456789012" require.Equal(t, "123456789012", GetTruncatedUID(uid)) } + +func TestSetOwnerReference(t *testing.T) { + tests := []struct { + name string + owner metav1.Object + object metav1.Object + wantErr bool + }{ + { + name: "base", + owner: &kubeovnv1.VpcEgressGateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("veg-%05d", rand.IntN(10000)), + UID: types.UID(uuid.New().String()), + }, + }, + object: &v1.Pod{}, + }, + { + name: "not registered", + owner: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("veg-%05d", rand.IntN(10000)), + UID: types.UID(uuid.New().String()), + }, + }, + object: &v1.Pod{}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := SetOwnerReference(tt.owner, tt.object) + if (err != nil) != tt.wantErr { + t.Errorf("SetOwnerReference() error = %#v, wantErr = %v", err, tt.wantErr) + } + if err != nil { + return + } + + refer := tt.object.GetOwnerReferences() + require.Len(t, refer, 1) + require.Equal(t, tt.owner.GetName(), refer[0].Name) + require.Equal(t, tt.owner.GetUID(), refer[0].UID) + }) + } +} + +func TestPodAttachmentIPs(t *testing.T) { + tests := []struct { + name string + pod *v1.Pod + network string + wantErr bool + ips []string + }{ + { + name: "ipv4", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + nadv1.NetworkStatusAnnot: `[{"name": "default/ipv4", "ips": ["1.1.1.1"]}]`, + }, + }, + }, + network: "default/ipv4", + ips: []string{"1.1.1.1"}, + }, + { + name: "ipv6", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + nadv1.NetworkStatusAnnot: `[{"name": "default/ipv6", "ips": ["fd00::1"]}]`, + }, + }, + }, + network: "default/ipv6", + ips: []string{"fd00::1"}, + }, + { + name: "dual-stack", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + nadv1.NetworkStatusAnnot: `[{"name": "default/dual", "ips": ["1.1.1.1", "fd00::1"]}]`, + }, + }, + }, + network: "default/dual", + ips: []string{"1.1.1.1", "fd00::1"}, + }, + { + name: "nil pod", + pod: nil, + wantErr: true, + }, + { + name: "no network status annotation", + pod: &v1.Pod{}, + wantErr: true, + }, + { + name: "unexpected network status annotation", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + nadv1.NetworkStatusAnnot: `foo_bar`, + }, + }, + }, + wantErr: true, + }, + { + name: "empty network name", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + nadv1.NetworkStatusAnnot: `[{"name": "default/xxx", "ips": ["1.1.1.1", "fd00::1"]}]`, + }, + }, + }, + network: "", + wantErr: true, + }, + { + name: "network status not found", + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + nadv1.NetworkStatusAnnot: `[{"name": "default/xyz", "ips": ["1.1.1.1", "fd00::1"]}]`, + }, + }, + }, + network: "default/abc", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ips, err := PodAttachmentIPs(tt.pod, tt.network) + if (err != nil) != tt.wantErr { + t.Errorf("PodAttachmentIPs() error = %#v, wantErr = %v", err, tt.wantErr) + } + if err != nil { + return + } + + require.ElementsMatch(t, tt.ips, ips) + }) + } +} + +func TestDeploymentIsReady(t *testing.T) { + tests := []struct { + name string + deploy *appsv1.Deployment + ready bool + }{ + { + name: "ready", + deploy: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: ptr.To[int32](1), + }, + Status: appsv1.DeploymentStatus{ + ObservedGeneration: 2, + Replicas: 1, + UpdatedReplicas: 1, + AvailableReplicas: 1, + }, + }, + ready: true, + }, + { + name: "generation mismatch", + deploy: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }, + Status: appsv1.DeploymentStatus{ + ObservedGeneration: 1, + }, + }, + ready: false, + }, + { + name: "condition Processing", + deploy: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: ptr.To[int32](1), + }, + Status: appsv1.DeploymentStatus{ + ObservedGeneration: 2, + Replicas: 1, + UpdatedReplicas: 1, + AvailableReplicas: 1, + Conditions: []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentProgressing, + }, + }, + }, + }, + ready: true, + }, + { + name: "ProgressDeadlineExceeded", + deploy: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }, + Status: appsv1.DeploymentStatus{ + ObservedGeneration: 2, + Conditions: []appsv1.DeploymentCondition{ + { + Type: appsv1.DeploymentProgressing, + Reason: "ProgressDeadlineExceeded", + }, + }, + }, + }, + ready: false, + }, + { + name: "updated replicas less than desired replicas", + deploy: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: ptr.To[int32](2), + }, + Status: appsv1.DeploymentStatus{ + ObservedGeneration: 2, + Replicas: 2, + UpdatedReplicas: 1, + AvailableReplicas: 1, + }, + }, + ready: false, + }, + { + name: "updated replicas less than current replicas", + deploy: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: ptr.To[int32](1), + }, + Status: appsv1.DeploymentStatus{ + ObservedGeneration: 2, + Replicas: 2, + UpdatedReplicas: 1, + AvailableReplicas: 1, + }, + }, + ready: false, + }, + { + name: "available replicas less than updated replicas", + deploy: &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Generation: 2, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: ptr.To[int32](2), + }, + Status: appsv1.DeploymentStatus{ + ObservedGeneration: 2, + Replicas: 2, + UpdatedReplicas: 2, + AvailableReplicas: 1, + }, + }, + ready: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ready := DeploymentIsReady(tt.deploy) + require.Equal(t, tt.ready, ready) + }) + } +} + +func Test_nodeMergePatch(t *testing.T) { + tests := []struct { + name string + patch string + wantErr bool + }{ + { + name: "valid_merge_patch", + patch: `{"metadata":{"labels":{"key1":"value1"}}}`, + wantErr: false, + }, + { + name: "invalid_merge_patch", + patch: "invalid_merge_patch", + wantErr: true, + }, + } + + client := fake.NewClientset(&v1.Node{}).CoreV1().Nodes() + _, err := client.Create(context.TODO(), &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node1", + }, + }, metav1.CreateOptions{}) + require.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err = nodeMergePatch(client, "node1", tt.patch) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} diff --git a/pkg/util/patch_test.go b/pkg/util/patch_test.go index 0dc5c877777..6c3453e4adc 100644 --- a/pkg/util/patch_test.go +++ b/pkg/util/patch_test.go @@ -13,6 +13,12 @@ import ( ) func TestGenerateStrategicMergePatchPayload(t *testing.T) { + type C chan struct{} + type unsupportedType struct { + C + *v1.Pod + } + type args struct { // original stands for the original object we seen before we handle original runtime.Object @@ -24,7 +30,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { tests := []struct { name string args args - want v1.Pod + want runtime.Object wantErr bool }{ { @@ -34,7 +40,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, wantErr: false, }, { @@ -44,7 +50,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1"}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1", "ovn1": "1", "ovn2": "2"}}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1", "ovn1": "1", "ovn2": "2"}}}, wantErr: false, }, { @@ -54,7 +60,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, wantErr: false, }, { @@ -64,9 +70,25 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1"}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, wantErr: false, }, + { + name: "argument original is of unsupported type", + args: args{ + original: &unsupportedType{}, + modified: &v1.Pod{}, + }, + wantErr: true, + }, + { + name: "argument modified is of unsupported type", + args: args{ + original: &v1.Pod{}, + modified: &unsupportedType{}, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -75,12 +97,16 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { t.Errorf("GenerateStrategicMergePatchPayload() error = %v, wantErr %v", err, tt.wantErr) return } + if err != nil { + return + } + b, _ := json.Marshal(tt.args.remote) // apply patch for remote obj newB, _ := strategicpatch.StrategicMergePatch(b, got, v1.Pod{}) patchedPod := v1.Pod{} _ = json.Unmarshal(newB, &patchedPod) - if !assert.Equal(t, tt.want, patchedPod, "patch: %s", got) { + if !assert.Equal(t, tt.want, runtime.Object(&patchedPod), "patch: %s", got) { t.Errorf("patch not correct, got = %+v, want= %+v", patchedPod, tt.want) } }) @@ -88,6 +114,12 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { } func TestGenerateMergePatchPayload(t *testing.T) { + type C chan struct{} + type unsupportedType struct { + C + *v1.Pod + } + type args struct { // original stands for the original object we seen before we handle original runtime.Object @@ -99,7 +131,7 @@ func TestGenerateMergePatchPayload(t *testing.T) { tests := []struct { name string args args - want v1.Pod + want runtime.Object wantErr bool }{ { @@ -109,7 +141,7 @@ func TestGenerateMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, wantErr: false, }, { @@ -119,7 +151,7 @@ func TestGenerateMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1"}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1", "ovn1": "1", "ovn2": "2"}}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1", "ovn1": "1", "ovn2": "2"}}}, wantErr: false, }, { @@ -129,7 +161,7 @@ func TestGenerateMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, wantErr: false, }, { @@ -139,9 +171,25 @@ func TestGenerateMergePatchPayload(t *testing.T) { modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, remote: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1"}}}, }, - want: v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, + want: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: nil}}, wantErr: false, }, + { + name: "argument original is of unsupported type", + args: args{ + original: &unsupportedType{}, + modified: &v1.Pod{}, + }, + wantErr: true, + }, + { + name: "argument modified is of unsupported type", + args: args{ + original: &v1.Pod{}, + modified: &unsupportedType{}, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -150,12 +198,16 @@ func TestGenerateMergePatchPayload(t *testing.T) { t.Errorf("GenerateMergePatchPayload() error = %v, wantErr %v", err, tt.wantErr) return } + if err != nil { + return + } + b, _ := json.Marshal(tt.args.remote) // apply patch for remote obj newB, _ := strategicpatch.StrategicMergePatch(b, got, v1.Pod{}) patchedPod := v1.Pod{} _ = json.Unmarshal(newB, &patchedPod) - if !assert.Equal(t, tt.want, patchedPod, "patch: %s", got) { + if !assert.Equal(t, tt.want, runtime.Object(&patchedPod), "patch: %s", got) { t.Errorf("patch not correct, got = %+v, want= %+v", patchedPod, tt.want) } }) diff --git a/pkg/util/pod_routes.go b/pkg/util/pod_routes.go index 55274baee2d..c84a79bb7d2 100644 --- a/pkg/util/pod_routes.go +++ b/pkg/util/pod_routes.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" - "k8s.io/klog/v2" - "github.com/kubeovn/kube-ovn/pkg/request" ) @@ -50,11 +48,8 @@ func (r PodRoutes) ToAnnotations() (map[string]string, error) { continue } - buf, err := json.Marshal(routes) - if err != nil { - klog.Error(err) - return nil, err - } + // no error will be returned here + buf, _ := json.Marshal(routes) annotations[fmt.Sprintf(RoutesAnnotationTemplate, provider)] = string(buf) } return annotations, nil diff --git a/pkg/util/pod_routes_test.go b/pkg/util/pod_routes_test.go new file mode 100644 index 00000000000..39b30b61fc5 --- /dev/null +++ b/pkg/util/pod_routes_test.go @@ -0,0 +1,61 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPodRoutes(t *testing.T) { + routes := NewPodRoutes() + require.NotNil(t, routes) + require.Empty(t, routes) + annotations, err := routes.ToAnnotations() + require.NoError(t, err) + require.Len(t, annotations, 0) + + routes.Add("foo", "0.0.0.1", "1.1.1.1") + routes.Add("foo", "0.0.1.0/24", "1.1.1.1") + routes.Add("foo", "0.1.0.0/16", "1.1.1.2") + require.Len(t, routes, 1) + require.Len(t, routes["foo"], 2) + require.Len(t, routes["foo"]["1.1.1.1"], 2) + require.Len(t, routes["foo"]["1.1.1.2"], 1) + annotations, err = routes.ToAnnotations() + require.NoError(t, err) + require.Len(t, annotations, 1) + + routes.Add("foo", "0.0.0.1", "") + routes.Add("foo", "", "1.1.1.3") + routes.Add("foo", "", "") + require.Len(t, routes, 1) + require.Len(t, routes["foo"], 2) + require.Len(t, routes["foo"]["1.1.1.1"], 2) + require.Len(t, routes["foo"]["1.1.1.2"], 1) + annotations, err = routes.ToAnnotations() + require.NoError(t, err) + require.Len(t, annotations, 1) + + routes.Add("bar", "192.168.0.1/32", "2.2.2.2") + require.Len(t, routes, 2) + require.Len(t, routes["foo"], 2) + require.Len(t, routes["foo"]["1.1.1.1"], 2) + require.Len(t, routes["foo"]["1.1.1.2"], 1) + require.Len(t, routes["bar"], 1) + require.Len(t, routes["bar"]["2.2.2.2"], 1) + annotations, err = routes.ToAnnotations() + require.NoError(t, err) + require.Len(t, annotations, 2) + + // empty routes + routes = PodRoutes{"foo": PodProviderRoutes{}} + annotations, err = routes.ToAnnotations() + require.NoError(t, err) + require.Empty(t, annotations) + + // empty gateway + routes["foo"] = PodProviderRoutes{"": []string{"1.1.1.1"}} + annotations, err = routes.ToAnnotations() + require.NoError(t, err) + require.Empty(t, annotations) +} diff --git a/pkg/util/strings_test.go b/pkg/util/strings_test.go index e2b90ce80af..c7d0b5cd657 100644 --- a/pkg/util/strings_test.go +++ b/pkg/util/strings_test.go @@ -48,31 +48,3 @@ func TestDoubleQuotedFields(t *testing.T) { }) } } - -func TestSha256Hash(t *testing.T) { - tests := []struct { - name string - input []byte - output string - }{ - { - name: "Empty input", - input: []byte(""), - output: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - }, - { - name: "Non empty input", - input: []byte("hello"), - output: "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := Sha256Hash(tt.input) - if got != tt.output { - t.Errorf("got %v, but want %v", got, tt.output) - } - }) - } -}