forked from kosmos-io/kosmos
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request kosmos-io#101 from GreatLazyMan/importexport
title: Add import and export subcommand for kosmosctl
- Loading branch information
Showing
5 changed files
with
374 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package rsmigrate | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/kubernetes" | ||
|
||
"github.com/kosmos.io/kosmos/pkg/apis/kosmos/v1alpha1" | ||
kosmosversioned "github.com/kosmos.io/kosmos/pkg/generated/clientset/versioned" | ||
"github.com/kosmos.io/kosmos/pkg/utils" | ||
) | ||
|
||
func getClientFromLeafCluster(leafCluster *v1alpha1.Knode) (kubernetes.Interface, kosmosversioned.Interface, error) { | ||
//generate clientset by leafCluster kubeconfig | ||
leafClusterKubeconfig := leafCluster.Spec.Kubeconfig | ||
if len(leafClusterKubeconfig) == 0 { | ||
return nil, nil, fmt.Errorf("leafcluster's kubeconfig is nil, it's unable to work normally") | ||
} | ||
k8sClient, err := utils.NewClientFromBytes(leafClusterKubeconfig) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("create kubernetes clientset error: %s ", err) | ||
} | ||
|
||
kosmosClient, err := utils.NewKosmosClientFromBytes(leafClusterKubeconfig) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("create kubernetes clientset for leafcluster crd error: %s ", err) | ||
} | ||
|
||
return k8sClient, kosmosClient, nil | ||
} | ||
|
||
func completeLeafClusterOptions(leafClusterOptions *LeafClusterOptions, masterClient kosmosversioned.Interface) error { | ||
//complete leafClusterOptions by leafCluster name | ||
if leafClusterOptions.LeafClusterName == "" { | ||
return fmt.Errorf("get leafcluster error: %s ", "leafcluster value can't be empty") | ||
} | ||
leafCluster, err := masterClient.KosmosV1alpha1().Knodes().Get(context.TODO(), leafClusterOptions.LeafClusterName, metav1.GetOptions{}) | ||
if err != nil { | ||
return fmt.Errorf("get leafcluster error: %s", err) | ||
} | ||
leafClusterOptions.LeafCluster = leafCluster | ||
k8sClient, kosmosClient, err := getClientFromLeafCluster(leafClusterOptions.LeafCluster) | ||
if err != nil { | ||
return fmt.Errorf("get leafcluster clientset error: %s", err) | ||
} | ||
leafClusterOptions.LeafClusterNativeClient = k8sClient | ||
leafClusterOptions.LeafClusterKosmosClient = kosmosClient | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package rsmigrate | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/spf13/cobra" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/client-go/util/homedir" | ||
ctlutil "k8s.io/kubectl/pkg/cmd/util" | ||
|
||
"github.com/kosmos.io/kosmos/pkg/apis/kosmos/v1alpha1" | ||
kosmosversioned "github.com/kosmos.io/kosmos/pkg/generated/clientset/versioned" | ||
"github.com/kosmos.io/kosmos/pkg/utils" | ||
) | ||
|
||
type LeafClusterOptions struct { | ||
LeafClusterName string | ||
LeafCluster *v1alpha1.Knode | ||
LeafClusterNativeClient kubernetes.Interface | ||
//clientset operate leafCluster releted resource | ||
LeafClusterKosmosClient kosmosversioned.Interface | ||
} | ||
|
||
type CommandOptions struct { | ||
MasterKubeConfig string | ||
MasterClient kubernetes.Interface | ||
//clientset operate leafCluster releted resource | ||
MasterKosmosClient kosmosversioned.Interface | ||
|
||
SrcLeafClusterOptions *LeafClusterOptions | ||
Namespace string | ||
} | ||
|
||
func (o *CommandOptions) Validate(cmd *cobra.Command) error { | ||
return nil | ||
} | ||
|
||
func (o *CommandOptions) Complete(f ctlutil.Factory, cmd *cobra.Command) error { | ||
var err error | ||
var kubeConfigStream []byte | ||
// get master kubernetes clientset | ||
if len(o.MasterKubeConfig) > 0 { | ||
kubeConfigStream, err = os.ReadFile(o.MasterKubeConfig) | ||
} else { | ||
kubeConfigStream, err = os.ReadFile(filepath.Join(homedir.HomeDir(), ".kube", "config")) | ||
} | ||
if err != nil { | ||
return fmt.Errorf("get master kubeconfig failed: %s", err) | ||
} | ||
|
||
masterClient, err := utils.NewClientFromBytes(kubeConfigStream) | ||
if err != nil { | ||
return fmt.Errorf("create master clientset error: %s ", err) | ||
} | ||
o.MasterClient = masterClient | ||
|
||
kosmosClient, err := utils.NewKosmosClientFromBytes(kubeConfigStream) | ||
if err != nil { | ||
return fmt.Errorf("get master rest client config error:%s", err) | ||
} | ||
|
||
o.MasterKosmosClient = kosmosClient | ||
|
||
// get src leafCluster options | ||
if cmd.Flags().Changed("leafcluster") { | ||
err := completeLeafClusterOptions(o.SrcLeafClusterOptions, o.MasterKosmosClient) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package rsmigrate | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
ctlutil "k8s.io/kubectl/pkg/cmd/util" | ||
"k8s.io/kubectl/pkg/util/i18n" | ||
"k8s.io/kubectl/pkg/util/templates" | ||
mcsv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" | ||
) | ||
|
||
var exportExample = templates.Examples(i18n.T(` | ||
# Export service in control plane | ||
kosmosctl export service foo -n namespacefoo --kubeconfig=[control plane kubeconfig] | ||
`)) | ||
|
||
var exportErr string = "kosmosctl export error" | ||
|
||
type CommandExportOptions struct { | ||
*CommandOptions | ||
} | ||
|
||
// NewCmdExport export resource to control plane | ||
func NewCmdExport(f ctlutil.Factory) *cobra.Command { | ||
o := &CommandExportOptions{CommandOptions: &CommandOptions{SrcLeafClusterOptions: &LeafClusterOptions{}}} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "export", | ||
Short: i18n.T("Export resource to control plane data storage center"), | ||
Long: "", | ||
Example: exportExample, | ||
SilenceUsage: true, | ||
DisableFlagsInUseLine: true, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
ctlutil.CheckErr(o.Complete(f, cmd)) | ||
ctlutil.CheckErr(o.Validate(cmd)) | ||
ctlutil.CheckErr(o.Run(cmd, args)) | ||
return nil | ||
}, | ||
} | ||
|
||
cmd.Flags().StringVarP(&o.MasterKubeConfig, "kubeconfig", "", "", "Absolute path to the master kubeconfig file.") | ||
cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "default", "The namespace scope for this CLI request") | ||
|
||
return cmd | ||
} | ||
|
||
func (o *CommandExportOptions) Complete(f ctlutil.Factory, cmd *cobra.Command) error { | ||
err := o.CommandOptions.Complete(f, cmd) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (o *CommandExportOptions) Validate(cmd *cobra.Command) error { | ||
err := o.CommandOptions.Validate(cmd) | ||
if err != nil { | ||
return fmt.Errorf("%s, valid args error: %s", exportErr, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (o *CommandExportOptions) Run(cmd *cobra.Command, args []string) error { | ||
if len(args) == 0 { | ||
return fmt.Errorf("args is null, resource type should be specified") | ||
} | ||
|
||
switch args[0] { | ||
case "svc", "services", "service": | ||
if len(args[1:]) != 1 { | ||
return fmt.Errorf("%s, exactly one NAME is required, got %d", exportErr, len(args[1:])) | ||
} | ||
|
||
var err error | ||
serviceExport := &mcsv1alpha1.ServiceExport{} | ||
serviceExport.Namespace = o.Namespace | ||
serviceExport.Kind = "ServiceExport" | ||
serviceExport.Name = args[1] | ||
|
||
// Create serviceExport, if exists,return error instead of updating it | ||
_, err = o.MasterKosmosClient.MulticlusterV1alpha1().ServiceExports(o.Namespace). | ||
Create(context.TODO(), serviceExport, metav1.CreateOptions{}) | ||
if err != nil { | ||
return fmt.Errorf("%s, create %s %s/%s %s: %s", exportErr, serviceExport.Kind, o.Namespace, args[1], args[0], err) | ||
} | ||
|
||
fmt.Printf("Create %s %s/%s successfully!\n", serviceExport.Kind, o.Namespace, args[1]) | ||
default: | ||
return fmt.Errorf("%s, not support export resouece %s", exportErr, args[0]) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package rsmigrate | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
v1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
ctlutil "k8s.io/kubectl/pkg/cmd/util" | ||
"k8s.io/kubectl/pkg/util/i18n" | ||
"k8s.io/kubectl/pkg/util/templates" | ||
mcsv1alpha1 "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1" | ||
) | ||
|
||
var importExample = templates.Examples(i18n.T(` | ||
# Import service from control plane to leafcluster | ||
kosmosctl import service foo -n namespacefoo --kubecnfig=[control plane kubeconfig] --to-leafcluster leafclusterfoo | ||
`)) | ||
|
||
var importErr string = "kosmosctl import error" | ||
|
||
type CommandImportOptions struct { | ||
*CommandOptions | ||
DstLeafClusterOptions *LeafClusterOptions | ||
} | ||
|
||
// NewCmdImport import resource | ||
func NewCmdImport(f ctlutil.Factory) *cobra.Command { | ||
o := &CommandImportOptions{ | ||
CommandOptions: &CommandOptions{SrcLeafClusterOptions: &LeafClusterOptions{}}, | ||
DstLeafClusterOptions: &LeafClusterOptions{}, | ||
} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "import", | ||
Short: i18n.T("Import resource to leafCluster"), | ||
Long: "", | ||
Example: importExample, | ||
SilenceUsage: true, | ||
DisableFlagsInUseLine: true, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
ctlutil.CheckErr(o.Complete(f, cmd)) | ||
ctlutil.CheckErr(o.Validate(cmd)) | ||
ctlutil.CheckErr(o.Run(f, cmd, args)) | ||
return nil | ||
}, | ||
} | ||
cmd.Flags().StringVarP(&o.MasterKubeConfig, "kubeconfig", "", "", "Absolute path to the master kubeconfig file.") | ||
cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", "default", "The namespace scope for this CLI request") | ||
cmd.Flags().StringVar(&o.DstLeafClusterOptions.LeafClusterName, "to-leafcluster", "", "Import resource to this destination leafcluster") | ||
|
||
return cmd | ||
} | ||
|
||
func (o *CommandImportOptions) Complete(f ctlutil.Factory, cmd *cobra.Command) error { | ||
err := o.CommandOptions.Complete(f, cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// get dst leafCluster options | ||
if cmd.Flags().Changed("to-leafcluster") { | ||
err := completeLeafClusterOptions(o.DstLeafClusterOptions, o.MasterKosmosClient) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (o *CommandImportOptions) Validate(cmd *cobra.Command) error { | ||
err := o.CommandOptions.Validate(cmd) | ||
if err != nil { | ||
return fmt.Errorf("%s, valid args error: %s", importErr, err) | ||
} | ||
|
||
if !cmd.Flags().Changed("to-leafcluster") { | ||
return fmt.Errorf("%s, required flag(s) 'to-leafcluster' not set", importErr) | ||
} | ||
return nil | ||
} | ||
|
||
func (o *CommandImportOptions) Run(f ctlutil.Factory, cmd *cobra.Command, args []string) error { | ||
if len(args) == 0 { | ||
return fmt.Errorf("args is null, resource should be specified") | ||
} | ||
|
||
switch args[0] { | ||
case "svc", "services", "service": | ||
if len(args[1:]) != 1 { | ||
return fmt.Errorf("%s, exactly one NAME is required, got %d", importErr, len(args[1:])) | ||
} | ||
|
||
var srcService *v1.Service | ||
var err error | ||
if o.SrcLeafClusterOptions.LeafClusterName != "" { | ||
srcService, err = o.SrcLeafClusterOptions.LeafClusterNativeClient.CoreV1().Services(o.Namespace).Get(context.TODO(), args[1], metav1.GetOptions{}) | ||
} else { | ||
srcService, err = o.MasterClient.CoreV1().Services(o.Namespace).Get(context.TODO(), args[1], metav1.GetOptions{}) | ||
} | ||
if err != nil { | ||
return fmt.Errorf("%s, get source service %s/%s error: %s", importErr, o.Namespace, args[1], err) | ||
} | ||
|
||
serviceImport := &mcsv1alpha1.ServiceImport{} | ||
serviceImport.Kind = "ServiceImport" | ||
serviceImport.Namespace = o.Namespace | ||
serviceImport.Name = args[1] | ||
serviceImport.Spec.Type = "ClusterSetIP" | ||
if srcService.Spec.ClusterIP == "None" || len(srcService.Spec.ClusterIP) == 0 { | ||
serviceImport.Spec.Type = "Headless" | ||
} | ||
|
||
serviceImport.Spec.Ports = make([]mcsv1alpha1.ServicePort, len(srcService.Spec.Ports)) | ||
for portIndex, svcPort := range srcService.Spec.Ports { | ||
serviceImport.Spec.Ports[portIndex] = mcsv1alpha1.ServicePort{ | ||
Name: svcPort.Name, | ||
Protocol: svcPort.Protocol, | ||
AppProtocol: svcPort.AppProtocol, | ||
Port: svcPort.Port, | ||
} | ||
} | ||
|
||
// Create serviceImport, if exists,return error instead of updating it | ||
if len(o.DstLeafClusterOptions.LeafClusterName) != 0 { | ||
_, err = o.DstLeafClusterOptions.LeafClusterKosmosClient.MulticlusterV1alpha1().ServiceImports(o.Namespace). | ||
Create(context.TODO(), serviceImport, metav1.CreateOptions{}) | ||
} else { | ||
_, err = o.MasterKosmosClient.MulticlusterV1alpha1().ServiceImports(o.Namespace). | ||
Create(context.TODO(), serviceImport, metav1.CreateOptions{}) | ||
} | ||
if err != nil { | ||
return fmt.Errorf("%s, create %s %s/%s %s: %s", importErr, serviceImport.Kind, o.Namespace, args[1], args[0], err) | ||
} | ||
|
||
fmt.Printf("Create %s %s/%s successfully!\n", serviceImport.Kind, o.Namespace, args[1]) | ||
default: | ||
return fmt.Errorf("%s, not support import resouece %s", importErr, args[0]) | ||
} | ||
|
||
return nil | ||
} |