If you are developing knative you may need to add or change:
Both tests can use our test library.
Reviewers of conformance and e2e tests (i.e. OWNERS) are responsible for the style and quality of the resulting tests. In order to not discourage contributions, when style change are required, the reviewers can make the changes themselves.
presubmit-tests.sh
is the entry point for both the end-to-end tests and the conformance tests
This script, and consequently, the e2e and conformance tests will be run before every code submission. You can run these tests manually with:
test/presubmit-tests.sh
In the test
dir you will find several libraries in the test
package
you can use in your tests.
You can:
- Use common test flags
- Get access to client objects
- Make requests against deployed services
- Poll Knative Serving resources
- Verify resource state transitions
- Generate boilerplate CRDs
- Ensure test cleanup
These flags are useful for running against an existing cluster, making use of your existing environment setup.
By importing github.com/knative/serving/test
you get access to a global variable called
test.Flags
which holds the values of the command line flags.
imagePath := strings.Join([]string{test.Flags.DockerRepo, image}, "/"))
See e2e_flags.go.
To initialize client objects that you can use the command line flags that describe the environment:
func setup(t *testing.T) *test.Clients {
clients, err := test.NewClients(kubeconfig, cluster, namespaceName)
if err != nil {
t.Fatalf("Couldn't initialize clients: %v", err)
}
return clients
}
The Clients
struct contains initialized clients for accessing:
- Kubernetes objects
Routes
Configurations
Revisions
For example, to create a Route
:
_, err = clients.Routes.Create(test.Route(namespaceName, routeName, configName))
And you can use the client to clean up Route
and Configuration
resources created
by your test:
func tearDown(clients *test.Clients) {
if clients != nil {
clients.Delete([]string{routeName}, []string{configName})
}
}
See clients.go.
After deploying (i.e. creating a Route
and a Configuration
) an endpoint will not be
ready to serve requests right away. To poll a deployed endpoint and wait for it to be
in the state you want it to be in (or timeout) use WaitForEndpointState
:
err = test.WaitForEndpointState(clients.Kube, resolvableDomain, updatedRoute.Status.Domain, namespaceName, routeName, func(body string) (bool, error) {
return body == expectedText, nil
})
if err != nil {
t.Fatalf("The endpoint for Route %s at domain %s didn't serve the expected text \"%s\": %v", routeName, updatedRoute.Status.Domain, expectedText, err)
}
This function makes use of the environment flag resolvableDomain
to determine if the ingress
should be used or the domain should be used directly.
See request.go.
After creating Knative Serving resources or making changes to them, you will need to wait for the system to realize those chnages. You can use the Knative Serving CRD polling methods to poll the resources until they get into the desired state or time out.
These functions use the kubernetes wait
package.
To poll they use PollImmediate
and the return values of the function you provide behave the same as
ConditionFunc
:
a bool
to indicate if the function should stop or continue polling, and an error
to indicate if
there has been an error.
For example, you can poll a Configuration
object to find the name of the Revision
that was created
for it:
var revisionName string
err := test.WaitForConfigurationState(clients.Configs, configName, func(c *v1alpha1.Configuration) (bool, error) {
if c.Status.LatestCreatedRevisionName != "" {
revisionName = c.Status.LatestCreatedRevisionName
return true, nil
}
return false, nil
})
See crd_polling.go.
To use the polling functions you must provide a function to check the state. Some of the expected transition states (as defined in the Knative Serving spec) are expressed in functions in states.go.
For example when a Revision
has been created, the system will start the resources required to
actually serve it, and then the Revision
object will be updated to indicate it is ready. This
can be polled with test.IsRevisionReady
:
err := test.WaitForRevisionState(clients.Revisions, revisionName, test.IsRevisionReady(revisionName))
if err != nil {
t.Fatalf("Revision %s did not become ready to serve traffic: %v", revisionName, err)
}
Once the Revision
is created, all traffic for a Route
should be routed to it. This can be polled with
test.AllRouteTrafficAtRevision
:
err = test.WaitForRouteState(clients.Routes, routeName, test.AllRouteTrafficAtRevision(routeName, revisionName))
if err != nil {
t.Fatalf("The Route %s was not updated to route traffic to the Revision %s: %v", routeName, revisionName, err)
}
See states.go.
Your tests will probably need to create Route
and Configuration
objects. You can use the
existing boilerplate to describe them.
For example to create a Configuration
object that uses a certain docker image:
_, err := clients.Configs.Create(test.Configuration(namespaceName, configName, imagePath))
if err != nil {
return err
}
Please expand these functions as more use cases are tested.
See crd.go.
To ensure your test is cleaned up, you should defer cleanup to execute after your test completes and also ensure the cleanup occurs if the test is interrupted:
defer tearDown(clients)
test.CleanupOnInterrupt(func() { tearDown(clients) })
See cleanup.go.