I am maintaining several custom operators that are using kubebuilder and the operator framework, and with the graduation of CustomResourceDefinition validation rules to beta, I wan't to move some of the more complex validation rules that were embedded in the code directly into the CRD.
I've implemented some more complex validation rules using CEL directly in the types files of my api folder and they are working great. The problem that I am facing is that some of the rules being quite complex and our team being new to CEL, I'd like to unit test them. The only solution I've found for now is to use EnvTest to test them but the initial setup lasts something like 5 seconds which is not acceptable for unit tests.
I was wondering if there was any solution to have it actually running as real unit tests without inflating the run time of the tests by a 500 factor.
Current tests using ginkgo and EnvTest :
Spec with a "complex rule" where if the type is private, I want the dependingValues array to not be empty:
// +kubebuilder:validation:XValidation:rule="self.type=='Private' ? size(self.dependingValues) > 0 : true", message="private specs must have at least one depending value"
type ExampleSpec struct {
Type string `json:"type"`
DependingValues []SomeType `json:"dependingValues"`
}
BeforeSuite:
var k8sClient client.Client
var testEnv *envtest.Environment
var cancel context.CancelFunc
var ctx context.Context
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
ctx, cancel = context.WithCancel(context.TODO())
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{
filepath.Join("..", "..", "config", "crd", "bases"),
},
ErrorIfCRDPathMissing: true,
}
cfg, err := testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
err = iacv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
})
var _ = AfterSuite(func() {
cancel()
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})
Test Table:
var _ = DescribeTable("ExampleSpecValidationTests",
func(example func() *v1.ExampleSpec, valid bool) {
err := k8sClient.Create(ctx, example())
if k8serrors.IsInvalid(err) {
Expect(err == nil).To(Equal(valid))
} else {
Expect(err).ToNot(HaveOccurred())
}
},
Entry("minimal spec", func() *v1.ExampleSpec {
return &minimalExampleSpec
}, true),
Entry("private with no depending values", func() *v1.ExampleSpec {
e := minimalExampleSpec
e.DependingValues = []DependingValues{}
return &e
}, false),
)