Ctrl K
ComunidadGuías…

api/handler/node_filesystem_stats.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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
}