From 09633375d6da03df6d5b935f4a124ea4c8479e54 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 2 May 2024 12:15:34 +1000 Subject: [PATCH 01/25] Move the registration work into k8ssandra-client (WIP) --- cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go | 2 + cmd/kubectl-k8ssandra/register/command.go | 64 +++++++ .../register/register_test.go | 176 ++++++++++++++++++ .../register/registration.go | 137 ++++++++++++++ cmd/kubectl-k8ssandra/register/suite_test.go | 19 ++ internal/envtest/envtest.go | 105 ++++++++--- internal/envtest/framework.go | 12 ++ internal/envtest/images.go | 41 ++++ internal/envtest/multi_envtest.go | 26 +++ pkg/helmutil/crds_test.go | 2 +- pkg/registration/get_kubeconfig.go | 79 ++++++++ pkg/registration/get_kubeconfig_test.go | 11 ++ pkg/registration/token_to_kubeconfig.go | 36 ++++ pkg/tasks/create_test.go | 51 ++--- ...ion_clientconfigs.config.k8ssandra.io.yaml | 61 ++++++ 15 files changed, 770 insertions(+), 52 deletions(-) create mode 100644 cmd/kubectl-k8ssandra/register/command.go create mode 100644 cmd/kubectl-k8ssandra/register/register_test.go create mode 100644 cmd/kubectl-k8ssandra/register/registration.go create mode 100644 cmd/kubectl-k8ssandra/register/suite_test.go create mode 100644 internal/envtest/framework.go create mode 100644 internal/envtest/images.go create mode 100644 internal/envtest/multi_envtest.go create mode 100644 pkg/registration/get_kubeconfig.go create mode 100644 pkg/registration/get_kubeconfig_test.go create mode 100644 pkg/registration/token_to_kubeconfig.go create mode 100644 testfiles/crd/apiextensions.k8s.io_v1_customresourcedefinition_clientconfigs.config.k8ssandra.io.yaml diff --git a/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go b/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go index d734803..f6d583e 100644 --- a/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go +++ b/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go @@ -11,6 +11,7 @@ import ( "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/config" "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/helm" "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/operate" + "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/register" "github.com/k8ssandra/k8ssandra-client/cmd/kubectl-k8ssandra/users" "github.com/spf13/cobra" @@ -53,6 +54,7 @@ func NewCmd(streams genericclioptions.IOStreams) *cobra.Command { // cmd.AddCommand(migrate.NewInstallCmd(streams)) cmd.AddCommand(config.NewCmd(streams)) cmd.AddCommand(helm.NewHelmCmd(streams)) + register.Init(cmd, streams) // cmd.Flags().BoolVar(&o.listNamespaces, "list", o.listNamespaces, "if true, print the list of all namespaces in the current KUBECONFIG") o.configFlags.AddFlags(cmd.Flags()) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go new file mode 100644 index 0000000..5d0254e --- /dev/null +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -0,0 +1,64 @@ +package register + +import ( + "fmt" + + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +var ( + StaticNamespace = "mission-control" +) + +var RegisterClusterCmd = &cobra.Command{ + Use: "register [flags]", + Short: "register a data plane into the control plane.", + Long: `register creates a ServiceAccount on a source cluster, copies its credentials and then creates a secret containing them on the destination cluster. It then also creates a ClientConfig on the destination cluster to reference the secret.`, + Run: entrypoint, +} + +func Init(cmd *cobra.Command, streams genericclioptions.IOStreams) { + RegisterClusterCmd.Flags().String("source-kubeconfig", + "", + "path to source cluster's kubeconfig file - defaults to KUBECONFIG then ~/.kube/config") + RegisterClusterCmd.Flags().String("dest-kubeconfig", + "", + "path to destination cluster's kubeconfig file - defaults to KUBECONFIG then ~/.kube/config") + RegisterClusterCmd.Flags().String("source-context", "", "context name for source cluster") + RegisterClusterCmd.Flags().String("dest-context", "", "context name for destination cluster") + RegisterClusterCmd.Flags().String("serviceaccount-name", "mission-control", "serviceaccount name for destination cluster") + RegisterClusterCmd.Flags().String("destination-name", "remote-mission-control", "name for remote clientConfig and secret on destination cluster") + cmd.AddCommand(RegisterClusterCmd) +} + +func entrypoint(cmd *cobra.Command, args []string) { + executor := NewRegistrationExecutorFromRegisterClusterCmd(*cmd) + for i := 0; i < 30; i++ { + res := executor.RegisterCluster() + switch { + case res.IsError(): + fmt.Println("Registration continuing", res.GetError()) + continue + case res.Completed(): + fmt.Println("Registration completed successfully") + return + case res.IsRequeue(): + fmt.Println("Registration continues") + continue + } + } + fmt.Println("Registration failed - retries exceeded") +} + +func NewRegistrationExecutorFromRegisterClusterCmd(cmd cobra.Command) *RegistrationExecutor { + return &RegistrationExecutor{ + SourceKubeconfig: cmd.Flag("source-kubeconfig").Value.String(), + DestKubeconfig: cmd.Flag("dest-kubeconfig").Value.String(), + SourceContext: cmd.Flag("source-context").Value.String(), + DestContext: cmd.Flag("dest-context").Value.String(), + ServiceAccount: cmd.Flag("serviceaccount-name").Value.String(), + Context: cmd.Context(), + DestinationName: cmd.Flag("destination-name").Value.String(), + } +} diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go new file mode 100644 index 0000000..ad293dd --- /dev/null +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -0,0 +1,176 @@ +package register + +import ( + "context" + "os" + "path/filepath" + "runtime" + "testing" + "time" + + configapi "github.com/k8ssandra/k8ssandra-operator/apis/config/v1beta1" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func TestRegister(t *testing.T) { + require.New(t) + client1 := (*multiEnv)[0].GetClient("mission-control") + client2 := (*multiEnv)[1].GetClient("mission-control") + + if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "mission-control"}}); err != nil { + t.Fatal(err) + } + + if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "mission-control"}}); err != nil { + t.Fatal(err) + } + + buildDir := os.Getenv("BUILD_DIR") + if buildDir == "" { + _, b, _, _ := runtime.Caller(0) + buildDir = filepath.Join(filepath.Dir(b), "../../../build") + } + + if _, err := os.Stat(buildDir); os.IsNotExist(err) { + if err := os.Mkdir(buildDir, os.ModePerm); err != nil { + t.Fatal(err) + } + } + + kc1, err := (*multiEnv)[0].GetKubeconfig(t) + if err != nil { + t.Fatal(err) + + } + f, err := os.Create(buildDir + "/kubeconfig1") + if err != nil { + t.Fatal(err) + } + defer func() { + if err := f.Close(); err != nil { + t.Fatal(err) + } + }() + if _, err := f.Write(kc1); err != nil { + t.Fatal(err) + } + + kc2, err := (*multiEnv)[1].GetKubeconfig(t) + if err != nil { + t.Fatal(err) + + } + f, err = os.Create(buildDir + "/kubeconfig2") + if err != nil { + t.Fatal(err) + } + if _, err := f.Write(kc2); err != nil { + t.Fatal(err) + } + ex := RegistrationExecutor{ + SourceKubeconfig: buildDir + "/kubeconfig1", + DestKubeconfig: buildDir + "/kubeconfig2", + SourceContext: "default-context", + DestContext: "default-context", + ServiceAccount: "mission-control", + Context: context.TODO(), + DestinationName: "test-destination", + } + ctx := context.Background() + + require.Eventually(t, func() bool { + res := ex.RegisterCluster() + switch { + case res.IsDone(): + return true + case res.IsError(): + t.Log(res.GetError()) + if res.GetError().Error() == "no secret found for service account mission-control" { + return true + } + } + return false + }, time.Second*30, time.Second*5) + + // This relies on a controller that is not running in the envtest. + + desiredSaSecret := &corev1.Secret{} + require.NoError(t, client1.Get(context.Background(), client.ObjectKey{Name: "mission-control-secret", Namespace: "mission-control"}, desiredSaSecret)) + patch := client.MergeFrom(desiredSaSecret.DeepCopy()) + desiredSaSecret.Data = map[string][]byte{ + "token": []byte("test-token"), + "ca.crt": []byte("test-ca"), + } + require.NoError(t, client1.Patch(ctx, desiredSaSecret, patch)) + + desiredSa := &corev1.ServiceAccount{} + require.NoError(t, client1.Get( + context.Background(), + client.ObjectKey{Name: "mission-control", Namespace: "mission-control"}, + desiredSa)) + + patch = client.MergeFrom(desiredSa.DeepCopy()) + desiredSa.Secrets = []corev1.ObjectReference{ + { + Name: "mission-control-secret", + }, + } + require.NoError(t, client1.Patch(ctx, desiredSa, patch)) + + // Continue reconciliation + + require.Eventually(t, func() bool { + res := ex.RegisterCluster() + switch { + case res.IsDone(): + return true + case res.IsError(): + t.Log(res.GetError()) + return false + } + return false + }, time.Second*3000, time.Second*5) + + if err := configapi.AddToScheme(client2.Scheme()); err != nil { + t.Fatal(err) + } + destSecret := &corev1.Secret{} + require.Eventually(t, func() bool { + err = client2.Get(ctx, + client.ObjectKey{Name: "test-destination", Namespace: "mission-control"}, destSecret) + if err != nil { + t.Log("didn't find dest secret") + return false + } + clientConfig := &configapi.ClientConfig{} + err = client2.Get(ctx, + client.ObjectKey{Name: "test-destination", Namespace: "mission-control"}, clientConfig) + if err != nil { + t.Log("didn't find dest client config") + return false + } + return err == nil + }, time.Second*60, time.Second*5) + + destKubeconfig := ClientConfigFromSecret(destSecret) + require.Equal(t, + desiredSaSecret.Data["ca.crt"], + destKubeconfig.Clusters["cluster"].CertificateAuthorityData) + + require.Equal(t, + string(desiredSaSecret.Data["token"]), + destKubeconfig.AuthInfos["cluster"].Token) +} + +func ClientConfigFromSecret(s *corev1.Secret) clientcmdapi.Config { + out, err := clientcmd.Load(s.Data["kubeconfig"]) + if err != nil { + panic(err) + } + return *out +} diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go new file mode 100644 index 0000000..ae01138 --- /dev/null +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -0,0 +1,137 @@ +package register + +import ( + "context" + "fmt" + "log" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/k8ssandra/k8ssandra-client/pkg/registration" + configapi "github.com/k8ssandra/k8ssandra-operator/apis/config/v1beta1" + "github.com/k8ssandra/k8ssandra-operator/pkg/result" +) + +type RegistrationExecutor struct { + DestinationName string + SourceKubeconfig string + DestKubeconfig string + SourceContext string + DestContext string + ServiceAccount string + Context context.Context +} + +func getDefaultSecret(saName string) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: saName + "-secret", + Namespace: StaticNamespace, + Annotations: map[string]string{ + "kubernetes.io/service-account.name": saName, + }, + }, + Type: corev1.SecretTypeServiceAccountToken, + } +} +func getDefaultServiceAccount(saName string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: saName, + Namespace: StaticNamespace, + }, + } + +} + +func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { + log.Printf("Registering cluster from %s Context: %s to %s Context: %s", + registration.GetKubeconfigFileLocation(e.SourceKubeconfig), e.SourceContext, + registration.GetKubeconfigFileLocation(e.DestKubeconfig), e.DestContext, + ) + srcClient, err := registration.GetClient(e.SourceKubeconfig, e.SourceContext) + if err != nil { + return result.Error(err) + } + destClient, err := registration.GetClient(e.DestKubeconfig, e.DestContext) + if err != nil { + return result.Error(err) + } + // Get ServiceAccount + serviceAccount := &corev1.ServiceAccount{} + if err := srcClient.Get(e.Context, client.ObjectKey{Name: e.ServiceAccount, Namespace: StaticNamespace}, serviceAccount); err != nil { + if err := srcClient.Create(e.Context, getDefaultServiceAccount(e.ServiceAccount)); err != nil { + return result.Error(err) + } + return result.Error(err) + } + // Get a secret in this namespace which holds the service account token + secretsList := &corev1.SecretList{} + if err := srcClient.List(e.Context, secretsList, client.InNamespace(StaticNamespace)); err != nil { + return result.Error(err) + } + var secret *corev1.Secret + for _, s := range secretsList.Items { + if s.Annotations["kubernetes.io/service-account.name"] == e.ServiceAccount && s.Type == corev1.SecretTypeServiceAccountToken { + secret = &s + break + } + } + if secret == nil { + secret = getDefaultSecret(e.ServiceAccount) + if err := srcClient.Create(e.Context, secret); err != nil { + return result.Error(err) + } + return result.Error(fmt.Errorf("no secret found for service account %s", e.ServiceAccount)) + } + + // Create Secret on destination cluster + host, err := registration.KubeconfigToHost(e.SourceKubeconfig, e.SourceContext) + if err != nil { + return result.Error(err) + } + saConfig, err := registration.TokenToKubeconfig(*secret, host) + if err != nil { + return result.Error(fmt.Errorf("error converting token to kubeconfig: %w, secret: %#v", err, secret)) + } + secretData, err := clientcmd.Write(saConfig) + if err != nil { + return result.Error(err) + } + destSecret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: e.DestinationName, + Namespace: StaticNamespace, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "kubeconfig": secretData, + }, + } + if err := destClient.Create(e.Context, &destSecret); err != nil { + return result.Error(err) + } + + // Create ClientConfig on destination cluster + if err := configapi.AddToScheme(destClient.Scheme()); err != nil { + return result.Error(err) + } + destClientConfig := configapi.ClientConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: e.DestinationName, + Namespace: StaticNamespace, + }, + Spec: configapi.ClientConfigSpec{ + KubeConfigSecret: corev1.LocalObjectReference{ + Name: e.DestinationName, + }, + }, + } + if err := destClient.Create(e.Context, &destClientConfig); err != nil { + return result.Error(err) + } + return result.Done() +} diff --git a/cmd/kubectl-k8ssandra/register/suite_test.go b/cmd/kubectl-k8ssandra/register/suite_test.go new file mode 100644 index 0000000..0211a89 --- /dev/null +++ b/cmd/kubectl-k8ssandra/register/suite_test.go @@ -0,0 +1,19 @@ +package register + +import ( + "os" + "testing" + + "github.com/k8ssandra/k8ssandra-client/internal/envtest" +) + +var ( + multiEnv *envtest.MultiK8sEnvironment +) + +func TestMain(m *testing.M) { + // metrics.DefaultBindAddress = "0" This no longer appears to exist... + os.Exit(envtest.RunMulti(m, func(e *envtest.MultiK8sEnvironment) { + multiEnv = e + }, 2)) +} diff --git a/internal/envtest/envtest.go b/internal/envtest/envtest.go index e04bf5d..ed153ef 100644 --- a/internal/envtest/envtest.go +++ b/internal/envtest/envtest.go @@ -3,17 +3,18 @@ package envtest import ( "context" "path/filepath" - "runtime" "strings" "testing" cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" controlapi "github.com/k8ssandra/cass-operator/apis/control/v1alpha1" + "github.com/k8ssandra/k8ssandra-client/pkg/kubernetes" k8ssandrataskapi "github.com/k8ssandra/k8ssandra-operator/apis/control/v1alpha1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "github.com/k8ssandra/k8ssandra-client/pkg/kubernetes" - apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/kubectl/pkg/scheme" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -21,51 +22,68 @@ import ( ) func Run(m *testing.M, setupFunc func(e *Environment)) (code int) { - env := NewEnvironment() - env.start() + ctx := ctrl.SetupSignalHandler() + env := NewEnvironment(ctx) + env.Start() setupFunc(env) exitCode := m.Run() - env.stop() + env.Stop() return exitCode } type Environment struct { - intClient client.Client + Client client.Client env *envtest.Environment cancelManager context.CancelFunc Context context.Context + Kubeconfig string } -func NewEnvironment() *Environment { +func NewEnvironment(ctx context.Context) *Environment { env := &Environment{} env.env = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join(RootDir(), "testfiles", "crd")}, + CRDDirectoryPaths: []string{ + filepath.Join(RootDir(), "testfiles", "crd"), + }, ErrorIfCRDPathMissing: true, } - - ctx := ctrl.SetupSignalHandler() ctx, cancel := context.WithCancel(ctx) env.Context = ctx env.cancelManager = cancel return env } -// https://stackoverflow.com/questions/31873396/is-it-possible-to-get-the-current-root-of-package-structure-as-a-string-in-golan -func RootDir() string { - _, b, _, _ := runtime.Caller(0) - return filepath.Join(filepath.Dir(b), "../..") +func (e *Environment) GetClient(namespace string) client.Client { + c, err := kubernetes.GetClientInNamespace(e.env.Config, namespace) + if err != nil { + panic(err) + } + return c } -func (e *Environment) Client(namespace string) client.Client { - return client.NewNamespacedClient(e.intClient, namespace) +func (e *Environment) RestConfig() *rest.Config { + return e.env.Config +} + +func (e *Environment) RawClient() client.Client { + return e.Client } -func (e *Environment) start() { +func (e *Environment) Start() { cfg, err := e.env.Start() if err != nil { panic(err) } + k8sClient, err := kubernetes.GetClient(cfg) + if err != nil { + panic(err) + } + + if err := cassdcapi.AddToScheme(k8sClient.Scheme()); err != nil { + panic(err) + } + if err := cassdcapi.AddToScheme(scheme.Scheme); err != nil { panic(err) } @@ -84,15 +102,14 @@ func (e *Environment) start() { //+kubebuilder:scaffold:scheme - k8sClient, err := client.New(cfg, client.Options{Scheme: scheme.Scheme}) - if err != nil { - panic(err) - } - - e.intClient = k8sClient + e.Client = k8sClient + // e.Kubeconfig, err = CreateKubeconfigFileForRestConfig(e.env.Config) + // if err != nil { + // panic(err) + // } } -func (e *Environment) stop() { +func (e *Environment) Stop() { e.cancelManager() if err := e.env.Stop(); err != nil { panic(err) @@ -101,13 +118,45 @@ func (e *Environment) stop() { func (e *Environment) CreateNamespace(t *testing.T) string { namespace := strings.ToLower(t.Name()) - if err := kubernetes.CreateNamespaceIfNotExists(e.intClient, namespace); err != nil { + if err := kubernetes.CreateNamespaceIfNotExists(e.Client, namespace); err != nil { t.FailNow() } return namespace } -func (e *Environment) RestConfig() *rest.Config { - return e.env.Config +func (e *Environment) GetKubeconfig(t *testing.T) ([]byte, error) { + clientConfig, err := CreateKubeconfigFileForRestConfig(e.env.Config) + if err != nil { + return nil, err + } + return clientcmd.Write(clientConfig) +} + +func CreateKubeconfigFileForRestConfig(restConfig *rest.Config) (clientcmdapi.Config, error) { + clusters := make(map[string]*clientcmdapi.Cluster) + clusters["default-cluster"] = &clientcmdapi.Cluster{ + Server: restConfig.Host, + CertificateAuthorityData: restConfig.CAData, + } + contexts := make(map[string]*clientcmdapi.Context) + contexts["default-context"] = &clientcmdapi.Context{ + Cluster: "default-cluster", + AuthInfo: "default-user", + } + authinfos := make(map[string]*clientcmdapi.AuthInfo) + authinfos["default-user"] = &clientcmdapi.AuthInfo{ + ClientCertificateData: restConfig.CertData, + ClientKeyData: restConfig.KeyData, + } + clientConfig := clientcmdapi.Config{ + Kind: "Config", + APIVersion: "v1", + Clusters: clusters, + Contexts: contexts, + CurrentContext: "default-context", + AuthInfos: authinfos, + } + + return clientConfig, nil } diff --git a/internal/envtest/framework.go b/internal/envtest/framework.go new file mode 100644 index 0000000..320fa88 --- /dev/null +++ b/internal/envtest/framework.go @@ -0,0 +1,12 @@ +package envtest + +import ( + "path/filepath" + "runtime" +) + +// https://stackoverflow.com/questions/31873396/is-it-possible-to-get-the-current-root-of-package-structure-as-a-string-in-golan +func RootDir() string { + _, b, _, _ := runtime.Caller(0) + return filepath.Join(filepath.Dir(b), "../..") +} diff --git a/internal/envtest/images.go b/internal/envtest/images.go new file mode 100644 index 0000000..3a46851 --- /dev/null +++ b/internal/envtest/images.go @@ -0,0 +1,41 @@ +package envtest + +import ( + "context" + "os" + "path/filepath" + + "github.com/k8ssandra/k8ssandra-client/pkg/kubernetes" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func DeployImageConfig(cli client.Client) error { + configPath := filepath.Join(RootDir(), "testfiles", "image_config.yaml") + b, err := os.ReadFile(configPath) + if err != nil { + return err + } + + if err := kubernetes.CreateNamespaceIfNotExists(cli, "mission-control"); err != nil { + return err + } + + confMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cass-operator-manager-config", + Namespace: "mission-control", + }, + Data: map[string]string{ + "image_config.yaml": string(b), + }, + } + + if err := cli.Create(context.TODO(), confMap); err != nil && !errors.IsAlreadyExists(err) { + return err + } + + return nil +} diff --git a/internal/envtest/multi_envtest.go b/internal/envtest/multi_envtest.go new file mode 100644 index 0000000..e8da462 --- /dev/null +++ b/internal/envtest/multi_envtest.go @@ -0,0 +1,26 @@ +package envtest + +import ( + "testing" + + ctrl "sigs.k8s.io/controller-runtime" +) + +type MultiK8sEnvironment []*Environment + +func RunMulti(m *testing.M, setupFunc func(e *MultiK8sEnvironment), numClusters int) (code int) { + e := make(MultiK8sEnvironment, numClusters) + ctx := ctrl.SetupSignalHandler() + for i := 0; i < numClusters; i++ { + e[i] = NewEnvironment(ctx) + e[i].Start() + } + defer func() { + for i := 0; i < numClusters; i++ { + e[i].Stop() + } + }() + setupFunc(&e) + exitCode := m.Run() + return exitCode +} diff --git a/pkg/helmutil/crds_test.go b/pkg/helmutil/crds_test.go index 9c554b6..f0d5898 100644 --- a/pkg/helmutil/crds_test.go +++ b/pkg/helmutil/crds_test.go @@ -20,7 +20,7 @@ func TestUpgradingCRDs(t *testing.T) { chartNames := []string{"cass-operator"} for _, chartName := range chartNames { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) require.NoError(cleanCache("k8ssandra", chartName)) // creating new upgrader diff --git a/pkg/registration/get_kubeconfig.go b/pkg/registration/get_kubeconfig.go new file mode 100644 index 0000000..93bd9fe --- /dev/null +++ b/pkg/registration/get_kubeconfig.go @@ -0,0 +1,79 @@ +package registration + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func GetClient(configFileLocation string, contextName string) (client.Client, error) { + clientConfig, err := clientcmd.LoadFromFile(GetKubeconfigFileLocation(configFileLocation)) + if err != nil { + return nil, err + } + var restConfig *rest.Config + if contextName == "" { + restConfig, err := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + panic(err) + } + return client.New(restConfig, client.Options{}) + } + + context, found := clientConfig.Contexts[contextName] + if !found { + panic(errors.New(fmt.Sprint("context not found in supplied kubeconfig ", "contextName: ", contextName, " configFileLocation: ", GetKubeconfigFileLocation(configFileLocation)))) + } + overrides := &clientcmd.ConfigOverrides{ + Context: *context, + ClusterInfo: *clientConfig.Clusters[context.Cluster], + AuthInfo: *clientConfig.AuthInfos[context.AuthInfo], + } + + cConfig := clientcmd.NewNonInteractiveClientConfig(*clientConfig, contextName, overrides, clientcmd.NewDefaultClientConfigLoadingRules()) + restConfig, err = cConfig.ClientConfig() + + if err != nil { + panic(err) + } + return client.New(restConfig, client.Options{}) +} + +func GetKubeconfigFileLocation(location string) string { + if location != "" { + return location + } else if kubeconfigEnvVar, found := os.LookupEnv("KUBECONFIG"); found { + return kubeconfigEnvVar + } else { + homeDir, err := os.UserHomeDir() + if err != nil { + panic(err) + } + return filepath.Join(homeDir, ".kube", "config") + } +} + +func KubeconfigToHost(configFileLocation string, contextName string) (string, error) { + clientConfig, err := clientcmd.LoadFromFile(GetKubeconfigFileLocation(configFileLocation)) + if err != nil { + return "", err + } + if contextName == "" { + restConfig, err := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + return "", err + } + return restConfig.Host, nil + } + + context, found := clientConfig.Contexts[contextName] + if !found { + panic(errors.New(fmt.Sprint("context not found in supplied kubeconfig ", "contextName: ", contextName, " configFileLocation: ", GetKubeconfigFileLocation(configFileLocation)))) + } + return clientConfig.Clusters[context.Cluster].Server, nil +} diff --git a/pkg/registration/get_kubeconfig_test.go b/pkg/registration/get_kubeconfig_test.go new file mode 100644 index 0000000..91694c3 --- /dev/null +++ b/pkg/registration/get_kubeconfig_test.go @@ -0,0 +1,11 @@ +package registration + +import "testing" + +func TestGetKubeconfigFileLocation(t *testing.T) { + // TODO +} + +func TestGetClient(t *testing.T) { + // TODO +} diff --git a/pkg/registration/token_to_kubeconfig.go b/pkg/registration/token_to_kubeconfig.go new file mode 100644 index 0000000..2e91a85 --- /dev/null +++ b/pkg/registration/token_to_kubeconfig.go @@ -0,0 +1,36 @@ +package registration + +import ( + "errors" + + corev1 "k8s.io/api/core/v1" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" +) + +func TokenToKubeconfig(s corev1.Secret, server string) (clientcmdapi.Config, error) { + caData, foundCa := s.Data["ca.crt"] + tokenData, foundToken := s.Data["token"] + if !foundCa || !foundToken { + return clientcmdapi.Config{}, errors.New("missing required data in secret") + } + + return clientcmdapi.Config{ + Clusters: map[string]*clientcmdapi.Cluster{ + "cluster": { + Server: server, + CertificateAuthorityData: caData, + }, + }, + AuthInfos: map[string]*clientcmdapi.AuthInfo{ + "cluster": { + Token: string(tokenData), + }, + }, + Contexts: map[string]*clientcmdapi.Context{ + "cluster": { + Cluster: "cluster", + AuthInfo: "cluster", + }, + }, + }, nil +} diff --git a/pkg/tasks/create_test.go b/pkg/tasks/create_test.go index f5f0746..77e554a 100644 --- a/pkg/tasks/create_test.go +++ b/pkg/tasks/create_test.go @@ -6,6 +6,7 @@ import ( cassdcapi "github.com/k8ssandra/cass-operator/apis/cassandra/v1beta1" controlapi "github.com/k8ssandra/cass-operator/apis/control/v1alpha1" + k8ssandrataskapi "github.com/k8ssandra/k8ssandra-operator/apis/control/v1alpha1" "github.com/stretchr/testify/assert" "github.com/k8ssandra/k8ssandra-client/pkg/tasks" @@ -13,11 +14,14 @@ import ( func TestCreateRestartTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" rackName := "rack1" + if err := controlapi.AddToScheme(kubeClient.Scheme()); err != nil { + panic(err) + } task, err := tasks.CreateRestartTask(context.Background(), kubeClient, dc, rackName) @@ -28,22 +32,23 @@ func TestCreateRestartTask(t *testing.T) { func TestCreateClusterRestartTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" rackName := "rack1" - + err := k8ssandrataskapi.AddToScheme(kubeClient.Scheme()) + assert.NoError(t, err) task, err := tasks.CreateClusterRestartTask(context.Background(), kubeClient, namespace, cluster, dcName, rackName) - assert.NoError(t, err) + assert.NotNil(t, task) assert.Equal(t, controlapi.CommandRestart, task.Spec.Template.Jobs[0].Command) } func TestCreateReplaceTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -61,7 +66,7 @@ func TestCreateReplaceTask(t *testing.T) { func TestCreateClusterReplaceTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -79,7 +84,7 @@ func TestCreateClusterReplaceTask(t *testing.T) { func TestCreateFlushTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -95,7 +100,7 @@ func TestCreateFlushTask(t *testing.T) { func TestCreateClusterFlushTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -111,7 +116,7 @@ func TestCreateClusterFlushTask(t *testing.T) { func TestCreateCleanupTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -127,7 +132,7 @@ func TestCreateCleanupTask(t *testing.T) { func TestCreateClusterCleanupTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -143,7 +148,7 @@ func TestCreateClusterCleanupTask(t *testing.T) { func TestCreateUpgradeSSTablesTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -159,7 +164,7 @@ func TestCreateUpgradeSSTablesTask(t *testing.T) { func TestCreateClusterUpgradeSSTablesTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -175,7 +180,7 @@ func TestCreateClusterUpgradeSSTablesTask(t *testing.T) { func TestCreateScrubTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -191,7 +196,7 @@ func TestCreateScrubTask(t *testing.T) { func TestCreateClusterScrubTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -207,7 +212,7 @@ func TestCreateClusterScrubTask(t *testing.T) { func TestCreateCompactionTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -235,7 +240,7 @@ func TestCreateCompactionTask(t *testing.T) { func TestCreateClusterCompactionTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -259,7 +264,7 @@ func TestCreateClusterCompactionTask(t *testing.T) { func TestCreateGCTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -275,7 +280,7 @@ func TestCreateGCTask(t *testing.T) { func TestCreateClusterGCTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -291,7 +296,7 @@ func TestCreateClusterGCTask(t *testing.T) { func TestCreateRebuildTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -312,7 +317,7 @@ func TestCreateRebuildTask(t *testing.T) { func TestCreateClusterRebuildTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -332,7 +337,7 @@ func TestCreateClusterRebuildTask(t *testing.T) { func TestCreateTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) command := controlapi.CommandRestart dc := &cassdcapi.CassandraDatacenter{} @@ -347,7 +352,7 @@ func TestCreateTask(t *testing.T) { func TestCreateTaskLongName(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) command := controlapi.CommandRestart dc := &cassdcapi.CassandraDatacenter{} @@ -362,7 +367,7 @@ func TestCreateTaskLongName(t *testing.T) { func TestCreateClusterWideTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.Client(namespace) + kubeClient := env.GetClient(namespace) cluster := "test-cluster" dcName := "" diff --git a/testfiles/crd/apiextensions.k8s.io_v1_customresourcedefinition_clientconfigs.config.k8ssandra.io.yaml b/testfiles/crd/apiextensions.k8s.io_v1_customresourcedefinition_clientconfigs.config.k8ssandra.io.yaml new file mode 100644 index 0000000..f03bb0a --- /dev/null +++ b/testfiles/crd/apiextensions.k8s.io_v1_customresourcedefinition_clientconfigs.config.k8ssandra.io.yaml @@ -0,0 +1,61 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: clientconfigs.config.k8ssandra.io +spec: + group: config.k8ssandra.io + names: + kind: ClientConfig + listKind: ClientConfigList + plural: clientconfigs + singular: clientconfig + scope: Namespaced + versions: + - name: v1beta1 + schema: + openAPIV3Schema: + description: ClientConfig is the Schema for the kubeconfigs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ClientConfigSpec defines the desired state of KubeConfig + properties: + contextName: + description: ContextName allows to override the object name for context-name. + If not set, the ClientConfig.Name is used as context name + type: string + kubeConfigSecret: + description: |- + KubeConfigSecret should reference an existing secret; the actual configuration will be read from + this secret's "kubeconfig" key. + properties: + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid? + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + served: true + storage: true From 3a73b93cf4db0029786cd20cd228a6430f6151e7 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 2 May 2024 12:32:04 +1000 Subject: [PATCH 02/25] De-MC the repo. --- cmd/kubectl-k8ssandra/register/command.go | 12 +++++----- .../register/register_test.go | 24 ++++++++++--------- .../register/registration.go | 22 +++++++++-------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index 5d0254e..40c714a 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -7,10 +7,6 @@ import ( "k8s.io/cli-runtime/pkg/genericclioptions" ) -var ( - StaticNamespace = "mission-control" -) - var RegisterClusterCmd = &cobra.Command{ Use: "register [flags]", Short: "register a data plane into the control plane.", @@ -27,8 +23,10 @@ func Init(cmd *cobra.Command, streams genericclioptions.IOStreams) { "path to destination cluster's kubeconfig file - defaults to KUBECONFIG then ~/.kube/config") RegisterClusterCmd.Flags().String("source-context", "", "context name for source cluster") RegisterClusterCmd.Flags().String("dest-context", "", "context name for destination cluster") - RegisterClusterCmd.Flags().String("serviceaccount-name", "mission-control", "serviceaccount name for destination cluster") - RegisterClusterCmd.Flags().String("destination-name", "remote-mission-control", "name for remote clientConfig and secret on destination cluster") + RegisterClusterCmd.Flags().String("source-namespace", "", "namespace containing service account for source cluster") + RegisterClusterCmd.Flags().String("dest-namespace", "", "namespace where secret and clientConfig will be created on destination cluster") + RegisterClusterCmd.Flags().String("serviceaccount-name", "k8ssandra-operator", "serviceaccount name for destination cluster") + RegisterClusterCmd.Flags().String("destination-name", "remote-k8ssandra-operator", "name for remote clientConfig and secret on destination cluster") cmd.AddCommand(RegisterClusterCmd) } @@ -57,6 +55,8 @@ func NewRegistrationExecutorFromRegisterClusterCmd(cmd cobra.Command) *Registrat DestKubeconfig: cmd.Flag("dest-kubeconfig").Value.String(), SourceContext: cmd.Flag("source-context").Value.String(), DestContext: cmd.Flag("dest-context").Value.String(), + SourceNamespace: cmd.Flag("source-namespace").Value.String(), + DestNamespace: cmd.Flag("dest-namespace").Value.String(), ServiceAccount: cmd.Flag("serviceaccount-name").Value.String(), Context: cmd.Context(), DestinationName: cmd.Flag("destination-name").Value.String(), diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index ad293dd..44f357d 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -19,14 +19,14 @@ import ( func TestRegister(t *testing.T) { require.New(t) - client1 := (*multiEnv)[0].GetClient("mission-control") - client2 := (*multiEnv)[1].GetClient("mission-control") + client1 := (*multiEnv)[0].GetClient("k8ssandra-operator") + client2 := (*multiEnv)[1].GetClient("k8ssandra-operator") - if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "mission-control"}}); err != nil { + if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}}); err != nil { t.Fatal(err) } - if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "mission-control"}}); err != nil { + if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}}); err != nil { t.Fatal(err) } @@ -77,7 +77,9 @@ func TestRegister(t *testing.T) { DestKubeconfig: buildDir + "/kubeconfig2", SourceContext: "default-context", DestContext: "default-context", - ServiceAccount: "mission-control", + SourceNamespace: "source-namespace", + DestNamespace: "dest-namespace", + ServiceAccount: "k8ssandra-operator", Context: context.TODO(), DestinationName: "test-destination", } @@ -90,7 +92,7 @@ func TestRegister(t *testing.T) { return true case res.IsError(): t.Log(res.GetError()) - if res.GetError().Error() == "no secret found for service account mission-control" { + if res.GetError().Error() == "no secret found for service account k8ssandra-operator" { return true } } @@ -100,7 +102,7 @@ func TestRegister(t *testing.T) { // This relies on a controller that is not running in the envtest. desiredSaSecret := &corev1.Secret{} - require.NoError(t, client1.Get(context.Background(), client.ObjectKey{Name: "mission-control-secret", Namespace: "mission-control"}, desiredSaSecret)) + require.NoError(t, client1.Get(context.Background(), client.ObjectKey{Name: "k8ssandra-operator-secret", Namespace: "source-namespace"}, desiredSaSecret)) patch := client.MergeFrom(desiredSaSecret.DeepCopy()) desiredSaSecret.Data = map[string][]byte{ "token": []byte("test-token"), @@ -111,13 +113,13 @@ func TestRegister(t *testing.T) { desiredSa := &corev1.ServiceAccount{} require.NoError(t, client1.Get( context.Background(), - client.ObjectKey{Name: "mission-control", Namespace: "mission-control"}, + client.ObjectKey{Name: "k8ssandra-operator", Namespace: "source-namespace"}, desiredSa)) patch = client.MergeFrom(desiredSa.DeepCopy()) desiredSa.Secrets = []corev1.ObjectReference{ { - Name: "mission-control-secret", + Name: "k8ssandra-operator-secret", }, } require.NoError(t, client1.Patch(ctx, desiredSa, patch)) @@ -142,14 +144,14 @@ func TestRegister(t *testing.T) { destSecret := &corev1.Secret{} require.Eventually(t, func() bool { err = client2.Get(ctx, - client.ObjectKey{Name: "test-destination", Namespace: "mission-control"}, destSecret) + client.ObjectKey{Name: "test-destination", Namespace: "dest-namespace"}, destSecret) if err != nil { t.Log("didn't find dest secret") return false } clientConfig := &configapi.ClientConfig{} err = client2.Get(ctx, - client.ObjectKey{Name: "test-destination", Namespace: "mission-control"}, clientConfig) + client.ObjectKey{Name: "test-destination", Namespace: "dest-namespace"}, clientConfig) if err != nil { t.Log("didn't find dest client config") return false diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index ae01138..439f009 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -21,15 +21,17 @@ type RegistrationExecutor struct { DestKubeconfig string SourceContext string DestContext string + SourceNamespace string + DestNamespace string ServiceAccount string Context context.Context } -func getDefaultSecret(saName string) *corev1.Secret { +func getDefaultSecret(saName, saNamespace string) *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: saName + "-secret", - Namespace: StaticNamespace, + Namespace: saNamespace, Annotations: map[string]string{ "kubernetes.io/service-account.name": saName, }, @@ -37,11 +39,11 @@ func getDefaultSecret(saName string) *corev1.Secret { Type: corev1.SecretTypeServiceAccountToken, } } -func getDefaultServiceAccount(saName string) *corev1.ServiceAccount { +func getDefaultServiceAccount(saName, saNamespace string) *corev1.ServiceAccount { return &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: saName, - Namespace: StaticNamespace, + Namespace: saNamespace, }, } @@ -62,15 +64,15 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { } // Get ServiceAccount serviceAccount := &corev1.ServiceAccount{} - if err := srcClient.Get(e.Context, client.ObjectKey{Name: e.ServiceAccount, Namespace: StaticNamespace}, serviceAccount); err != nil { - if err := srcClient.Create(e.Context, getDefaultServiceAccount(e.ServiceAccount)); err != nil { + if err := srcClient.Get(e.Context, client.ObjectKey{Name: e.ServiceAccount, Namespace: e.SourceNamespace}, serviceAccount); err != nil { + if err := srcClient.Create(e.Context, getDefaultServiceAccount(e.ServiceAccount, e.SourceNamespace)); err != nil { return result.Error(err) } return result.Error(err) } // Get a secret in this namespace which holds the service account token secretsList := &corev1.SecretList{} - if err := srcClient.List(e.Context, secretsList, client.InNamespace(StaticNamespace)); err != nil { + if err := srcClient.List(e.Context, secretsList, client.InNamespace(e.SourceNamespace)); err != nil { return result.Error(err) } var secret *corev1.Secret @@ -81,7 +83,7 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { } } if secret == nil { - secret = getDefaultSecret(e.ServiceAccount) + secret = getDefaultSecret(e.ServiceAccount, e.SourceNamespace) if err := srcClient.Create(e.Context, secret); err != nil { return result.Error(err) } @@ -104,7 +106,7 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { destSecret := corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: e.DestinationName, - Namespace: StaticNamespace, + Namespace: e.DestNamespace, }, Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ @@ -122,7 +124,7 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { destClientConfig := configapi.ClientConfig{ ObjectMeta: metav1.ObjectMeta{ Name: e.DestinationName, - Namespace: StaticNamespace, + Namespace: e.DestNamespace, }, Spec: configapi.ClientConfigSpec{ KubeConfigSecret: corev1.LocalObjectReference{ From eea7f979fb794fc83b62a7f1aa09673c7be24f7d Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 2 May 2024 12:34:45 +1000 Subject: [PATCH 03/25] Fix swapped namespaces. --- cmd/kubectl-k8ssandra/register/register_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 44f357d..bb5ce74 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -19,14 +19,14 @@ import ( func TestRegister(t *testing.T) { require.New(t) - client1 := (*multiEnv)[0].GetClient("k8ssandra-operator") - client2 := (*multiEnv)[1].GetClient("k8ssandra-operator") + client1 := (*multiEnv)[0].GetClient("source-namespace") + client2 := (*multiEnv)[1].GetClient("dest-namespace") - if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}}); err != nil { + if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}}); err != nil { t.Fatal(err) } - if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}}); err != nil { + if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}}); err != nil { t.Fatal(err) } From d3204c228b10b9fc603ddae2c9e02cc8a64b4014 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Fri, 3 May 2024 11:48:05 +1000 Subject: [PATCH 04/25] More error handling, more defaults. --- cmd/kubectl-k8ssandra/register/command.go | 4 ++-- cmd/kubectl-k8ssandra/register/registration.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index 40c714a..5a649c0 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -23,8 +23,8 @@ func Init(cmd *cobra.Command, streams genericclioptions.IOStreams) { "path to destination cluster's kubeconfig file - defaults to KUBECONFIG then ~/.kube/config") RegisterClusterCmd.Flags().String("source-context", "", "context name for source cluster") RegisterClusterCmd.Flags().String("dest-context", "", "context name for destination cluster") - RegisterClusterCmd.Flags().String("source-namespace", "", "namespace containing service account for source cluster") - RegisterClusterCmd.Flags().String("dest-namespace", "", "namespace where secret and clientConfig will be created on destination cluster") + RegisterClusterCmd.Flags().String("source-namespace", "k8ssandra-operator", "namespace containing service account for source cluster") + RegisterClusterCmd.Flags().String("dest-namespace", "k8ssandra-operator", "namespace where secret and clientConfig will be created on destination cluster") RegisterClusterCmd.Flags().String("serviceaccount-name", "k8ssandra-operator", "serviceaccount name for destination cluster") RegisterClusterCmd.Flags().String("destination-name", "remote-k8ssandra-operator", "name for remote clientConfig and secret on destination cluster") cmd.AddCommand(RegisterClusterCmd) diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index 439f009..de5d388 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -114,7 +114,7 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { }, } if err := destClient.Create(e.Context, &destSecret); err != nil { - return result.Error(err) + return result.Error(fmt.Errorf("error creating secret. err: %s sa %s", err, e.ServiceAccount)) } // Create ClientConfig on destination cluster From 78eb81a4ad3e71a8a94776b7ab048959396dcbf2 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Fri, 3 May 2024 12:12:28 +1000 Subject: [PATCH 05/25] Make some flags persistent. --- cmd/kubectl-k8ssandra/register/command.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index 5a649c0..b8ff6bf 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -27,6 +27,14 @@ func Init(cmd *cobra.Command, streams genericclioptions.IOStreams) { RegisterClusterCmd.Flags().String("dest-namespace", "k8ssandra-operator", "namespace where secret and clientConfig will be created on destination cluster") RegisterClusterCmd.Flags().String("serviceaccount-name", "k8ssandra-operator", "serviceaccount name for destination cluster") RegisterClusterCmd.Flags().String("destination-name", "remote-k8ssandra-operator", "name for remote clientConfig and secret on destination cluster") + + if err := RegisterClusterCmd.MarkFlagRequired("source-context"); err != nil { + panic(err) + + } + if err := RegisterClusterCmd.MarkFlagRequired("dest-context"); err != nil { + panic(err) + } cmd.AddCommand(RegisterClusterCmd) } From 7ef25987be0c89e8155095053526ef5c429ebcf3 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Fri, 3 May 2024 12:16:42 +1000 Subject: [PATCH 06/25] Error out if you try to register a cluster to itself. --- cmd/kubectl-k8ssandra/register/registration.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index de5d388..274bf72 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -2,6 +2,7 @@ package register import ( "context" + "errors" "fmt" "log" @@ -54,6 +55,9 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { registration.GetKubeconfigFileLocation(e.SourceKubeconfig), e.SourceContext, registration.GetKubeconfigFileLocation(e.DestKubeconfig), e.DestContext, ) + if e.SourceContext == e.DestContext && e.SourceKubeconfig == e.DestKubeconfig { + panic(errors.New("source and destination context and kubeconfig are the same, you should not register the same cluster to itself. Reference it by leaving the k8sContext field blank instead")) + } srcClient, err := registration.GetClient(e.SourceKubeconfig, e.SourceContext) if err != nil { return result.Error(err) From 82120d983446255ef1b67b9ac8810355759e7e0c Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 13:13:16 +1000 Subject: [PATCH 07/25] Remove calls to t.Fatal() as per Micke's request. --- .../register/register_test.go | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index bb5ce74..730d120 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -23,11 +23,11 @@ func TestRegister(t *testing.T) { client2 := (*multiEnv)[1].GetClient("dest-namespace") if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}}); err != nil { - t.Fatal(err) + require.NoError(t, err) } if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}}); err != nil { - t.Fatal(err) + require.NoError(t, err) } buildDir := os.Getenv("BUILD_DIR") @@ -38,39 +38,39 @@ func TestRegister(t *testing.T) { if _, err := os.Stat(buildDir); os.IsNotExist(err) { if err := os.Mkdir(buildDir, os.ModePerm); err != nil { - t.Fatal(err) + require.NoError(t, err) } } kc1, err := (*multiEnv)[0].GetKubeconfig(t) if err != nil { - t.Fatal(err) + require.NoError(t, err) } f, err := os.Create(buildDir + "/kubeconfig1") if err != nil { - t.Fatal(err) + require.NoError(t, err) } defer func() { if err := f.Close(); err != nil { - t.Fatal(err) + require.NoError(t, err) } }() if _, err := f.Write(kc1); err != nil { - t.Fatal(err) + require.NoError(t, err) } kc2, err := (*multiEnv)[1].GetKubeconfig(t) if err != nil { - t.Fatal(err) + require.NoError(t, err) } f, err = os.Create(buildDir + "/kubeconfig2") if err != nil { - t.Fatal(err) + require.NoError(t, err) } if _, err := f.Write(kc2); err != nil { - t.Fatal(err) + require.NoError(t, err) } ex := RegistrationExecutor{ SourceKubeconfig: buildDir + "/kubeconfig1", @@ -139,7 +139,7 @@ func TestRegister(t *testing.T) { }, time.Second*3000, time.Second*5) if err := configapi.AddToScheme(client2.Scheme()); err != nil { - t.Fatal(err) + require.NoError(t, err) } destSecret := &corev1.Secret{} require.Eventually(t, func() bool { From 13ccd30441e730a4268ceaaee7fd5aaeebb00c9f Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 14:27:46 +1000 Subject: [PATCH 08/25] Micke's feedback, fix up require so it doesn't require *testing.T, ensure IsNotFound is checked prior to Create. --- cmd/kubectl-k8ssandra/register/command.go | 7 ++- .../register/register_test.go | 55 +++++++++++-------- .../register/registration.go | 14 +++-- pkg/registration/get_kubeconfig_test.go | 11 ---- 4 files changed, 44 insertions(+), 43 deletions(-) delete mode 100644 pkg/registration/get_kubeconfig_test.go diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index b8ff6bf..da7a5a6 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -3,6 +3,7 @@ package register import ( "fmt" + "github.com/charmbracelet/log" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" ) @@ -44,13 +45,13 @@ func entrypoint(cmd *cobra.Command, args []string) { res := executor.RegisterCluster() switch { case res.IsError(): - fmt.Println("Registration continuing", res.GetError()) + log.Info("Registration continuing", "msg", res.GetError()) continue case res.Completed(): - fmt.Println("Registration completed successfully") + log.Info("Registration completed successfully") return case res.IsRequeue(): - fmt.Println("Registration continues") + log.Info("Registration continues") continue } } diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 730d120..0f347f2 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -18,16 +18,16 @@ import ( ) func TestRegister(t *testing.T) { - require.New(t) + require := require.New(t) client1 := (*multiEnv)[0].GetClient("source-namespace") client2 := (*multiEnv)[1].GetClient("dest-namespace") if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}}); err != nil { - require.NoError(t, err) + require.NoError(err) } if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}}); err != nil { - require.NoError(t, err) + require.NoError(err) } buildDir := os.Getenv("BUILD_DIR") @@ -38,39 +38,46 @@ func TestRegister(t *testing.T) { if _, err := os.Stat(buildDir); os.IsNotExist(err) { if err := os.Mkdir(buildDir, os.ModePerm); err != nil { - require.NoError(t, err) + require.NoError(err) } } kc1, err := (*multiEnv)[0].GetKubeconfig(t) if err != nil { - require.NoError(t, err) + require.NoError(err) } f, err := os.Create(buildDir + "/kubeconfig1") + t.Cleanup(func() { + require.NoError(f.Close()) + require.NoError(os.RemoveAll(buildDir + "/kubeconfig1")) + }) if err != nil { - require.NoError(t, err) + require.NoError(err) } - defer func() { - if err := f.Close(); err != nil { - require.NoError(t, err) - } - }() + t.Cleanup(func() { + require.NoError(f.Close()) + require.NoError(os.RemoveAll(buildDir + "/kubeconfig2")) + }) if _, err := f.Write(kc1); err != nil { - require.NoError(t, err) + require.NoError(err) } kc2, err := (*multiEnv)[1].GetKubeconfig(t) if err != nil { - require.NoError(t, err) + require.NoError(err) } f, err = os.Create(buildDir + "/kubeconfig2") if err != nil { - require.NoError(t, err) + require.NoError(err) } + t.Cleanup(func() { + require.NoError(f.Close()) + require.NoError(os.RemoveAll(buildDir + "/kubeconfig2")) + }) if _, err := f.Write(kc2); err != nil { - require.NoError(t, err) + require.NoError(err) } ex := RegistrationExecutor{ SourceKubeconfig: buildDir + "/kubeconfig1", @@ -85,7 +92,7 @@ func TestRegister(t *testing.T) { } ctx := context.Background() - require.Eventually(t, func() bool { + require.Eventually(func() bool { res := ex.RegisterCluster() switch { case res.IsDone(): @@ -102,16 +109,16 @@ func TestRegister(t *testing.T) { // This relies on a controller that is not running in the envtest. desiredSaSecret := &corev1.Secret{} - require.NoError(t, client1.Get(context.Background(), client.ObjectKey{Name: "k8ssandra-operator-secret", Namespace: "source-namespace"}, desiredSaSecret)) + require.NoError(client1.Get(context.Background(), client.ObjectKey{Name: "k8ssandra-operator-secret", Namespace: "source-namespace"}, desiredSaSecret)) patch := client.MergeFrom(desiredSaSecret.DeepCopy()) desiredSaSecret.Data = map[string][]byte{ "token": []byte("test-token"), "ca.crt": []byte("test-ca"), } - require.NoError(t, client1.Patch(ctx, desiredSaSecret, patch)) + require.NoError(client1.Patch(ctx, desiredSaSecret, patch)) desiredSa := &corev1.ServiceAccount{} - require.NoError(t, client1.Get( + require.NoError(client1.Get( context.Background(), client.ObjectKey{Name: "k8ssandra-operator", Namespace: "source-namespace"}, desiredSa)) @@ -122,11 +129,11 @@ func TestRegister(t *testing.T) { Name: "k8ssandra-operator-secret", }, } - require.NoError(t, client1.Patch(ctx, desiredSa, patch)) + require.NoError(client1.Patch(ctx, desiredSa, patch)) // Continue reconciliation - require.Eventually(t, func() bool { + require.Eventually(func() bool { res := ex.RegisterCluster() switch { case res.IsDone(): @@ -136,13 +143,13 @@ func TestRegister(t *testing.T) { return false } return false - }, time.Second*3000, time.Second*5) + }, time.Second*300, time.Second*1) if err := configapi.AddToScheme(client2.Scheme()); err != nil { - require.NoError(t, err) + require.NoError(err) } destSecret := &corev1.Secret{} - require.Eventually(t, func() bool { + require.Eventually(func() bool { err = client2.Get(ctx, client.ObjectKey{Name: "test-destination", Namespace: "dest-namespace"}, destSecret) if err != nil { diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index 274bf72..90c95a5 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -4,9 +4,10 @@ import ( "context" "errors" "fmt" - "log" + "github.com/charmbracelet/log" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" @@ -28,7 +29,7 @@ type RegistrationExecutor struct { Context context.Context } -func getDefaultSecret(saName, saNamespace string) *corev1.Secret { +func getDefaultSecret(saNamespace, saName string) *corev1.Secret { return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: saName + "-secret", @@ -40,6 +41,7 @@ func getDefaultSecret(saName, saNamespace string) *corev1.Secret { Type: corev1.SecretTypeServiceAccountToken, } } + func getDefaultServiceAccount(saName, saNamespace string) *corev1.ServiceAccount { return &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ @@ -69,8 +71,10 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { // Get ServiceAccount serviceAccount := &corev1.ServiceAccount{} if err := srcClient.Get(e.Context, client.ObjectKey{Name: e.ServiceAccount, Namespace: e.SourceNamespace}, serviceAccount); err != nil { - if err := srcClient.Create(e.Context, getDefaultServiceAccount(e.ServiceAccount, e.SourceNamespace)); err != nil { - return result.Error(err) + if apierrors.IsNotFound(err) { + if err := srcClient.Create(e.Context, getDefaultServiceAccount(e.ServiceAccount, e.SourceNamespace)); err != nil { + return result.Error(err) + } } return result.Error(err) } @@ -87,7 +91,7 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { } } if secret == nil { - secret = getDefaultSecret(e.ServiceAccount, e.SourceNamespace) + secret = getDefaultSecret(e.SourceNamespace, e.ServiceAccount) if err := srcClient.Create(e.Context, secret); err != nil { return result.Error(err) } diff --git a/pkg/registration/get_kubeconfig_test.go b/pkg/registration/get_kubeconfig_test.go deleted file mode 100644 index 91694c3..0000000 --- a/pkg/registration/get_kubeconfig_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package registration - -import "testing" - -func TestGetKubeconfigFileLocation(t *testing.T) { - // TODO -} - -func TestGetClient(t *testing.T) { - // TODO -} From 54bfc21041b3bdbb1aa85b805dc2688c187bb612 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 14:28:27 +1000 Subject: [PATCH 09/25] Resolve Micke's issues with GetClient. --- .../register/register_test.go | 4 +- internal/envtest/envtest.go | 2 +- pkg/helmutil/crds_test.go | 2 +- pkg/tasks/create_test.go | 42 +++++++++---------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 0f347f2..9a60f85 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -19,8 +19,8 @@ import ( func TestRegister(t *testing.T) { require := require.New(t) - client1 := (*multiEnv)[0].GetClient("source-namespace") - client2 := (*multiEnv)[1].GetClient("dest-namespace") + client1 := (*multiEnv)[0].GetClientInNamespace("source-namespace") + client2 := (*multiEnv)[1].GetClientInNamespace("dest-namespace") if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}}); err != nil { require.NoError(err) diff --git a/internal/envtest/envtest.go b/internal/envtest/envtest.go index ed153ef..d742fea 100644 --- a/internal/envtest/envtest.go +++ b/internal/envtest/envtest.go @@ -53,7 +53,7 @@ func NewEnvironment(ctx context.Context) *Environment { return env } -func (e *Environment) GetClient(namespace string) client.Client { +func (e *Environment) GetClientInNamespace(namespace string) client.Client { c, err := kubernetes.GetClientInNamespace(e.env.Config, namespace) if err != nil { panic(err) diff --git a/pkg/helmutil/crds_test.go b/pkg/helmutil/crds_test.go index f0d5898..017767b 100644 --- a/pkg/helmutil/crds_test.go +++ b/pkg/helmutil/crds_test.go @@ -20,7 +20,7 @@ func TestUpgradingCRDs(t *testing.T) { chartNames := []string{"cass-operator"} for _, chartName := range chartNames { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) require.NoError(cleanCache("k8ssandra", chartName)) // creating new upgrader diff --git a/pkg/tasks/create_test.go b/pkg/tasks/create_test.go index 77e554a..1b35a51 100644 --- a/pkg/tasks/create_test.go +++ b/pkg/tasks/create_test.go @@ -14,7 +14,7 @@ import ( func TestCreateRestartTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -32,7 +32,7 @@ func TestCreateRestartTask(t *testing.T) { func TestCreateClusterRestartTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -48,7 +48,7 @@ func TestCreateClusterRestartTask(t *testing.T) { func TestCreateReplaceTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -66,7 +66,7 @@ func TestCreateReplaceTask(t *testing.T) { func TestCreateClusterReplaceTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -84,7 +84,7 @@ func TestCreateClusterReplaceTask(t *testing.T) { func TestCreateFlushTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -100,7 +100,7 @@ func TestCreateFlushTask(t *testing.T) { func TestCreateClusterFlushTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -116,7 +116,7 @@ func TestCreateClusterFlushTask(t *testing.T) { func TestCreateCleanupTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -132,7 +132,7 @@ func TestCreateCleanupTask(t *testing.T) { func TestCreateClusterCleanupTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -148,7 +148,7 @@ func TestCreateClusterCleanupTask(t *testing.T) { func TestCreateUpgradeSSTablesTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -164,7 +164,7 @@ func TestCreateUpgradeSSTablesTask(t *testing.T) { func TestCreateClusterUpgradeSSTablesTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -180,7 +180,7 @@ func TestCreateClusterUpgradeSSTablesTask(t *testing.T) { func TestCreateScrubTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -196,7 +196,7 @@ func TestCreateScrubTask(t *testing.T) { func TestCreateClusterScrubTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -212,7 +212,7 @@ func TestCreateClusterScrubTask(t *testing.T) { func TestCreateCompactionTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -240,7 +240,7 @@ func TestCreateCompactionTask(t *testing.T) { func TestCreateClusterCompactionTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -264,7 +264,7 @@ func TestCreateClusterCompactionTask(t *testing.T) { func TestCreateGCTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -280,7 +280,7 @@ func TestCreateGCTask(t *testing.T) { func TestCreateClusterGCTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -296,7 +296,7 @@ func TestCreateClusterGCTask(t *testing.T) { func TestCreateRebuildTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) dc := &cassdcapi.CassandraDatacenter{} dc.Name = "test-dc" @@ -317,7 +317,7 @@ func TestCreateRebuildTask(t *testing.T) { func TestCreateClusterRebuildTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "test-dc" @@ -337,7 +337,7 @@ func TestCreateClusterRebuildTask(t *testing.T) { func TestCreateTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) command := controlapi.CommandRestart dc := &cassdcapi.CassandraDatacenter{} @@ -352,7 +352,7 @@ func TestCreateTask(t *testing.T) { func TestCreateTaskLongName(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) command := controlapi.CommandRestart dc := &cassdcapi.CassandraDatacenter{} @@ -367,7 +367,7 @@ func TestCreateTaskLongName(t *testing.T) { func TestCreateClusterWideTask(t *testing.T) { namespace := env.CreateNamespace(t) - kubeClient := env.GetClient(namespace) + kubeClient := env.GetClientInNamespace(namespace) cluster := "test-cluster" dcName := "" From 4a69e6556152914f7f3e420afd2e0aa02c8556f0 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 14:33:46 +1000 Subject: [PATCH 10/25] Fix unecessary *testing.T. --- internal/envtest/envtest.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/envtest/envtest.go b/internal/envtest/envtest.go index d742fea..a3fcac6 100644 --- a/internal/envtest/envtest.go +++ b/internal/envtest/envtest.go @@ -125,7 +125,7 @@ func (e *Environment) CreateNamespace(t *testing.T) string { return namespace } -func (e *Environment) GetKubeconfig(t *testing.T) ([]byte, error) { +func (e *Environment) GetKubeconfig() ([]byte, error) { clientConfig, err := CreateKubeconfigFileForRestConfig(e.env.Config) if err != nil { return nil, err From 0823adcbc1ea398c87a3c6debbc157cdd1291cae Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 14:35:09 +1000 Subject: [PATCH 11/25] Remove unused file. --- internal/envtest/images.go | 41 -------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 internal/envtest/images.go diff --git a/internal/envtest/images.go b/internal/envtest/images.go deleted file mode 100644 index 3a46851..0000000 --- a/internal/envtest/images.go +++ /dev/null @@ -1,41 +0,0 @@ -package envtest - -import ( - "context" - "os" - "path/filepath" - - "github.com/k8ssandra/k8ssandra-client/pkg/kubernetes" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func DeployImageConfig(cli client.Client) error { - configPath := filepath.Join(RootDir(), "testfiles", "image_config.yaml") - b, err := os.ReadFile(configPath) - if err != nil { - return err - } - - if err := kubernetes.CreateNamespaceIfNotExists(cli, "mission-control"); err != nil { - return err - } - - confMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cass-operator-manager-config", - Namespace: "mission-control", - }, - Data: map[string]string{ - "image_config.yaml": string(b), - }, - } - - if err := cli.Create(context.TODO(), confMap); err != nil && !errors.IsAlreadyExists(err) { - return err - } - - return nil -} From c6f50d90fdddbdaae96f2582ffaf91cb293451f1 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 16:11:23 +1000 Subject: [PATCH 12/25] Rename cmd init func to SetupRegisterClusterCmd and clean up other mistakes. --- cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go | 2 +- cmd/kubectl-k8ssandra/register/command.go | 2 +- .../register/register_test.go | 75 +++++++------------ 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go b/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go index f6d583e..4631978 100644 --- a/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go +++ b/cmd/kubectl-k8ssandra/k8ssandra/k8ssandra.go @@ -54,7 +54,7 @@ func NewCmd(streams genericclioptions.IOStreams) *cobra.Command { // cmd.AddCommand(migrate.NewInstallCmd(streams)) cmd.AddCommand(config.NewCmd(streams)) cmd.AddCommand(helm.NewHelmCmd(streams)) - register.Init(cmd, streams) + register.SetupRegisterClusterCmd(cmd, streams) // cmd.Flags().BoolVar(&o.listNamespaces, "list", o.listNamespaces, "if true, print the list of all namespaces in the current KUBECONFIG") o.configFlags.AddFlags(cmd.Flags()) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index da7a5a6..5af263c 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -15,7 +15,7 @@ var RegisterClusterCmd = &cobra.Command{ Run: entrypoint, } -func Init(cmd *cobra.Command, streams genericclioptions.IOStreams) { +func SetupRegisterClusterCmd(cmd *cobra.Command, streams genericclioptions.IOStreams) { RegisterClusterCmd.Flags().String("source-kubeconfig", "", "path to source cluster's kubeconfig file - defaults to KUBECONFIG then ~/.kube/config") diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 9a60f85..2fd0d5f 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -21,14 +21,8 @@ func TestRegister(t *testing.T) { require := require.New(t) client1 := (*multiEnv)[0].GetClientInNamespace("source-namespace") client2 := (*multiEnv)[1].GetClientInNamespace("dest-namespace") - - if err := client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}}); err != nil { - require.NoError(err) - } - - if err := client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}}); err != nil { - require.NoError(err) - } + require.NoError(client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}})) + require.NoError(client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}})) buildDir := os.Getenv("BUILD_DIR") if buildDir == "" { @@ -36,52 +30,41 @@ func TestRegister(t *testing.T) { buildDir = filepath.Join(filepath.Dir(b), "../../../build") } - if _, err := os.Stat(buildDir); os.IsNotExist(err) { - if err := os.Mkdir(buildDir, os.ModePerm); err != nil { - require.NoError(err) - } - } + testDir := filepath.Join(buildDir, time.Now().String()) - kc1, err := (*multiEnv)[0].GetKubeconfig(t) - if err != nil { + if _, err := os.Stat(testDir); os.IsNotExist(err) { + err := os.Mkdir(testDir, os.ModePerm) require.NoError(err) - - } - f, err := os.Create(buildDir + "/kubeconfig1") - t.Cleanup(func() { - require.NoError(f.Close()) - require.NoError(os.RemoveAll(buildDir + "/kubeconfig1")) - }) - if err != nil { + } else if err != nil { require.NoError(err) } + + kc1, err := (*multiEnv)[0].GetKubeconfig() + require.NoError(err) + f1, err := os.Create(testDir + "/kubeconfig1") + require.NoError(err) t.Cleanup(func() { - require.NoError(f.Close()) - require.NoError(os.RemoveAll(buildDir + "/kubeconfig2")) + require.NoError(f1.Close()) + require.NoError(os.RemoveAll(testDir + "/kubeconfig1")) }) - if _, err := f.Write(kc1); err != nil { - require.NoError(err) - } + _, err = f1.Write(kc1) + require.NoError(err) - kc2, err := (*multiEnv)[1].GetKubeconfig(t) - if err != nil { - require.NoError(err) - - } - f, err = os.Create(buildDir + "/kubeconfig2") - if err != nil { - require.NoError(err) - } + f2, err := os.Create(testDir + "/kubeconfig2") + require.NoError(err) t.Cleanup(func() { - require.NoError(f.Close()) - require.NoError(os.RemoveAll(buildDir + "/kubeconfig2")) + require.NoError(f2.Close()) + require.NoError(os.RemoveAll(testDir + "/kubeconfig2")) }) - if _, err := f.Write(kc2); err != nil { - require.NoError(err) - } + + kc2, err := (*multiEnv)[1].GetKubeconfig() + require.NoError(err) + _, err = f2.Write(kc2) + require.NoError(err) + ex := RegistrationExecutor{ - SourceKubeconfig: buildDir + "/kubeconfig1", - DestKubeconfig: buildDir + "/kubeconfig2", + SourceKubeconfig: testDir + "/kubeconfig1", + DestKubeconfig: testDir + "/kubeconfig2", SourceContext: "default-context", DestContext: "default-context", SourceNamespace: "source-namespace", @@ -167,11 +150,11 @@ func TestRegister(t *testing.T) { }, time.Second*60, time.Second*5) destKubeconfig := ClientConfigFromSecret(destSecret) - require.Equal(t, + require.Equal( desiredSaSecret.Data["ca.crt"], destKubeconfig.Clusters["cluster"].CertificateAuthorityData) - require.Equal(t, + require.Equal( string(desiredSaSecret.Data["token"]), destKubeconfig.AuthInfos["cluster"].Token) } From 77954835630b48c24c70242b86240c9b9995a5aa Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 16:15:11 +1000 Subject: [PATCH 13/25] Use envtest.RootDir() where possible, --- cmd/kubectl-k8ssandra/register/register_test.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 2fd0d5f..7e34ff7 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -4,10 +4,10 @@ import ( "context" "os" "path/filepath" - "runtime" "testing" "time" + "github.com/k8ssandra/k8ssandra-client/internal/envtest" configapi "github.com/k8ssandra/k8ssandra-operator/apis/config/v1beta1" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -24,12 +24,7 @@ func TestRegister(t *testing.T) { require.NoError(client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}})) require.NoError(client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}})) - buildDir := os.Getenv("BUILD_DIR") - if buildDir == "" { - _, b, _, _ := runtime.Caller(0) - buildDir = filepath.Join(filepath.Dir(b), "../../../build") - } - + buildDir := filepath.Join(envtest.RootDir(), "build") testDir := filepath.Join(buildDir, time.Now().String()) if _, err := os.Stat(testDir); os.IsNotExist(err) { From f35eda301fdc82d06a362404c3952891abe87710 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 16:19:54 +1000 Subject: [PATCH 14/25] File naming. --- .../{get_kubeconfig.go => get_client_from_kubeconfig.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pkg/registration/{get_kubeconfig.go => get_client_from_kubeconfig.go} (100%) diff --git a/pkg/registration/get_kubeconfig.go b/pkg/registration/get_client_from_kubeconfig.go similarity index 100% rename from pkg/registration/get_kubeconfig.go rename to pkg/registration/get_client_from_kubeconfig.go From 08c6eff73e0928aa00aff227ceefdf6a9ef1c2ae Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Thu, 16 May 2024 16:53:56 +1000 Subject: [PATCH 15/25] Make client private in Environment. --- internal/envtest/envtest.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/envtest/envtest.go b/internal/envtest/envtest.go index a3fcac6..5459e89 100644 --- a/internal/envtest/envtest.go +++ b/internal/envtest/envtest.go @@ -32,7 +32,7 @@ func Run(m *testing.M, setupFunc func(e *Environment)) (code int) { } type Environment struct { - Client client.Client + client client.Client env *envtest.Environment cancelManager context.CancelFunc Context context.Context @@ -66,7 +66,7 @@ func (e *Environment) RestConfig() *rest.Config { } func (e *Environment) RawClient() client.Client { - return e.Client + return e.client } func (e *Environment) Start() { @@ -102,7 +102,7 @@ func (e *Environment) Start() { //+kubebuilder:scaffold:scheme - e.Client = k8sClient + e.client = k8sClient // e.Kubeconfig, err = CreateKubeconfigFileForRestConfig(e.env.Config) // if err != nil { // panic(err) @@ -118,7 +118,7 @@ func (e *Environment) Stop() { func (e *Environment) CreateNamespace(t *testing.T) string { namespace := strings.ToLower(t.Name()) - if err := kubernetes.CreateNamespaceIfNotExists(e.Client, namespace); err != nil { + if err := kubernetes.CreateNamespaceIfNotExists(e.client, namespace); err != nil { t.FailNow() } From 225d5bb77c6b225ebfe94af0a65fd63297ddfe24 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Fri, 17 May 2024 13:46:31 +1000 Subject: [PATCH 16/25] Make sure whole test directory gets cleaned up. --- cmd/kubectl-k8ssandra/register/register_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 7e34ff7..bf9ebf5 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -33,6 +33,9 @@ func TestRegister(t *testing.T) { } else if err != nil { require.NoError(err) } + t.Cleanup(func() { + require.NoError(os.RemoveAll(testDir)) + }) kc1, err := (*multiEnv)[0].GetKubeconfig() require.NoError(err) @@ -40,7 +43,6 @@ func TestRegister(t *testing.T) { require.NoError(err) t.Cleanup(func() { require.NoError(f1.Close()) - require.NoError(os.RemoveAll(testDir + "/kubeconfig1")) }) _, err = f1.Write(kc1) require.NoError(err) @@ -49,7 +51,6 @@ func TestRegister(t *testing.T) { require.NoError(err) t.Cleanup(func() { require.NoError(f2.Close()) - require.NoError(os.RemoveAll(testDir + "/kubeconfig2")) }) kc2, err := (*multiEnv)[1].GetKubeconfig() From 96095c70d3c59be261cc490f79eb8cfb726d382a Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Fri, 17 May 2024 16:25:22 +1000 Subject: [PATCH 17/25] Ensure the build directory is created if it does not exist in tests. --- cmd/kubectl-k8ssandra/register/register_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index bf9ebf5..301636a 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -28,7 +28,7 @@ func TestRegister(t *testing.T) { testDir := filepath.Join(buildDir, time.Now().String()) if _, err := os.Stat(testDir); os.IsNotExist(err) { - err := os.Mkdir(testDir, os.ModePerm) + err := os.MkdirAll(testDir, os.ModePerm) require.NoError(err) } else if err != nil { require.NoError(err) From c0197edc1f8664ee508d40473d9787983c24664f Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 20 May 2024 17:04:52 +1000 Subject: [PATCH 18/25] Micke's requested changes. --- cmd/kubectl-k8ssandra/register/command.go | 1 + .../register/register_test.go | 22 +++++++++---------- .../register/registration.go | 2 +- internal/envtest/envtest.go | 1 - .../envtest/{framework.go => root_dir.go} | 0 5 files changed, 13 insertions(+), 13 deletions(-) rename internal/envtest/{framework.go => root_dir.go} (100%) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index 5af263c..8b8734b 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -41,6 +41,7 @@ func SetupRegisterClusterCmd(cmd *cobra.Command, streams genericclioptions.IOStr func entrypoint(cmd *cobra.Command, args []string) { executor := NewRegistrationExecutorFromRegisterClusterCmd(*cmd) + // insert validation that if e.SourceContext == e.DestContext && e.SourceKubeconfig == e.DestKubeconfig { for i := 0; i < 30; i++ { res := executor.RegisterCluster() switch { diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 301636a..ce80681 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -3,11 +3,9 @@ package register import ( "context" "os" - "path/filepath" "testing" "time" - "github.com/k8ssandra/k8ssandra-client/internal/envtest" configapi "github.com/k8ssandra/k8ssandra-operator/apis/config/v1beta1" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -24,15 +22,17 @@ func TestRegister(t *testing.T) { require.NoError(client1.Create((*multiEnv)[0].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "source-namespace"}})) require.NoError(client2.Create((*multiEnv)[1].Context, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "dest-namespace"}})) - buildDir := filepath.Join(envtest.RootDir(), "build") - testDir := filepath.Join(buildDir, time.Now().String()) - - if _, err := os.Stat(testDir); os.IsNotExist(err) { - err := os.MkdirAll(testDir, os.ModePerm) - require.NoError(err) - } else if err != nil { - require.NoError(err) - } + testDir, err := os.MkdirTemp("", "k8ssandra-operator-test-****") + require.NoError(err) + // buildDir := filepath.Join(envtest.RootDir(), "build") + // testDir := filepath.Join(buildDir, time.Now()) + + // if _, err := os.Stat(testDir); os.IsNotExist(err) { + // err := os.MkdirAll(testDir, os.ModePerm) + // require.NoError(err) + // } else if err != nil { + // require.NoError(err) + // } t.Cleanup(func() { require.NoError(os.RemoveAll(testDir)) }) diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index 90c95a5..84d617e 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/charmbracelet/log" corev1 "k8s.io/api/core/v1" @@ -49,7 +50,6 @@ func getDefaultServiceAccount(saName, saNamespace string) *corev1.ServiceAccount Namespace: saNamespace, }, } - } func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { diff --git a/internal/envtest/envtest.go b/internal/envtest/envtest.go index 5459e89..120c290 100644 --- a/internal/envtest/envtest.go +++ b/internal/envtest/envtest.go @@ -36,7 +36,6 @@ type Environment struct { env *envtest.Environment cancelManager context.CancelFunc Context context.Context - Kubeconfig string } func NewEnvironment(ctx context.Context) *Environment { diff --git a/internal/envtest/framework.go b/internal/envtest/root_dir.go similarity index 100% rename from internal/envtest/framework.go rename to internal/envtest/root_dir.go From e62fdfb1f8ceecd444e6b2adf85bc0dc8d52f4e0 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 20 May 2024 17:38:41 +1000 Subject: [PATCH 19/25] Remove reconcile.Result --- cmd/kubectl-k8ssandra/register/command.go | 13 ++++---- cmd/kubectl-k8ssandra/register/errors.go | 17 ++++++++++ .../register/register_test.go | 23 ++++++-------- .../register/registration.go | 31 +++++++++---------- 4 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 cmd/kubectl-k8ssandra/register/errors.go diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index 8b8734b..ff0d89c 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -44,16 +44,15 @@ func entrypoint(cmd *cobra.Command, args []string) { // insert validation that if e.SourceContext == e.DestContext && e.SourceKubeconfig == e.DestKubeconfig { for i := 0; i < 30; i++ { res := executor.RegisterCluster() - switch { - case res.IsError(): - log.Info("Registration continuing", "msg", res.GetError()) + switch v := res.(type) { + case RetryableError: + log.Info("Registration continuing", "msg", v.Error()) continue - case res.Completed(): + case nil: log.Info("Registration completed successfully") return - case res.IsRequeue(): - log.Info("Registration continues") - continue + case NonRecoverableError: + panic(fmt.Sprintf("Registration failed: %s", v.Error())) } } fmt.Println("Registration failed - retries exceeded") diff --git a/cmd/kubectl-k8ssandra/register/errors.go b/cmd/kubectl-k8ssandra/register/errors.go new file mode 100644 index 0000000..1a86f87 --- /dev/null +++ b/cmd/kubectl-k8ssandra/register/errors.go @@ -0,0 +1,17 @@ +package register + +type RetryableError struct { + Message string +} + +func (e RetryableError) Error() string { + return e.Message +} + +type NonRecoverableError struct { + Message string +} + +func (e NonRecoverableError) Error() string { + return e.Message +} diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index ce80681..9609bad 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -2,6 +2,7 @@ package register import ( "context" + "fmt" "os" "testing" "time" @@ -73,14 +74,15 @@ func TestRegister(t *testing.T) { require.Eventually(func() bool { res := ex.RegisterCluster() - switch { - case res.IsDone(): - return true - case res.IsError(): - t.Log(res.GetError()) - if res.GetError().Error() == "no secret found for service account k8ssandra-operator" { + switch v := res.(type) { + case RetryableError: + if res.Error() == "no secret found for service account k8ssandra-operator" { return true } + case nil: + return true + case NonRecoverableError: + panic(fmt.Sprintf("Registration failed: %s", v.Error())) } return false }, time.Second*30, time.Second*5) @@ -114,14 +116,7 @@ func TestRegister(t *testing.T) { require.Eventually(func() bool { res := ex.RegisterCluster() - switch { - case res.IsDone(): - return true - case res.IsError(): - t.Log(res.GetError()) - return false - } - return false + return res == nil }, time.Second*300, time.Second*1) if err := configapi.AddToScheme(client2.Scheme()); err != nil { diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index 84d617e..37a8ed5 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -15,7 +15,6 @@ import ( "github.com/k8ssandra/k8ssandra-client/pkg/registration" configapi "github.com/k8ssandra/k8ssandra-operator/apis/config/v1beta1" - "github.com/k8ssandra/k8ssandra-operator/pkg/result" ) type RegistrationExecutor struct { @@ -52,7 +51,7 @@ func getDefaultServiceAccount(saName, saNamespace string) *corev1.ServiceAccount } } -func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { +func (e *RegistrationExecutor) RegisterCluster() error { log.Printf("Registering cluster from %s Context: %s to %s Context: %s", registration.GetKubeconfigFileLocation(e.SourceKubeconfig), e.SourceContext, registration.GetKubeconfigFileLocation(e.DestKubeconfig), e.DestContext, @@ -62,26 +61,26 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { } srcClient, err := registration.GetClient(e.SourceKubeconfig, e.SourceContext) if err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } destClient, err := registration.GetClient(e.DestKubeconfig, e.DestContext) if err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } // Get ServiceAccount serviceAccount := &corev1.ServiceAccount{} if err := srcClient.Get(e.Context, client.ObjectKey{Name: e.ServiceAccount, Namespace: e.SourceNamespace}, serviceAccount); err != nil { if apierrors.IsNotFound(err) { if err := srcClient.Create(e.Context, getDefaultServiceAccount(e.ServiceAccount, e.SourceNamespace)); err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } } - return result.Error(err) + return RetryableError{Message: err.Error()} } // Get a secret in this namespace which holds the service account token secretsList := &corev1.SecretList{} if err := srcClient.List(e.Context, secretsList, client.InNamespace(e.SourceNamespace)); err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } var secret *corev1.Secret for _, s := range secretsList.Items { @@ -93,23 +92,23 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { if secret == nil { secret = getDefaultSecret(e.SourceNamespace, e.ServiceAccount) if err := srcClient.Create(e.Context, secret); err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } - return result.Error(fmt.Errorf("no secret found for service account %s", e.ServiceAccount)) + return RetryableError{Message: fmt.Sprintf("no secret found for service account %s", e.ServiceAccount)} } // Create Secret on destination cluster host, err := registration.KubeconfigToHost(e.SourceKubeconfig, e.SourceContext) if err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } saConfig, err := registration.TokenToKubeconfig(*secret, host) if err != nil { - return result.Error(fmt.Errorf("error converting token to kubeconfig: %w, secret: %#v", err, secret)) + return RetryableError{fmt.Sprintf("error converting token to kubeconfig: %s, secret: %#v", err.Error(), secret)} } secretData, err := clientcmd.Write(saConfig) if err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } destSecret := corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ @@ -122,12 +121,12 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { }, } if err := destClient.Create(e.Context, &destSecret); err != nil { - return result.Error(fmt.Errorf("error creating secret. err: %s sa %s", err, e.ServiceAccount)) + return RetryableError{fmt.Sprintf("error creating secret. err: %s sa %s", err, e.ServiceAccount)} } // Create ClientConfig on destination cluster if err := configapi.AddToScheme(destClient.Scheme()); err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } destClientConfig := configapi.ClientConfig{ ObjectMeta: metav1.ObjectMeta{ @@ -141,7 +140,7 @@ func (e *RegistrationExecutor) RegisterCluster() result.ReconcileResult { }, } if err := destClient.Create(e.Context, &destClientConfig); err != nil { - return result.Error(err) + return RetryableError{Message: err.Error()} } - return result.Done() + return nil } From 12ae412fcb6a774d119a9b867ba6a4563b4675bc Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 20 May 2024 18:20:36 +1000 Subject: [PATCH 20/25] Clean up handling of error when src and dest context are the same. --- cmd/kubectl-k8ssandra/register/command.go | 2 +- cmd/kubectl-k8ssandra/register/registration.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index ff0d89c..700a914 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -41,7 +41,7 @@ func SetupRegisterClusterCmd(cmd *cobra.Command, streams genericclioptions.IOStr func entrypoint(cmd *cobra.Command, args []string) { executor := NewRegistrationExecutorFromRegisterClusterCmd(*cmd) - // insert validation that if e.SourceContext == e.DestContext && e.SourceKubeconfig == e.DestKubeconfig { + for i := 0; i < 30; i++ { res := executor.RegisterCluster() switch v := res.(type) { diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index 37a8ed5..976d006 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -2,7 +2,6 @@ package register import ( "context" - "errors" "fmt" "github.com/charmbracelet/log" @@ -57,7 +56,7 @@ func (e *RegistrationExecutor) RegisterCluster() error { registration.GetKubeconfigFileLocation(e.DestKubeconfig), e.DestContext, ) if e.SourceContext == e.DestContext && e.SourceKubeconfig == e.DestKubeconfig { - panic(errors.New("source and destination context and kubeconfig are the same, you should not register the same cluster to itself. Reference it by leaving the k8sContext field blank instead")) + return NonRecoverableError{Message: "source and destination context and kubeconfig are the same, you should not register the same cluster to itself. Reference it by leaving the k8sContext field blank instead"} } srcClient, err := registration.GetClient(e.SourceKubeconfig, e.SourceContext) if err != nil { From 8e1334ad5a28f9ec18e2f4f02339add00cec5073 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 27 May 2024 13:11:58 +1000 Subject: [PATCH 21/25] destination name from source context name as per Alex' request. --- cmd/kubectl-k8ssandra/register/command.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index 700a914..5752083 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -27,7 +27,7 @@ func SetupRegisterClusterCmd(cmd *cobra.Command, streams genericclioptions.IOStr RegisterClusterCmd.Flags().String("source-namespace", "k8ssandra-operator", "namespace containing service account for source cluster") RegisterClusterCmd.Flags().String("dest-namespace", "k8ssandra-operator", "namespace where secret and clientConfig will be created on destination cluster") RegisterClusterCmd.Flags().String("serviceaccount-name", "k8ssandra-operator", "serviceaccount name for destination cluster") - RegisterClusterCmd.Flags().String("destination-name", "remote-k8ssandra-operator", "name for remote clientConfig and secret on destination cluster") + RegisterClusterCmd.Flags().String("destination-name", "", "name for remote clientConfig and secret on destination cluster") if err := RegisterClusterCmd.MarkFlagRequired("source-context"); err != nil { panic(err) @@ -59,15 +59,21 @@ func entrypoint(cmd *cobra.Command, args []string) { } func NewRegistrationExecutorFromRegisterClusterCmd(cmd cobra.Command) *RegistrationExecutor { + + destName := cmd.Flag("destination-name").Value.String() + srcContext := cmd.Flag("source-context").Value.String() + if destName == "" { + destName = srcContext + } return &RegistrationExecutor{ SourceKubeconfig: cmd.Flag("source-kubeconfig").Value.String(), DestKubeconfig: cmd.Flag("dest-kubeconfig").Value.String(), - SourceContext: cmd.Flag("source-context").Value.String(), + SourceContext: srcContext, DestContext: cmd.Flag("dest-context").Value.String(), SourceNamespace: cmd.Flag("source-namespace").Value.String(), DestNamespace: cmd.Flag("dest-namespace").Value.String(), ServiceAccount: cmd.Flag("serviceaccount-name").Value.String(), Context: cmd.Context(), - DestinationName: cmd.Flag("destination-name").Value.String(), + DestinationName: destName, } } From b191ada3968f5090ef1a4545acc867d2a1671916 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 27 May 2024 13:24:24 +1000 Subject: [PATCH 22/25] Ensure the default context is set on the registration kubeconfig secret. --- pkg/registration/token_to_kubeconfig.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/registration/token_to_kubeconfig.go b/pkg/registration/token_to_kubeconfig.go index 2e91a85..de18bf7 100644 --- a/pkg/registration/token_to_kubeconfig.go +++ b/pkg/registration/token_to_kubeconfig.go @@ -32,5 +32,6 @@ func TokenToKubeconfig(s corev1.Secret, server string) (clientcmdapi.Config, err AuthInfo: "cluster", }, }, + CurrentContext: "cluster", }, nil } From 9f5554d790403210c60082c881342e64d223c3a2 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 27 May 2024 14:26:11 +1000 Subject: [PATCH 23/25] Use sanitized src context name as default secret and clientConfig names. --- cmd/kubectl-k8ssandra/register/command.go | 3 ++- pkg/registration/sanitize_strings.go | 25 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 pkg/registration/sanitize_strings.go diff --git a/cmd/kubectl-k8ssandra/register/command.go b/cmd/kubectl-k8ssandra/register/command.go index 5752083..95de8a5 100644 --- a/cmd/kubectl-k8ssandra/register/command.go +++ b/cmd/kubectl-k8ssandra/register/command.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/charmbracelet/log" + "github.com/k8ssandra/k8ssandra-client/pkg/registration" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" ) @@ -63,7 +64,7 @@ func NewRegistrationExecutorFromRegisterClusterCmd(cmd cobra.Command) *Registrat destName := cmd.Flag("destination-name").Value.String() srcContext := cmd.Flag("source-context").Value.String() if destName == "" { - destName = srcContext + destName = registration.CleanupForKubernetes(srcContext) } return &RegistrationExecutor{ SourceKubeconfig: cmd.Flag("source-kubeconfig").Value.String(), diff --git a/pkg/registration/sanitize_strings.go b/pkg/registration/sanitize_strings.go new file mode 100644 index 0000000..85e66d8 --- /dev/null +++ b/pkg/registration/sanitize_strings.go @@ -0,0 +1,25 @@ +package registration + +import ( + "regexp" + "strings" + + "k8s.io/apimachinery/pkg/util/validation" +) + +var dns1035LabelFmt = "[a-z]([-a-z0-9]*[a-z0-9])?" +var dns1035LabelRegexp = regexp.MustCompile(dns1035LabelFmt) + +func CleanupForKubernetes(input string) string { + if len(validation.IsDNS1035Label(input)) > 0 { + r := dns1035LabelRegexp + + // Invalid domain name, Kubernetes will reject this. Try to modify it to a suitable string + input = strings.ToLower(input) + input = strings.ReplaceAll(input, "_", "-") + validParts := r.FindAllString(input, -1) + return strings.Join(validParts, "") + } + + return input +} From a358366c4aa5c8aa7d8a650327721ecc65ef306b Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 27 May 2024 15:42:23 +1000 Subject: [PATCH 24/25] Make sure any isnotFound error doesn't stall the process. --- cmd/kubectl-k8ssandra/register/registration.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index 976d006..4ac9cb5 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -101,7 +101,7 @@ func (e *RegistrationExecutor) RegisterCluster() error { if err != nil { return RetryableError{Message: err.Error()} } - saConfig, err := registration.TokenToKubeconfig(*secret, host) + saConfig, err := registration.TokenToKubeconfig(*secret, host, e.SourceContext) if err != nil { return RetryableError{fmt.Sprintf("error converting token to kubeconfig: %s, secret: %#v", err.Error(), secret)} } @@ -119,7 +119,7 @@ func (e *RegistrationExecutor) RegisterCluster() error { "kubeconfig": secretData, }, } - if err := destClient.Create(e.Context, &destSecret); err != nil { + if err := destClient.Create(e.Context, &destSecret); err != nil && !errors.IsAlreadyExists(err) { return RetryableError{fmt.Sprintf("error creating secret. err: %s sa %s", err, e.ServiceAccount)} } @@ -138,7 +138,7 @@ func (e *RegistrationExecutor) RegisterCluster() error { }, }, } - if err := destClient.Create(e.Context, &destClientConfig); err != nil { + if err := destClient.Create(e.Context, &destClientConfig); err != nil && !errors.IsAlreadyExists(err) { return RetryableError{Message: err.Error()} } return nil From 4994b445d7e12fab2f4cf46fe5eea500965f6e24 Mon Sep 17 00:00:00 2001 From: Miles Garnsey Date: Mon, 27 May 2024 17:12:35 +1000 Subject: [PATCH 25/25] Line up all context names across clientConfig and kubeconfig in secret. --- cmd/kubectl-k8ssandra/register/register_test.go | 4 ++-- cmd/kubectl-k8ssandra/register/registration.go | 4 +++- pkg/registration/token_to_kubeconfig.go | 14 +++++++------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cmd/kubectl-k8ssandra/register/register_test.go b/cmd/kubectl-k8ssandra/register/register_test.go index 9609bad..56f1cd8 100644 --- a/cmd/kubectl-k8ssandra/register/register_test.go +++ b/cmd/kubectl-k8ssandra/register/register_test.go @@ -143,11 +143,11 @@ func TestRegister(t *testing.T) { destKubeconfig := ClientConfigFromSecret(destSecret) require.Equal( desiredSaSecret.Data["ca.crt"], - destKubeconfig.Clusters["cluster"].CertificateAuthorityData) + destKubeconfig.Clusters["test-destination"].CertificateAuthorityData) require.Equal( string(desiredSaSecret.Data["token"]), - destKubeconfig.AuthInfos["cluster"].Token) + destKubeconfig.AuthInfos["test-destination"].Token) } func ClientConfigFromSecret(s *corev1.Secret) clientcmdapi.Config { diff --git a/cmd/kubectl-k8ssandra/register/registration.go b/cmd/kubectl-k8ssandra/register/registration.go index 4ac9cb5..71f68a6 100644 --- a/cmd/kubectl-k8ssandra/register/registration.go +++ b/cmd/kubectl-k8ssandra/register/registration.go @@ -7,6 +7,7 @@ import ( "github.com/charmbracelet/log" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" @@ -101,7 +102,7 @@ func (e *RegistrationExecutor) RegisterCluster() error { if err != nil { return RetryableError{Message: err.Error()} } - saConfig, err := registration.TokenToKubeconfig(*secret, host, e.SourceContext) + saConfig, err := registration.TokenToKubeconfig(*secret, host, e.DestinationName) if err != nil { return RetryableError{fmt.Sprintf("error converting token to kubeconfig: %s, secret: %#v", err.Error(), secret)} } @@ -136,6 +137,7 @@ func (e *RegistrationExecutor) RegisterCluster() error { KubeConfigSecret: corev1.LocalObjectReference{ Name: e.DestinationName, }, + ContextName: e.DestinationName, }, } if err := destClient.Create(e.Context, &destClientConfig); err != nil && !errors.IsAlreadyExists(err) { diff --git a/pkg/registration/token_to_kubeconfig.go b/pkg/registration/token_to_kubeconfig.go index de18bf7..fb73064 100644 --- a/pkg/registration/token_to_kubeconfig.go +++ b/pkg/registration/token_to_kubeconfig.go @@ -7,7 +7,7 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" ) -func TokenToKubeconfig(s corev1.Secret, server string) (clientcmdapi.Config, error) { +func TokenToKubeconfig(s corev1.Secret, server, destinationName string) (clientcmdapi.Config, error) { caData, foundCa := s.Data["ca.crt"] tokenData, foundToken := s.Data["token"] if !foundCa || !foundToken { @@ -16,22 +16,22 @@ func TokenToKubeconfig(s corev1.Secret, server string) (clientcmdapi.Config, err return clientcmdapi.Config{ Clusters: map[string]*clientcmdapi.Cluster{ - "cluster": { + destinationName: { Server: server, CertificateAuthorityData: caData, }, }, AuthInfos: map[string]*clientcmdapi.AuthInfo{ - "cluster": { + destinationName: { Token: string(tokenData), }, }, Contexts: map[string]*clientcmdapi.Context{ - "cluster": { - Cluster: "cluster", - AuthInfo: "cluster", + destinationName: { + Cluster: destinationName, + AuthInfo: destinationName, }, }, - CurrentContext: "cluster", + CurrentContext: destinationName, }, nil }