mirror of https://github.com/knative/client.git
Improves(e2e): imrpoves integration tests and to better run locally (#274)
1. refactors basic workflow (move code to common) 2. use different namespace accross test runs to isolate 3. include wait logic in CreateNamespace to allow multiple runs 4. include wait logic in DeleteNamespace to allow multiple runs This is important since we need more integration tests and some of these changes should allow `./test/e2e-tests-local.sh` to run on local clusters (minikube or something other than test-infra) and correctly behave during mutiple runs.
This commit is contained in:
parent
75a41def4e
commit
9513dedfba
|
|
@ -25,99 +25,80 @@ import (
|
|||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
e env
|
||||
k kn
|
||||
)
|
||||
|
||||
const (
|
||||
KnDefaultTestImage string = "gcr.io/knative-samples/helloworld-go"
|
||||
)
|
||||
|
||||
func Setup(t *testing.T) func(t *testing.T) {
|
||||
e = buildEnv(t)
|
||||
k = kn{t, e.Namespace, Logger{}}
|
||||
CreateTestNamespace(t, e.Namespace)
|
||||
return Teardown
|
||||
}
|
||||
|
||||
func Teardown(t *testing.T) {
|
||||
DeleteTestNamespace(t, e.Namespace)
|
||||
}
|
||||
|
||||
func TestBasicWorkflow(t *testing.T) {
|
||||
teardown := Setup(t)
|
||||
defer teardown(t)
|
||||
test := NewE2eTest(t)
|
||||
test.Setup(t)
|
||||
defer test.Teardown(t)
|
||||
|
||||
t.Run("returns no service before running tests", func(t *testing.T) {
|
||||
testServiceListEmpty(t, k)
|
||||
test.serviceListEmpty(t)
|
||||
})
|
||||
|
||||
t.Run("create hello service and returns no error", func(t *testing.T) {
|
||||
testServiceCreate(t, k, "hello")
|
||||
test.serviceCreate(t, "hello")
|
||||
})
|
||||
|
||||
t.Run("returns valid info about hello service", func(t *testing.T) {
|
||||
testServiceList(t, k, "hello")
|
||||
testServiceDescribe(t, k, "hello")
|
||||
test.serviceList(t, "hello")
|
||||
test.serviceDescribe(t, "hello")
|
||||
})
|
||||
|
||||
t.Run("update hello service's configuration and returns no error", func(t *testing.T) {
|
||||
testServiceUpdate(t, k, "hello", []string{"--env", "TARGET=kn", "--port", "8888"})
|
||||
test.serviceUpdate(t, "hello", []string{"--env", "TARGET=kn", "--port", "8888"})
|
||||
})
|
||||
|
||||
t.Run("create another service and returns no error", func(t *testing.T) {
|
||||
testServiceCreate(t, k, "svc2")
|
||||
test.serviceCreate(t, "svc2")
|
||||
})
|
||||
|
||||
t.Run("returns a list of revisions associated with hello and svc2 services", func(t *testing.T) {
|
||||
testRevisionListForService(t, k, "hello")
|
||||
testRevisionListForService(t, k, "svc2")
|
||||
test.revisionListForService(t, "hello")
|
||||
test.revisionListForService(t, "svc2")
|
||||
})
|
||||
|
||||
t.Run("returns a list of routes associated with hello and svc2 services", func(t *testing.T) {
|
||||
testRouteList(t, k)
|
||||
testRouteListWithArgument(t, k, "hello")
|
||||
test.routeList(t)
|
||||
test.routeListWithArgument(t, "hello")
|
||||
})
|
||||
|
||||
t.Run("delete hello and svc2 services and returns no error", func(t *testing.T) {
|
||||
testServiceDelete(t, k, "hello")
|
||||
testServiceDelete(t, k, "svc2")
|
||||
test.serviceDelete(t, "hello")
|
||||
test.serviceDelete(t, "svc2")
|
||||
})
|
||||
|
||||
t.Run("returns no service after completing tests", func(t *testing.T) {
|
||||
testServiceListEmpty(t, k)
|
||||
test.serviceListEmpty(t)
|
||||
})
|
||||
}
|
||||
|
||||
// Private test functions
|
||||
|
||||
func testServiceListEmpty(t *testing.T, k kn) {
|
||||
out, err := k.RunWithOpts([]string{"service", "list"}, runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) serviceListEmpty(t *testing.T) {
|
||||
out, err := test.kn.RunWithOpts([]string{"service", "list"}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, util.ContainsAll(out, "No resources found."))
|
||||
}
|
||||
|
||||
func testServiceCreate(t *testing.T, k kn, serviceName string) {
|
||||
out, err := k.RunWithOpts([]string{"service", "create",
|
||||
func (test *e2eTest) serviceCreate(t *testing.T, serviceName string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"service", "create",
|
||||
fmt.Sprintf("%s", serviceName),
|
||||
"--image", KnDefaultTestImage}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, util.ContainsAll(out, "Service", serviceName, "successfully created in namespace", k.namespace, "OK"))
|
||||
assert.Check(t, util.ContainsAll(out, "Service", serviceName, "successfully created in namespace", test.kn.namespace, "OK"))
|
||||
}
|
||||
|
||||
func testServiceList(t *testing.T, k kn, serviceName string) {
|
||||
out, err := k.RunWithOpts([]string{"service", "list", serviceName}, runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) serviceList(t *testing.T, serviceName string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"service", "list", serviceName}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedOutput := fmt.Sprintf("%s", serviceName)
|
||||
assert.Check(t, util.ContainsAll(out, expectedOutput))
|
||||
}
|
||||
|
||||
func testRevisionListForService(t *testing.T, k kn, serviceName string) {
|
||||
out, err := k.RunWithOpts([]string{"revision", "list", "-s", serviceName}, runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) revisionListForService(t *testing.T, serviceName string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"revision", "list", "-s", serviceName}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
outputLines := strings.Split(out, "\n")
|
||||
|
|
@ -129,8 +110,8 @@ func testRevisionListForService(t *testing.T, k kn, serviceName string) {
|
|||
}
|
||||
}
|
||||
|
||||
func testServiceDescribe(t *testing.T, k kn, serviceName string) {
|
||||
out, err := k.RunWithOpts([]string{"service", "describe", serviceName}, runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) serviceDescribe(t *testing.T, serviceName string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"service", "describe", serviceName}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedOutputHeader := `apiVersion: serving.knative.dev/v1alpha1
|
||||
|
|
@ -139,37 +120,37 @@ metadata:`
|
|||
expectedOutput := `generation: 1
|
||||
name: %s
|
||||
namespace: %s`
|
||||
expectedOutput = fmt.Sprintf(expectedOutput, serviceName, k.namespace)
|
||||
expectedOutput = fmt.Sprintf(expectedOutput, serviceName, test.kn.namespace)
|
||||
assert.Check(t, util.ContainsAll(out, expectedOutputHeader, expectedOutput))
|
||||
}
|
||||
|
||||
func testServiceUpdate(t *testing.T, k kn, serviceName string, args []string) {
|
||||
out, err := k.RunWithOpts(append([]string{"service", "update", serviceName}, args...), runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) serviceUpdate(t *testing.T, serviceName string, args []string) {
|
||||
out, err := test.kn.RunWithOpts(append([]string{"service", "update", serviceName}, args...), runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedOutput := fmt.Sprintf("Service '%s' updated", serviceName)
|
||||
assert.Check(t, util.ContainsAll(out, expectedOutput))
|
||||
}
|
||||
|
||||
func testRouteList(t *testing.T, k kn) {
|
||||
out, err := k.RunWithOpts([]string{"route", "list"}, runOpts{})
|
||||
func (test *e2eTest) routeList(t *testing.T) {
|
||||
out, err := test.kn.RunWithOpts([]string{"route", "list"}, runOpts{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedHeaders := []string{"NAME", "URL", "AGE", "CONDITIONS", "TRAFFIC"}
|
||||
assert.Check(t, util.ContainsAll(out, expectedHeaders...))
|
||||
}
|
||||
|
||||
func testRouteListWithArgument(t *testing.T, k kn, routeName string) {
|
||||
out, err := k.RunWithOpts([]string{"route", "list", routeName}, runOpts{})
|
||||
func (test *e2eTest) routeListWithArgument(t *testing.T, routeName string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"route", "list", routeName}, runOpts{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedOutput := fmt.Sprintf("100%% -> %s", routeName)
|
||||
assert.Check(t, util.ContainsAll(out, routeName, expectedOutput))
|
||||
}
|
||||
|
||||
func testServiceDelete(t *testing.T, k kn, serviceName string) {
|
||||
out, err := k.RunWithOpts([]string{"service", "delete", serviceName}, runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) serviceDelete(t *testing.T, serviceName string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"service", "delete", serviceName}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, util.ContainsAll(out, "Service", serviceName, "successfully deleted in namespace", k.namespace))
|
||||
assert.Check(t, util.ContainsAll(out, "Service", serviceName, "successfully deleted in namespace", test.kn.namespace))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type runOpts struct {
|
||||
|
|
@ -35,22 +36,53 @@ type runOpts struct {
|
|||
Redact bool
|
||||
}
|
||||
|
||||
const (
|
||||
KnDefaultTestImage string = "gcr.io/knative-samples/helloworld-go"
|
||||
MaxRetries int = 10
|
||||
RetrySleepDuration time.Duration = 30 * time.Second
|
||||
)
|
||||
|
||||
type e2eTest struct {
|
||||
env env
|
||||
kn kn
|
||||
}
|
||||
|
||||
func NewE2eTest(t *testing.T) *e2eTest {
|
||||
return &e2eTest{
|
||||
env: buildEnv(t),
|
||||
}
|
||||
}
|
||||
|
||||
// Setup set up an enviroment for kn integration test returns the Teardown cleanup function
|
||||
func (test *e2eTest) Setup(t *testing.T) {
|
||||
test.env.Namespace = fmt.Sprintf("%s%d", test.env.Namespace, namespaceCount)
|
||||
namespaceCount++
|
||||
test.kn = kn{t, test.env.Namespace, Logger{}}
|
||||
test.CreateTestNamespace(t, test.env.Namespace)
|
||||
}
|
||||
|
||||
// Teardown clean up
|
||||
func (test *e2eTest) Teardown(t *testing.T) {
|
||||
test.DeleteTestNamespace(t, test.env.Namespace)
|
||||
}
|
||||
|
||||
// CreateTestNamespace creates and tests a namesspace creation invoking kubectl
|
||||
func CreateTestNamespace(t *testing.T, namespace string) {
|
||||
kubectl := kubectl{t, Logger{}}
|
||||
out, err := kubectl.RunWithOpts([]string{"create", "namespace", namespace}, runOpts{})
|
||||
func (test *e2eTest) CreateTestNamespace(t *testing.T, namespace string) {
|
||||
logger := Logger{}
|
||||
expectedOutputRegexp := fmt.Sprintf("namespace?.+%s.+created", namespace)
|
||||
out, err := createNamespace(t, namespace, MaxRetries, logger)
|
||||
if err != nil {
|
||||
t.Fatalf(fmt.Sprintf("Error executing 'kubectl create namespace' command. Error: %s", err.Error()))
|
||||
logger.Fatalf("Could not create namespace, giving up")
|
||||
}
|
||||
|
||||
expectedOutputRegexp := fmt.Sprintf("namespace?.+%s.+created", namespace)
|
||||
// check that last output indeed show created namespace
|
||||
if !matchRegexp(t, expectedOutputRegexp, out) {
|
||||
t.Fatalf("Expected output incorrect, expecting to include:\n%s\n Instead found:\n%s\n", expectedOutputRegexp, out)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTestNamespace deletes and tests a namesspace deletion invoking kubectl
|
||||
func DeleteTestNamespace(t *testing.T, namespace string) {
|
||||
func (test *e2eTest) DeleteTestNamespace(t *testing.T, namespace string) {
|
||||
kubectl := kubectl{t, Logger{}}
|
||||
out, err := kubectl.RunWithOpts([]string{"delete", "namespace", namespace}, runOpts{})
|
||||
if err != nil {
|
||||
|
|
@ -63,7 +95,61 @@ func DeleteTestNamespace(t *testing.T, namespace string) {
|
|||
}
|
||||
}
|
||||
|
||||
// WaitForNamespaceDeleted wait until namespace is deleted
|
||||
func (test *e2eTest) WaitForNamespaceDeleted(t *testing.T, namespace string) {
|
||||
logger := Logger{}
|
||||
deleted := checkNamespaceDeleted(t, namespace, MaxRetries, logger)
|
||||
if !deleted {
|
||||
t.Fatalf(fmt.Sprintf("Error deleting namespace %s, timed out", namespace))
|
||||
}
|
||||
}
|
||||
|
||||
// Private functions
|
||||
func checkNamespaceDeleted(t *testing.T, namespace string, maxRetries int, logger Logger) bool {
|
||||
kubectlGetNamespace := func() (string, error) {
|
||||
kubectl := kubectl{t, logger}
|
||||
return kubectl.RunWithOpts([]string{"get", "namespace"}, runOpts{})
|
||||
}
|
||||
|
||||
retries := 0
|
||||
for retries < MaxRetries {
|
||||
output, _ := kubectlGetNamespace()
|
||||
if !strings.Contains(output, namespace) {
|
||||
return true
|
||||
}
|
||||
|
||||
retries++
|
||||
logger.Debugf("Namespace is terminating, waiting %ds, and trying again: %d of %d\n", int(RetrySleepDuration.Seconds()), retries, maxRetries)
|
||||
time.Sleep(RetrySleepDuration)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func createNamespace(t *testing.T, namespace string, maxRetries int, logger Logger) (string, error) {
|
||||
kubectlCreateNamespace := func() (string, error) {
|
||||
kubectl := kubectl{t, logger}
|
||||
return kubectl.RunWithOpts([]string{"create", "namespace", namespace}, runOpts{AllowError: true})
|
||||
}
|
||||
|
||||
var (
|
||||
retries int
|
||||
err error
|
||||
out string
|
||||
)
|
||||
|
||||
for retries < maxRetries {
|
||||
out, err := kubectlCreateNamespace()
|
||||
if err == nil {
|
||||
return out, nil
|
||||
}
|
||||
retries++
|
||||
logger.Debugf("Could not create namespace, waiting %ds, and trying again: %d of %d\n", int(RetrySleepDuration.Seconds()), retries, maxRetries)
|
||||
time.Sleep(RetrySleepDuration)
|
||||
}
|
||||
|
||||
return out, err
|
||||
}
|
||||
|
||||
func runCLIWithOpts(cli string, args []string, opts runOpts, logger Logger) (string, error) {
|
||||
logger.Debugf("Running '%s'...\n", cmdCLIDesc(cli, args))
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ type env struct {
|
|||
|
||||
const defaultKnE2ENamespace = "kne2etests"
|
||||
|
||||
var namespaceCount = 0
|
||||
|
||||
func buildEnv(t *testing.T) env {
|
||||
env := env{
|
||||
Namespace: os.Getenv("KN_E2E_NAMESPACE"),
|
||||
|
|
|
|||
|
|
@ -25,31 +25,34 @@ import (
|
|||
)
|
||||
|
||||
func TestRevisionWorkflow(t *testing.T) {
|
||||
teardown := Setup(t)
|
||||
defer teardown(t)
|
||||
test := NewE2eTest(t)
|
||||
test.Setup(t)
|
||||
defer test.Teardown(t)
|
||||
|
||||
t.Run("create hello service and returns no error", func(t *testing.T) {
|
||||
testServiceCreate(t, k, "hello")
|
||||
test.serviceCreate(t, "hello")
|
||||
})
|
||||
|
||||
t.Run("delete latest revision from hello service and returns no error", func(t *testing.T) {
|
||||
testDeleteRevision(t, k, "hello")
|
||||
test.deleteRevision(t, "hello")
|
||||
})
|
||||
|
||||
t.Run("delete hello service and returns no error", func(t *testing.T) {
|
||||
testServiceDelete(t, k, "hello")
|
||||
test.serviceDelete(t, "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func testDeleteRevision(t *testing.T, k kn, serviceName string) {
|
||||
revName, err := k.RunWithOpts([]string{"revision", "list", "-o=jsonpath={.items[0].metadata.name}"}, runOpts{})
|
||||
// Private
|
||||
|
||||
func (test *e2eTest) deleteRevision(t *testing.T, serviceName string) {
|
||||
revName, err := test.kn.RunWithOpts([]string{"revision", "list", "-o=jsonpath={.items[0].metadata.name}"}, runOpts{})
|
||||
assert.NilError(t, err)
|
||||
if strings.Contains(revName, "No resources found.") {
|
||||
t.Errorf("Could not find revision name.")
|
||||
}
|
||||
|
||||
out, err := k.RunWithOpts([]string{"revision", "delete", revName}, runOpts{})
|
||||
out, err := test.kn.RunWithOpts([]string{"revision", "delete", revName}, runOpts{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, util.ContainsAll(out, "Revision", revName, "deleted", "namespace", k.namespace))
|
||||
assert.Check(t, util.ContainsAll(out, "Revision", revName, "deleted", "namespace", test.kn.namespace))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,50 +25,53 @@ import (
|
|||
)
|
||||
|
||||
func TestServiceOptions(t *testing.T) {
|
||||
teardown := Setup(t)
|
||||
defer teardown(t)
|
||||
test := NewE2eTest(t)
|
||||
test.Setup(t)
|
||||
defer test.Teardown(t)
|
||||
|
||||
t.Run("create hello service with concurrency options and returns no error", func(t *testing.T) {
|
||||
testServiceCreateWithOptions(t, k, "hello", []string{"--concurrency-limit", "250", "--concurrency-target", "300"})
|
||||
test.serviceCreateWithOptions(t, "hello", []string{"--concurrency-limit", "250", "--concurrency-target", "300"})
|
||||
})
|
||||
|
||||
t.Run("returns valid concurrency options for hello service", func(t *testing.T) {
|
||||
testServiceDescribeConcurrencyLimit(t, k, "hello", "250")
|
||||
testServiceDescribeConcurrencyTarget(t, k, "hello", "300")
|
||||
test.serviceDescribeConcurrencyLimit(t, "hello", "250")
|
||||
test.serviceDescribeConcurrencyTarget(t, "hello", "300")
|
||||
})
|
||||
|
||||
t.Run("update concurrency limit for hello service and returns no error", func(t *testing.T) {
|
||||
testServiceUpdate(t, k, "hello", []string{"--concurrency-limit", "300"})
|
||||
test.serviceUpdate(t, "hello", []string{"--concurrency-limit", "300"})
|
||||
})
|
||||
|
||||
t.Run("returns correct concurrency limit for hello service", func(t *testing.T) {
|
||||
testServiceDescribeConcurrencyLimit(t, k, "hello", "300")
|
||||
test.serviceDescribeConcurrencyLimit(t, "hello", "300")
|
||||
})
|
||||
|
||||
t.Run("delete hello service and returns no error", func(t *testing.T) {
|
||||
testServiceDelete(t, k, "hello")
|
||||
test.serviceDelete(t, "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func testServiceCreateWithOptions(t *testing.T, k kn, serviceName string, options []string) {
|
||||
// Private
|
||||
|
||||
func (test *e2eTest) serviceCreateWithOptions(t *testing.T, serviceName string, options []string) {
|
||||
command := []string{"service", "create", serviceName, "--image", KnDefaultTestImage}
|
||||
command = append(command, options...)
|
||||
out, err := k.RunWithOpts(command, runOpts{NoNamespace: false})
|
||||
out, err := test.kn.RunWithOpts(command, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, util.ContainsAll(out, "Service", serviceName, "successfully created in namespace", k.namespace, "OK"))
|
||||
assert.Check(t, util.ContainsAll(out, "Service", serviceName, "successfully created in namespace", test.kn.namespace, "OK"))
|
||||
}
|
||||
|
||||
func testServiceDescribeConcurrencyLimit(t *testing.T, k kn, serviceName, concurrencyLimit string) {
|
||||
out, err := k.RunWithOpts([]string{"service", "describe", serviceName}, runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) serviceDescribeConcurrencyLimit(t *testing.T, serviceName, concurrencyLimit string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"service", "describe", serviceName}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedOutput := fmt.Sprintf("containerConcurrency: %s", concurrencyLimit)
|
||||
assert.Check(t, util.ContainsAll(out, expectedOutput))
|
||||
}
|
||||
|
||||
func testServiceDescribeConcurrencyTarget(t *testing.T, k kn, serviceName, concurrencyTarget string) {
|
||||
out, err := k.RunWithOpts([]string{"service", "describe", serviceName}, runOpts{NoNamespace: false})
|
||||
func (test *e2eTest) serviceDescribeConcurrencyTarget(t *testing.T, serviceName, concurrencyTarget string) {
|
||||
out, err := test.kn.RunWithOpts([]string{"service", "describe", serviceName}, runOpts{NoNamespace: false})
|
||||
assert.NilError(t, err)
|
||||
|
||||
expectedOutput := fmt.Sprintf("autoscaling.knative.dev/target: \"%s\"", concurrencyTarget)
|
||||
|
|
|
|||
Loading…
Reference in New Issue