@@ -32,13 +32,15 @@ import (
3232 "github.com/onsi/gomega/gstruct"
3333 "github.com/onsi/gomega/types"
3434
35+ admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
3536 v1 "k8s.io/api/core/v1"
3637 resourceapi "k8s.io/api/resource/v1alpha3"
3738 apierrors "k8s.io/apimachinery/pkg/api/errors"
3839 "k8s.io/apimachinery/pkg/api/resource"
3940 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4041 "k8s.io/apimachinery/pkg/labels"
4142 "k8s.io/apimachinery/pkg/runtime"
43+ applyv1 "k8s.io/client-go/applyconfigurations/core/v1"
4244 "k8s.io/client-go/kubernetes"
4345 "k8s.io/dynamic-resource-allocation/controller"
4446 "k8s.io/klog/v2"
@@ -56,6 +58,9 @@ const (
5658 podStartTimeout = 5 * time .Minute
5759)
5860
61+ //go:embed test-driver/deploy/example/admin-access-policy.yaml
62+ var adminAccessPolicyYAML string
63+
5964// networkResources can be passed to NewDriver directly.
6065func networkResources () app.Resources {
6166 return app.Resources {}
@@ -966,6 +971,63 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
966971 driver := NewDriver (f , nodes , networkResources )
967972 b := newBuilder (f , driver )
968973
974+ ginkgo .It ("support validating admission policy for admin access" , func (ctx context.Context ) {
975+ // Create VAP, after making it unique to the current test.
976+ adminAccessPolicyYAML := strings .ReplaceAll (adminAccessPolicyYAML , "dra.example.com" , b .f .UniqueName )
977+ driver .createFromYAML (ctx , []byte (adminAccessPolicyYAML ), "" )
978+
979+ // Wait for both VAPs to be processed. This ensures that there are no check errors in the status.
980+ matchStatus := gomega .Equal (admissionregistrationv1.ValidatingAdmissionPolicyStatus {ObservedGeneration : 1 , TypeChecking : & admissionregistrationv1.TypeChecking {}})
981+ gomega .Eventually (ctx , framework .ListObjects (b .f .ClientSet .AdmissionregistrationV1 ().ValidatingAdmissionPolicies ().List , metav1.ListOptions {})).Should (gomega .HaveField ("Items" , gomega .ContainElements (
982+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
983+ "ObjectMeta" : gomega .HaveField ("Name" , "resourceclaim-policy." + b .f .UniqueName ),
984+ "Status" : matchStatus ,
985+ }),
986+ gstruct .MatchFields (gstruct .IgnoreExtras , gstruct.Fields {
987+ "ObjectMeta" : gomega .HaveField ("Name" , "resourceclaimtemplate-policy." + b .f .UniqueName ),
988+ "Status" : matchStatus ,
989+ }),
990+ )))
991+
992+ // Attempt to create claim and claim template with admin access. Must fail eventually.
993+ claim := b .externalClaim ()
994+ claim .Spec .Devices .Requests [0 ].AdminAccess = true
995+ _ , claimTemplate := b .podInline ()
996+ claimTemplate .Spec .Spec .Devices .Requests [0 ].AdminAccess = true
997+ matchVAPError := gomega .MatchError (gomega .ContainSubstring ("admin access to devices not enabled" /* in namespace " + b.f.Namespace.Name */ ))
998+ gomega .Eventually (ctx , func (ctx context.Context ) error {
999+ // First delete, in case that it succeeded earlier.
1000+ if err := b .f .ClientSet .ResourceV1alpha3 ().ResourceClaims (b .f .Namespace .Name ).Delete (ctx , claim .Name , metav1.DeleteOptions {}); err != nil && ! apierrors .IsNotFound (err ) {
1001+ return err
1002+ }
1003+ _ , err := b .f .ClientSet .ResourceV1alpha3 ().ResourceClaims (b .f .Namespace .Name ).Create (ctx , claim , metav1.CreateOptions {})
1004+ return err
1005+ }).Should (matchVAPError )
1006+
1007+ gomega .Eventually (ctx , func (ctx context.Context ) error {
1008+ // First delete, in case that it succeeded earlier.
1009+ if err := b .f .ClientSet .ResourceV1alpha3 ().ResourceClaimTemplates (b .f .Namespace .Name ).Delete (ctx , claimTemplate .Name , metav1.DeleteOptions {}); err != nil && ! apierrors .IsNotFound (err ) {
1010+ return err
1011+ }
1012+ _ , err := b .f .ClientSet .ResourceV1alpha3 ().ResourceClaimTemplates (b .f .Namespace .Name ).Create (ctx , claimTemplate , metav1.CreateOptions {})
1013+ return err
1014+ }).Should (matchVAPError )
1015+
1016+ // After labeling the namespace, creation must (eventually...) succeed.
1017+ _ , err := b .f .ClientSet .CoreV1 ().Namespaces ().Apply (ctx ,
1018+ applyv1 .Namespace (b .f .Namespace .Name ).WithLabels (map [string ]string {"admin-access." + b .f .UniqueName : "on" }),
1019+ metav1.ApplyOptions {FieldManager : b .f .UniqueName })
1020+ framework .ExpectNoError (err )
1021+ gomega .Eventually (ctx , func (ctx context.Context ) error {
1022+ _ , err := b .f .ClientSet .ResourceV1alpha3 ().ResourceClaims (b .f .Namespace .Name ).Create (ctx , claim , metav1.CreateOptions {})
1023+ return err
1024+ }).Should (gomega .Succeed ())
1025+ gomega .Eventually (ctx , func (ctx context.Context ) error {
1026+ _ , err := b .f .ClientSet .ResourceV1alpha3 ().ResourceClaimTemplates (b .f .Namespace .Name ).Create (ctx , claimTemplate , metav1.CreateOptions {})
1027+ return err
1028+ }).Should (gomega .Succeed ())
1029+ })
1030+
9691031 ginkgo .It ("truncates the name of a generated resource claim" , func (ctx context.Context ) {
9701032 pod , template := b .podInline ()
9711033 pod .Name = strings .Repeat ("p" , 63 )
@@ -1126,19 +1188,19 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
11261188 mustCreate := func (clientSet kubernetes.Interface , clientName string , slice * resourceapi.ResourceSlice ) * resourceapi.ResourceSlice {
11271189 ginkgo .GinkgoHelper ()
11281190 slice , err := clientSet .ResourceV1alpha3 ().ResourceSlices ().Create (ctx , slice , metav1.CreateOptions {})
1129- gomega . Expect (err ). ToNot ( gomega . HaveOccurred () , fmt .Sprintf ("CREATE: %s + %s" , clientName , slice .Name ))
1191+ framework . ExpectNoError (err , fmt .Sprintf ("CREATE: %s + %s" , clientName , slice .Name ))
11301192 return slice
11311193 }
11321194 mustUpdate := func (clientSet kubernetes.Interface , clientName string , slice * resourceapi.ResourceSlice ) * resourceapi.ResourceSlice {
11331195 ginkgo .GinkgoHelper ()
11341196 slice , err := clientSet .ResourceV1alpha3 ().ResourceSlices ().Update (ctx , slice , metav1.UpdateOptions {})
1135- gomega . Expect (err ). ToNot ( gomega . HaveOccurred () , fmt .Sprintf ("UPDATE: %s + %s" , clientName , slice .Name ))
1197+ framework . ExpectNoError (err , fmt .Sprintf ("UPDATE: %s + %s" , clientName , slice .Name ))
11361198 return slice
11371199 }
11381200 mustDelete := func (clientSet kubernetes.Interface , clientName string , slice * resourceapi.ResourceSlice ) {
11391201 ginkgo .GinkgoHelper ()
11401202 err := clientSet .ResourceV1alpha3 ().ResourceSlices ().Delete (ctx , slice .Name , metav1.DeleteOptions {})
1141- gomega . Expect (err ). ToNot ( gomega . HaveOccurred () , fmt .Sprintf ("DELETE: %s + %s" , clientName , slice .Name ))
1203+ framework . ExpectNoError (err , fmt .Sprintf ("DELETE: %s + %s" , clientName , slice .Name ))
11421204 }
11431205 mustCreateAndDelete := func (clientSet kubernetes.Interface , clientName string , slice * resourceapi.ResourceSlice ) {
11441206 ginkgo .GinkgoHelper ()
0 commit comments