diff --git a/.gitignore b/.gitignore index 5fa2ffa0ffd521c3989ebc87ca0a4b36679083d2..00dfa3bc67467b7267c2815c8f6645be0759b6bf 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ controller/config/*_test.toml controller/configs/ci-testing-gosdn.toml controller/stores_testing controller/stores/** +controller/cmd/gosdn/stores/ controller/plugins controller/config/.gosdnc.toml controller/debug.test diff --git a/api/go/gosdn/rbac/role.pb.go b/api/go/gosdn/rbac/role.pb.go index e3dfae038facef552767a4dfb61c881d77077c54..c4212deaae5a8bc582972d2bb6bf556f3bfd73b9 100644 --- a/api/go/gosdn/rbac/role.pb.go +++ b/api/go/gosdn/rbac/role.pb.go @@ -213,6 +213,7 @@ type GetRoleRequest struct { Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` RoleName string `protobuf:"bytes,2,opt,name=role_name,json=roleName,proto3" json:"role_name,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` } func (x *GetRoleRequest) Reset() { @@ -261,6 +262,13 @@ func (x *GetRoleRequest) GetRoleName() string { return "" } +func (x *GetRoleRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + type GetRoleResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -808,12 +816,13 @@ var file_gosdn_rbac_role_proto_rawDesc = []byte{ 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x52, 0x6f, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x5b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x6c, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, diff --git a/api/go/gosdn/rbac/user.pb.go b/api/go/gosdn/rbac/user.pb.go index f999c7ddee55ebdc9a4de8f525a42ab5ea03669c..ef2f31b24e1600f2bb8f43c9ffd20442610dc423 100644 --- a/api/go/gosdn/rbac/user.pb.go +++ b/api/go/gosdn/rbac/user.pb.go @@ -221,6 +221,7 @@ type GetUserRequest struct { Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` } func (x *GetUserRequest) Reset() { @@ -269,6 +270,13 @@ func (x *GetUserRequest) GetName() string { return "" } +func (x *GetUserRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + type GetUserResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -703,11 +711,12 @@ var file_gosdn_rbac_user_proto_rawDesc = []byte{ 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x67, 0x6f, 0x73, 0x64, 0x6e, 0x2e, 0x72, 0x62, 0x61, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x22, 0x42, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x73, 0x22, 0x52, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, diff --git a/api/openapiv2/gosdn_northbound.swagger.json b/api/openapiv2/gosdn_northbound.swagger.json index 0aacfb1a8e9cf36fa6671b8d53e13a200f5261b1..77a7432ec7f3ef99fa1726f4191f2c62446fc709 100644 --- a/api/openapiv2/gosdn_northbound.swagger.json +++ b/api/openapiv2/gosdn_northbound.swagger.json @@ -991,6 +991,12 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "id", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -1169,6 +1175,12 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "id", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ diff --git a/api/proto/buf.lock b/api/proto/buf.lock index 31d7e095cfec7e5cc23dadd123fe5d30bf1cb7eb..9342c3302b88ff91b1cb3489a2548db7f0d8e81e 100644 --- a/api/proto/buf.lock +++ b/api/proto/buf.lock @@ -4,7 +4,11 @@ deps: - remote: buf.build owner: googleapis repository: googleapis +<<<<<<< HEAD commit: 295f0f9eef8d46f490dfdea65323955b +======= + commit: fdc236b6d1644b29a6161156ce08d8a2 +>>>>>>> develop - remote: buf.build owner: grpc-ecosystem repository: grpc-gateway diff --git a/api/proto/gosdn/rbac/role.proto b/api/proto/gosdn/rbac/role.proto index 044fbef8a5f3edd2ba63023554f2d148aa3f6a87..29edc1cd4f9bd7f777eec1bf14ceace0eec03c21 100644 --- a/api/proto/gosdn/rbac/role.proto +++ b/api/proto/gosdn/rbac/role.proto @@ -84,6 +84,7 @@ message CreateRolesResponse { message GetRoleRequest { int64 timestamp = 1; string role_name = 2; + string id = 3; } message GetRoleResponse { diff --git a/api/proto/gosdn/rbac/user.proto b/api/proto/gosdn/rbac/user.proto index 3ad7f88de24cb5a0adbc5504c335c9d0f215f6e6..8de04465cc4add2b4223930157118d5e6858cab1 100644 --- a/api/proto/gosdn/rbac/user.proto +++ b/api/proto/gosdn/rbac/user.proto @@ -78,6 +78,7 @@ message CreateUsersResponse { message GetUserRequest { int64 timestamp = 1; string name = 2; + string id = 3; } message GetUserResponse { diff --git a/cli/cmd/userGet.go b/cli/cmd/userGet.go index b5f1a0bd9ba21765a81df889fabc182029126615..1a487a3a7f02a2aa275f479ca064210ee89fbe38 100644 --- a/cli/cmd/userGet.go +++ b/cli/cmd/userGet.go @@ -33,6 +33,7 @@ package cmd import ( "code.fbi.h-da.de/danet/gosdn/controller/api" + "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -45,7 +46,12 @@ var userGetCmd = &cobra.Command{ Long: `Requests one user using the provided name to search for it in the stored users.`, RunE: func(cmd *cobra.Command, args []string) error { - resp, err := api.GetUser(createContextWithAuthorization(), viper.GetString("controllerAPIEndpoint"), nbUserName) + resp, err := api.GetUser( + createContextWithAuthorization(), + viper.GetString("controllerAPIEndpoint"), + nbUserName, + uuid.Nil, + ) if err != nil { return err } diff --git a/controller/api/initialise_test.go b/controller/api/initialise_test.go index 21fc210175207bc3ccd3a766c040aada357f9184..46726014e7acdd5493f6de98a6caea0e3b71f714 100644 --- a/controller/api/initialise_test.go +++ b/controller/api/initialise_test.go @@ -13,6 +13,7 @@ import ( 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/config" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" @@ -81,10 +82,12 @@ func bootstrapUnitTest() { log.Fatal(err) } + eventService := eventservice.NewMockEventService() + pndStore = nucleus.NewMemoryPndStore() sbiStore = nucleus.NewMemorySbiStore() - userService = rbacImpl.NewUserService(rbacImpl.NewMemoryUserStore()) - roleService = rbacImpl.NewRoleService(rbacImpl.NewMemoryRoleStore()) + userService = rbacImpl.NewUserService(rbacImpl.NewMemoryUserStore(), eventService) + roleService = rbacImpl.NewRoleService(rbacImpl.NewMemoryRoleStore(), eventService) clearAndCreateAuthTestSetup() previousHostname := "previousHostname" diff --git a/controller/api/role.go b/controller/api/role.go index b9d6bcc5c66186db408cf6c974a1bfab101d0769..cef73197238fa77d13a81d9a561a9ea3ea20ee2b 100644 --- a/controller/api/role.go +++ b/controller/api/role.go @@ -6,6 +6,7 @@ import ( apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client" + "github.com/google/uuid" ) // CreateRoles creates roles with provided data @@ -24,7 +25,7 @@ func CreateRoles(ctx context.Context, addr string, roles []*apb.Role) (*apb.Crea } // GetRole returns one requested role found by name -func GetRole(ctx context.Context, addr, name string) (*apb.GetRoleResponse, error) { +func GetRole(ctx context.Context, addr, name string, uuid uuid.UUID) (*apb.GetRoleResponse, error) { roleClient, err := nbi.RoleClient(addr, dialOptions...) if err != nil { return nil, err @@ -33,6 +34,7 @@ func GetRole(ctx context.Context, addr, name string) (*apb.GetRoleResponse, erro r := &apb.GetRoleRequest{ Timestamp: time.Now().UnixNano(), RoleName: name, + Id: uuid.String(), } return roleClient.GetRole(ctx, r) diff --git a/controller/api/role_test.go b/controller/api/role_test.go index c98b0d277579b2376b86aa5b03d7e2e051b09a50..785ae3128e3c36d770bd67208492d27cb8aaccb4 100644 --- a/controller/api/role_test.go +++ b/controller/api/role_test.go @@ -61,6 +61,7 @@ func TestGetRole(t *testing.T) { ctx context.Context addr string name string + id uuid.UUID } tests := []struct { name string @@ -74,6 +75,7 @@ func TestGetRole(t *testing.T) { ctx: context.TODO(), addr: testAPIEndpoint, name: "adminTestRole", + id: uuid.Nil, }, want: &apb.GetRoleResponse{ Status: apb.Status_STATUS_OK, @@ -90,6 +92,7 @@ func TestGetRole(t *testing.T) { ctx: context.TODO(), addr: testAPIEndpoint, name: "not role", + id: uuid.Nil, }, want: nil, wantErr: true, @@ -97,7 +100,7 @@ func TestGetRole(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetRole(tt.args.ctx, tt.args.addr, tt.args.name) + got, err := GetRole(tt.args.ctx, tt.args.addr, tt.args.name, tt.args.id) if (err != nil) != tt.wantErr { t.Errorf("GetRole() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/controller/api/user.go b/controller/api/user.go index 31d6828607c38c994dabde2e12fd2d4e7e9bfb54..f5f49ea4d6fc44a8dea527cc72db2556163b03d5 100644 --- a/controller/api/user.go +++ b/controller/api/user.go @@ -6,6 +6,8 @@ import ( apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" nbi "code.fbi.h-da.de/danet/gosdn/controller/northbound/client" + + "github.com/google/uuid" ) // CreateUsers creates users with provided data @@ -24,7 +26,7 @@ func CreateUsers(ctx context.Context, addr string, users []*apb.User) (*apb.Crea } //GetUser returns one requested user found by name -func GetUser(ctx context.Context, addr, name string) (*apb.GetUserResponse, error) { +func GetUser(ctx context.Context, addr, name string, uuid uuid.UUID) (*apb.GetUserResponse, error) { userClient, err := nbi.UserClient(addr, dialOptions...) if err != nil { return nil, err @@ -33,6 +35,7 @@ func GetUser(ctx context.Context, addr, name string) (*apb.GetUserResponse, erro r := &apb.GetUserRequest{ Timestamp: time.Now().UnixNano(), Name: name, + Id: uuid.String(), } return userClient.GetUser(ctx, r) diff --git a/controller/api/user_test.go b/controller/api/user_test.go index ab8cf858ec7e2fa7c4bf651b01da4517afb2c4f9..afa2f6dc6aa0f3bc9edbde0030dab5f20d74b31d 100644 --- a/controller/api/user_test.go +++ b/controller/api/user_test.go @@ -65,6 +65,7 @@ func TestGetUser(t *testing.T) { ctx context.Context addr string name string + id uuid.UUID } tests := []struct { name string @@ -78,6 +79,7 @@ func TestGetUser(t *testing.T) { ctx: context.TODO(), addr: testAPIEndpoint, name: "testAdmin", + id: uuid.Nil, }, want: &apb.GetUserResponse{ Status: apb.Status_STATUS_OK, @@ -94,6 +96,7 @@ func TestGetUser(t *testing.T) { ctx: context.TODO(), addr: testAPIEndpoint, name: "foos", + id: uuid.Nil, }, want: nil, wantErr: true, @@ -102,7 +105,7 @@ func TestGetUser(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetUser(tt.args.ctx, tt.args.addr, tt.args.name) + got, err := GetUser(tt.args.ctx, tt.args.addr, tt.args.name, tt.args.id) if (err != nil) != tt.wantErr { t.Errorf("GetUser() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/controller/config/config.go b/controller/config/config.go index 059821cf767b683eeb40d7c7054ee2307ccead50..dded01cadf31958c52211ffb211c6c19aab5d9dc 100644 --- a/controller/config/config.go +++ b/controller/config/config.go @@ -21,6 +21,13 @@ const ( jwtDurationKey = "defaultJWTDuration" defaultJWTDuration = time.Hour * 24 jwtSecretKey = "jwtSecret" + + // RabbitMQ Broker + amqpPrefixKey = "amqpPrefix" + amqpUserKey = "amqpUser" + amqpPasswordKey = "amqpPassword" + amqpHostKey = "amqpHost" + amqpPortKey = "amqpPort" ) // BasePndUUID is an uuid for the base PND @@ -50,6 +57,21 @@ var JWTDuration time.Duration // JWTSecret determines the scret that is used to sign tokens var JWTSecret string +// AMQPPrefix is the amqp prefix +var AMQPPrefix string + +// AMQPUser is the amqp user +var AMQPUser string + +// AMQPPassword is the amqp user password +var AMQPPassword string + +// AMQPHost is the amqp host +var AMQPHost string + +// AMQPPort is the amqp port +var AMQPPort string + // Init gets called on module import func Init() { err := InitializeConfig() @@ -103,6 +125,8 @@ func InitializeConfig() error { JWTSecret = viper.GetString(jwtSecretKey) + loadAMQPConfig() + if err := viper.WriteConfig(); err != nil { return err } @@ -171,3 +195,11 @@ func getDurationFromViper(viperKey string, unit time.Duration) (time.Duration, e return 0, viper.ConfigParseError{} } + +func loadAMQPConfig() { + AMQPPrefix = getStringFromViper(amqpPrefixKey) + AMQPUser = getStringFromViper(amqpUserKey) + AMQPPassword = getStringFromViper(amqpPasswordKey) + AMQPHost = getStringFromViper(amqpHostKey) + AMQPPort = getStringFromViper(amqpPortKey) +} diff --git a/controller/configs/containerlab-gosdn.toml.example b/controller/configs/containerlab-gosdn.toml.example index 2c5e7f28d8a88179317e17d4528afc108a5eed45..3fffe360b55fc3f9d7909890e1a2bd844d05dc35 100644 --- a/controller/configs/containerlab-gosdn.toml.example +++ b/controller/configs/containerlab-gosdn.toml.example @@ -12,3 +12,8 @@ socket = ":55055" databaseConnection = "mongodb://root:example@clab-gosdn_csbi_arista_base-mongodb:27017" filesystemPathToStores = "stores" +amqpPrefix = "amqp://" +amqpUser = "guest" +amqpPassword = "guest" +amqpHost = "localhost" +amqpPort = "5672" diff --git a/controller/configs/development-gosdn.toml.example b/controller/configs/development-gosdn.toml.example index 6ae6a6a9450b2c2637cbec9366b0b8e8bcdfa9d1..b1cf52c3a1548b582a5ff48a6d836c51880c3bb9 100644 --- a/controller/configs/development-gosdn.toml.example +++ b/controller/configs/development-gosdn.toml.example @@ -12,3 +12,9 @@ socket = ":55055" databaseConnection = "mongodb://root:example@localhost:27017" filesystemPathToStores = "stores" + +amqpPrefix = "amqp://" +amqpUser = "guest" +amqpPassword = "guest" +amqpHost = "localhost" +amqpPort = "5672" diff --git a/controller/controller.go b/controller/controller.go index cb853c45d6b94fc97552340672830dfad5706de3..a1a8333c6a01fc30d5709ca918d05114423cbe0b 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -25,6 +25,7 @@ import ( apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" "code.fbi.h-da.de/danet/gosdn/controller/config" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" @@ -33,6 +34,8 @@ import ( rbacImpl "code.fbi.h-da.de/danet/gosdn/controller/rbac" "code.fbi.h-da.de/danet/gosdn/controller/store" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus" ) @@ -41,13 +44,14 @@ var coreOnce sync.Once // Core is the representation of the controller's core type Core struct { - pndStore networkdomain.PndStore - userService rbac.UserService - roleService rbac.RoleService - httpServer *http.Server - grpcServer *grpc.Server - nbi *nbi.NorthboundInterface - stopChan chan os.Signal + pndStore networkdomain.PndStore + userService rbac.UserService + roleService rbac.RoleService + httpServer *http.Server + grpcServer *grpc.Server + nbi *nbi.NorthboundInterface + eventService eventInterfaces.Service + stopChan chan os.Signal csbiClient cpb.CsbiServiceClient } @@ -61,11 +65,17 @@ func initialize() error { return err } + eventService, err := eventservice.NewEventService() + if err != nil { + return err + } + c = &Core{ - pndStore: nucleus.NewPndStore(), - userService: rbacImpl.NewUserService(rbacImpl.NewUserStore()), - roleService: rbacImpl.NewRoleService(rbacImpl.NewRoleStore()), - stopChan: make(chan os.Signal, 1), + pndStore: nucleus.NewPndStore(), + userService: rbacImpl.NewUserService(rbacImpl.NewUserStore(), eventService), + roleService: rbacImpl.NewRoleService(rbacImpl.NewRoleStore(), eventService), + eventService: eventService, + stopChan: make(chan os.Signal, 1), } // Setting up signal capturing @@ -265,6 +275,7 @@ func shutdown() error { coreLock.Lock() defer coreLock.Unlock() c.grpcServer.GracefulStop() + c.eventService.CloseConnection() return stopHttpServer() } diff --git a/controller/event/event.go b/controller/event/event.go new file mode 100644 index 0000000000000000000000000000000000000000..4f9aaef8db2bc5a702fac335c62d4bb44030acd4 --- /dev/null +++ b/controller/event/event.go @@ -0,0 +1,48 @@ +package event + +import "github.com/google/uuid" + +// Event is a event that can be published via the event service as payload. +type Event struct { + ID uuid.UUID `json:"id,omitempty"` + EntityID uuid.UUID `json:"entity_id,omitempty"` + Type string `json:"type,omitempty"` +} + +const ( + // TypeAdd is an add event. + TypeAdd = "add" + + // TypeUpdate is an update event. + TypeUpdate = "update" + + // TypeDelete is a delete event. + TypeDelete = "delete" +) + +// NewAddEvent creates a new add event. +func NewAddEvent(entityID uuid.UUID) Event { + return Event{ + ID: uuid.New(), + EntityID: entityID, + Type: TypeAdd, + } +} + +// NewDeleteEvent creates a new delete event. +func NewDeleteEvent(entityID uuid.UUID) Event { + return Event{ + ID: uuid.New(), + EntityID: entityID, + Type: TypeDelete, + } +} + +// NewUpdateEvent creates a new update event. +func NewUpdateEvent(entityID uuid.UUID) Event { + return Event{ + ID: uuid.New(), + EntityID: entityID, + Type: TypeUpdate, + } +} diff --git a/controller/eventService/Service.go b/controller/eventService/Service.go new file mode 100644 index 0000000000000000000000000000000000000000..5c13102d4926f0246cf2c250fe009fa72de3ca9b --- /dev/null +++ b/controller/eventService/Service.go @@ -0,0 +1,82 @@ +package eventservice + +import ( + "encoding/json" + + "code.fbi.h-da.de/danet/gosdn/controller/config" + "code.fbi.h-da.de/danet/gosdn/controller/event" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + + interfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" + + amqp "github.com/rabbitmq/amqp091-go" +) + +// EventService is used to setup a connection to a broker and publish events to topics. +type EventService struct { + connection *amqp.Connection + channel *amqp.Channel +} + +// NewEventService creates a new connection to the broker and opens a channel for later usage. +func NewEventService() (interfaces.Service, error) { + // TODO: This is an fugly hack to mitigate that some tests are trying to connect to an actual broker. (staester) + if config.AMQPPrefix == "" { + return NewMockEventService(), nil + } + + conn, err := amqp.Dial(amqpURIBuilder(config.AMQPPrefix, config.AMQPUser, config.AMQPPassword, config.AMQPHost, config.AMQPPort)) + if err != nil { + return nil, &errors.ErrAMQPInitFail{Action: "failed to connect to RabbitMQ", Err: err} + } + + ch, err := conn.Channel() + if err != nil { + return nil, &errors.ErrAMQPInitFail{Action: "failed to open a channel", Err: err} + } + + return &EventService{ + connection: conn, + channel: ch, + }, nil +} + +// PublishEvent declares a queue and publishes events. +func (e *EventService) PublishEvent(topic string, event event.Event) error { + q, err := e.channel.QueueDeclare( + topic, // name + false, // durable + false, // delete when unused + false, // exclusive + false, // no-wait + nil, // arguments + ) + if err != nil { + return &errors.ErrAMQPInitFail{Action: "failed declaring queue", Err: err} + } + + eventBody, err := json.Marshal(event) + if err != nil { + return &errors.ErrCouldNotMarshall{Identifier: topic + " " + event.EntityID.String(), Type: event.Type, Err: err} + } + + err = e.channel.Publish( + "", // exchange + q.Name, // routing key + false, // mandatory + false, // immediate + amqp.Publishing{ + ContentType: "application/json", + Body: eventBody, + }) + if err != nil { + return &errors.ErrAMQPMessageFail{Action: "failed to publish message", Err: err} + } + + return nil +} + +// CloseConnection closes an exisiting connection. +func (e *EventService) CloseConnection() { + e.connection.Close() +} diff --git a/controller/eventService/utils.go b/controller/eventService/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..891142fef2e7bf809daca4885fb58921d4e4f9e3 --- /dev/null +++ b/controller/eventService/utils.go @@ -0,0 +1,35 @@ +package eventservice + +import ( + "fmt" + + "code.fbi.h-da.de/danet/gosdn/controller/event" + interfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" +) + +func amqpURIBuilder(prefix, user, pass, host, port string) string { + return fmt.Sprintf("%s%s:%s@%s:%s/", prefix, user, pass, host, port) +} + +// MockEventService is used to setup a connection to a broker and publish events to topics. +type MockEventService struct { + Queue map[string][]event.Event +} + +// NewMockEventService creates a new connection to the broker and opens a channel for later usage. +func NewMockEventService() interfaces.Service { + return &MockEventService{ + Queue: make(map[string][]event.Event), + } +} + +// PublishEvent declares a queue and publishes events. +func (e *MockEventService) PublishEvent(topic string, event event.Event) error { + e.Queue[topic] = append(e.Queue[topic], event) + + return nil +} + +// CloseConnection closes an exisiting connection. +func (e *MockEventService) CloseConnection() { +} diff --git a/controller/interfaces/event/service.go b/controller/interfaces/event/service.go new file mode 100644 index 0000000000000000000000000000000000000000..49cf8f6bc09711ecbfbd382b703a156bed675c6c --- /dev/null +++ b/controller/interfaces/event/service.go @@ -0,0 +1,9 @@ +package event + +import "code.fbi.h-da.de/danet/gosdn/controller/event" + +// Service is the event service +type Service interface { + PublishEvent(topic string, event event.Event) error + CloseConnection() +} diff --git a/controller/northbound/server/auth_interceptor_test.go b/controller/northbound/server/auth_interceptor_test.go index 0b4bb3757e4e00fbb5694ea1ccf0b90bd6a0e3d2..a1863957682e90d9068aee629233ea54e67f2bf7 100644 --- a/controller/northbound/server/auth_interceptor_test.go +++ b/controller/northbound/server/auth_interceptor_test.go @@ -9,6 +9,7 @@ import ( apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/nucleus" "code.fbi.h-da.de/danet/gosdn/controller/rbac" "google.golang.org/grpc" @@ -20,11 +21,13 @@ import ( func getTestAuthInterceptorServer(t *testing.T) (*AuthInterceptor, *User, *Role, *SbiServer) { initUUIDs(t) jwtManager := rbac.NewJWTManager("test", time.Minute) + eventService := eventservice.NewMockEventService() + userStore := rbac.NewMemoryUserStore() - userService := rbac.NewUserService(userStore) + userService := rbac.NewUserService(userStore, eventService) roleStore := rbac.NewMemoryRoleStore() - roleService := rbac.NewRoleService(roleStore) + roleService := rbac.NewRoleService(roleStore, eventService) mockPnd := getMockPnd(t) diff --git a/controller/northbound/server/auth_test.go b/controller/northbound/server/auth_test.go index fa3e0d9ef891be7ff9d4f961b7501fb0d3c931c6..8d916d80c92cc0e8eacc8149cf9d1bcbedcb4d82 100644 --- a/controller/northbound/server/auth_test.go +++ b/controller/northbound/server/auth_test.go @@ -7,18 +7,20 @@ import ( "time" apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/rbac" "google.golang.org/grpc/metadata" ) func getTestAuthServer(t *testing.T) *Auth { jwtManager := rbac.NewJWTManager("test", time.Minute) + eventService := eventservice.NewMockEventService() userStore := rbac.NewMemoryUserStore() - userService := rbac.NewUserService(userStore) + userService := rbac.NewUserService(userStore, eventService) roleStore := rbac.NewMemoryRoleStore() - roleService := rbac.NewRoleService(roleStore) + roleService := rbac.NewRoleService(roleStore, eventService) s := NewAuthServer(jwtManager, userService) err := clearAndCreateAuthTestSetup(s.userService, roleService) diff --git a/controller/northbound/server/role.go b/controller/northbound/server/role.go index 4cb05b6e16e885082f2e6a1b88466f000f2a7961..4141c6250d51b81984b652430e852f6f0a3a4eb4 100644 --- a/controller/northbound/server/role.go +++ b/controller/northbound/server/role.go @@ -2,6 +2,7 @@ package server import ( "context" + "fmt" "time" apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" @@ -59,7 +60,12 @@ func (r Role) GetRole(ctx context.Context, request *apb.GetRoleRequest) (*apb.Ge start := metrics.StartHook(labels, grpcRequestsTotal) defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds) - roleData, err := r.roleService.Get(store.Query{Name: request.RoleName}) + roleID, err := uuid.Parse(request.Id) + if err != nil { + return nil, fmt.Errorf("could not parse role uuid") + } + + roleData, err := r.roleService.Get(store.Query{Name: request.RoleName, ID: roleID}) if err != nil { return nil, err } diff --git a/controller/northbound/server/role_test.go b/controller/northbound/server/role_test.go index 1f75f2bc410eac0764b83aff2b33aaa7caf8d037..b9d0a62bbd992f585e08197930c61573eb440a0f 100644 --- a/controller/northbound/server/role_test.go +++ b/controller/northbound/server/role_test.go @@ -9,16 +9,19 @@ import ( apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" "code.fbi.h-da.de/danet/gosdn/controller/rbac" "github.com/google/uuid" + + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" ) func getTestRoleServer(t *testing.T) *Role { jwtManager := rbac.NewJWTManager("test", time.Second) + eventService := eventservice.NewMockEventService() userStore := rbac.NewMemoryUserStore() - userService := rbac.NewUserService(userStore) + userService := rbac.NewUserService(userStore, eventService) roleStore := rbac.NewMemoryRoleStore() - roleService := rbac.NewRoleService(roleStore) + roleService := rbac.NewRoleService(roleStore, eventService) s := NewRoleServer(jwtManager, roleService) err := clearAndCreateAuthTestSetup(userService, roleService) @@ -90,6 +93,7 @@ func TestRole_GetRole(t *testing.T) { ctx: context.TODO(), request: &apb.GetRoleRequest{ RoleName: "adminTestRole", + Id: uuid.Nil.String(), }, }, want: &apb.GetRoleResponse{ @@ -107,6 +111,7 @@ func TestRole_GetRole(t *testing.T) { ctx: context.TODO(), request: &apb.GetRoleRequest{ RoleName: "not role", + Id: uuid.Nil.String(), }, }, want: nil, diff --git a/controller/northbound/server/user.go b/controller/northbound/server/user.go index 23885863725164f7643f933203c60fdf3c57438d..2e0bfed22e52e23552021744a3421268ea3b1e00 100644 --- a/controller/northbound/server/user.go +++ b/controller/northbound/server/user.go @@ -3,6 +3,7 @@ package server import ( "context" "encoding/base64" + "fmt" "time" apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" @@ -82,7 +83,12 @@ func (u User) GetUser(ctx context.Context, request *apb.GetUserRequest) (*apb.Ge start := metrics.StartHook(labels, grpcRequestsTotal) defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds) - userData, err := u.userService.Get(store.Query{Name: request.Name}) + userID, err := uuid.Parse(request.Id) + if err != nil { + return nil, fmt.Errorf("could not parse user uuid") + } + + userData, err := u.userService.Get(store.Query{Name: request.Name, ID: userID}) if err != nil { return nil, err } diff --git a/controller/northbound/server/user_test.go b/controller/northbound/server/user_test.go index c84533635306c916a2f0011510cef50b0bc110de..83a9c081390a16e961aa7c6b7185f4e0c9de07bd 100644 --- a/controller/northbound/server/user_test.go +++ b/controller/northbound/server/user_test.go @@ -7,18 +7,20 @@ import ( "time" apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/rbac" "github.com/google/uuid" ) func getTestUserServer(t *testing.T) *User { jwtManager := rbac.NewJWTManager("test", time.Second) + eventService := eventservice.NewMockEventService() userStore := rbac.NewMemoryUserStore() - userService := rbac.NewUserService(userStore) + userService := rbac.NewUserService(userStore, eventService) roleStore := rbac.NewMemoryRoleStore() - roleService := rbac.NewRoleService(roleStore) + roleService := rbac.NewRoleService(roleStore, eventService) s := NewUserServer(jwtManager, userService) err := clearAndCreateAuthTestSetup(s.userService, roleService) @@ -93,6 +95,7 @@ func TestUser_GetUser(t *testing.T) { ctx: context.TODO(), request: &apb.GetUserRequest{ Name: "testAdmin", + Id: uuid.Nil.String(), }, }, want: &apb.GetUserResponse{Status: apb.Status_STATUS_OK, @@ -106,6 +109,7 @@ func TestUser_GetUser(t *testing.T) { ctx: context.TODO(), request: &apb.GetUserRequest{ Name: "nope", + Id: uuid.Nil.String(), }, }, want: nil, diff --git a/controller/nucleus/deviceService.go b/controller/nucleus/deviceService.go index c565274e85199fa1e0a383b395d7c7bd8f75cce4..618963bd7fff2988f169498975e029ffc03ec402 100644 --- a/controller/nucleus/deviceService.go +++ b/controller/nucleus/deviceService.go @@ -4,7 +4,9 @@ import ( "fmt" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" + "code.fbi.h-da.de/danet/gosdn/controller/event" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" @@ -13,18 +15,29 @@ import ( tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" ) +const ( + // DeviceEventTopic is the used topic for device related entity changes. + DeviceEventTopic = "device" +) + // DeviceService provides a device service implementation. // This services provides abstraction between the user (e.g a PND) and the matching store (e.g. deviceStore) type DeviceService struct { - deviceStore device.Store - sbiService southbound.Service + deviceStore device.Store + sbiService southbound.Service + eventService eventInterfaces.Service } // NewDeviceService creates a device service. -func NewDeviceService(deviceStore device.Store, sbiService southbound.Service) device.Service { +func NewDeviceService( + deviceStore device.Store, + sbiService southbound.Service, + eventService eventInterfaces.Service, +) device.Service { return &DeviceService{ - deviceStore: deviceStore, - sbiService: sbiService, + deviceStore: deviceStore, + sbiService: sbiService, + eventService: eventService, } } @@ -71,6 +84,8 @@ func (s *DeviceService) Add(deviceToAdd device.Device) error { return err } + s.eventService.PublishEvent(DeviceEventTopic, event.NewAddEvent(deviceToAdd.ID())) + return nil } @@ -81,6 +96,8 @@ func (s *DeviceService) Update(deviceToUpdate device.Device) error { return err } + s.eventService.PublishEvent(DeviceEventTopic, event.NewUpdateEvent(deviceToUpdate.ID())) + return nil } @@ -96,6 +113,9 @@ func (s *DeviceService) Delete(deviceToDelete device.Device) error { return err } } + + s.eventService.PublishEvent(DeviceEventTopic, event.NewDeleteEvent(deviceToDelete.ID())) + return nil } diff --git a/controller/nucleus/deviceService_test.go b/controller/nucleus/deviceService_test.go index b4ee228dd0d2475a84f47077fb9ad6627a95e5b5..0364bdf6601ec8229ddea8fe5c808043970a67fc 100644 --- a/controller/nucleus/deviceService_test.go +++ b/controller/nucleus/deviceService_test.go @@ -3,6 +3,7 @@ package nucleus import ( "testing" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/mocks" @@ -22,12 +23,14 @@ func getMockDevice(deviceID uuid.UUID, sbi southbound.SouthboundInterface) devic } func getDeviceTestStores(t *testing.T, deviceID uuid.UUID) (device.Service, southbound.Service, device.Device, southbound.SouthboundInterface) { + eventService := eventservice.NewMockEventService() sbiStore := NewMemorySbiStore() deviceStore := NewMemoryDeviceStore() - sbiService := NewSbiService(sbiStore) + sbiService := NewSbiService(sbiStore, eventService) deviceService := NewDeviceService( deviceStore, sbiService, + eventService, ) sbi, err := NewSBI(spb.Type_TYPE_OPENCONFIG) diff --git a/controller/nucleus/errors/errors.go b/controller/nucleus/errors/errors.go index db262595f4543e6c17108dc3953760a0b1b37d16..d155b6950024a187d6b97a467c3f282acc627de0 100644 --- a/controller/nucleus/errors/errors.go +++ b/controller/nucleus/errors/errors.go @@ -229,3 +229,25 @@ type ErrNoNewChanges struct { func (e ErrNoNewChanges) Error() string { return fmt.Sprintf("There are no changes between %v and %v", e.Original, e.Modified) } + +// ErrAMQPInitFail implements the Error interface and is called if there is any issue related to +// the setup of the event management. +type ErrAMQPInitFail struct { + Action string + Err error +} + +func (e ErrAMQPInitFail) Error() string { + return fmt.Sprintf("Action: %s, Internal error: %v", e.Action, e.Err) +} + +// ErrAMQPMessageFail implements the Error interface and is called if there is any issue with sending +// or receiving messages. +type ErrAMQPMessageFail struct { + Action string + Err error +} + +func (e ErrAMQPMessageFail) Error() string { + return fmt.Sprintf("Action: %s, Internal error: %v", e.Action, e.Err) +} diff --git a/controller/nucleus/initialise_test.go b/controller/nucleus/initialise_test.go index 958991f27435204cc0f977126d6195929f6e4fca..98bbe72e9ffec75644f03e9ebf3ae2061146bdd6 100644 --- a/controller/nucleus/initialise_test.go +++ b/controller/nucleus/initialise_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/store" @@ -153,12 +154,15 @@ func mockDevice() device.Device { } func newPnd() pndImplementation { + eventService := eventservice.NewMockEventService() + sbiStore := NewMemorySbiStore() deviceStore := NewMemoryDeviceStore() - sbiService := NewSbiService(sbiStore) + sbiService := NewSbiService(sbiStore, eventService) deviceService := NewDeviceService( deviceStore, sbiService, + eventService, ) return pndImplementation{ diff --git a/controller/nucleus/principalNetworkDomain.go b/controller/nucleus/principalNetworkDomain.go index b77652285933942dd8eaa2f9f47d843aa4e30b6d..337220f8709118e3fb36cf8f7af7c27316f7bad7 100644 --- a/controller/nucleus/principalNetworkDomain.go +++ b/controller/nucleus/principalNetworkDomain.go @@ -20,11 +20,13 @@ import ( ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "google.golang.org/grpc" "google.golang.org/protobuf/proto" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/change" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" @@ -53,14 +55,20 @@ func NewPND( c cpb.CsbiServiceClient, callback func(uuid.UUID, chan device.Details), ) (networkdomain.NetworkDomain, error) { + eventService, err := eventservice.NewEventService() + if err != nil { + return nil, err + } + sbiStore := NewSbiStore(id) deviceStore := NewDeviceStore(id) changeStore := store.NewChangeStore() - sbiService := NewSbiService(sbiStore) + sbiService := NewSbiService(sbiStore, eventService) deviceService := NewDeviceService( deviceStore, sbiService, + eventService, ) changeStore, ok := changeStoreMap[id] @@ -77,8 +85,9 @@ func NewPND( changes: changeStore, Id: id, - csbiClient: c, - callback: callback, + csbiClient: c, + callback: callback, + eventService: eventService, } existingSBIs, err := sbiStore.GetAll() @@ -106,8 +115,9 @@ type pndImplementation struct { //nolint Id uuid.UUID `json:"id,omitempty"` - csbiClient cpb.CsbiServiceClient - callback func(uuid.UUID, chan device.Details) + csbiClient cpb.CsbiServiceClient + callback func(uuid.UUID, chan device.Details) + eventService eventInterfaces.Service } func (pnd *pndImplementation) PendingChanges() []uuid.UUID { diff --git a/controller/nucleus/principalNetworkDomain_test.go b/controller/nucleus/principalNetworkDomain_test.go index ac28816d94daad4aaa19d070a72e79fdfb73af71..0cd9c3b14a2eb3e9089cf672d53300d0dcd3e0d1 100644 --- a/controller/nucleus/principalNetworkDomain_test.go +++ b/controller/nucleus/principalNetworkDomain_test.go @@ -10,6 +10,7 @@ import ( ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" @@ -366,10 +367,11 @@ func Test_pndImplementation_RemoveSbi(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + eventService := eventservice.NewMockEventService() sbiStore := NewMemorySbiStore() deviceStore := NewMemoryDeviceStore() - sbiService := NewSbiService(sbiStore) - deviceService := NewDeviceService(deviceStore, sbiService) + sbiService := NewSbiService(sbiStore, eventService) + deviceService := NewDeviceService(deviceStore, sbiService, eventService) pnd := &pndImplementation{ Name: "test-remove-sbi", @@ -431,10 +433,11 @@ func Test_pndImplementation_RemoveSbiWithAssociatedDevices(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + eventService := eventservice.NewMockEventService() sbiStore := NewMemorySbiStore() deviceStore := NewMemoryDeviceStore() - sbiService := NewSbiService(sbiStore) - deviceService := NewDeviceService(deviceStore, sbiService) + sbiService := NewSbiService(sbiStore, eventService) + deviceService := NewDeviceService(deviceStore, sbiService, eventService) pnd := &pndImplementation{ Name: "test-remove-sbi", diff --git a/controller/nucleus/sbiService.go b/controller/nucleus/sbiService.go index f00247f790a23add96cc00b0abc34008fe184326..c52a8f882138141c129aff11ae97d41fc9f84285 100644 --- a/controller/nucleus/sbiService.go +++ b/controller/nucleus/sbiService.go @@ -3,20 +3,29 @@ package nucleus import ( "os" + "code.fbi.h-da.de/danet/gosdn/controller/event" + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) +const ( + // SbiEventTopic is the used topic for sbi related entity changes. + SbiEventTopic = "sbi" +) + // SbiService provides a sbi service implementation. type SbiService struct { - sbiStore southbound.Store + sbiStore southbound.Store + eventService eventInterfaces.Service } // NewSbiService creates a sbi service. -func NewSbiService(sbiStore southbound.Store) southbound.Service { +func NewSbiService(sbiStore southbound.Store, eventService eventInterfaces.Service) southbound.Service { return &SbiService{ - sbiStore: sbiStore, + sbiStore: sbiStore, + eventService: eventService, } } @@ -63,6 +72,8 @@ func (s *SbiService) Add(sbiToAdd southbound.SouthboundInterface) error { return err } + s.eventService.PublishEvent(SbiEventTopic, event.NewAddEvent(sbiToAdd.ID())) + return nil } @@ -80,6 +91,8 @@ func (s *SbiService) Delete(sbiToDelete southbound.SouthboundInterface) error { } } + s.eventService.PublishEvent(SbiEventTopic, event.NewDeleteEvent(sbiToDelete.ID())) + return nil } diff --git a/controller/nucleus/sbiService_test.go b/controller/nucleus/sbiService_test.go index d26d6d72e1096326926d1033594645e14f09f603..13436109465a1b69bad2ee602b35c79970dc263f 100644 --- a/controller/nucleus/sbiService_test.go +++ b/controller/nucleus/sbiService_test.go @@ -3,6 +3,7 @@ package nucleus import ( "testing" + eventservice "code.fbi.h-da.de/danet/gosdn/controller/eventService" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" @@ -13,8 +14,9 @@ func getMockSbi(sbiID uuid.UUID) southbound.SouthboundInterface { } func getSbiTestStores(t *testing.T, sbiID uuid.UUID) (southbound.Service, southbound.SouthboundInterface) { + eventService := eventservice.NewMockEventService() sbiStore := NewMemorySbiStore() - sbiService := NewSbiService(sbiStore) + sbiService := NewSbiService(sbiStore, eventService) mockSbi := getMockSbi(sbiID) diff --git a/controller/rbac/rbacService.go b/controller/rbac/rbacService.go index 1cbb6f94d25638e0ab337ed06c8eeee1c60b5b26..72f9f9127563757ae4ff1c102a6cb72dd6b0bae9 100644 --- a/controller/rbac/rbacService.go +++ b/controller/rbac/rbacService.go @@ -1,21 +1,35 @@ package rbac import ( + "code.fbi.h-da.de/danet/gosdn/controller/event" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" + + eventInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/event" +) + +const ( + // UserEventTopic is the used topic for user related entity changes. + UserEventTopic = "user" + // RoleEventTopic is the used topic for role related entity changes. + RoleEventTopic = "role" ) -//UserService provides a user service implementation. +// UserService provides a user service implementation. type UserService struct { - userStore rbac.UserStore + userStore rbac.UserStore + eventService eventInterfaces.Service } // NewUserService creates a user service. -func NewUserService(userStore rbac.UserStore) rbac.UserService { - return &UserService{ - userStore: userStore, +func NewUserService(userStore rbac.UserStore, eventService eventInterfaces.Service) rbac.UserService { + userService := &UserService{ + userStore: userStore, + eventService: eventService, } + + return userService } // Add adds a user to the user store. @@ -25,6 +39,8 @@ func (s *UserService) Add(userToAdd rbac.User) error { return err } + s.eventService.PublishEvent(UserEventTopic, event.NewAddEvent(userToAdd.ID())) + return nil } @@ -35,6 +51,8 @@ func (s *UserService) Delete(userToDelete rbac.User) error { return err } + s.eventService.PublishEvent(UserEventTopic, event.NewDeleteEvent(userToDelete.ID())) + return nil } @@ -45,6 +63,8 @@ func (s *UserService) Update(userToUpdate rbac.User) error { return err } + s.eventService.PublishEvent(UserEventTopic, event.NewUpdateEvent(userToUpdate.ID())) + return nil } @@ -78,15 +98,17 @@ func (s *UserService) createUserFromStore(loadedUser rbac.LoadedUser) rbac.User return NewUser(uuid.MustParse(loadedUser.ID), loadedUser.UserName, loadedUser.Roles, loadedUser.Password, loadedUser.Token, loadedUser.Salt) } -//RoleService provides a role service implementation. +// RoleService provides a role service implementation. type RoleService struct { - roleStore rbac.RoleStore + roleStore rbac.RoleStore + eventService eventInterfaces.Service } // NewRoleService creates a role service. -func NewRoleService(roleStore rbac.RoleStore) rbac.RoleService { +func NewRoleService(roleStore rbac.RoleStore, eventService eventInterfaces.Service) rbac.RoleService { return &RoleService{ - roleStore: roleStore, + roleStore: roleStore, + eventService: eventService, } } @@ -97,6 +119,8 @@ func (s *RoleService) Add(roleToAdd rbac.Role) error { return err } + s.eventService.PublishEvent(RoleEventTopic, event.NewAddEvent(roleToAdd.ID())) + return nil } @@ -107,6 +131,8 @@ func (s *RoleService) Delete(roleToDelete rbac.Role) error { return err } + s.eventService.PublishEvent(RoleEventTopic, event.NewDeleteEvent(roleToDelete.ID())) + return nil } @@ -117,6 +143,8 @@ func (s *RoleService) Update(roleToUpdate rbac.Role) error { return err } + s.eventService.PublishEvent(RoleEventTopic, event.NewUpdateEvent(roleToUpdate.ID())) + return nil } diff --git a/docker-compose.yml b/docker-compose.yml index 1dedf6ff2bc85191d4165e89297a075759baf707..57be1632040f72bf1bd6982e446cae067c423109 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,3 +16,10 @@ services: environment: ME_CONFIG_MONGODB_ADMINUSERNAME: root ME_CONFIG_MONGODB_ADMINPASSWORD: example + + + rabbitmq: + image: rabbitmq:3-management + ports: + - 127.0.0.1:5672:5672 + - 127.0.0.1:15672:15672 diff --git a/go.mod b/go.mod index 0b0117e5f1f7ede6ddca58d2840cb053b961eac1..0686446d582f931ff515eb83301c4b85c6ee9c76 100644 --- a/go.mod +++ b/go.mod @@ -89,6 +89,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.30.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/rabbitmq/amqp091-go v1.3.4 github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -98,7 +99,7 @@ require ( github.com/xdg-go/stringprep v1.0.2 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.opencensus.io v0.23.0 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect + golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect diff --git a/go.sum b/go.sum index 770a1e02db29e35de2b863a66ff3546716d7da91..593d67fdefb443b18522fe10c1dacd3b0ccb5048 100644 --- a/go.sum +++ b/go.sum @@ -837,6 +837,8 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/pterm/pterm v0.12.41 h1:e2BRfFo1H9nL8GY0S3ImbZqfZ/YimOk9XtkhoobKJVs= github.com/pterm/pterm v0.12.41/go.mod h1:LW/G4J2A42XlTaPTAGRPvbBfF4UXvHWhC6SN7ueU4jU= +github.com/rabbitmq/amqp091-go v1.3.4 h1:tXuIslN1nhDqs2t6Jrz3BAoqvt4qIZzxvdbdcxWtHYU= +github.com/rabbitmq/amqp091-go v1.3.4/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=