Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions api/reservations.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func reservationClusterHandler() func(c *fiber.Ctx) error {
// @Produce json
// @Success 201 {object} db.Reservation
// @Failure 404 {object} string
// @Failure 400 {object} string
// @Failure 500 {object} string
// @Router /api/reservations/ [post]
func reservationCreateHandler() func(c *fiber.Ctx) error {
Expand All @@ -135,6 +136,19 @@ func reservationCreateHandler() func(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

if r.StartTime > r.EndTime {
return fiber.NewError(fiber.StatusBadRequest, "Start-date cannot be later than end-date")
}

b, err := isAvailable(*r)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}

if !b {
return fiber.NewError(fiber.StatusBadRequest, "Not enough nodes available to make reservation")
}

reservation, err := db.AddReservation(*r)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
Expand Down Expand Up @@ -172,6 +186,19 @@ func reservationUpdateHandler() func(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}

if r.StartTime > r.EndTime {
return fiber.NewError(fiber.StatusBadRequest, "Start-date cannot be later than end-date")
}

b, err := isAvailable(*r)
if err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}

if !b {
return fiber.NewError(fiber.StatusBadRequest, "Not enough nodes available to make reservation")
}

reservation, err := db.EditReservation(id, *r)
if err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
Expand Down Expand Up @@ -214,3 +241,39 @@ func reservationDeleteHandler() func(c *fiber.Ctx) error {
return c.SendStatus(204)
}
}

func isAvailable(reservation db.Reservation) (bool, error) {

cluster, err := db.GetCResourceById(reservation.ClusterID)
if err != nil {
return false, err
}

reservations, err := db.GetReservationsByClusterId(reservation.ClusterID)
if err != nil {
return false, err
}

clusterReservations := make(map[int64]int64) //Maps startTime to nodes used

for _, r := range reservations {
if r.ID == reservation.ID { //If reservation already exists, skip this reservation
continue
}
for start := reservation.StartTime; start <= reservation.EndTime; start += 86400 {
if start < r.EndTime {
clusterReservations[start] += r.Nodes
} else {
clusterReservations[start] += 0
}
}
}

for _, n := range clusterReservations {
if cluster.Nodes-n < reservation.Nodes {
return false, nil
}
}

return true, nil
}
44 changes: 41 additions & 3 deletions tests/reservations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ func Test_reservations(t *testing.T) {
createdCResource := executeTestReq[db.CResource](t, app, createOnecResourceTest, tokenStr)

foosReservation := db.Reservation{
ClusterID: createdCResource.ID,
Nodes: 5,
UserID: createdUser.ID,
StartTime: start.Unix(),
EndTime: end.Unix(),
IsExpired: false,
}

endBeforeStartReservation := db.Reservation{
ClusterID: createdCResource.ID,
Nodes: 5,
UserID: createdUser.ID,
StartTime: end.Unix(),
EndTime: start.Unix(),
IsExpired: false,
}

insufficienNodesReservation := db.Reservation{
ClusterID: createdCResource.ID,
Nodes: 10,
UserID: createdUser.ID,
Expand All @@ -52,11 +70,11 @@ func Test_reservations(t *testing.T) {
}

start = start.Truncate(time.Hour * 24)
end = end.Truncate(time.Hour * 72)
end = end.Truncate(time.Hour * 24)

expiredReservation := db.Reservation{
ClusterID: createdCResource.ID,
Nodes: 10,
Nodes: 4,
UserID: createdUser.ID,
StartTime: start.Unix(),
EndTime: end.Unix(),
Expand Down Expand Up @@ -115,7 +133,7 @@ func Test_reservations(t *testing.T) {
executeTestReq[db.Reservation](t, app, getOneTest, tokenStr)

editedReservation := createdReservation
editedReservation.Nodes = 15
editedReservation.Nodes = 6
editOneTest := TestReq{
description: "Edit one reservation (expect 200)",
expectedCode: 200,
Expand All @@ -126,6 +144,26 @@ func Test_reservations(t *testing.T) {
}
executeTestReq[db.Reservation](t, app, editOneTest, tokenStr)

endBeforeStartTest := TestReq{
description: "Add one reservation where end < start",
expectedCode: 400,
route: "/api/reservations/",
method: "POST",
body: endBeforeStartReservation,
expectedData: nil,
}
_ = executeTestReq[db.Reservation](t, app, endBeforeStartTest, tokenStr)

insufficienNodesTest := TestReq{
description: "Add one reservation with more nodes than the cluster has available",
expectedCode: 400,
route: "/api/reservations",
method: "POST",
body: insufficienNodesReservation,
expectedData: nil,
}
_ = executeTestReq[db.Reservation](t, app, insufficienNodesTest, tokenStr)

deleteOneTest := TestReq{
description: "Delete one reservation (expect 204)",
expectedCode: 204,
Expand Down