Repository: kptdev/kpt

Issue:
TestE2E/PorchSuite/TestFunctionRepository is flaky
We see regular failures reported from this test and rerunning the test seem to fix it. Examples:

- https://github.com/GoogleContainerTools/kpt/actions/runs/3047195945/jobs/4910914984

Discussion:
@mortent: The fix in #3555 did not seem to work. Got another flake in https://github.com/GoogleContainerTools/kpt/actions/runs/3093856957/jobs/5006671877@mortent: The root cause here seems to be that walking the OCI registry to discover all functions is relatively slow. That means that is the cache is not ready when a request comes in, reading all functions from GCR takes longer than the timeout for a request.@liamfallon: Duplicated to Porch in the Nephio project: https://github.com/nephio-project/nephio/issues/698

Solution patch:
diff --git a/site/guides/porch-user-guide.md b/site/guides/porch-user-guide.md
index 68ec1fb1..53b16c14 100644
--- a/site/guides/porch-user-guide.md
+++ b/site/guides/porch-user-guide.md
@@ -245,8 +245,7 @@ Several commands in the `kpt alpha rpkg` group support package authoring:
 
 * `init` - Initializes a new package revision in the target repository.
 * `clone` - Creates a clone of a source package in the target repository.
-* `edit` - Creates a copy of a source package in the target repository to start
-  creation of a new revision of the package.
+* `copy` - Creates a new package revision from an existing one.
 * `push` - Pushes package resources into a remote package.
 * `del` - Deletes one or more packages in registered repositories.
 
@@ -270,7 +269,7 @@ deployments-c32b851b591b860efda29ba0e006725c8c1f7764  new-package  v1        fal
 ```
 
 The new package is created in the `Draft` lifecycle stage. This is true also for
-all commands that create new package revision (`init`, `clone` and `edit`).
+all commands that create new package revision (`init`, `clone` and `copy`).
 
 Additional flags supported by the `kpt alpha rpkg init` command are:
 



Test patch:
diff --git a/porch/test/e2e/e2e_test.go b/porch/test/e2e/e2e_test.go
index 83173a64..2be126b7 100644
--- a/porch/test/e2e/e2e_test.go
+++ b/porch/test/e2e/e2e_test.go
@@ -33,6 +33,8 @@ import (
 	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/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"
@@ -539,7 +541,7 @@ func (t *PorchSuite) TestUpdateResources(ctx context.Context) {
 }
 
 func (t *PorchSuite) TestFunctionRepository(ctx context.Context) {
-	t.CreateF(ctx, &configapi.Repository{
+	repo := &configapi.Repository{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "function-repository",
 			Namespace: t.namespace,
@@ -552,17 +554,22 @@ func (t *PorchSuite) TestFunctionRepository(ctx context.Context) {
 				Registry: "gcr.io/kpt-fn",
 			},
 		},
-	})
+	}
+	t.CreateF(ctx, repo)
 
 	t.Cleanup(func() {
 		t.DeleteL(ctx, &configapi.Repository{
 			ObjectMeta: metav1.ObjectMeta{
-				Name:      "function-repository",
-				Namespace: t.namespace,
+				Name:      repo.Name,
+				Namespace: repo.Namespace,
 			},
 		})
 	})
 
+	// Make sure the repository is ready before we test to (hopefully)
+	// avoid flakiness.
+	t.waitUntilRepositoryReady(ctx, repo.Name, repo.Namespace)
+
 	list := &porchapi.FunctionList{}
 	t.ListE(ctx, list, client.InNamespace(t.namespace))
 
@@ -1639,3 +1646,29 @@ func (t *PorchSuite) mustNotExist(ctx context.Context, obj client.Object) {
 		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.
+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)
+	}
+}