Skip to content

Commit

Permalink
Approved: Merge pull request #73 from EliasDeHondt/#10
Browse files Browse the repository at this point in the history
Issue 10
  • Loading branch information
VW03 authored Feb 12, 2025
2 parents 1be9ba3 + 3ea2440 commit 28b22f2
Show file tree
Hide file tree
Showing 17 changed files with 329 additions and 37 deletions.
149 changes: 147 additions & 2 deletions App/Backend/cmd/kubernetes/Client.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
package kubernetes

import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/metrics/pkg/apis/metrics"
metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
"time"
)

type Metrics struct {
CpuUsage float64
MemUsage float64
DiskUsage int64
DiskCapacity int64
}

type FakeMetricsClient struct {
NodeMetrics map[string]*metrics.NodeMetrics
PodMetrics map[string]*metrics.PodMetrics
}

type IClient interface {
GetNodes() corev1.NodeInterface
GetPods(namespace string) corev1.PodInterface
Expand All @@ -15,14 +32,18 @@ type IClient interface {
GetConfigMaps(namespace string) corev1.ConfigMapInterface
GetSecrets(namespace string) corev1.SecretInterface
GetDeployments(namespace string) appsv1.DeploymentInterface
GetTotalUsage() (*Metrics, error)
GetUsageForNode(nodeName string) (*Metrics, error)
}

type FakeClient struct {
Client *fake.Clientset
Client *fake.Clientset
MetricsClient *FakeMetricsClient
}

type Client struct {
Client *kubernetes.Clientset
Client *kubernetes.Clientset
MetricsClient *metricsv.Clientset
}

func (client *FakeClient) GetNodes() corev1.NodeInterface {
Expand Down Expand Up @@ -80,3 +101,127 @@ func (client *Client) GetSecrets(namespace string) corev1.SecretInterface {
func (client *Client) GetDeployments(namespace string) appsv1.DeploymentInterface {
return client.Client.AppsV1().Deployments(namespace)
}

func (client *FakeClient) GetTotalUsage() (*Metrics, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

nodes, err := client.Client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}

var totalCpu int64
var totalMem int64
var totalDisk int64
var totalCpuUsage int64
var totalMemUsage int64
var totalDiskUsage int64

for _, node := range nodes.Items {
totalCpu += node.Status.Capacity.Cpu().MilliValue()
totalMem += node.Status.Capacity.Memory().Value()
totalDisk += node.Status.Capacity.Storage().Value()

nodeMetrics := client.MetricsClient.NodeMetrics[node.Name]

totalCpuUsage += nodeMetrics.Usage.Cpu().MilliValue()
totalMemUsage += nodeMetrics.Usage.Memory().Value()
totalDiskUsage += nodeMetrics.Usage.Storage().Value()
}

cpuUsagePercent := float64(totalCpuUsage) / float64(totalCpu) * 100
memUsagePercent := float64(totalMemUsage) / float64(totalMem) * 100

return &Metrics{CpuUsage: cpuUsagePercent, MemUsage: memUsagePercent, DiskUsage: totalDiskUsage, DiskCapacity: totalDisk}, nil
}

func (client *FakeClient) GetUsageForNode(nodeName string) (*Metrics, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

node, err := client.Client.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
if err != nil {
return nil, err
}

totalCpu := node.Status.Capacity.Cpu().MilliValue()
totalMem := node.Status.Capacity.Memory().Value()
totalDisk := node.Status.Capacity.Storage().Value()

nodeMetrics := client.MetricsClient.NodeMetrics[nodeName]

usedCpu := nodeMetrics.Usage.Cpu().MilliValue()
usedMem := nodeMetrics.Usage.Memory().Value()
usedDisk := nodeMetrics.Usage.Storage().Value()

cpuUsagePercent := float64(usedCpu) / float64(totalCpu) * 100
memoryUsagePercent := float64(usedMem) / float64(totalMem) * 100

return &Metrics{CpuUsage: cpuUsagePercent, MemUsage: memoryUsagePercent, DiskUsage: usedDisk, DiskCapacity: totalDisk}, nil
}

func (client *Client) GetTotalUsage() (*Metrics, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

nodes, err := client.Client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}

var totalCpu int64
var totalMem int64
var totalDisk int64
var totalCpuUsage int64
var totalMemUsage int64
var totalDiskUsage int64

for _, node := range nodes.Items {
totalCpu += node.Status.Capacity.Cpu().MilliValue()
totalMem += node.Status.Capacity.Memory().Value()
totalDisk += node.Status.Capacity.Storage().Value()

nodeMetrics, err := client.MetricsClient.MetricsV1beta1().NodeMetricses().Get(ctx, node.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}

totalCpuUsage += nodeMetrics.Usage.Cpu().MilliValue()
totalMemUsage += nodeMetrics.Usage.Memory().Value()
totalDiskUsage += nodeMetrics.Usage.Storage().Value()
}

cpuUsagePercent := float64(totalCpuUsage) / float64(totalCpu) * 100
memUsagePercent := float64(totalMemUsage) / float64(totalMem) * 100

return &Metrics{CpuUsage: cpuUsagePercent, MemUsage: memUsagePercent, DiskUsage: totalDiskUsage, DiskCapacity: totalDisk}, nil
}

func (client *Client) GetUsageForNode(nodeName string) (*Metrics, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

node, err := client.Client.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{})
if err != nil {
return nil, err
}

totalCpu := node.Status.Capacity.Cpu().MilliValue()
totalMem := node.Status.Capacity.Memory().Value()
totalDisk := node.Status.Capacity.Storage().Value()

nodeMetrics, err := client.MetricsClient.MetricsV1beta1().NodeMetricses().Get(ctx, nodeName, metav1.GetOptions{})
if err != nil {
return nil, err
}

usedCpu := nodeMetrics.Usage.Cpu().MilliValue()
usedMem := nodeMetrics.Usage.Memory().Value()
usedDisk := nodeMetrics.Usage.Storage().Value()

cpuUsagePercent := float64(usedCpu) / float64(totalCpu) * 100
memoryUsagePercent := float64(usedMem) / float64(totalMem) * 100

return &Metrics{CpuUsage: cpuUsagePercent, MemUsage: memoryUsagePercent, DiskUsage: usedDisk, DiskCapacity: totalDisk}, nil
}
6 changes: 3 additions & 3 deletions App/Backend/cmd/kubernetes/Node.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Node struct {

//TODO: remove fake clientset and use a real clientset

func NewNode(node v1.Node, clientset IClient) Node {
func NewNode(node v1.Node, clientset *IClient) Node {
return Node{
Name: node.Name,
Status: isNodeOnline(&node),
Expand All @@ -38,10 +38,10 @@ func calculateNodeAge(node *v1.Node) string {
return fmt.Sprintf("%d:%d:%d", int(age.Hours()/24), int(age.Hours())%24, int(age.Minutes())%60)
}

func getPodsInNode(nodeName string, clientset IClient) int {
func getPodsInNode(nodeName string, clientset *IClient) int {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
pods, err := clientset.GetPods("").List(ctx, metav1.ListOptions{
pods, err := (*clientset).GetPods("").List(ctx, metav1.ListOptions{
FieldSelector: "spec.nodeName=" + nodeName,
})
if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions App/Backend/cmd/kubernetes/Pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type Pod struct {

//TODO: remove fake clientset and use a real clientset

func NewPod(pod v1.Pod, clientset IClient) Pod {
func NewPod(pod v1.Pod, clientset *IClient) Pod {

runningServices, totalServices := getServiceHealthForPod(pod, clientset)

Expand Down Expand Up @@ -56,7 +56,7 @@ func isPodOnline(pod *v1.Pod) string {
return "NO STATUS ❓"
}

func getServiceHealthForPod(pod v1.Pod, clientset IClient) (int, int) {
func getServiceHealthForPod(pod v1.Pod, clientset *IClient) (int, int) {
services := getServicesForPod(pod, clientset)
totalServices := len(services)
runningServices := 0
Expand All @@ -70,12 +70,12 @@ func getServiceHealthForPod(pod v1.Pod, clientset IClient) (int, int) {
return runningServices, totalServices
}

func getServicesForPod(pod v1.Pod, clientset IClient) []string {
func getServicesForPod(pod v1.Pod, clientset *IClient) []string {
var matchingServices []string
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

services, err := clientset.GetServices(pod.Namespace).List(ctx, metav1.ListOptions{})
services, err := (*clientset).GetServices(pod.Namespace).List(ctx, metav1.ListOptions{})
if err != nil {
log.Printf("Error listing services: %v", err)
return nil
Expand Down Expand Up @@ -106,11 +106,11 @@ func isPodMatchingService(pod v1.Pod, service v1.Service) bool {
return true
}

func isServiceRunning(namespace, serviceName string, clientset IClient) bool {
func isServiceRunning(namespace, serviceName string, clientset *IClient) bool {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

endpoints, err := clientset.GetEndpoints(namespace).Get(ctx, serviceName, metav1.GetOptions{})
endpoints, err := (*clientset).GetEndpoints(namespace).Get(ctx, serviceName, metav1.GetOptions{})
if err != nil {
log.Printf("Error getting endpoints for service %s: %v", serviceName, err)
return false
Expand Down
85 changes: 84 additions & 1 deletion App/Backend/cmd/kubernetes/Setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,86 @@ import (
"context"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest"
"k8s.io/metrics/pkg/apis/metrics"
metricsv "k8s.io/metrics/pkg/client/clientset/versioned"
"time"
)

func GetClients() *IClient {
config, err := rest.InClusterConfig()

if err != nil {
client := TestFakeClient()
return &client
}

c, err := kubernetes.NewForConfig(config)
if err != nil {
client := TestFakeClient()
return &client
}

mc, err := metricsv.NewForConfig(config)
if err != nil {
client := TestFakeClient()
return &client
}

var client IClient = &Client{c, mc}
return &client
}

func TestFakeClient() IClient {
var clientset IClient = &FakeClient{fake.NewClientset()}

fakeMetricsClient := &FakeMetricsClient{
NodeMetrics: map[string]*metrics.NodeMetrics{
"node-1": {
Usage: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("500m"),
corev1.ResourceMemory: resource.MustParse("2Gi"),
corev1.ResourceStorage: resource.MustParse("1Gi"),
},
},
"node-2": {
Usage: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("300m"),
corev1.ResourceMemory: resource.MustParse("1Gi"),
corev1.ResourceStorage: resource.MustParse("2Gi"),
},
},
},
PodMetrics: map[string]*metrics.PodMetrics{
"default/pod-1": {
Containers: []metrics.ContainerMetrics{
{
Name: "container-1",
Usage: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("200m"),
corev1.ResourceMemory: resource.MustParse("512Mi"),
},
},
},
},
"default/pod-2": {
Containers: []metrics.ContainerMetrics{
{
Name: "container-1",
Usage: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("256Mi"),
},
},
},
},
},
}

var clientset IClient = &FakeClient{fake.NewClientset(), fakeMetricsClient}

nodes := []*corev1.Node{
{
Expand All @@ -29,6 +102,11 @@ func TestFakeClient() IClient {
Addresses: []corev1.NodeAddress{
{Type: corev1.NodeInternalIP, Address: "192.168.1.1"},
},
Capacity: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1000m"),
corev1.ResourceMemory: resource.MustParse("8Gi"),
corev1.ResourceStorage: resource.MustParse("1Gi"),
},
},
},
{
Expand All @@ -47,6 +125,11 @@ func TestFakeClient() IClient {
Addresses: []corev1.NodeAddress{
{Type: corev1.NodeInternalIP, Address: "192.168.1.2"},
},
Capacity: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("1000m"),
corev1.ResourceMemory: resource.MustParse("8Gi"),
corev1.ResourceStorage: resource.MustParse("3Gi"),
},
},
},
{
Expand Down
4 changes: 2 additions & 2 deletions App/Backend/cmd/kubernetes/handlers/GetConfigMaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ func GetConfigMapsHandler(ctx *gin.Context) {
ctx.JSON(http.StatusOK, configMapList)
}

func GetConfigMaps(c kubernetes.IClient, namespace string) (*[]kubernetes.ConfigMap, error) {
func GetConfigMaps(c *kubernetes.IClient, namespace string) (*[]kubernetes.ConfigMap, error) {
ct, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

list, err := c.GetConfigMaps(namespace).List(ct, metav1.ListOptions{})
list, err := (*c).GetConfigMaps(namespace).List(ct, metav1.ListOptions{})
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions App/Backend/cmd/kubernetes/handlers/GetDeployments.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ func GetDeploymentsHandler(ctx *gin.Context) {
ctx.JSON(http.StatusOK, deploymentList)
}

func GetDeployments(c kubernetes.IClient, namespace string) (*[]kubernetes.Deployment, error) {
func GetDeployments(c *kubernetes.IClient, namespace string) (*[]kubernetes.Deployment, error) {
ct, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

list, err := c.GetDeployments(namespace).List(ct, metav1.ListOptions{})
list, err := (*c).GetDeployments(namespace).List(ct, metav1.ListOptions{})
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 28b22f2

Please sign in to comment.