@@ -24,6 +24,7 @@ import (
24
24
"sync"
25
25
"time"
26
26
27
+ "github.com/awslabs/operatorpkg/option"
27
28
"github.com/awslabs/operatorpkg/status"
28
29
"github.com/samber/lo"
29
30
corev1 "k8s.io/api/core/v1"
@@ -40,6 +41,10 @@ import (
40
41
"sigs.k8s.io/karpenter/pkg/utils/resources"
41
42
)
42
43
44
+ func init () {
45
+ v1 .WellKnownLabels = v1 .WellKnownLabels .Insert (v1alpha1 .LabelReservationID )
46
+ }
47
+
43
48
var _ cloudprovider.CloudProvider = (* CloudProvider )(nil )
44
49
45
50
type CloudProvider struct {
@@ -61,14 +66,17 @@ type CloudProvider struct {
61
66
Drifted cloudprovider.DriftReason
62
67
NodeClassGroupVersionKind []schema.GroupVersionKind
63
68
RepairPolicy []cloudprovider.RepairPolicy
69
+
70
+ ReservationManagerProvider * ReservationManagerProvider
64
71
}
65
72
66
73
func NewCloudProvider () * CloudProvider {
67
74
return & CloudProvider {
68
- AllowedCreateCalls : math .MaxInt ,
69
- CreatedNodeClaims : map [string ]* v1.NodeClaim {},
70
- InstanceTypesForNodePool : map [string ][]* cloudprovider.InstanceType {},
71
- ErrorsForNodePool : map [string ]error {},
75
+ AllowedCreateCalls : math .MaxInt ,
76
+ CreatedNodeClaims : map [string ]* v1.NodeClaim {},
77
+ InstanceTypesForNodePool : map [string ][]* cloudprovider.InstanceType {},
78
+ ErrorsForNodePool : map [string ]error {},
79
+ ReservationManagerProvider : NewReservationManagerProvider (),
72
80
}
73
81
}
74
82
@@ -102,6 +110,7 @@ func (c *CloudProvider) Reset() {
102
110
TolerationDuration : 30 * time .Minute ,
103
111
},
104
112
}
113
+ c .ReservationManagerProvider .Reset ()
105
114
}
106
115
107
116
func (c * CloudProvider ) Create (ctx context.Context , nodeClaim * v1.NodeClaim ) (* v1.NodeClaim , error ) {
@@ -139,14 +148,19 @@ func (c *CloudProvider) Create(ctx context.Context, nodeClaim *v1.NodeClaim) (*v
139
148
labels [key ] = requirement .Values ()[0 ]
140
149
}
141
150
}
142
- // Find Offering
143
- for _ , o := range instanceType .Offerings .Available () {
144
- if reqs .IsCompatible (o .Requirements , scheduling .AllowUndefinedWellKnownLabels ) {
145
- labels [corev1 .LabelTopologyZone ] = o .Requirements .Get (corev1 .LabelTopologyZone ).Any ()
146
- labels [v1 .CapacityTypeLabelKey ] = o .Requirements .Get (v1 .CapacityTypeLabelKey ).Any ()
147
- break
151
+ // Find offering, prioritizing reserved instances
152
+ offering := func () cloudprovider.Offering {
153
+ offerings := instanceType .Offerings .Available ().Compatible (reqs )
154
+ lo .Must0 (len (offerings ) != 0 , "created nodeclaim with no available offerings" )
155
+ if reservedOfferings := offerings .WithCapacityType (v1 .CapacityTypeReserved ); len (reservedOfferings ) != 0 {
156
+ c .ReservationManagerProvider .create (reservedOfferings [0 ].Requirements .Get (v1alpha1 .LabelReservationID ).Any ())
157
+ return reservedOfferings [0 ]
148
158
}
149
- }
159
+ return offerings [0 ]
160
+ }()
161
+ labels [corev1 .LabelTopologyZone ] = offering .Requirements .Get (corev1 .LabelTopologyZone ).Any ()
162
+ labels [v1 .CapacityTypeLabelKey ] = offering .Requirements .Get (v1 .CapacityTypeLabelKey ).Any ()
163
+
150
164
created := & v1.NodeClaim {
151
165
ObjectMeta : metav1.ObjectMeta {
152
166
Name : nodeClaim .Name ,
@@ -189,7 +203,8 @@ func (c *CloudProvider) List(_ context.Context) ([]*v1.NodeClaim, error) {
189
203
}), nil
190
204
}
191
205
192
- func (c * CloudProvider ) GetInstanceTypes (_ context.Context , np * v1.NodePool ) ([]* cloudprovider.InstanceType , error ) {
206
+ // Note: this fake implementation does **not** support availability snapshots. The burden of testing snapshot support should be on the cloudprovider implementation.
207
+ func (c * CloudProvider ) GetInstanceTypes (_ context.Context , np * v1.NodePool , opts ... option.Function [cloudprovider.GetInstanceTypeOptions ]) ([]* cloudprovider.InstanceType , error ) {
193
208
if np != nil {
194
209
if err , ok := c .ErrorsForNodePool [np .Name ]; ok {
195
210
return nil , err
@@ -200,7 +215,23 @@ func (c *CloudProvider) GetInstanceTypes(_ context.Context, np *v1.NodePool) ([]
200
215
}
201
216
}
202
217
if c .InstanceTypes != nil {
203
- return c .InstanceTypes , nil
218
+ return lo .Map (c .InstanceTypes , func (it * cloudprovider.InstanceType , _ int ) * cloudprovider.InstanceType {
219
+ for i := range it .Offerings {
220
+ if it .Offerings [i ].Requirements .Get (v1 .CapacityTypeLabelKey ).Any () != v1 .CapacityTypeReserved {
221
+ continue
222
+ }
223
+ lo .Must0 (
224
+ it .Offerings [i ].Requirements .Has (v1alpha1 .LabelReservationID ),
225
+ "reserved offering for instance type %s must define requirement for label %s" ,
226
+ it .Name ,
227
+ v1alpha1 .LabelReservationID ,
228
+ )
229
+ reservationID := it .Offerings [i ].Requirements .Get (v1alpha1 .LabelReservationID ).Any ()
230
+ it .Offerings [i ].ReservationManager = c .ReservationManagerProvider .ReservationManager (reservationID , opts ... )
231
+ it .Offerings [i ].Available = c .ReservationManagerProvider .Capacity (reservationID ) > 0
232
+ }
233
+ return it
234
+ }), nil
204
235
}
205
236
return []* cloudprovider.InstanceType {
206
237
NewInstanceType (InstanceTypeOptions {
0 commit comments