mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 09:49:50 +00:00
DRA e2e: update VAP for a kubelet plugin
This fixes the message (node name and "cluster-scoped" were switched) and simplifies the VAP: - a single matchCondition short circuits completely unless they're a user we care about - variables to extract the userNodeName and objectNodeName once (using optionals to gracefully turn missing claims and fields into empty strings) - leaves very tiny concise validations Co-authored-by: Jordan Liggitt <liggitt@google.com>
This commit is contained in:
parent
9f36c8d718
commit
357a2926a1
@ -1197,7 +1197,16 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Messages from test-driver/deploy/example/plugin-permissions.yaml
|
// Messages from test-driver/deploy/example/plugin-permissions.yaml
|
||||||
matchVAPDeniedError := gomega.MatchError(gomega.ContainSubstring("may only modify resourceslices that belong to the node the pod is running on"))
|
matchVAPDeniedError := func(nodeName string, slice *resourceapi.ResourceSlice) types.GomegaMatcher {
|
||||||
|
subStr := fmt.Sprintf("this user running on node '%s' may not modify ", nodeName)
|
||||||
|
switch {
|
||||||
|
case slice.Spec.NodeName != "":
|
||||||
|
subStr += fmt.Sprintf("resourceslices on node '%s'", slice.Spec.NodeName)
|
||||||
|
default:
|
||||||
|
subStr += "cluster resourceslices"
|
||||||
|
}
|
||||||
|
return gomega.MatchError(gomega.ContainSubstring(subStr))
|
||||||
|
}
|
||||||
mustCreate := func(clientSet kubernetes.Interface, clientName string, slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice {
|
mustCreate := func(clientSet kubernetes.Interface, clientName string, slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice {
|
||||||
ginkgo.GinkgoHelper()
|
ginkgo.GinkgoHelper()
|
||||||
slice, err := clientSet.ResourceV1alpha3().ResourceSlices().Create(ctx, slice, metav1.CreateOptions{})
|
slice, err := clientSet.ResourceV1alpha3().ResourceSlices().Create(ctx, slice, metav1.CreateOptions{})
|
||||||
@ -1237,17 +1246,17 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create with different clients, keep it in the end.
|
// Create with different clients, keep it in the end.
|
||||||
mustFailToCreate(realNodeClient, "real plugin", fictionalNodeSlice, matchVAPDeniedError)
|
mustFailToCreate(realNodeClient, "real plugin", fictionalNodeSlice, matchVAPDeniedError(realNodeName, fictionalNodeSlice))
|
||||||
mustCreateAndDelete(fictionalNodeClient, "fictional plugin", fictionalNodeSlice)
|
mustCreateAndDelete(fictionalNodeClient, "fictional plugin", fictionalNodeSlice)
|
||||||
createdFictionalNodeSlice := mustCreate(f.ClientSet, "admin", fictionalNodeSlice)
|
createdFictionalNodeSlice := mustCreate(f.ClientSet, "admin", fictionalNodeSlice)
|
||||||
|
|
||||||
// Update with different clients.
|
// Update with different clients.
|
||||||
mustFailToUpdate(realNodeClient, "real plugin", createdFictionalNodeSlice, matchVAPDeniedError)
|
mustFailToUpdate(realNodeClient, "real plugin", createdFictionalNodeSlice, matchVAPDeniedError(realNodeName, createdFictionalNodeSlice))
|
||||||
createdFictionalNodeSlice = mustUpdate(fictionalNodeClient, "fictional plugin", createdFictionalNodeSlice)
|
createdFictionalNodeSlice = mustUpdate(fictionalNodeClient, "fictional plugin", createdFictionalNodeSlice)
|
||||||
createdFictionalNodeSlice = mustUpdate(f.ClientSet, "admin", createdFictionalNodeSlice)
|
createdFictionalNodeSlice = mustUpdate(f.ClientSet, "admin", createdFictionalNodeSlice)
|
||||||
|
|
||||||
// Delete with different clients.
|
// Delete with different clients.
|
||||||
mustFailToDelete(realNodeClient, "real plugin", createdFictionalNodeSlice, matchVAPDeniedError)
|
mustFailToDelete(realNodeClient, "real plugin", createdFictionalNodeSlice, matchVAPDeniedError(realNodeName, createdFictionalNodeSlice))
|
||||||
mustDelete(fictionalNodeClient, "fictional plugin", createdFictionalNodeSlice)
|
mustDelete(fictionalNodeClient, "fictional plugin", createdFictionalNodeSlice)
|
||||||
|
|
||||||
// Now the same for a slice which is not associated with a node.
|
// Now the same for a slice which is not associated with a node.
|
||||||
@ -1272,18 +1281,18 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Create with different clients, keep it in the end.
|
// Create with different clients, keep it in the end.
|
||||||
mustFailToCreate(realNodeClient, "real plugin", clusterSlice, matchVAPDeniedError)
|
mustFailToCreate(realNodeClient, "real plugin", clusterSlice, matchVAPDeniedError(realNodeName, clusterSlice))
|
||||||
mustFailToCreate(fictionalNodeClient, "fictional plugin", clusterSlice, matchVAPDeniedError)
|
mustFailToCreate(fictionalNodeClient, "fictional plugin", clusterSlice, matchVAPDeniedError(fictionalNodeName, clusterSlice))
|
||||||
createdClusterSlice := mustCreate(f.ClientSet, "admin", clusterSlice)
|
createdClusterSlice := mustCreate(f.ClientSet, "admin", clusterSlice)
|
||||||
|
|
||||||
// Update with different clients.
|
// Update with different clients.
|
||||||
mustFailToUpdate(realNodeClient, "real plugin", createdClusterSlice, matchVAPDeniedError)
|
mustFailToUpdate(realNodeClient, "real plugin", createdClusterSlice, matchVAPDeniedError(realNodeName, createdClusterSlice))
|
||||||
mustFailToUpdate(fictionalNodeClient, "fictional plugin", createdClusterSlice, matchVAPDeniedError)
|
mustFailToUpdate(fictionalNodeClient, "fictional plugin", createdClusterSlice, matchVAPDeniedError(fictionalNodeName, createdClusterSlice))
|
||||||
createdClusterSlice = mustUpdate(f.ClientSet, "admin", createdClusterSlice)
|
createdClusterSlice = mustUpdate(f.ClientSet, "admin", createdClusterSlice)
|
||||||
|
|
||||||
// Delete with different clients.
|
// Delete with different clients.
|
||||||
mustFailToDelete(realNodeClient, "real plugin", createdClusterSlice, matchVAPDeniedError)
|
mustFailToDelete(realNodeClient, "real plugin", createdClusterSlice, matchVAPDeniedError(realNodeName, createdClusterSlice))
|
||||||
mustFailToDelete(fictionalNodeClient, "fictional plugin", createdClusterSlice, matchVAPDeniedError)
|
mustFailToDelete(fictionalNodeClient, "fictional plugin", createdClusterSlice, matchVAPDeniedError(fictionalNodeName, createdClusterSlice))
|
||||||
mustDelete(f.ClientSet, "admin", createdClusterSlice)
|
mustDelete(f.ClientSet, "admin", createdClusterSlice)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -50,29 +50,25 @@ spec:
|
|||||||
apiVersions: ["v1alpha3"]
|
apiVersions: ["v1alpha3"]
|
||||||
operations: ["CREATE", "UPDATE", "DELETE"]
|
operations: ["CREATE", "UPDATE", "DELETE"]
|
||||||
resources: ["resourceslices"]
|
resources: ["resourceslices"]
|
||||||
variables:
|
matchConditions:
|
||||||
- name: hasNodeName
|
- name: isRestrictedUser
|
||||||
expression: >-
|
|
||||||
"authentication.kubernetes.io/node-name" in request.userInfo.extra
|
|
||||||
- name: isKubeletPlugin
|
|
||||||
expression: >-
|
expression: >-
|
||||||
request.userInfo.username == "system:serviceaccount:dra-kubelet-plugin-namespace:dra-kubelet-plugin-service-account"
|
request.userInfo.username == "system:serviceaccount:dra-kubelet-plugin-namespace:dra-kubelet-plugin-service-account"
|
||||||
|
variables:
|
||||||
|
- name: userNodeName
|
||||||
|
expression: >-
|
||||||
|
request.userInfo.extra[?'authentication.kubernetes.io/node-name'][0].orValue('')
|
||||||
- name: objectNodeName
|
- name: objectNodeName
|
||||||
expression: >-
|
expression: >-
|
||||||
(request.operation == "DELETE" ? oldObject : object).spec.?nodeName.orValue("")
|
(request.operation == "DELETE" ? oldObject : object).spec.?nodeName.orValue("")
|
||||||
validations:
|
validations:
|
||||||
- expression: >-
|
- expression: variables.userNodeName != ""
|
||||||
!variables.isKubeletPlugin || variables.hasNodeName
|
message: >-
|
||||||
message: This user must have a "authentication.kubernetes.io/node-name" claim. ServiceAccountTokenNodeBindingValidation must be enabled in the cluster.
|
no node association found for user, this user must run in a pod on a node and ServiceAccountTokenPodNodeInfo must be enabled
|
||||||
- expression: >-
|
- expression: variables.userNodeName == variables.objectNodeName
|
||||||
!variables.isKubeletPlugin || !variables.hasNodeName ||
|
|
||||||
variables.objectNodeName == request.userInfo.extra["authentication.kubernetes.io/node-name"][0]
|
|
||||||
message: This DRA kubelet plugin may only modify resourceslices that belong to the node the pod is running on.
|
|
||||||
# This is useful for debugging. Can be dropped in a production deployment.
|
|
||||||
messageExpression: >-
|
messageExpression: >-
|
||||||
"The DRA kubelet plugin on node " + request.userInfo.extra["authentication.kubernetes.io/node-name"][0] +
|
"this user running on node '"+variables.userNodeName+"' may not modify " +
|
||||||
" may only modify resourceslices that belong to the node the pod is running on, not " +
|
(variables.objectNodeName == "" ?"cluster resourceslices" : "resourceslices on node '"+variables.objectNodeName+"'")
|
||||||
(variables.objectNodeName == "" ? variables.objectNodeName : "a cluster-scoped slice") + "."
|
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: ValidatingAdmissionPolicyBinding
|
kind: ValidatingAdmissionPolicyBinding
|
||||||
|
Loading…
Reference in New Issue
Block a user