Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions staging/src/github.com/kcp-dev/sdk/testing/helpers/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
Copyright 2026 The kcp 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 helpers

import (
"testing"

"github.com/stretchr/testify/require"
)

// TolerateErr returns true if the error is tolerated.
type TolerateErr func(error) bool

// TolerateOrFail verifies that the supplied error is not nil, and is
// tolerated by at least one of the TolerateErr functions. In that case,
// true is returned. False is returned if the error is nil.
//
// TolerateOrFail fails the test if err is not nil, and none of the
// TolerateErr functions tolerate the error.
func TolerateOrFail(t *testing.T, err error, tolerateErrFuncs ...TolerateErr) bool {
if err == nil {
return false
}

t.Helper()
for _, tolerateErr := range tolerateErrFuncs {
if tolerateErr(err) {
return true
}
}

require.NoError(t, err)
return false
}
4 changes: 2 additions & 2 deletions test/e2e/authorizer/rootcacertconfigmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
kcpkubernetesclientset "github.com/kcp-dev/client-go/kubernetes"
"github.com/kcp-dev/sdk/apis/core"
kcptesting "github.com/kcp-dev/sdk/testing"
kcptestinghelpers "github.com/kcp-dev/sdk/testing/helpers"

"github.com/kcp-dev/kcp/test/e2e/framework"
)
Expand Down Expand Up @@ -63,10 +64,9 @@ func TestRootCACertConfigmap(t *testing.T) {
t.Log("Waiting for default configmap to be created")
require.Eventually(t, func() bool {
configmap, err := kubeClusterClient.Cluster(wsPath).CoreV1().ConfigMaps(namespace.Name).Get(ctx, DefaultRootCACertConfigmap, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsNotFound) {
return false
}
require.NoError(t, err, "failed to get configmap")

if v, ok := configmap.Data["ca.crt"]; ok {
if len(v) > 0 {
Expand Down
40 changes: 9 additions & 31 deletions test/e2e/virtual/apiexport/authorizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,14 +411,7 @@ metadata:
serviceProvider2AdminApiExportVWCfg := rest.CopyConfig(serviceProvider2Admin)
serviceProvider2AdminClient, err := kcpclientset.NewForConfig(serviceProvider2Admin)
require.NoError(t, err)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := serviceProvider2AdminClient.Cluster(serviceProvider2Path).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "today-cowboys", metav1.GetOptions{})
require.NoError(t, err)
var found bool
serviceProvider2AdminApiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClient, tenantWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
return found, fmt.Sprintf("waiting for virtual workspace URLs to be available: %v", apiExportEndpointSlice.Status.APIExportEndpoints)
}, wait.ForeverTestTimeout, time.Millisecond*100)
serviceProvider2AdminApiExportVWCfg.Host = vwURL(t, serviceProvider2AdminClient, kcpClient, serviceProvider2Path, "today-cowboys", tenantWorkspace, tenantPath)

serviceProvider2DynamicVWClientForTenantWorkspace, err := kcpdynamic.NewForConfig(serviceProvider2AdminApiExportVWCfg)
require.NoError(t, err)
Expand Down Expand Up @@ -481,14 +474,7 @@ metadata:
shadowVWCfg := rest.CopyConfig(serviceProvider2Admin)
shadowVWClient, err := kcpclientset.NewForConfig(serviceProvider2Admin)
require.NoError(t, err)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := shadowVWClient.Cluster(serviceProvider2Path).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "today-cowboys", metav1.GetOptions{})
require.NoError(t, err)
var found bool
shadowVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClient, tenantShadowCRDWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
return found, fmt.Sprintf("waiting for virtual workspace URLs to be available: %v", apiExportEndpointSlice.Status.APIExportEndpoints)
}, wait.ForeverTestTimeout, time.Millisecond*100)
shadowVWCfg.Host = vwURL(t, shadowVWClient, kcpClient, serviceProvider2Path, "today-cowboys", tenantShadowCRDWorkspace, tenantShadowCRDPath)

