Skip to content

Commit 9a781ed

Browse files
committed
Add support for multiple resource limits
1 parent 9ebe338 commit 9a781ed

File tree

13 files changed

+237
-321
lines changed

13 files changed

+237
-321
lines changed

cluster-autoscaler/cloudprovider/resource_limiter.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,6 @@ func (r *ResourceLimiter) AppliesTo(node *apiv1.Node) bool {
9999
return true
100100
}
101101

102-
func (r *ResourceLimiter) MaxLimits() map[string]int64 {
102+
func (r *ResourceLimiter) Limits() map[string]int64 {
103103
return r.maxLimits
104104
}
105-
106-
func (r *ResourceLimiter) MinLimits() map[string]int64 {
107-
return r.minLimits
108-
}

cluster-autoscaler/resourcelimits/factory.go

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

cluster-autoscaler/resourcelimits/provider.go

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

cluster-autoscaler/resourcelimits/provider_test.go

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package resourcequotas
2+
3+
import (
4+
"fmt"
5+
6+
corev1 "k8s.io/api/core/v1"
7+
"k8s.io/autoscaler/cluster-autoscaler/context"
8+
"k8s.io/autoscaler/cluster-autoscaler/processors/customresources"
9+
)
10+
11+
// TrackerFactory builds trackers.
12+
type TrackerFactory struct {
13+
crp customresources.CustomResourcesProcessor
14+
quotaProviders []Provider
15+
usageCalculator *usageCalculator
16+
}
17+
18+
type TrackerOptions struct {
19+
CRP customresources.CustomResourcesProcessor
20+
Providers []Provider
21+
NodeFilter NodeFilter
22+
}
23+
24+
// NewTrackerFactory creates a new TrackerFactory.
25+
func NewTrackerFactory(opts TrackerOptions) *TrackerFactory {
26+
uc := newUsageCalculator(opts.CRP, opts.NodeFilter)
27+
return &TrackerFactory{
28+
crp: opts.CRP,
29+
quotaProviders: opts.Providers,
30+
usageCalculator: uc,
31+
}
32+
}
33+
34+
// NewQuotasTracker builds a new Tracker.
35+
func (f *TrackerFactory) NewQuotasTracker(ctx *context.AutoscalingContext, nodes []*corev1.Node) (*Tracker, error) {
36+
quotas, err := f.quotas()
37+
if err != nil {
38+
return nil, err
39+
}
40+
usages, err := f.usageCalculator.calculateUsages(ctx, nodes, quotas)
41+
if err != nil {
42+
return nil, err
43+
}
44+
limitsLeft := make(map[string]resourceList)
45+
for _, rq := range quotas {
46+
limitsLeft[rq.ID()] = make(resourceList)
47+
limits := rq.Limits()
48+
for resourceType, limit := range limits {
49+
usage := usages[rq.ID()][resourceType]
50+
limitsLeft[rq.ID()][resourceType] = max(0, limit-usage)
51+
}
52+
}
53+
tracker := newTracker(f.crp, quotas, limitsLeft)
54+
return tracker, nil
55+
}
56+
57+
func (f *TrackerFactory) quotas() ([]Quota, error) {
58+
var quotas []Quota
59+
for _, provider := range f.quotaProviders {
60+
provQuotas, err := provider.Quotas()
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to get quotas from provider: %w", err)
63+
}
64+
for _, rq := range provQuotas {
65+
quotas = append(quotas, rq)
66+
}
67+
}
68+
return quotas, nil
69+
}

cluster-autoscaler/resourcelimits/factory_test.go renamed to cluster-autoscaler/resourcequotas/factory_test.go

Lines changed: 3 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package resourcelimits
1+
package resourcequotas
22

