mirror of
https://github.com/cirruslabs/orchard.git
synced 2026-10-21 10:45:25 +02:00
Add pagination support for listing VM events (#386)
* Add pagination support for listing VM events Introduced a paginated event listing API, added support for pagination parameters in the request, and included cursor-based navigation using headers. Relevant tests and Badger store implementations were updated to support the new logic. * Remove support for ordering VM events Dropped `ListOrder` type, `order` query parameter, and related logic for ordering VM events. Updated tests, API schema, and Badger store to reflect the removal. * Remove invalid VM events ordering test Deleted a test case for invalid VM events ordering since the `order` query parameter and related functionality have been removed. * Add support for ordering VM events Implemented `order` query parameter for specifying sort order (ascending/descending) of VM events. Updated API schema, Badger store, and added related tests. * Add support for limiting and ordering VM logs Introduced `--limit` and `--order` flags for controlling the number of log lines and their sort order (ascending/descending). Updated API client to handle new options. * Update internal/controller/store/badger/badger_events.go Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com> * fix: address PR review feedback - switch logs CLI to --tail with desc ordering - reuse ParseLogsOrder in controller with helpful errors - always use ListEventsPage and scope event cursors - move events pagination coverage to integration test 🤖 Generated with [Codex](https://chatgpt.com/codex) Co-Authored-By: Codex <codex@openai.com> * refactor: simplify prefix trimming and improve error formatting - Replaced manual prefix check with `bytes.TrimPrefix` in Badger store. - Enhanced error message formatting in VM logs controller. * fix: address PR review feedback - use suggested reverse seek in badger events pagination - add events pagination client helper and use it in integration test 🤖 Generated with [Codex](https://chatgpt.com/codex) Co-Authored-By: Codex <codex@openai.com> --------- Co-authored-by: Nikolay Edigaryev <edigaryev@gmail.com> Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
co-authored by
Codex
Nikolay Edigaryev
parent
ea89d01760
commit
6fe523ef69
@@ -1,9 +1,11 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cirruslabs/orchard/internal/controller/lifecycle"
|
||||
@@ -11,6 +13,7 @@ import (
|
||||
"github.com/cirruslabs/orchard/internal/responder"
|
||||
"github.com/cirruslabs/orchard/internal/simplename"
|
||||
"github.com/cirruslabs/orchard/internal/worker/ondiskname"
|
||||
"github.com/cirruslabs/orchard/pkg/client"
|
||||
"github.com/cirruslabs/orchard/pkg/resource/v1"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -364,21 +367,87 @@ func (controller *Controller) listVMEvents(ctx *gin.Context) responder.Responder
|
||||
}
|
||||
|
||||
name := ctx.Param("name")
|
||||
options, parseResponder := parseListVMEventsOptions(ctx)
|
||||
if parseResponder != nil {
|
||||
return parseResponder
|
||||
}
|
||||
|
||||
return controller.storeView(func(txn storepkg.Transaction) responder.Responder {
|
||||
vm, err := txn.GetVM(name)
|
||||
if err != nil {
|
||||
return responder.Error(err)
|
||||
}
|
||||
events, err := txn.ListEvents("vms", vm.UID)
|
||||
|
||||
page, err := txn.ListEventsPage(options, "vms", vm.UID)
|
||||
if err != nil {
|
||||
return responder.Error(err)
|
||||
}
|
||||
if len(page.NextCursor) != 0 {
|
||||
ctx.Header("X-Next-Cursor", encodeEventCursor(page.NextCursor))
|
||||
}
|
||||
|
||||
return responder.JSON(http.StatusOK, events)
|
||||
return responder.JSON(http.StatusOK, page.Items)
|
||||
})
|
||||
}
|
||||
|
||||
func parseListVMEventsOptions(ctx *gin.Context) (storepkg.ListOptions, responder.Responder) {
|
||||
var options storepkg.ListOptions
|
||||
|
||||
limitRaw := ctx.Query("limit")
|
||||
orderRaw := ctx.Query("order")
|
||||
cursorRaw := ctx.Query("cursor")
|
||||
|
||||
if limitRaw != "" {
|
||||
limit, ok := parsePositiveInt(limitRaw)
|
||||
if !ok {
|
||||
return options, responder.JSON(http.StatusBadRequest,
|
||||
NewErrorResponse("invalid limit %q: expected positive integer", limitRaw))
|
||||
}
|
||||
options.Limit = limit
|
||||
}
|
||||
|
||||
if orderRaw != "" {
|
||||
order, err := client.ParseLogsOrder(orderRaw)
|
||||
if err != nil {
|
||||
return options, responder.JSON(http.StatusBadRequest, NewErrorResponse("%s", err))
|
||||
}
|
||||
options.Order = storepkg.ListOrder(order)
|
||||
}
|
||||
|
||||
if cursorRaw != "" {
|
||||
cursor, err := decodeEventCursor(cursorRaw)
|
||||
if err != nil {
|
||||
return options, responder.JSON(http.StatusBadRequest,
|
||||
NewErrorResponse("invalid cursor %q", cursorRaw))
|
||||
}
|
||||
options.Cursor = cursor
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func parsePositiveInt(raw string) (int, bool) {
|
||||
value, err := strconv.ParseInt(raw, 10, 0)
|
||||
if err != nil || value <= 0 {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return int(value), true
|
||||
}
|
||||
|
||||
func encodeEventCursor(cursor []byte) string {
|
||||
return base64.RawURLEncoding.EncodeToString(cursor)
|
||||
}
|
||||
|
||||
func decodeEventCursor(cursorRaw string) ([]byte, error) {
|
||||
cursor, err := base64.RawURLEncoding.DecodeString(cursorRaw)
|
||||
if err == nil {
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
return base64.URLEncoding.DecodeString(cursorRaw)
|
||||
}
|
||||
|
||||
func (controller *Controller) validateHostDirs(hostDirs []v1.HostDir) responder.Responder {
|
||||
if len(hostDirs) == 0 {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user