serviceProvider2DynamicVWClientForShadowTenantWorkspace, err := kcpdynamic.NewForConfig(shadowVWCfg)
require.NoError(t, err)
Expand Down Expand Up @@ -635,17 +621,7 @@ func TestAPIExportBindingAuthorizer(t *testing.T) {
serviceProviderAdminApiExportVWCfg := rest.CopyConfig(serviceProviderAdmin)
serviceProviderAdminClient, err := kcpclientset.NewForConfig(serviceProviderAdmin)
require.NoError(t, err)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := serviceProviderAdminClient.Cluster(serviceProviderPath).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "wild.wild.west", metav1.GetOptions{})
require.NoError(t, err)
var found bool
serviceProviderAdminApiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClient, tenantWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
if !found {
return false, fmt.Sprintf("waiting for virtual workspace URLs to be available: %v", apiExportEndpointSlice.Status.APIExportEndpoints)
}
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100)
serviceProviderAdminApiExportVWCfg.Host = vwURL(t, serviceProviderAdminClient, kcpClient, serviceProviderPath, "wild.wild.west", tenantWorkspace, tenantPath)

serviceProviderDynamicVWClientForTenantWorkspace, err := kcpdynamic.NewForConfig(serviceProviderAdminApiExportVWCfg)
require.NoError(t, err)
Expand Down Expand Up @@ -1041,7 +1017,7 @@ func TestRootAPIExportAuthorizers(t *testing.T) {

t.Logf("Get virtual workspace client for service APIExport in workspace %q", servicePath)
serviceAPIExportVWCfg := framework.StaticTokenUserConfig(providerUser, rest.CopyConfig(cfg))
serviceAPIExportVWCfg.Host = vwURL(t, kcpClient, servicePath, apiExport.Name, userWorkspace, userPath)
serviceAPIExportVWCfg.Host = vwURL(t, kcpClient, kcpClient, servicePath, apiExport.Name, userWorkspace, userPath)
serviceDynamicVWClient, err := kcpdynamic.NewForConfig(serviceAPIExportVWCfg)
require.NoError(t, err)

Expand Down Expand Up @@ -1074,13 +1050,15 @@ func TestRootAPIExportAuthorizers(t *testing.T) {
require.NoError(t, err)
}

func vwURL(t *testing.T, kcpClusterClient kcpclientset.ClusterInterface, path logicalcluster.Path, export string, ws *tenancyv1alpha1.Workspace, wsPath logicalcluster.Path) string {
func vwURL(t *testing.T, providerClusterClient, kcpClusterClient kcpclientset.ClusterInterface, path logicalcluster.Path, export string, ws *tenancyv1alpha1.Workspace, wsPath logicalcluster.Path) string {
t.Helper()

var vwURL string
kcptestinghelpers.Eventually(t, func() (bool, string) {
exportEndpointSlice, err := kcpClusterClient.Cluster(path).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), export, metav1.GetOptions{})
require.NoError(t, err)
exportEndpointSlice, err := providerClusterClient.Cluster(path).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), export, metav1.GetOptions{})
if kcptestinghelpers.TolerateOrFail(t, err) {
return false, err.Error()
}
urls := framework.ExportVirtualWorkspaceURLs(exportEndpointSlice)
var found bool
vwURL, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClusterClient, ws, urls)
Expand Down
18 changes: 12 additions & 6 deletions test/e2e/virtual/apiexport/binding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ func TestBinding(t *testing.T) {
serviceProviderVirtualWorkspaceConfig := rest.CopyConfig(serviceProviderUser)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := kcpClient.Cluster(serviceWorkspacePath).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "api-manager", metav1.GetOptions{})
if err != nil {
return false, fmt.Sprintf("waiting on apiexport to be available %v", err.Error())
if kcptestinghelpers.TolerateOrFail(t, err, kerrors.IsNotFound) {
return false, fmt.Sprintf("waiting on APIExportEndpointSlice to be available %v", err.Error())
}
var found bool
serviceProviderVirtualWorkspaceConfig.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClient, consumerWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
Expand Down Expand Up @@ -310,7 +310,9 @@ func TestAPIBindingPermissionClaimsVerbs(t *testing.T) {
apiExportVWCfg := rest.CopyConfig(cfg)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := kcpClusterClient.Cluster(providerPath).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "today-cowboys", metav1.GetOptions{})
require.NoError(t, err)
if kcptestinghelpers.TolerateOrFail(t, err, kerrors.IsNotFound) {
return false, fmt.Sprintf("waiting on APIExportEndpointSlice to be available %v", err.Error())
}
var found bool
apiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClusterClient, consumerWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
Expand Down Expand Up @@ -489,7 +491,9 @@ func TestAPIBindingPermissionClaimsSSA(t *testing.T) {
apiExportVWCfg := rest.CopyConfig(cfg)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := kcpClusterClient.Cluster(providerPath).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "today-cowboys", metav1.GetOptions{})
require.NoError(t, err)
if kcptestinghelpers.TolerateOrFail(t, err, kerrors.IsNotFound) {
return false, fmt.Sprintf("waiting on APIExportEndpointSlice to be available %v", err.Error())
}
var found bool
apiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClusterClient, consumerWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
Expand Down Expand Up @@ -566,7 +570,7 @@ func TestAPIBindingPermissionClaimsSSA(t *testing.T) {
FieldManager: "test-manager",
FieldValidation: "Ignore",
})
if err != nil {
if kcptestinghelpers.TolerateOrFail(t, err, kerrors.IsConflict, kerrors.IsForbidden) {
return false, err.Error()
}
require.NotNil(t, configMap)
Expand Down Expand Up @@ -661,7 +665,9 @@ func TestAPIBindingPermissionClaimsSelectors(t *testing.T) {
apiExportVWCfg := rest.CopyConfig(cfg)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := kcpClusterClient.Cluster(providerPath).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "today-cowboys", metav1.GetOptions{})
require.NoError(t, err)
if kcptestinghelpers.TolerateOrFail(t, err, kerrors.IsNotFound) {
return false, fmt.Sprintf("waiting on APIExportEndpointSlice to be available %v", err.Error())
}
var found bool
apiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClusterClient, consumerWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
Expand Down
5 changes: 4 additions & 1 deletion test/e2e/virtual/apiexport/openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/stretchr/testify/require"

apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/rest"
Expand Down Expand Up @@ -73,7 +74,9 @@ func TestAPIExportOpenAPI(t *testing.T) {
apiExportVWCfg := rest.CopyConfig(cfg)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := kcpClients.Cluster(serviceProviderPath).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "today-cowboys", metav1.GetOptions{})
require.NoError(t, err)
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsNotFound) {
return false, fmt.Sprintf("waiting on APIExportEndpointSlice to be available %v", err.Error())
}
var found bool
apiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClients, consumerWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
Expand Down
22 changes: 9 additions & 13 deletions test/e2e/virtual/apiexport/virtualworkspace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
apiExportVWCfg := rest.CopyConfig(cfg)
kcptestinghelpers.Eventually(t, func() (bool, string) {
apiExportEndpointSlice, err := kcpClients.Cluster(serviceProviderPath).ApisV1alpha1().APIExportEndpointSlices().Get(t.Context(), "today-cowboys", metav1.GetOptions{})
require.NoError(t, err)
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsNotFound) {
return false, fmt.Sprintf("waiting on APIExportEndpointSlice to be available %v", err.Error())
}
var found bool
apiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(t.Context(), kcpClients, consumerWorkspace, framework.ExportVirtualWorkspaceURLs(apiExportEndpointSlice))
require.NoError(t, err)
Expand Down Expand Up @@ -153,10 +155,9 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
t.Logf("Verify that user-1 can now wildcard list cowboys")
kcptestinghelpers.Eventually(t, func() (bool, string) {
cbs, err := wwUser1VC.WildwestV1alpha1().Cowboys().List(t.Context(), metav1.ListOptions{})
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
require.NoError(t, err)
require.Len(t, cbs.Items, 1, "expected to find exactly one cowboy")
cowboy = &cbs.Items[0]
return true, ""
Expand Down Expand Up @@ -202,10 +203,9 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
_, err = wwUser1VC.Cluster(consumerClusterName.Path()).WildwestV1alpha1().Cowboys(cowboy.Namespace).Update(t.Context(), cowboy, metav1.UpdateOptions{})
return err
})
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
require.NoError(t, err)
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100, "expected user-1 to update cowboys")

