...
1
16
17 package namedresources
18
19 import (
20 "context"
21 "errors"
22 "fmt"
23 "slices"
24
25 resourceapi "k8s.io/api/resource/v1alpha2"
26 "k8s.io/apiserver/pkg/cel/environment"
27 "k8s.io/dynamic-resource-allocation/structured/namedresources/cel"
28 )
29
30
31
32
33 type Model struct {
34 Instances []InstanceAllocation
35 }
36
37 type InstanceAllocation struct {
38 Allocated bool
39 Instance *resourceapi.NamedResourcesInstance
40 }
41
42
43
44 func AddResources(m *Model, resources *resourceapi.NamedResourcesResources) {
45 if resources == nil {
46 return
47 }
48
49 for i := range resources.Instances {
50 m.Instances = append(m.Instances, InstanceAllocation{Instance: &resources.Instances[i]})
51 }
52 }
53
54
55
56 func AddAllocation(m *Model, result *resourceapi.NamedResourcesAllocationResult) {
57 if result == nil {
58 return
59 }
60 for i := range m.Instances {
61 if m.Instances[i].Instance.Name == result.Name {
62 m.Instances[i].Allocated = true
63 break
64 }
65 }
66 }
67
68 func NewClaimController(filter *resourceapi.NamedResourcesFilter, requests []*resourceapi.NamedResourcesRequest) (*Controller, error) {
69 c := &Controller{}
70 if filter != nil {
71 compilation := cel.Compiler.CompileCELExpression(filter.Selector, environment.StoredExpressions)
72 if compilation.Error != nil {
73
74 return nil, fmt.Errorf("compile class filter CEL expression: %w", compilation.Error)
75 }
76 c.filter = &compilation
77 }
78 for _, request := range requests {
79 compilation := cel.Compiler.CompileCELExpression(request.Selector, environment.StoredExpressions)
80 if compilation.Error != nil {
81
82 return nil, fmt.Errorf("compile request CEL expression: %w", compilation.Error)
83 }
84 c.requests = append(c.requests, compilation)
85 }
86 return c, nil
87 }
88
89 type Controller struct {
90 filter *cel.CompilationResult
91 requests []cel.CompilationResult
92 }
93
94 func (c *Controller) NodeIsSuitable(ctx context.Context, model Model) (bool, error) {
95 indices, err := c.allocate(ctx, model)
96 return len(indices) == len(c.requests), err
97 }
98
99 func (c *Controller) Allocate(ctx context.Context, model Model) ([]*resourceapi.NamedResourcesAllocationResult, error) {
100 indices, err := c.allocate(ctx, model)
101 if err != nil {
102 return nil, err
103 }
104 if len(indices) != len(c.requests) {
105 return nil, errors.New("insufficient resources")
106 }
107 results := make([]*resourceapi.NamedResourcesAllocationResult, len(c.requests))
108 for i := range c.requests {
109 results[i] = &resourceapi.NamedResourcesAllocationResult{Name: model.Instances[indices[i]].Instance.Name}
110 }
111 return results, nil
112 }
113
114 func (c *Controller) allocate(ctx context.Context, model Model) ([]int, error) {
115
116 instances := slices.Clone(model.Instances)
117 indices := make([]int, 0, len(c.requests))
118
119 for _, request := range c.requests {
120 for i, instance := range instances {
121 if instance.Allocated {
122 continue
123 }
124 if c.filter != nil {
125 okay, err := c.filter.Evaluate(ctx, instance.Instance.Attributes)
126 if err != nil {
127 return nil, fmt.Errorf("evaluate filter CEL expression: %w", err)
128 }
129 if !okay {
130 continue
131 }
132 }
133 okay, err := request.Evaluate(ctx, instance.Instance.Attributes)
134 if err != nil {
135 return nil, fmt.Errorf("evaluate request CEL expression: %w", err)
136 }
137 if !okay {
138 continue
139 }
140
141
142
143
144
145
146 instances[i].Allocated = true
147 indices = append(indices, i)
148 break
149 }
150 }
151 return indices, nil
152
153 }
154
View as plain text