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
2 changes: 1 addition & 1 deletion contrib/kcp/bootstrap/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func NewConfig(options *options.CompletedOptions) (*Config, error) {
return nil, err
}
config.ClientConfig = rest.CopyConfig(config.ClientConfig)
config.ClientConfig = rest.AddUserAgent(config.ClientConfig, "kube-bind-kcp-init")
config.ClientConfig = rest.AddUserAgent(config.ClientConfig, "kbind-kcp-init")

config.ClientConfig, err = newKCPRestConfig(config.ClientConfig)
if err != nil {
Expand Down
11 changes: 4 additions & 7 deletions contrib/kcp/bootstrap/config/config/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,13 @@ import (
kcpdynamic "github.com/kcp-dev/client-go/dynamic"
confighelpers "github.com/kcp-dev/kcp/config/helpers"
"github.com/kcp-dev/logicalcluster/v3"
"github.com/kcp-dev/sdk/apis/core"
kcpclientset "github.com/kcp-dev/sdk/client/clientset/versioned/cluster"
"k8s.io/apimachinery/pkg/util/sets"
)

//go:embed *.yaml
var fs embed.FS

// RootClusterName is the workspace to host common faros APIs.
var RootClusterName = logicalcluster.NewPath("root")

// Bootstrap creates resources in this package by continuously retrying the list.
// This is blocking, i.e. it only returns (with error) when the context is closed or with nil when
// the bootstrapping is successfully completed.
Expand All @@ -44,8 +40,9 @@ func Bootstrap(
apiExtensionClusterClient kcpapiextensionsclientset.ClusterInterface,
dynamicClusterClient kcpdynamic.ClusterInterface,
batteriesIncluded sets.Set[string],
baseWorkspace logicalcluster.Path,
) error {
rootDiscoveryClient := apiExtensionClusterClient.Cluster(core.RootCluster.Path()).Discovery()
rootDynamicClient := dynamicClusterClient.Cluster(core.RootCluster.Path())
return confighelpers.Bootstrap(ctx, rootDiscoveryClient, rootDynamicClient, batteriesIncluded, fs)
discoveryClient := apiExtensionClusterClient.Cluster(baseWorkspace).Discovery()
dynamicClient := dynamicClusterClient.Cluster(baseWorkspace)
return confighelpers.Bootstrap(ctx, discoveryClient, dynamicClient, batteriesIncluded, fs)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ metadata:
bootstrap.kcp.io/create-only: "true"
spec:
type:
name: organization
name: universal
path: root
14 changes: 5 additions & 9 deletions contrib/kcp/bootstrap/config/core/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ import (
"github.com/kube-bind/kube-bind/contrib/kcp/bootstrap/config/core/resources"
)

var (
// KubeBindRootClusterName is the workspace to host common APIs.
KubeBindRootClusterName = logicalcluster.NewPath("root:kube-bind")
)

// Bootstrap creates resources in this package by continuously retrying the list.
// This is blocking, i.e. it only returns (with error) when the context is closed or with nil when
// the bootstrapping is successfully completed.
Expand All @@ -42,10 +37,11 @@ func Bootstrap(
apiExtensionClusterClient kcpapiextensionsclientset.ClusterInterface,
dynamicClusterClient kcpdynamic.ClusterInterface,
batteriesIncluded sets.Set[string],
baseWorkspace logicalcluster.Path,
) error {
computeDiscoveryClient := apiExtensionClusterClient.Cluster(KubeBindRootClusterName).Discovery()
computeDynamicClient := dynamicClusterClient.Cluster(KubeBindRootClusterName)
discoveryClient := apiExtensionClusterClient.Cluster(baseWorkspace).Discovery()
dynamicClient := dynamicClusterClient.Cluster(baseWorkspace)

crdClient := apiExtensionClusterClient.ApiextensionsV1().Cluster(KubeBindRootClusterName).CustomResourceDefinitions()
return resources.Bootstrap(ctx, kcpClientSet, computeDiscoveryClient, computeDynamicClient, crdClient, batteriesIncluded)
crdClient := apiExtensionClusterClient.ApiextensionsV1().Cluster(baseWorkspace).CustomResourceDefinitions()
return resources.Bootstrap(ctx, kcpClientSet, discoveryClient, dynamicClient, crdClient, batteriesIncluded)
}
14 changes: 12 additions & 2 deletions contrib/kcp/bootstrap/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package options
import (
"fmt"

"github.com/kcp-dev/sdk/apis/core"
"github.com/spf13/pflag"
"k8s.io/component-base/logs"
logsv1 "k8s.io/component-base/logs/api/v1"
Expand All @@ -29,9 +30,13 @@ type Options struct {

ExtraOptions
}

type ExtraOptions struct {
KCPKubeConfig string
KCPContext string

RootWorkspace string
KBindWorkspace string
}

type completedOptions struct {
Expand All @@ -50,8 +55,11 @@ func NewOptions() *Options {
logs.Verbosity = logsv1.VerbosityLevel(2)

return &Options{
Logs: logs,
ExtraOptions: ExtraOptions{},
Logs: logs,
ExtraOptions: ExtraOptions{
RootWorkspace: core.RootCluster.Path().String(),
KBindWorkspace: "root:kbind",
},
}
}

Expand All @@ -60,6 +68,8 @@ func (options *Options) AddFlags(fs *pflag.FlagSet) {

fs.StringVar(&options.KCPKubeConfig, "kcp-kubeconfig", options.KCPKubeConfig, "path to a kcp kubeconfig. Required to bootstrap the server.")
fs.StringVar(&options.KCPContext, "context", options.KCPContext, "Name of the context in the kcp kubeconfig file to use")
fs.StringVar(&options.RootWorkspace, "root-workspace", options.RootWorkspace, "Workspace path to use as the root (must exist already)")
fs.StringVar(&options.KBindWorkspace, "kbind-workspace", options.KBindWorkspace, "kbind workspace to use (must exist already)")
}

func (options *Options) Complete() (*CompletedOptions, error) {
Expand Down
6 changes: 6 additions & 0 deletions contrib/kcp/bootstrap/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package bootstrap
import (
"context"

"github.com/kcp-dev/logicalcluster/v3"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"

Expand Down Expand Up @@ -49,17 +50,21 @@ func (s *Server) Start(ctx context.Context) error {
s.Config.ApiextensionsClient,
s.Config.DynamicClusterClient,
fakeBatteries,
logicalcluster.NewPath(s.Config.Options.ExtraOptions.RootWorkspace),
); err != nil {
logger.Error(err, "failed to bootstrap initial config workspace")
return nil // don't klog.Fatal. This only happens when context is cancelled.
}

kbindWorkspace := logicalcluster.NewPath(s.Config.Options.ExtraOptions.KBindWorkspace)

if err := bootstrapcore.Bootstrap(
ctx,
s.Config.KcpClusterClient,
s.Config.ApiextensionsClient,
s.Config.DynamicClusterClient,
fakeBatteries,
kbindWorkspace,
); err != nil {
logger.Error(err, "failed to bootstrap core workspace")
return nil // don't klog.Fatal. This only happens when context is cancelled.
Expand All @@ -71,6 +76,7 @@ func (s *Server) Start(ctx context.Context) error {
s.Config.ApiextensionsClient,
s.Config.DynamicClusterClient,
fakeBatteries,
kbindWorkspace,
); err != nil {
logger.Error(err, "failed to bootstrap workspace")
return nil // don't klog.Fatal. This only happens when context is cancelled.
Expand Down
18 changes: 7 additions & 11 deletions contrib/kcp/deploy/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ import (
//go:embed examples/*.yaml
var Examples embed.FS

var (
// KubeBindRootClusterName is the workspace to host common APIs.
KubeBindRootClusterName = logicalcluster.NewPath("root:kube-bind")
)

// Bootstrap creates resources in this package by continuously retrying the list.
// This is blocking, i.e. it only returns (with error) when the context is closed or with nil when
// the bootstrapping is successfully completed.
Expand All @@ -53,21 +48,22 @@ func Bootstrap(
apiExtensionClusterClient kcpapiextensionsclientset.ClusterInterface,
dynamicClusterClient kcpdynamic.ClusterInterface,
batteriesIncluded sets.Set[string],
baseWorkspace logicalcluster.Path,
) error {
computeDiscoveryClient := apiExtensionClusterClient.Cluster(KubeBindRootClusterName).Discovery()
computeDynamicClient := dynamicClusterClient.Cluster(KubeBindRootClusterName)
discoveryClient := apiExtensionClusterClient.Cluster(baseWorkspace).Discovery()
dynamicClient := dynamicClusterClient.Cluster(baseWorkspace)

crdClient := apiExtensionClusterClient.ApiextensionsV1().Cluster(KubeBindRootClusterName).CustomResourceDefinitions()
kcpClient := kcpClientSet.Cluster(KubeBindRootClusterName)
crdClient := apiExtensionClusterClient.ApiextensionsV1().Cluster(baseWorkspace).CustomResourceDefinitions()
kcpClient := kcpClientSet.Cluster(baseWorkspace)

err := resources.Bootstrap(ctx, kcpClientSet, computeDiscoveryClient, computeDynamicClient, crdClient, batteriesIncluded)
err := resources.Bootstrap(ctx, kcpClientSet, discoveryClient, dynamicClient, crdClient, batteriesIncluded)
if err != nil {
return err
}

// create recursive apibinding so we can start controllers.
// this is a temporary solution until we have a better way to bootstrap controllers.
return bindAPIExport(ctx, kcpClient, "kube-bind.io", KubeBindRootClusterName)
return bindAPIExport(ctx, kcpClient, "kube-bind.io", baseWorkspace)
}

func bindAPIExport(ctx context.Context, kcpClient kcpclient.Interface, exportName string, clusterPath logicalcluster.Path) error {
Expand Down
6 changes: 3 additions & 3 deletions contrib/kcp/test/e2e/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
"github.com/kube-bind/kube-bind/test/e2e/framework"
)

func bootstrapBackend(t *testing.T, rest *rest.Config, scope kubebindv1alpha2.InformerScope) string {
func bootstrapBackend(t *testing.T, rest *rest.Config, scope kubebindv1alpha2.InformerScope, kbindPath logicalcluster.Path) string {
t.Helper()
t.Log("Bootstrapping backend")

Expand All @@ -46,7 +46,7 @@ func bootstrapBackend(t *testing.T, rest *rest.Config, scope kubebindv1alpha2.In

exportUrl := ""
kcptestinghelpers.Eventually(t, func() (bool, string) {
exportES, err := client.Cluster(logicalcluster.NewPath("root").Join("kube-bind")).
exportES, err := client.Cluster(kbindPath).
ApisV1alpha1().
APIExportEndpointSlices().
Get(t.Context(), "kube-bind.io", metav1.GetOptions{})
Expand All @@ -61,7 +61,7 @@ func bootstrapBackend(t *testing.T, rest *rest.Config, scope kubebindv1alpha2.In
}, wait.ForeverTestTimeout, time.Millisecond*100)
require.NotEmpty(t, exportUrl, "APIExportEndpointSlice URL is empty")

_, backendKubeconfig := wsConfig(t, rest, logicalcluster.NewPath("root").Join("kube-bind"))
_, backendKubeconfig := wsConfig(t, rest, kbindPath)

t.Log("Starting kube-bind backend for kcp")
addr, _ := framework.StartBackend(t,
Expand Down
73 changes: 67 additions & 6 deletions contrib/kcp/test/e2e/kcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,56 @@ package e2e

import (
"fmt"
"path/filepath"
"strings"
"testing"
"time"

"github.com/kcp-dev/logicalcluster/v3"
kcpapisv1alpha2 "github.com/kcp-dev/sdk/apis/apis/v1alpha2"
kcptenancyv1alpha1 "github.com/kcp-dev/sdk/apis/tenancy/v1alpha1"
kcpclientset "github.com/kcp-dev/sdk/client/clientset/versioned/cluster"
kcptestinghelpers "github.com/kcp-dev/sdk/testing/helpers"
"github.com/spf13/pflag"
"github.com/stretchr/testify/require"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"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"
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
"github.com/kube-bind/kube-bind/test/e2e/framework"
)

// waitForClusterEngaged blocks until the kcp multicluster provider has engaged
// the workspace described by cfg. The provider creates a kube-bind.io Cluster
// named "default" in every workspace it engages, so we poll for that object.
func waitForClusterEngaged(t testing.TB, cfg *rest.Config) {
t.Helper()

gvr := schema.GroupVersionResource{
Group: kubebindv1alpha2.SchemeGroupVersion.Group,
Version: kubebindv1alpha2.SchemeGroupVersion.Version,
Resource: "clusters",
}

client := framework.DynamicClient(t, cfg).Resource(gvr)

kcptestinghelpers.Eventually(t, func() (bool, string) {
_, err := client.Get(t.Context(), kubebindv1alpha2.DefaultClusterName, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return false, "kube-bind.io Cluster \"default\" not created yet"
}
return false, fmt.Sprintf("error getting kube-bind.io Cluster: %v", err)
}
return true, ""
}, wait.ForeverTestTimeout, time.Millisecond*100)
}

func wsConfig(t testing.TB, rest *rest.Config, workspace logicalcluster.Path) (*rest.Config, string) {
cfg := rest
cfg.Host += "/clusters/" + workspace.String()
Expand Down Expand Up @@ -126,20 +157,46 @@ func createApiBinding(t testing.TB, client *kcpclientset.ClusterClientset, path
return inCluster
}

func bootstrapKCP(t testing.TB, rest *rest.Config) {
// bootstrapKCP bootstraps kcp for a test. It creates a per-test root workspace
// (root:<test>) with a kbind workspace beneath it (root:<test>:kbind) and installs
// the kube-bind APIs there. It returns a rest.Config pointing at the per-test root
// workspace (so consumer/provider workspaces can be created as siblings of kbind)
// and the path of the kbind workspace.
func bootstrapKCP(t testing.TB, rest *rest.Config) (*rest.Config, logicalcluster.Path) {
t.Helper()
t.Log("Bootstrapping kcp")

cfg := rest
cfg.Host += "/clusters/root"
adminApiCfg := framework.RestToKubeconfig(cfg, "default")
adminKubeconfig := framework.WriteKubeconfig(t, adminApiCfg, "admin.kubeconfig")
wsName := strings.ToLower(t.Name())
kbindLeafName := "kbind"
rootWorkspace := fmt.Sprintf("root:%s", wsName)
kbindWorkspace := fmt.Sprintf("%s:%s", rootWorkspace, kbindLeafName)

// The provided rest.Config should already point to a running kcp server.
// It should already have /clusters/root in the path if read from kubeconfig.
// Just ensure we have the right base URL for bootstrap.
cfg := *rest
adminApiCfg := framework.RestToKubeconfig(&cfg, "default")

// create the two workspaces we want our tests to run in
testRootCfg, _ := framework.NewWorkspace(t, rest, func(ws *kcptenancyv1alpha1.Workspace) {
ws.Name = wsName
ws.GenerateName = ""
})

framework.NewWorkspace(t, testRootCfg, func(ws *kcptenancyv1alpha1.Workspace) {
ws.Name = kbindLeafName
ws.GenerateName = ""
})

adminKubeconfig := framework.WriteKubeconfigToPath(t, adminApiCfg, filepath.Join(framework.WorkDir(t), "admin.kubeconfig"))

fs := pflag.NewFlagSet("kcp-bootstrap", pflag.ContinueOnError)
options := options.NewOptions()
options.AddFlags(fs)

options.KCPKubeConfig = adminKubeconfig
options.RootWorkspace = rootWorkspace
options.KBindWorkspace = kbindWorkspace

completed, err := options.Complete()
require.NoError(t, err)
Expand All @@ -149,5 +206,9 @@ func bootstrapKCP(t testing.TB, rest *rest.Config) {

bootstrapper, err := bootstrap.NewServer(t.Context(), config)
require.NoError(t, err)
require.NoError(t, bootstrapper.Start(t.Context()))

err = bootstrapper.Start(t.Context())
require.NoError(t, err)

return testRootCfg, logicalcluster.NewPath(kbindWorkspace)
}
Loading
Loading