diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index cf65752e..2e25cbbc 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.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. @@ -23,23 +23,17 @@ import ( "strings" "testing" "time" - + "github.com/google/go-cmp/cmp" porchapi "github.com/nephio-project/porch/api/porch/v1alpha1" configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" - internalapi "github.com/nephio-project/porch/internal/api/porchinternal/v1alpha1" kptfilev1 "github.com/nephio-project/porch/pkg/kpt/api/kptfile/v1" "github.com/nephio-project/porch/pkg/repository" "github.com/stretchr/testify/assert" - coreapi "k8s.io/api/core/v1" 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/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" "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" @@ -91,7 +85,6 @@ func Run(suite interface{}, t *testing.T) { type PorchSuite struct { TestSuite - gitConfig GitConfig } @@ -108,6 +101,81 @@ func (p *PorchSuite) GitConfig(repoID string) GitConfig { return config } +func (t *PorchSuite) registerMainGitRepositoryF(ctx context.Context, name string, opts ...repositoryOption) { + repoID := t.namespace + "-" + name + config := t.GitConfig(repoID) + + var secret string + // Create auth secret if necessary + if config.Username != "" || config.Password != "" { + secret = fmt.Sprintf("%s-auth", name) + immutable := true + t.CreateF(ctx, &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secret, + Namespace: t.namespace, + }, + Immutable: &immutable, + Data: map[string][]byte{ + "username": []byte(config.Username), + "password": []byte(config.Password), + }, + Type: corev1.SecretTypeBasicAuth, + }) + t.Cleanup(func() { + t.DeleteE(ctx, &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secret, + Namespace: t.namespace, + }, + }) + }) + } + + repository := &configapi.Repository{ + TypeMeta: metav1.TypeMeta{ + Kind: "Repository", + APIVersion: configapi.GroupVersion.Identifier(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: t.namespace, + }, + Spec: configapi.RepositorySpec{ + Description: "Porch Test Repository Description", + Type: configapi.RepositoryTypeGit, + Content: configapi.RepositoryContentPackage, + Git: &configapi.GitRepository{ + Repo: config.Repo, + Branch: config.Branch, + Directory: config.Directory, + SecretRef: configapi.SecretRef{ + Name: secret, + }, + }, + }, + } + + // Apply options + for _, o := range opts { + o(repository) + } + + // Register repository + t.CreateF(ctx, repository) + + t.Cleanup(func() { + t.DeleteE(ctx, &configapi.Repository{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: t.namespace, + }, + }) + t.waitUntilRepositoryDeleted(ctx, name, t.namespace) + t.waitUntilAllPackagesDeleted(ctx, name) + }) +} + func (t *PorchSuite) TestGitRepository(ctx context.Context) { // Register the repository as 'git' t.registerMainGitRepositoryF(ctx, "git") @@ -387,7 +455,6 @@ func (t *PorchSuite) TestInitTaskPackage(ctx context.Context) { func (t *PorchSuite) TestCloneIntoDeploymentRepository(ctx context.Context) { const downstreamRepository = "deployment" const downstreamPackage = "istions" - const downstreamRevision = "v2" const downstreamWorkspace = "test-workspace" // Register the deployment repository @@ -485,7 +552,7 @@ func (t *PorchSuite) TestCloneIntoDeploymentRepository(ctx context.Context) { } // Check generated context - var configmap coreapi.ConfigMap + var configmap corev1.ConfigMap t.FindAndDecodeF(&istions, "package-context.yaml", &configmap) if got, want := configmap.Name, "kptfile.kpt.dev"; got != want { t.Errorf("package context name: got %s, want %s", got, want) @@ -1704,7 +1771,7 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) { } // Get the fn runner pods and delete them. - podList := &coreapi.PodList{} + podList := &corev1.PodList{} t.ListF(ctx, podList, client.InNamespace("porch-fn-system")) for _, pod := range podList.Items { img := pod.Spec.Containers[0].Image @@ -2314,399 +2381,3 @@ func (t *PorchSuite) TestPackageRevisionFinalizers(ctx context.Context) { }, 10*time.Second) } -func (t *PorchSuite) validateFinalizers(ctx context.Context, name string, finalizers []string) { - var pr porchapi.PackageRevision - t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, - Name: name, - }, &pr) - - if len(finalizers) != len(pr.Finalizers) { - diff := cmp.Diff(finalizers, pr.Finalizers) - t.Errorf("Expected %d finalizers, but got %s", len(finalizers), diff) - } - - for _, finalizer := range finalizers { - var found bool - for _, f := range pr.Finalizers { - if f == finalizer { - found = true - } - } - if !found { - t.Errorf("Expected finalizer %v, but didn't find it", finalizer) - } - } -} - -func (t *PorchSuite) validateOwnerReferences(ctx context.Context, name string, ownerRefs []metav1.OwnerReference) { - var pr porchapi.PackageRevision - t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, - Name: name, - }, &pr) - - if len(ownerRefs) != len(pr.OwnerReferences) { - diff := cmp.Diff(ownerRefs, pr.OwnerReferences) - t.Errorf("Expected %d ownerReferences, but got %s", len(ownerRefs), diff) - } - - for _, ownerRef := range ownerRefs { - var found bool - for _, or := range pr.OwnerReferences { - if or == ownerRef { - found = true - } - } - if !found { - t.Errorf("Expected ownerRef %v, but didn't find it", ownerRef) - } - } -} - -func (t *PorchSuite) validateLabelsAndAnnos(ctx context.Context, name string, labels, annos map[string]string) { - var pr porchapi.PackageRevision - t.GetF(ctx, client.ObjectKey{ - Namespace: t.namespace, - Name: name, - }, &pr) - - actualLabels := pr.ObjectMeta.Labels - actualAnnos := pr.ObjectMeta.Annotations - - // Make this check to handle empty vs nil maps - if !(len(labels) == 0 && len(actualLabels) == 0) { - if diff := cmp.Diff(actualLabels, labels); diff != "" { - t.Errorf("Unexpected result (-want, +got): %s", diff) - } - } - - if !(len(annos) == 0 && len(actualAnnos) == 0) { - if diff := cmp.Diff(actualAnnos, annos); diff != "" { - t.Errorf("Unexpected result (-want, +got): %s", diff) - } - } -} - -func (t *PorchSuite) registerGitRepositoryF(ctx context.Context, repo, name, directory string) { - t.CreateF(ctx, &configapi.Repository{ - TypeMeta: metav1.TypeMeta{ - Kind: "Repository", - APIVersion: configapi.GroupVersion.Identifier(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: t.namespace, - }, - Spec: configapi.RepositorySpec{ - Type: configapi.RepositoryTypeGit, - Content: configapi.RepositoryContentPackage, - Git: &configapi.GitRepository{ - Repo: repo, - Branch: "main", - Directory: directory, - }, - }, - }) - - t.Cleanup(func() { - t.DeleteL(ctx, &configapi.Repository{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: t.namespace, - }, - }) - }) - - // Make sure the repository is ready before we test to (hopefully) - // avoid flakiness. - t.waitUntilRepositoryReady(ctx, name, t.namespace) -} - -type repositoryOption func(*configapi.Repository) - -func (t *PorchSuite) registerMainGitRepositoryF(ctx context.Context, name string, opts ...repositoryOption) { - repoID := t.namespace + "-" + name - config := t.GitConfig(repoID) - - var secret string - // Create auth secret if necessary - if config.Username != "" || config.Password != "" { - secret = fmt.Sprintf("%s-auth", name) - immutable := true - t.CreateF(ctx, &coreapi.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secret, - Namespace: t.namespace, - }, - Immutable: &immutable, - Data: map[string][]byte{ - "username": []byte(config.Username), - "password": []byte(config.Password), - }, - Type: coreapi.SecretTypeBasicAuth, - }) - - t.Cleanup(func() { - t.DeleteE(ctx, &coreapi.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secret, - Namespace: t.namespace, - }, - }) - }) - } - - repository := &configapi.Repository{ - TypeMeta: metav1.TypeMeta{ - Kind: "Repository", - APIVersion: configapi.GroupVersion.Identifier(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: t.namespace, - }, - Spec: configapi.RepositorySpec{ - Description: "Porch Test Repository Description", - Type: configapi.RepositoryTypeGit, - Content: configapi.RepositoryContentPackage, - Git: &configapi.GitRepository{ - Repo: config.Repo, - Branch: config.Branch, - Directory: config.Directory, - SecretRef: configapi.SecretRef{ - Name: secret, - }, - }, - }, - } - - // Apply options - for _, o := range opts { - o(repository) - } - - // Register repository - t.CreateF(ctx, repository) - - t.Cleanup(func() { - t.DeleteE(ctx, &configapi.Repository{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: t.namespace, - }, - }) - t.waitUntilRepositoryDeleted(ctx, name, t.namespace) - t.waitUntilAllPackagesDeleted(ctx, name) - }) -} - -func withDeployment() repositoryOption { - return func(r *configapi.Repository) { - r.Spec.Deployment = true - } -} - -func withType(t configapi.RepositoryType) repositoryOption { - return func(r *configapi.Repository) { - r.Spec.Type = t - } -} - -func withContent(content configapi.RepositoryContent) repositoryOption { - return func(r *configapi.Repository) { - r.Spec.Content = content - } -} - -// Creates an empty package draft by initializing an empty package -func (t *PorchSuite) createPackageDraftF(ctx context.Context, repository, name, workspace string) *porchapi.PackageRevision { - pr := &porchapi.PackageRevision{ - TypeMeta: metav1.TypeMeta{ - Kind: "PackageRevision", - APIVersion: porchapi.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: t.namespace, - }, - Spec: porchapi.PackageRevisionSpec{ - PackageName: name, - WorkspaceName: porchapi.WorkspaceName(workspace), - RepositoryName: repository, - Tasks: []porchapi.Task{ - { - Type: porchapi.TaskTypeInit, - Init: &porchapi.PackageInitTaskSpec{}, - }, - }, - }, - } - t.CreateF(ctx, pr) - return pr -} - -func (t *PorchSuite) mustExist(ctx context.Context, key client.ObjectKey, obj client.Object) { - t.GetF(ctx, key, obj) - if got, want := obj.GetName(), key.Name; got != want { - t.Errorf("%T.Name: got %q, want %q", obj, got, want) - } - if got, want := obj.GetNamespace(), key.Namespace; got != want { - t.Errorf("%T.Namespace: got %q, want %q", obj, got, want) - } -} - -func (t *PorchSuite) mustNotExist(ctx context.Context, obj client.Object) { - 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): - t.Errorf("Expected NotFound error. got %v", err) - } -} - -// waitUntilRepositoryReady waits for up to 10 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 *PorchSuite) waitUntilRepositoryReady(ctx context.Context, name, namespace string) { - nn := types.NamespacedName{ - Name: name, - Namespace: namespace, - } - var innerErr error - err := wait.PollImmediateWithContext(ctx, time.Second, 10*time.Second, func(ctx context.Context) (bool, error) { - var repo configapi.Repository - if err := t.client.Get(ctx, nn, &repo); err != nil { - innerErr = err - return false, nil - } - for _, c := range repo.Status.Conditions { - if c.Type == configapi.RepositoryReady { - return c.Status == metav1.ConditionTrue, nil - } - } - return false, nil - }) - if err != nil { - t.Errorf("Repository not ready after wait: %v", innerErr) - } - - // While we're using an aggregated apiserver, make sure we can query the generated objects - if err := wait.PollImmediateWithContext(ctx, time.Second, 10*time.Second, func(ctx context.Context) (bool, error) { - var revisions porchapi.PackageRevisionList - if err := t.client.List(ctx, &revisions, client.InNamespace(nn.Namespace)); err != nil { - innerErr = err - return false, nil - } - return true, nil - }); err != nil { - t.Errorf("unable to query PackageRevisions after wait: %v", innerErr) - } - - // Check for functions also (until we move them to CRDs) - if err := wait.PollImmediateWithContext(ctx, time.Second, 10*time.Second, func(ctx context.Context) (bool, error) { - var functions porchapi.FunctionList - if err := t.client.List(ctx, &functions, client.InNamespace(nn.Namespace)); err != nil { - innerErr = err - return false, nil - } - return true, nil - }); err != nil { - t.Errorf("unable to query Functions after wait: %v", innerErr) - } - -} - -func (t *PorchSuite) waitUntilRepositoryDeleted(ctx context.Context, name, namespace string) { - err := wait.PollImmediateWithContext(ctx, time.Second, 20*time.Second, func(ctx context.Context) (done bool, err error) { - var repo configapi.Repository - nn := types.NamespacedName{ - Name: name, - Namespace: namespace, - } - if err := t.client.Get(ctx, nn, &repo); err != nil { - if apierrors.IsNotFound(err) { - return true, nil - } - return false, nil - } - return false, nil - }) - if err != nil { - t.Fatalf("Repository %s/%s not deleted", namespace, name) - } -} - -func (t *PorchSuite) waitUntilAllPackagesDeleted(ctx context.Context, repoName string) { - err := wait.PollImmediateWithContext(ctx, time.Second, 60*time.Second, func(ctx context.Context) (done bool, err error) { - var pkgRevList porchapi.PackageRevisionList - 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 strings.HasPrefix(fmt.Sprintf("%s-", pkgRev.Name), repoName) { - t.Logf("Found package %s from repo %s", pkgRev.Name, repoName) - return false, nil - } - } - - var internalPkgRevList internalapi.PackageRevList - if err := t.client.List(ctx, &internalPkgRevList); err != nil { - t.Logf("error list internal packages: %v", err) - return false, nil - } - for _, internalPkgRev := range internalPkgRevList.Items { - if strings.HasPrefix(fmt.Sprintf("%s-", internalPkgRev.Name), repoName) { - t.Logf("Found internalPkg %s from repo %s", internalPkgRev.Name, repoName) - return false, nil - } - } - return true, nil - }) - if err != nil { - t.Fatalf("Packages from repo %s still remains", repoName) - } -} - -func (t *PorchSuite) waitUntilObjectDeleted(ctx context.Context, gvk schema.GroupVersionKind, namespacedName types.NamespacedName, d time.Duration) { - var innerErr error - err := wait.PollImmediateWithContext(ctx, time.Second, d, func(ctx context.Context) (bool, error) { - var u unstructured.Unstructured - u.SetGroupVersionKind(gvk) - if err := t.client.Get(ctx, namespacedName, &u); err != nil { - if apierrors.IsNotFound(err) { - return true, nil - } - innerErr = err - return false, err - } - return false, nil - }) - if err != nil { - t.Errorf("Object %s not deleted after %s: %v", namespacedName.String(), d.String(), innerErr) - } -} - -func (t *PorchSuite) waitUntilMainBranchPackageRevisionExists(ctx context.Context, pkgName string) { - err := wait.PollImmediateWithContext(ctx, time.Second, 120*time.Second, func(ctx context.Context) (done bool, err error) { - var pkgRevList porchapi.PackageRevisionList - if err := t.client.List(ctx, &pkgRevList); err != nil { - t.Logf("error listing packages: %v", err) - return false, nil - } - for _, pkgRev := range pkgRevList.Items { - pkgName := pkgRev.Spec.PackageName - pkgRevision := pkgRev.Spec.Revision - if pkgRevision == "main" && - pkgName == pkgRev.Spec.PackageName { - return true, nil - } - } - return false, nil - }) - if err != nil { - t.Fatalf("Main branch package revision for %s not found", pkgName) - } -} diff --git a/test/e2e/e2e_test_utils.go b/test/e2e/e2e_test_utils.go new file mode 100644 index 00000000..1786b2e4 --- /dev/null +++ b/test/e2e/e2e_test_utils.go @@ -0,0 +1,354 @@ +// Copyright 2024 The 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 ( + "context" + "fmt" + "time" + "strings" + "github.com/google/go-cmp/cmp" + porchapi "github.com/nephio-project/porch/api/porch/v1alpha1" + configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1" + internalapi "github.com/nephio-project/porch/internal/api/porchinternal/v1alpha1" + 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/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func (t *TestSuite) validateFinalizers(ctx context.Context, name string, finalizers []string) { + var pr porchapi.PackageRevision + t.GetF(ctx, client.ObjectKey{ + Namespace: t.namespace, + Name: name, + }, &pr) + + if len(finalizers) != len(pr.Finalizers) { + diff := cmp.Diff(finalizers, pr.Finalizers) + t.Errorf("Expected %d finalizers, but got %s", len(finalizers), diff) + } + + for _, finalizer := range finalizers { + var found bool + for _, f := range pr.Finalizers { + if f == finalizer { + found = true + } + } + if !found { + t.Errorf("Expected finalizer %v, but didn't find it", finalizer) + } + } +} + +func (t *TestSuite) validateOwnerReferences(ctx context.Context, name string, ownerRefs []metav1.OwnerReference) { + var pr porchapi.PackageRevision + t.GetF(ctx, client.ObjectKey{ + Namespace: t.namespace, + Name: name, + }, &pr) + + if len(ownerRefs) != len(pr.OwnerReferences) { + diff := cmp.Diff(ownerRefs, pr.OwnerReferences) + t.Errorf("Expected %d ownerReferences, but got %s", len(ownerRefs), diff) + } + + for _, ownerRef := range ownerRefs { + var found bool + for _, or := range pr.OwnerReferences { + if or == ownerRef { + found = true + } + } + if !found { + t.Errorf("Expected ownerRef %v, but didn't find it", ownerRef) + } + } +} + +func (t *TestSuite) validateLabelsAndAnnos(ctx context.Context, name string, labels, annos map[string]string) { + var pr porchapi.PackageRevision + t.GetF(ctx, client.ObjectKey{ + Namespace: t.namespace, + Name: name, + }, &pr) + + actualLabels := pr.ObjectMeta.Labels + actualAnnos := pr.ObjectMeta.Annotations + + // Make this check to handle empty vs nil maps + if !(len(labels) == 0 && len(actualLabels) == 0) { + if diff := cmp.Diff(actualLabels, labels); diff != "" { + t.Errorf("Unexpected result (-want, +got): %s", diff) + } + } + + if !(len(annos) == 0 && len(actualAnnos) == 0) { + if diff := cmp.Diff(actualAnnos, annos); diff != "" { + t.Errorf("Unexpected result (-want, +got): %s", diff) + } + } +} + +func (t *TestSuite) registerGitRepositoryF(ctx context.Context, repo, name, directory string) { + t.CreateF(ctx, &configapi.Repository{ + TypeMeta: metav1.TypeMeta{ + Kind: "Repository", + APIVersion: configapi.GroupVersion.Identifier(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: t.namespace, + }, + Spec: configapi.RepositorySpec{ + Type: configapi.RepositoryTypeGit, + Content: configapi.RepositoryContentPackage, + Git: &configapi.GitRepository{ + Repo: repo, + Branch: "main", + Directory: directory, + }, + }, + }) + + t.Cleanup(func() { + t.DeleteL(ctx, &configapi.Repository{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: t.namespace, + }, + }) + }) + + // Make sure the repository is ready before we test to (hopefully) + // avoid flakiness. + t.waitUntilRepositoryReady(ctx, name, t.namespace) +} + +type repositoryOption func(*configapi.Repository) + +func withDeployment() repositoryOption { + return func(r *configapi.Repository) { + r.Spec.Deployment = true + } +} + +func withType(t configapi.RepositoryType) repositoryOption { + return func(r *configapi.Repository) { + r.Spec.Type = t + } +} + +func withContent(content configapi.RepositoryContent) repositoryOption { + return func(r *configapi.Repository) { + r.Spec.Content = content + } +} + +// Creates an empty package draft by initializing an empty package +func (t *TestSuite) createPackageDraftF(ctx context.Context, repository, name, workspace string) *porchapi.PackageRevision { + pr := &porchapi.PackageRevision{ + TypeMeta: metav1.TypeMeta{ + Kind: "PackageRevision", + APIVersion: porchapi.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: t.namespace, + }, + Spec: porchapi.PackageRevisionSpec{ + PackageName: name, + WorkspaceName: porchapi.WorkspaceName(workspace), + RepositoryName: repository, + Tasks: []porchapi.Task{ + { + Type: porchapi.TaskTypeInit, + Init: &porchapi.PackageInitTaskSpec{}, + }, + }, + }, + } + t.CreateF(ctx, pr) + return pr +} + +func (t *TestSuite) mustExist(ctx context.Context, key client.ObjectKey, obj client.Object) { + t.GetF(ctx, key, obj) + if got, want := obj.GetName(), key.Name; got != want { + t.Errorf("%T.Name: got %q, want %q", obj, got, want) + } + if got, want := obj.GetNamespace(), key.Namespace; got != want { + t.Errorf("%T.Namespace: got %q, want %q", obj, got, want) + } +} + +func (t *TestSuite) mustNotExist(ctx context.Context, obj client.Object) { + 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): + t.Errorf("Expected NotFound error. got %v", err) + } +} + +// waitUntilRepositoryReady waits for up to 10 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) { + nn := types.NamespacedName{ + Name: name, + Namespace: namespace, + } + var innerErr error + err := wait.PollImmediateWithContext(ctx, time.Second, 10*time.Second, func(ctx context.Context) (bool, error) { + var repo configapi.Repository + if err := t.client.Get(ctx, nn, &repo); err != nil { + innerErr = err + return false, nil + } + for _, c := range repo.Status.Conditions { + if c.Type == configapi.RepositoryReady { + return c.Status == metav1.ConditionTrue, nil + } + } + return false, nil + }) + if err != nil { + t.Errorf("Repository not ready after wait: %v", innerErr) + } + + // While we're using an aggregated apiserver, make sure we can query the generated objects + if err := wait.PollImmediateWithContext(ctx, time.Second, 10*time.Second, func(ctx context.Context) (bool, error) { + var revisions porchapi.PackageRevisionList + if err := t.client.List(ctx, &revisions, client.InNamespace(nn.Namespace)); err != nil { + innerErr = err + return false, nil + } + return true, nil + }); err != nil { + t.Errorf("unable to query PackageRevisions after wait: %v", innerErr) + } + + // Check for functions also (until we move them to CRDs) + if err := wait.PollImmediateWithContext(ctx, time.Second, 10*time.Second, func(ctx context.Context) (bool, error) { + var functions porchapi.FunctionList + if err := t.client.List(ctx, &functions, client.InNamespace(nn.Namespace)); err != nil { + innerErr = err + return false, nil + } + return true, nil + }); err != nil { + t.Errorf("unable to query Functions after wait: %v", innerErr) + } + +} + +func (t *TestSuite) waitUntilRepositoryDeleted(ctx context.Context, name, namespace string) { + err := wait.PollImmediateWithContext(ctx, time.Second, 20*time.Second, func(ctx context.Context) (done bool, err error) { + var repo configapi.Repository + nn := types.NamespacedName{ + Name: name, + Namespace: namespace, + } + if err := t.client.Get(ctx, nn, &repo); err != nil { + if apierrors.IsNotFound(err) { + return true, nil + } + return false, nil + } + return false, nil + }) + if err != nil { + t.Fatalf("Repository %s/%s not deleted", namespace, name) + } +} + +func (t *TestSuite) waitUntilAllPackagesDeleted(ctx context.Context, repoName string) { + err := wait.PollImmediateWithContext(ctx, time.Second, 60*time.Second, func(ctx context.Context) (done bool, err error) { + var pkgRevList porchapi.PackageRevisionList + 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 strings.HasPrefix(fmt.Sprintf("%s-", pkgRev.Name), repoName) { + t.Logf("Found package %s from repo %s", pkgRev.Name, repoName) + return false, nil + } + } + + var internalPkgRevList internalapi.PackageRevList + if err := t.client.List(ctx, &internalPkgRevList); err != nil { + t.Logf("error list internal packages: %v", err) + return false, nil + } + for _, internalPkgRev := range internalPkgRevList.Items { + if strings.HasPrefix(fmt.Sprintf("%s-", internalPkgRev.Name), repoName) { + t.Logf("Found internalPkg %s from repo %s", internalPkgRev.Name, repoName) + return false, nil + } + } + return true, nil + }) + if err != nil { + t.Fatalf("Packages from repo %s still remains", repoName) + } +} + +func (t *TestSuite) waitUntilObjectDeleted(ctx context.Context, gvk schema.GroupVersionKind, namespacedName types.NamespacedName, d time.Duration) { + var innerErr error + err := wait.PollImmediateWithContext(ctx, time.Second, d, func(ctx context.Context) (bool, error) { + var u unstructured.Unstructured + u.SetGroupVersionKind(gvk) + if err := t.client.Get(ctx, namespacedName, &u); err != nil { + if apierrors.IsNotFound(err) { + return true, nil + } + innerErr = err + return false, err + } + return false, nil + }) + if err != nil { + t.Errorf("Object %s not deleted after %s: %v", namespacedName.String(), d.String(), innerErr) + } +} + +func (t *TestSuite) waitUntilMainBranchPackageRevisionExists(ctx context.Context, pkgName string) { + err := wait.PollImmediateWithContext(ctx, time.Second, 120*time.Second, func(ctx context.Context) (done bool, err error) { + var pkgRevList porchapi.PackageRevisionList + if err := t.client.List(ctx, &pkgRevList); err != nil { + t.Logf("error listing packages: %v", err) + return false, nil + } + for _, pkgRev := range pkgRevList.Items { + pkgName := pkgRev.Spec.PackageName + pkgRevision := pkgRev.Spec.Revision + if pkgRevision == "main" && + pkgName == pkgRev.Spec.PackageName { + return true, nil + } + } + return false, nil + }) + if err != nil { + t.Fatalf("Main branch package revision for %s not found", pkgName) + } +}