Skip to content

Commit be79c1d

Browse files
author
Sunil Thaha
authored
Merge pull request #2145 from vimalk78/pod-power-tests
chore(pod): added tests for pod power
2 parents 66f99f3 + 70fb2a3 commit be79c1d

File tree

13 files changed

+234
-37
lines changed

13 files changed

+234
-37
lines changed

config/config.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -386,15 +386,14 @@ func (c *Config) Validate(skips ...SkipValidation) error {
386386
}
387387
}
388388
{ // Kubernetes
389-
if c.Kube.Config != "" && !ptr.Deref(c.Kube.Enabled, false) {
390-
errs = append(errs, fmt.Sprintf("%s supplied but %s set to false", KubeConfigFlag, KubernetesFlag))
391-
}
392-
if c.Kube.Node != "" && !ptr.Deref(c.Kube.Enabled, false) {
393-
errs = append(errs, fmt.Sprintf("%s supplied but %s set to false", KubeNodeNameFlag, KubernetesFlag))
394-
}
395-
if ptr.Deref(c.Kube.Enabled, false) && c.Kube.Config != "" {
396-
if err := canReadFile(c.Kube.Config); err != nil {
397-
errs = append(errs, fmt.Sprintf("unreadable kubeconfig: %s", c.Kube.Config))
389+
if ptr.Deref(c.Kube.Enabled, false) {
390+
if c.Kube.Config != "" {
391+
if err := canReadFile(c.Kube.Config); err != nil {
392+
errs = append(errs, fmt.Sprintf("unreadable kubeconfig: %s", c.Kube.Config))
393+
}
394+
}
395+
if c.Kube.Node == "" {
396+
errs = append(errs, fmt.Sprintf("%s not supplied but %s set to true", KubeNodeNameFlag, KubernetesFlag))
398397
}
399398
}
400399
}

config/config_test.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -301,21 +301,13 @@ func TestInvalidConfigurationValues(t *testing.T) {
301301
},
302302
error: "unreadable kubeconfig",
303303
}, {
304-
name: "kube not enabled, kubeconfig supplied",
304+
name: "kube enabled, nodeName not supplied",
305305
config: &Config{
306306
Kube: Kube{
307-
Config: "/some/existing/file",
308-
},
309-
},
310-
error: "kube.config supplied but kube.enable set to false",
311-
}, {
312-
name: "kube not enabled, nodeName supplied",
313-
config: &Config{
314-
Kube: Kube{
315-
Node: "dummyNode",
307+
Enabled: ptr.To(true),
316308
},
317309
},
318-
error: "kube.node-name supplied but kube.enable set to false",
310+
error: "kube.node-name not supplied but kube.enable set to true",
319311
}}
320312

321313
// test yaml marshall

internal/exporter/prometheus/collector/power_collector.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func timeDesc(level, device string, labels []string) *prometheus.Desc {
9595
// by fetching all data in a single snapshot during collection
9696
func NewPowerCollector(monitor PowerDataProvider, logger *slog.Logger) *PowerCollector {
9797
const (
98-
// these labels should rename the same across all descriptors to ease querying
98+
// these labels should remain the same across all descriptors to ease querying
9999
zone = "zone"
100100
cntrID = "container_id"
101101
vmID = "vm_id"
@@ -124,8 +124,8 @@ func NewPowerCollector(monitor PowerDataProvider, logger *slog.Logger) *PowerCol
124124
processCPUWattsDescriptor: wattsDesc("process", "cpu", []string{"pid", "comm", "exe", "type", cntrID, vmID, zone}),
125125
processCPUTimeDescriptor: timeDesc("process", "cpu", []string{"pid", "comm", "exe", "type", cntrID, vmID}),
126126

127-
containerCPUJoulesDescriptor: joulesDesc("container", "cpu", []string{cntrID, "container_name", "runtime", zone}),
128-
containerCPUWattsDescriptor: wattsDesc("container", "cpu", []string{cntrID, "container_name", "runtime", zone}),
127+
containerCPUJoulesDescriptor: joulesDesc("container", "cpu", []string{cntrID, "container_name", "runtime", zone, podID}),
128+
containerCPUWattsDescriptor: wattsDesc("container", "cpu", []string{cntrID, "container_name", "runtime", zone, podID}),
129129

130130
vmCPUJoulesDescriptor: joulesDesc("vm", "cpu", []string{vmID, "vm_name", "hypervisor", zone}),
131131
vmCPUWattsDescriptor: wattsDesc("vm", "cpu", []string{vmID, "vm_name", "hypervisor", zone}),
@@ -329,13 +329,16 @@ func (c *PowerCollector) collectContainerMetrics(ch chan<- prometheus.Metric, co
329329
usage.EnergyTotal.Joules(),
330330
id, container.Name, string(container.Runtime),
331331
zoneName,
332+
container.PodID,
332333
)
333334

334335
ch <- prometheus.MustNewConstMetric(
335336
c.containerCPUWattsDescriptor,
336337
prometheus.GaugeValue,
337338
usage.Power.Watts(),
338-
id, container.Name, string(container.Runtime), zoneName,
339+
id, container.Name, string(container.Runtime),
340+
zoneName,
341+
container.PodID,
339342
)
340343
}
341344
}

internal/k8s/pod/pod.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"log/slog"
1111
"strings"
1212

13+
"github.com/sustainable-computing-io/kepler/internal/logger"
1314
"github.com/sustainable-computing-io/kepler/internal/service"
1415
"go.uber.org/zap/zapcore"
1516
corev1 "k8s.io/api/core/v1"
@@ -131,11 +132,7 @@ func (pi *podInformer) Init() error {
131132
}
132133
pi.manager = mgr
133134

134-
opts := zap.Options{
135-
Development: true, // enables DebugLevel by default
136-
Level: zapcore.DebugLevel,
137-
}
138-
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)).WithCallDepth(0))
135+
pi.setControllerRuntimeLogLevel()
139136

