From bddcf7a56c021cf634d75e23a0541c9f36ee6e16 Mon Sep 17 00:00:00 2001 From: druppelt <44848632+druppelt@users.noreply.github.com> Date: Wed, 11 Sep 2024 21:38:10 +0200 Subject: [PATCH] resources.requests support (#14) & fix rounding errors for statefulset cpu resources --- cmd/kuota-calc.go | 30 ++++++++++------ examples/deployment.yaml | 2 +- internal/calc/calc.go | 28 +++++++++------ internal/calc/calc_test.go | 16 ++++++--- internal/calc/cronjob.go | 8 +++-- internal/calc/cronjob_test.go | 26 ++++++++------ internal/calc/daemonset.go | 8 +++-- internal/calc/daemonset_test.go | 28 +++++++++------ internal/calc/deployment.go | 25 ++++++++----- internal/calc/deployment_test.go | 60 ++++++++++++++++++++----------- internal/calc/job.go | 8 +++-- internal/calc/job_test.go | 22 +++++++----- internal/calc/pod.go | 8 +++-- internal/calc/pod_test.go | 22 +++++++----- internal/calc/statefulset.go | 19 ++++++---- internal/calc/statefulset_test.go | 24 ++++++++----- 16 files changed, 216 insertions(+), 118 deletions(-) diff --git a/cmd/kuota-calc.go b/cmd/kuota-calc.go index eebee10..f65e5d4 100644 --- a/cmd/kuota-calc.go +++ b/cmd/kuota-calc.go @@ -121,18 +121,20 @@ func (opts *KuotaCalcOpts) run() error { func (opts *KuotaCalcOpts) printDetailed(usage []*calc.ResourceUsage) { w := tabwriter.NewWriter(opts.Out, 0, 0, 4, ' ', tabwriter.TabIndent) - fmt.Fprintf(w, "Version\tKind\tName\tReplicas\tStrategy\tMaxReplicas\tCPU\tMemory\t\n") + fmt.Fprintf(w, "Version\tKind\tName\tReplicas\tStrategy\tMaxReplicas\tCPURequest\tCPULimit\tMemoryRequest\tMemoryLimit\t\n") for _, u := range usage { - fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\t%d\t%s\t%s\t\n", + fmt.Fprintf(w, "%s\t%s\t%s\t%d\t%s\t%d\t%s\t%s\t%s\t%s\t\n", u.Details.Version, u.Details.Kind, u.Details.Name, u.Details.Replicas, u.Details.Strategy, u.Details.MaxReplicas, - u.CPU, - u.Memory, + u.CpuMin, + u.CpuMax, + u.MemoryMin, + u.MemoryMax, ) } @@ -145,17 +147,23 @@ func (opts *KuotaCalcOpts) printDetailed(usage []*calc.ResourceUsage) { func (opts *KuotaCalcOpts) printSummary(usage []*calc.ResourceUsage) { var ( - cpuUsage resource.Quantity - memoryUsage resource.Quantity + cpuMinUsage resource.Quantity + cpuMaxUsage resource.Quantity + memoryMinUsage resource.Quantity + memoryMaxUsage resource.Quantity ) for _, u := range usage { - cpuUsage.Add(*u.CPU) - memoryUsage.Add(*u.Memory) + cpuMinUsage.Add(*u.CpuMin) + cpuMaxUsage.Add(*u.CpuMax) + memoryMinUsage.Add(*u.MemoryMin) + memoryMaxUsage.Add(*u.MemoryMax) } - fmt.Fprintf(opts.Out, "CPU: %s\nMemory: %s\n", - cpuUsage.String(), - memoryUsage.String(), + fmt.Fprintf(opts.Out, "CPU Request: %s\nCPU Limit: %s\nMemory Request: %s\nMemory Limit: %s\n", + cpuMinUsage.String(), + cpuMaxUsage.String(), + memoryMinUsage.String(), + memoryMaxUsage.String(), ) } diff --git a/examples/deployment.yaml b/examples/deployment.yaml index 86b3a6d..7c89297 100644 --- a/examples/deployment.yaml +++ b/examples/deployment.yaml @@ -18,7 +18,7 @@ spec: template: metadata: labels: - app: myapp + app: myapp spec: containers: - image: myapp:0.1.0 diff --git a/internal/calc/calc.go b/internal/calc/calc.go index 195251c..5e26ee0 100644 --- a/internal/calc/calc.go +++ b/internal/calc/calc.go @@ -42,9 +42,11 @@ func (cErr CalculationError) Unwrap() error { // ResourceUsage summarizes the usage of compute resources for a k8s resource. type ResourceUsage struct { - CPU *resource.Quantity - Memory *resource.Quantity - Details Details + CpuMin *resource.Quantity + CpuMax *resource.Quantity + MemoryMin *resource.Quantity + MemoryMax *resource.Quantity + Details Details } // Details contains a few details of a k8s resource, which are needed to generate a detailed resource @@ -58,22 +60,28 @@ type Details struct { MaxReplicas int32 } -func podResources(podSpec *v1.PodSpec) (cpu, memory *resource.Quantity) { - cpu = new(resource.Quantity) - memory = new(resource.Quantity) +func podResources(podSpec *v1.PodSpec) (cpuMin, cpuMax, memoryMin, memoryMax *resource.Quantity) { + cpuMin = new(resource.Quantity) + cpuMax = new(resource.Quantity) + memoryMin = new(resource.Quantity) + memoryMax = new(resource.Quantity) for i := range podSpec.Containers { container := podSpec.Containers[i] - cpu.Add(*container.Resources.Limits.Cpu()) - memory.Add(*container.Resources.Limits.Memory()) + cpuMin.Add(*container.Resources.Requests.Cpu()) + cpuMax.Add(*container.Resources.Limits.Cpu()) + memoryMin.Add(*container.Resources.Requests.Memory()) + memoryMax.Add(*container.Resources.Limits.Memory()) } for i := range podSpec.InitContainers { container := podSpec.InitContainers[i] - cpu.Add(*container.Resources.Limits.Cpu()) - memory.Add(*container.Resources.Limits.Memory()) + cpuMin.Add(*container.Resources.Requests.Cpu()) + cpuMax.Add(*container.Resources.Limits.Cpu()) + memoryMin.Add(*container.Resources.Requests.Memory()) + memoryMax.Add(*container.Resources.Limits.Memory()) } return diff --git a/internal/calc/calc_test.go b/internal/calc/calc_test.go index 2744bbc..094e7a2 100644 --- a/internal/calc/calc_test.go +++ b/internal/calc/calc_test.go @@ -2,6 +2,7 @@ package calc import ( "errors" + "k8s.io/apimachinery/pkg/api/resource" "testing" "github.com/stretchr/testify/require" @@ -19,7 +20,6 @@ spec: kind: Service name: coffee-svc ` - var normalDeployment = `--- apiVersion: apps/v1 kind: Deployment @@ -96,6 +96,9 @@ spec: - image: myinit:v1.0.7 name: myinit resources: + requests: + cpu: '50m' + memory: '100Mi' limits: cpu: '100m' memory: '200Mi' @@ -454,7 +457,7 @@ spec: restartPolicy: Never backoffLimit: 4` -var normalCronJob =`--- +var normalCronJob = `--- apiVersion: batch/v1 kind: CronJob metadata: @@ -500,7 +503,7 @@ spec: memory: 2Gi terminationGracePeriodSeconds: 30` -var normalDaemonSet = ` +var normalDaemonSet = ` apiVersion: apps/v1 kind: DaemonSet metadata: @@ -544,8 +547,11 @@ func TestResourceQuotaFromYaml(t *testing.T) { usage, err = ResourceQuotaFromYaml([]byte(unsupportedOpenshiftRoute)) t.Log(err) r.Error(err) - r.True(errors.Is(err,ErrResourceNotSupported)) + r.True(errors.Is(err, ErrResourceNotSupported)) r.Nil(usage) - r.True(errors.As(err,&calcErr)) + r.True(errors.As(err, &calcErr)) } +func AssertEqualQuantities(r *require.Assertions, expected resource.Quantity, actual resource.Quantity, name string) { + r.Conditionf(func() bool { return expected.Equal(actual) }, name+" expected: "+expected.String()+" but was: "+actual.String()) +} diff --git a/internal/calc/cronjob.go b/internal/calc/cronjob.go index d3e4343..e742221 100644 --- a/internal/calc/cronjob.go +++ b/internal/calc/cronjob.go @@ -3,11 +3,13 @@ package calc import batchV1 "k8s.io/api/batch/v1" func cronjob(cronjob batchV1.CronJob) *ResourceUsage { - cpu, memory := podResources(&cronjob.Spec.JobTemplate.Spec.Template.Spec) + cpuMin, cpuMax, memoryMin, memoryMax := podResources(&cronjob.Spec.JobTemplate.Spec.Template.Spec) resourceUsage := ResourceUsage{ - CPU: cpu, - Memory: memory, + CpuMin: cpuMin, + CpuMax: cpuMax, + MemoryMin: memoryMin, + MemoryMax: memoryMax, Details: Details{ Version: cronjob.APIVersion, Kind: cronjob.Kind, diff --git a/internal/calc/cronjob_test.go b/internal/calc/cronjob_test.go index 08be17b..5e26e3c 100644 --- a/internal/calc/cronjob_test.go +++ b/internal/calc/cronjob_test.go @@ -9,19 +9,23 @@ import ( func TestCronJob(t *testing.T) { var tests = []struct { - name string - cronjob string - cpu resource.Quantity - memory resource.Quantity + name string + cronjob string + cpuMin resource.Quantity + cpuMax resource.Quantity + memoryMin resource.Quantity + memoryMax resource.Quantity replicas int32 maxReplicas int32 strategy string }{ { - name: "ok", - cronjob: normalCronJob, - cpu: resource.MustParse("1"), - memory: resource.MustParse("4Gi"), + name: "ok", + cronjob: normalCronJob, + cpuMin: resource.MustParse("250m"), + cpuMax: resource.MustParse("1"), + memoryMin: resource.MustParse("2Gi"), + memoryMax: resource.MustParse("4Gi"), }, } @@ -34,8 +38,10 @@ func TestCronJob(t *testing.T) { r.NoError(err) r.NotEmpty(usage) - r.Equalf(test.cpu.Value(), usage.CPU.Value(), "cpu value") - r.Equalf(test.memory.Value(), usage.Memory.Value(), "memory value") + AssertEqualQuantities(r, test.cpuMin, *usage.CpuMin, "cpu request value") + AssertEqualQuantities(r, test.cpuMax, *usage.CpuMax, "cpu limit value") + AssertEqualQuantities(r, test.memoryMin, *usage.MemoryMin, "memory request value") + AssertEqualQuantities(r, test.memoryMax, *usage.MemoryMax, "memory limit value") r.Equalf(test.replicas, usage.Details.Replicas, "replicas") r.Equalf(test.maxReplicas, usage.Details.MaxReplicas, "maxReplicas") r.Equalf(string(test.strategy), usage.Details.Strategy, "strategy") diff --git a/internal/calc/daemonset.go b/internal/calc/daemonset.go index 2242d73..31557c2 100644 --- a/internal/calc/daemonset.go +++ b/internal/calc/daemonset.go @@ -5,11 +5,13 @@ import ( ) func daemonSet(dSet appsv1.DaemonSet) *ResourceUsage { - cpu, memory := podResources(&dSet.Spec.Template.Spec) + cpuMin, cpuMax, memoryMin, memoryMax := podResources(&dSet.Spec.Template.Spec) resourceUsage := ResourceUsage{ - CPU: cpu, - Memory: memory, + CpuMin: cpuMin, + CpuMax: cpuMax, + MemoryMin: memoryMin, + MemoryMax: memoryMax, Details: Details{ Version: dSet.APIVersion, Kind: dSet.Kind, diff --git a/internal/calc/daemonset_test.go b/internal/calc/daemonset_test.go index bbf23e8..0dbe954 100644 --- a/internal/calc/daemonset_test.go +++ b/internal/calc/daemonset_test.go @@ -10,21 +10,25 @@ import ( func TestDaemonSet(t *testing.T) { var tests = []struct { - name string - daemonset string - cpu resource.Quantity - memory resource.Quantity + name string + daemonset string + cpuMin resource.Quantity + cpuMax resource.Quantity + memoryMin resource.Quantity + memoryMax resource.Quantity replicas int32 maxReplicas int32 strategy appsv1.StatefulSetUpdateStrategyType }{ { - name: "ok", - daemonset: normalDaemonSet, - replicas: 1, + name: "ok", + daemonset: normalDaemonSet, + replicas: 1, maxReplicas: 1, - cpu: resource.MustParse("2"), - memory: resource.MustParse("2Gi"), + cpuMin: resource.MustParse("500m"), + cpuMax: resource.MustParse("2"), + memoryMin: resource.MustParse("200Mi"), + memoryMax: resource.MustParse("2Gi"), }, } @@ -37,8 +41,10 @@ func TestDaemonSet(t *testing.T) { r.NoError(err) r.NotEmpty(usage) - r.Equalf(test.cpu.Value(), usage.CPU.Value(), "cpu value") - r.Equalf(test.memory.Value(), usage.Memory.Value(), "memory value") + AssertEqualQuantities(r, test.cpuMin, *usage.CpuMin, "cpu request value") + AssertEqualQuantities(r, test.cpuMax, *usage.CpuMax, "cpu limit value") + AssertEqualQuantities(r, test.memoryMin, *usage.MemoryMin, "memory request value") + AssertEqualQuantities(r, test.memoryMax, *usage.MemoryMax, "memory limit value") r.Equalf(test.replicas, usage.Details.Replicas, "replicas") r.Equalf(test.maxReplicas, usage.Details.MaxReplicas, "maxReplicas") r.Equalf(string(test.strategy), usage.Details.Strategy, "strategy") diff --git a/internal/calc/deployment.go b/internal/calc/deployment.go index ec21a14..fc19534 100644 --- a/internal/calc/deployment.go +++ b/internal/calc/deployment.go @@ -22,8 +22,10 @@ func deployment(deployment appsv1.Deployment) (*ResourceUsage, error) { if *replicas == 0 { return &ResourceUsage{ - CPU: new(resource.Quantity), - Memory: new(resource.Quantity), + CpuMin: new(resource.Quantity), + CpuMax: new(resource.Quantity), + MemoryMin: new(resource.Quantity), + MemoryMax: new(resource.Quantity), Details: Details{ Version: deployment.APIVersion, Kind: deployment.Kind, @@ -90,16 +92,23 @@ func deployment(deployment appsv1.Deployment) (*ResourceUsage, error) { return nil, fmt.Errorf("deployment: %s deployment strategy %q is unknown", deployment.Name, strategy.Type) } - cpu, memory := podResources(&deployment.Spec.Template.Spec) + cpuMin, cpuMax, memoryMin, memoryMax := podResources(&deployment.Spec.Template.Spec) - mem := float64(memory.Value()) * float64(*replicas) * resourceOverhead - memory.Set(int64(math.Round(mem))) + memMin := float64(memoryMin.Value()) * float64(*replicas) * resourceOverhead + memoryMin.Set(int64(math.Round(memMin))) - cpu.SetMilli(int64(math.Round(float64(cpu.MilliValue()) * float64(*replicas) * resourceOverhead))) + memMax := float64(memoryMax.Value()) * float64(*replicas) * resourceOverhead + memoryMax.Set(int64(math.Round(memMax))) + + cpuMin.SetMilli(int64(math.Round(float64(cpuMin.MilliValue()) * float64(*replicas) * resourceOverhead))) + + cpuMax.SetMilli(int64(math.Round(float64(cpuMax.MilliValue()) * float64(*replicas) * resourceOverhead))) resourceUsage := ResourceUsage{ - CPU: cpu, - Memory: memory, + CpuMin: cpuMin, + CpuMax: cpuMax, + MemoryMin: memoryMin, + MemoryMax: memoryMax, Details: Details{ Version: deployment.APIVersion, Kind: deployment.Kind, diff --git a/internal/calc/deployment_test.go b/internal/calc/deployment_test.go index fa53758..eb5fe4d 100644 --- a/internal/calc/deployment_test.go +++ b/internal/calc/deployment_test.go @@ -12,8 +12,10 @@ func TestDeployment(t *testing.T) { var tests = []struct { name string deployment string - cpu resource.Quantity - memory resource.Quantity + cpuMin resource.Quantity + cpuMax resource.Quantity + memoryMin resource.Quantity + memoryMax resource.Quantity replicas int32 maxReplicas int32 strategy appsv1.DeploymentStrategyType @@ -21,8 +23,10 @@ func TestDeployment(t *testing.T) { { name: "normal deployment", deployment: normalDeployment, - cpu: resource.MustParse("5500m"), - memory: resource.MustParse("44Gi"), + cpuMin: resource.MustParse("2750m"), + cpuMax: resource.MustParse("5500m"), + memoryMin: resource.MustParse("22Gi"), + memoryMax: resource.MustParse("44Gi"), replicas: 10, maxReplicas: 11, strategy: appsv1.RollingUpdateDeploymentStrategyType, @@ -30,8 +34,10 @@ func TestDeployment(t *testing.T) { { name: "deployment without strategy", deployment: deploymentWithoutStrategy, - cpu: resource.MustParse("11"), - memory: resource.MustParse("44Gi"), + cpuMin: resource.MustParse("2750m"), + cpuMax: resource.MustParse("11"), + memoryMin: resource.MustParse("22Gi"), + memoryMax: resource.MustParse("44Gi"), replicas: 10, maxReplicas: 11, strategy: appsv1.RollingUpdateDeploymentStrategyType, @@ -39,8 +45,10 @@ func TestDeployment(t *testing.T) { { name: "deployment with absolute unavailable/surge values", deployment: deploymentWithAbsoluteValues, - cpu: resource.MustParse("12"), - memory: resource.MustParse("48Gi"), + cpuMin: resource.MustParse("3"), + cpuMax: resource.MustParse("12"), + memoryMin: resource.MustParse("24Gi"), + memoryMax: resource.MustParse("48Gi"), replicas: 10, maxReplicas: 12, strategy: appsv1.RollingUpdateDeploymentStrategyType, @@ -48,8 +56,10 @@ func TestDeployment(t *testing.T) { { name: "zero replica deployment", deployment: zeroReplicaDeployment, - cpu: resource.MustParse("0"), - memory: resource.MustParse("0"), + cpuMin: resource.MustParse("0"), + cpuMax: resource.MustParse("0"), + memoryMin: resource.MustParse("0"), + memoryMax: resource.MustParse("0"), replicas: 0, maxReplicas: 0, strategy: appsv1.RollingUpdateDeploymentStrategyType, @@ -57,8 +67,10 @@ func TestDeployment(t *testing.T) { { name: "recreate deployment", deployment: recrateDeployment, - cpu: resource.MustParse("10"), - memory: resource.MustParse("40Gi"), + cpuMin: resource.MustParse("2500m"), + cpuMax: resource.MustParse("10"), + memoryMin: resource.MustParse("20Gi"), + memoryMax: resource.MustParse("40Gi"), replicas: 10, maxReplicas: 10, strategy: appsv1.RecreateDeploymentStrategyType, @@ -66,17 +78,23 @@ func TestDeployment(t *testing.T) { { name: "deployment without max unavailable/surge values", deployment: deploymentWithoutValues, - cpu: resource.MustParse("11"), - memory: resource.MustParse("44Gi"), + cpuMin: resource.MustParse("2750m"), + cpuMax: resource.MustParse("11"), + memoryMin: resource.MustParse("22Gi"), + memoryMax: resource.MustParse("44Gi"), replicas: 10, maxReplicas: 11, strategy: appsv1.RollingUpdateDeploymentStrategyType, }, { - name: "deployment with init container(s)", - deployment: initContainerDeployment, - cpu: resource.MustParse("4400m"), - memory: resource.MustParse("17184Mi"), + name: "deployment with init container(s)", + deployment: initContainerDeployment, + // TODO the expectation shouldn't be for the init container resources to be considered for all containers, + // just the amount that can be in non-ready state simultaneously + cpuMin: resource.MustParse("1200m"), + cpuMax: resource.MustParse("4400m"), + memoryMin: resource.MustParse("8592Mi"), + memoryMax: resource.MustParse("17184Mi"), replicas: 3, maxReplicas: 4, strategy: appsv1.RollingUpdateDeploymentStrategyType, @@ -91,8 +109,10 @@ func TestDeployment(t *testing.T) { r.NoError(err) r.NotEmpty(usage) - r.Equalf(test.cpu.MilliValue(), usage.CPU.MilliValue(), "cpu value") - r.Equal(0, test.memory.Cmp(*usage.Memory), "memory value %d != %d", test.memory.Value(), usage.Memory.Value()) + AssertEqualQuantities(r, test.cpuMin, *usage.CpuMin, "cpu request value") + AssertEqualQuantities(r, test.cpuMax, *usage.CpuMax, "cpu limit value") + AssertEqualQuantities(r, test.memoryMin, *usage.MemoryMin, "memory request value") + AssertEqualQuantities(r, test.memoryMax, *usage.MemoryMax, "memory limit value") r.Equal(test.replicas, usage.Details.Replicas, "replicas") r.Equal(string(test.strategy), usage.Details.Strategy, "strategy") r.Equal(test.maxReplicas, usage.Details.MaxReplicas, "maxReplicas") diff --git a/internal/calc/job.go b/internal/calc/job.go index 42bde23..c8e42ee 100644 --- a/internal/calc/job.go +++ b/internal/calc/job.go @@ -3,11 +3,13 @@ package calc import batchV1 "k8s.io/api/batch/v1" func job(job batchV1.Job) *ResourceUsage { - cpu, memory := podResources(&job.Spec.Template.Spec) + cpuMin, cpuMax, memoryMin, memoryMax := podResources(&job.Spec.Template.Spec) resourceUsage := ResourceUsage{ - CPU: cpu, - Memory: memory, + CpuMin: cpuMin, + CpuMax: cpuMax, + MemoryMin: memoryMin, + MemoryMax: memoryMax, Details: Details{ Version: job.APIVersion, Kind: job.Kind, diff --git a/internal/calc/job_test.go b/internal/calc/job_test.go index 9493893..9a68898 100644 --- a/internal/calc/job_test.go +++ b/internal/calc/job_test.go @@ -11,17 +11,21 @@ func TestJob(t *testing.T) { var tests = []struct { name string job string - cpu resource.Quantity - memory resource.Quantity + cpuMin resource.Quantity + cpuMax resource.Quantity + memoryMin resource.Quantity + memoryMax resource.Quantity replicas int32 maxReplicas int32 strategy string }{ { - name: "ok", - job: normalJob, - cpu: resource.MustParse("1"), - memory: resource.MustParse("4Gi"), + name: "ok", + job: normalJob, + cpuMin: resource.MustParse("250m"), + cpuMax: resource.MustParse("1"), + memoryMin: resource.MustParse("2Gi"), + memoryMax: resource.MustParse("4Gi"), }, } @@ -34,8 +38,10 @@ func TestJob(t *testing.T) { r.NoError(err) r.NotEmpty(usage) - r.Equalf(test.cpu.Value(), usage.CPU.Value(), "cpu value") - r.Equalf(test.memory.Value(), usage.Memory.Value(), "memory value") + AssertEqualQuantities(r, test.cpuMin, *usage.CpuMin, "cpu request value") + AssertEqualQuantities(r, test.cpuMax, *usage.CpuMax, "cpu limit value") + AssertEqualQuantities(r, test.memoryMin, *usage.MemoryMin, "memory request value") + AssertEqualQuantities(r, test.memoryMax, *usage.MemoryMax, "memory limit value") r.Equalf(test.replicas, usage.Details.Replicas, "replicas") r.Equalf(test.maxReplicas, usage.Details.MaxReplicas, "maxReplicas") r.Equalf(string(test.strategy), usage.Details.Strategy, "strategy") diff --git a/internal/calc/pod.go b/internal/calc/pod.go index da16e18..22a9b3a 100644 --- a/internal/calc/pod.go +++ b/internal/calc/pod.go @@ -3,11 +3,13 @@ package calc import v1 "k8s.io/api/core/v1" func pod(pod v1.Pod) *ResourceUsage { - cpu, memory := podResources(&pod.Spec) + cpuMin, cpuMax, memoryMin, memoryMax := podResources(&pod.Spec) resourceUsage := ResourceUsage{ - CPU: cpu, - Memory: memory, + CpuMin: cpuMin, + CpuMax: cpuMax, + MemoryMin: memoryMin, + MemoryMax: memoryMax, Details: Details{ Version: pod.APIVersion, Kind: pod.Kind, diff --git a/internal/calc/pod_test.go b/internal/calc/pod_test.go index cb97bf9..5bd6e77 100644 --- a/internal/calc/pod_test.go +++ b/internal/calc/pod_test.go @@ -12,17 +12,21 @@ func TestPod(t *testing.T) { var tests = []struct { name string pod string - cpu resource.Quantity - memory resource.Quantity + cpuMin resource.Quantity + cpuMax resource.Quantity + memoryMin resource.Quantity + memoryMax resource.Quantity replicas int32 maxReplicas int32 strategy appsv1.StatefulSetUpdateStrategyType }{ { - name: "ok", - pod: normalPod, - cpu: resource.MustParse("1"), - memory: resource.MustParse("4Gi"), + name: "ok", + pod: normalPod, + cpuMin: resource.MustParse("250m"), + cpuMax: resource.MustParse("1"), + memoryMin: resource.MustParse("2Gi"), + memoryMax: resource.MustParse("4Gi"), }, } @@ -35,8 +39,10 @@ func TestPod(t *testing.T) { r.NoError(err) r.NotEmpty(usage) - r.Equalf(test.cpu.Value(), usage.CPU.Value(), "cpu value") - r.Equalf(test.memory.Value(), usage.Memory.Value(), "memory value") + AssertEqualQuantities(r, test.cpuMin, *usage.CpuMin, "cpu request value") + AssertEqualQuantities(r, test.cpuMax, *usage.CpuMax, "cpu limit value") + AssertEqualQuantities(r, test.memoryMin, *usage.MemoryMin, "memory request value") + AssertEqualQuantities(r, test.memoryMax, *usage.MemoryMax, "memory limit value") r.Equalf(test.replicas, usage.Details.Replicas, "replicas") r.Equalf(test.maxReplicas, usage.Details.MaxReplicas, "maxReplicas") r.Equalf(string(test.strategy), usage.Details.Strategy, "strategy") diff --git a/internal/calc/statefulset.go b/internal/calc/statefulset.go index 3f497ef..564413e 100644 --- a/internal/calc/statefulset.go +++ b/internal/calc/statefulset.go @@ -19,16 +19,23 @@ func statefulSet(s appsv1.StatefulSet) *ResourceUsage { replicas = 1 } - cpu, memory := podResources(&s.Spec.Template.Spec) + cpuMin, cpuMax, memoryMin, memoryMax := podResources(&s.Spec.Template.Spec) - mem := float64(memory.Value()) * float64(replicas) - memory.Set(int64(math.Round(mem))) + memMin := float64(memoryMin.Value()) * float64(replicas) + memoryMin.Set(int64(math.Round(memMin))) - cpu.Set(int64(math.Round(float64(cpu.Value()) * float64(replicas)))) + memMax := float64(memoryMax.Value()) * float64(replicas) + memoryMax.Set(int64(math.Round(memMax))) + + cpuMin.SetMilli(int64(math.Round(float64(cpuMin.MilliValue()) * float64(replicas)))) + + cpuMax.SetMilli(int64(math.Round(float64(cpuMax.MilliValue()) * float64(replicas)))) resourceUsage := ResourceUsage{ - CPU: cpu, - Memory: memory, + CpuMin: cpuMin, + CpuMax: cpuMax, + MemoryMin: memoryMin, + MemoryMax: memoryMax, Details: Details{ Version: s.APIVersion, Kind: s.Kind, diff --git a/internal/calc/statefulset_test.go b/internal/calc/statefulset_test.go index 39c16ba..a9614c0 100644 --- a/internal/calc/statefulset_test.go +++ b/internal/calc/statefulset_test.go @@ -12,8 +12,10 @@ func TestStatefulSet(t *testing.T) { var tests = []struct { name string statefulset string - cpu resource.Quantity - memory resource.Quantity + cpuMin resource.Quantity + cpuMax resource.Quantity + memoryMin resource.Quantity + memoryMax resource.Quantity replicas int32 maxReplicas int32 strategy appsv1.StatefulSetUpdateStrategyType @@ -21,8 +23,10 @@ func TestStatefulSet(t *testing.T) { { name: "ok", statefulset: normalStatefulSet, - cpu: resource.MustParse("2"), - memory: resource.MustParse("8Gi"), + cpuMin: resource.MustParse("500m"), + cpuMax: resource.MustParse("2"), + memoryMin: resource.MustParse("4Gi"), + memoryMax: resource.MustParse("8Gi"), replicas: 2, maxReplicas: 2, strategy: appsv1.RollingUpdateStatefulSetStrategyType, @@ -30,8 +34,10 @@ func TestStatefulSet(t *testing.T) { { name: "no replicas", statefulset: noReplicasStatefulSet, - cpu: resource.MustParse("1"), - memory: resource.MustParse("4Gi"), + cpuMin: resource.MustParse("250m"), + cpuMax: resource.MustParse("1"), + memoryMin: resource.MustParse("2Gi"), + memoryMax: resource.MustParse("4Gi"), replicas: 1, maxReplicas: 1, strategy: appsv1.RollingUpdateStatefulSetStrategyType, @@ -46,8 +52,10 @@ func TestStatefulSet(t *testing.T) { r.NoError(err) r.NotEmpty(usage) - r.Equalf(test.cpu.Value(), usage.CPU.Value(), "cpu value") - r.Equalf(test.memory.Value(), usage.Memory.Value(), "memory value") + AssertEqualQuantities(r, test.cpuMin, *usage.CpuMin, "cpu request value") + AssertEqualQuantities(r, test.cpuMax, *usage.CpuMax, "cpu limit value") + AssertEqualQuantities(r, test.memoryMin, *usage.MemoryMin, "memory request value") + AssertEqualQuantities(r, test.memoryMax, *usage.MemoryMax, "memory limit value") r.Equalf(test.replicas, usage.Details.Replicas, "replicas") r.Equalf(test.maxReplicas, usage.Details.MaxReplicas, "maxReplicas") r.Equalf(string(test.strategy), usage.Details.Strategy, "strategy")