Skip to content

Commit 1a13926

Browse files
committed
feat: example capped resources handler
1 parent 81f602a commit 1a13926

File tree

6 files changed

+120
-73
lines changed

6 files changed

+120
-73
lines changed

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,7 @@ Additional flags:
4949

5050
### Example Implementation
5151

52-
The repository includes an example handler implementation in `example/resourceslice/handler.go` that:
53-
54-
- Generates CPU resources between 1 and 10 cores
55-
- Generates Memory resources between 1 and 5 GB
56-
- Allocates 110 pods
57-
- Uses a deterministic hash of the ResourceSlice name for consistent resource allocation
52+
The repository includes an example handler implementation in `examples/cappedresources/handler.go`, that accepts every resource request but caps the amount of resources the resource slice can use if they exceed the configured thresholds.
5853

5954
## Creating Custom Handlers
6055

example/resourceslice/handler.go

Lines changed: 0 additions & 63 deletions
This file was deleted.

examples/cappedresources/doc.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package cappedresources contains an handler that accepts every resource request but caps
2+
// the amount of resources the resource slice can use if they exceed the configured thresholds.
3+
package cappedresources
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cappedresources
2+
3+
import (
4+
"context"
5+
6+
authv1beta1 "github.com/liqotech/liqo/apis/authentication/v1beta1"
7+
corev1 "k8s.io/api/core/v1"
8+
"k8s.io/klog/v2"
9+
ctrl "sigs.k8s.io/controller-runtime"
10+
11+
rshandler "github.com/liqotech/resource-slice-class-controller-template/pkg/resourceslice/handler"
12+
)
13+
14+
// Handler implements the Handler interface for ResourceSlice.
15+
type Handler struct {
16+
capResources corev1.ResourceList
17+
}
18+
19+
// NewHandler creates a new capped resources handler.
20+
func NewHandler(maxResources corev1.ResourceList) rshandler.Handler {
21+
return &Handler{
22+
capResources: maxResources,
23+
}
24+
}
25+
26+
// Handle processes the ResourceSlice.
27+
func (h *Handler) Handle(_ context.Context, resourceSlice *authv1beta1.ResourceSlice) (ctrl.Result, error) {
28+
// Generate and update resources in status
29+
resources := h.getCappedResources(resourceSlice.Spec.Resources)
30+
31+
resourceSlice.Status.Resources = resources
32+
33+
klog.InfoS("Updated ResourceSlice status",
34+
"name", resourceSlice.Name,
35+
"namespace", resourceSlice.Namespace,
36+
"cpu", resources.Cpu().String(),
37+
"memory", resources.Memory().String(),
38+
"pods", resources.Pods().String())
39+
40+
return ctrl.Result{}, nil
41+
}
42+
43+
// getCappedResources sets the requested resources, but caps the amount of resources
44+
// to the maximum resources defined in the handler.
45+
func (h *Handler) getCappedResources(reqResources corev1.ResourceList) corev1.ResourceList {
46+
cappedResources := corev1.ResourceList{}
47+
48+
for name, reqQuantity := range reqResources {
49+
capQuantity, ok := h.capResources[name]
50+
if ok && reqQuantity.Cmp(capQuantity) > 0 {
51+
cappedResources[name] = capQuantity
52+
} else {
53+
cappedResources[name] = reqQuantity
54+
}
55+
}
56+
57+
return cappedResources
58+
}

main.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
// Package main contains an an example main to setup and run a controller handling a ResourceSlice class
1+
// Package main contains an an example main to setup and run a controller handling a ResourceSlice class.
22
package main
33

