diff --git a/pkg/reconciler/knativeeventing/knativeeventing.go b/pkg/reconciler/knativeeventing/knativeeventing.go index cac2ba86..2ca621b9 100644 --- a/pkg/reconciler/knativeeventing/knativeeventing.go +++ b/pkg/reconciler/knativeeventing/knativeeventing.go @@ -214,8 +214,7 @@ func (r *Reconciler) updateStatus(desired *eventingv1alpha1.KnativeEventing) (*e // Delete obsolete resources from previous versions func (r *Reconciler) deleteObsoleteResources(manifest *mf.Manifest, instance *eventingv1alpha1.KnativeEventing) error { resource := &unstructured.Unstructured{} - - resource.SetNamespace("knative-eventing") + resource.SetNamespace(instance.GetNamespace()) // Remove old resources from 0.12 // https://github.com/knative/eventing-operator/issues/90 diff --git a/test/e2e/e2e.go b/test/client/setup.go similarity index 95% rename from test/e2e/e2e.go rename to test/client/setup.go index 1d250ae4..2a105607 100644 --- a/test/e2e/e2e.go +++ b/test/client/setup.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Knative Authors +Copyright 2020 The Knative Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package e2e +package client import ( "testing" @@ -20,6 +20,7 @@ import ( // Apparently just importing it is enough. @_@ side effects @_@. // https://github.com/kubernetes/client-go/issues/242 _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "knative.dev/eventing-operator/test" pkgTest "knative.dev/pkg/test" ) diff --git a/test/e2e-upgrade-tests.sh b/test/e2e-upgrade-tests.sh index 9115b81a..22a660b4 100755 --- a/test/e2e-upgrade-tests.sh +++ b/test/e2e-upgrade-tests.sh @@ -28,6 +28,9 @@ source $(dirname $0)/../vendor/knative.dev/test-infra/scripts/e2e-tests.sh +# Latest eventing operator release. +readonly LATEST_EVENTING_RELEASE_VERSION=$(git describe --match "v[0-9]*" --abbrev=0) + OPERATOR_DIR=$(dirname $0)/.. KNATIVE_EVENTING_DIR=${OPERATOR_DIR}/.. @@ -48,9 +51,37 @@ function install_eventing_operator() { wait_until_pods_running default || fail_test "Eventing Operator did not come up" } +function install_latest_operator_release() { + header "Installing Knative Eventing operator latest public release" + local full_url="https://github.com/knative/eventing-operator/releases/download/${LATEST_EVENTING_RELEASE_VERSION}/eventing-operator.yaml" + + local release_yaml="$(mktemp)" + wget "${full_url}" -O "${release_yaml}" \ + || fail_test "Unable to download latest Knative Eventing Operator release." + + kubectl apply -f "${release_yaml}" || fail_test "Knative Eventing Operator latest release installation failed" + create_custom_resource + wait_until_pods_running ${TEST_NAMESPACE} +} + +function create_custom_resource() { + echo ">> Creating the custom resource of Knative Eventing:" + cat <> Creating test namespaces" kubectl create namespace $TEST_NAMESPACE + install_latest_operator_release +} + +function install_head() { generate_latest_eventing_manifest install_eventing_operator } @@ -95,12 +126,16 @@ function generate_latest_eventing_manifest() { # Skip installing istio as an add-on initialize $@ --skip-istio-addon +TIMEOUT=20m + +install_head + # If we got this far, the operator installed Knative Eventing header "Running tests for Knative Eventing Operator" failed=0 -# Run the integration tests -go_test_e2e -timeout=20m ./test/e2e || failed=1 +# Run the postupgrade tests +go_test_e2e -tags=postupgrade -timeout=${TIMEOUT} ./test/upgrade || failed=1 # Require that tests succeeded. (( failed )) && fail_test diff --git a/test/e2e/knativeeventingdeployment_test.go b/test/e2e/knativeeventingdeployment_test.go index bb4ff847..c79da269 100644 --- a/test/e2e/knativeeventingdeployment_test.go +++ b/test/e2e/knativeeventingdeployment_test.go @@ -1,7 +1,7 @@ // +build e2e /* -Copyright 2019 The Knative Authors +Copyright 2020 The Knative Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -16,18 +16,10 @@ limitations under the License. package e2e import ( - "errors" - "path/filepath" - "runtime" "testing" - "k8s.io/apimachinery/pkg/api/meta" - - mf "github.com/manifestival/manifestival" - apierrs "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" "knative.dev/eventing-operator/test" + "knative.dev/eventing-operator/test/client" "knative.dev/eventing-operator/test/resources" "knative.dev/pkg/test/logstream" ) @@ -36,7 +28,7 @@ import ( func TestKnativeEventingDeployment(t *testing.T) { cancel := logstream.Start(t) defer cancel() - clients := Setup(t) + clients := client.Setup(t) names := test.ResourceNames{ KnativeEventing: test.EventingOperatorName, @@ -47,119 +39,24 @@ func TestKnativeEventingDeployment(t *testing.T) { defer test.TearDown(clients, names) // Create a KnativeEventing - if _, err := resources.CreateKnativeEventing(clients.KnativeEventing(), names); err != nil { + if _, err := resources.EnsureKnativeEventingExists(clients.KnativeEventing(), names); err != nil { t.Fatalf("KnativeService %q failed to create: %v", names.KnativeEventing, err) } // Test if KnativeEventing can reach the READY status t.Run("create", func(t *testing.T) { - knativeEventingVerify(t, clients, names) + resources.AssertKEOperatorCRReadyStatus(t, clients, names) }) // Delete the deployments one by one to see if they will be recreated. t.Run("restore", func(t *testing.T) { - knativeEventingVerify(t, clients, names) - deploymentRecreation(t, clients, names) + resources.AssertKEOperatorCRReadyStatus(t, clients, names) + resources.DeleteAndVerifyDeployments(t, clients, names) }) // Delete the KnativeEventing to see if all resources will be removed t.Run("delete", func(t *testing.T) { - knativeEventingVerify(t, clients, names) - knativeEventingDelete(t, clients, names) - }) -} - -// knativeEventingVerify verifies if the KnativeEventing can reach the READY status. -func knativeEventingVerify(t *testing.T, clients *test.Clients, names test.ResourceNames) { - if _, err := resources.WaitForKnativeEventingState(clients.KnativeEventing(), names.KnativeEventing, - resources.IsKnativeEventingReady); err != nil { - t.Fatalf("KnativeService %q failed to get to the READY status: %v", names.KnativeEventing, err) - } - -} - -// deploymentRecreation verify whether all the deployments for knative eventing are able to recreate, when they are deleted. -func deploymentRecreation(t *testing.T, clients *test.Clients, names test.ResourceNames) { - dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{}) - if err != nil { - t.Fatalf("Failed to get any deployment under the namespace %q: %v", - test.EventingOperatorNamespace, err) - } - if len(dpList.Items) == 0 { - t.Fatalf("No deployment under the namespace %q was found", - test.EventingOperatorNamespace) - } - // Delete the first deployment and verify the operator recreates it - deployment := dpList.Items[0] - if err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Delete(deployment.Name, - &metav1.DeleteOptions{}); err != nil { - t.Fatalf("Failed to delete deployment %s/%s: %v", deployment.Namespace, deployment.Name, err) - } - - waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) { - dep, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{}) - if err != nil { - // If the deployment is not found, we continue to wait for the availability. - if apierrs.IsNotFound(err) { - return false, nil - } - return false, err - } - return resources.IsDeploymentAvailable(dep) + resources.AssertKEOperatorCRReadyStatus(t, clients, names) + resources.KEOperatorCRDelete(t, clients, names) }) - - if waitErr != nil { - t.Fatalf("The deployment %s/%s failed to reach the desired state: %v", deployment.Namespace, deployment.Name, err) - } - - if _, err := resources.WaitForKnativeEventingState(clients.KnativeEventing(), test.EventingOperatorName, - resources.IsKnativeEventingReady); err != nil { - t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.EventingOperatorName, err) - } - t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name) -} - -// knativeEventingDelete deletes tha KnativeEventing to see if all resources will be deleted -func knativeEventingDelete(t *testing.T, clients *test.Clients, names test.ResourceNames) { - if err := clients.KnativeEventing().Delete(names.KnativeEventing, &metav1.DeleteOptions{}); err != nil { - t.Fatalf("KnativeEventing %q failed to delete: %v", names.KnativeEventing, err) - } - _, b, _, _ := runtime.Caller(0) - m, err := mf.NewManifest(filepath.Join((filepath.Dir(b)+"/.."), "config/"), false, clients.Config) - if err != nil { - t.Fatal("Failed to load manifest", err) - } - if err := verifyNoKnativeEventings(clients); err != nil { - t.Fatal(err) - } - for _, u := range m.Resources { - if u.GetKind() == "Namespace" { - // The namespace should be skipped, because when the CR is removed, the Manifest to be removed has - // been modified, since the namespace can be injected. - continue - } - waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) { - gvrs, _ := meta.UnsafeGuessKindToResource(u.GroupVersionKind()) - if _, err := clients.Dynamic.Resource(gvrs).Get(u.GetName(), metav1.GetOptions{}); apierrs.IsNotFound(err) { - return true, nil - } - return false, err - }) - - if waitErr != nil { - t.Fatalf("The %s %s failed to be deleted: %v", u.GetKind(), u.GetName(), waitErr) - } - t.Logf("The %s %s has been deleted.", u.GetKind(), u.GetName()) - } -} - -func verifyNoKnativeEventings(clients *test.Clients) error { - eventings, err := clients.KnativeEventingAll().List(metav1.ListOptions{}) - if err != nil { - return err - } - if len(eventings.Items) > 0 { - return errors.New("Unable to verify cluster-scoped resources are deleted if any KnativeEventing exists") - } - return nil } diff --git a/test/resources/knativeeventing.go b/test/resources/knativeeventing.go index 70504443..49110bd4 100644 --- a/test/resources/knativeeventing.go +++ b/test/resources/knativeeventing.go @@ -26,12 +26,14 @@ import ( "github.com/pkg/errors" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" + + "knative.dev/pkg/test/logging" "knative.dev/eventing-operator/pkg/apis/eventing/v1alpha1" eventingv1alpha1 "knative.dev/eventing-operator/pkg/client/clientset/versioned/typed/eventing/v1alpha1" "knative.dev/eventing-operator/test" - "knative.dev/pkg/test/logging" ) const ( @@ -61,16 +63,20 @@ func WaitForKnativeEventingState(clients eventingv1alpha1.KnativeEventingInterfa return lastState, nil } -// CreateKnativeEventing creates a KnativeEventingServing with the name names.KnativeEventing under the namespace names.Namespace. -func CreateKnativeEventing(clients eventingv1alpha1.KnativeEventingInterface, names test.ResourceNames) (*v1alpha1.KnativeEventing, error) { - ks := &v1alpha1.KnativeEventing{ - ObjectMeta: metav1.ObjectMeta{ - Name: names.KnativeEventing, - Namespace: names.Namespace, - }, +// EnsureKnativeEventingExists creates a KnativeEventingServing with the name names.KnativeEventing under the namespace names.Namespace. +func EnsureKnativeEventingExists(clients eventingv1alpha1.KnativeEventingInterface, names test.ResourceNames) (*v1alpha1.KnativeEventing, error) { + // If this function is called by the upgrade tests, we only create the custom resource, if it does not exist. + ke, err := clients.Get(names.KnativeEventing, metav1.GetOptions{}) + if apierrs.IsNotFound(err) { + ke := &v1alpha1.KnativeEventing{ + ObjectMeta: metav1.ObjectMeta{ + Name: names.KnativeEventing, + Namespace: names.Namespace, + }, + } + return clients.Create(ke) } - svc, err := clients.Create(ks) - return svc, err + return ke, err } // IsKnativeEventingReady will check the status conditions of the KnativeEventing and return true if the KnativeEventing is ready. diff --git a/test/resources/verify.go b/test/resources/verify.go new file mode 100644 index 00000000..1a108521 --- /dev/null +++ b/test/resources/verify.go @@ -0,0 +1,139 @@ +/* +Copyright 2020 The Knative Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resources + +import ( + "errors" + "path/filepath" + "runtime" + "testing" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/util/wait" + apierrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + // Mysteriously required to support GCP auth (required by k8s libs). + // Apparently just importing it is enough. @_@ side effects @_@. + // https://github.com/kubernetes/client-go/issues/242 + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + mf "github.com/manifestival/manifestival" + + pkgTest "knative.dev/pkg/test" + "knative.dev/eventing-operator/test" +) + +// Setup creates the client objects needed in the e2e tests. +func Setup(t *testing.T) *test.Clients { + clients, err := test.NewClients( + pkgTest.Flags.Kubeconfig, + pkgTest.Flags.Cluster) + if err != nil { + t.Fatalf("Couldn't initialize clients: %v", err) + } + return clients +} + +// AssertKEOperatorCRReadyStatus verifies if the KnativeEventing can reach the READY status. +func AssertKEOperatorCRReadyStatus(t *testing.T, clients *test.Clients, names test.ResourceNames) { + if _, err := WaitForKnativeEventingState(clients.KnativeEventing(), names.KnativeEventing, + IsKnativeEventingReady); err != nil { + t.Fatalf("KnativeService %q failed to get to the READY status: %v", names.KnativeEventing, err) + } +} + +// DeleteAndVerifyDeployments verify whether all the deployments for knative eventing are able to recreate, when they are deleted. +func DeleteAndVerifyDeployments(t *testing.T, clients *test.Clients, names test.ResourceNames) { + dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{}) + if err != nil { + t.Fatalf("Failed to get any deployment under the namespace %q: %v", + test.EventingOperatorNamespace, err) + } + if len(dpList.Items) == 0 { + t.Fatalf("No deployment under the namespace %q was found", + test.EventingOperatorNamespace) + } + // Delete the first deployment and verify the operator recreates it + deployment := dpList.Items[0] + if err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Delete(deployment.Name, + &metav1.DeleteOptions{}); err != nil { + t.Fatalf("Failed to delete deployment %s/%s: %v", deployment.Namespace, deployment.Name, err) + } + + waitErr := wait.PollImmediate(Interval, Timeout, func() (bool, error) { + dep, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{}) + if err != nil { + // If the deployment is not found, we continue to wait for the availability. + if apierrs.IsNotFound(err) { + return false, nil + } + return false, err + } + return IsDeploymentAvailable(dep) + }) + + if waitErr != nil { + t.Fatalf("The deployment %s/%s failed to reach the desired state: %v", deployment.Namespace, deployment.Name, err) + } + + if _, err := WaitForKnativeEventingState(clients.KnativeEventing(), test.EventingOperatorName, + IsKnativeEventingReady); err != nil { + t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.EventingOperatorName, err) + } + t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name) +} + +// KEOperatorCRDelete deletes tha KnativeEventing to see if all resources will be deleted +func KEOperatorCRDelete(t *testing.T, clients *test.Clients, names test.ResourceNames) { + if err := clients.KnativeEventing().Delete(names.KnativeEventing, &metav1.DeleteOptions{}); err != nil { + t.Fatalf("KnativeEventing %q failed to delete: %v", names.KnativeEventing, err) + } + _, b, _, _ := runtime.Caller(0) + m, err := mf.NewManifest(filepath.Join((filepath.Dir(b)+"/.."), "config/"), false, clients.Config) + if err != nil { + t.Fatal("Failed to load manifest", err) + } + if err := verifyNoKnativeEventings(clients); err != nil { + t.Fatal(err) + } + for _, u := range m.Resources { + if u.GetKind() == "Namespace" { + // The namespace should be skipped, because when the CR is removed, the Manifest to be removed has + // been modified, since the namespace can be injected. + continue + } + waitErr := wait.PollImmediate(Interval, Timeout, func() (bool, error) { + gvrs, _ := meta.UnsafeGuessKindToResource(u.GroupVersionKind()) + if _, err := clients.Dynamic.Resource(gvrs).Get(u.GetName(), metav1.GetOptions{}); apierrs.IsNotFound(err) { + return true, nil + } + return false, err + }) + + if waitErr != nil { + t.Fatalf("The %s %s failed to be deleted: %v", u.GetKind(), u.GetName(), waitErr) + } + t.Logf("The %s %s has been deleted.", u.GetKind(), u.GetName()) + } +} + +func verifyNoKnativeEventings(clients *test.Clients) error { + eventings, err := clients.KnativeEventingAll().List(metav1.ListOptions{}) + if err != nil { + return err + } + if len(eventings.Items) > 0 { + return errors.New("Unable to verify cluster-scoped resources are deleted if any KnativeEventing exists") + } + return nil +} diff --git a/test/upgrade/knativeeventingupgrade_test.go b/test/upgrade/knativeeventingupgrade_test.go new file mode 100644 index 00000000..ae53b557 --- /dev/null +++ b/test/upgrade/knativeeventingupgrade_test.go @@ -0,0 +1,98 @@ +// +build postupgrade + +/* +Copyright 2020 The Knative Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "knative.dev/pkg/test/logstream" + "knative.dev/eventing-operator/test" + "knative.dev/eventing-operator/test/resources" + "knative.dev/eventing-operator/test/client" +) + +// TestKnativeEventingUpgrade verifies the KnativeEventing creation, deployment recreation, and KnativeEventing deletion +// after upgraded to the latest HEAD at master, with the latest generated manifest of KnativeEventing. +func TestKnativeEventingUpgrade(t *testing.T) { + cancel := logstream.Start(t) + defer cancel() + clients := client.Setup(t) + + names := test.ResourceNames{ + KnativeEventing: test.EventingOperatorName, + Namespace: test.EventingOperatorNamespace, + } + + test.CleanupOnInterrupt(func() { test.TearDown(clients, names) }) + defer test.TearDown(clients, names) + + // Create a KnativeEventing + if _, err := resources.EnsureKnativeEventingExists(clients.KnativeEventing(), names); err != nil { + t.Fatalf("KnativeService %q failed to create: %v", names.KnativeEventing, err) + } + + // Test if KnativeEventing can reach the READY status + t.Run("create", func(t *testing.T) { + resources.AssertKEOperatorCRReadyStatus(t, clients, names) + }) + + // Verify if resources match the latest requirement after upgrade + t.Run("verify resources", func(t *testing.T) { + resources.AssertKEOperatorCRReadyStatus(t, clients, names) + // TODO: We only verify the deployment, but we need to add other resources as well, like ServiceAccount, ClusterRoleBinding, etc. + expectedDeployments := []string{"eventing-controller", "eventing-webhook", "imc-controller", + "imc-dispatcher", "broker-controller"} + knativeEventingVerifyDeployment(t, clients, names, expectedDeployments) + }) + + // TODO: We will add one or sections here to run the tests tagged with postupgrade in knative evening. + + // Delete the KnativeEventing to see if all resources will be removed + t.Run("delete", func(t *testing.T) { + resources.AssertKEOperatorCRReadyStatus(t, clients, names) + resources.KEOperatorCRDelete(t, clients, names) + }) +} + +// knativeEventingVerifyDeployment verify whether the deployments have the correct number and names. +func knativeEventingVerifyDeployment(t *testing.T, clients *test.Clients, names test.ResourceNames, + expectedDeployments []string) { + dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{}) + assertEqual(t, err, nil) + assertEqual(t, len(dpList.Items), len(expectedDeployments)) + for _, deployment := range dpList.Items { + assertEqual(t, stringInList(deployment.Name, expectedDeployments), true) + } +} + +func assertEqual(t *testing.T, actual, expected interface{}) { + if actual == expected { + return + } + t.Fatalf("expected does not equal actual. \nExpected: %v\nActual: %v", expected, actual) +} + +func stringInList(a string, list []string) bool { + for _, b := range list { + if b == a { + return true + } + } + return false +} +