Expand All @@ -218,10 +218,9 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
_, err = wwUser1VC.Cluster(consumerClusterName.Path()).WildwestV1alpha1().Cowboys(cowboy.Namespace).UpdateStatus(t.Context(), cowboy, metav1.UpdateOptions{})
return err
})
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
require.NoError(t, err)
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100, "expected user-1 to update status of cowboys")
}
Expand All @@ -238,10 +237,9 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
t.Logf("Verify that user-1 can now create cowboys")
kcptestinghelpers.Eventually(t, func() (bool, string) {
_, err = wwUser1VC.Cluster(consumerClusterName.Path()).WildwestV1alpha1().Cowboys("default").Create(t.Context(), newCowboy("default", "another"), metav1.CreateOptions{})
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
require.NoError(t, err)
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100, "expected user-1 to create a cowboy")
}
Expand All @@ -263,10 +261,9 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
kcptestinghelpers.Eventually(t, func() (bool, string) {
patch := `{"spec":{"intent":"3"}}`
_, err = wwUser1VC.Cluster(consumerClusterName.Path()).WildwestV1alpha1().Cowboys(cowboy.Namespace).Patch(t.Context(), cowboy.Name, types.MergePatchType, []byte(patch), metav1.PatchOptions{})
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
require.NoError(t, err)
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100, "expected user-1 to patch a cowboy")

Expand Down Expand Up @@ -300,10 +297,9 @@ func TestAPIExportVirtualWorkspace(t *testing.T) {
t.Logf("Verify that user-1 can now delete cowboys")
kcptestinghelpers.Eventually(t, func() (bool, string) {
err = wwUser1VC.Cluster(consumerClusterName.Path()).WildwestV1alpha1().Cowboys("default").Delete(t.Context(), "another", metav1.DeleteOptions{})
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
require.NoError(t, err)
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100, "expected user-1 to delete a cowboy")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,11 +382,8 @@ func TestInitializingWorkspacesVirtualWorkspaceAccess(t *testing.T) {
// Because of that, we can't use exactly 30 seconds as timeout, instead we use 1 minute to be on the safe side.
require.Eventually(t, func() bool {
clusters, err := user1VwKcpClusterClients[initializer].CoreV1alpha1().LogicalClusters().List(ctx, metav1.ListOptions{}) // no list options, all filtering is implicit
if err != nil {
if !errors.IsForbidden(err) {
require.NoError(t, err)
}
return false // wait until cr, crb are replicated
if kcptestinghelpers.TolerateOrFail(t, err, errors.IsForbidden) {
return false
}
actual.Items = append(actual.Items, clusters.Items...)
return true
Expand Down
14 changes: 4 additions & 10 deletions test/e2e/virtual/replication/virtualworkspace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,12 @@ func TestCachedResourceVirtualWorkspace(t *testing.T) {
kcptestinghelpers.Eventually(t, func() (bool, string) {
var err error
sherrifList, err = listSheriffs(t.Context(), user1CachedResourceDynClient, consumerClusterName)
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
if len(sherrifList.Items) < 2 {
return false, fmt.Sprintf("waiting until there are two items in list, have %d", len(sherrifList.Items))
}
require.NoError(t, err)
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100, "expected user-1 to list sheriffs")
require.Len(t, sherrifList.Items, 2, "expected to find exactly two sheriffs")
Expand Down Expand Up @@ -321,12 +320,12 @@ func TestCachedResourceVirtualWorkspace(t *testing.T) {
LabelSelector: labels.SelectorFromSet(labels.Set(sheriffLabels)).String(),
ResourceVersion: sherrifList.ResourceVersion, // We want to see only changes to existing sheriffs.
})
if apierrors.IsForbidden(err) {
if kcptestinghelpers.TolerateOrFail(t, err, apierrors.IsForbidden) {
return false, fmt.Sprintf("waiting until rbac cache is primed: %v", err)
}
require.NoError(t, err)
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100, "expected user-1 to watch sheriffs")
defer sheriffWatch.Stop()

sheriffWatchCh := sheriffWatch.ResultChan()
waitForEvent := func() (watch.Event, bool) {
Expand All @@ -343,7 +342,7 @@ func TestCachedResourceVirtualWorkspace(t *testing.T) {
expectedNext, actualNext bool,
inspectObj func(obj *unstructured.Unstructured),
) {
require.Equal(t, expectedNext, actualNext, "unexpected channel state")
require.Equal(t, expectedNext, actualNext, "unexpected channel state with event type %q", actualEvent.Type)
if !expectedNext {
// We don't expect any more events, nothing to check anymore.
return
Expand All @@ -367,11 +366,6 @@ func TestCachedResourceVirtualWorkspace(t *testing.T) {
require.Equal(t, sheriffOne.Name, obj.GetName(), "expected to receive the first sheriff")
require.Equal(t, sheriffLabels, obj.GetLabels(), "expected the sheriff to have labels defined")
})

t.Logf("Verify that stopping the watch works")
sheriffWatch.Stop()
e, next = waitForEvent()
checkEvent(e, watch.Error, false, next, nil)
}
}

Expand Down
Loading