Skip to content

Commit

Permalink
Add validation unit tests template
Browse files Browse the repository at this point in the history
currently fails with bazel visibility error, see issue
google/cel-go#947

tests need more scenarios to fit all validation rules but the gist is
done.
in order to run test with visibility check excluded:
```
hack/dockerized bazel test --test_output=errors --cache_test_results=no
--check_visibility=false //pkg/virt-operator/...
```

Signed-off-by: Ram Lavi <[email protected]>
  • Loading branch information
RamLavi committed Jun 4, 2024
1 parent 9e3f041 commit 8428876
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 4 deletions.
5 changes: 5 additions & 0 deletions pkg/virt-operator/resource/generate/components/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,19 @@ go_test(
"//staging/src/kubevirt.io/api/core/v1:go_default_library",
"//staging/src/kubevirt.io/client-go/testutils:go_default_library",
"//vendor/github.com/google/cel-go/cel:go_default_library",
"//vendor/github.com/google/cel-go/common/types:go_default_library",
"//vendor/github.com/onsi/ginkgo/v2:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
"//vendor/github.com/openshift/api/security/v1:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/cel:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/cel:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/cel/environment:go_default_library",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,25 @@
package components_test

import (
"context"
"fmt"
"strings"

celgo "github.com/google/cel-go/cel"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apiserver/pkg/cel/environment"

admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/admission/plugin/cel"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/cel/environment"

celgo "github.com/google/cel-go/cel"
celtypes "github.com/google/cel-go/common/types"

"kubevirt.io/kubevirt/pkg/virt-operator/resource/generate/components"
)
Expand All @@ -45,7 +54,6 @@ var _ = Describe("Validation Admission Policy", func() {
Expect(validatingAdmissionPolicyBinding.Kind).ToNot(BeEmpty())
})
})

Context("ValidatingAdmissionPolicy", func() {
It("should generate the expected policy", func() {
const userName = "system:serviceaccount:kubevirt-ns:kubevirt-handler"
Expand All @@ -55,7 +63,6 @@ var _ = Describe("Validation Admission Policy", func() {
Expect(validatingAdmissionPolicy.Spec.MatchConditions[0].Expression).To(Equal(expectedMatchConditionExpression))
Expect(validatingAdmissionPolicy.Kind).ToNot(BeEmpty())
})

Context("Validation Compile test", func() {
var celCompiler *cel.CompositedCompiler
BeforeEach(func() {
Expand All @@ -82,6 +89,102 @@ var _ = Describe("Validation Admission Policy", func() {
}
})
})
Context("Validation Filter test", func() {
var celCompiler *cel.CompositedCompiler
BeforeEach(func() {
compositionEnvTemplateWithoutStrictCost, err := cel.NewCompositionEnv(cel.VariablesTypeName, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
Expect(err).ToNot(HaveOccurred())
celCompiler = cel.NewCompositedCompilerFromTemplate(compositionEnvTemplateWithoutStrictCost)
})
It("should fail patching the node with non-kubevirt label", func() {
const userName = "system:serviceaccount:kubevirt-ns:kubevirt-handler"
validatingAdmissionPolicy := components.NewHandlerV1ValidatingAdmissionPolicy(userName)

//replace variables if all else fails
for idx, _ := range validatingAdmissionPolicy.Spec.Validations {
for _, variable := range validatingAdmissionPolicy.Spec.Variables {
validatingAdmissionPolicy.Spec.Validations[idx].Expression = strings.ReplaceAll(validatingAdmissionPolicy.Spec.Validations[idx].Expression, "variables."+variable.Name, variable.Expression)
}
}
options := cel.OptionalVariableDeclarations{
HasParams: false,
HasAuthorizer: false,
}
mode := environment.NewExpressions
celCompiler.CompileAndStoreVariables(convertV1Variables(validatingAdmissionPolicy.Spec.Variables), options, mode)

var expressions []cel.ExpressionAccessor
for _, validation := range validatingAdmissionPolicy.Spec.Validations {
expressions = append(expressions, convertV1Validation(validation))
}
filterResults := celCompiler.FilterCompiler.Compile(expressions, options, mode)
Expect(filterResults.CompilationErrors()).To(HaveLen(0))

userInfo := &user.DefaultInfo{
Name: userName,
}
const nodeName = "node01"
oldNode := &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: nodeName,
Labels: map[string]string{"label1": "val1"},
Annotations: map[string]string{"annotations1": "val1"},
},
Spec: corev1.NodeSpec{
Unschedulable: false,
},
}
newNode := &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: nodeName,
Labels: map[string]string{"label1": "val1", "kubevirt.io/permittedLabel": ""},
Annotations: map[string]string{"annotations1": "val1"},
},
Spec: corev1.NodeSpec{
Unschedulable: false,
},
}
nodeAttribiute := admission.NewAttributesRecord(
oldNode,
newNode,
corev1.SchemeGroupVersion.WithKind("Node"),
corev1.NamespaceAll,
nodeName,
corev1.SchemeGroupVersion.WithResource("nodes"),
"",
admission.Update,
&metav1.CreateOptions{},
false,
userInfo,
)
versionedAttr, err := admission.NewVersionedAttributes(nodeAttribiute, nodeAttribiute.GetKind(), newObjectInterfacesForTest())
Expect(err).ToNot(HaveOccurred())

optionalVars := cel.OptionalVariableBindings{}
evalResults, _, err := filterResults.ForInput(
context.TODO(),
versionedAttr,
cel.CreateAdmissionRequest(versionedAttr.Attributes, metav1.GroupVersionResource(versionedAttr.GetResource()), metav1.GroupVersionKind(versionedAttr.VersionedKind)),
optionalVars,
nil,
celconfig.RuntimeCELCostBudget)
Expect(err).ToNot(HaveOccurred())

//varsPrettyVars, err := json.MarshalIndent(celCompiler.CompositionEnv.CompiledVariables, "", "\t")
//Expect(err).NotTo(HaveOccurred())
//dataPrettyJSON, err := json.MarshalIndent(evalResults, "", "\t")
//Expect(err).NotTo(HaveOccurred())
//fmt.Printf("\ncelCompiler.CompositionEnv.CompiledVariables = %v\nevalResults = \n%s\n\n", string(varsPrettyVars), string(dataPrettyJSON))

for resultIdx := range evalResults {
result := evalResults[resultIdx]
validation := validatingAdmissionPolicy.Spec.Validations[resultIdx]
Expect(result.Error).To(BeNil(), fmt.Sprintf("validation policy expression %q failed", result.ExpressionAccessor.GetExpression()))
Expect(result.EvalResult).To(Equal(celtypes.True), fmt.Sprintf("validation policy expression %q returned false. reason: %q", result.ExpressionAccessor.GetExpression(), validation.Message))
}

})
})
})

})
Expand Down Expand Up @@ -134,3 +237,10 @@ func convertV1Validation(validation admissionregistrationv1.Validation) cel.Expr
Reason: validation.Reason,
}
}

// newObjectInterfacesForTest returns an ObjectInterfaces appropriate for test cases in this file.
func newObjectInterfacesForTest() admission.ObjectInterfaces {
scheme := runtime.NewScheme()
corev1.AddToScheme(scheme)
return admission.NewObjectInterfacesFromScheme(scheme)
}

0 comments on commit 8428876

Please sign in to comment.