@@ -35,6 +35,7 @@ import (
35
35
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
36
36
appsv1 "k8s.io/api/apps/v1"
37
37
v1 "k8s.io/api/core/v1"
38
+ resourcealphaapi "k8s.io/api/resource/v1alpha3"
38
39
resourceapi "k8s.io/api/resource/v1beta1"
39
40
apierrors "k8s.io/apimachinery/pkg/api/errors"
40
41
"k8s.io/apimachinery/pkg/api/resource"
@@ -1280,6 +1281,88 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
1280
1281
prioritizedListTests ()
1281
1282
})
1282
1283
1284
+ framework .Context ("with device taints" , feature .DRADeviceTaints , framework .WithFeatureGate (features .DRADeviceTaints ), func () {
1285
+ nodes := NewNodes (f , 1 , 1 )
1286
+ driver := NewDriver (f , nodes , func () Resources {
1287
+ return Resources {
1288
+ Tainted : true ,
1289
+ }
1290
+ })
1291
+ b := newBuilder (f , driver )
1292
+
1293
+ f .It ("DeviceTaint keeps pod pending" , func (ctx context.Context ) {
1294
+ pod , template := b .podInline ()
1295
+ b .create (ctx , pod , template )
1296
+ framework .ExpectNoError (e2epod .WaitForPodNameUnschedulableInNamespace (ctx , f .ClientSet , pod .Name , f .Namespace .Name ))
1297
+ })
1298
+
1299
+ f .It ("DeviceToleration enables pod scheduling" , func (ctx context.Context ) {
1300
+ pod , template := b .podInline ()
1301
+ template .Spec .Spec .Devices .Requests [0 ].Tolerations = []resourceapi.DeviceToleration {{
1302
+ Effect : resourceapi .DeviceTaintEffectNoSchedule ,
1303
+ Operator : resourceapi .DeviceTolerationOpExists ,
1304
+ // No key: tolerate *all* taints with this effect.
1305
+ }}
1306
+ b .create (ctx , pod , template )
1307
+ b .testPod (ctx , f , pod )
1308
+ })
1309
+
1310
+ f .It ("DeviceTaintRule evicts pod" , func (ctx context.Context ) {
1311
+ pod , template := b .podInline ()
1312
+ template .Spec .Spec .Devices .Requests [0 ].Tolerations = []resourceapi.DeviceToleration {{
1313
+ Effect : resourceapi .DeviceTaintEffectNoSchedule ,
1314
+ Operator : resourceapi .DeviceTolerationOpExists ,
1315
+ // No key: tolerate *all* taints with this effect.
1316
+ }}
1317
+ // Add a finalizer to ensure that we get a chance to test the pod status after eviction (= deletion).
1318
+ pod .Finalizers = []string {"e2e-test/dont-delete-me" }
1319
+ b .create (ctx , pod , template )
1320
+ b .testPod (ctx , f , pod )
1321
+ ginkgo .DeferCleanup (func (ctx context.Context ) {
1322
+ // Unblock shutdown by removing the finalizer.
1323
+ pod , err := f .ClientSet .CoreV1 ().Pods (f .Namespace .Name ).Get (ctx , pod .Name , metav1.GetOptions {})
1324
+ framework .ExpectNoError (err , "get pod" )
1325
+ pod .Finalizers = nil
1326
+ _ , err = f .ClientSet .CoreV1 ().Pods (f .Namespace .Name ).Update (ctx , pod , metav1.UpdateOptions {})
1327
+ framework .ExpectNoError (err , "remove finalizers from pod" )
1328
+ })
1329
+
1330
+ // Now evict it.
1331
+ ginkgo .By ("Evicting pod..." )
1332
+ taint := & resourcealphaapi.DeviceTaintRule {
1333
+ ObjectMeta : metav1.ObjectMeta {
1334
+ GenerateName : "device-taint-rule-" + f .UniqueName + "-" ,
1335
+ },
1336
+ Spec : resourcealphaapi.DeviceTaintRuleSpec {
1337
+ // All devices of the current driver instance.
1338
+ DeviceSelector : & resourcealphaapi.DeviceTaintSelector {
1339
+ Driver : & driver .Name ,
1340
+ },
1341
+ Taint : resourcealphaapi.DeviceTaint {
1342
+ Effect : resourcealphaapi .DeviceTaintEffectNoExecute ,
1343
+ Key : "test.example.com/evict" ,
1344
+ Value : "now" ,
1345
+ // No TimeAdded, gets defaulted.
1346
+ },
1347
+ },
1348
+ }
1349
+ createdTaint := b .create (ctx , taint )
1350
+ taint = createdTaint [0 ].(* resourcealphaapi.DeviceTaintRule )
1351
+ gomega .Expect (* taint ).Should (gomega .HaveField ("Spec.Taint.TimeAdded.Time" , gomega .BeTemporally ("~" , time .Now (), time .Minute /* allow for some clock drift and delays */ )))
1352
+
1353
+ framework .ExpectNoError (e2epod .WaitForPodTerminatingInNamespaceTimeout (ctx , f .ClientSet , pod .Name , f .Namespace .Name , f .Timeouts .PodStart ))
1354
+ pod , err := f .ClientSet .CoreV1 ().Pods (f .Namespace .Name ).Get (ctx , pod .Name , metav1.GetOptions {})
1355
+ framework .ExpectNoError (err , "get pod" )
1356
+ gomega .Expect (pod ).Should (gomega .HaveField ("Status.Conditions" , gomega .ContainElement (gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
1357
+ // LastTransitionTime is unknown.
1358
+ "Type" : gomega .Equal (v1 .DisruptionTarget ),
1359
+ "Status" : gomega .Equal (v1 .ConditionTrue ),
1360
+ "Reason" : gomega .Equal ("DeletionByDeviceTaintManager" ),
1361
+ "Message" : gomega .Equal ("Device Taint manager: deleting due to NoExecute taint" ),
1362
+ }))))
1363
+ })
1364
+ })
1365
+
1283
1366
// TODO (https://github.com/kubernetes/kubernetes/issues/123699): move most of the test below into `testDriver` so that they get
1284
1367
// executed with different parameters.
1285
1368
@@ -2006,6 +2089,12 @@ func (b *builder) create(ctx context.Context, objs ...klog.KMetadata) []klog.KMe
2006
2089
err := b .f .ClientSet .ResourceV1beta1 ().ResourceSlices ().Delete (ctx , createdObj .GetName (), metav1.DeleteOptions {})
2007
2090
framework .ExpectNoError (err , "delete node resource slice" )
2008
2091
})
2092
+ case * resourcealphaapi.DeviceTaintRule :
2093
+ createdObj , err = b .f .ClientSet .ResourceV1alpha3 ().DeviceTaintRules ().Create (ctx , obj , metav1.CreateOptions {})
2094
+ ginkgo .DeferCleanup (func (ctx context.Context ) {
2095
+ err := b .f .ClientSet .ResourceV1alpha3 ().DeviceTaintRules ().Delete (ctx , createdObj .GetName (), metav1.DeleteOptions {})
2096
+ framework .ExpectNoError (err , "delete DeviceTaintRule" )
2097
+ })
2009
2098
case * appsv1.DaemonSet :
2010
2099
createdObj , err = b .f .ClientSet .AppsV1 ().DaemonSets (b .f .Namespace .Name ).Create (ctx , obj , metav1.CreateOptions {})
2011
2100
// Cleanup not really needed, but speeds up namespace shutdown.
0 commit comments