Skip to content

Commit 1a90cb0

Browse files
committed
Create CPU scaling component stub
Creates a stub implementation for CPU scaling component connected to CPUScalingConfiguration API. CPUScalingManager is the overarching interface for controlling workers on assigned CPUs and accepting new scaling configurations. CPUScalingWorker is run asynchronously and is responsible for storing the scaling configuration tied to the CPU and for calling the updater. CPUScalingUpdater is run as a part of worker and is responsible for performing actual scaling tasks based on scaling configuration received from the worker.
1 parent 62af5f8 commit 1a90cb0

File tree

11 files changed

+905
-10
lines changed

11 files changed

+905
-10
lines changed

build/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ RUN go mod download
1414
# Copy the go source
1515
COPY build/manager/main.go main.go
1616
COPY api/ api/
17-
COPY internal/controller/ internal/controller/
17+
COPY internal/ internal/
1818
COPY pkg/ pkg/
1919
COPY e-sms/ e-sms/
2020

build/nodeagent/main.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
corev1 "k8s.io/api/core/v1"
4646

4747
"github.com/intel/kubernetes-power-manager/internal/controller"
48+
"github.com/intel/kubernetes-power-manager/internal/scaling"
4849
"github.com/intel/kubernetes-power-manager/pkg/podstate"
4950
// +kubebuilder:scaffold:imports
5051
)
@@ -192,6 +193,12 @@ func main() {
192193
monitoring.RegisterESMICollectors(esmiClient, powerLibrary, logger)
193194
}
194195

