Getting started
Install
go get github.com/yyewolf/petri@v0.37
petri needs Go 1.26 or newer, the same as Kubernetes 1.37.
Pick the petri minor that matches your k8s.io/client-go minor. A test binary
can link only one API server, and it is built from the same Kubernetes
release as your client libraries:
| petri | Kubernetes | k8s.io/client-go |
|---|---|---|
v0.37.x | 1.37 | v0.37.x |
controller-runtime works with petri as it is: pass cp.Config() to
ctrl.NewManager. The optional sub-module adds a drop-in replacement for
envtest and a few test helpers; see
controller-runtime. To use it:
go get github.com/yyewolf/petri/controllerruntime@v0.37
A first test
quickstart_test.go
package quickstart_test
import (
"testing"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/yyewolf/petri"
)
func TestDeploymentIsReconciled(t *testing.T) {
t.Parallel()
cp := petri.Start(t) // the control plane shared by this test binary
ns := cp.Namespace(t) // a fresh namespace, deleted when the test ends
cs := cp.Client() // an admin clientset; cp.Config() gives a *rest.Config
cp.EnableControllers(t, petri.DeploymentController)
labels := map[string]string{"app": "web"}
deployments := cs.AppsV1().Deployments(ns)
_, err := deployments.Create(t.Context(), &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{Name: "web"},
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{MatchLabels: labels},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: labels},
Spec: corev1.PodSpec{
Containers: []corev1.Container{{Name: "web", Image: "nginx"}},
},
},
},
}, metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
// The Deployment controller has seen the Deployment once it reports the
// generation it acted on.
petri.WaitFor(t, deployments, "web", func(d *appsv1.Deployment) (bool, error) {
return d != nil && d.Status.ObservedGeneration == d.Generation, nil
})
}
petri.Start(t)returns the control plane that every test in the binary shares, and starts it on first use. You set nothing up inTestMainand tear nothing down.cp.Namespace(t)gives the test its own namespace, so parallel tests do not see each other's objects. The namespace is deleted when the test ends.cp.Client()andcp.Config()give admin access, like envtest's config.- The namespace controller and the garbage collector always run. A test
enables any other controller it needs, such as the Deployment controller
here, with
EnableControllers. petri.WaitForwatches the object until the condition holds. It fails the test after 30 seconds.
Run it
go test ./...
The first build compiles the API server and takes a few minutes. Later runs
use Go's build cache. Builds with -race need much more memory than with
envtest, because the race detector instruments the API server too; see
resource usage.