140137
pi.logger.Info("pod informer initialized")
141138

@@ -252,3 +249,27 @@ func getConfig(kubeConfigPath string) (*rest.Config, error) {
252249
func (i *podInformer) Name() string {
253250
return "podInformer"
254251
}
252+
253+
func (pi *podInformer) setControllerRuntimeLogLevel() {
254+
level := logger.LogLevel()
255+
opts := zap.Options{
256+
Level: slogLevelToZapLevel(level),
257+
}
258+
if level == slog.LevelDebug {
259+
opts.Development = true
260+
}
261+
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)).WithCallDepth(0))
262+
}
263+
264+
func slogLevelToZapLevel(level slog.Level) zapcore.Level {
265+
switch {
266+
case level <= slog.LevelDebug:
267+
return zapcore.DebugLevel
268+
case level <= slog.LevelInfo:
269+
return zapcore.InfoLevel
270+
case level <= slog.LevelWarn:
271+
return zapcore.WarnLevel
272+
default:
273+
return zapcore.ErrorLevel
274+
}
275+
}

internal/k8s/pod/pod_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/stretchr/testify/assert"
1414
"github.com/stretchr/testify/mock"
15+
"go.uber.org/zap/zapcore"
1516
corev1 "k8s.io/api/core/v1"
1617
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1718
"k8s.io/apimachinery/pkg/runtime"
@@ -297,3 +298,22 @@ func TestPodInformer_RunIntegration(t *testing.T) {
297298
}
298299
})
299300
}
301+
302+
func TestSlogLevelToZapLevel(t *testing.T) {
303+
tests := []struct {
304+
input slog.Level
305+
expected zapcore.Level
306+
}{
307+
{slog.LevelDebug, zapcore.DebugLevel},
308+
{slog.LevelInfo, zapcore.InfoLevel},
309+
{slog.LevelWarn, zapcore.WarnLevel},
310+
{slog.LevelError, zapcore.ErrorLevel},
311+
{slog.Level(-10), zapcore.DebugLevel},
312+
{slog.Level(10), zapcore.ErrorLevel},
313+
}
314+
315+
for _, tc := range tests {
316+
result := slogLevelToZapLevel(tc.input)
317+
assert.Equal(t, tc.expected, result, "Conversion failed for slog level: %v", tc.input)
318+
}
319+
}

internal/logger/logger.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ import (
1111
"strings"
1212
)
1313

14+
var logLevel slog.Level
15+
1416
func New(level, format string, w io.Writer) *slog.Logger {
15-
logLevel := parseLogLevel(level)
17+
logLevel = parseLogLevel(level)
1618
return slog.New(handlerForFormat(format, logLevel, w))
1719
}
1820

21+
func LogLevel() slog.Level {
22+
return logLevel
23+
}
24+
1925
func handlerForFormat(format string, logLevel slog.Level, w io.Writer) slog.Handler {
2026
switch format {
2127
case "json":

internal/monitor/container.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ func (pm *PowerMonitor) firstContainerRead(snapshot *Snapshot) error {
2525
CPUTotalTime: ctnr.CPUTotalTime,
2626
Zones: make(ZoneUsageMap, len(zones)),
2727
}
28+
if ctnr.Pod != nil {
29+
container.PodID = ctnr.Pod.ID
30+
}
2831

2932
// Initialize each zone with zero values
3033
for _, zone := range zones {
@@ -78,6 +81,9 @@ func (pm *PowerMonitor) calculateContainerPower(prev, newSnapshot *Snapshot) err
7881
CPUTotalTime: c.CPUTotalTime,
7982
Zones: make(ZoneUsageMap),
8083
}
84+
if c.Pod != nil {
85+
container.PodID = c.Pod.ID
86+
}
8187

8288
// Calculate CPU time ratio for this container
8389

internal/monitor/monitor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ func (pm *PowerMonitor) firstReading(newSnapshot *Snapshot) error {
320320

321321
// First read for pods
322322
if err := pm.firstPodRead(newSnapshot); err != nil {
323-
return fmt.Errorf(containerPowerError, err)
323+
return fmt.Errorf(podPowerError, err)
324324
}
325325

326326
return nil

internal/monitor/monitor_collection_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ func TestCollectionLoop(t *testing.T) {
3030
resourceInformer := &MockResourceInformer{}
3131
resourceInformer.SetExpectations(t, tr)
3232
resourceInformer.On("Refresh").Return(nil)
33-
// resourceInformer.On("Pods").Return(nil)
3433

3534
monitor := NewPowerMonitor(
3635
mockMeter,

internal/resource/informer.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ func (ri *resourceInformer) Refresh() error {
219219
containersNoPod = append(containersNoPod, container.ID)
220220
} else {
221221
ri.logger.Debug("Failed to get pod for container", "container", container.ID, "error", err)
222-
refreshErrs = errors.Join(refreshErrs, err)
222+
refreshErrs = errors.Join(refreshErrs, fmt.Errorf("failed to get pod for container: %w", err))
223223
}
224224
continue
225225
}
@@ -290,11 +290,9 @@ func (ri *resourceInformer) Refresh() error {
290290
ri.vms.Terminated = vmsTerminated
291291

292292
// Find terminated pods
293-
totalPodsDelta := float64(0)
294293
podsTerminated := make(map[string]*Pod)
295294
for id, pod := range ri.podCache {
296295
if _, isRunning := podsRunning[id]; isRunning {
297-
totalPodsDelta += pod.CPUTimeDelta
298296
continue
299297
}
300298
podsTerminated[id] = pod

0 commit comments

Comments
 (0)