mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-28 03:51:08 +01:00
feat(api): add endpoint to retrieve container stats
- Introduced a new GET endpoint `/docker/stats/:id` to fetch statistics for a specified container by its ID or route alias. - Implemented the `Stats` function in the `dockerapi` package to handle the request and return container stats in both JSON and WebSocket formats. - Added error handling for invalid requests and container not found scenarios.
This commit is contained in:
117
internal/api/v1/docker/stats.go
Normal file
117
internal/api/v1/docker/stats.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package dockerapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/http/websocket"
|
||||
"github.com/yusing/goutils/synk"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type ContainerStatsResponse container.StatsResponse // @name ContainerStatsResponse
|
||||
|
||||
// @x-id "stats"
|
||||
// @BasePath /api/v1
|
||||
// @Summary Get container stats
|
||||
// @Description Get container stats by container id
|
||||
// @Tags docker,websocket
|
||||
// @Produce json
|
||||
// @Param id path string true "Container ID or route alias"
|
||||
// @Success 200 {object} ContainerStatsResponse
|
||||
// @Failure 400 {object} apitypes.ErrorResponse "Invalid request: id is required or route is not a docker container"
|
||||
// @Failure 403 {object} apitypes.ErrorResponse
|
||||
// @Failure 404 {object} apitypes.ErrorResponse "Container not found"
|
||||
// @Failure 500 {object} apitypes.ErrorResponse
|
||||
// @Router /docker/stats/{id} [get]
|
||||
func Stats(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("id is required"))
|
||||
return
|
||||
}
|
||||
|
||||
dockerCfg, ok := docker.GetDockerCfgByContainerID(id)
|
||||
if !ok {
|
||||
var route types.Route
|
||||
route, ok = routes.GetIncludeExcluded(id)
|
||||
if ok {
|
||||
cont := route.ContainerInfo()
|
||||
if cont == nil {
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("route is not a docker container"))
|
||||
return
|
||||
}
|
||||
dockerCfg = cont.DockerCfg
|
||||
id = cont.ContainerID
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, apitypes.Error("container or route not found"))
|
||||
return
|
||||
}
|
||||
|
||||
dockerClient, err := docker.NewClient(dockerCfg)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to create docker client"))
|
||||
return
|
||||
}
|
||||
defer dockerClient.Close()
|
||||
|
||||
if httpheaders.IsWebsocket(c.Request.Header) {
|
||||
stats, err := dockerClient.ContainerStats(c.Request.Context(), id, client.ContainerStatsOptions{Stream: true})
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to get container stats"))
|
||||
return
|
||||
}
|
||||
defer stats.Body.Close()
|
||||
|
||||
manager, err := websocket.NewManagerWithUpgrade(c)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to create websocket manager"))
|
||||
return
|
||||
}
|
||||
defer manager.Close()
|
||||
|
||||
buf := synk.GetSizedBytesPool().GetSized(4096)
|
||||
defer synk.GetSizedBytesPool().Put(buf)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-manager.Done():
|
||||
return
|
||||
default:
|
||||
_, err = io.CopyBuffer(manager.NewWriter(websocket.TextMessage), stats.Body, buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) || errors.Is(err, task.ErrProgramExiting) {
|
||||
return
|
||||
}
|
||||
c.Error(apitypes.InternalServerError(err, "failed to copy container stats"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stats, err := dockerClient.ContainerStats(c.Request.Context(), id, client.ContainerStatsOptions{Stream: false})
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to get container stats"))
|
||||
return
|
||||
}
|
||||
defer stats.Body.Close()
|
||||
|
||||
_, err = io.Copy(c.Writer, stats.Body)
|
||||
if err != nil {
|
||||
c.Error(apitypes.InternalServerError(err, "failed to copy container stats"))
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user