faro/internal/api/api.go

99 lines
2.7 KiB
Go

package api
import (
"context"
"encoding/json"
"log/slog"
"net/http"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
)
// ContainerLister is the subset of the Docker client used by the API.
type ContainerLister interface {
ContainerList(ctx context.Context, options client.ContainerListOptions) (client.ContainerListResult, error)
}
type API struct {
localEngine ContainerLister
logger *slog.Logger
}
func NewHandler(localEngine ContainerLister, logger *slog.Logger) http.Handler {
api := &API{localEngine: localEngine, logger: logger}
mux := http.NewServeMux()
mux.HandleFunc("GET /api/v1/engines/{engineID}/containers", api.listContainers)
return mux
}
func (a *API) listContainers(w http.ResponseWriter, r *http.Request) {
if r.PathValue("engineID") != "1" {
writeError(w, http.StatusNotFound, "engine_not_found", "engine not found")
return
}
result, err := a.localEngine.ContainerList(r.Context(), client.ContainerListOptions{All: true})
if err != nil {
a.logger.Error("list Docker containers", "engine_id", 1, "error", err)
writeError(w, http.StatusBadGateway, "docker_unavailable", "could not list containers from the Docker Engine")
return
}
items := make([]containerResponse, 0, len(result.Items))
for _, item := range result.Items {
items = append(items, newContainerResponse(item))
}
writeJSON(w, http.StatusOK, listContainersResponse{Containers: items})
}
type listContainersResponse struct {
Containers []containerResponse `json:"containers"`
}
type containerResponse struct {
ID string `json:"id"`
Names []string `json:"names"`
Image string `json:"image"`
ImageID string `json:"imageId"`
Command string `json:"command"`
Created int64 `json:"created"`
State string `json:"state"`
Status string `json:"status"`
Labels map[string]string `json:"labels"`
}
func newContainerResponse(item container.Summary) containerResponse {
return containerResponse{
ID: item.ID,
Names: item.Names,
Image: item.Image,
ImageID: item.ImageID,
Command: item.Command,
Created: item.Created,
State: string(item.State),
Status: item.Status,
Labels: item.Labels,
}
}
type errorResponse struct {
Error apiError `json:"error"`
}
type apiError struct {
Code string `json:"code"`
Message string `json:"message"`
}
func writeError(w http.ResponseWriter, status int, code, message string) {
writeJSON(w, status, errorResponse{Error: apiError{Code: code, Message: message}})
}
func writeJSON(w http.ResponseWriter, status int, value any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(value)
}