Skip to content

Commit

Permalink
Merge pull request #266 from pdettori/issue-255
Browse files Browse the repository at this point in the history
🐛 set hosting cluster context preference in kflex init
  • Loading branch information
pdettori authored May 28, 2024
2 parents 5c9bfe8 + 44b776b commit 9cb2ef9
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 61 deletions.
43 changes: 29 additions & 14 deletions cmd/kflex/ctx/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,34 @@ func (c *CPCtx) Context(chattyStatus, failIfNone bool) {

switch c.CP.Name {
case "":
util.PrintStatus("Checking for saved initial context...", done, &wg, chattyStatus)
util.PrintStatus("Checking for saved hosting cluster context...", done, &wg, chattyStatus)
time.Sleep(1 * time.Second)
done <- true
if kubeconfig.IsInitialConfigSet(kconf) {
util.PrintStatus("Switching to initial context...", done, &wg, chattyStatus)
if err = kubeconfig.SwitchToInitialContext(kconf, false); err != nil {
fmt.Fprintf(os.Stderr, "Error switching kubeconfig to initial context: %s\n", err)
if kubeconfig.IsHostingClusterContextPreferenceSet(kconf) {
util.PrintStatus("Switching to hosting cluster context...", done, &wg, chattyStatus)
if err = kubeconfig.SwitchToHostingClusterContext(kconf, false); err != nil {
fmt.Fprintf(os.Stderr, "Error switching kubeconfig to hosting cluster context: %s\n", err)
os.Exit(1)
}
done <- true
} else if failIfNone {
fmt.Fprintln(os.Stderr, "The initial (hosting) context is not known!\n"+
"You can make it known to kflex by doing `kubectl config use-context $name_of_hosting_context` "+
"and then either `kflex create ...` or `kflex ctx $some_control_plane_name")
os.Exit(1)
if !c.isCurrentContextHostingClusterContext() {
fmt.Fprintln(os.Stderr, "The hosting cluster context is not known!\n"+
"You can make it known to kflex by doing `kubectl config use-context $name_of_hosting_context` "+
"and then either `kflex ctx` or `kflex create ...`")
os.Exit(1)
}
util.PrintStatus("Hosting cluster context not set, setting it to current context", done, &wg, chattyStatus)
kubeconfig.SetHostingClusterContextPreference(kconf)
done <- true
}
default:
ctxName := certs.GenerateContextName(c.Name)
util.PrintStatus(fmt.Sprintf("Switching to context %s...", ctxName), done, &wg, chattyStatus)
if err = kubeconfig.SwitchContext(kconf, c.Name); err != nil {
fmt.Fprintf(os.Stderr, "kubeconfig context %s not found, trying to load from server...\n", err)
if err := c.switchToInitialContextAndWrite(kconf); err != nil {
fmt.Fprintf(os.Stderr, "Error switching back to initial context: %s\n", err)
if err := c.switchToHostingClusterContextAndWrite(kconf); err != nil {
fmt.Fprintf(os.Stderr, "Error switching back to hosting cluster context: %s\n", err)
os.Exit(1)
}
if err = c.loadAndMergeFromServer(kconf); err != nil {
Expand Down Expand Up @@ -125,9 +130,9 @@ func (c *CPCtx) loadAndMergeFromServer(kconfig *api.Config) error {
return nil
}

func (c *CPCtx) switchToInitialContextAndWrite(kconf *api.Config) error {
if kubeconfig.IsInitialConfigSet(kconf) {
if err := kubeconfig.SwitchToInitialContext(kconf, false); err != nil {
func (c *CPCtx) switchToHostingClusterContextAndWrite(kconf *api.Config) error {
if kubeconfig.IsHostingClusterContextPreferenceSet(kconf) {
if err := kubeconfig.SwitchToHostingClusterContext(kconf, false); err != nil {
return err
}
if err := kubeconfig.WriteKubeconfig(c.Ctx, kconf); err != nil {
Expand All @@ -136,3 +141,13 @@ func (c *CPCtx) switchToInitialContextAndWrite(kconf *api.Config) error {
}
return nil
}

func (c *CPCtx) isCurrentContextHostingClusterContext() bool {
clientsetp, err := kfclient.GetClientSet(c.Kubeconfig)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting clientset: %s\n", err)
os.Exit(1)
}
clientset := *clientsetp
return util.CheckResourceExists(clientset, "tenancy.kflex.kubestellar.org", "v1alpha1", "controlplanes")
}
2 changes: 1 addition & 1 deletion cmd/kflex/delete/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (c *CPDelete) Delete(chattyStatus bool) {
os.Exit(1)
}

if err = kubeconfig.SwitchToInitialContext(kconf, false); err != nil {
if err = kubeconfig.SwitchToHostingClusterContext(kconf, false); err != nil {
fmt.Fprintf(os.Stderr, "no initial kubeconfig context was found: %s\n", err)
}

Expand Down
9 changes: 9 additions & 0 deletions cmd/kflex/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/kubestellar/kubeflex/pkg/client"
"github.com/kubestellar/kubeflex/pkg/helm"
kcfg "github.com/kubestellar/kubeflex/pkg/kubeconfig"
"github.com/kubestellar/kubeflex/pkg/util"
)

Expand All @@ -47,6 +48,14 @@ func Init(ctx context.Context, kubeconfig, version, buildDate string, domain, ex
util.PrintStatus(fmt.Sprintf("Kubeflex %s %s", version, buildDate), done, &wg, chattyStatus)
done <- true

util.PrintStatus("Setting hosting cluster preference in kubeconfig", done, &wg, chattyStatus)
err = kcfg.SaveHostingClusterContextPreference(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error setting hosting cluster context preference: %v\n", err)
os.Exit(1)
}
done <- true

util.PrintStatus("Ensuring kubeflex-system namespace...", done, &wg, chattyStatus)
ensureSystemNamespace(kubeconfig, util.SystemNamespace)
done <- true
Expand Down
2 changes: 1 addition & 1 deletion cmd/kflex/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ var deleteCmd = &cobra.Command{
var ctxCmd = &cobra.Command{
Use: "ctx",
Short: "Switch kubeconfig context to a control plane instance",
Long: `Running without an argument switches the context back to the initial context,
Long: `Running without an argument switches the context back to the hosting cluster context,
while providing the control plane name as argument switches the context to
that control plane`,
Args: cobra.MaximumNArgs(1),
Expand Down
8 changes: 4 additions & 4 deletions docs/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ been stored in an extension in your kubeconfig file (see
failure will look like the following.

$ kflex create cp1
✔ Checking for saved initial context...
✔ Checking for saved hosting cluster context...
◐ Creating new control plane cp1 of type k8s ...Error creating instance: no matches for kind "ControlPlane" in version "tenancy.kflex.kubestellar.org/v1alpha1"

To create a new control plane with name `cp1` using the KubeFlex CLI:
Expand Down Expand Up @@ -311,8 +311,8 @@ Let's create an OCM control plane:

```shell
$ kflex create cp3 --type ocm
✔ Checking for saved initial context...
✔ Switching to initial context...
✔ Checking for saved hosting cluster context...
✔ Switching to hosting cluster context...
✔ Creating new control plane cp3...
✔ Waiting for API server to become ready...
```
Expand Down Expand Up @@ -459,7 +459,7 @@ Let's create a vcluster control plane:

```shell
$ kflex create cp2 --type vcluster
✔ Checking for saved initial context...
✔ Checking for saved hosting cluster context...
✔ Creating new control plane cp2...
✔ Waiting for API server to become ready...
```
Expand Down
26 changes: 18 additions & 8 deletions pkg/kubeconfig/config-util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package kubeconfig

import (
"context"
"encoding/json"
"fmt"

Expand Down Expand Up @@ -46,8 +47,8 @@ func merge(existing, new *clientcmdapi.Config) error {
existing.Contexts[k] = v
}

if !IsInitialConfigSet(existing) {
saveInitialContextName(existing)
if !IsHostingClusterContextPreferenceSet(existing) {
SetHostingClusterContextPreference(existing)
}

// set the current context to the nex context
Expand Down Expand Up @@ -83,9 +84,9 @@ func DeleteContext(config *clientcmdapi.Config, cpName string) error {
return nil
}

func SwitchToInitialContext(config *clientcmdapi.Config, removeExtension bool) error {
if !IsInitialConfigSet(config) {
return nil
func SwitchToHostingClusterContext(config *clientcmdapi.Config, removeExtension bool) error {
if !IsHostingClusterContextPreferenceSet(config) {
return fmt.Errorf("hosting cluster preference context not set")
}
// found that the only way to unmarshal the runtime.Object into a ConfigMap
// was to use the unMarshallCM() function based on json marshal/unmarshal
Expand All @@ -96,7 +97,7 @@ func SwitchToInitialContext(config *clientcmdapi.Config, removeExtension bool) e

contextData, ok := cm.Data[InitialContextName]
if !ok {
return fmt.Errorf("initial context data not set")
return fmt.Errorf("hosting cluster preference context data not set")
}
config.CurrentContext = contextData

Expand All @@ -107,7 +108,7 @@ func SwitchToInitialContext(config *clientcmdapi.Config, removeExtension bool) e
return nil
}

func saveInitialContextName(config *clientcmdapi.Config) {
func SetHostingClusterContextPreference(config *clientcmdapi.Config) {
runtimeObjects := make(map[string]runtime.Object)
runtimeObjects[ConfigExtensionName] = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -123,7 +124,7 @@ func saveInitialContextName(config *clientcmdapi.Config) {
}
}

func IsInitialConfigSet(config *clientcmdapi.Config) bool {
func IsHostingClusterContextPreferenceSet(config *clientcmdapi.Config) bool {
if config.Preferences.Extensions != nil {
_, ok := config.Preferences.Extensions[ConfigExtensionName]
if ok {
Expand All @@ -133,6 +134,15 @@ func IsInitialConfigSet(config *clientcmdapi.Config) bool {
return false
}

func SaveHostingClusterContextPreference(ctx context.Context) error {
kconfig, err := LoadKubeconfig(ctx)
if err != nil {
return fmt.Errorf("setHostingClusterContextPreference: error loading kubeconfig %s", err)
}
SetHostingClusterContextPreference(kconfig)
return WriteKubeconfig(ctx, kconfig)
}

func unMarshallCM(obj runtime.Object) (*corev1.ConfigMap, error) {
jsonData, err := json.Marshal(obj)
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions pkg/kubeconfig/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@ func loadControlPlaneKubeconfig(ctx context.Context, client kubernetes.Clientset
namespace := util.GenerateNamespaceFromControlPlaneName(name)

var ks *v1.Secret
var errGet error
err := wait.PollUntilContextTimeout(ctx, 1*time.Second, 5*time.Minute, false, func(ctx context.Context) (bool, error) {
var err error
ks, err = client.CoreV1().Secrets(namespace).Get(ctx,
ks, errGet = client.CoreV1().Secrets(namespace).Get(ctx,
util.GetKubeconfSecretNameByControlPlaneType(controlPlaneType),
metav1.GetOptions{})
if err != nil {
if errGet != nil {
return false, nil
}
return true, nil
})
if err != nil {
return nil, fmt.Errorf("error waiting for control plane kubeconfig secret: %s", err)
return nil, fmt.Errorf("error waiting for control plane kubeconfig secret: %s, %s", err, errGet)
}

key := util.GetKubeconfSecretKeyNameByControlPlaneType(controlPlaneType)
Expand Down
30 changes: 1 addition & 29 deletions pkg/util/ocp.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,9 @@
package util

import (
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
)

const (
kubeflexServiceAccount = "system:serviceaccount:kubeflex-system:kubeflex-controller-manager"
)

func IsOpenShift(clientset kubernetes.Clientset) bool {
return CheckResourceExists(clientset, "project.openshift.io", "v1", "Project")
}

func CheckResourceExists(clientset kubernetes.Clientset, group, version, kind string) bool {
discoveryClient := clientset.Discovery()

exists, _ := checkResourceExists(discoveryClient, group, version, kind)

return exists
}

func checkResourceExists(discoveryClient discovery.DiscoveryInterface, group, version, kind string) (bool, error) {
resourceList, err := discoveryClient.ServerResourcesForGroupVersion(group + "/" + version)
if err != nil {
return false, err
}

for _, resource := range resourceList.APIResources {
if resource.Kind == kind {
return true, nil
}
}

return false, nil
return CheckResourceExists(clientset, "project.openshift.io", "v1", "projects")
}
24 changes: 24 additions & 0 deletions pkg/util/unstructured.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/restmapper"
)
Expand Down Expand Up @@ -124,3 +125,26 @@ func IsClusterScoped(gvk schema.GroupVersionKind, apiResourceLists []*metav1.API
}
return false, fmt.Errorf("resource %s in group %s with version %s was not found", gvk.Kind, gvk.Group, gvk.Version)
}

func CheckResourceExists(clientset kubernetes.Clientset, group, version, name string) bool {
discoveryClient := clientset.Discovery()

exists, _ := findResource(discoveryClient, group, version, name)

return exists
}

func findResource(discoveryClient discovery.DiscoveryInterface, group, version, name string) (bool, error) {
resourceList, err := discoveryClient.ServerResourcesForGroupVersion(group + "/" + version)
if err != nil {
return false, err
}

for _, resource := range resourceList.APIResources {
if resource.Name == name {
return true, nil
}
}

return false, nil
}

0 comments on commit 9cb2ef9

Please sign in to comment.