From c4a91bec2aa9cb6ac5f77baa886e613a119ba0a5 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Mon, 6 Oct 2025 12:15:09 +0200 Subject: [PATCH 01/14] Bump kcp-dev/multicluster-provider to latest main Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- go.mod | 3 +-- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 87499ce8c..c73e32aa1 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.24.0 replace ( github.com/kube-bind/kube-bind => ./ github.com/kube-bind/kube-bind/cli => ./cli - github.com/kube-bind/kube-bind/contrib => ./contrib github.com/kube-bind/kube-bind/sdk => ./sdk ) @@ -20,7 +19,7 @@ require ( github.com/kcp-dev/code-generator/v3 v3.0.0-20250707080944-4094fb87e20f github.com/kcp-dev/kcp/pkg/apis v0.11.0 github.com/kcp-dev/kcp/sdk v0.28.1 - github.com/kcp-dev/multicluster-provider v0.1.0 + github.com/kcp-dev/multicluster-provider v0.2.1-0.20251002133408-9a8d21dc2872 github.com/kube-bind/kube-bind/cli v0.0.0-20250515145715-d9f20e7c840d github.com/kube-bind/kube-bind/sdk v0.4.1 github.com/martinlindhe/base36 v1.1.1 diff --git a/go.sum b/go.sum index 20c8be3f6..e3387965e 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/kcp-dev/kcp/sdk v0.28.1 h1:bTtuHVjFRjbwFEqXTPxc1J1JP2Hc3mTYqQ2xfJsi16 github.com/kcp-dev/kcp/sdk v0.28.1/go.mod h1:8oZpWxkoMu2TDpx5DgdIGDigByKHKkeqVMA4GiWneoI= github.com/kcp-dev/logicalcluster/v3 v3.0.5 h1:JbYakokb+5Uinz09oTXomSUJVQsqfxEvU4RyHUYxHOU= github.com/kcp-dev/logicalcluster/v3 v3.0.5/go.mod h1:EWBUBxdr49fUB1cLMO4nOdBWmYifLbP1LfoL20KkXYY= -github.com/kcp-dev/multicluster-provider v0.1.0 h1:LS4z4d6AbsYg7Lj9Hlmkbv1M+ZIyw4laNpSsUgF3tRI= -github.com/kcp-dev/multicluster-provider v0.1.0/go.mod h1:8a53s17AhgsEq5mL7VDHZ30eflhu7sFS0isHG1zRz0Y= +github.com/kcp-dev/multicluster-provider v0.2.1-0.20251002133408-9a8d21dc2872 h1:8lXF2+AwZL1BpWTUVwO/32lzzQ8ggTPSlHlN3Mm9qCA= +github.com/kcp-dev/multicluster-provider v0.2.1-0.20251002133408-9a8d21dc2872/go.mod h1:sDD7nNBNjZLE43280MkDiU7djR+0Ru4N5lAw+b4KQvM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -278,8 +278,8 @@ go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qq go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/goleak v1.3.1-0.20241121203838-4ff5fa6529ee h1:uOMbcH1Dmxv45VkkpZQYoerZFeDncWpjbN7ATiQOO7c= +go.uber.org/goleak v1.3.1-0.20241121203838-4ff5fa6529ee/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= From e9d6e5221badf7abcfa32c609b06605454e308bf Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Mon, 6 Oct 2025 11:00:25 +0200 Subject: [PATCH 02/14] Return generated id and secret in CreateDexClient Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- test/e2e/framework/backend.go | 9 +++------ test/e2e/framework/dex.go | 14 ++++++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/test/e2e/framework/backend.go b/test/e2e/framework/backend.go index 01878587d..06736b5e9 100644 --- a/test/e2e/framework/backend.go +++ b/test/e2e/framework/backend.go @@ -40,8 +40,6 @@ func StartBackend(t *testing.T, clientConfig *rest.Config, args ...string) (net. require.NotEmpty(t, signingKey, "error creating signing key") return StartBackendWithoutDefaultArgs(t, clientConfig, append([]string{ - "--oidc-issuer-client-secret=ZXhhbXBsZS1hcHAtc2VjcmV0", - "--oidc-issuer-client-id=kube-bind", "--oidc-issuer-url=http://127.0.0.1:5556/dex", "--cookie-signing-key=" + base64.StdEncoding.EncodeToString(signingKey), }, args...)...) @@ -74,11 +72,10 @@ func StartBackendWithoutDefaultArgs(t *testing.T, clientConfig *rest.Config, arg opts.Serve.Listener, err = net.Listen("tcp", "localhost:0") require.NoError(t, err) addr := opts.Serve.Listener.Addr() - _, port, err := net.SplitHostPort(addr.String()) - require.NoError(t, err) - opts.OIDC.IssuerClientID = "kube-bind-" + port - CreateDexClient(t, addr) + dexId, dexSecret := CreateDexClient(t, addr) + opts.OIDC.IssuerClientID = dexId + opts.OIDC.IssuerClientSecret = dexSecret opts.ExtraOptions.TestingSkipNameValidation = true opts.ExtraOptions.SchemaSource = options.CustomResourceDefinitionSource.String() diff --git a/test/e2e/framework/dex.go b/test/e2e/framework/dex.go index 3ea90b4c3..fbc75043c 100644 --- a/test/e2e/framework/dex.go +++ b/test/e2e/framework/dex.go @@ -18,6 +18,7 @@ package framework import ( "context" + "crypto/rand" "fmt" "net" "net/http" @@ -80,7 +81,7 @@ func StartDex(t testing.TB) { t.Log("Dex is ready") } -func CreateDexClient(t testing.TB, addr net.Addr) { +func CreateDexClient(t testing.TB, addr net.Addr) (string, string) { t.Helper() _, port, err := net.SplitHostPort(addr.String()) @@ -90,10 +91,13 @@ func CreateDexClient(t testing.TB, addr net.Addr) { defer conn.Close() client := dexapi.NewDexClient(conn) + secret := rand.Text() + id := "kube-bind-" + port + _, err = client.CreateClient(t.Context(), &dexapi.CreateClientReq{ Client: &dexapi.Client{ - Id: "kube-bind-" + port, - Secret: "ZXhhbXBsZS1hcHAtc2VjcmV0", + Id: id, + Secret: secret, RedirectUris: []string{fmt.Sprintf("http://%s/callback", addr)}, Public: true, Name: "kube-bind on port " + port, @@ -106,7 +110,9 @@ func CreateDexClient(t testing.TB, addr net.Addr) { defer cancel() conn, err := grpc.NewClient("127.0.0.1:5557", grpc.WithTransportCredentials(grpcinsecure.NewCredentials())) require.NoError(t, err) - _, err = dexapi.NewDexClient(conn).DeleteClient(ctx, &dexapi.DeleteClientReq{Id: "kube-bind-" + port}) + _, err = dexapi.NewDexClient(conn).DeleteClient(ctx, &dexapi.DeleteClientReq{Id: id}) require.NoError(t, err) }) + + return id, secret } From 3ce60b8206b1ef86ddf8cd5004c620f8ead597e7 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Mon, 6 Oct 2025 12:56:18 +0200 Subject: [PATCH 03/14] Add cookie encryption key for backend Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- test/e2e/framework/backend.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/e2e/framework/backend.go b/test/e2e/framework/backend.go index 06736b5e9..76b7baea8 100644 --- a/test/e2e/framework/backend.go +++ b/test/e2e/framework/backend.go @@ -38,10 +38,13 @@ import ( func StartBackend(t *testing.T, clientConfig *rest.Config, args ...string) (net.Addr, *backend.Server) { signingKey := securecookie.GenerateRandomKey(32) require.NotEmpty(t, signingKey, "error creating signing key") + encryptionKey := securecookie.GenerateRandomKey(32) + require.NotEmpty(t, encryptionKey, "error creating encryption key") return StartBackendWithoutDefaultArgs(t, clientConfig, append([]string{ "--oidc-issuer-url=http://127.0.0.1:5556/dex", "--cookie-signing-key=" + base64.StdEncoding.EncodeToString(signingKey), + "--cookie-encryption-key=" + base64.StdEncoding.EncodeToString(encryptionKey), }, args...)...) } From c1581e0477cfe35a094cc31d2a9b95f4072f8454 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Sun, 5 Oct 2025 02:28:30 +0200 Subject: [PATCH 04/14] Add framework.ApplyFiles and .ApplyManifests Signed-off-by: Nelo-T. Wallus --- go.mod | 2 +- test/e2e/framework/apply.go | 121 ++++++++++++++++++++++++++++++++++ test/e2e/framework/clients.go | 10 +-- 3 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 test/e2e/framework/apply.go diff --git a/go.mod b/go.mod index c73e32aa1..dd61a9704 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/vmihailenco/msgpack/v4 v4.3.12 golang.org/x/oauth2 v0.29.0 google.golang.org/grpc v1.70.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.33.3 k8s.io/apiextensions-apiserver v0.33.3 k8s.io/apimachinery v0.33.3 @@ -149,7 +150,6 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect rsc.io/qr v0.2.0 // indirect diff --git a/test/e2e/framework/apply.go b/test/e2e/framework/apply.go new file mode 100644 index 000000000..9795e384f --- /dev/null +++ b/test/e2e/framework/apply.go @@ -0,0 +1,121 @@ +/* +Copyright 2025 The Kube Bind 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 framework + +import ( + "errors" + "io" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/discovery/cached/memory" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" +) + +func ignoreEof(err error) error { + if errors.Is(err, io.EOF) { + return nil + } + return err +} + +// ApplyFiles reads the given file paths and applies the manifests +// contained in them to the cluster specified by config. +// This is equivalent to `kubectl apply -f `. +func ApplyFiles(t testing.TB, config *rest.Config, filePaths ...string) { + t.Helper() + t.Logf("Reading manifests from files: %v", filePaths) + + manifests := []any{} + for _, filePath := range filePaths { + f, err := os.Open(filePath) + require.NoError(t, err, "Failed to open file %s", filePath) + defer f.Close() + + dec := yaml.NewDecoder(f) + for { + var node yaml.Node + err := dec.Decode(&node) + require.NoError(t, ignoreEof(err), "Failed to decode YAML from file %s", filePath) + if errors.Is(err, io.EOF) { + break + } + manifests = append(manifests, node) + } + } + + ApplyManifest(t, config, manifests...) +} + +// ApplyManifest applies the given manifests to the cluster specified by config. +func ApplyManifest(t testing.TB, config *rest.Config, manifests ...any) { + t.Helper() + t.Logf("Applying %d manifests", len(manifests)) + + dynamicClient := DynamicClient(t, config) + discoveryClient := DiscoveryClient(t, config) + mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(discoveryClient)) + + for _, manifest := range manifests { + var u unstructured.Unstructured + + switch m := manifest.(type) { + case unstructured.Unstructured: + u = m + case string: + obj := map[string]any{} + err := yaml.Unmarshal([]byte(m), &obj) + require.NoError(t, err, "Failed to unmarshal manifest") + u.Object = obj + case yaml.Node: + obj := map[string]any{} + err := m.Decode(&obj) + require.NoError(t, err, "Failed to decode YAML node") + u.Object = obj + default: + require.Fail(t, "Unsupported manifest type %T", m) + } + + gvk := u.GroupVersionKind() + + t.Logf("Creating resource %s/%s of type %s", u.GetNamespace(), u.GetName(), gvk.String()) + require.Eventually(t, func() bool { + t.Logf("Finding REST mapping for GVK %#v", gvk) + mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version) + if err != nil { + t.Logf("Error finding REST mapping for GVK %s: %v", gvk.String(), err) + return false + } + + _, err = dynamicClient.Resource(mapping.Resource). + Namespace(u.GetNamespace()). + Apply(t.Context(), u.GetName(), &u, metav1.ApplyOptions{FieldManager: "kube-bind-test-framework"}) + if err != nil { + t.Logf("Error creating resource %s/%s of type %s: %v", u.GetNamespace(), u.GetName(), gvk.String(), err) + return false + } + return true + }, wait.ForeverTestTimeout, time.Millisecond*100) + } +} diff --git a/test/e2e/framework/clients.go b/test/e2e/framework/clients.go index ad8d18df9..8ec5cbb2b 100644 --- a/test/e2e/framework/clients.go +++ b/test/e2e/framework/clients.go @@ -28,31 +28,31 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -func DynamicClient(t *testing.T, config *rest.Config) dynamic.Interface { +func DynamicClient(t testing.TB, config *rest.Config) dynamic.Interface { c, err := dynamic.NewForConfig(config) require.NoError(t, err) return c } -func KubeClient(t *testing.T, config *rest.Config) kubernetes.Interface { +func KubeClient(t testing.TB, config *rest.Config) kubernetes.Interface { c, err := kubernetes.NewForConfig(config) require.NoError(t, err) return c } -func ApiextensionsClient(t *testing.T, config *rest.Config) apiextensionsclient.Interface { +func ApiextensionsClient(t testing.TB, config *rest.Config) apiextensionsclient.Interface { c, err := apiextensionsclient.NewForConfig(config) require.NoError(t, err) return c } -func DiscoveryClient(t *testing.T, config *rest.Config) discovery.DiscoveryInterface { +func DiscoveryClient(t testing.TB, config *rest.Config) discovery.DiscoveryInterface { c, err := discovery.NewDiscoveryClientForConfig(config) require.NoError(t, err) return c } -func NewRESTConfig(t *testing.T, kubeconfig string) *rest.Config { +func NewRESTConfig(t testing.TB, kubeconfig string) *rest.Config { config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) require.NoError(t, err, "Failed to build config from kubeconfig file") return config From 3b94015d7298760068e138eaba3ebdc5f83a00d1 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Mon, 6 Oct 2025 14:02:07 +0200 Subject: [PATCH 05/14] Update framework.StartBackend and split CRD installation Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- contrib/kcp/go.mod | 4 ++++ test/e2e/bind/happy-case_test.go | 5 +++- test/e2e/framework/backend.go | 40 ++++++++++++++++++-------------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/contrib/kcp/go.mod b/contrib/kcp/go.mod index 85fe9522f..685d4f426 100644 --- a/contrib/kcp/go.mod +++ b/contrib/kcp/go.mod @@ -9,6 +9,7 @@ replace ( ) require ( + github.com/headzoo/surf v1.0.1 github.com/kcp-dev/client-go v0.0.0-20250728134101-0355faa9361b github.com/kcp-dev/kcp v0.28.1 github.com/kcp-dev/kcp/sdk v0.28.1 @@ -49,6 +50,9 @@ require ( github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/test/e2e/bind/happy-case_test.go b/test/e2e/bind/happy-case_test.go index f9412a510..2bcebe56b 100644 --- a/test/e2e/bind/happy-case_test.go +++ b/test/e2e/bind/happy-case_test.go @@ -64,8 +64,11 @@ func testHappyCase(t *testing.T, resourceScope apiextensionsv1.ResourceScope, in t.Logf("Creating provider workspace") providerConfig, providerKubeconfig := framework.NewWorkspace(t, framework.ClientConfig(t), framework.WithGenerateName("test-happy-case-provider")) + t.Logf("Installing kubebind CRDs") + framework.InstallKubebindCRDs(t, providerConfig) + t.Logf("Starting backend with random port") - addr, _ := framework.StartBackend(t, providerConfig, "--kubeconfig="+providerKubeconfig, "--listen-address=:0", "--consumer-scope="+string(informerScope)) + addr, _ := framework.StartBackend(t, "--kubeconfig="+providerKubeconfig, "--listen-address=:0", "--consumer-scope="+string(informerScope)) t.Logf("Creating CRD on provider side") providerfixtures.Bootstrap(t, framework.DiscoveryClient(t, providerConfig), framework.DynamicClient(t, providerConfig), nil) diff --git a/test/e2e/framework/backend.go b/test/e2e/framework/backend.go index 76b7baea8..a29d26cce 100644 --- a/test/e2e/framework/backend.go +++ b/test/e2e/framework/backend.go @@ -35,39 +35,41 @@ import ( kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2" ) -func StartBackend(t *testing.T, clientConfig *rest.Config, args ...string) (net.Addr, *backend.Server) { +func InstallKubebindCRDs(t testing.TB, clientConfig *rest.Config) { + crdClient, err := apiextensionsclient.NewForConfig(clientConfig) + require.NoError(t, err) + err = crd.Create(t.Context(), + crdClient.ApiextensionsV1().CustomResourceDefinitions(), + metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "clusterbindings"}, + metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "apiserviceexports"}, + metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "apiservicenamespaces"}, + metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "apiserviceexportrequests"}, + metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "boundschemas"}, + ) + require.NoError(t, err) +} + +func StartBackend(t testing.TB, args ...string) (net.Addr, *backend.Server) { signingKey := securecookie.GenerateRandomKey(32) require.NotEmpty(t, signingKey, "error creating signing key") encryptionKey := securecookie.GenerateRandomKey(32) require.NotEmpty(t, encryptionKey, "error creating encryption key") - return StartBackendWithoutDefaultArgs(t, clientConfig, append([]string{ + return StartBackendWithoutDefaultArgs(t, append([]string{ "--oidc-issuer-url=http://127.0.0.1:5556/dex", "--cookie-signing-key=" + base64.StdEncoding.EncodeToString(signingKey), "--cookie-encryption-key=" + base64.StdEncoding.EncodeToString(encryptionKey), }, args...)...) } -func StartBackendWithoutDefaultArgs(t *testing.T, clientConfig *rest.Config, args ...string) (net.Addr, *backend.Server) { +func StartBackendWithoutDefaultArgs(t testing.TB, args ...string) (net.Addr, *backend.Server) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - crdClient, err := apiextensionsclient.NewForConfig(clientConfig) - require.NoError(t, err) - err = crd.Create(ctx, - crdClient.ApiextensionsV1().CustomResourceDefinitions(), - metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "clusterbindings"}, - metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "apiserviceexports"}, - metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "apiservicenamespaces"}, - metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "apiserviceexportrequests"}, - metav1.GroupResource{Group: kubebindv1alpha2.GroupName, Resource: "boundschemas"}, - ) - require.NoError(t, err) - fs := pflag.NewFlagSet("backend", pflag.ContinueOnError) opts := options.NewOptions() opts.AddFlags(fs) - err = fs.Parse(args) + err := fs.Parse(args) require.NoError(t, err) // use a random port via an explicit listener. Then add a kube-bind- client to dex @@ -79,12 +81,16 @@ func StartBackendWithoutDefaultArgs(t *testing.T, clientConfig *rest.Config, arg dexId, dexSecret := CreateDexClient(t, addr) opts.OIDC.IssuerClientID = dexId opts.OIDC.IssuerClientSecret = dexSecret + opts.OIDC.CallbackURL = "http://" + addr.String() + "/callback" + // Skip name conflict validation - when run in-process with multiple + // controllers they all will register the same metric names, which + // causes subsequent controllers to fail. opts.ExtraOptions.TestingSkipNameValidation = true - opts.ExtraOptions.SchemaSource = options.CustomResourceDefinitionSource.String() completed, err := opts.Complete() require.NoError(t, err) + require.NoError(t, completed.Validate()) config, err := backend.NewConfig(completed) require.NoError(t, err) From e194c60f3c8939c03c8c77aaff7e9e62be49478e Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Sun, 5 Oct 2025 02:38:13 +0200 Subject: [PATCH 06/14] Add logging config for controller-runtime Signed-off-by: Nelo-T. Wallus --- test/e2e/framework/logs.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/e2e/framework/logs.go b/test/e2e/framework/logs.go index 1007cadae..6a26c3089 100644 --- a/test/e2e/framework/logs.go +++ b/test/e2e/framework/logs.go @@ -22,6 +22,8 @@ import ( "github.com/spf13/pflag" "k8s.io/component-base/logs" logsv1 "k8s.io/component-base/logs/api/v1" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" ) func init() { @@ -34,4 +36,8 @@ func init() { if err := logsv1.ValidateAndApply(logs, nil); err != nil { panic(err) } + + // prevent warnings from controller-runtime about loggers not being + // configured + log.SetLogger(zap.New(zap.UseDevMode(true))) } From a1c91ca16b4ff93073457c4d6ba1b6227dabdc66 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Thu, 2 Oct 2025 12:05:06 +0200 Subject: [PATCH 07/14] Add dex as dependency for kcp contrib tests Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index df0607eb0..9f066a2a1 100644 --- a/Makefile +++ b/Makefile @@ -286,7 +286,7 @@ CONTRIBS_E2E := $(patsubst %,test-e2e-contrib-%,$(CONTRIBS)) .PHONY: test-e2e-contribs $(CONTRIBS_E2E) test-e2e-contribs: $(CONTRIBS_E2E) ## Run e2e tests for external integrations -test-e2e-contrib-kcp: build $(KCP) +test-e2e-contrib-kcp: build $(DEX) $(KCP) $(CONTRIBS_E2E): cd contrib/$(patsubst test-e2e-contrib-%,%,$@) && $(GO_TEST) -race -count $(COUNT) -p $(E2E_PARALLELISM) -parallel $(E2E_PARALLELISM) ./test/e2e/... From 31b627000afef03c39bade64d73b09560f3940fc Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Wed, 1 Oct 2025 17:20:52 +0200 Subject: [PATCH 08/14] Add WriteKubeconfig Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- test/e2e/framework/kubeconfig.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/e2e/framework/kubeconfig.go b/test/e2e/framework/kubeconfig.go index 41ecaf785..f555bbc47 100644 --- a/test/e2e/framework/kubeconfig.go +++ b/test/e2e/framework/kubeconfig.go @@ -18,6 +18,7 @@ package framework import ( "os" + "path/filepath" "testing" "github.com/stretchr/testify/require" @@ -61,3 +62,11 @@ func RestToKubeconfig(config *rest.Config, namespace string) clientcmdapi.Config }, } } + +func WriteKubeconfig(t testing.TB, config clientcmdapi.Config, name string) string { + t.Helper() + p := filepath.Join(t.TempDir(), name) + err := clientcmd.WriteToFile(config, p) + require.NoError(t, err, "error writing kubeconfig to %q", p) + return p +} From 84c95c060f314d0d056c0431faf42a93465f7f03 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Wed, 1 Oct 2025 15:28:16 +0200 Subject: [PATCH 09/14] Bump kcp to v0.28.3 Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9f066a2a1..226781f7c 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ CODE_GENERATOR_BIN := code-generator CODE_GENERATOR := $(TOOLS_GOBIN_DIR)/$(CODE_GENERATOR_BIN)-$(CODE_GENERATOR_VER) export CODE_GENERATOR # so hack scripts can use it -KCP_VER := v0.28.0 +KCP_VER := v0.28.3 KCP_BIN := kcp KCP := $(TOOLS_GOBIN_DIR)/$(KCP_BIN)-$(KCP_VER) KCP_CMD ?= $(KCP) From c2fbf5e0cfdaacf41db1c5b4e4c143ccb61c7d7f Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Mon, 22 Sep 2025 08:01:46 +0200 Subject: [PATCH 10/14] Pin kcp to a functional sdk/testing commit Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- contrib/kcp/go.mod | 44 ++++++++++++++++++- contrib/kcp/go.sum | 105 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 141 insertions(+), 8 deletions(-) diff --git a/contrib/kcp/go.mod b/contrib/kcp/go.mod index 685d4f426..ea57ebf6d 100644 --- a/contrib/kcp/go.mod +++ b/contrib/kcp/go.mod @@ -8,38 +8,58 @@ replace ( github.com/kube-bind/kube-bind/sdk => ../../sdk ) +// kcp pinned to a commit on main as sdk/testing requires +// features not in the latest release +// Can use versioned when v0.28.2 releases +replace github.com/kcp-dev/kcp/sdk => github.com/kcp-dev/kcp/sdk v0.28.1-0.20251003164010-742ce0ea6b8c + require ( github.com/headzoo/surf v1.0.1 github.com/kcp-dev/client-go v0.0.0-20250728134101-0355faa9361b - github.com/kcp-dev/kcp v0.28.1 + github.com/kcp-dev/kcp v0.28.3 github.com/kcp-dev/kcp/sdk v0.28.1 github.com/kcp-dev/logicalcluster/v3 v3.0.5 + github.com/kube-bind/kube-bind v0.0.0-00010101000000-000000000000 + github.com/kube-bind/kube-bind/sdk v0.4.1 github.com/spf13/pflag v1.0.7 + github.com/stretchr/testify v1.10.0 k8s.io/apiextensions-apiserver v0.33.3 k8s.io/apimachinery v0.33.3 k8s.io/apiserver v0.33.3 + k8s.io/cli-runtime v0.32.0 k8s.io/client-go v0.33.3 k8s.io/component-base v0.33.3 k8s.io/klog/v2 v2.130.1 + sigs.k8s.io/yaml v1.4.0 ) require ( cel.dev/expr v0.19.1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect + github.com/PuerkitoBio/goquery v1.8.0 // indirect + github.com/andybalholm/cascadia v1.3.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/coreos/go-oidc/v3 v3.15.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dexidp/dex/api/v2 v2.3.0 // indirect + github.com/egymgmbh/go-prefix-writer v0.0.0-20180609083313-7326ea162eca // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect @@ -49,6 +69,7 @@ require ( github.com/google/cel-go v0.23.2 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/securecookie v1.1.1 // indirect @@ -59,20 +80,33 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20250728122101-adbf20db3e51 // indirect + github.com/kcp-dev/kcp/pkg/apis v0.11.0 // indirect + github.com/kcp-dev/multicluster-provider v0.2.1-0.20251002133408-9a8d21dc2872 // indirect + github.com/kube-bind/kube-bind/cli v0.0.0-20250515145715-d9f20e7c840d // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.9.0 // indirect + github.com/martinlindhe/base36 v1.1.1 // indirect + github.com/mdp/qrterminal/v3 v3.2.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/gomega v1.36.2 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/cobra v1.9.1 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect + github.com/vmihailenco/tagparser v0.1.1 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/xlab/treeprint v1.2.0 // indirect go.etcd.io/etcd/api/v3 v3.5.21 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.21 // indirect go.etcd.io/etcd/client/v3 v3.5.21 // indirect @@ -97,6 +131,8 @@ require ( golang.org/x/term v0.32.0 // indirect golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.11.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect google.golang.org/grpc v1.70.0 // indirect @@ -107,11 +143,15 @@ require ( k8s.io/api v0.33.3 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect + rsc.io/qr v0.2.0 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect + sigs.k8s.io/controller-runtime v0.21.0 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + sigs.k8s.io/kustomize/api v0.19.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect + sigs.k8s.io/multicluster-runtime v0.21.0-alpha.9.0.20251002124257-36facc7fbe82 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect ) replace ( diff --git a/contrib/kcp/go.sum b/contrib/kcp/go.sum index 7c4beb29f..d4f0c2406 100644 --- a/contrib/kcp/go.sum +++ b/contrib/kcp/go.sum @@ -1,7 +1,13 @@ cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= +github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= +github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= +github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -12,25 +18,41 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg= +github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dexidp/dex/api/v2 v2.3.0 h1:gv79YqTBTGU6z/QE3RNFzlS6KrPRUudVUN8o858gpCc= +github.com/dexidp/dex/api/v2 v2.3.0/go.mod h1:y9As69T4WZOERCS/CfB9D8Dbb12tafU9ywv2ZZLf4EI= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/egymgmbh/go-prefix-writer v0.0.0-20180609083313-7326ea162eca h1:7oodhZp9MZW0DBkrZXyUsJWKQFy35SVxjZ8K4vHXnk8= +github.com/egymgmbh/go-prefix-writer v0.0.0-20180609083313-7326ea162eca/go.mod h1:UhMFM+dnOcm1f0Pve8uqRaxAhEYki+/CuA2BTDp2T04= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= +github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -51,6 +73,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= @@ -63,12 +87,22 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= @@ -77,6 +111,10 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ= +github.com/headzoo/surf v1.0.1 h1:wk3+LT8gjnCxEwfBJl6MhaNg154En5KjgmgzAG9uMS0= +github.com/headzoo/surf v1.0.1/go.mod h1:/bct0m/iMNEqpn520y01yoaWxsAEigGFPnvyR1ewR5M= +github.com/headzoo/ut v0.0.0-20181013193318-a13b5a7a02ca h1:utFgFwgxaqx5OthzE3DSGrtOq7rox5r2sxZ2wbfTuK0= +github.com/headzoo/ut v0.0.0-20181013193318-a13b5a7a02ca/go.mod h1:8926sG02TCOX4RFRzIMFIzRw4xuc/TwO2gtN7teMJZ4= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= @@ -89,10 +127,12 @@ github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20250728122101-adbf20db3e51 h1:l38RD github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20250728122101-adbf20db3e51/go.mod h1:rF1jfvUfPjFXs+HV/LN1BtPzAz1bfjJOwVa+hAVfroQ= github.com/kcp-dev/client-go v0.0.0-20250728134101-0355faa9361b h1:2LGrXvY9sc4l5yjKIbMZ86GEou7NyrHhA4qBPaeFfxs= github.com/kcp-dev/client-go v0.0.0-20250728134101-0355faa9361b/go.mod h1:QdO8AaGAZPr/rIZ1iVanCM3tUOiiuX897GWv7WTByLE= -github.com/kcp-dev/kcp v0.28.1 h1:T7Ky7u9hvprkGrBnKuw0QZoP8O6TCbXqJz2Kwt6Tx+o= -github.com/kcp-dev/kcp v0.28.1/go.mod h1:q28Fx8sU/KA8kz8HGwtaqA7Iom8oR90ydoPK39jMaxo= -github.com/kcp-dev/kcp/sdk v0.28.1 h1:bTtuHVjFRjbwFEqXTPxc1J1JP2Hc3mTYqQ2xfJsi16M= -github.com/kcp-dev/kcp/sdk v0.28.1/go.mod h1:8oZpWxkoMu2TDpx5DgdIGDigByKHKkeqVMA4GiWneoI= +github.com/kcp-dev/kcp v0.28.3 h1:KUpPTt3xjbUgPCsi4EmLtOyCHEJtCU/Sm17H+MtkQFw= +github.com/kcp-dev/kcp v0.28.3/go.mod h1:9Ww0Ex8K0hBuIVpOFX9WsnDbuxtiiNPRgz5ReJyhqUo= +github.com/kcp-dev/kcp/pkg/apis v0.11.0 h1:K6p+tNHNcvfACCPLcHgY0EMLeaIwR1jS491FyLfXMII= +github.com/kcp-dev/kcp/pkg/apis v0.11.0/go.mod h1:8cUAmfMJcksauz53UtsLYG8Phhx62rvuCnd/5t/Zihk= +github.com/kcp-dev/kcp/sdk v0.28.1-0.20251003164010-742ce0ea6b8c h1:6XXxfEOmm7teNQuHnkUi7AEhPyFFPGs77wOKmzUumhI= +github.com/kcp-dev/kcp/sdk v0.28.1-0.20251003164010-742ce0ea6b8c/go.mod h1:aC2BPGPvy8QtkI2gQNH9NfW6xpfGIKZkR93gy9O02BE= github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20250816165010-ffe1d7c8649b h1:CyQuxPfhWg8KdwfmY5aE6KABsh/QhkDXTH2msezxCFY= github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20250816165010-ffe1d7c8649b/go.mod h1:uiagPCm7MlCfQpIm2xwPTRf8727wbCZCMgHI9uHcMpg= github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20250816165010-ffe1d7c8649b h1:5SZfhKCAqq6lu+tOOf33CDGhSty9guXc90CAfQXk3bw= @@ -101,6 +141,8 @@ github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20250816165 github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20250816165010-ffe1d7c8649b/go.mod h1:6XMZJoNYwuMArBvS2acFkTR1KqyHSp2QXRLRx9eTk5w= github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20250816165010-ffe1d7c8649b h1:C21pLvKT2MUE38+ZNDXeucEbRdb7rewRpBp4C5lzz6M= github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20250816165010-ffe1d7c8649b/go.mod h1:STCgTiD+xCCHsfLOPHn5sNVsyktakX/ctW3dMv3erh0= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cli-runtime v0.0.0-20250816165010-ffe1d7c8649b h1:sQ7otAwO/YMn5cFt4Fftzt6P0cz2gmW7OnTx2i1Qfis= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cli-runtime v0.0.0-20250816165010-ffe1d7c8649b/go.mod h1:m/w/xSuYRnkX3ocgdBKM/TX7B8aNzScl6qCp1Z3XIGw= github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20250816165010-ffe1d7c8649b h1:kEieYK/XCUycPf5DCEUZNPvDVHr4ao+rxZvdOQXlMQk= github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20250816165010-ffe1d7c8649b/go.mod h1:omt22adyHpxAelVTfG1bssg+xoAUc+Cg+0CXn0Oaim0= github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20250816165010-ffe1d7c8649b h1:OazHpbyl1+WvViAUEZw2PxMZNrd5LOPDD+bhnfL5cQM= @@ -109,29 +151,46 @@ github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20250816165010-ffe1d github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20250816165010-ffe1d7c8649b/go.mod h1:kwWmCq21qHjyuqQeIaShb3CUcfgX89fWTgjEbDle9XM= github.com/kcp-dev/logicalcluster/v3 v3.0.5 h1:JbYakokb+5Uinz09oTXomSUJVQsqfxEvU4RyHUYxHOU= github.com/kcp-dev/logicalcluster/v3 v3.0.5/go.mod h1:EWBUBxdr49fUB1cLMO4nOdBWmYifLbP1LfoL20KkXYY= +github.com/kcp-dev/multicluster-provider v0.2.1-0.20251002133408-9a8d21dc2872 h1:8lXF2+AwZL1BpWTUVwO/32lzzQ8ggTPSlHlN3Mm9qCA= +github.com/kcp-dev/multicluster-provider v0.2.1-0.20251002133408-9a8d21dc2872/go.mod h1:sDD7nNBNjZLE43280MkDiU7djR+0Ru4N5lAw+b4KQvM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/martinlindhe/base36 v1.1.1 h1:1F1MZ5MGghBXDZ2KJ3QfxmiydlWOGB8HCEtkap5NkVg= +github.com/martinlindhe/base36 v1.1.1/go.mod h1:vMS8PaZ5e/jV9LwFKlm0YLnXl/hpOihiBxKkIoc3g08= +github.com/mdp/qrterminal/v3 v3.2.0 h1:qteQMXO3oyTK4IHwj2mWsKYYRBOp1Pj2WRYFYYNTCdk= +github.com/mdp/qrterminal/v3 v3.2.0/go.mod h1:XGGuua4Lefrl7TLEsSONiD+UEjQXJZ4mPzF+gWYIJkk= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM= github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -148,6 +207,8 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= @@ -165,6 +226,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -172,10 +234,16 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= @@ -216,8 +284,8 @@ go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qq go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/goleak v1.3.1-0.20241121203838-4ff5fa6529ee h1:uOMbcH1Dmxv45VkkpZQYoerZFeDncWpjbN7ATiQOO7c= +go.uber.org/goleak v1.3.1-0.20241121203838-4ff5fa6529ee/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= @@ -232,9 +300,12 @@ golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5N golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= @@ -247,12 +318,18 @@ golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= @@ -267,6 +344,11 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= @@ -278,6 +360,7 @@ google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40Rmc google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= @@ -295,10 +378,20 @@ k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUy k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= +rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ= +sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o= +sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA= +sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY= +sigs.k8s.io/multicluster-runtime v0.21.0-alpha.9.0.20251002124257-36facc7fbe82 h1:RRenMVUCSVrxcLb5y84IrF4q6dxcaBW/xfq3rxkdhwM= +sigs.k8s.io/multicluster-runtime v0.21.0-alpha.9.0.20251002124257-36facc7fbe82/go.mod h1:CpBzLMLQKdm+UCchd2FiGPiDdCxM5dgCCPKuaQ6Fsv0= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= From 10e1c462db3fa2ef47494ea4957aaafb1282e860 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Wed, 1 Oct 2025 15:09:11 +0200 Subject: [PATCH 11/14] Update kcp readme Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- contrib/kcp/README.md | 86 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/contrib/kcp/README.md b/contrib/kcp/README.md index 4dfdb3c0a..efc4ab65e 100644 --- a/contrib/kcp/README.md +++ b/contrib/kcp/README.md @@ -23,9 +23,23 @@ It will do the following: # How to run +## Preparation + 1. Start dex + +```bash +make run-dex +``` + 2. Start kcp -3. Bootstrap kcp: + +```bash +make run-kcp +``` + +## Backend + +2. Bootstrap kcp: ```bash cp .kcp/admin.kubeconfig .kcp/backend.kubeconfig export KUBECONFIG=.kcp/backend.kubeconfig @@ -49,18 +63,22 @@ k ws use :root:kube-bind --schema-source apiresourceschemas ``` +This process will keep running, so open a new terminal. + +## Provider 5. Copy the kubeconfig to the provider and create provider workspace: ```bash cp .kcp/admin.kubeconfig .kcp/provider.kubeconfig export KUBECONFIG=.kcp/provider.kubeconfig k ws use :root -kubectl ws create provider --enter +kubectl create-workspace provider --enter ``` 6. Bind the APIExport to the provider workspace ```bash -kubectl kcp bind apiexport root:kube-bind:kube-bind.io --accept-permission-claim clusterrolebindings.rbac.authorization.k8s.io \ +kubectl kcp bind apiexport root:kube-bind:kube-bind.io \ + --accept-permission-claim clusterrolebindings.rbac.authorization.k8s.io \ --accept-permission-claim clusterroles.rbac.authorization.k8s.io \ --accept-permission-claim customresourcedefinitions.apiextensions.k8s.io \ --accept-permission-claim serviceaccounts.core \ @@ -77,7 +95,6 @@ kubectl kcp bind apiexport root:kube-bind:kube-bind.io --accept-permission-claim kubectl create -f contrib/kcp/deploy/examples/apiexport.yaml kubectl create -f contrib/kcp/deploy/examples/apiresourceschema-cowboys.yaml kubectl create -f contrib/kcp/deploy/examples/apiresourceschema-sheriffs.yaml -# recursive bind kubectl kcp bind apiexport root:provider:cowboys-stable ``` @@ -86,9 +103,11 @@ kubectl kcp bind apiexport root:provider:cowboys-stable ```bash kubectl get logicalcluster # NAME PHASE URL AGE -# cluster Ready https://192.168.2.166:6443/clusters/1d5vpxvdpy0opbj1 +# cluster Ready https://192.168.2.166:6443/clusters/1d5vpxvdpy0opbj1 ``` +## Consumer + 9. Now we gonna initiate consumer: ```bash cp .kcp/admin.kubeconfig .kcp/consumer.kubeconfig @@ -104,15 +123,24 @@ kubectl ws create consumer --enter # Extract secret for binding process. Note that secret name is not the same as output from command above. Check secret # name by running `kubectl get secret -n kube-bind` -kubectl get secret kubeconfig-hxwlc -n kube-bind -o jsonpath='{.data.kubeconfig}' | base64 -d > remote.kubeconfig +kubectl get secrets -n kube-bind -o jsonpath='{.items[0].data.kubeconfig}' | base64 -d > remote.kubeconfig -./bin/kubectl-bind apiservice --remote-kubeconfig remote.kubeconfig -f contrib/kcp/deploy/examples/apiserviceexport-namespaced.yaml --skip-konnector --remote-namespace kube-bind-697cb +namespace=$(yq '.contexts[0].context.namespace' remote.kubeconfig) -export KUBECONFIG=.kcp/consumer.kubeconfig -go run ./cmd/konnector/ --lease-namespace default +./bin/kubectl-bind apiservice -v 6 --remote-kubeconfig remote.kubeconfig -f apiserviceexport.yaml --skip-konnector --remote-namespace "$namespace" +``` + +This will keep running, so switch to a new terminal. +### Consumer Konnector -11. (Optional) Add second consumer to test +Start konnector: + +```bash +./bin/konnector --lease-namespace default --kubeconfig .kcp/consumer.kubeconfig +``` + +Optionally add second consumer to test ```bash cp .kcp/admin.kubeconfig .kcp/consumer2.kubeconfig @@ -121,27 +149,53 @@ kubectl ws use :root kubectl ws create consumer2 --enter ./bin/kubectl-bind http://127.0.0.1:8080/clusters/2vgrh380y0cq38du/exports --dry-run -o yaml > apiserviceexport2.yaml -kubectl get secret kubeconfig-wvvsb -n kube-bind -o jsonpath='{.data.kubeconfig}' | base64 -d > remote2.kubeconfig -./bin/kubectl-bind apiservice --remote-kubeconfig remote2.kubeconfig -f apiserviceexport.yaml --skip-konnector --remote-namespace kube-bind-m5zx4 +kubectl get secrets -n kube-bind -o jsonpath='{.items[0].data.kubeconfig}' | base64 -d > remote2.kubeconfig +./bin/kubectl-bind apiservice -v 6 --remote-kubeconfig remote2.kubeconfig -f apiserviceexport2.yaml --skip-konnector --remote-namespace "$(yq '.contexts[0].context.namespace' remote2.kubeconfig)" -export KUBECONFIG=.kcp/consumer2.kubeconfig -go run ./cmd/konnector/ --lease-namespace default --server-address :8091 +./bin/konnector --lease-namespace default --kubeconfig .kcp/consumer2.kubeconfig --server-address :8091 ``` +This will keep running, so switch to a new terminal. + +## Testing + Create objects: ``` +export KUBECONFIG=.kcp/consumer.kubeconfig kubectl apply -f contrib/kcp/deploy/examples/cowboy.yaml +kubectl apply -f contrib/kcp/deploy/examples/sheriff.yaml ``` ## Debug ```bash + cp .kcp/admin.kubeconfig .kcp/debug.kubeconfig export KUBECONFIG=.kcp/debug.kubeconfig k ws use :root:kube-bind -k -s "$(kubectl get apiexportendpointslice kube-bind.io -o jsonpath="{.status.endpoints[0].url}")/clusters/*" api-resources -k -s "$(kubectl get apiexportendpointslice kube-bind.io -o jsonpath="{.status.endpoints[0].url}")/clusters/*" get crd +k -s "$(kubectl get apiexportendpointslice kube-bind.io -o jsonpath='{.status.endpoints[0].url}')/clusters/*" api-resources + +k -s "$(kubectl get apiexportendpointslice kube-bind.io -o jsonpath='{.status.endpoints[0].url}')/clusters/*" get crd + +namespace=$(yq '.contexts[0].context.namespace' remote.kubeconfig) + +# some claimed objects + +kubectl create cm provider -n "$namespace-default" +kubectl label cm provider app=wildwest -n "$namespace-default" + +kubectl create cm consumer -n default +kubectl label cm consumer app=wildwest -n default + +kubectl create secret generic provider-secret +kubectl label secret provider-secret app=wildwest + +kubectl create namespace bob +kubectl create secret generic wildwest-secrets1 -n bob +kubectl label secret wildwest-secrets1 app=wildwest -n bob + +``` From 4e46da2cba947ddbeae459870fb720f0b267279a Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Wed, 1 Oct 2025 15:09:03 +0200 Subject: [PATCH 12/14] Add kcp e2e test Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- contrib/kcp/test/e2e/backend.go | 88 ++++++++++++ contrib/kcp/test/e2e/browser.go | 99 +++++++++++++ contrib/kcp/test/e2e/kcp.go | 154 +++++++++++++++++++++ contrib/kcp/test/e2e/kcp_test.go | 229 ++++++++++++++++++++++++++++++- 4 files changed, 567 insertions(+), 3 deletions(-) create mode 100644 contrib/kcp/test/e2e/backend.go create mode 100644 contrib/kcp/test/e2e/browser.go create mode 100644 contrib/kcp/test/e2e/kcp.go diff --git a/contrib/kcp/test/e2e/backend.go b/contrib/kcp/test/e2e/backend.go new file mode 100644 index 000000000..163a292a5 --- /dev/null +++ b/contrib/kcp/test/e2e/backend.go @@ -0,0 +1,88 @@ +/* +Copyright 2025 The Kube Bind 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 ( + "fmt" + "net/http" + "testing" + "time" + + kcpclientset "github.com/kcp-dev/kcp/sdk/client/clientset/versioned/cluster" + kcptestinghelpers "github.com/kcp-dev/kcp/sdk/testing/helpers" + kcptestingserver "github.com/kcp-dev/kcp/sdk/testing/server" + "github.com/kcp-dev/logicalcluster/v3" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + + kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2" + "github.com/kube-bind/kube-bind/test/e2e/framework" +) + +func bootstrapBackend(t *testing.T, server kcptestingserver.RunningServer, scope kubebindv1alpha2.InformerScope) string { + t.Helper() + t.Log("Bootstrapping backend") + + client, err := kcpclientset.NewForConfig(server.BaseConfig(t)) + require.NoError(t, err) + + exportUrl := "" + kcptestinghelpers.Eventually(t, func() (bool, string) { + exportES, err := client.Cluster(logicalcluster.NewPath("root").Join("kube-bind")). + ApisV1alpha1(). + APIExportEndpointSlices(). + Get(t.Context(), "kube-bind.io", metav1.GetOptions{}) + if err != nil { + return false, fmt.Sprintf("Error getting APIExportEndpointSlice: %v", err) + } + if len(exportES.Status.APIExportEndpoints) == 0 { + return false, "APIExportEndpoints is empty" + } + exportUrl = exportES.Status.APIExportEndpoints[0].URL + return true, "" + }, wait.ForeverTestTimeout, time.Millisecond*100) + require.NotEmpty(t, exportUrl, "APIExportEndpointSlice URL is empty") + + _, backendKubeconfig := wsConfig(t, server, logicalcluster.NewPath("root").Join("kube-bind")) + + t.Log("Starting kube-bind backend for KCP") + addr, _ := framework.StartBackend(t, + "--kubeconfig="+backendKubeconfig, + "--multicluster-runtime-provider=kcp", + "--server-url="+exportUrl, + "--pretty-name=BigCorp.com", + "--namespace-prefix=kube-bind-", + "--schema-source=apiresourceschemas", + "--consumer-scope="+string(scope), + ) + + t.Log("Wait for backend to be ready") + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://"+addr.String()+"/healthz", nil) + require.NoError(t, err) + kcptestinghelpers.Eventually(t, func() (bool, string) { + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false, "" + } + defer resp.Body.Close() + return resp.StatusCode == http.StatusOK, "" + }, wait.ForeverTestTimeout, time.Millisecond*100) + t.Log("Backend is ready") + + return addr.String() +} diff --git a/contrib/kcp/test/e2e/browser.go b/contrib/kcp/test/e2e/browser.go new file mode 100644 index 000000000..b3d09749e --- /dev/null +++ b/contrib/kcp/test/e2e/browser.go @@ -0,0 +1,99 @@ +/* +Copyright 2025 The Kube Bind 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" + "testing" + "time" + + "github.com/headzoo/surf" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/rest" + "sigs.k8s.io/yaml" + + "github.com/kube-bind/kube-bind/test/e2e/framework" +) + +func performBindingWithBrowser(t *testing.T, backendAddr string, clusterID string, consumerCfg *rest.Config, consumerKubeconfig, resource string) { + bindURL := fmt.Sprintf("http://%s/clusters/%s/exports", backendAddr, clusterID) + t.Logf("Bind URL: %s", bindURL) + + // Test binding dry run first (similar to happy-case test) + t.Run("Service is bound dry run", func(t *testing.T) { + authURLDryRunCh := make(chan string, 1) + go simulateKCPBrowser(t, authURLDryRunCh, resource) + + iostreams, _, bufOut, _ := genericclioptions.NewTestIOStreams() + framework.Bind(t, iostreams, authURLDryRunCh, nil, bindURL, "--kubeconfig", consumerKubeconfig, "--dry-run") + _, err := yaml.YAMLToJSON(bufOut.Bytes()) + require.NoError(t, err, "Generated output is not valid YAML") + }) + + // Perform actual binding (similar to happy-case test) + t.Run("Service is bound", func(t *testing.T) { + authURLCh := make(chan string, 1) + go simulateKCPBrowser(t, authURLCh, resource) + + iostreams, _, _, _ := genericclioptions.NewTestIOStreams() + invocations := make(chan framework.SubCommandInvocation, 1) + framework.Bind(t, iostreams, authURLCh, invocations, bindURL, "--kubeconfig", consumerKubeconfig) + inv := <-invocations + + inv.Args = append( + inv.Args, + "--kubeconfig="+consumerKubeconfig, + "--skip-konnector=true", + "--no-banner", + "-f=-", // api service export from stdin + ) + + framework.BindAPIService(t, inv.Stdin, "", inv.Args...) + + // Wait for CRD to be created on consumer side + t.Logf("Waiting for %s CRD to be created on consumer side", resource) + crdClient := framework.ApiextensionsClient(t, consumerCfg).ApiextensionsV1().CustomResourceDefinitions() + require.Eventually(t, func() bool { + _, err := crdClient.Get(context.Background(), resource+".wildwest.dev", metav1.GetOptions{}) + return err == nil + }, wait.ForeverTestTimeout, time.Millisecond*100) + }) +} + +// simulateKCPBrowser simulates browser interaction for KCP binding. +func simulateKCPBrowser(t *testing.T, authURLCh chan string, resource string) { + browser := surf.NewBrowser() + authURL := <-authURLCh + + t.Logf("Browsing to auth URL: %s", authURL) + err := browser.Open(authURL) + require.NoError(t, err, "Failed to open auth URL") + + t.Logf("Waiting for browser to be at /resources") + framework.BrowserEventuallyAtPath(t, browser, "/resources") + + t.Logf("Clicking %s resource", resource) + err = browser.Click("a." + resource) + require.NoError(t, err, "Failed to click resource link") + + t.Logf("Waiting for browser to be forwarded to client") + framework.BrowserEventuallyAtPath(t, browser, "/callback") +} diff --git a/contrib/kcp/test/e2e/kcp.go b/contrib/kcp/test/e2e/kcp.go new file mode 100644 index 000000000..bd4182cf6 --- /dev/null +++ b/contrib/kcp/test/e2e/kcp.go @@ -0,0 +1,154 @@ +/* +Copyright 2025 The Kube Bind 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 ( + "fmt" + "strings" + "testing" + "time" + + kcpapisv1alpha2 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha2" + kcpclientset "github.com/kcp-dev/kcp/sdk/client/clientset/versioned/cluster" + kcptestinghelpers "github.com/kcp-dev/kcp/sdk/testing/helpers" + kcptestingserver "github.com/kcp-dev/kcp/sdk/testing/server" + "github.com/kcp-dev/logicalcluster/v3" + "github.com/spf13/pflag" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/rest" + + "github.com/kube-bind/kube-bind/contrib/kcp/bootstrap" + "github.com/kube-bind/kube-bind/contrib/kcp/bootstrap/options" + "github.com/kube-bind/kube-bind/test/e2e/framework" +) + +func wsConfig(t testing.TB, server kcptestingserver.RunningServer, workspace logicalcluster.Path) (*rest.Config, string) { + cfg := server.BaseConfig(t) + cfg.Host += "/clusters/" + workspace.String() + + kubeconfig := framework.WriteKubeconfig(t, + framework.RestToKubeconfig(cfg, "default"), + workspace.Base()+".kubeconfig", + ) + + return cfg, kubeconfig +} + +func generateApiBinding(t testing.TB, path logicalcluster.Path, name, exportName string, acceptPermissionClaims ...string) *kcpapisv1alpha2.APIBinding { + t.Helper() + + apiBinding := &kcpapisv1alpha2.APIBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: kcpapisv1alpha2.APIBindingSpec{ + Reference: kcpapisv1alpha2.BindingReference{ + Export: &kcpapisv1alpha2.ExportBindingReference{ + Path: path.String(), + Name: exportName, + }, + }, + }, + } + + for _, claim := range acceptPermissionClaims { + split := strings.SplitN(claim, ".", 2) + require.Len(t, split, 2, "invalid permission claim: %s, must be in the form .", claim) + + resource, group := split[0], split[1] + require.NotEmpty(t, group, "invalid permission claim: %s, group cannot be empty", claim) + require.NotEmpty(t, resource, "invalid permission claim: %s, resource cannot be empty", claim) + + if group == "core" { + group = "" + } + + apiBinding.Spec.PermissionClaims = append( + apiBinding.Spec.PermissionClaims, + kcpapisv1alpha2.AcceptablePermissionClaim{ + State: kcpapisv1alpha2.ClaimAccepted, + ScopedPermissionClaim: kcpapisv1alpha2.ScopedPermissionClaim{ + Selector: kcpapisv1alpha2.PermissionClaimSelector{ + MatchAll: true, + }, + PermissionClaim: kcpapisv1alpha2.PermissionClaim{ + GroupResource: kcpapisv1alpha2.GroupResource{ + Group: group, + Resource: resource, + }, + Verbs: []string{"*"}, + }, + }, + }, + ) + } + + return apiBinding +} + +func createApiBinding(t testing.TB, client *kcpclientset.ClusterClientset, path logicalcluster.Path, apiBinding *kcpapisv1alpha2.APIBinding) *kcpapisv1alpha2.APIBinding { + t.Helper() + + _, err := client.Cluster(path). + ApisV1alpha2(). + APIBindings(). + Create(t.Context(), apiBinding, metav1.CreateOptions{}) + require.NoError(t, err, "Error creating APIBinding") + + var inCluster *kcpapisv1alpha2.APIBinding + kcptestinghelpers.Eventually(t, func() (bool, string) { + var err error + inCluster, err = client. + Cluster(path). + ApisV1alpha2(). + APIBindings(). + Get(t.Context(), apiBinding.Name, metav1.GetOptions{}) + if err != nil { + return false, fmt.Sprintf("Error getting APIBinding: %v", err) + } + return inCluster.Status.Phase == kcpapisv1alpha2.APIBindingPhaseBound, fmt.Sprintf("APIBinding not ready: %#v", inCluster.Status) + }, wait.ForeverTestTimeout, time.Millisecond*100) + return inCluster +} + +func bootstrapKCP(t testing.TB, server kcptestingserver.RunningServer) { + t.Helper() + t.Log("Bootstrapping KCP") + + cfg := server.BaseConfig(t) + cfg.Host += "/clusters/root" + adminApiCfg := framework.RestToKubeconfig(cfg, "default") + adminKubeconfig := framework.WriteKubeconfig(t, adminApiCfg, "admin.kubeconfig") + + fs := pflag.NewFlagSet("kcp-bootstrap", pflag.ContinueOnError) + options := options.NewOptions() + options.AddFlags(fs) + + options.KCPKubeConfig = adminKubeconfig + + completed, err := options.Complete() + require.NoError(t, err) + + config, err := bootstrap.NewConfig(completed) + require.NoError(t, err) + + bootstrapper, err := bootstrap.NewServer(t.Context(), config) + require.NoError(t, err) + require.NoError(t, bootstrapper.Start(t.Context())) +} diff --git a/contrib/kcp/test/e2e/kcp_test.go b/contrib/kcp/test/e2e/kcp_test.go index ab8a4470c..4fc6aa363 100644 --- a/contrib/kcp/test/e2e/kcp_test.go +++ b/contrib/kcp/test/e2e/kcp_test.go @@ -16,8 +16,231 @@ limitations under the License. package e2e -import "testing" +import ( + "fmt" + "strings" + "testing" + "time" -func TestSkip(t *testing.T) { - t.Skip("noop so test-e2e-contribs doesn't fail") + kcpclientset "github.com/kcp-dev/kcp/sdk/client/clientset/versioned/cluster" + kcptesting "github.com/kcp-dev/kcp/sdk/testing" + kcptestinghelpers "github.com/kcp-dev/kcp/sdk/testing/helpers" + "github.com/kcp-dev/logicalcluster/v3" + "github.com/stretchr/testify/require" + 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/util/wait" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/rest" + + kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2" + "github.com/kube-bind/kube-bind/test/e2e/framework" +) + +func TestKCPClusterScope(t *testing.T) { + t.Parallel() + testKcpIntegration(t, kubebindv1alpha2.ClusterScope) +} + +func TestKCPNamespacedScope(t *testing.T) { + t.Parallel() + testKcpIntegration(t, kubebindv1alpha2.NamespacedScope) +} + +func testKcpIntegration(t *testing.T, scope kubebindv1alpha2.InformerScope) { + t.Helper() + t.Logf("Testing KCP integration with informer scope %s, tempdir: %s", scope, t.TempDir()) + + // dex + framework.StartDex(t) + + // kcp bootstrap + server := kcptesting.PrivateKcpServer(t) + bootstrapKCP(t, server) + + // consumer + t.Log("Create consumer workspace") + consumerWsPath, _ := kcptesting.NewWorkspaceFixture(t, server, logicalcluster.NewPath("root"), kcptesting.WithName("consumer")) + + t.Log("Create a consumer kubeconfig") + consumerCfg, consumerKubeconfig := wsConfig(t, server, consumerWsPath) + + t.Log("Start konnector for consumer workspace") + framework.StartKonnector(t, consumerCfg, "--kubeconfig="+consumerKubeconfig) + + // backend + backendAddr := bootstrapBackend(t, server, scope) + + // provider + t.Log("Create provider workspace") + providerWsPath, _ := kcptesting.NewWorkspaceFixture(t, server, logicalcluster.NewPath("root"), kcptesting.WithName("provider")) + + t.Log("Create a provider kubeconfig") + providerCfg, _ := wsConfig(t, server, providerWsPath) + + cfg := server.BaseConfig(t) + kcpClusterClient, err := kcpclientset.NewForConfig(cfg) + require.NoError(t, err, "failed to create kcp client") + + t.Log("Bind kube-bind.io APIExport to provider workspace") + createApiBinding(t, + kcpClusterClient, + providerWsPath, + generateApiBinding(t, logicalcluster.NewPath("root").Join("kube-bind"), "kube-bind.io", "kube-bind.io", + "clusterrolebindings.rbac.authorization.k8s.io", + "clusterroles.rbac.authorization.k8s.io", + "customresourcedefinitions.apiextensions.k8s.io", + "serviceaccounts.core", + "configmaps.core", + "secrets.core", + "namespaces.core", + "roles.rbac.authorization.k8s.io", + "rolebindings.rbac.authorization.k8s.io", + "apiresourceschemas.apis.kcp.io", + ), + ) + + t.Log("Applying example APIExport and APIResourceSchemas to provider workspace") + framework.ApplyFiles(t, + providerCfg, + "../../deploy/examples/apiexport.yaml", + "../../deploy/examples/apiresourceschema-cowboys.yaml", // namespaced + "../../deploy/examples/apiresourceschema-sheriffs.yaml", // cluster scoped + ) + + t.Log("Bind the APIExport locally") + createApiBinding(t, + kcpClusterClient, + providerWsPath, + generateApiBinding(t, providerWsPath, "cowboys-stable", "cowboys-stable"), + ) + + t.Log("Get logical cluster of provider workspace") + providerCluster, err := kcpClusterClient.Cluster(providerWsPath).CoreV1alpha1().LogicalClusters().Get(t.Context(), "cluster", metav1.GetOptions{}) + require.NoError(t, err) + require.NotEmpty(t, providerCluster.Status.URL, "provider cluster URL is empty") + + // .Status.URL should have the form: ://(....)/clusters/, the is what is needed for kube-bind + // The URL overall is irrelevant, so split on `/` and validate that + // at least the tail end is /cluster/"] + require.GreaterOrEqual(t, len(providerClusterSplit), 2, "Unexpected URL format: %s", providerCluster.Status.URL) + // Validate the second last segment is "clusters" as a sanity check + require.Equal(t, "clusters", providerClusterSplit[len(providerClusterSplit)-2], "Unexpected URL format: %s", providerCluster.Status.URL) + // Can assume that the last entry is now the cluster-id, grab it and + // sanity check that it's not empty + providerClusterID := providerClusterSplit[len(providerClusterSplit)-1] + require.NotEmpty(t, providerClusterID, "Retreived cluster id is empty, source URL: %s", providerCluster.Status.URL) + + // kube-bind process + t.Log("Perform binding process with browser") + var kind, resource string + switch scope { + case kubebindv1alpha2.ClusterScope: + kind = "Sheriff" + resource = "sheriffs" + case kubebindv1alpha2.NamespacedScope: + kind = "Cowboy" + resource = "cowboys" + default: + require.Fail(t, "unhandled scope %q", scope) + } + performBindingWithBrowser(t, backendAddr, providerClusterID, consumerCfg, consumerKubeconfig, resource) + + t.Log("Testing resource creation and synchronization...") + testKCPResourceSync(t, consumerCfg, providerCfg, scope, kind, resource) +} + +func testKcpClient(t testing.TB, cfg *rest.Config, scope kubebindv1alpha2.InformerScope, gvr schema.GroupVersionResource, namespace string) dynamic.ResourceInterface { + t.Helper() + + client := framework.DynamicClient(t, cfg).Resource(gvr) + if scope == kubebindv1alpha2.NamespacedScope && namespace != "" { + return client.Namespace(namespace) + } + return client +} + +func testKCPResourceSync(t *testing.T, consumerCfg, providerCfg *rest.Config, scope kubebindv1alpha2.InformerScope, kind, resource string) { + serviceGVR := schema.GroupVersionResource{Group: "wildwest.dev", Version: "v1alpha1", Resource: resource} + + consumerClient := testKcpClient(t, consumerCfg, scope, serviceGVR, "default") + // provider side has a namespace with a random name for the consumer + // so it needs to be a non namespaced client + providerClient := testKcpClient(t, providerCfg, scope, serviceGVR, "") + + t.Run("instance created downstream syncs upstream", func(t *testing.T) { + t.Logf("Creating %s instance on consumer side", kind) + + resourceInstance := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": "wildwest.dev/v1alpha1", + "kind": kind, + "metadata": map[string]any{ + "name": "test-" + resource, + }, + "spec": map[string]any{ + "intent": "draw", + }, + }, + } + + kcptestinghelpers.Eventually(t, func() (bool, string) { + _, err := consumerClient.Create(t.Context(), resourceInstance, metav1.CreateOptions{}) + return err == nil, fmt.Sprintf("Error creating %s instance: %v", resource, err) + }, wait.ForeverTestTimeout, time.Millisecond*100) + + t.Logf("Waiting for %s instance to be synced to provider side", resource) + var instances *unstructured.UnstructuredList + kcptestinghelpers.Eventually(t, func() (bool, string) { + var err error + instances, err = providerClient.List(t.Context(), metav1.ListOptions{}) + return err == nil && len(instances.Items) >= 1, fmt.Sprintf("Error listing %s instances: %v", resource, err) + }, wait.ForeverTestTimeout, time.Millisecond*100) + + require.Equal(t, 1, len(instances.Items), "Expected exactly one %s instance on provider side", resource) + }) + + t.Run("instance spec updated downstream syncs upstream", func(t *testing.T) { + t.Logf("Updating %s spec on consumer side", resource) + + require.Eventually(t, func() bool { + obj, err := consumerClient.Get(t.Context(), "test-"+resource, metav1.GetOptions{}) + if err != nil { + return false + } + + unstructured.SetNestedField(obj.Object, "holster", "spec", "intent") //nolint:errcheck + _, err = consumerClient.Update(t.Context(), obj, metav1.UpdateOptions{}) + return err == nil + }, wait.ForeverTestTimeout, 5*time.Second, "waiting for %s spec to be updated on consumer side", resource) + + t.Logf("Waiting for %s spec update to sync to provider side", resource) + require.Eventually(t, func() bool { + instances, err := providerClient.List(t.Context(), metav1.ListOptions{}) + if err != nil || len(instances.Items) == 0 { + return false + } + + intent, found, err := unstructured.NestedString(instances.Items[0].Object, "spec", "intent") + return err == nil && found && intent == "holster" + }, wait.ForeverTestTimeout, 5*time.Second, "waiting for %s spec update to sync to provider side", resource) + }) + + t.Run("instance deleted downstream is deleted upstream", func(t *testing.T) { + t.Logf("Deleting %s instance on consumer side", resource) + + err := consumerClient.Delete(t.Context(), "test-"+resource, metav1.DeleteOptions{}) + require.NoError(t, err, "Failed to delete %s on consumer side", resource) + + t.Logf("Waiting for %s instance to be deleted on provider side", resource) + require.Eventually(t, func() bool { + instances, err := providerClient.List(t.Context(), metav1.ListOptions{}) + return err == nil && len(instances.Items) == 0 + }, wait.ForeverTestTimeout, 5*time.Second, "waiting for %s instance to be deleted on provider side", resource) + }) } From b7ecb5e6827043826a6d1a7cdd5b416d3a48afba Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Mon, 6 Oct 2025 12:50:58 +0200 Subject: [PATCH 13/14] Just ignore dex on non-linux Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- test/e2e/framework/dex.go | 18 ++++++++++++------ test/e2e/framework/dex_linux.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 test/e2e/framework/dex_linux.go diff --git a/test/e2e/framework/dex.go b/test/e2e/framework/dex.go index fbc75043c..10138164c 100644 --- a/test/e2e/framework/dex.go +++ b/test/e2e/framework/dex.go @@ -40,6 +40,16 @@ import ( var dexOnce sync.Once +var dexKill = func(t testing.TB, cmd *exec.Cmd) { + t.Helper() + // Do nothing. Without a suite setup and teardown we cannot + // determine when to kill dex and Pdeathsig is only supported on + // Linux. + // On macOS (and Windows) the process will continue to run. + // Which per se isn't a problem for development. + cmd.SysProcAttr = &syscall.SysProcAttr{} +} + func StartDex(t testing.TB) { t.Helper() @@ -57,12 +67,8 @@ func StartDex(t testing.TB) { dexConfig, ) - // Ensures that dex is killed when the process ends. - dexCmd.SysProcAttr = &syscall.SysProcAttr{ - Pdeathsig: syscall.SIGKILL, - Setpgid: true, - Pgid: 0, - } + // Set os-dependend killing + dexKill(t, dexCmd) require.NoError(t, dexCmd.Start()) }) diff --git a/test/e2e/framework/dex_linux.go b/test/e2e/framework/dex_linux.go new file mode 100644 index 000000000..29088bd1a --- /dev/null +++ b/test/e2e/framework/dex_linux.go @@ -0,0 +1,32 @@ +/* +Copyright 2025 The Kube Bind 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 framework + +import ( + "os/exec" + "syscall" + "testing" +) + +func init() { + dexKill = func(t testing.TB, cmd *exec.Cmd) { + t.Helper() + cmd.SysProcAttr = &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGKILL, + } + } +} From 72122c604f492c2abfd350b229dece7edcb28579 Mon Sep 17 00:00:00 2001 From: "Nelo-T. Wallus" Date: Mon, 6 Oct 2025 14:02:27 +0200 Subject: [PATCH 14/14] Drop dependency on build step for contrib kcp e2e tests Signed-off-by: Nelo-T. Wallus Signed-off-by: Nelo-T. Wallus --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 226781f7c..ca5229fd0 100644 --- a/Makefile +++ b/Makefile @@ -286,7 +286,7 @@ CONTRIBS_E2E := $(patsubst %,test-e2e-contrib-%,$(CONTRIBS)) .PHONY: test-e2e-contribs $(CONTRIBS_E2E) test-e2e-contribs: $(CONTRIBS_E2E) ## Run e2e tests for external integrations -test-e2e-contrib-kcp: build $(DEX) $(KCP) +test-e2e-contrib-kcp: $(DEX) $(KCP) $(CONTRIBS_E2E): cd contrib/$(patsubst test-e2e-contrib-%,%,$@) && $(GO_TEST) -race -count $(COUNT) -p $(E2E_PARALLELISM) -parallel $(E2E_PARALLELISM) ./test/e2e/...