196+
cpuScalingMgr := scaling.NewCPUScalingManager(&powerLibrary)
197+
if err = mgr.Add(cpuScalingMgr); err != nil {
198+
setupLog.Error(err, "unable to register runnable", "runable", "CPUScalingManager")
199+
os.Exit(1)
200+
}
201+
195202
if err = (&controller.PowerProfileReconciler{
196203
Client: mgr.GetClient(),
197204
Log: ctrl.Log.WithName("controllers").WithName("PowerProfile"),
@@ -268,10 +275,11 @@ func main() {
268275
os.Exit(1)
269276
}
270277
if err = (&controller.CPUScalingConfigurationReconciler{
271-
Client: mgr.GetClient(),
272-
Log: ctrl.Log.WithName("controllers").WithName("CPUScalingConfiguration"),
273-
Scheme: mgr.GetScheme(),
274-
PowerLibrary: powerLibrary,
278+
Client: mgr.GetClient(),
279+
Log: ctrl.Log.WithName("controllers").WithName("CPUScalingConfiguration"),
280+
Scheme: mgr.GetScheme(),
281+
PowerLibrary: powerLibrary,
282+
CPUScalingManager: cpuScalingMgr,
275283
}).SetupWithManager(mgr); err != nil {
276284
setupLog.Error(err, "unable to create controller", "controller", "CPUScalingConfiguration")
277285
os.Exit(1)

internal/controller/cpuscalingconfiguration_controller.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,17 @@ import (
3131
"sigs.k8s.io/controller-runtime/pkg/predicate"
3232

3333
powerv1 "github.com/intel/kubernetes-power-manager/api/v1"
34+
"github.com/intel/kubernetes-power-manager/internal/scaling"
3435
"github.com/intel/power-optimization-library/pkg/power"
3536
)
3637

3738
// CPUScalingConfigurationReconciler reconciles a CPUScalingConfiguration object
3839
type CPUScalingConfigurationReconciler struct {
3940
client.Client
40-
Log logr.Logger
41-
Scheme *runtime.Scheme
42-
PowerLibrary power.Host
41+
Log logr.Logger
42+
Scheme *runtime.Scheme
43+
PowerLibrary power.Host
44+
CPUScalingManager scaling.CPUScalingManager
4345
}
4446

4547
var (
@@ -81,7 +83,8 @@ func (r *CPUScalingConfigurationReconciler) Reconcile(ctx context.Context, req c
8183
logger.V(5).Info("retrieving the cpu scaling configuration instance")
8284
if err != nil {
8385
if errors.IsNotFound(err) {
84-
// TODO: resource was deleted, inform the scaler
86+
r.CPUScalingManager.StopWorkers()
87+
8588
return ctrl.Result{}, nil
8689
}
8790

@@ -101,7 +104,9 @@ func (r *CPUScalingConfigurationReconciler) Reconcile(ctx context.Context, req c
101104
return ctrl.Result{}, nil
102105
}
103106

104-
// TODO: update the scaler if neccessary
107+
r.CPUScalingManager.UpdateConfig(
108+
r.parseConfig(config.Spec.Items),
109+
)
105110

106111
return ctrl.Result{}, nil
107112
}
@@ -140,6 +145,22 @@ func (r *CPUScalingConfigurationReconciler) validateSamplePeriods(configItems []
140145
return nil
141146
}
142147

148+
func (r *CPUScalingConfigurationReconciler) parseConfig(configItems []powerv1.ConfigItem) []scaling.CPUScalingOpts {
149+
optsList := make([]scaling.CPUScalingOpts, 0)
150+
151+
for _, item := range configItems {
152+
for _, cpuID := range item.CpuIDs {
153+
opts := scaling.CPUScalingOpts{
154+
CpuID: cpuID,
155+
SamplePeriod: item.SamplePeriod.Duration,
156+
}
157+
optsList = append(optsList, opts)
158+
}
159+
}
160+
161+
return optsList
162+
}
163+
143164
// SetupWithManager sets up the controller with the Manager.
144165
func (r *CPUScalingConfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error {
145166
return ctrl.NewControllerManagedBy(mgr).

internal/controller/cpuscalingconfiguration_controller_test.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,31 @@ import (
3434
"k8s.io/apimachinery/pkg/runtime"
3535

3636
powerv1 "github.com/intel/kubernetes-power-manager/api/v1"
37+
"github.com/intel/kubernetes-power-manager/internal/scaling"
3738
"github.com/intel/kubernetes-power-manager/pkg/testutils"
3839
"github.com/intel/power-optimization-library/pkg/power"
3940
"github.com/stretchr/testify/assert"
41+
"github.com/stretchr/testify/mock"
4042
)
4143

4244
const (
4345
nodeName string = "TestNode"
4446
namespace string = "power-manager"
4547
)
4648

49+
type ScalingMgrMock struct {
50+
scaling.CPUScalingManager
51+
mock.Mock
52+
}
53+
54+
func (m *ScalingMgrMock) StopWorkers() {
55+
m.Called()
56+
}
57+
58+
func (m *ScalingMgrMock) UpdateConfig(configs []scaling.CPUScalingOpts) {
59+
m.Called(configs)
60+
}
61+
4762
func createCPUScalingConfigurationReconcilerObject(objs []client.Object) (*CPUScalingConfigurationReconciler, error) {
4863
log.SetLogger(zap.New(
4964
zap.UseDevMode(true),
@@ -63,6 +78,7 @@ func createCPUScalingConfigurationReconcilerObject(objs []client.Object) (*CPUSc
6378
ctrl.Log.WithName("testing"),
6479
scheme.Scheme,
6580
nil,
81+
nil,
6682
}
6783

6884
return r, nil
@@ -210,3 +226,155 @@ func TestCPUScalingConfiguration_Reconcile_Validation(t *testing.T) {
210226
assert.Contains(t, config.Status.Errors, tc.expectedErr)
211227
}
212228
}
229+
230+
func TestCPUScalingConfiguration_Reconcile_parseConfig(t *testing.T) {
231+
configItems := []powerv1.ConfigItem{
232+
{
233+
CpuIDs: []uint{0, 1},
234+
SamplePeriod: metav1.Duration{
235+
Duration: 10 * time.Millisecond,
236+
},
237+
},
238+
{
239+
CpuIDs: []uint{5},
240+
SamplePeriod: metav1.Duration{
241+
Duration: 100 * time.Millisecond,
242+
},
243+
},
244+
}
245+
expectedOpts := []scaling.CPUScalingOpts{
246+
{
247+
CpuID: 0,
248+
SamplePeriod: 10 * time.Millisecond,
249+
},
250+
{
251+
CpuID: 1,
252+
SamplePeriod: 10 * time.Millisecond,
253+
},
254+
{
255+
CpuID: 5,
256+
SamplePeriod: 100 * time.Millisecond,
257+
},
258+
}
259+
260+
r, err := createCPUScalingConfigurationReconcilerObject([]client.Object{})
261+
assert.Nil(t, err)
262+
263+
config := r.parseConfig(configItems)
264+
265+
assert.ElementsMatch(t, expectedOpts, config)
266+
}
267+
268+
func TestCPUScalingConfiguration_Reconcile_Success(t *testing.T) {
269+
t.Setenv("NODE_NAME", nodeName)
270+
271+
req := reconcile.Request{
272+
NamespacedName: client.ObjectKey{
273+
Name: nodeName,
274+
Namespace: namespace,
275+
},
276+
}
277+
config := &powerv1.CPUScalingConfiguration{
278+
ObjectMeta: metav1.ObjectMeta{
279+
Name: nodeName,
280+
Namespace: namespace,
281+
UID: "test",
282+
},
283+
Spec: powerv1.CPUScalingConfigurationSpec{
284+
Items: []powerv1.ConfigItem{
285+
{
286+
CpuIDs: []uint{0, 1},
287+
SamplePeriod: metav1.Duration{
288+
Duration: 10 * time.Millisecond,
289+
},
290+
},
291+
{
292+
CpuIDs: []uint{5},
293+
SamplePeriod: metav1.Duration{
294+
Duration: 100 * time.Millisecond,
295+
},
296+
},
297+
},
298+
},
299+
}
300+
expectedOpts := []scaling.CPUScalingOpts{
301+
{
302+
CpuID: 0,
303+
SamplePeriod: 10 * time.Millisecond,
304+
},
305+
{
306+
CpuID: 1,
307+
SamplePeriod: 10 * time.Millisecond,
308+
},
309+
{
310+
CpuID: 5,
311+
SamplePeriod: 100 * time.Millisecond,
312+
},
313+
}
314+
315+
cpuListMock := power.CpuList{}
316+
for i := 0; i < 10; i++ {
317+
mockCore := new(testutils.MockCPU)
318+
mockCore.On("GetID").Return(uint(i))
319+
cpuListMock = append(cpuListMock, mockCore)
320+
}
321+
nodemk := new(testutils.MockHost)
322+
nodemk.On("GetAllCpus").Return(&cpuListMock)
323+
324+
mgrmk := new(ScalingMgrMock)
325+
mgrmk.On("UpdateConfig", expectedOpts).Return()
326+
mgrmk.On("StopWorkers").Return()
327+
328+
r, err := createCPUScalingConfigurationReconcilerObject([]client.Object{config})
329+
assert.Nil(t, err)
330+
331+
r.PowerLibrary = nodemk
332+
r.CPUScalingManager = mgrmk
333+
334+
_, err = r.Reconcile(context.TODO(), req)
335+
assert.Nil(t, err)
336+
mgrmk.AssertNotCalled(t, "StopWorkers")
337+
mgrmk.AssertCalled(t, "UpdateConfig", expectedOpts)
338+
339+
err = r.Client.Get(context.TODO(), client.ObjectKey{
340+
Name: nodeName,
341+
Namespace: namespace,
342+
}, config)
343+
assert.Nil(t, err)
344+
assert.Empty(t, config.Status.Errors)
345+
}
346+
347+
func TestCPUScalingConfiguration_Reconcile_NotFound(t *testing.T) {
348+
t.Setenv("NODE_NAME", nodeName)
349+
350+
req := reconcile.Request{
351+
NamespacedName: client.ObjectKey{
352+
Name: nodeName,
353+
Namespace: namespace,
354+
},
355+
}
356+
357+
cpuListMock := power.CpuList{}
358+
for i := 0; i < 10; i++ {
359+
mockCore := new(testutils.MockCPU)
360+
mockCore.On("GetID").Return(uint(i))
361+
cpuListMock = append(cpuListMock, mockCore)
362+
}
363+
nodemk := new(testutils.MockHost)
364+
nodemk.On("GetAllCpus").Return(&cpuListMock)
365+
366+
mgrmk := new(ScalingMgrMock)
367+
mgrmk.On("UpdateConfig").Return()
368+
mgrmk.On("StopWorkers").Return()
369+
370+
r, err := createCPUScalingConfigurationReconcilerObject([]client.Object{})
371+
assert.Nil(t, err)
372+
373+
r.PowerLibrary = nodemk
374+
r.CPUScalingManager = mgrmk
375+
376+
_, err = r.Reconcile(context.TODO(), req)
377+
assert.Nil(t, err)
378+
mgrmk.AssertCalled(t, "StopWorkers")
379+
mgrmk.AssertNotCalled(t, "UpdateConfig")
380+
}

internal/scaling/common.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package scaling
2+
3+
import "time"
4+
5+
type CPUScalingOpts struct {
6+
CpuID uint
7+
SamplePeriod time.Duration
8+
}

0 commit comments

Comments
 (0)