33
import (
44
"testing"
@@ -209,90 +209,10 @@ func TestMaxLimitsTracker(t *testing.T) {
209209
}
210210
factory := NewTrackerFactory(TrackerOptions{
211211
CRP: crp,
212-
Providers: []Provider{NewCloudLimitersProvider(cloudProvider)},
212+
Providers: []Provider{NewCloudQuotasProvider(cloudProvider)},
213213
NodeFilter: tc.nodeFilter,
214214
})
215-
tracker, err := factory.NewMaxLimitsTracker(ctx, tc.nodes)
216-
if err != nil {
217-
t.Errorf("failed to create tracker: %v", err)
218-
}
219-
var ng cloudprovider.NodeGroup
220-
result, err := tracker.CheckDelta(ctx, ng, tc.newNode, tc.nodeDelta)
221-
if err != nil {
222-
t.Errorf("failed to check delta: %v", err)
223-
}
224-
if diff := cmp.Diff(tc.wantResult, result, cmpopts.SortSlices(func(a, b string) bool { return a < b }), cmpopts.EquateEmpty()); diff != "" {
225-
t.Errorf("CheckDelta() mismatch (-want +got):\n%s", diff)
226-
}
227-
})
228-
}
229-
}
230-
231-
func TestMinLimitsTracker(t *testing.T) {
232-
testCases := []struct {
233-
name string
234-
crp customresources.CustomResourcesProcessor
235-
nodeFilter NodeFilter
236-
nodes []*apiv1.Node
237-
limits map[string]int64
238-
newNode *apiv1.Node
239-
nodeDelta int
240-
wantResult *CheckDeltaResult
241-
}{
242-
{
243-
name: "default config allowed operation",
244-
nodes: []*apiv1.Node{
245-
test.BuildTestNode("n1", 1000, 2*units.GiB),
246-
test.BuildTestNode("n2", 2000, 4*units.GiB),
247-
test.BuildTestNode("n3", 3000, 8*units.GiB),
248-
},
249-
limits: map[string]int64{
250-
"cpu": 1,
251-
"memory": 2 * units.GiB,
252-
},
253-
newNode: test.BuildTestNode("n1", 1000, 2*units.GiB),
254-
nodeDelta: 1,
255-
wantResult: &CheckDeltaResult{
256-
AllowedDelta: 1,
257-
},
258-
},
259-
{
260-
name: "default config exceeded operation",
261-
nodes: []*apiv1.Node{
262-
test.BuildTestNode("n1", 1000, 2*units.GiB),
263-
test.BuildTestNode("n2", 2000, 4*units.GiB),
264-
test.BuildTestNode("n3", 3000, 8*units.GiB),
265-
},
266-
limits: map[string]int64{
267-
"cpu": 6,
268-
"memory": 10 * units.GiB,
269-
},
270-
newNode: test.BuildTestNode("n3", 3000, 8*units.GiB),
271-
nodeDelta: 1,
272-
wantResult: &CheckDeltaResult{
273-
AllowedDelta: 0,
274-
ExceededResources: map[string][]string{
275-
"cluster-wide": {"cpu", "memory"},
276-
},
277-
},
278-
},
279-
}
280-
for _, tc := range testCases {
281-
t.Run(tc.name, func(t *testing.T) {
282-
cloudProvider := cptest.NewTestCloudProviderBuilder().Build()
283-
resourceLimiter := cloudprovider.NewResourceLimiter(tc.limits, nil)
284-
cloudProvider.SetResourceLimiter(resourceLimiter)
285-
ctx := &context.AutoscalingContext{CloudProvider: cloudProvider}
286-
crp := tc.crp
287-
if crp == nil {
288-
crp = &fakeCustomResourcesProcessor{}
289-
}
290-
factory := NewTrackerFactory(TrackerOptions{
291-
CRP: crp,
292-
Providers: []Provider{NewCloudLimitersProvider(cloudProvider)},
293-
NodeFilter: tc.nodeFilter,
294-
})
295-
tracker, err := factory.NewMinLimitsTracker(ctx, tc.nodes)
215+
tracker, err := factory.NewQuotasTracker(ctx, tc.nodes)
296216
if err != nil {
297217
t.Errorf("failed to create tracker: %v", err)
298218
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package resourcequotas
2+
3+
import (
4+
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
5+
)
6+
7+
type Provider interface {
8+
Quotas() ([]Quota, error)
9+
}
10+
type CloudQuotasProvider struct {
11+
cloudProvider cloudprovider.CloudProvider
12+
}
13+
14+
func (p *CloudQuotasProvider) Quotas() ([]Quota, error) {
15+
rl, err := p.cloudProvider.GetResourceLimiter()
16+
if err != nil {
17+
return nil, err
18+
}
19+
return []Quota{rl}, nil
20+
}
21+
22+
func NewCloudQuotasProvider(cloudProvider cloudprovider.CloudProvider) *CloudQuotasProvider {
23+
return &CloudQuotasProvider{
24+
cloudProvider: cloudProvider,
25+
}
26+
}

0 commit comments

Comments
 (0)