api/handler/nodes_disk_test.go
Rama main · réplica de lectura del repositorio original.
package handler
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
func TestGetNodeFilesystemStatsUsesKubeletSummaryProxy(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/api/v1/nodes/worker-1/proxy/stats/summary", r.URL.Path)
_, err := w.Write([]byte(`{
"node": {
"fs": {"capacityBytes": 1000, "availableBytes": 250},
"runtime": {"imageFs": {"capacityBytes": 2000, "availableBytes": 400}}
}
}`))
require.NoError(t, err)
}))
defer server.Close()
clientset, err := kubernetes.NewForConfig(&rest.Config{Host: server.URL})
require.NoError(t, err)
handler := &nodesHandle{clientset: clientset}
stats, err := handler.getNodeFilesystemStats(context.Background(), "worker-1")
require.NoError(t, err)
assert.Equal(t, uint64(750), stats.root.usedBytes)
assert.Equal(t, uint64(1600), stats.container.usedBytes)
}
func TestGetNodeFilesystemStatsReturnsProxyErrors(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
http.Error(w, "kubelet unavailable", http.StatusServiceUnavailable)
}))
defer server.Close()
clientset, err := kubernetes.NewForConfig(&rest.Config{Host: server.URL})
require.NoError(t, err)
handler := &nodesHandle{clientset: clientset}
_, err = handler.getNodeFilesystemStats(context.Background(), "worker-1")
require.Error(t, err)
}
func TestListNodesIncludesKubeletFilesystemStats(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.URL.Path {
case "/api/v1/nodes":
_, err := w.Write([]byte(`{
"apiVersion": "v1",
"kind": "NodeList",
"items": [{
"metadata": {"name": "worker-1"},
"status": {
"capacity": {"cpu": "4", "memory": "8Gi", "ephemeral-storage": "100Gi"},
"nodeInfo": {"architecture": "amd64", "operatingSystem": "linux"}
}
}]
}`))
require.NoError(t, err)
case "/api/v1/nodes/worker-1/proxy/stats/summary":
_, err := w.Write([]byte(`{
"node": {
"fs": {"capacityBytes": 1000, "availableBytes": 250},
"runtime": {"imageFs": {"capacityBytes": 2000, "availableBytes": 400}}
}
}`))
require.NoError(t, err)
default:
http.NotFound(w, r)
}
}))
defer server.Close()
clientset, err := kubernetes.NewForConfig(&rest.Config{Host: server.URL})
require.NoError(t, err)
handler := &nodesHandle{clientset: clientset, prometheusCli: noopPrometheus{}}
nodes, err := handler.ListNodes(context.Background())
require.NoError(t, err)
require.Len(t, nodes, 1)
assert.Equal(t, uint64(1000), nodes[0].Resource.CapDisk)
assert.Equal(t, uint64(750), nodes[0].Resource.ReqDisk)
assert.Equal(t, uint64(2000), nodes[0].Resource.CapContainerDisk)
assert.Equal(t, uint64(1600), nodes[0].Resource.ReqContainerDisk)
}
func TestListNodesDegradesFilesystemStatsAfterTimeout(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.URL.Path {
case "/api/v1/nodes":
_, err := w.Write([]byte(`{
"apiVersion": "v1",
"kind": "NodeList",
"items": [{
"metadata": {"name": "worker-1"},
"status": {
"capacity": {"cpu": "4", "memory": "8Gi", "ephemeral-storage": "100Gi"},
"nodeInfo": {"architecture": "amd64", "operatingSystem": "linux"}
}
}]
}`))
require.NoError(t, err)
case "/api/v1/nodes/worker-1/proxy/stats/summary":
<-r.Context().Done()
default:
http.NotFound(w, r)
}
}))
defer server.Close()
clientset, err := kubernetes.NewForConfig(&rest.Config{Host: server.URL})
require.NoError(t, err)
handler := &nodesHandle{
clientset: clientset,
prometheusCli: noopPrometheus{},
nodeFilesystemStatsTimeout: 20 * time.Millisecond,
}
startedAt := time.Now()
nodes, err := handler.ListNodes(context.Background())
require.NoError(t, err)
require.Len(t, nodes, 1)
assert.Less(t, time.Since(startedAt), 500*time.Millisecond)
assert.Zero(t, nodes[0].Resource.CapDisk)
assert.Zero(t, nodes[0].Resource.ReqDisk)
assert.Zero(t, nodes[0].Resource.CapContainerDisk)
assert.Zero(t, nodes[0].Resource.ReqContainerDisk)
}
func TestGetNodeFilesystemStatsRespectsShorterCallerDeadline(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
}))
defer server.Close()
clientset, err := kubernetes.NewForConfig(&rest.Config{Host: server.URL})
require.NoError(t, err)
handler := &nodesHandle{
clientset: clientset,
nodeFilesystemStatsTimeout: time.Second,
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
defer cancel()
startedAt := time.Now()
_, err = handler.getNodeFilesystemStats(ctx, "worker-1")
require.ErrorIs(t, err, context.DeadlineExceeded)
assert.Less(t, time.Since(startedAt), 500*time.Millisecond)
}
func TestParseNodeFilesystemStatsUsesKubeletReportedFilesystems(t *testing.T) {
raw := []byte(`{
"node": {
"fs": {"capacityBytes": 1000, "availableBytes": 250, "usedBytes": 700},
"runtime": {
"imageFs": {"capacityBytes": 2000, "availableBytes": 400, "usedBytes": 1500}
}
}
}`)
stats, err := parseNodeFilesystemStats(raw)
require.NoError(t, err)
assert.Equal(t, uint64(1000), stats.root.capacityBytes)
assert.Equal(t, uint64(750), stats.root.usedBytes)
assert.Equal(t, uint64(2000), stats.container.capacityBytes)
assert.Equal(t, uint64(1600), stats.container.usedBytes)
}
func TestParseNodeFilesystemStatsSelectsMostUtilizedRuntimeFilesystem(t *testing.T) {
raw := []byte(`{
"node": {
"fs": {"capacityBytes": 1000, "availableBytes": 500},
"runtime": {
"imageFs": {"capacityBytes": 2000, "availableBytes": 800},
"containerFs": {"capacityBytes": 500, "availableBytes": 50}
}
}
}`)
stats, err := parseNodeFilesystemStats(raw)
require.NoError(t, err)
assert.Equal(t, uint64(500), stats.container.capacityBytes)
assert.Equal(t, uint64(450), stats.container.usedBytes)
}
func TestParseNodeFilesystemStatsFallsBackToUsedBytes(t *testing.T) {
raw := []byte(`{
"node": {
"fs": {"capacityBytes": 1000, "usedBytes": 300},
"runtime": {
"imageFs": {"capacityBytes": 2000, "usedBytes": 600}
}
}
}`)
stats, err := parseNodeFilesystemStats(raw)
require.NoError(t, err)
assert.Equal(t, uint64(300), stats.root.usedBytes)
assert.Equal(t, uint64(600), stats.container.usedBytes)
}
func TestParseNodeFilesystemStatsDoesNotInventContainerData(t *testing.T) {
raw := []byte(`{
"node": {
"fs": {"capacityBytes": 1000, "availableBytes": 250}
}
}`)
stats, err := parseNodeFilesystemStats(raw)
require.NoError(t, err)
assert.Equal(t, uint64(1000), stats.root.capacityBytes)
assert.Equal(t, uint64(750), stats.root.usedBytes)
assert.Zero(t, stats.container.capacityBytes)
assert.Zero(t, stats.container.usedBytes)
}
func TestParseNodeFilesystemStatsRejectsMalformedSummary(t *testing.T) {
_, err := parseNodeFilesystemStats([]byte(`{"node":`))
require.Error(t, err)
}