From 96ef5700753d21ef9beffc965e7323c87f0df835 Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Tue, 27 Aug 2024 16:51:10 +0200 Subject: [PATCH 01/10] Prepare adding end-to-end tests to the controller go module --- .dockerignore | 1 + Makefile | 16 +- go.mod | 2 + test/e2e/e2e_test.go | 449 +++++++++--------- test/e2e/{e2e_utils_test.go => suit_utils.go} | 179 +++++-- test/e2e/suite.go | 70 ++- test/e2e/update_test.go | 14 +- 7 files changed, 433 insertions(+), 298 deletions(-) rename test/e2e/{e2e_utils_test.go => suit_utils.go} (64%) diff --git a/.dockerignore b/.dockerignore index 12b36705..74561975 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,4 @@ .cache/ default.etcd/ apiserver.local.config/ +__debug_bin* diff --git a/Makefile b/Makefile index eb0d0fa3..1fa9414d 100644 --- a/Makefile +++ b/Makefile @@ -286,15 +286,15 @@ load-images-to-kind: ## Build porch images and load them into a kind cluster # only build test-git-server & function-runner if they are not already loaded into kind @if ! docker exec "${KIND_CONTEXT_NAME}-control-plane" crictl images | grep -q "$(IMAGE_REPO)/$(TEST_GIT_SERVER_IMAGE) *${IMAGE_TAG} " ; then \ echo "Building $(IMAGE_REPO)/$(TEST_GIT_SERVER_IMAGE):${IMAGE_TAG}" ; \ - IMAGE_NAME="$(TEST_GIT_SERVER_IMAGE)" make -C test/ build-image ; \ + IMAGE_NAME="$(TEST_GIT_SERVER_IMAGE)" make -C test/ build-image && \ kind load docker-image $(IMAGE_REPO)/$(TEST_GIT_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ else \ echo "Skipping building $(IMAGE_REPO)/$(TEST_GIT_SERVER_IMAGE):${IMAGE_TAG} as it is already loaded into kind" ; \ fi @if ! docker exec "${KIND_CONTEXT_NAME}-control-plane" crictl images | grep -q "$(IMAGE_REPO)/$(PORCH_FUNCTION_RUNNER_IMAGE) *${IMAGE_TAG} " ; then \ echo "Building $(IMAGE_REPO)/$(PORCH_FUNCTION_RUNNER_IMAGE):${IMAGE_TAG}" ; \ - IMAGE_NAME="$(PORCH_FUNCTION_RUNNER_IMAGE)" WRAPPER_SERVER_IMAGE_NAME="$(PORCH_WRAPPER_SERVER_IMAGE)" make -C func/ build-image ; \ - kind load docker-image $(IMAGE_REPO)/$(PORCH_FUNCTION_RUNNER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ + IMAGE_NAME="$(PORCH_FUNCTION_RUNNER_IMAGE)" WRAPPER_SERVER_IMAGE_NAME="$(PORCH_WRAPPER_SERVER_IMAGE)" make -C func/ build-image && \ + kind load docker-image $(IMAGE_REPO)/$(PORCH_FUNCTION_RUNNER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} && \ kind load docker-image $(IMAGE_REPO)/$(PORCH_WRAPPER_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ else \ echo "Skipping building $(IMAGE_REPO)/$(PORCH_FUNCTION_RUNNER_IMAGE):${IMAGE_TAG} as it is already loaded into kind" ; \ @@ -302,19 +302,19 @@ load-images-to-kind: ## Build porch images and load them into a kind cluster # NOTE: SKIP_PORCHSERVER_BUILD must be evaluated at runtime, hence the shell conditional (if) here @if [ "$(SKIP_PORCHSERVER_BUILD)" = "false" ]; then \ echo "Building $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):${IMAGE_TAG}" ; \ - docker buildx build --load --tag $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):$(IMAGE_TAG) -f ./build/Dockerfile "$(PORCHDIR)" ; \ + docker buildx build --load --tag $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):$(IMAGE_TAG) -f ./build/Dockerfile "$(PORCHDIR)" && \ kind load docker-image $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ fi @if [ "$(SKIP_CONTROLLER_BUILD)" = "false" ]; then \ echo "Building $(IMAGE_REPO)/$(PORCH_CONTROLLERS_IMAGE):${IMAGE_TAG}" ; \ - IMAGE_NAME="$(PORCH_CONTROLLERS_IMAGE)" make -C controllers/ build-image ; \ + IMAGE_NAME="$(PORCH_CONTROLLERS_IMAGE)" make -C controllers/ build-image && \ kind load docker-image $(IMAGE_REPO)/$(PORCH_CONTROLLERS_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ fi else - kind load docker-image $(IMAGE_REPO)/$(TEST_GIT_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ - kind load docker-image $(IMAGE_REPO)/$(PORCH_FUNCTION_RUNNER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ - kind load docker-image $(IMAGE_REPO)/$(PORCH_WRAPPER_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ + kind load docker-image $(IMAGE_REPO)/$(TEST_GIT_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} + kind load docker-image $(IMAGE_REPO)/$(PORCH_FUNCTION_RUNNER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} + kind load docker-image $(IMAGE_REPO)/$(PORCH_WRAPPER_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} kind load docker-image $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} kind load docker-image $(IMAGE_REPO)/$(PORCH_CONTROLLERS_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} endif diff --git a/go.mod b/go.mod index cdae39dd..c01db5b5 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/nephio-project/porch go 1.22 +replace github.com/GoogleContainerTools/kpt-functions-sdk/go/fn => github.com/GoogleContainerTools/kpt-functions-sdk/go/fn v0.0.0-20220506190241-f85503febd54 + require ( cloud.google.com/go/iam v1.1.1 github.com/GoogleContainerTools/kpt v1.0.0-beta.48 diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 25c6156b..e874e531 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -50,48 +50,30 @@ var ( configMapGVK = corev1.SchemeGroupVersion.WithKind("ConfigMap") ) +type PorchSuite struct { + TestSuiteWithGit +} + +var _ TSetter = &PorchSuite{} +var _ Initializer = &PorchSuite{} + func TestE2E(t *testing.T) { e2e := os.Getenv("E2E") if e2e == "" { t.Skip("set E2E to run this test") } - Run(&PorchSuite{}, t) -} - -func Run(suite interface{}, t *testing.T) { - sv := reflect.ValueOf(suite) - st := reflect.TypeOf(suite) - ctx := context.Background() - - t.Run(st.Elem().Name(), func(t *testing.T) { - var ts *TestSuite = sv.Elem().FieldByName("TestSuite").Addr().Interface().(*TestSuite) - - ts.T = t - if init, ok := suite.(Initializer); ok { - init.Initialize(ctx) - } - - for i, max := 0, st.NumMethod(); i < max; i++ { - m := st.Method(i) - if strings.HasPrefix(m.Name, "Test") { - t.Run(m.Name, func(t *testing.T) { - ts.T = t - m.Func.Call([]reflect.Value{sv, reflect.ValueOf(ctx)}) - }) - } - } - }) + RunSuite(&PorchSuite{}, t) } func (t *PorchSuite) TestGitRepository(ctx context.Context) { // Register the repository as 'git' - t.registerMainGitRepositoryF(ctx, "git") + t.RegisterMainGitRepositoryF(ctx, "git") // Create Package Revision pr := &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "test-bucket", @@ -128,7 +110,7 @@ func (t *PorchSuite) TestGitRepository(ctx context.Context) { // Get package resources var resources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &resources) @@ -146,11 +128,11 @@ func (t *PorchSuite) TestGitRepository(ctx context.Context) { } func (t *PorchSuite) TestGitRepositoryWithReleaseTagsAndDirectory(ctx context.Context) { - t.registerGitRepositoryF(ctx, kptRepo, "kpt-repo", "package-examples") + t.RegisterGitRepositoryF(ctx, kptRepo, "kpt-repo", "package-examples") - t.Log("Listing PackageRevisions in " + t.namespace) + t.Log("Listing PackageRevisions in " + t.Namespace) var list porchapi.PackageRevisionList - t.ListF(ctx, &list, client.InNamespace(t.namespace)) + t.ListF(ctx, &list, client.InNamespace(t.Namespace)) for _, pr := range list.Items { if strings.HasPrefix(pr.Spec.PackageName, "package-examples") { @@ -161,15 +143,15 @@ func (t *PorchSuite) TestGitRepositoryWithReleaseTagsAndDirectory(ctx context.Co func (t *PorchSuite) TestCloneFromUpstream(ctx context.Context) { // Register Upstream Repository - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") var list porchapi.PackageRevisionList - t.ListE(ctx, &list, client.InNamespace(t.namespace)) + t.ListE(ctx, &list, client.InNamespace(t.Namespace)) basens := MustFindPackageRevision(t.T, &list, repository.PackageRevisionKey{Repository: "test-blueprints", Package: "basens", Revision: "v1"}) // Register the repository as 'downstream' - t.registerMainGitRepositoryF(ctx, "downstream") + t.RegisterMainGitRepositoryF(ctx, "downstream") // Create PackageRevision from upstream repo pr := &porchapi.PackageRevision{ @@ -178,7 +160,7 @@ func (t *PorchSuite) TestCloneFromUpstream(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "istions", @@ -204,7 +186,7 @@ func (t *PorchSuite) TestCloneFromUpstream(ctx context.Context) { // Get istions resources var istions porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &istions) @@ -220,7 +202,7 @@ func (t *PorchSuite) TestCloneFromUpstream(ctx context.Context) { t.Errorf("istions package upstreamLock.git is missing") } if kptfile.UpstreamLock.Git.Commit == "" { - t.Errorf("isions package upstreamLock.gkti.commit is missing") + t.Errorf("istions package upstreamLock.git.commit is missing") } // Remove commit from comparison @@ -263,7 +245,7 @@ func (t *PorchSuite) TestInitEmptyPackage(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a new package (via init) pr := &porchapi.PackageRevision{ @@ -272,7 +254,7 @@ func (t *PorchSuite) TestInitEmptyPackage(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "empty-package", @@ -285,7 +267,7 @@ func (t *PorchSuite) TestInitEmptyPackage(ctx context.Context) { // Get the package var newPackage porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &newPackage) @@ -312,7 +294,7 @@ func (t *PorchSuite) TestInitTaskPackage(ctx context.Context) { keywords := []string{"test"} // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a new package (via init) pr := &porchapi.PackageRevision{ @@ -321,7 +303,7 @@ func (t *PorchSuite) TestInitTaskPackage(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "new-package", @@ -344,7 +326,7 @@ func (t *PorchSuite) TestInitTaskPackage(ctx context.Context) { // Get the package var newPackage porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &newPackage) @@ -367,13 +349,13 @@ func (t *PorchSuite) TestCloneIntoDeploymentRepository(ctx context.Context) { const downstreamWorkspace = "test-workspace" // Register the deployment repository - t.registerMainGitRepositoryF(ctx, downstreamRepository, withDeployment()) + t.RegisterMainGitRepositoryF(ctx, downstreamRepository, WithDeployment()) // Register the upstream repository - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") var upstreamPackages porchapi.PackageRevisionList - t.ListE(ctx, &upstreamPackages, client.InNamespace(t.namespace)) + t.ListE(ctx, &upstreamPackages, client.InNamespace(t.Namespace)) upstreamPackage := MustFindPackageRevision(t.T, &upstreamPackages, repository.PackageRevisionKey{ Repository: "test-blueprints", Package: "basens", @@ -388,7 +370,7 @@ func (t *PorchSuite) TestCloneIntoDeploymentRepository(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.Identifier(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: downstreamPackage, @@ -413,7 +395,7 @@ func (t *PorchSuite) TestCloneIntoDeploymentRepository(ctx context.Context) { // Get istions resources var istions porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &istions) @@ -429,7 +411,7 @@ func (t *PorchSuite) TestCloneIntoDeploymentRepository(ctx context.Context) { t.Errorf("istions package upstreamLock.git is missing") } if kptfile.UpstreamLock.Git.Commit == "" { - t.Errorf("isions package upstreamLock.gkti.commit is missing") + t.Errorf("istions package upstreamLock.git.commit is missing") } // Remove commit from comparison @@ -480,7 +462,7 @@ func (t *PorchSuite) TestEditPackageRevision(ctx context.Context) { workspace2 = "workspace2" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a new package (via init) pr := &porchapi.PackageRevision{ @@ -489,7 +471,7 @@ func (t *PorchSuite) TestEditPackageRevision(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: packageName, @@ -507,7 +489,7 @@ func (t *PorchSuite) TestEditPackageRevision(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: otherPackageName, @@ -525,7 +507,7 @@ func (t *PorchSuite) TestEditPackageRevision(ctx context.Context) { }, }, } - if err := t.client.Create(ctx, invalidEditPR); err == nil { + if err := t.Client.Create(ctx, invalidEditPR); err == nil { t.Fatalf("Expected error for source revision being from different package") } @@ -537,7 +519,7 @@ func (t *PorchSuite) TestEditPackageRevision(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: packageName, @@ -555,7 +537,7 @@ func (t *PorchSuite) TestEditPackageRevision(ctx context.Context) { }, }, } - if err := t.client.Create(ctx, editPR); err == nil { + if err := t.Client.Create(ctx, editPR); err == nil { t.Fatalf("Expected error for source revision not being published") } @@ -573,7 +555,7 @@ func (t *PorchSuite) TestEditPackageRevision(ctx context.Context) { // Check its task list var pkgRev porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: editPR.Name, }, &pkgRev) tasks := pkgRev.Spec.Tasks @@ -592,7 +574,7 @@ func (t *PorchSuite) TestUpdateResources(ctx context.Context) { workspace = "workspace" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a new package (via init) pr := &porchapi.PackageRevision{ @@ -601,7 +583,7 @@ func (t *PorchSuite) TestUpdateResources(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: packageName, @@ -614,7 +596,7 @@ func (t *PorchSuite) TestUpdateResources(ctx context.Context) { // Get the package resources var newPackage porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &newPackage) @@ -654,7 +636,7 @@ func (t *PorchSuite) TestUpdateResources(ctx context.Context) { golden := filepath.Join("testdata", "update-resources", "want-config-map.yaml") if diff := t.CompareGoldenFileYAML(golden, updated); diff != "" { - t.Errorf("Unexpected updated confg map contents: (-want,+got): %s", diff) + t.Errorf("Unexpected updated config map contents: (-want,+got): %s", diff) } } @@ -668,7 +650,7 @@ func (t *PorchSuite) TestUpdateResourcesEmptyPatch(ctx context.Context) { workspace = "workspace" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a new package (via init) pr := &porchapi.PackageRevision{ @@ -677,7 +659,7 @@ func (t *PorchSuite) TestUpdateResourcesEmptyPatch(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: packageName, @@ -690,7 +672,7 @@ func (t *PorchSuite) TestUpdateResourcesEmptyPatch(ctx context.Context) { // Check its task list var newPackage porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &newPackage) tasksBeforeUpdate := newPackage.Spec.Tasks @@ -699,7 +681,7 @@ func (t *PorchSuite) TestUpdateResourcesEmptyPatch(ctx context.Context) { // Get the package resources var newPackageResources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &newPackageResources) @@ -709,7 +691,7 @@ func (t *PorchSuite) TestUpdateResourcesEmptyPatch(ctx context.Context) { // Check the task list var newPackageUpdated porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &newPackageUpdated) tasksAfterUpdate := newPackageUpdated.Spec.Tasks @@ -722,7 +704,7 @@ func (t *PorchSuite) TestFunctionRepository(ctx context.Context) { repo := &configapi.Repository{ ObjectMeta: metav1.ObjectMeta{ Name: "function-repository", - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: configapi.RepositorySpec{ Description: "Test Function Repository", @@ -746,7 +728,7 @@ func (t *PorchSuite) TestFunctionRepository(ctx context.Context) { // Make sure the repository is ready before we test to (hopefully) // avoid flakiness. - t.waitUntilRepositoryReady(ctx, repo.Name, repo.Namespace) + t.WaitUntilRepositoryReady(ctx, repo.Name, repo.Namespace) // Wait here for the repository to be cached in porch. We wait // first one minute, since Porch waits 1 minute before it syncs @@ -758,7 +740,7 @@ func (t *PorchSuite) TestFunctionRepository(ctx context.Context) { <-time.NewTimer(2 * time.Minute).C list := &porchapi.FunctionList{} - t.ListE(ctx, list, client.InNamespace(t.namespace)) + t.ListE(ctx, list, client.InNamespace(t.Namespace)) if got := len(list.Items); got == 0 { t.Errorf("Found no functions in gcr.io/kpt-fn repository; expected at least one") @@ -766,10 +748,10 @@ func (t *PorchSuite) TestFunctionRepository(ctx context.Context) { } func (t *PorchSuite) TestPublicGitRepository(ctx context.Context) { - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "demo-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "demo-blueprints", "") var list porchapi.PackageRevisionList - t.ListE(ctx, &list, client.InNamespace(t.namespace)) + t.ListE(ctx, &list, client.InNamespace(t.Namespace)) if got := len(list.Items); got == 0 { t.Errorf("Found no package revisions in %s; expected at least one", testBlueprintsRepo) @@ -784,7 +766,7 @@ func (t *PorchSuite) TestProposeApprove(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a new package (via init) pr := &porchapi.PackageRevision{ @@ -793,7 +775,7 @@ func (t *PorchSuite) TestProposeApprove(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: packageName, @@ -811,7 +793,7 @@ func (t *PorchSuite) TestProposeApprove(ctx context.Context) { var pkg porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &pkg) @@ -821,7 +803,7 @@ func (t *PorchSuite) TestProposeApprove(ctx context.Context) { var proposed porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &proposed) @@ -831,7 +813,7 @@ func (t *PorchSuite) TestProposeApprove(ctx context.Context) { // Approve using Update should fail. proposed.Spec.Lifecycle = porchapi.PackageRevisionLifecyclePublished - if err := t.client.Update(ctx, &proposed); err == nil { + if err := t.Client.Update(ctx, &proposed); err == nil { t.Fatalf("Finalization of a package via Update unexpectedly succeeded") } @@ -857,24 +839,24 @@ func (t *PorchSuite) TestDeleteDraft(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a draft package - created := t.createPackageDraftF(ctx, repository, packageName, workspace) + created := t.CreatePackageDraftF(ctx, repository, packageName, workspace) // Check the package exists var draft porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &draft) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &draft) // Delete the package t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: created.Name, }, }) - t.mustNotExist(ctx, &draft) + t.MustNotExist(ctx, &draft) } func (t *PorchSuite) TestDeleteProposed(ctx context.Context) { @@ -886,14 +868,14 @@ func (t *PorchSuite) TestDeleteProposed(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a draft package - created := t.createPackageDraftF(ctx, repository, packageName, workspace) + created := t.CreatePackageDraftF(ctx, repository, packageName, workspace) // Check the package exists var pkg porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) // Propose the package revision to be finalized pkg.Spec.Lifecycle = porchapi.PackageRevisionLifecycleProposed @@ -902,12 +884,12 @@ func (t *PorchSuite) TestDeleteProposed(ctx context.Context) { // Delete the package t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: created.Name, }, }) - t.mustNotExist(ctx, &pkg) + t.MustNotExist(ctx, &pkg) } func (t *PorchSuite) TestDeleteFinal(ctx context.Context) { @@ -918,14 +900,14 @@ func (t *PorchSuite) TestDeleteFinal(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a draft package - created := t.createPackageDraftF(ctx, repository, packageName, workspace) + created := t.CreatePackageDraftF(ctx, repository, packageName, workspace) // Check the package exists var pkg porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) // Propose the package revision to be finalized t.Log("Proposing package") @@ -936,17 +918,17 @@ func (t *PorchSuite) TestDeleteFinal(ctx context.Context) { pkg.Spec.Lifecycle = porchapi.PackageRevisionLifecyclePublished t.UpdateApprovalF(ctx, &pkg, metav1.UpdateOptions{}) - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) // Try to delete the package. This should fail because it hasn't been proposed for deletion. t.Log("Trying to delete package (should fail)") t.DeleteL(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: created.Name, }, }) - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) // Propose deletion and then delete the package t.Log("Proposing deletion of package") @@ -956,12 +938,12 @@ func (t *PorchSuite) TestDeleteFinal(ctx context.Context) { t.Log("Deleting package") t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: created.Name, }, }) - t.mustNotExist(ctx, &pkg) + t.MustNotExist(ctx, &pkg) } func (t *PorchSuite) TestProposeDeleteAndUndo(ctx context.Context) { @@ -972,14 +954,14 @@ func (t *PorchSuite) TestProposeDeleteAndUndo(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a draft package - created := t.createPackageDraftF(ctx, repository, packageName, workspace) + created := t.CreatePackageDraftF(ctx, repository, packageName, workspace) // Check the package exists var pkg porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) // Propose the package revision to be finalized pkg.Spec.Lifecycle = porchapi.PackageRevisionLifecycleProposed @@ -987,18 +969,18 @@ func (t *PorchSuite) TestProposeDeleteAndUndo(ctx context.Context) { pkg.Spec.Lifecycle = porchapi.PackageRevisionLifecyclePublished t.UpdateApprovalF(ctx, &pkg, metav1.UpdateOptions{}) - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) - _ = t.waitUntilPackageRevisionExists(ctx, repository, packageName, "main") + _ = t.WaitUntilPackageRevisionExists(ctx, repository, packageName, "main") var list porchapi.PackageRevisionList - t.ListF(ctx, &list, client.InNamespace(t.namespace)) + t.ListF(ctx, &list, client.InNamespace(t.Namespace)) for i := range list.Items { pkgRev := list.Items[i] t.Run(fmt.Sprintf("revision %s", pkgRev.Spec.Revision), func(newT *testing.T) { // This is a bit awkward, we should find a better way to allow subtests - // with our custom implmentation of t. + // with our custom implementation of t. oldT := t.T t.T = newT defer func() { @@ -1016,11 +998,11 @@ func (t *PorchSuite) TestProposeDeleteAndUndo(ctx context.Context) { // Try to delete the package. This should fail because the lifecycle should be changed back to Published. t.DeleteL(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pkgRev.Name, }, }) - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: pkgRev.Name}, &pkgRev) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: pkgRev.Name}, &pkgRev) // Propose deletion and then delete the package pkgRev.Spec.Lifecycle = porchapi.PackageRevisionLifecycleDeletionProposed @@ -1028,12 +1010,12 @@ func (t *PorchSuite) TestProposeDeleteAndUndo(ctx context.Context) { t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pkgRev.Name, }, }) - t.mustNotExist(ctx, &pkgRev) + t.MustNotExist(ctx, &pkgRev) }) } } @@ -1047,14 +1029,14 @@ func (t *PorchSuite) TestDeleteAndRecreate(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a draft package - created := t.createPackageDraftF(ctx, repository, packageName, workspace) + created := t.CreatePackageDraftF(ctx, repository, packageName, workspace) // Check the package exists var pkg porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) t.Log("Propose the package revision to be finalized") pkg.Spec.Lifecycle = porchapi.PackageRevisionLifecycleProposed @@ -1064,9 +1046,9 @@ func (t *PorchSuite) TestDeleteAndRecreate(ctx context.Context) { pkg.Spec.Lifecycle = porchapi.PackageRevisionLifecyclePublished t.UpdateApprovalF(ctx, &pkg, metav1.UpdateOptions{}) - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) - mainPkg := t.waitUntilPackageRevisionExists(ctx, repository, packageName, "main") + mainPkg := t.WaitUntilPackageRevisionExists(ctx, repository, packageName, "main") t.Log("Propose deletion and then delete the package with revision v1") pkg.Spec.Lifecycle = porchapi.PackageRevisionLifecycleDeletionProposed @@ -1074,11 +1056,11 @@ func (t *PorchSuite) TestDeleteAndRecreate(ctx context.Context) { t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: created.Name, }, }) - t.mustNotExist(ctx, &pkg) + t.MustNotExist(ctx, &pkg) t.Log("Propose deletion and then delete the package with revision main") mainPkg.Spec.Lifecycle = porchapi.PackageRevisionLifecycleDeletionProposed @@ -1086,17 +1068,17 @@ func (t *PorchSuite) TestDeleteAndRecreate(ctx context.Context) { t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: mainPkg.Name, }, }) - t.mustNotExist(ctx, mainPkg) + t.MustNotExist(ctx, mainPkg) // Recreate the package with the same name and workspace - created = t.createPackageDraftF(ctx, repository, packageName, workspace) + created = t.CreatePackageDraftF(ctx, repository, packageName, workspace) // Check the package exists - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: created.Name}, &pkg) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: created.Name}, &pkg) // Ensure that there is only one init task in the package revision history foundInitTask := false @@ -1120,14 +1102,14 @@ func (t *PorchSuite) TestDeleteFromMain(ctx context.Context) { ) // Register the repository - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) t.Logf("Create and approve package: %s", packageNameFirst) - createdFirst := t.createPackageDraftF(ctx, repository, packageNameFirst, workspace) + createdFirst := t.CreatePackageDraftF(ctx, repository, packageNameFirst, workspace) // Check the package exists var pkgFirst porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: createdFirst.Name}, &pkgFirst) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: createdFirst.Name}, &pkgFirst) // Propose the package revision to be finalized pkgFirst.Spec.Lifecycle = porchapi.PackageRevisionLifecycleProposed @@ -1136,14 +1118,14 @@ func (t *PorchSuite) TestDeleteFromMain(ctx context.Context) { pkgFirst.Spec.Lifecycle = porchapi.PackageRevisionLifecyclePublished t.UpdateApprovalF(ctx, &pkgFirst, metav1.UpdateOptions{}) - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: createdFirst.Name}, &pkgFirst) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: createdFirst.Name}, &pkgFirst) t.Logf("Create and approve package: %s", packageNameSecond) - createdSecond := t.createPackageDraftF(ctx, repository, packageNameSecond, workspace) + createdSecond := t.CreatePackageDraftF(ctx, repository, packageNameSecond, workspace) // Check the package exists var pkgSecond porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: createdSecond.Name}, &pkgSecond) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: createdSecond.Name}, &pkgSecond) // Propose the package revision to be finalized pkgSecond.Spec.Lifecycle = porchapi.PackageRevisionLifecycleProposed @@ -1152,11 +1134,11 @@ func (t *PorchSuite) TestDeleteFromMain(ctx context.Context) { pkgSecond.Spec.Lifecycle = porchapi.PackageRevisionLifecyclePublished t.UpdateApprovalF(ctx, &pkgSecond, metav1.UpdateOptions{}) - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: createdSecond.Name}, &pkgSecond) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: createdSecond.Name}, &pkgSecond) t.Log("Wait for the 'main' revisions to get created") - firstPkgRevFromMain := t.waitUntilPackageRevisionExists(ctx, repository, packageNameFirst, "main") - secondPkgRevFromMain := t.waitUntilPackageRevisionExists(ctx, repository, packageNameSecond, "main") + firstPkgRevFromMain := t.WaitUntilPackageRevisionExists(ctx, repository, packageNameFirst, "main") + secondPkgRevFromMain := t.WaitUntilPackageRevisionExists(ctx, repository, packageNameSecond, "main") t.Log("Propose deletion of both main packages") firstPkgRevFromMain.Spec.Lifecycle = porchapi.PackageRevisionLifecycleDeletionProposed @@ -1167,7 +1149,7 @@ func (t *PorchSuite) TestDeleteFromMain(ctx context.Context) { t.Log("Delete the first package revision from main") t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: firstPkgRevFromMain.Name, }, }) @@ -1175,21 +1157,21 @@ func (t *PorchSuite) TestDeleteFromMain(ctx context.Context) { t.Log("Delete the second package revision from main") t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: secondPkgRevFromMain.Name, }, }) // Propose and delete the original package revisions (cleanup) var list porchapi.PackageRevisionList - t.ListE(ctx, &list, client.InNamespace(t.namespace)) + t.ListE(ctx, &list, client.InNamespace(t.Namespace)) for _, pkgrev := range list.Items { t.Logf("Propose deletion and delete package revision: %s", pkgrev.Name) pkgrev.Spec.Lifecycle = porchapi.PackageRevisionLifecycleDeletionProposed t.UpdateApprovalF(ctx, &pkgrev, metav1.UpdateOptions{}) t.DeleteE(ctx, &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pkgrev.Name, }, }) @@ -1204,7 +1186,7 @@ func (t *PorchSuite) TestCloneLeadingSlash(ctx context.Context) { workspace = "workspace" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Clone the package. Use leading slash in the directory (regression test) new := &porchapi.PackageRevision{ @@ -1213,7 +1195,7 @@ func (t *PorchSuite) TestCloneLeadingSlash(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.Identifier(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: packageName, @@ -1241,7 +1223,7 @@ func (t *PorchSuite) TestCloneLeadingSlash(ctx context.Context) { t.CreateF(ctx, new) var pr porchapi.PackageRevision - t.mustExist(ctx, client.ObjectKey{Namespace: t.namespace, Name: new.Name}, &pr) + t.MustExist(ctx, client.ObjectKey{Namespace: t.Namespace, Name: new.Name}, &pr) } func (t *PorchSuite) TestPackageUpdate(ctx context.Context) { @@ -1249,16 +1231,16 @@ func (t *PorchSuite) TestPackageUpdate(ctx context.Context) { gitRepository = "package-update" ) - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") var list porchapi.PackageRevisionList - t.ListE(ctx, &list, client.InNamespace(t.namespace)) + t.ListE(ctx, &list, client.InNamespace(t.Namespace)) basensV1 := MustFindPackageRevision(t.T, &list, repository.PackageRevisionKey{Repository: "test-blueprints", Package: "basens", Revision: "v1"}) basensV2 := MustFindPackageRevision(t.T, &list, repository.PackageRevisionKey{Repository: "test-blueprints", Package: "basens", Revision: "v2"}) // Register the repository as 'downstream' - t.registerMainGitRepositoryF(ctx, gitRepository) + t.RegisterMainGitRepositoryF(ctx, gitRepository) // Create PackageRevision from upstream repo pr := &porchapi.PackageRevision{ @@ -1267,7 +1249,7 @@ func (t *PorchSuite) TestPackageUpdate(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "testns", @@ -1292,7 +1274,7 @@ func (t *PorchSuite) TestPackageUpdate(ctx context.Context) { var revisionResources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &revisionResources) @@ -1306,7 +1288,7 @@ func (t *PorchSuite) TestPackageUpdate(ctx context.Context) { var newrr porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &newrr) @@ -1314,7 +1296,7 @@ func (t *PorchSuite) TestPackageUpdate(ctx context.Context) { t.Logf("PRR: %s", string(by)) t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, pr) @@ -1330,7 +1312,7 @@ func (t *PorchSuite) TestPackageUpdate(ctx context.Context) { t.UpdateE(ctx, pr, &client.UpdateOptions{}) t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &revisionResources) @@ -1344,14 +1326,14 @@ func (t *PorchSuite) TestRegisterRepository(ctx context.Context) { const ( repository = "register" ) - t.registerMainGitRepositoryF(ctx, repository, + t.RegisterMainGitRepositoryF(ctx, repository, withContent(configapi.RepositoryContentPackage), withType(configapi.RepositoryTypeGit), - withDeployment()) + WithDeployment()) var repo configapi.Repository t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: repository, }, &repo) @@ -1368,12 +1350,12 @@ func (t *PorchSuite) TestRegisterRepository(ctx context.Context) { func (t *PorchSuite) TestBuiltinFunctionEvaluator(ctx context.Context) { // Register the repository as 'git-fn' - t.registerMainGitRepositoryF(ctx, "git-builtin-fn") + t.RegisterMainGitRepositoryF(ctx, "git-builtin-fn") // Create Package Revision pr := &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "test-builtin-fn-bucket", @@ -1423,7 +1405,7 @@ func (t *PorchSuite) TestBuiltinFunctionEvaluator(ctx context.Context) { // Get package resources var resources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &resources) @@ -1446,12 +1428,12 @@ func (t *PorchSuite) TestBuiltinFunctionEvaluator(ctx context.Context) { func (t *PorchSuite) TestExecFunctionEvaluator(ctx context.Context) { // Register the repository as 'git-fn' - t.registerMainGitRepositoryF(ctx, "git-fn") + t.RegisterMainGitRepositoryF(ctx, "git-fn") // Create Package Revision pr := &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "test-fn-bucket", @@ -1500,7 +1482,7 @@ for resource in ctx.resource_list["items"]: // Get package resources var resources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &resources) @@ -1522,16 +1504,16 @@ for resource in ctx.resource_list["items"]: } func (t *PorchSuite) TestPodFunctionEvaluatorWithDistrolessImage(ctx context.Context) { - if t.local { - t.Skipf("Skipping due to not having pod evalutor in local mode") + if t.testRunnerIsLocal { + t.Skipf("Skipping due to not having pod evaluator in local mode") } - t.registerMainGitRepositoryF(ctx, "git-fn-distroless") + t.RegisterMainGitRepositoryF(ctx, "git-fn-distroless") // Create Package Revision pr := &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "test-fn-redis-bucket", @@ -1585,7 +1567,7 @@ data: // Get package resources var resources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &resources) @@ -1603,8 +1585,8 @@ data: } func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { - if t.local { - t.Skipf("Skipping due to not having pod evalutor in local mode") + if t.testRunnerIsLocal { + t.Skipf("Skipping due to not having pod evaluator in local mode") } const ( @@ -1613,12 +1595,12 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { ) // Register the repository as 'git-fn' - t.registerMainGitRepositoryF(ctx, "git-fn-pod") + t.RegisterMainGitRepositoryF(ctx, "git-fn-pod") // Create Package Revision pr := &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "test-fn-pod-hierarchy", @@ -1663,7 +1645,7 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { // Get package resources var resources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &resources) @@ -1697,7 +1679,7 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { // Create another Package Revision pr2 := &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "test-fn-pod-hierarchy", @@ -1741,7 +1723,7 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { // Get package resources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr2.Name, }, &resources) @@ -1764,16 +1746,16 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { } func (t *PorchSuite) TestPodEvaluatorWithFailure(ctx context.Context) { - if t.local { - t.Skipf("Skipping due to not having pod evalutor in local mode") + if t.testRunnerIsLocal { + t.Skipf("Skipping due to not having pod evaluator in local mode") } - t.registerMainGitRepositoryF(ctx, "git-fn-pod-failure") + t.RegisterMainGitRepositoryF(ctx, "git-fn-pod-failure") // Create Package Revision pr := &porchapi.PackageRevision{ ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "test-fn-pod-bucket", @@ -1803,7 +1785,7 @@ func (t *PorchSuite) TestPodEvaluatorWithFailure(ctx context.Context) { }, }, } - err := t.client.Create(ctx, pr) + err := t.Client.Create(ctx, pr) expectedErrMsg := "Validating arbitrary CRDs is not supported" if err == nil || !strings.Contains(err.Error(), expectedErrMsg) { t.Fatalf("expected the error to contain %q, but got %v", expectedErrMsg, err) @@ -1821,14 +1803,14 @@ func (t *PorchSuite) TestRepositoryError(ctx context.Context) { }, ObjectMeta: metav1.ObjectMeta{ Name: repositoryName, - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: configapi.RepositorySpec{ Description: "Repository With Error", Type: configapi.RepositoryTypeGit, Content: configapi.RepositoryContentPackage, Git: &configapi.GitRepository{ - // Use `incalid` domain: https://www.rfc-editor.org/rfc/rfc6761#section-6.4 + // Use `invalid` domain: https://www.rfc-editor.org/rfc/rfc6761#section-6.4 Repo: "https://repo.invalid/repository.git", }, }, @@ -1837,7 +1819,7 @@ func (t *PorchSuite) TestRepositoryError(ctx context.Context) { t.DeleteL(ctx, &configapi.Repository{ ObjectMeta: metav1.ObjectMeta{ Name: repositoryName, - Namespace: t.namespace, + Namespace: t.Namespace, }, }) }) @@ -1854,7 +1836,7 @@ func (t *PorchSuite) TestRepositoryError(ctx context.Context) { var repository configapi.Repository t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: repositoryName, }, &repository) @@ -1888,7 +1870,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { annoVal2 = "bar" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a package with labels and annotations. pr := porchapi.PackageRevision{ @@ -1897,7 +1879,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, Labels: map[string]string{ labelKey1: labelVal1, }, @@ -1921,7 +1903,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { }, } t.CreateF(ctx, &pr) - t.validateLabelsAndAnnos(ctx, pr.Name, + t.ValidateLabelsAndAnnos(ctx, pr.Name, map[string]string{ labelKey1: labelVal1, }, @@ -1941,7 +1923,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { Name: pr.Name, }, &pr) - t.validateLabelsAndAnnos(ctx, pr.Name, + t.ValidateLabelsAndAnnos(ctx, pr.Name, map[string]string{ labelKey1: labelVal1, }, @@ -1954,7 +1936,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { // Approve the package pr.Spec.Lifecycle = porchapi.PackageRevisionLifecyclePublished _ = t.UpdateApprovalF(ctx, &pr, metav1.UpdateOptions{}) - t.validateLabelsAndAnnos(ctx, pr.Name, + t.ValidateLabelsAndAnnos(ctx, pr.Name, map[string]string{ labelKey1: labelVal1, porchapi.LatestPackageRevisionKey: porchapi.LatestPackageRevisionValue, @@ -1977,7 +1959,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { delete(pr.ObjectMeta.Annotations, annoKey2) pr.Spec.Revision = "v1" t.UpdateF(ctx, &pr) - t.validateLabelsAndAnnos(ctx, pr.Name, + t.ValidateLabelsAndAnnos(ctx, pr.Name, map[string]string{ labelKey2: labelVal2, porchapi.LatestPackageRevisionKey: porchapi.LatestPackageRevisionValue, @@ -1995,7 +1977,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.Identifier(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "cloned-package", @@ -2016,7 +1998,7 @@ func (t *PorchSuite) TestNewPackageRevisionLabels(ctx context.Context) { }, } t.CreateF(ctx, &clonedPr) - t.validateLabelsAndAnnos(ctx, clonedPr.Name, + t.ValidateLabelsAndAnnos(ctx, clonedPr.Name, map[string]string{}, map[string]string{}, ) @@ -2030,10 +2012,10 @@ func (t *PorchSuite) TestRegisteredPackageRevisionLabels(ctx context.Context) { annoVal = "foo" ) - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") var list porchapi.PackageRevisionList - t.ListE(ctx, &list, client.InNamespace(t.namespace)) + t.ListE(ctx, &list, client.InNamespace(t.Namespace)) basens := MustFindPackageRevision(t.T, &list, repository.PackageRevisionKey{Repository: "test-blueprints", Package: "basens", Revision: "v1"}) if basens.ObjectMeta.Labels == nil { @@ -2046,7 +2028,7 @@ func (t *PorchSuite) TestRegisteredPackageRevisionLabels(ctx context.Context) { basens.ObjectMeta.Annotations[annoKey] = annoVal t.UpdateF(ctx, basens) - t.validateLabelsAndAnnos(ctx, basens.Name, + t.ValidateLabelsAndAnnos(ctx, basens.Name, map[string]string{ labelKey: labelVal, }, @@ -2064,7 +2046,7 @@ func (t *PorchSuite) TestPackageRevisionGCWithOwner(ctx context.Context) { cmName = "foo" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) // Create a new package (via init) pr := &porchapi.PackageRevision{ @@ -2073,7 +2055,7 @@ func (t *PorchSuite) TestPackageRevisionGCWithOwner(ctx context.Context) { APIVersion: packageRevisionGVK.GroupVersion().String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "empty-package", @@ -2090,7 +2072,7 @@ func (t *PorchSuite) TestPackageRevisionGCWithOwner(ctx context.Context) { }, ObjectMeta: metav1.ObjectMeta{ Name: cmName, - Namespace: t.namespace, + Namespace: t.Namespace, OwnerReferences: []metav1.OwnerReference{ { APIVersion: porchapi.SchemeGroupVersion.String(), @@ -2107,7 +2089,7 @@ func (t *PorchSuite) TestPackageRevisionGCWithOwner(ctx context.Context) { t.CreateF(ctx, cm) t.DeleteF(ctx, pr) - t.waitUntilObjectDeleted( + t.WaitUntilObjectDeleted( ctx, packageRevisionGVK, types.NamespacedName{ @@ -2116,7 +2098,7 @@ func (t *PorchSuite) TestPackageRevisionGCWithOwner(ctx context.Context) { }, 10*time.Second, ) - t.waitUntilObjectDeleted( + t.WaitUntilObjectDeleted( ctx, configMapGVK, types.NamespacedName{ @@ -2135,7 +2117,7 @@ func (t *PorchSuite) TestPackageRevisionGCAsOwner(ctx context.Context) { cmName = "foo" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) cm := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ @@ -2144,7 +2126,7 @@ func (t *PorchSuite) TestPackageRevisionGCAsOwner(ctx context.Context) { }, ObjectMeta: metav1.ObjectMeta{ Name: cmName, - Namespace: t.namespace, + Namespace: t.Namespace, }, Data: map[string]string{ "foo": "bar", @@ -2158,7 +2140,7 @@ func (t *PorchSuite) TestPackageRevisionGCAsOwner(ctx context.Context) { APIVersion: packageRevisionGVK.GroupVersion().String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, OwnerReferences: []metav1.OwnerReference{ { APIVersion: "v1", @@ -2177,7 +2159,7 @@ func (t *PorchSuite) TestPackageRevisionGCAsOwner(ctx context.Context) { t.CreateF(ctx, pr) t.DeleteF(ctx, cm) - t.waitUntilObjectDeleted( + t.WaitUntilObjectDeleted( ctx, configMapGVK, types.NamespacedName{ @@ -2186,7 +2168,7 @@ func (t *PorchSuite) TestPackageRevisionGCAsOwner(ctx context.Context) { }, 10*time.Second, ) - t.waitUntilObjectDeleted( + t.WaitUntilObjectDeleted( ctx, packageRevisionGVK, types.NamespacedName{ @@ -2205,7 +2187,7 @@ func (t *PorchSuite) TestPackageRevisionOwnerReferences(ctx context.Context) { cmName = "foo" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) cm := &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ @@ -2214,7 +2196,7 @@ func (t *PorchSuite) TestPackageRevisionOwnerReferences(ctx context.Context) { }, ObjectMeta: metav1.ObjectMeta{ Name: cmName, - Namespace: t.namespace, + Namespace: t.Namespace, }, Data: map[string]string{ "foo": "bar", @@ -2228,7 +2210,7 @@ func (t *PorchSuite) TestPackageRevisionOwnerReferences(ctx context.Context) { APIVersion: packageRevisionGVK.GroupVersion().String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "empty-package", @@ -2237,7 +2219,7 @@ func (t *PorchSuite) TestPackageRevisionOwnerReferences(ctx context.Context) { }, } t.CreateF(ctx, pr) - t.validateOwnerReferences(ctx, pr.Name, []metav1.OwnerReference{}) + t.ValidateOwnerReferences(ctx, pr.Name, []metav1.OwnerReference{}) ownerRef := metav1.OwnerReference{ APIVersion: "v1", @@ -2247,11 +2229,11 @@ func (t *PorchSuite) TestPackageRevisionOwnerReferences(ctx context.Context) { } pr.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ownerRef} t.UpdateF(ctx, pr) - t.validateOwnerReferences(ctx, pr.Name, []metav1.OwnerReference{ownerRef}) + t.ValidateOwnerReferences(ctx, pr.Name, []metav1.OwnerReference{ownerRef}) pr.ObjectMeta.OwnerReferences = []metav1.OwnerReference{} t.UpdateF(ctx, pr) - t.validateOwnerReferences(ctx, pr.Name, []metav1.OwnerReference{}) + t.ValidateOwnerReferences(ctx, pr.Name, []metav1.OwnerReference{}) } func (t *PorchSuite) TestPackageRevisionFinalizers(ctx context.Context) { @@ -2261,7 +2243,7 @@ func (t *PorchSuite) TestPackageRevisionFinalizers(ctx context.Context) { description = "empty-package description" ) - t.registerMainGitRepositoryF(ctx, repository) + t.RegisterMainGitRepositoryF(ctx, repository) pr := &porchapi.PackageRevision{ TypeMeta: metav1.TypeMeta{ @@ -2269,7 +2251,7 @@ func (t *PorchSuite) TestPackageRevisionFinalizers(ctx context.Context) { APIVersion: packageRevisionGVK.GroupVersion().String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "empty-package", @@ -2278,18 +2260,18 @@ func (t *PorchSuite) TestPackageRevisionFinalizers(ctx context.Context) { }, } t.CreateF(ctx, pr) - t.validateFinalizers(ctx, pr.Name, []string{}) + t.ValidateFinalizers(ctx, pr.Name, []string{}) pr.Finalizers = append(pr.Finalizers, "foo-finalizer") t.UpdateF(ctx, pr) - t.validateFinalizers(ctx, pr.Name, []string{"foo-finalizer"}) + t.ValidateFinalizers(ctx, pr.Name, []string{"foo-finalizer"}) t.DeleteF(ctx, pr) - t.validateFinalizers(ctx, pr.Name, []string{"foo-finalizer"}) + t.ValidateFinalizers(ctx, pr.Name, []string{"foo-finalizer"}) pr.Finalizers = []string{} t.UpdateF(ctx, pr) - t.waitUntilObjectDeleted(ctx, packageRevisionGVK, types.NamespacedName{ + t.WaitUntilObjectDeleted(ctx, packageRevisionGVK, types.NamespacedName{ Name: pr.Name, Namespace: pr.Namespace, }, 10*time.Second) @@ -2299,7 +2281,7 @@ func (t *PorchSuite) TestPackageRevisionInMultipleNamespaces(ctx context.Context registerRepoAndTestRevisions := func(repoName string, ns string, oldPRs []porchapi.PackageRevision) []porchapi.PackageRevision { - t.registerGitRepositoryF(ctx, testBlueprintsRepo, repoName, "", inNamespace(ns)) + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, repoName, "", InNamespace(ns)) prList := porchapi.PackageRevisionList{} t.ListF(ctx, &prList, client.InNamespace(ns)) newPRs := prList.Items @@ -2346,7 +2328,7 @@ func (t *PorchSuite) TestPackageRevisionInMultipleNamespaces(ctx context.Context APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ - Name: t.namespace + "-2", + Name: t.Namespace + "-2", }, } t.CreateF(ctx, ns2) @@ -2354,7 +2336,7 @@ func (t *PorchSuite) TestPackageRevisionInMultipleNamespaces(ctx context.Context t.DeleteE(ctx, ns2) }) - prs1 := registerRepoAndTestRevisions("test-blueprints", t.namespace, nil) + prs1 := registerRepoAndTestRevisions("test-blueprints", t.Namespace, nil) nPRs := len(prs1) t.Logf("Number of PRs in repo: %v", nPRs) @@ -2363,7 +2345,7 @@ func (t *PorchSuite) TestPackageRevisionInMultipleNamespaces(ctx context.Context t.Errorf("number of PackageRevisions in namespace %s: want %v, got %d", ns2.Name, nPRs, len(prs2)) } - prs3 := registerRepoAndTestRevisions("test-3-blueprints", t.namespace, prs1) + prs3 := registerRepoAndTestRevisions("test-3-blueprints", t.Namespace, prs1) if len(prs3) != nPRs { t.Errorf("number of PackageRevisions in repo-3: want %v, got %d", nPRs, len(prs2)) } @@ -2371,11 +2353,11 @@ func (t *PorchSuite) TestPackageRevisionInMultipleNamespaces(ctx context.Context func (t *PorchSuite) TestUniquenessOfUIDs(ctx context.Context) { - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-2-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "test-2-blueprints", "") prList := porchapi.PackageRevisionList{} - t.ListE(ctx, &prList, client.InNamespace(t.namespace)) + t.ListE(ctx, &prList, client.InNamespace(t.Namespace)) uids := make(map[types.UID]*porchapi.PackageRevision) for _, pr := range prList.Items { @@ -2387,14 +2369,39 @@ func (t *PorchSuite) TestUniquenessOfUIDs(ctx context.Context) { } } -func (t *PorchSuite) TestPackageRevisionFieldselectors(ctx context.Context) { - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") +func (t *PorchSuite) TestPackageRevisionFieldSelectors(ctx context.Context) { + repo := "test-blueprints" + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, repo, "") prList := porchapi.PackageRevisionList{} + pkgName := "basens" + pkgSelector := client.MatchingFields(fields.Set{"spec.packageName": pkgName}) + t.ListE(ctx, &prList, client.InNamespace(t.Namespace), pkgSelector) + if len(prList.Items) == 0 { + t.Errorf("Expected at least one PackageRevision with packageName=%q, but got none", pkgName) + } + for _, pr := range prList.Items { + if pr.Spec.PackageName != pkgName { + t.Errorf("PackageRevision %s packageName: want %q, but got %q", pr.Name, pkgName, pr.Spec.PackageName) + } + } + + revName := "v1" + revSelector := client.MatchingFields(fields.Set{"spec.revision": revName}) + t.ListE(ctx, &prList, client.InNamespace(t.Namespace), revSelector) + if len(prList.Items) == 0 { + t.Errorf("Expected at least one PackageRevision with revision=%q, but got none", revName) + } + for _, pr := range prList.Items { + if pr.Spec.Revision != revName { + t.Errorf("PackageRevision %s revision: want %q, but got %q", pr.Name, pkgName, pr.Spec.PackageName) + } + } + wsName := "v1" wsSelector := client.MatchingFields(fields.Set{"spec.workspaceName": wsName}) - t.ListE(ctx, &prList, client.InNamespace(t.namespace), wsSelector) + t.ListE(ctx, &prList, client.InNamespace(t.Namespace), wsSelector) if len(prList.Items) == 0 { t.Errorf("Expected at least one PackageRevision with workspaceName=%q, but got none", wsName) } @@ -2405,7 +2412,7 @@ func (t *PorchSuite) TestPackageRevisionFieldselectors(ctx context.Context) { } publishedSelector := client.MatchingFields(fields.Set{"spec.lifecycle": string(porchapi.PackageRevisionLifecyclePublished)}) - t.ListE(ctx, &prList, client.InNamespace(t.namespace), publishedSelector) + t.ListE(ctx, &prList, client.InNamespace(t.Namespace), publishedSelector) if len(prList.Items) == 0 { t.Errorf("Expected at least one PackageRevision with lifecycle=%q, but got none", porchapi.PackageRevisionLifecyclePublished) } @@ -2416,7 +2423,7 @@ func (t *PorchSuite) TestPackageRevisionFieldselectors(ctx context.Context) { } draftSelector := client.MatchingFields(fields.Set{"spec.lifecycle": string(porchapi.PackageRevisionLifecycleDraft)}) - t.ListE(ctx, &prList, client.InNamespace(t.namespace), draftSelector) + t.ListE(ctx, &prList, client.InNamespace(t.Namespace), draftSelector) // TODO: add draft packages to the test repo // if len(prList.Items) == 0 { // t.Errorf("Expected at least one PackageRevision with lifecycle=%q, but got none", porchapi.PackageRevisionLifecycleDraft) @@ -2426,4 +2433,16 @@ func (t *PorchSuite) TestPackageRevisionFieldselectors(ctx context.Context) { t.Errorf("PackageRevision %s lifecycle: want %q, but got %q", pr.Name, porchapi.PackageRevisionLifecycleDraft, pr.Spec.Lifecycle) } } + + // test combined selectors + combinedSelector := client.MatchingFields(fields.Set{"spec.revision": revName, "spec.packageName": pkgName}) + t.ListE(ctx, &prList, client.InNamespace(t.Namespace), combinedSelector) + if len(prList.Items) == 0 { + t.Errorf("Expected at least one PackageRevision with packageName=%q and revision=%q, but got none", pkgName, revName) + } + for _, pr := range prList.Items { + if pr.Spec.PackageName != pkgName || pr.Spec.Revision != revName { + t.Errorf("PackageRevision %s: want %v/%v, but got %v/%v", pr.Name, pkgName, revName, pr.Spec.PackageName, pr.Spec.Revision) + } + } } diff --git a/test/e2e/e2e_utils_test.go b/test/e2e/suit_utils.go similarity index 64% rename from test/e2e/e2e_utils_test.go rename to test/e2e/suit_utils.go index 7481afe4..801325a0 100644 --- a/test/e2e/e2e_utils_test.go +++ b/test/e2e/suit_utils.go @@ -28,42 +28,44 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" ) -type PorchSuite struct { +type TestSuiteWithGit struct { TestSuite gitConfig GitConfig } -var _ Initializer = &PorchSuite{} +var _ Initializer = &TestSuiteWithGit{} +var _ TSetter = &TestSuiteWithGit{} -func (p *PorchSuite) Initialize(ctx context.Context) { +func (p *TestSuiteWithGit) Initialize(ctx context.Context) { p.TestSuite.Initialize(ctx) p.gitConfig = p.CreateGitRepo() } -func (p *PorchSuite) GitConfig(repoID string) GitConfig { +func (p *TestSuiteWithGit) GitConfig(repoID string) GitConfig { config := p.gitConfig config.Repo = config.Repo + "/" + repoID return config } -func (t *PorchSuite) registerMainGitRepositoryF(ctx context.Context, name string, opts ...repositoryOption) { +func (t *TestSuiteWithGit) RegisterMainGitRepositoryF(ctx context.Context, name string, opts ...RepositoryOption) { t.Helper() - repoID := t.namespace + "-" + name + repoID := t.Namespace + "-" + name config := t.GitConfig(repoID) t.registerGitRepositoryFromConfigF(ctx, name, config, opts...) } -func (t *TestSuite) validateFinalizers(ctx context.Context, name string, finalizers []string) { +func (t *TestSuite) ValidateFinalizers(ctx context.Context, name string, finalizers []string) { t.Helper() var pr porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: name, }, &pr) @@ -85,11 +87,11 @@ func (t *TestSuite) validateFinalizers(ctx context.Context, name string, finaliz } } -func (t *TestSuite) validateOwnerReferences(ctx context.Context, name string, ownerRefs []metav1.OwnerReference) { +func (t *TestSuite) ValidateOwnerReferences(ctx context.Context, name string, ownerRefs []metav1.OwnerReference) { t.Helper() var pr porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: name, }, &pr) @@ -111,11 +113,11 @@ func (t *TestSuite) validateOwnerReferences(ctx context.Context, name string, ow } } -func (t *TestSuite) validateLabelsAndAnnos(ctx context.Context, name string, labels, annos map[string]string) { +func (t *TestSuite) ValidateLabelsAndAnnos(ctx context.Context, name string, labels, annos map[string]string) { t.Helper() var pr porchapi.PackageRevision t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: name, }, &pr) @@ -136,7 +138,7 @@ func (t *TestSuite) validateLabelsAndAnnos(ctx context.Context, name string, lab } } -func (t *TestSuite) registerGitRepositoryF(ctx context.Context, repo, name, directory string, opts ...repositoryOption) { +func (t *TestSuite) RegisterGitRepositoryF(ctx context.Context, repo, name, directory string, opts ...RepositoryOption) { t.Helper() config := GitConfig{ Repo: repo, @@ -146,7 +148,7 @@ func (t *TestSuite) registerGitRepositoryF(ctx context.Context, repo, name, dire t.registerGitRepositoryFromConfigF(ctx, name, config, opts...) } -func (t *TestSuite) registerGitRepositoryFromConfigF(ctx context.Context, name string, config GitConfig, opts ...repositoryOption) { +func (t *TestSuite) registerGitRepositoryFromConfigF(ctx context.Context, name string, config GitConfig, opts ...RepositoryOption) { t.Helper() var secret string // Create auth secret if necessary @@ -156,7 +158,7 @@ func (t *TestSuite) registerGitRepositoryFromConfigF(ctx context.Context, name s t.CreateF(ctx, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secret, - Namespace: t.namespace, + Namespace: t.Namespace, }, Immutable: &immutable, Data: map[string][]byte{ @@ -170,7 +172,7 @@ func (t *TestSuite) registerGitRepositoryFromConfigF(ctx context.Context, name s t.DeleteE(ctx, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secret, - Namespace: t.namespace, + Namespace: t.Namespace, }, }) }) @@ -183,7 +185,7 @@ func (t *TestSuite) registerGitRepositoryFromConfigF(ctx context.Context, name s }, ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: configapi.RepositorySpec{ Description: "Porch Test Repository Description", @@ -209,45 +211,45 @@ func (t *TestSuite) registerGitRepositoryFromConfigF(ctx context.Context, name s t.CreateF(ctx, repository) t.Cleanup(func() { - t.Helper() t.DeleteE(ctx, repository) - t.waitUntilRepositoryDeleted(ctx, name, t.namespace) - t.waitUntilAllPackagesDeleted(ctx, name) + t.WaitUntilRepositoryDeleted(ctx, name, t.Namespace) + t.WaitUntilAllPackagesDeleted(ctx, name) }) // Make sure the repository is ready before we test to (hopefully) // avoid flakiness. - t.waitUntilRepositoryReady(ctx, repository.Name, repository.Namespace) + t.WaitUntilRepositoryReady(ctx, repository.Name, repository.Namespace) + t.Logf("Repository %s/%s is ready", repository.Namespace, repository.Name) } -type repositoryOption func(*configapi.Repository) +type RepositoryOption func(*configapi.Repository) -func withDeployment() repositoryOption { +func WithDeployment() RepositoryOption { return func(r *configapi.Repository) { r.Spec.Deployment = true } } -func withType(t configapi.RepositoryType) repositoryOption { +func withType(t configapi.RepositoryType) RepositoryOption { return func(r *configapi.Repository) { r.Spec.Type = t } } -func withContent(content configapi.RepositoryContent) repositoryOption { +func withContent(content configapi.RepositoryContent) RepositoryOption { return func(r *configapi.Repository) { r.Spec.Content = content } } -func inNamespace(ns string) repositoryOption { +func InNamespace(ns string) RepositoryOption { return func(repo *configapi.Repository) { repo.Namespace = ns } } // Creates an empty package draft by initializing an empty package -func (t *TestSuite) createPackageDraftF(ctx context.Context, repository, name, workspace string) *porchapi.PackageRevision { +func (t *TestSuite) CreatePackageDraftF(ctx context.Context, repository, name, workspace string) *porchapi.PackageRevision { t.Helper() pr := &porchapi.PackageRevision{ TypeMeta: metav1.TypeMeta{ @@ -255,7 +257,7 @@ func (t *TestSuite) createPackageDraftF(ctx context.Context, repository, name, w APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: name, @@ -273,7 +275,7 @@ func (t *TestSuite) createPackageDraftF(ctx context.Context, repository, name, w return pr } -func (t *TestSuite) mustExist(ctx context.Context, key client.ObjectKey, obj client.Object) { +func (t *TestSuite) MustExist(ctx context.Context, key client.ObjectKey, obj client.Object) { t.Helper() t.Logf("Checking existence of %q...", key) t.GetF(ctx, key, obj) @@ -285,9 +287,9 @@ func (t *TestSuite) mustExist(ctx context.Context, key client.ObjectKey, obj cli } } -func (t *TestSuite) mustNotExist(ctx context.Context, obj client.Object) { +func (t *TestSuite) MustNotExist(ctx context.Context, obj client.Object) { t.Helper() - switch err := t.client.Get(ctx, client.ObjectKeyFromObject(obj), obj); { + switch err := t.Client.Get(ctx, client.ObjectKeyFromObject(obj), obj); { case err == nil: t.Errorf("No error returned getting a deleted package; expected error") case !apierrors.IsNotFound(err): @@ -295,21 +297,21 @@ func (t *TestSuite) mustNotExist(ctx context.Context, obj client.Object) { } } -// waitUntilRepositoryReady waits for up to 60 seconds for the repository with the +// WaitUntilRepositoryReady waits for up to 60 seconds for the repository with the // provided name and namespace is ready, i.e. the Ready condition is true. // It also queries for Functions and PackageRevisions, to ensure these are also // ready - this is an artifact of the way we've implemented the aggregated apiserver, // where the first fetch can sometimes be synchronous. -func (t *TestSuite) waitUntilRepositoryReady(ctx context.Context, name, namespace string) { +func (t *TestSuite) WaitUntilRepositoryReady(ctx context.Context, name, namespace string) { t.Helper() nn := types.NamespacedName{ Name: name, Namespace: namespace, } var innerErr error - err := wait.PollUntilContextTimeout(ctx, time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) { + err := wait.PollUntilContextTimeout(ctx, time.Second, 120*time.Second, true, func(ctx context.Context) (bool, error) { var repo configapi.Repository - if err := t.client.Get(ctx, nn, &repo); err != nil { + if err := t.Client.Get(ctx, nn, &repo); err != nil { innerErr = err return false, nil } @@ -332,7 +334,7 @@ func (t *TestSuite) waitUntilRepositoryReady(ctx context.Context, name, namespac // While we're using an aggregated apiserver, make sure we can query the generated objects if err := wait.PollUntilContextTimeout(ctx, time.Second, 10*time.Second, true, func(ctx context.Context) (bool, error) { var revisions porchapi.PackageRevisionList - if err := t.client.List(ctx, &revisions, client.InNamespace(nn.Namespace)); err != nil { + if err := t.Client.List(ctx, &revisions, client.InNamespace(nn.Namespace)); err != nil { innerErr = err return false, nil } @@ -342,7 +344,7 @@ func (t *TestSuite) waitUntilRepositoryReady(ctx context.Context, name, namespac } } -func (t *TestSuite) waitUntilRepositoryDeleted(ctx context.Context, name, namespace string) { +func (t *TestSuite) WaitUntilRepositoryDeleted(ctx context.Context, name, namespace string) { t.Helper() err := wait.PollUntilContextTimeout(ctx, time.Second, 20*time.Second, true, func(ctx context.Context) (done bool, err error) { var repo configapi.Repository @@ -350,7 +352,7 @@ func (t *TestSuite) waitUntilRepositoryDeleted(ctx context.Context, name, namesp Name: name, Namespace: namespace, } - if err := t.client.Get(ctx, nn, &repo); err != nil { + if err := t.Client.Get(ctx, nn, &repo); err != nil { if apierrors.IsNotFound(err) { return true, nil } @@ -363,12 +365,12 @@ func (t *TestSuite) waitUntilRepositoryDeleted(ctx context.Context, name, namesp } } -func (t *TestSuite) waitUntilAllPackagesDeleted(ctx context.Context, repoName string) { +func (t *TestSuite) WaitUntilAllPackagesDeleted(ctx context.Context, repoName string) { t.Helper() err := wait.PollUntilContextTimeout(ctx, time.Second, 60*time.Second, true, func(ctx context.Context) (done bool, err error) { t.Helper() var pkgRevList porchapi.PackageRevisionList - if err := t.client.List(ctx, &pkgRevList); err != nil { + if err := t.Client.List(ctx, &pkgRevList); err != nil { t.Logf("error listing packages: %v", err) return false, nil } @@ -380,7 +382,7 @@ func (t *TestSuite) waitUntilAllPackagesDeleted(ctx context.Context, repoName st } var internalPkgRevList internalapi.PackageRevList - if err := t.client.List(ctx, &internalPkgRevList); err != nil { + if err := t.Client.List(ctx, &internalPkgRevList); err != nil { t.Logf("error list internal packages: %v", err) return false, nil } @@ -397,13 +399,13 @@ func (t *TestSuite) waitUntilAllPackagesDeleted(ctx context.Context, repoName st } } -func (t *TestSuite) waitUntilObjectDeleted(ctx context.Context, gvk schema.GroupVersionKind, namespacedName types.NamespacedName, d time.Duration) { +func (t *TestSuite) WaitUntilObjectDeleted(ctx context.Context, gvk schema.GroupVersionKind, namespacedName types.NamespacedName, d time.Duration) { t.Helper() var innerErr error err := wait.PollUntilContextTimeout(ctx, time.Second, d, true, func(ctx context.Context) (bool, error) { var u unstructured.Unstructured u.SetGroupVersionKind(gvk) - if err := t.client.Get(ctx, namespacedName, &u); err != nil { + if err := t.Client.Get(ctx, namespacedName, &u); err != nil { if apierrors.IsNotFound(err) { return true, nil } @@ -417,29 +419,106 @@ func (t *TestSuite) waitUntilObjectDeleted(ctx context.Context, gvk schema.Group } } -func (t *TestSuite) waitUntilPackageRevisionExists(ctx context.Context, repository string, pkgName string, revision string) *porchapi.PackageRevision { +func (t *TestSuite) WaitUntilPackageRevisionFulfillingConditionExists( + ctx context.Context, + timeout time.Duration, + condition func(porchapi.PackageRevision) bool, +) (*porchapi.PackageRevision, error) { + t.Helper() var foundPkgRev *porchapi.PackageRevision - timeout := 120 * time.Second err := wait.PollUntilContextTimeout(ctx, time.Second, timeout, true, func(ctx context.Context) (done bool, err error) { var pkgRevList porchapi.PackageRevisionList - if err := t.client.List(ctx, &pkgRevList); err != nil { + if err := t.Client.List(ctx, &pkgRevList); err != nil { t.Logf("error listing packages: %v", err) return false, nil } for _, pkgRev := range pkgRevList.Items { - if pkgRev.Spec.RepositoryName == repository && - pkgRev.Spec.PackageName == pkgName && - pkgRev.Spec.Revision == revision { - + if condition(pkgRev) { foundPkgRev = &pkgRev return true, nil } } return false, nil }) + return foundPkgRev, err +} + +func (t *TestSuite) WaitUntilPackageRevisionExists(ctx context.Context, repository string, pkgName string, revision string) *porchapi.PackageRevision { + t.Helper() + t.Logf("Waiting for package revision (%v/%v/%v) to exist", repository, pkgName, revision) + timeout := 120 * time.Second + foundPkgRev, err := t.WaitUntilPackageRevisionFulfillingConditionExists(ctx, timeout, func(pkgRev porchapi.PackageRevision) bool { + return pkgRev.Spec.RepositoryName == repository && + pkgRev.Spec.PackageName == pkgName && + pkgRev.Spec.Revision == revision + }) if err != nil { t.Fatalf("Package revision (%v/%v/%v) not found in time (%v)", repository, pkgName, revision, timeout) } return foundPkgRev } + +func (t *TestSuite) WaitUntilDraftPackageRevisionExists(ctx context.Context, repository string, pkgName string) *porchapi.PackageRevision { + t.Helper() + t.Logf("Waiting for a draft revision for package %v/%v to exist", repository, pkgName) + timeout := 120 * time.Second + foundPkgRev, err := t.WaitUntilPackageRevisionFulfillingConditionExists(ctx, timeout, func(pkgRev porchapi.PackageRevision) bool { + return pkgRev.Spec.RepositoryName == repository && + pkgRev.Spec.PackageName == pkgName && + pkgRev.Spec.Lifecycle == porchapi.PackageRevisionLifecycleDraft + }) + if err != nil { + t.Fatalf("No draft package revision found for package %v/%v in time (%v)", repository, pkgName, timeout) + } + return foundPkgRev +} + +func (t *TestSuite) WaitUntilPackageRevisionResourcesExists( + ctx context.Context, + key types.NamespacedName, +) *porchapi.PackageRevisionResources { + + t.Helper() + t.Logf("Waiting for PackageRevisionResources object %v to exist", key) + timeout := 120 * time.Second + var foundPrr *porchapi.PackageRevisionResources + err := wait.PollUntilContextTimeout(ctx, time.Second, timeout, true, func(ctx context.Context) (done bool, err error) { + var prrList porchapi.PackageRevisionResourcesList + if err := t.Client.List(ctx, &prrList); err != nil { + t.Logf("error listing package revision resources: %v", err) + return false, nil + } + for _, prr := range prrList.Items { + if client.ObjectKeyFromObject(&prr) == key { + foundPrr = &prr + return true, nil + } + } + return false, nil + }) + if err != nil { + t.Fatalf("PackageRevisionResources object wasn't found for package %v in time (%v)", key, timeout) + } + return foundPrr +} + +func (t *TestSuite) GetContentsOfPackageRevision(ctx context.Context, repository string, pkgName string, revision string) map[string]string { + + t.Helper() + var prrList porchapi.PackageRevisionResourcesList + selector := client.MatchingFields(fields.Set{ + "spec.repository": repository, + "spec.packageName": pkgName, + "spec.revision": revision, + }) + t.ListF(ctx, &prrList, selector, client.InNamespace(t.Namespace)) + + if len(prrList.Items) == 0 { + t.Fatalf("PackageRevisionResources object wasn't found for package revision %v/%v/%v", repository, pkgName, revision) + } + if len(prrList.Items) > 1 { + t.Fatalf("Multiple PackageRevisionResources objects were found for package revision %v/%v/%v", repository, pkgName, revision) + } + return prrList.Items[0].Spec.Resources +} diff --git a/test/e2e/suite.go b/test/e2e/suite.go index 6a37e930..393cab77 100644 --- a/test/e2e/suite.go +++ b/test/e2e/suite.go @@ -22,6 +22,7 @@ import ( "net" "net/http" "os" + "reflect" "strings" "sync" "testing" @@ -80,15 +81,15 @@ func (p Password) String() string { type TestSuite struct { *testing.T kubeconfig *rest.Config - client client.Client + Client client.Client // Strongly-typed client handy for reading e.g. pod logs kubeClient kubernetes.Interface clientset porchclient.Interface - namespace string // K8s namespace for this test run - local bool // Tests running against local dev porch + Namespace string // K8s namespace for this test run + testRunnerIsLocal bool // Tests running against local dev porch } type Initializer interface { @@ -97,6 +98,39 @@ type Initializer interface { var _ Initializer = &TestSuite{} +type TSetter interface { + SetT(tt *testing.T) +} + +var _ TSetter = &TestSuite{} + +// RunSuite runs a test suite by calling each Test* method in the suite. +// Before each Test* method, it sets the T field of the suite to the current test. +// Before running the suite, it initializes the suite by calling Initialize. +func RunSuite(suite interface{}, t *testing.T) { + suiteType := reflect.TypeOf(suite) + ctx := context.Background() + + t.Run(suiteType.Elem().Name(), func(t *testing.T) { + suite.(TSetter).SetT(t) // panic if SetT() is not implemented + suite.(Initializer).Initialize(ctx) // panic if Initialize() is not implemented + + for i, max := 0, suiteType.NumMethod(); i < max; i++ { + method := suiteType.Method(i) + if strings.HasPrefix(method.Name, "Test") { + t.Run(method.Name, func(t *testing.T) { + suite.(TSetter).SetT(t) + method.Func.Call([]reflect.Value{reflect.ValueOf(suite), reflect.ValueOf(ctx)}) + }) + } + } + }) +} + +func (t *TestSuite) SetT(tt *testing.T) { + t.T = tt +} + func (t *TestSuite) Initialize(ctx context.Context) { cfg, err := config.GetConfig() if err != nil { @@ -114,7 +148,7 @@ func (t *TestSuite) Initialize(ctx context.Context) { }); err != nil { t.Fatalf("Failed to initialize k8s client (%s): %v", cfg.Host, err) } else { - t.client = c + t.Client = c t.kubeconfig = cfg } @@ -130,7 +164,7 @@ func (t *TestSuite) Initialize(ctx context.Context) { t.clientset = cs } - t.local = !t.IsTestRunnerInCluster() + t.testRunnerIsLocal = !t.IsTestRunnerInCluster() namespace := fmt.Sprintf("porch-test-%d", time.Now().UnixMicro()) t.CreateF(ctx, &coreapi.Namespace{ @@ -139,8 +173,8 @@ func (t *TestSuite) Initialize(ctx context.Context) { }, }) - t.namespace = namespace - c := t.client + t.Namespace = namespace + c := t.Client t.Cleanup(func() { if err := c.Delete(ctx, &coreapi.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -176,7 +210,7 @@ func (t *TestSuite) IsTestRunnerInCluster() bool { Name: "v1alpha1.porch.kpt.dev", }, &porch) service := coreapi.Service{} - err := t.client.Get(ctx, client.ObjectKey{ + err := t.Client.Get(ctx, client.ObjectKey{ Namespace: porch.Spec.Service.Namespace, Name: "function-runner", }, &service) @@ -222,14 +256,14 @@ type ErrorHandler func(format string, args ...interface{}) func (t *TestSuite) get(ctx context.Context, key client.ObjectKey, obj client.Object, eh ErrorHandler) { t.Helper() - if err := t.client.Get(ctx, key, obj); err != nil { + if err := t.Client.Get(ctx, key, obj); err != nil { eh("failed to get resource %T %s: %v", obj, key, err) } } func (t *TestSuite) list(ctx context.Context, list client.ObjectList, opts []client.ListOption, eh ErrorHandler) { t.Helper() - if err := t.client.List(ctx, list, opts...); err != nil { + if err := t.Client.List(ctx, list, opts...); err != nil { eh("failed to list resources %T %+v: %v", list, list, err) } } @@ -261,7 +295,7 @@ func (t *TestSuite) create(ctx context.Context, obj client.Object, opts []client t.Logf("took %v to create %s/%s", time.Since(start), obj.GetNamespace(), obj.GetName()) }() - if err := t.client.Create(ctx, obj, opts...); err != nil { + if err := t.Client.Create(ctx, obj, opts...); err != nil { eh("failed to create resource %s: %v", DebugFormat(obj), err) } } @@ -270,7 +304,7 @@ func (t *TestSuite) delete(ctx context.Context, obj client.Object, opts []client t.Helper() t.Logf("deleting object %v", DebugFormat(obj)) - if err := t.client.Delete(ctx, obj, opts...); err != nil { + if err := t.Client.Delete(ctx, obj, opts...); err != nil { eh("failed to delete resource %s: %v", DebugFormat(obj), err) } } @@ -279,7 +313,7 @@ func (t *TestSuite) update(ctx context.Context, obj client.Object, opts []client t.Helper() t.Logf("updating object %v", DebugFormat(obj)) - if err := t.client.Update(ctx, obj, opts...); err != nil { + if err := t.Client.Update(ctx, obj, opts...); err != nil { eh("failed to update resource %s: %v", DebugFormat(obj), err) } } @@ -288,7 +322,7 @@ func (t *TestSuite) patch(ctx context.Context, obj client.Object, patch client.P t.Helper() t.Logf("patching object %v", DebugFormat(obj)) - if err := t.client.Patch(ctx, obj, patch, opts...); err != nil { + if err := t.Client.Patch(ctx, obj, patch, opts...); err != nil { eh("failed to patch resource %s: %v", DebugFormat(obj), err) } } @@ -511,7 +545,7 @@ func (t *TestSuite) createInClusterGitServer(ctx context.Context, exposeByLoadBa gitImage := porchtest.InferGitServerImage(porch.Spec.Template.Spec.Containers[0].Image) var deploymentKey = client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: "git-server", } var replicas int32 = 1 @@ -569,7 +603,7 @@ func (t *TestSuite) createInClusterGitServer(ctx context.Context, exposeByLoadBa }) serviceKey := client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: "git-server-service", } service := coreapi.Service{ @@ -671,14 +705,14 @@ func (t *TestSuite) createInClusterGitServer(ctx context.Context, exposeByLoadBa } var endpoint coreapi.Endpoints - err := t.client.Get(ctx, serviceKey, &endpoint) + err := t.Client.Get(ctx, serviceKey, &endpoint) if err != nil || !endpointIsReady(&endpoint) { t.Logf("waiting for Endpoint to be ready: %+v", endpoint) continue } if exposeByLoadBalancer { var svc coreapi.Service - t.client.Get(ctx, serviceKey, &svc) + t.Client.Get(ctx, serviceKey, &svc) if len(svc.Status.LoadBalancer.Ingress) == 0 || svc.Status.LoadBalancer.Ingress[0].IP == "" { t.Logf("waiting for LoadBalancer to be assigned: %+v", svc) continue diff --git a/test/e2e/update_test.go b/test/e2e/update_test.go index 3e3425a2..e3e5d333 100644 --- a/test/e2e/update_test.go +++ b/test/e2e/update_test.go @@ -28,16 +28,16 @@ func (t *PorchSuite) TestPackageUpdateRecloneAndReplay(ctx context.Context) { gitRepository = "package-update" ) - t.registerGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") + t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "test-blueprints", "") var list porchapi.PackageRevisionList - t.ListE(ctx, &list, client.InNamespace(t.namespace)) + t.ListE(ctx, &list, client.InNamespace(t.Namespace)) basensV2 := MustFindPackageRevision(t.T, &list, repository.PackageRevisionKey{Repository: "test-blueprints", Package: "basens", Revision: "v2"}) t.Logf("basensV2 = %v", basensV2) // Register the repository as 'downstream' - t.registerMainGitRepositoryF(ctx, gitRepository) + t.RegisterMainGitRepositoryF(ctx, gitRepository) // Create PackageRevision from upstream repo pr := &porchapi.PackageRevision{ @@ -46,7 +46,7 @@ func (t *PorchSuite) TestPackageUpdateRecloneAndReplay(ctx context.Context) { APIVersion: porchapi.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, + Namespace: t.Namespace, }, Spec: porchapi.PackageRevisionSpec{ PackageName: "testRecloneAndReplay", @@ -72,7 +72,7 @@ func (t *PorchSuite) TestPackageUpdateRecloneAndReplay(ctx context.Context) { t.CreateF(ctx, pr) t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, pr) @@ -90,13 +90,13 @@ func (t *PorchSuite) TestPackageUpdateRecloneAndReplay(ctx context.Context) { t.UpdateF(ctx, pr) t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, pr) var revisionResources porchapi.PackageRevisionResources t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, + Namespace: t.Namespace, Name: pr.Name, }, &revisionResources) From cb80dc5dcdb670e8f2ef8e2f8db1550bfe3e5deb Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Tue, 27 Aug 2024 16:37:05 +0200 Subject: [PATCH 02/10] Make TestFunctionRepository less flaky --- test/e2e/e2e_test.go | 58 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index e874e531..24c3f0d4 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -16,6 +16,7 @@ package e2e import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -31,10 +32,12 @@ import ( "github.com/nephio-project/porch/pkg/repository" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/kustomize/kyaml/yaml" @@ -730,23 +733,54 @@ func (t *PorchSuite) TestFunctionRepository(ctx context.Context) { // avoid flakiness. t.WaitUntilRepositoryReady(ctx, repo.Name, repo.Namespace) - // Wait here for the repository to be cached in porch. We wait - // first one minute, since Porch waits 1 minute before it syncs - // the repo for the first time. Then wait another minute so that - // the sync has (hopefully) finished. - // TODO(mortent): We need a better solution for this. This is only - // temporary to fix the current flakiness with the e2e tests. - t.Log("Waiting for 2 minutes for the repository to be cached") - <-time.NewTimer(2 * time.Minute).C - - list := &porchapi.FunctionList{} - t.ListE(ctx, list, client.InNamespace(t.Namespace)) - + t.Log("Waiting for 1 minute to avoid most of the initial transient period") + <-time.NewTimer(1 * time.Minute).C + + t.Log("Waiting for the gcr.io/kpt-fn repository to be cached in porch") + timeout := 5 * time.Minute + list := porchapi.FunctionList{} + err := wait.PollUntilContextTimeout(ctx, 5*time.Second, timeout, false, func(ctx context.Context) (bool, error) { + err := t.Client.List(ctx, &list, client.InNamespace(t.Namespace)) + if err != nil { + if apierrors.IsTimeout(err) || + errors.Is(err, os.ErrDeadlineExceeded) || + errors.Is(err, context.DeadlineExceeded) { + // in case of a timeout, we simply retry + t.Logf("timeout -> retry: %v", err) + return false, nil + } else { + // unfortunately, transient errors can happen during normal operation, so we retry instead of failing + t.Logf("Warning: error listing functions in gcr.io/kpt-fn repository: %v", err) + return false, nil + } + } + if len(list.Items) == 0 { + // unfortunately, sometimes an empty list is returned without any errors during the transient period, so we retry instead of failing + t.Log("Warning: an empty list of functions was returned (temporarily?)") + return false, nil + } + return true, nil + }) + if err != nil { + t.Errorf("Error listing functions in gcr.io/kpt-fn repository with timeout of %v: %v", timeout, err) + } if got := len(list.Items); got == 0 { t.Errorf("Found no functions in gcr.io/kpt-fn repository; expected at least one") } } +func (t *PorchSuite) newClientWithTimeout(timeout time.Duration) client.Client { + cfg := *t.kubeconfig + cfg.Timeout = timeout + c, err := client.New(&cfg, client.Options{ + Scheme: t.Client.Scheme(), + }) + if err != nil { + t.Fatalf("Failed to create client with timeout: %v", err) + } + return c +} + func (t *PorchSuite) TestPublicGitRepository(ctx context.Context) { t.RegisterGitRepositoryF(ctx, testBlueprintsRepo, "demo-blueprints", "") From 45114786fd0880980839009a6e522eb15c3102be Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Wed, 28 Aug 2024 10:51:00 +0200 Subject: [PATCH 03/10] Make sure that we test the right porchctl binary during end-to-end test --- test/e2e/cli/cli_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/e2e/cli/cli_test.go b/test/e2e/cli/cli_test.go index 58d9a243..5576ecf8 100644 --- a/test/e2e/cli/cli_test.go +++ b/test/e2e/cli/cli_test.go @@ -116,6 +116,14 @@ func runTestCase(t *testing.T, repoURL string, tc TestCaseConfig, searchAndRepla command.Args[i] = strings.ReplaceAll(arg, search, replace) } } + if command.Args[0] == "porchctl" { + // make sure that we are testing the porchctl command built from this codebase + fullCmdPath, err := filepath.Abs(filepath.Join("..", "..", "..", ".build", "porchctl")) + if err != nil { + t.Fatalf("Failed to get absolute path to .build directory: %v", err) + } + command.Args[0] = fullCmdPath + } cmd := exec.Command(command.Args[0], command.Args[1:]...) var stdout, stderr bytes.Buffer From 2d31406f62f3757f4cebd930f8f88faf25cbcb09 Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Wed, 28 Aug 2024 10:51:47 +0200 Subject: [PATCH 04/10] Make sure that porch-server and/or porch-controller pods are reloaded after `make run-in-kind` --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 1fa9414d..78ac2a9c 100644 --- a/Makefile +++ b/Makefile @@ -303,12 +303,14 @@ load-images-to-kind: ## Build porch images and load them into a kind cluster @if [ "$(SKIP_PORCHSERVER_BUILD)" = "false" ]; then \ echo "Building $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):${IMAGE_TAG}" ; \ docker buildx build --load --tag $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):$(IMAGE_TAG) -f ./build/Dockerfile "$(PORCHDIR)" && \ - kind load docker-image $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ + kind load docker-image $(IMAGE_REPO)/$(PORCH_SERVER_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} && \ + kubectl delete deployment -n porch-system --ignore-not-found=true porch-server ; \ fi @if [ "$(SKIP_CONTROLLER_BUILD)" = "false" ]; then \ echo "Building $(IMAGE_REPO)/$(PORCH_CONTROLLERS_IMAGE):${IMAGE_TAG}" ; \ IMAGE_NAME="$(PORCH_CONTROLLERS_IMAGE)" make -C controllers/ build-image && \ - kind load docker-image $(IMAGE_REPO)/$(PORCH_CONTROLLERS_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} ; \ + kind load docker-image $(IMAGE_REPO)/$(PORCH_CONTROLLERS_IMAGE):${IMAGE_TAG} -n ${KIND_CONTEXT_NAME} && \ + kubectl delete deployment -n porch-system --ignore-not-found=true porch-controllers ; \ fi else @@ -353,5 +355,5 @@ test-e2e: ## Run end-to-end tests E2E=1 go test -v -failfast ./test/e2e/cli .PHONY: test-e2e-clean -test-e2e-clean: ## Run end-to-end tests aginst a newly deployed porch in a newly created kind cluster +test-e2e-clean: porchctl ## Run end-to-end tests aginst a newly deployed porch in a newly created kind cluster ./scripts/clean-e2e-test.sh From 63915596f6ed4dbf5dde059c088005b0b277c16d Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Wed, 28 Aug 2024 11:19:20 +0200 Subject: [PATCH 05/10] Adjust e2e cli github action --- .github/workflows/porchctl-cli-e2e.yaml | 5 +-- test/e2e/cli/cli_test.go | 6 +--- test/e2e/cli/cluster.go | 43 +++++++++++++++---------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/.github/workflows/porchctl-cli-e2e.yaml b/.github/workflows/porchctl-cli-e2e.yaml index db889414..d59c0371 100644 --- a/.github/workflows/porchctl-cli-e2e.yaml +++ b/.github/workflows/porchctl-cli-e2e.yaml @@ -49,10 +49,7 @@ jobs: tag: v1.0.0-beta.49 chmod: 0755 - name: Build and install porchctl - run: | - go build -o ${GITHUB_WORKSPACE}.build/ ./cmd/porchctl - mv ${GITHUB_WORKSPACE}.build/porchctl /usr/local/bin/porchctl - sudo chmod 755 /usr/local/bin/porchctl + run: make porchctl - name: Create k8s Kind Cluster uses: helm/kind-action@v1 with: diff --git a/test/e2e/cli/cli_test.go b/test/e2e/cli/cli_test.go index 5576ecf8..b07469f9 100644 --- a/test/e2e/cli/cli_test.go +++ b/test/e2e/cli/cli_test.go @@ -118,11 +118,7 @@ func runTestCase(t *testing.T, repoURL string, tc TestCaseConfig, searchAndRepla } if command.Args[0] == "porchctl" { // make sure that we are testing the porchctl command built from this codebase - fullCmdPath, err := filepath.Abs(filepath.Join("..", "..", "..", ".build", "porchctl")) - if err != nil { - t.Fatalf("Failed to get absolute path to .build directory: %v", err) - } - command.Args[0] = fullCmdPath + command.Args[0] = PorchctlFullPath(t) } cmd := exec.Command(command.Args[0], command.Args[1:]...) diff --git a/test/e2e/cli/cluster.go b/test/e2e/cli/cluster.go index d8f26fd4..adb67a88 100644 --- a/test/e2e/cli/cluster.go +++ b/test/e2e/cli/cluster.go @@ -17,6 +17,7 @@ package e2e import ( "bytes" "os/exec" + "path/filepath" "strings" "testing" "time" @@ -80,6 +81,22 @@ func InferGitServerImage(porchImage string) string { return repo + TestGitServerImage + ":" + tag } +func PorchctlFullPath(t *testing.T) string { + porchctlFullPath, err := filepath.Abs(filepath.Join("..", "..", "..", ".build", "porchctl")) + if err != nil { + t.Fatalf("Failed to get absolute path to .build directory: %v", err) + } + return porchctlFullPath +} + +func Porchctl(t *testing.T, args ...string) error { + cmd := exec.Command(PorchctlFullPath(t), args...) + t.Logf("running command: porchctl %v", strings.Join(cmd.Args, " ")) + outBytes, err := cmd.CombinedOutput() + t.Logf("porchctl output: %v", string(outBytes)) + return err +} + func KubectlApply(t *testing.T, config string) { cmd := exec.Command("kubectl", "apply", "-f", "-") cmd.Stdin = strings.NewReader(config) @@ -141,7 +158,7 @@ func KubectlWaitForLoadBalancerIp(t *testing.T, namespace, name string) string { err := cmd.Run() ip := stdout.String() - if err == nil && len(ip) > 0 { // Loadbalancer assigned an external IP + if err == nil && len(ip) > 0 { // LoadBalancer assigned an external IP t.Logf("LoadBalancer external IP: %s", ip) return ip } @@ -169,11 +186,9 @@ func KubectlWaitForGitDNS(t *testing.T, gitServerURL string) { // We expect repos to automatically be created (albeit empty) repoURL := gitServerURL + "/" + name - cmd := exec.Command("porchctl", "repo", "register", "--namespace", name, "--name", name, repoURL) - t.Logf("running command %v", strings.Join(cmd.Args, " ")) - out, err := cmd.CombinedOutput() + err := Porchctl(t, "repo", "register", "--namespace", name, "--name", name, repoURL) if err != nil { - t.Fatalf("Failed to register probe repository: %v\n%s", err, string(out)) + t.Fatalf("Failed to register probe repository: %v", err) } // Based on experience, DNS seems to get updated inside the cluster within @@ -182,10 +197,7 @@ func KubectlWaitForGitDNS(t *testing.T, gitServerURL string) { // IP address directly. giveUp := time.Now().Add(1 * time.Minute) for { - cmd := exec.Command("porchctl", "rpkg", "get", "--namespace", name) - t.Logf("running command %v", strings.Join(cmd.Args, " ")) - out, err := cmd.CombinedOutput() - t.Logf("output: %v", string(out)) + err := Porchctl(t, "rpkg", "get", "--namespace", name) if err == nil { break @@ -232,12 +244,12 @@ func RemovePackagerevFinalizers(t *testing.T, namespace string) { t.Fatalf("Error when getting packagerevs from namespace: %v: %s", err, stderr.String()) } - packagerevs := realySplit(stdout.String(), " ") + packagerevs := reallySplit(stdout.String(), " ") if len(packagerevs) == 0 { t.Log("kubectl get packagerevs didn't return any objects - continue") return } - t.Logf("Removing Finalizers from PackagRevs: %v", packagerevs) + t.Logf("Removing Finalizers from PackageRevs: %v", packagerevs) for _, pkgrev := range packagerevs { cmd := exec.Command("kubectl", "patch", "packagerev", pkgrev, "--type", "json", "--patch=[{\"op\": \"remove\", \"path\": \"/metadata/finalizers\"}]", "--namespace", namespace) @@ -248,7 +260,7 @@ func RemovePackagerevFinalizers(t *testing.T, namespace string) { } } -func realySplit(s, sep string) []string { +func reallySplit(s, sep string) []string { if len(s) == 0 { return []string{} } @@ -256,11 +268,8 @@ func realySplit(s, sep string) []string { } func RegisterRepository(t *testing.T, repoURL, namespace, name string) { - cmd := exec.Command("porchctl", "repo", "register", "--namespace", namespace, "--name", name, repoURL) - t.Logf("running command %v", strings.Join(cmd.Args, " ")) - out, err := cmd.CombinedOutput() + err := Porchctl(t, "repo", "register", "--namespace", namespace, "--name", name, repoURL) if err != nil { - t.Fatalf("Failed to register repository %q: %v\n%s", repoURL, err, string(out)) + t.Fatalf("Failed to register repository %q: %v", repoURL, err) } - t.Logf("output: %v", string(out)) } From 30395d789356edb921fce089d971e0f1c469f092 Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Wed, 28 Aug 2024 17:27:13 +0200 Subject: [PATCH 06/10] Changes from review --- test/e2e/{suit_utils.go => suite_utils.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/e2e/{suit_utils.go => suite_utils.go} (100%) diff --git a/test/e2e/suit_utils.go b/test/e2e/suite_utils.go similarity index 100% rename from test/e2e/suit_utils.go rename to test/e2e/suite_utils.go From ffa97cfc1591b6fac2d15adee8448bf9d5db9118 Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Thu, 29 Aug 2024 12:20:46 +0200 Subject: [PATCH 07/10] Refactor end-to-end CLI tests by introducing a Suite that hold all common variables --- test/e2e/cli/cli_e2e_test.go | 31 +++ test/e2e/cli/cluster.go | 60 ------ test/e2e/cli/config.go | 2 +- test/e2e/cli/{cli_test.go => suite.go} | 276 ++++++++++++++++--------- 4 files changed, 206 insertions(+), 163 deletions(-) create mode 100644 test/e2e/cli/cli_e2e_test.go rename test/e2e/cli/{cli_test.go => suite.go} (64%) diff --git a/test/e2e/cli/cli_e2e_test.go b/test/e2e/cli/cli_e2e_test.go new file mode 100644 index 00000000..37b76c82 --- /dev/null +++ b/test/e2e/cli/cli_e2e_test.go @@ -0,0 +1,31 @@ +// Copyright 2024 The kpt and Nephio 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 ( + "os" + "path/filepath" + "testing" +) + +func TestPorch(t *testing.T) { + e2e := os.Getenv("E2E") + if e2e == "" { + t.Skip("set E2E to run this test") + } + + suite := NewCliTestSuite(t, filepath.Join(".", "testdata")) + suite.RunTests(t) +} diff --git a/test/e2e/cli/cluster.go b/test/e2e/cli/cluster.go index adb67a88..787b2292 100644 --- a/test/e2e/cli/cluster.go +++ b/test/e2e/cli/cluster.go @@ -17,7 +17,6 @@ package e2e import ( "bytes" "os/exec" - "path/filepath" "strings" "testing" "time" @@ -81,22 +80,6 @@ func InferGitServerImage(porchImage string) string { return repo + TestGitServerImage + ":" + tag } -func PorchctlFullPath(t *testing.T) string { - porchctlFullPath, err := filepath.Abs(filepath.Join("..", "..", "..", ".build", "porchctl")) - if err != nil { - t.Fatalf("Failed to get absolute path to .build directory: %v", err) - } - return porchctlFullPath -} - -func Porchctl(t *testing.T, args ...string) error { - cmd := exec.Command(PorchctlFullPath(t), args...) - t.Logf("running command: porchctl %v", strings.Join(cmd.Args, " ")) - outBytes, err := cmd.CombinedOutput() - t.Logf("porchctl output: %v", string(outBytes)) - return err -} - func KubectlApply(t *testing.T, config string) { cmd := exec.Command("kubectl", "apply", "-f", "-") cmd.Stdin = strings.NewReader(config) @@ -175,42 +158,6 @@ func KubectlWaitForLoadBalancerIp(t *testing.T, namespace, name string) string { } } -// Kubernetes DNS needs time to propagate the updated address -// Wait until we can register the repository and list its contents. -func KubectlWaitForGitDNS(t *testing.T, gitServerURL string) { - const name = "test-git-dns-resolve" - - KubectlCreateNamespace(t, name) - defer KubectlDeleteNamespace(t, name) - - // We expect repos to automatically be created (albeit empty) - repoURL := gitServerURL + "/" + name - - err := Porchctl(t, "repo", "register", "--namespace", name, "--name", name, repoURL) - if err != nil { - t.Fatalf("Failed to register probe repository: %v", err) - } - - // Based on experience, DNS seems to get updated inside the cluster within - // few seconds. We will wait about a minute. - // If this turns out to be an issue, we will sidestep DNS and use the Endpoints - // IP address directly. - giveUp := time.Now().Add(1 * time.Minute) - for { - err := Porchctl(t, "rpkg", "get", "--namespace", name) - - if err == nil { - break - } - - if time.Now().After(giveUp) { - t.Fatalf("Git service DNS resolution failed: %v", err) - } - - time.Sleep(5 * time.Second) - } -} - func KubectlCreateNamespace(t *testing.T, name string) { cmd := exec.Command("kubectl", "create", "namespace", name) t.Logf("running command %v", strings.Join(cmd.Args, " ")) @@ -266,10 +213,3 @@ func reallySplit(s, sep string) []string { } return strings.Split(s, sep) } - -func RegisterRepository(t *testing.T, repoURL, namespace, name string) { - err := Porchctl(t, "repo", "register", "--namespace", namespace, "--name", name, repoURL) - if err != nil { - t.Fatalf("Failed to register repository %q: %v", repoURL, err) - } -} diff --git a/test/e2e/cli/config.go b/test/e2e/cli/config.go index 9c6abc25..84aea6e9 100644 --- a/test/e2e/cli/config.go +++ b/test/e2e/cli/config.go @@ -36,7 +36,7 @@ type Command struct { ExitCode int `yaml:"exitCode,omitempty"` // Yaml indicates that stdout is yaml and the test will reformat it for stable ordering Yaml bool `yaml:"yaml,omitempty"` - // IgnoreWhitespace inidicates that whitespace differences should be ignored in the output + // IgnoreWhitespace indicates that whitespace differences should be ignored in the output IgnoreWhitespace bool `yaml:"ignoreWhitespace,omitempty"` } diff --git a/test/e2e/cli/cli_test.go b/test/e2e/cli/suite.go similarity index 64% rename from test/e2e/cli/cli_test.go rename to test/e2e/cli/suite.go index b07469f9..0960f55b 100644 --- a/test/e2e/cli/cli_test.go +++ b/test/e2e/cli/suite.go @@ -1,4 +1,4 @@ -// Copyright 2022 The kpt and Nephio Authors +// Copyright 2024 The kpt and Nephio Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -37,37 +37,45 @@ const ( defaultTestGitServerUrl = "http://" + testGitName + "." + testGitNamespace + ".svc.cluster.local:8080" ) -func TestPorch(t *testing.T) { - e2e := os.Getenv("E2E") - if e2e == "" { - t.Skip("set E2E to run this test") - } +type CliTestSuite struct { + // The path of the directory containing the test cases. + TestDataPath string + // SearchAndReplace contains (search, replace) pairs that will be applied to the command output before comparison. + SearchAndReplace map[string]string + // GitServerURL is the URL of the git server to use for the tests. + GitServerURL string + // PorchctlCommand is the full path to the porchctl command to be tested. + PorchctlCommand string +} + +// NewCliTestSuite creates a new CliTestSuite based on the configuration in the testdata directory. +func NewCliTestSuite(t *testing.T, testdataDir string) *CliTestSuite { + var err error - abs, err := filepath.Abs(filepath.Join(".", "testdata")) + s := &CliTestSuite{} + // set base dir + s.TestDataPath, err = filepath.Abs(testdataDir) if err != nil { t.Fatalf("Failed to get absolute path to testdata directory: %v", err) } - runTests(t, abs) -} - -func runUtilityCommand(t *testing.T, command string, args ...string) error { - cmd := exec.Command(command, args...) - t.Logf("running utility command %s %s", command, strings.Join(args, " ")) - return cmd.Run() -} - -func runTests(t *testing.T, path string) { - var searchAndReplace = map[string]string{} - - gitServerURL := startGitServer(t, path) - if gitServerURL != defaultTestGitServerUrl { - searchAndReplace[defaultTestGitServerUrl] = gitServerURL + // find porchctl to test + s.PorchctlCommand, err = filepath.Abs(filepath.Join("..", "..", "..", ".build", "porchctl")) + if err != nil { + t.Fatalf("Failed to get absolute path to .build/porchctl command: %v", err) + } + if _, err := os.Stat(s.PorchctlCommand); err != nil { + t.Fatalf("porchctl command not found at %q: %v", s.PorchctlCommand, err) } - testCases := scanTestCases(t, path) + // start git server + s.StartGitServer(t) // sets s.GitServerURL + s.SearchAndReplace = map[string]string{} + if s.GitServerURL != defaultTestGitServerUrl { + s.SearchAndReplace[defaultTestGitServerUrl] = s.GitServerURL + } - // remove any tmp files from previous test runs - err := runUtilityCommand(t, "rm", "-rf", "/tmp/porch-e2e") + // prepare tmp directory used by the commands in the test cases + err = runUtilityCommand(t, "rm", "-rf", "/tmp/porch-e2e") if err != nil { t.Fatalf("Failed to clean up older run: %v", err) } @@ -75,50 +83,46 @@ func runTests(t *testing.T, path string) { if err != nil { t.Fatalf("Failed to create temp directory: %v", err) } + return s +} +// RunTests runs the test cases in the testdata directory. +func (s *CliTestSuite) RunTests(t *testing.T) { + testCases := s.ScanTestCases(t) for _, tc := range testCases { t.Run(tc.TestCase, func(t *testing.T) { if tc.Skip != "" { t.Skipf("Skipping test: %s", tc.Skip) } - repoURL := gitServerURL + "/" + strings.ReplaceAll(tc.TestCase, "/", "-") - runTestCase(t, repoURL, tc, searchAndReplace) + s.RunTestCase(t, tc) }) } } -func normalizeWhitespace(s1 string) string { - parts := strings.Split(s1, " ") - words := make([]string, 0, len(parts)) - for _, part := range parts { - if part != "" { - words = append(words, part) - } - } - return strings.Join(words, " ") -} +// RunTestCase runs a single test case. +func (s *CliTestSuite) RunTestCase(t *testing.T, tc TestCaseConfig) { + repoURL := s.GitServerURL + "/" + strings.ReplaceAll(tc.TestCase, "/", "-") -func runTestCase(t *testing.T, repoURL string, tc TestCaseConfig, searchAndReplace map[string]string) { KubectlCreateNamespace(t, tc.TestCase) t.Cleanup(func() { KubectlDeleteNamespace(t, tc.TestCase) }) if tc.Repository != "" { - RegisterRepository(t, repoURL, tc.TestCase, tc.Repository) + s.RegisterRepository(t, repoURL, tc.TestCase, tc.Repository) } for i := range tc.Commands { time.Sleep(1 * time.Second) command := &tc.Commands[i] for i, arg := range command.Args { - for search, replace := range searchAndReplace { + for search, replace := range s.SearchAndReplace { command.Args[i] = strings.ReplaceAll(arg, search, replace) } } if command.Args[0] == "porchctl" { // make sure that we are testing the porchctl command built from this codebase - command.Args[0] = PorchctlFullPath(t) + command.Args[0] = s.PorchctlCommand } cmd := exec.Command(command.Args[0], command.Args[1:]...) @@ -140,7 +144,7 @@ func runTestCase(t *testing.T, repoURL string, tc TestCaseConfig, searchAndRepla stdoutStr := stdout.String() stderrStr := stderr.String() - for search, replace := range searchAndReplace { + for search, replace := range s.SearchAndReplace { command.Stdout = strings.ReplaceAll(command.Stdout, search, replace) command.Stderr = strings.ReplaceAll(command.Stderr, search, replace) } @@ -182,6 +186,134 @@ func runTestCase(t *testing.T, repoURL string, tc TestCaseConfig, searchAndRepla } } +func (s *CliTestSuite) StartGitServer(t *testing.T) { + isPorchInCluster := IsPorchServerRunningInCluster(t) + gitServerImage := GetGitServerImageName(t) + t.Logf("Git Image: %s", gitServerImage) + + configFile := filepath.Join(s.TestDataPath, "git-server.yaml") + configBytes, err := os.ReadFile(configFile) + if err != nil { + t.Fatalf("Failed to read git server config file %q: %v", configFile, err) + } + config := string(configBytes) + config = strings.ReplaceAll(config, "GIT_SERVER_IMAGE", gitServerImage) + if !isPorchInCluster { + config = strings.ReplaceAll(config, "ClusterIP", "LoadBalancer") + } + + t.Cleanup(func() { + KubectlDeleteNamespace(t, testGitNamespace) + }) + + KubectlApply(t, config) + KubectlWaitForDeployment(t, testGitNamespace, testGitName) + KubectlWaitForService(t, testGitNamespace, testGitName) + + if isPorchInCluster { + s.GitServerURL = defaultTestGitServerUrl + s.KubectlWaitForGitDNS(t) + } else { + ip := KubectlWaitForLoadBalancerIp(t, testGitNamespace, testGitName) + s.GitServerURL = "http://" + ip + ":8080" + } +} + +// ScanTestCases parses the test case configs from the testdata directory. +func (s *CliTestSuite) ScanTestCases(t *testing.T) []TestCaseConfig { + testCases := []TestCaseConfig{} + + if err := filepath.Walk(s.TestDataPath, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + return nil + } + if path == s.TestDataPath { + return nil + } + + tc := ReadTestCaseConfig(t, info.Name(), path) + testCases = append(testCases, tc) + + return nil + }); err != nil { + t.Fatalf("Failed to scan test cases: %v", err) + } + + return testCases +} + +// Porchctl runs the porchctl command under test with the given arguments. +func (s *CliTestSuite) Porchctl(t *testing.T, args ...string) error { + cmd := exec.Command(s.PorchctlCommand, args...) + t.Logf("running command: porchctl %v", strings.Join(args, " ")) + outBytes, err := cmd.CombinedOutput() + t.Logf("porchctl output: %v", string(outBytes)) + return err +} + +func (s *CliTestSuite) RegisterRepository(t *testing.T, repoURL, namespace, name string) { + err := s.Porchctl(t, "repo", "register", "--namespace", namespace, "--name", name, repoURL) + if err != nil { + t.Fatalf("Failed to register repository %q: %v", repoURL, err) + } +} + +// Kubernetes DNS needs time to propagate the git server's domain name. +// Wait until we can register the repository and list its contents. +func (s *CliTestSuite) KubectlWaitForGitDNS(t *testing.T) { + const name = "test-git-dns-resolve" + + KubectlCreateNamespace(t, name) + defer KubectlDeleteNamespace(t, name) + + // We expect repos to automatically be created (albeit empty) + repoURL := s.GitServerURL + "/" + name + + err := s.Porchctl(t, "repo", "register", "--namespace", name, "--name", name, repoURL) + if err != nil { + t.Fatalf("Failed to register probe repository: %v", err) + } + + // Based on experience, DNS seems to get updated inside the cluster within + // few seconds. We will wait about a minute. + // If this turns out to be an issue, we will sidestep DNS and use the Endpoints + // IP address directly. + giveUp := time.Now().Add(1 * time.Minute) + for { + err := s.Porchctl(t, "rpkg", "get", "--namespace", name) + + if err == nil { + break + } + + if time.Now().After(giveUp) { + t.Fatalf("Git service DNS resolution failed: %v", err) + } + + time.Sleep(5 * time.Second) + } +} + +func runUtilityCommand(t *testing.T, command string, args ...string) error { + cmd := exec.Command(command, args...) + t.Logf("running utility command %s %s", command, strings.Join(args, " ")) + return cmd.Run() +} + +func normalizeWhitespace(s1 string) string { + parts := strings.Split(s1, " ") + words := make([]string, 0, len(parts)) + for _, part := range parts { + if part != "" { + words = append(words, part) + } + } + return strings.Join(words, " ") +} + func removeArmPlatformWarning(got string) string { got = strings.Replace( got, @@ -252,66 +384,6 @@ func reorderYamlStdout(t *testing.T, buf *bytes.Buffer) { } } -func startGitServer(t *testing.T, path string) string { - isPorchInCluster := IsPorchServerRunningInCluster(t) - gitServerImage := GetGitServerImageName(t) - t.Logf("Git Image: %s", gitServerImage) - - configFile := filepath.Join(path, "git-server.yaml") - configBytes, err := os.ReadFile(configFile) - if err != nil { - t.Fatalf("Failed to read git server config file %q: %v", configFile, err) - } - config := string(configBytes) - config = strings.ReplaceAll(config, "GIT_SERVER_IMAGE", gitServerImage) - if !isPorchInCluster { - config = strings.ReplaceAll(config, "ClusterIP", "LoadBalancer") - } - - t.Cleanup(func() { - KubectlDeleteNamespace(t, testGitNamespace) - }) - - KubectlApply(t, config) - KubectlWaitForDeployment(t, testGitNamespace, testGitName) - KubectlWaitForService(t, testGitNamespace, testGitName) - - gitServerURL := defaultTestGitServerUrl - if isPorchInCluster { - KubectlWaitForGitDNS(t, gitServerURL) - } else { - ip := KubectlWaitForLoadBalancerIp(t, testGitNamespace, testGitName) - gitServerURL = "http://" + ip + ":8080" - } - - return gitServerURL -} - -func scanTestCases(t *testing.T, root string) []TestCaseConfig { - testCases := []TestCaseConfig{} - - if err := filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - return nil - } - if path == root { - return nil - } - - tc := ReadTestCaseConfig(t, info.Name(), path) - testCases = append(testCases, tc) - - return nil - }); err != nil { - t.Fatalf("Failed to scan test cases: %v", err) - } - - return testCases -} - func updateCommand(command *Command, exit error, stdout, stderr string) { command.ExitCode = exitCode(exit) command.Stdout = stdout From baf84897ac86d62e8bb3cd0e87cbeab940b3862e Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Thu, 29 Aug 2024 12:36:25 +0200 Subject: [PATCH 08/10] Make the rest of TestSuite members also public --- test/e2e/e2e_test.go | 10 +++++----- test/e2e/suite.go | 18 +++++++++--------- test/e2e/suite_logs.go | 8 ++++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 24c3f0d4..5233073d 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -769,8 +769,8 @@ func (t *PorchSuite) TestFunctionRepository(ctx context.Context) { } } -func (t *PorchSuite) newClientWithTimeout(timeout time.Duration) client.Client { - cfg := *t.kubeconfig +func (t *PorchSuite) NewClientWithTimeout(timeout time.Duration) client.Client { + cfg := *t.Kubeconfig cfg.Timeout = timeout c, err := client.New(&cfg, client.Options{ Scheme: t.Client.Scheme(), @@ -1538,7 +1538,7 @@ for resource in ctx.resource_list["items"]: } func (t *PorchSuite) TestPodFunctionEvaluatorWithDistrolessImage(ctx context.Context) { - if t.testRunnerIsLocal { + if t.TestRunnerIsLocal { t.Skipf("Skipping due to not having pod evaluator in local mode") } @@ -1619,7 +1619,7 @@ data: } func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { - if t.testRunnerIsLocal { + if t.TestRunnerIsLocal { t.Skipf("Skipping due to not having pod evaluator in local mode") } @@ -1780,7 +1780,7 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { } func (t *PorchSuite) TestPodEvaluatorWithFailure(ctx context.Context) { - if t.testRunnerIsLocal { + if t.TestRunnerIsLocal { t.Skipf("Skipping due to not having pod evaluator in local mode") } diff --git a/test/e2e/suite.go b/test/e2e/suite.go index 393cab77..f5787c25 100644 --- a/test/e2e/suite.go +++ b/test/e2e/suite.go @@ -80,16 +80,16 @@ func (p Password) String() string { type TestSuite struct { *testing.T - kubeconfig *rest.Config + Kubeconfig *rest.Config Client client.Client // Strongly-typed client handy for reading e.g. pod logs - kubeClient kubernetes.Interface + KubeClient kubernetes.Interface - clientset porchclient.Interface + Clientset porchclient.Interface Namespace string // K8s namespace for this test run - testRunnerIsLocal bool // Tests running against local dev porch + TestRunnerIsLocal bool // Tests running against local dev porch } type Initializer interface { @@ -149,22 +149,22 @@ func (t *TestSuite) Initialize(ctx context.Context) { t.Fatalf("Failed to initialize k8s client (%s): %v", cfg.Host, err) } else { t.Client = c - t.kubeconfig = cfg + t.Kubeconfig = cfg } if kubeClient, err := kubernetes.NewForConfig(cfg); err != nil { t.Fatalf("failed to initialize kubernetes clientset: %v", err) } else { - t.kubeClient = kubeClient + t.KubeClient = kubeClient } if cs, err := porchclient.NewForConfig(cfg); err != nil { t.Fatalf("Failed to initialize Porch clientset: %v", err) } else { - t.clientset = cs + t.Clientset = cs } - t.testRunnerIsLocal = !t.IsTestRunnerInCluster() + t.TestRunnerIsLocal = !t.IsTestRunnerInCluster() namespace := fmt.Sprintf("porch-test-%d", time.Now().UnixMicro()) t.CreateF(ctx, &coreapi.Namespace{ @@ -330,7 +330,7 @@ func (t *TestSuite) patch(ctx context.Context, obj client.Object, patch client.P func (t *TestSuite) updateApproval(ctx context.Context, obj *porchapi.PackageRevision, opts metav1.UpdateOptions, eh ErrorHandler) *porchapi.PackageRevision { t.Helper() t.Logf("updating approval of %v", DebugFormat(obj)) - if res, err := t.clientset.PorchV1alpha1().PackageRevisions(obj.Namespace).UpdateApproval(ctx, obj.Name, obj, opts); err != nil { + if res, err := t.Clientset.PorchV1alpha1().PackageRevisions(obj.Namespace).UpdateApproval(ctx, obj.Name, obj, opts); err != nil { eh("failed to update approval of %s/%s: %v", obj.Namespace, obj.Name, err) return nil } else { diff --git a/test/e2e/suite_logs.go b/test/e2e/suite_logs.go index ab065e04..419ae28a 100644 --- a/test/e2e/suite_logs.go +++ b/test/e2e/suite_logs.go @@ -56,12 +56,12 @@ func (t *TestSuite) hasOwner(child, parent runtime.Object) bool { func (t *TestSuite) dumpLogsForDeployment(ctx context.Context, deploymentKey client.ObjectKey, eh ErrorHandler) { t.Helper() - deployment, err := t.kubeClient.AppsV1().Deployments(deploymentKey.Namespace).Get(ctx, deploymentKey.Name, metav1.GetOptions{}) + deployment, err := t.KubeClient.AppsV1().Deployments(deploymentKey.Namespace).Get(ctx, deploymentKey.Name, metav1.GetOptions{}) if err != nil { eh("failed to get deployemnt %v: %v", deploymentKey, err) } - replicaSets, err := t.kubeClient.AppsV1().ReplicaSets(deployment.Namespace).List(ctx, metav1.ListOptions{}) + replicaSets, err := t.KubeClient.AppsV1().ReplicaSets(deployment.Namespace).List(ctx, metav1.ListOptions{}) if err != nil { eh("failed to list replicasets: %v", err) } @@ -77,7 +77,7 @@ func (t *TestSuite) dumpLogsForDeployment(ctx context.Context, deploymentKey cli func (t *TestSuite) dumpLogsForReplicaSet(ctx context.Context, replicaSet *appsv1.ReplicaSet, eh ErrorHandler) { t.Helper() - pods, err := t.kubeClient.CoreV1().Pods(replicaSet.Namespace).List(ctx, metav1.ListOptions{}) + pods, err := t.KubeClient.CoreV1().Pods(replicaSet.Namespace).List(ctx, metav1.ListOptions{}) if err != nil { eh("failed to list pods: %v", err) } @@ -104,7 +104,7 @@ func (t *TestSuite) dumpLogsForPod(ctx context.Context, pod *corev1.Pod, eh Erro func (t *TestSuite) dumpLogsForPodContainer(ctx context.Context, podKey client.ObjectKey, containerName string, eh ErrorHandler) { t.Helper() - req := t.kubeClient.CoreV1().Pods(podKey.Namespace).GetLogs(podKey.Name, &corev1.PodLogOptions{Container: containerName}) + req := t.KubeClient.CoreV1().Pods(podKey.Namespace).GetLogs(podKey.Name, &corev1.PodLogOptions{Container: containerName}) podLogs, err := req.Stream(ctx) if err != nil { eh("failed to open pod logs %v %s: %v", podKey, containerName, err) From 10dfb69156cdf7842bc1cfd4f8086e859fbe37eb Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Thu, 29 Aug 2024 12:46:30 +0200 Subject: [PATCH 09/10] Avoid importing "e2e CLI" package into the "e2e test" package --- test/e2e/cli/cluster.go | 16 ++-------------- test/e2e/suite.go | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/test/e2e/cli/cluster.go b/test/e2e/cli/cluster.go index 787b2292..65f25a6c 100644 --- a/test/e2e/cli/cluster.go +++ b/test/e2e/cli/cluster.go @@ -20,10 +20,8 @@ import ( "strings" "testing" "time" -) -const ( - TestGitServerImage = "test-git-server" + e2etest "github.com/nephio-project/porch/test/e2e" ) func IsPorchServerRunningInCluster(t *testing.T) bool { @@ -67,17 +65,7 @@ func GetGitServerImageName(t *testing.T) string { if image == "" { t.Fatalf("Cannot determine Porch server image: output was %q", out) } - return InferGitServerImage(image) -} - -func InferGitServerImage(porchImage string) string { - slash := strings.LastIndex(porchImage, "/") - repo := porchImage[:slash+1] - image := porchImage[slash+1:] - colon := strings.LastIndex(image, ":") - tag := image[colon+1:] - - return repo + TestGitServerImage + ":" + tag + return e2etest.InferGitServerImage(image) } func KubectlApply(t *testing.T, config string) { diff --git a/test/e2e/suite.go b/test/e2e/suite.go index f5787c25..67909ab5 100644 --- a/test/e2e/suite.go +++ b/test/e2e/suite.go @@ -40,7 +40,6 @@ import ( "github.com/nephio-project/porch/pkg/git" kptfilev1 "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" "github.com/nephio-project/porch/pkg/repository" - porchtest "github.com/nephio-project/porch/test/e2e/cli" appsv1 "k8s.io/api/apps/v1" coreapi "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -58,6 +57,7 @@ const ( // TODO: accept a flag? PorchTestConfigFile = "porch-test-config.yaml" updateGoldenFiles = "UPDATE_GOLDEN_FILES" + TestGitServerImage = "test-git-server" ) type GitConfig struct { @@ -542,7 +542,7 @@ func (t *TestSuite) createInClusterGitServer(ctx context.Context, exposeByLoadBa Name: "function-runner", }, &porch) - gitImage := porchtest.InferGitServerImage(porch.Spec.Template.Spec.Containers[0].Image) + gitImage := InferGitServerImage(porch.Spec.Template.Spec.Containers[0].Image) var deploymentKey = client.ObjectKey{ Namespace: t.Namespace, @@ -732,6 +732,16 @@ func (t *TestSuite) createInClusterGitServer(ctx context.Context, exposeByLoadBa } } +func InferGitServerImage(porchImage string) string { + slash := strings.LastIndex(porchImage, "/") + repo := porchImage[:slash+1] + image := porchImage[slash+1:] + colon := strings.LastIndex(image, ":") + tag := image[colon+1:] + + return repo + TestGitServerImage + ":" + tag +} + func endpointIsReady(endpoints *coreapi.Endpoints) bool { if len(endpoints.Subsets) == 0 { return false From 072acf3c80981c2f0cea53b4dc40037055481b31 Mon Sep 17 00:00:00 2001 From: Istvan Kispal Date: Thu, 29 Aug 2024 14:28:07 +0200 Subject: [PATCH 10/10] GH action: Wait more before first end-to-end test case --- .github/workflows/porch-e2e.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/porch-e2e.yaml b/.github/workflows/porch-e2e.yaml index 5547ae46..a92c903e 100644 --- a/.github/workflows/porch-e2e.yaml +++ b/.github/workflows/porch-e2e.yaml @@ -56,8 +56,8 @@ jobs: cluster_name: kind - name: Build Images and Deploy porch kpt pkg run: IMAGE_REPO=porch-kind IMAGE_TAG=${GITHUB_SHA:0:8} KIND_CONTEXT_NAME=kind make run-in-kind - - name: Sleep for 15 seconds - run: sleep 15s + - name: Sleep for 30 seconds + run: sleep 30s shell: bash - name: e2e test run: E2E=1 go test -v -timeout 20m ${GITHUB_WORKSPACE}/test/e2e