package api import ( "context" "net" "os" "testing" "time" "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi" mnepb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/networkelement" rpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/plugin-registry" ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" "code.fbi.h-da.de/danet/gosdn/controller/app" "code.fbi.h-da.de/danet/gosdn/controller/config" "code.fbi.h-da.de/danet/gosdn/controller/conflict" eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/plugin" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" "code.fbi.h-da.de/danet/gosdn/controller/mocks" nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/server" "code.fbi.h-da.de/danet/gosdn/controller/nucleus" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/util/proto" rbacImpl "code.fbi.h-da.de/danet/gosdn/controller/rbac" "code.fbi.h-da.de/danet/gosdn/controller/store" "code.fbi.h-da.de/danet/gosdn/controller/topology" "code.fbi.h-da.de/danet/gosdn/controller/topology/links" "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" routingtables "code.fbi.h-da.de/danet/gosdn/controller/topology/routing-tables" topoStore "code.fbi.h-da.de/danet/gosdn/controller/topology/store" "code.fbi.h-da.de/danet/gosdn/models/generated/openconfig" "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/mock" "go.mongodb.org/mongo-driver/mongo" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/test/bufconn" gpb "github.com/openconfig/gnmi/proto/gnmi" pb "google.golang.org/protobuf/proto" ) /* Based on this StackOverflow answer: https://stackoverflow.com/a/52080545/4378176 */ const bufSize = 1024 * 1024 const bufnet = "bufnet" const pndID = "2043519e-46d1-4963-9a8e-d99007e104b8" const changeID = "0992d600-f7d4-4906-9559-409b04d59a5f" const sbiID = "f6fd4b35-f039-4111-9156-5e4501bb8a5a" const mneID = "7e0ed8cc-ebf5-46fa-9794-741494914883" var pndStore networkdomain.PndStore var userService rbac.UserService var roleService rbac.RoleService var pluginStore plugin.Store var lis *bufconn.Listener var pndUUID uuid.UUID var sbiUUID uuid.UUID func bootstrapUnitTest() { dialOptions = []grpc.DialOption{ grpc.WithContextDialer(bufDialer), grpc.WithTransportCredentials(insecure.NewCredentials()), } lis = bufconn.Listen(bufSize) s := grpc.NewServer() changeUUID, err := uuid.Parse(changeID) if err != nil { log.Fatal(err) } pndUUID, err = uuid.Parse(pndID) if err != nil { log.Fatal(err) } sbiUUID, err = uuid.Parse(sbiID) if err != nil { log.Fatal(err) } mneUUID, err := uuid.Parse(mneID) if err != nil { log.Fatal(err) } eventService := eventservice.NewMockEventService() pndStore = nucleus.NewMemoryPndStore() userService = rbacImpl.NewUserService(rbacImpl.NewMemoryUserStore(), eventService) roleService = rbacImpl.NewRoleService(rbacImpl.NewMemoryRoleStore(), eventService) if err := clearAndCreateAuthTestSetup(); err != nil { log.Fatal(err) } previousHostname := "previousHostname" intendedHostname := "intendedHostname" mockChange := &mocks.Change{} mockChange.On("ID").Return(uuid.MustParse(changeID)) mockChange.On("Age").Return(time.Hour) mockChange.On("Commit").Return(nil) mockChange.On("Confirm").Return(nil) mockChange.On("State").Return(mnepb.ChangeState_CHANGE_STATE_INCONSISTENT) mockChange.On("PreviousState").Return(&openconfig.Device{ System: &openconfig.OpenconfigSystem_System{ Config: &openconfig.OpenconfigSystem_System_Config{ Hostname: &previousHostname, }, }, }) mockChange.On("IntendedState").Return(&openconfig.Device{ System: &openconfig.OpenconfigSystem_System{ Config: &openconfig.OpenconfigSystem_System_Config{ Hostname: &intendedHostname, }, }, }) defaultPluginID, err := uuid.Parse("b70c8425-68c7-4d4b-bb5e-5586572bd64b") if err != nil { log.Fatal(err) } pluginMock := &mocks.Plugin{} pluginMock.On("ID").Return(defaultPluginID) pluginMock.On("Unmarshal", mock.Anything, mock.Anything).Return(nil) pluginMock.On("GetNode", mock.Anything).Return([]*gpb.Notification{}, nil) pluginMock.On("Model", mock.Anything).Return([]byte( "{\n\t\"Acl\": null,\n\t\"Bfd\": null,\n\t\"Components\": null,\n\t\"Interfaces\": null,\n\t\"Keychains\": null,\n\t\"Lldp\": null,\n\t\"Messages\": null,\n\t\"NetworkInstances\": null,\n\t\"RoutingPolicy\": null,\n\t\"System\": null\n}"), nil, ) mockNetworkElement := &mocks.NetworkElement{} mockNetworkElement.On("Plugin").Return(pluginMock) mockNetworkElement.On("ID").Return(mneUUID) mockNetworkElement.On("GetModel").Return(pluginMock.Model(false)) mockNetworkElement.On("Name").Return("openconfig") mockNetworkElement.On("GetPlugin").Return(pluginMock) mockNetworkElement.On("TransportAddress").Return("127.0.0.1:6030") mockNetworkElement.On("GetMetadata").Return(conflict.Metadata{ResourceVersion: 0}) mockPnd := mocks.NetworkDomain{} mockPnd.On("ID").Return(pndUUID) mockPnd.On("GetName").Return("test") mockPnd.On("GetDescription").Return("test") mockPnd.On("PendingChanges").Return([]uuid.UUID{changeUUID}) mockPnd.On("CommittedChanges").Return([]uuid.UUID{changeUUID}) mockPnd.On("GetChange", mock.Anything).Return(mockChange, nil) mockPnd.On("AddNetworkElement", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) mockPnd.On("GetNetworkElement", mock.Anything).Return(mockNetworkElement, nil) mockPnd.On("Commit", mock.Anything).Return(nil) mockPnd.On("Confirm", mock.Anything).Return(nil) mockPnd.On("NetworkElements").Return([]networkelement.NetworkElement{ &nucleus.CommonNetworkElement{ UUID: mneUUID, Plugin: &mocks.Plugin{}, }, }) mockPnd.On("ChangeMNE", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(uuid.Nil, nil) if err := pndStore.Add(context.TODO(), &mockPnd); err != nil { log.Fatal(err) } jwtManager := rbacImpl.NewJWTManager("", (10000 * time.Hour)) appService := app.NewMockAppService() nodeStore := topoStore.NewGenericStore[nodes.Node]() nodeService := nodes.NewNodeService(nodeStore, eventService) portStore := topoStore.NewGenericStore[ports.Port]() portService := ports.NewPortService(portStore, eventService) topoloyStore := topoStore.NewGenericStore[links.Link]() topologyService := topology.NewTopologyService(topoloyStore, nodeService, portService, eventService) routeStore := topoStore.NewGenericStore[routingtables.RoutingTable]() routeService := routingtables.NewRoutingTableService(routeStore, nodeService, portService, eventService) pluginStore = nucleus.NewMemoryPluginStore() pluginService := nucleus.NewPluginService(pluginStore, eventService, nucleus.NewPluginThroughReattachConfig, rpb.NewPluginRegistryServiceClient(&grpc.ClientConn{})) networkElementStore := nucleus.NewNetworkElementStore(&mongo.Database{}, pndUUID) networkElementService := nucleus.NewNetworkElementService(networkElementStore, pluginService, eventService) mne, _ := nucleus.NewNetworkElement("test", mneUUID, &tpb.TransportOption{ Address: "test", Username: "test", Password: "test", TransportOption: &tpb.TransportOption_GnmiTransportOption{ GnmiTransportOption: &tpb.GnmiTransportOption{}, }, }, pndUUID, pluginMock, [][]string{}, conflict.Metadata{ResourceVersion: 0}) _ = networkElementService.Add(mne) networkElementWatcher := nucleus.NewNetworkElementWatcher(networkElementService, eventService) pndService := &mocks.PndService{} pndService.On("GetAll").Return([]networkdomain.NetworkDomain{}, nil) pndService.On("Add", mock.Anything).Return(nil) pndService.On("Get", mock.Anything).Return(nucleus.NewPND(pndUUID, "test", "test"), nil) changeStore := *store.NewChangeStore() err = changeStore.Add(mockChange) if err != nil { log.Fatal(err) } northbound := nbi.NewNBI( pndStore, pndService, networkElementService, changeStore, userService, roleService, *jwtManager, topologyService, nodeService, portService, routeService, appService, pluginService, rpb.NewPluginRegistryServiceClient(&grpc.ClientConn{}), csbi.NewCsbiServiceClient(&grpc.ClientConn{}), func(u uuid.UUID, c chan networkelement.Details) {}, networkElementWatcher, ) ppb.RegisterPndServiceServer(s, northbound.Pnd) apb.RegisterAuthServiceServer(s, northbound.Auth) apb.RegisterUserServiceServer(s, northbound.User) apb.RegisterRoleServiceServer(s, northbound.Role) mnepb.RegisterNetworkElementServiceServer(s, northbound.NetworkElement) go func() { if err := s.Serve(lis); err != nil { log.Fatalf("Server exited with error: %v", err) } }() } func bufDialer(context.Context, string) (net.Conn, error) { return lis.Dial() } var testAddress = "10.254.254.105:6030" var testAPIEndpoint = "gosdn-latest.apps.ocp.fbi.h-da.de" var testUsername = "admin" var testPassword = "arista" var opt *tpb.TransportOption var gnmiMessages map[string]pb.Message func TestMain(m *testing.M) { bootstrapUnitTest() bootstrapIntegrationTest() os.Exit(m.Run()) } func bootstrapIntegrationTest() { log.SetLevel(config.LogLevel) addr := os.Getenv("CEOS_TEST_ENDPOINT") if addr != "" { testAddress = addr log.Infof("CEOS_TEST_ENDPOINT set to %v", testAddress) } api := os.Getenv("GOSDN_TEST_API_ENDPOINT") if api != "" { testAPIEndpoint = api log.Infof("GOSDN_TEST_API_ENDPOINT set to %v", testAPIEndpoint) } u := os.Getenv("GOSDN_TEST_USER") if u != "" { testUsername = u log.Infof("GOSDN_TEST_USER set to %v", testUsername) } p := os.Getenv("GOSDN_TEST_PASSWORD") if p != "" { testPassword = p log.Infof("GOSDN_TEST_PASSWORD set to %v", testPassword) } gnmiMessages = map[string]pb.Message{ "../test/proto/cap-resp-arista-ceos": &gpb.CapabilityResponse{}, "../test/proto/req-full-node": &gpb.GetRequest{}, "../test/proto/req-full-node-arista-ceos": &gpb.GetRequest{}, "../test/proto/req-interfaces-arista-ceos": &gpb.GetRequest{}, "../test/proto/req-interfaces-interface-arista-ceos": &gpb.GetRequest{}, "../test/proto/req-interfaces-wildcard": &gpb.GetRequest{}, "../test/proto/resp-full-node": &gpb.GetResponse{}, "../test/proto/resp-full-node-arista-ceos": &gpb.GetResponse{}, "../test/proto/resp-interfaces-arista-ceos": &gpb.GetResponse{}, "../test/proto/resp-interfaces-interface-arista-ceos": &gpb.GetResponse{}, "../test/proto/resp-interfaces-wildcard": &gpb.GetResponse{}, "../test/proto/resp-set-system-config-hostname": &gpb.SetResponse{}, } for k, v := range gnmiMessages { if err := proto.Read(k, v); err != nil { log.Fatalf("error parsing %v: %v", k, err) } } opt = &tpb.TransportOption{ Address: testAddress, Username: testUsername, Password: testPassword, } }