44
import (
55
"flag"
66
"os"
77

88
authv1beta1 "github.com/liqotech/liqo/apis/authentication/v1beta1"
9+
corev1 "k8s.io/api/core/v1"
10+
"k8s.io/apimachinery/pkg/api/resource"
911
"k8s.io/apimachinery/pkg/runtime"
1012
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
1113
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
@@ -14,7 +16,7 @@ import (
1416
"sigs.k8s.io/controller-runtime/pkg/healthz"
1517
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
1618

17-
examplehandler "github.com/liqotech/resource-slice-class-controller-template/example/resourceslice"
19+
cappedresources "github.com/liqotech/resource-slice-class-controller-template/examples/cappedresources"
1820
"github.com/liqotech/resource-slice-class-controller-template/pkg/controller"
1921
)
2022

@@ -66,7 +68,18 @@ func main() {
6668
}
6769

6870
// Create the handler
69-
rsHandler := examplehandler.NewHandler()
71+
cappedQuantities := map[corev1.ResourceName]string{
72+
corev1.ResourceCPU: "4",
73+
corev1.ResourceMemory: "8Gi",
74+
corev1.ResourcePods: "110",
75+
corev1.ResourceEphemeralStorage: "20Gi",
76+
}
77+
parsedQuantities, err := parseQuantities(cappedQuantities)
78+
if err != nil {
79+
klog.Errorf("unable to parse quantities: %v", err)
80+
os.Exit(1)
81+
}
82+
rsHandler := cappedresources.NewHandler(parsedQuantities)
7083

7184
if err = controller.NewResourceSliceReconciler(
7285
mgr.GetClient(),
@@ -94,3 +107,15 @@ func main() {
94107
os.Exit(1)
95108
}
96109
}
110+
111+
func parseQuantities(quantities map[corev1.ResourceName]string) (corev1.ResourceList, error) {
112+
resources := corev1.ResourceList{}
113+
for name, quantity := range quantities {
114+
qnt, err := resource.ParseQuantity(quantity)
115+
if err != nil {
116+
return nil, err
117+
}
118+
resources[name] = qnt
119+
}
120+
return resources, nil
121+
}

pkg/controller/resource_slice_controller.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ package controller
44
import (
55
"context"
66
"fmt"
7+
"strconv"
78

89
authv1beta1 "github.com/liqotech/liqo/apis/authentication/v1beta1"
10+
"github.com/liqotech/liqo/pkg/consts"
911
"github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1013
"k8s.io/apimachinery/pkg/runtime"
1114
"k8s.io/client-go/tools/record"
1215
"k8s.io/klog/v2"
1316
ctrl "sigs.k8s.io/controller-runtime"
17+
"sigs.k8s.io/controller-runtime/pkg/builder"
1418
"sigs.k8s.io/controller-runtime/pkg/client"
1519
"sigs.k8s.io/controller-runtime/pkg/predicate"
1620

@@ -40,8 +44,16 @@ func NewResourceSliceReconciler(cl client.Client, scheme *runtime.Scheme, record
4044

4145
// SetupWithManager sets up the controller with the Manager.
4246
func (r *ResourceSliceReconciler) SetupWithManager(mgr ctrl.Manager) error {
47+
// generate the predicate to filter just the ResourceSlices that are replicated
48+
// (i.e., the ones for which we have the role of provider)
49+
replicatedResSliceFilter, err := predicate.LabelSelectorPredicate(replicatedResourcesLabelSelector())
50+
if err != nil {
51+
klog.Error(err)
52+
return err
53+
}
54+
4355
return ctrl.NewControllerManagedBy(mgr).
44-
For(&authv1beta1.ResourceSlice{}).
56+
For(&authv1beta1.ResourceSlice{}, builder.WithPredicates(replicatedResSliceFilter)).
4557
WithEventFilter(predicate.NewPredicateFuncs(func(obj client.Object) bool {
4658
resourceSlice, ok := obj.(*authv1beta1.ResourceSlice)
4759
if !ok {
@@ -101,3 +113,20 @@ func (r *ResourceSliceReconciler) Reconcile(ctx context.Context, req ctrl.Reques
101113
// Return the reconciliation result
102114
return res, nil
103115
}
116+
117+
// replicatedResourcesLabelSelector is an helper function which returns a label selector to list all the replicated resources.
118+
func replicatedResourcesLabelSelector() metav1.LabelSelector {
119+
return metav1.LabelSelector{
120+
MatchExpressions: []metav1.LabelSelectorRequirement{
121+
{
122+
Key: consts.ReplicationOriginLabel,
123+
Operator: metav1.LabelSelectorOpExists,
124+
},
125+
{
126+
Key: consts.ReplicationStatusLabel,
127+
Operator: metav1.LabelSelectorOpIn,
128+
Values: []string{strconv.FormatBool(true)},
129+
},
130+
},
131+
}
132+
}

0 commit comments

Comments
 (0)