api/handler/node_filesystem_stats.go
Rama main · réplica de lectura del repositorio original.
package handler
import (
"context"
"encoding/json"
"time"
"github.com/goodrain/rainbond/api/model"
)
const defaultNodeFilesystemStatsTimeout = 3 * time.Second
type kubeletFilesystemStats struct {
CapacityBytes *uint64 `json:"capacityBytes"`
AvailableBytes *uint64 `json:"availableBytes"`
UsedBytes *uint64 `json:"usedBytes"`
}
type kubeletStatsSummary struct {
Node struct {
Fs *kubeletFilesystemStats `json:"fs"`
Runtime struct {
ImageFs *kubeletFilesystemStats `json:"imageFs"`
ContainerFs *kubeletFilesystemStats `json:"containerFs"`
} `json:"runtime"`
} `json:"node"`
}
type filesystemUsage struct {
capacityBytes uint64
usedBytes uint64
}
type nodeFilesystemStats struct {
root filesystemUsage
container filesystemUsage
}
func (n *nodesHandle) getNodeFilesystemStats(ctx context.Context, nodeName string) (nodeFilesystemStats, error) {
timeout := n.nodeFilesystemStatsTimeout
if timeout <= 0 {
timeout = defaultNodeFilesystemStatsTimeout
}
queryCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
raw, err := n.clientset.CoreV1().RESTClient().Get().
Resource("nodes").
Name(nodeName).
SubResource("proxy").
Suffix("stats", "summary").
DoRaw(queryCtx)
if err != nil {
return nodeFilesystemStats{}, err
}
return parseNodeFilesystemStats(raw)
}
func (n *nodesHandle) setNodeFilesystemStats(ctx context.Context, nodeName string, resource *model.Resource) error {
stats, err := n.getNodeFilesystemStats(ctx, nodeName)
if err != nil {
return err
}
resource.CapDisk = stats.root.capacityBytes
resource.ReqDisk = stats.root.usedBytes
resource.CapContainerDisk = stats.container.capacityBytes
resource.ReqContainerDisk = stats.container.usedBytes
return nil
}
func nodeDiskStatsStatus(resource model.Resource, err error) string {
if err != nil || resource.CapDisk == 0 && resource.CapContainerDisk == 0 {
return "unavailable"
}
if resource.CapDisk == 0 || resource.CapContainerDisk == 0 {
return "partial"
}
return "ok"
}
func parseNodeFilesystemStats(raw []byte) (nodeFilesystemStats, error) {
var summary kubeletStatsSummary
if err := json.Unmarshal(raw, &summary); err != nil {
return nodeFilesystemStats{}, err
}
return nodeFilesystemStats{
root: filesystemUsageFromKubelet(summary.Node.Fs),
container: mostUtilizedFilesystem(
filesystemUsageFromKubelet(summary.Node.Runtime.ImageFs),
filesystemUsageFromKubelet(summary.Node.Runtime.ContainerFs),
),
}, nil
}
func filesystemUsageFromKubelet(stats *kubeletFilesystemStats) filesystemUsage {
if stats == nil || stats.CapacityBytes == nil || *stats.CapacityBytes == 0 {
return filesystemUsage{}
}
capacity := *stats.CapacityBytes
if stats.AvailableBytes != nil && *stats.AvailableBytes <= capacity {
return filesystemUsage{
capacityBytes: capacity,
usedBytes: capacity - *stats.AvailableBytes,
}
}
if stats.UsedBytes == nil {
return filesystemUsage{}
}
used := *stats.UsedBytes
if used > capacity {
used = capacity
}
return filesystemUsage{capacityBytes: capacity, usedBytes: used}
}
func mostUtilizedFilesystem(filesystems ...filesystemUsage) filesystemUsage {
var selected filesystemUsage
for _, filesystem := range filesystems {
if filesystem.capacityBytes == 0 {
continue
}
if selected.capacityBytes == 0 ||
float64(filesystem.usedBytes)/float64(filesystem.capacityBytes) >
float64(selected.usedBytes)/float64(selected.capacityBytes) {
selected = filesystem
}
}
return selected
}