diff --git a/controller/topology/links/link.go b/controller/topology/links/link.go new file mode 100644 index 0000000000000000000000000000000000000000..0d0632493fbb3256114cc2b76ceb93ebf87cc118 --- /dev/null +++ b/controller/topology/links/link.go @@ -0,0 +1,17 @@ +package links + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/topology/nodes" + "code.fbi.h-da.de/danet/gosdn/controller/topology/ports" + "github.com/google/uuid" +) + +// Link is a representation of a physical or virtual link between two nodes and their ports +type Link struct { + ID uuid.UUID `bson:"_id"` + Name string `bson:"name,omitempty"` + SourceNode nodes.Node `bson:"source_node,omitempty"` + TargetNode nodes.Node `bson:"target_node,omitempty"` + SourcePort ports.Port `bson:"source_port,omitempty"` + TargetPort ports.Port `bson:"target_port,omitempty"` +} diff --git a/controller/topology/nodes/node.go b/controller/topology/nodes/node.go new file mode 100644 index 0000000000000000000000000000000000000000..88d250aebd0c94a28a24604aa2efb3882c833da5 --- /dev/null +++ b/controller/topology/nodes/node.go @@ -0,0 +1,9 @@ +package nodes + +import "github.com/google/uuid" + +// Node is a representation of a network element +type Node struct { + ID uuid.UUID `bson:"_id"` + Name string `bson:"name"` +} diff --git a/controller/topology/nodes/nodeService.go b/controller/topology/nodes/nodeService.go new file mode 100644 index 0000000000000000000000000000000000000000..c540f85c6f7fea907f60ca960ddcd77b077147bf --- /dev/null +++ b/controller/topology/nodes/nodeService.go @@ -0,0 +1,86 @@ +package nodes + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" +) + +// Service defines a interface for a NodeService +type Service interface { + EnsureExists(Node) (Node, error) + Update(Node) error + Delete(Node) error + Get(store.Query) (Node, error) + GetAll() ([]Node, error) +} + +// NodeService is a NodeService +type NodeService struct { + store Store +} + +// NewNodeService creates a NodeService +func NewNodeService(store Store) Service { + return &NodeService{ + store: store, + } +} + +// EnsureExists either creates a new node or returns an already existing node +func (p *NodeService) EnsureExists(node Node) (Node, error) { + if node.ID == uuid.Nil { + node.ID = uuid.New() + err := p.store.Add(node) + if err != nil { + return node, err + } + + return node, nil + } + + node, err := p.Get(store.Query{ID: node.ID}) + if err != nil { + return node, err + } + + return node, nil +} + +// Update updates an existing node +func (p *NodeService) Update(node Node) error { + err := p.store.Update(node) + if err != nil { + return err + } + + return nil +} + +// Delete deletes a node +func (p *NodeService) Delete(node Node) error { + err := p.store.Delete(node) + if err != nil { + return err + } + + return nil +} + +// Get gets a node +func (p *NodeService) Get(query store.Query) (Node, error) { + node, err := p.store.Get(query) + if err != nil { + return node, err + } + + return node, nil +} + +// GetAll gets all existing nodes +func (p *NodeService) GetAll() ([]Node, error) { + nodes, err := p.store.GetAll() + if err != nil { + return nodes, err + } + return nodes, nil +} diff --git a/controller/topology/nodes/nodeStore.go b/controller/topology/nodes/nodeStore.go new file mode 100644 index 0000000000000000000000000000000000000000..517c4f74db054d650718c8df0aacfc696c733b55 --- /dev/null +++ b/controller/topology/nodes/nodeStore.go @@ -0,0 +1,192 @@ +package nodes + +import ( + "fmt" + "log" + + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Store defines a NodeStore interface +type Store interface { + Add(Node) error + Update(Node) error + Delete(Node) error + Get(store.Query) (Node, error) + GetAll() ([]Node, error) +} + +// DatabaseNodeStore is a database store for nodes +type DatabaseNodeStore struct { + storeName string +} + +// NewDatabaseNodeStore returns a NodeStore +func NewDatabaseNodeStore() Store { + return &DatabaseNodeStore{ + storeName: fmt.Sprintf("node-store.json"), + } +} + +// Get takes a nodes's UUID or name and returns the nodes. +func (s *DatabaseNodeStore) Get(query store.Query) (Node, error) { + var loadedNode Node + + if query.ID.String() != "" { + loadedNode, err := s.getByID(query.ID) + if err != nil { + return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedNode, nil + } + + loadedNode, err := s.getByName(query.Name) + if err != nil { + return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedNode, nil +} + +func (s *DatabaseNodeStore) getByID(idOfLink uuid.UUID) (Node, error) { + var loadedNode Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfLink.String()}}) + if result == nil { + return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + err := result.Decode(&loadedNode) + if err != nil { + log.Printf("Failed marshalling %v", err) + return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedNode, nil +} + +func (s *DatabaseNodeStore) getByName(nameOfDevice string) (Node, error) { + var loadedNode Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfDevice}}) + if result == nil { + return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + err := result.Decode(&loadedNode) + if err != nil { + log.Printf("Failed marshalling %v", err) + return loadedNode, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedNode, nil +} + +// GetAll returns all stored nodes. +func (s *DatabaseNodeStore) GetAll() ([]Node, error) { + var loadedNode []Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + + cursor, err := collection.Find(ctx, bson.D{}) + if err != nil { + return []Node{}, err + } + defer cursor.Close(ctx) + + err = cursor.All(ctx, &loadedNode) + if err != nil { + log.Printf("Failed marshalling %v", err) + + return loadedNode, errors.ErrCouldNotMarshall{StoreName: s.storeName} + } + + return loadedNode, nil +} + +// Add adds a node to the node store. +func (s *DatabaseNodeStore) Add(node Node) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + _, err := client.Database(database.DatabaseName). + Collection(s.storeName). + InsertOne(ctx, node) + if err != nil { + log.Printf("Could not add Node: %v", err) + return errors.ErrCouldNotCreate{StoreName: s.storeName} + } + + return nil +} + +// Update updates a existing node. +func (s *DatabaseNodeStore) Update(node Node) error { + var updatedLoadedNodes Node + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + update := bson.D{primitive.E{Key: "$set", Value: node}} + + upsert := false + after := options.After + opt := options.FindOneAndUpdateOptions{ + Upsert: &upsert, + ReturnDocument: &after, + } + + err := client.Database(database.DatabaseName). + Collection(s.storeName). + FindOneAndUpdate( + ctx, bson.M{"_id": node.ID.String()}, update, &opt). + Decode(&updatedLoadedNodes) + if err != nil { + log.Printf("Could not update Node: %v", err) + + return errors.ErrCouldNotUpdate{StoreName: s.storeName} + } + + return nil +} + +// Delete deletes a node from the node store. +func (s *DatabaseNodeStore) Delete(node Node) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + _, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: node.ID.String()}}) + if err != nil { + return err + } + + return nil +} diff --git a/controller/topology/ports/port.go b/controller/topology/ports/port.go new file mode 100644 index 0000000000000000000000000000000000000000..841ae9b903aeaae399e8a8288712aa32eb7d8aa2 --- /dev/null +++ b/controller/topology/ports/port.go @@ -0,0 +1,9 @@ +package ports + +import "github.com/google/uuid" + +// Port is a representation of physical port on a network element +type Port struct { + ID uuid.UUID `bson:"_id"` + Name string `bson:"name,omitempty"` +} diff --git a/controller/topology/ports/portService.go b/controller/topology/ports/portService.go new file mode 100644 index 0000000000000000000000000000000000000000..fadf319349df12714c5eefee9dff176dc596ade4 --- /dev/null +++ b/controller/topology/ports/portService.go @@ -0,0 +1,86 @@ +package ports + +import ( + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" +) + +// Service defines an interface for a PortService +type Service interface { + EnsureExists(Port) (Port, error) + Update(Port) error + Delete(Port) error + Get(store.Query) (Port, error) + GetAll() ([]Port, error) +} + +// PortService is a service for ports +type PortService struct { + store Store +} + +// NewPortService creates a new PortService +func NewPortService(store Store) Service { + return &PortService{ + store: store, + } +} + +// EnsureExists either creates a new port or returns an already existing port +func (p *PortService) EnsureExists(port Port) (Port, error) { + if port.ID == uuid.Nil { + port.ID = uuid.New() + err := p.store.Add(port) + if err != nil { + return port, err + } + + return port, nil + } + + port, err := p.Get(store.Query{ID: port.ID}) + if err != nil { + return port, err + } + + return port, nil +} + +// Update updates an existing port +func (p *PortService) Update(port Port) error { + err := p.store.Update(port) + if err != nil { + return err + } + + return nil +} + +// Delete deletes a port +func (p *PortService) Delete(port Port) error { + err := p.store.Delete(port) + if err != nil { + return err + } + + return nil +} + +// Get gets a port +func (p *PortService) Get(query store.Query) (Port, error) { + port, err := p.store.Get(query) + if err != nil { + return port, err + } + + return port, nil +} + +// GetAll gets all existing ports +func (p *PortService) GetAll() ([]Port, error) { + nodes, err := p.store.GetAll() + if err != nil { + return nodes, err + } + return nodes, nil +} diff --git a/controller/topology/ports/portStore.go b/controller/topology/ports/portStore.go new file mode 100644 index 0000000000000000000000000000000000000000..7b2b6bc43c901d68f1752a7e27ccd3be3f597740 --- /dev/null +++ b/controller/topology/ports/portStore.go @@ -0,0 +1,193 @@ +package ports + +import ( + "fmt" + "log" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Store defines a PortStore interface +type Store interface { + Add(Port) error + Update(Port) error + Delete(Port) error + Get(store.Query) (Port, error) + GetAll() ([]Port, error) +} + +// DatabasePortStore is a database store for ports +type DatabasePortStore struct { + storeName string +} + +// NewDatabasePortStore returns a PortStore +func NewDatabasePortStore() Store { + return &DatabasePortStore{ + storeName: fmt.Sprintf("port-store.json"), + } +} + +// Get takes a Ports's UUID or name and returns the port. +func (s *DatabasePortStore) Get(query store.Query) (Port, error) { + var loadedPort Port + + if query.ID.String() != "" { + loadedPort, err := s.getByID(query.ID) + if err != nil { + return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedPort, nil + } + + loadedPort, err := s.getByName(query.Name) + if err != nil { + return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedPort, nil +} + +func (s *DatabasePortStore) getByID(idOfLink uuid.UUID) (Port, error) { + var loadedPort Port + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfLink.String()}}) + if result == nil { + return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + err := result.Decode(&loadedPort) + if err != nil { + log.Printf("Failed marshalling %v", err) + return loadedPort, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedPort, nil +} + +func (s *DatabasePortStore) getByName(nameOfDevice string) (Port, error) { + var loadedDevice Port + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfDevice}}) + if result == nil { + return loadedDevice, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + err := result.Decode(&loadedDevice) + if err != nil { + log.Printf("Failed marshalling %v", err) + return loadedDevice, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedDevice, nil +} + +// GetAll returns all stored ports. +func (s *DatabasePortStore) GetAll() ([]Port, error) { + var loadedPort []Port + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + + cursor, err := collection.Find(ctx, bson.D{}) + if err != nil { + return []Port{}, err + } + defer cursor.Close(ctx) + + err = cursor.All(ctx, &loadedPort) + if err != nil { + log.Printf("Failed marshalling %v", err) + + return loadedPort, errors.ErrCouldNotMarshall{StoreName: s.storeName} + } + + return loadedPort, nil +} + +// Add adds a port to the port store. +func (s *DatabasePortStore) Add(port Port) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + _, err := client.Database(database.DatabaseName). + Collection(s.storeName). + InsertOne(ctx, port) + if err != nil { + log.Printf("Could not add Port: %v", err) + return errors.ErrCouldNotCreate{StoreName: s.storeName} + } + + return nil +} + +// Update updates a existing port. +func (s *DatabasePortStore) Update(port Port) error { + var updatedLoadedDevice device.LoadedDevice + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + update := bson.D{primitive.E{Key: "$set", Value: port}} + + upsert := false + after := options.After + opt := options.FindOneAndUpdateOptions{ + Upsert: &upsert, + ReturnDocument: &after, + } + + err := client.Database(database.DatabaseName). + Collection(s.storeName). + FindOneAndUpdate( + ctx, bson.M{"_id": port.ID.String()}, update, &opt). + Decode(&updatedLoadedDevice) + if err != nil { + log.Printf("Could not update Port: %v", err) + + return errors.ErrCouldNotUpdate{StoreName: s.storeName} + } + + return nil +} + +// Delete deletes a port from the port store. +func (s *DatabasePortStore) Delete(port Port) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + _, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: port.ID.String()}}) + if err != nil { + return err + } + + return nil +} diff --git a/controller/topology/topology.go b/controller/topology/topology.go new file mode 100644 index 0000000000000000000000000000000000000000..18f8a87cb9ac71f1df15f450796b8e42ba62059d --- /dev/null +++ b/controller/topology/topology.go @@ -0,0 +1 @@ +package topology diff --git a/controller/topology/topologyService.go b/controller/topology/topologyService.go new file mode 100644 index 0000000000000000000000000000000000000000..a017db246ce5f3425e4ad5221da53b6e9bd33910 --- /dev/null +++ b/controller/topology/topologyService.go @@ -0,0 +1,62 @@ +package topology + +import "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + +// TService defines an interface for a TopologyService +type TService interface { + AddLink(links.Link) error + UpdateLink(links.Link) error + DeleteLink(links.Link) error + GetAll() ([]links.Link, error) +} + +// Service is a service for ports +type Service struct { + store Store +} + +// NewTopologyService creates a new TopologyService +func NewTopologyService(store Store) TService { + return &Service{ + store: store, + } +} + +// AddLink adds a new link to the topology +func (t *Service) AddLink(link links.Link) error { + err := t.store.AddLink(link) + if err != nil { + return err + } + + return nil +} + +// UpdateLink updates an existing link +func (t *Service) UpdateLink(link links.Link) error { + err := t.store.UpdateLink(link) + if err != nil { + return err + } + + return nil +} + +// DeleteLink deletes a link +func (t *Service) DeleteLink(link links.Link) error { + err := t.store.DeleteLink(link) + if err != nil { + return err + } + + return nil +} + +// GetAll returns the current topology +func (t *Service) GetAll() ([]links.Link, error) { + topo, err := t.store.GetAll() + if err != nil { + return topo, err + } + return topo, nil +} diff --git a/controller/topology/topologyStore.go b/controller/topology/topologyStore.go new file mode 100644 index 0000000000000000000000000000000000000000..363985b44529d4a37d09de99e513d90ac0ad43a0 --- /dev/null +++ b/controller/topology/topologyStore.go @@ -0,0 +1,193 @@ +package topology + +import ( + "fmt" + "log" + + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/database" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + "code.fbi.h-da.de/danet/gosdn/controller/store" + "code.fbi.h-da.de/danet/gosdn/controller/topology/links" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Store defines a Topology store interface +type Store interface { + AddLink(links.Link) error + UpdateLink(links.Link) error + DeleteLink(links.Link) error + Get(store.Query) (links.Link, error) + GetAll() ([]links.Link, error) +} + +// DatabaseTopologyStore is a database store for the topology +type DatabaseTopologyStore struct { + storeName string +} + +// NewDatabaseTopologyStore returns a TopologyStore +func NewDatabaseTopologyStore() Store { + return &DatabaseTopologyStore{ + storeName: fmt.Sprintf("topology-store.json"), + } +} + +// Get takes a link's UUID or name and returns the link. +func (s *DatabaseTopologyStore) Get(query store.Query) (links.Link, error) { + var loadedTopology links.Link + + if query.ID.String() != "" { + loadedDevice, err := s.getByID(query.ID) + if err != nil { + return loadedDevice, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedDevice, nil + } + + loadedTopology, err := s.getByName(query.Name) + if err != nil { + return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedTopology, nil +} + +func (s *DatabaseTopologyStore) getByID(idOfLink uuid.UUID) (links.Link, error) { + var loadedTopology links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "_id", Value: idOfLink.String()}}) + if result == nil { + return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + err := result.Decode(&loadedTopology) + if err != nil { + log.Printf("Failed marshalling %v", err) + return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedTopology, nil +} + +func (s *DatabaseTopologyStore) getByName(nameOfDevice string) (links.Link, error) { + var loadedTopology links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + result := collection.FindOne(ctx, bson.D{primitive.E{Key: "name", Value: nameOfDevice}}) + if result == nil { + return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + err := result.Decode(&loadedTopology) + if err != nil { + log.Printf("Failed marshalling %v", err) + return loadedTopology, errors.ErrCouldNotFind{StoreName: s.storeName} + } + + return loadedTopology, nil +} + +// GetAll returns all stored links. +func (s *DatabaseTopologyStore) GetAll() ([]links.Link, error) { + var loadedTopology []links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + + cursor, err := collection.Find(ctx, bson.D{}) + if err != nil { + return loadedTopology, err + } + defer cursor.Close(ctx) + + err = cursor.All(ctx, &loadedTopology) + if err != nil { + log.Printf("Failed marshalling %v", err) + + return loadedTopology, errors.ErrCouldNotMarshall{StoreName: s.storeName} + } + + return loadedTopology, nil +} + +// AddLink adds a link to the link store. +func (s *DatabaseTopologyStore) AddLink(link links.Link) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + _, err := client.Database(database.DatabaseName). + Collection(s.storeName). + InsertOne(ctx, link) + if err != nil { + log.Printf("Could not add Link: %v", err) + return errors.ErrCouldNotCreate{StoreName: s.storeName} + } + + return nil +} + +// UpdateLink updates a existing link. +func (s *DatabaseTopologyStore) UpdateLink(linkToUpdate links.Link) error { + var updatedLink links.Link + + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + update := bson.D{primitive.E{Key: "$set", Value: linkToUpdate}} + + upsert := false + after := options.After + opt := options.FindOneAndUpdateOptions{ + Upsert: &upsert, + ReturnDocument: &after, + } + + err := client.Database(database.DatabaseName). + Collection(s.storeName). + FindOneAndUpdate( + ctx, bson.M{"_id": linkToUpdate.ID.String()}, update, &opt). + Decode(&updatedLink) + if err != nil { + log.Printf("Could not update link: %v", err) + + return errors.ErrCouldNotUpdate{StoreName: s.storeName} + } + + return nil +} + +// DeleteLink deletes a link from the link store. +func (s *DatabaseTopologyStore) DeleteLink(linkToDelete links.Link) error { + client, ctx, cancel := database.GetMongoConnection() + defer cancel() + defer client.Disconnect(ctx) + + db := client.Database(database.DatabaseName) + collection := db.Collection(s.storeName) + _, err := collection.DeleteOne(ctx, bson.D{primitive.E{Key: linkToDelete.ID.String()}}) + if err != nil { + return err + } + + return nil +}