Expose reservation info in application rest endpoints (#437)

Closes: #437

Signed-off-by: Manikandan R <manirajv06@gmail.com>
diff --git a/pkg/scheduler/objects/application.go b/pkg/scheduler/objects/application.go
index 1a31a37..2f497c5 100644
--- a/pkg/scheduler/objects/application.go
+++ b/pkg/scheduler/objects/application.go
@@ -646,8 +646,8 @@
 	return deltaPendingResource, nil
 }
 
-// Return if the application has any reservations.
-func (sa *Application) hasReserved() bool {
+// HasReserved returns true if the application has any reservations.
+func (sa *Application) HasReserved() bool {
 	sa.RLock()
 	defer sa.RUnlock()
 	return len(sa.reservations) > 0
diff --git a/pkg/scheduler/objects/application_test.go b/pkg/scheduler/objects/application_test.go
index 8c5d12f..b79785a 100644
--- a/pkg/scheduler/objects/application_test.go
+++ b/pkg/scheduler/objects/application_test.go
@@ -94,7 +94,7 @@
 	if app == nil || app.ApplicationID != appID1 {
 		t.Fatalf("app create failed which should not have %v", app)
 	}
-	if app.hasReserved() {
+	if app.HasReserved() {
 		t.Fatal("new app should not have reservations")
 	}
 	if app.IsReservedOnNode("") {
@@ -139,13 +139,13 @@
 	if app.IsReservedOnNode("unknown") {
 		t.Error("app should not have reservations for unknown node")
 	}
-	if app.hasReserved() && !app.IsReservedOnNode(nodeID1) {
+	if app.HasReserved() && !app.IsReservedOnNode(nodeID1) {
 		t.Errorf("app should have reservations for node %s", nodeID1)
 	}
 
 	// node name similarity check: chop of the last char to make sure we check the full name
 	similar := nodeID1[:len(nodeID1)-1]
-	if app.hasReserved() && app.IsReservedOnNode(similar) {
+	if app.HasReserved() && app.IsReservedOnNode(similar) {
 		t.Errorf("similar app should not have reservations for node %s", similar)
 	}
 
@@ -195,7 +195,7 @@
 	if app == nil || app.ApplicationID != appID1 {
 		t.Fatalf("app create failed which should not have %v", app)
 	}
-	if app.hasReserved() {
+	if app.HasReserved() {
 		t.Fatal("new app should not have reservations")
 	}
 	if len(app.GetAskReservations("")) != 0 {
@@ -249,7 +249,7 @@
 	}
 	// clean up all asks and reservations
 	reservedAsks := app.RemoveAllocationAsk("")
-	if app.hasReserved() || node1.IsReserved() || node2.IsReserved() || reservedAsks != 2 {
+	if app.HasReserved() || node1.IsReserved() || node2.IsReserved() || reservedAsks != 2 {
 		t.Errorf("ask removal did not clean up all reservations, reserved released = %d", reservedAsks)
 	}
 }
@@ -504,7 +504,7 @@
 	if !resources.Equals(res, delta) || reservedAsks != 1 {
 		t.Errorf("resource ask2 should have been removed from app: %v, (reserved released = %d)", delta, reservedAsks)
 	}
-	if app.hasReserved() || node.IsReserved() {
+	if app.HasReserved() || node.IsReserved() {
 		t.Fatal("app and node should not have reservations")
 	}
 
@@ -529,7 +529,7 @@
 		t.Errorf("resource ask2 should have been removed from app: %v, (reserved released = %d)", delta, reservedAsks)
 	}
 	// app reservation is removed even though the node removal failed
-	if app.hasReserved() || node.IsReserved() {
+	if app.HasReserved() || node.IsReserved() {
 		t.Fatal("app and node should not have reservations")
 	}
 	// add a new reservation: use the existing ask1
@@ -541,7 +541,7 @@
 		t.Errorf("all resource asks should have been removed from app: %v, (reserved released = %d)", app.GetPendingResource(), reservedAsks)
 	}
 	// app reservation is removed due to ask removal
-	if app.hasReserved() || node.IsReserved() {
+	if app.HasReserved() || node.IsReserved() {
 		t.Fatal("app and node should not have reservations")
 	}
 }
diff --git a/pkg/webservice/dao/application_info.go b/pkg/webservice/dao/application_info.go
index 5190dac..6887522 100644
--- a/pkg/webservice/dao/application_info.go
+++ b/pkg/webservice/dao/application_info.go
@@ -37,6 +37,8 @@
 	RejectedMessage string                 `json:"rejectedMessage"`
 	StateLog        []StateDAOInfo         `json:"stateLog"`
 	PlaceholderData []PlaceholderDAOInfo   `json:"placeholderData"`
+	HasReserved     bool                   `json:"hasReserved"`
+	Reservations    []string               `json:"reservations"`
 }
 
 type StateDAOInfo struct {
diff --git a/pkg/webservice/handlers.go b/pkg/webservice/handlers.go
index cb2109d..324bf59 100644
--- a/pkg/webservice/handlers.go
+++ b/pkg/webservice/handlers.go
@@ -245,6 +245,8 @@
 		RejectedMessage: app.GetRejectedMessage(),
 		PlaceholderData: placeholderInfo,
 		StateLog:        stateLogInfo,
+		HasReserved:     app.HasReserved(),
+		Reservations:    app.GetReservations(),
 	}
 }
 
diff --git a/pkg/webservice/handlers_test.go b/pkg/webservice/handlers_test.go
index c12de9c..225232e 100644
--- a/pkg/webservice/handlers_test.go
+++ b/pkg/webservice/handlers_test.go
@@ -1074,6 +1074,12 @@
 	assert.NilError(t, err, "failed to unmarshal applications dao response from response body: %s", string(resp.outputBytes))
 	assert.Equal(t, len(appsDao), 2)
 
+	if !appsDao[0].HasReserved {
+		assert.Equal(t, len(appsDao[0].Reservations), 0)
+	} else {
+		assert.Check(t, len(appsDao[0].Reservations) > 0, "app should have at least 1 reservation")
+	}
+
 	// check PlaceholderData
 	assert.Equal(t, len(appsDao[0].PlaceholderData), 1)
 	assert.Equal(t, appsDao[0].PlaceholderData[0].TaskGroupName, tg)
@@ -1158,6 +1164,12 @@
 	err = json.Unmarshal(resp.outputBytes, &appsDao)
 	assert.NilError(t, err, "failed to unmarshal applications dao response from response body: %s", string(resp.outputBytes))
 
+	if !appsDao.HasReserved {
+		assert.Equal(t, len(appsDao.Reservations), 0)
+	} else {
+		assert.Check(t, len(appsDao.Reservations) > 0, "app should have at least 1 reservation")
+	}
+
 	// test nonexistent partition
 	var req1 *http.Request
 	req1, err = http.NewRequest("GET", "/ws/v1/partition/default/queue/root.default/application/app-1", strings.NewReader(""))