Migrate from urfave/cli to cobra (#426)

* wip

* env var fix

* cluster create

* cluster create and delete

* cluster list

* cluster cmd

* kubeconfig

* policy create

* policy delete and list, and added root commands

* removed urfavecli from k3kcli

* fix policy command

* k3k-kubelet to cobra

* updated docs

* updated go.mod

* updated test

* added deletion

* added cleanup and flake attempts

* wip bind env

* simplified config
This commit is contained in:
Enrico Candino
2025-07-24 16:49:40 +02:00
committed by GitHub
parent 5eb1d2a5bb
commit 084701fcd9
34 changed files with 825 additions and 942 deletions
+13 -10
View File
@@ -1,17 +1,20 @@
package cmds
import (
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)
func NewClusterCmd(appCtx *AppContext) *cli.Command {
return &cli.Command{
Name: "cluster",
Usage: "cluster command",
Subcommands: []*cli.Command{
NewClusterCreateCmd(appCtx),
NewClusterDeleteCmd(appCtx),
NewClusterListCmd(appCtx),
},
func NewClusterCmd(appCtx *AppContext) *cobra.Command {
cmd := &cobra.Command{
Use: "cluster",
Short: "cluster command",
}
cmd.AddCommand(
NewClusterCreateCmd(appCtx),
NewClusterDeleteCmd(appCtx),
NewClusterListCmd(appCtx),
)
return cmd
}
+28 -29
View File
@@ -10,7 +10,7 @@ import (
"time"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"k8s.io/utils/ptr"
@@ -33,10 +33,10 @@ type CreateConfig struct {
serviceCIDR string
servers int
agents int
serverArgs cli.StringSlice
agentArgs cli.StringSlice
serverEnvs cli.StringSlice
agentEnvs cli.StringSlice
serverArgs []string
agentArgs []string
serverEnvs []string
agentEnvs []string
persistenceType string
storageClassName string
storageRequestSize string
@@ -48,33 +48,32 @@ type CreateConfig struct {
customCertsPath string
}
func NewClusterCreateCmd(appCtx *AppContext) *cli.Command {
func NewClusterCreateCmd(appCtx *AppContext) *cobra.Command {
createConfig := &CreateConfig{}
flags := CommonFlags(appCtx)
flags = append(flags, FlagNamespace(appCtx))
flags = append(flags, newCreateFlags(createConfig)...)
return &cli.Command{
Name: "create",
Usage: "Create new cluster",
UsageText: "k3kcli cluster create [command options] NAME",
Action: createAction(appCtx, createConfig),
Flags: flags,
HideHelpCommand: true,
cmd := &cobra.Command{
Use: "create",
Short: "Create new cluster",
Example: "k3kcli cluster create [command options] NAME",
PreRunE: func(cmd *cobra.Command, args []string) error {
return validateCreateConfig(createConfig)
},
RunE: createAction(appCtx, createConfig),
Args: cobra.ExactArgs(1),
}
CobraFlagNamespace(appCtx, cmd.Flags())
createFlags(cmd, createConfig)
return cmd
}
func createAction(appCtx *AppContext, config *CreateConfig) cli.ActionFunc {
return func(clx *cli.Context) error {
func createAction(appCtx *AppContext, config *CreateConfig) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client := appCtx.Client
name := args[0]
if clx.NArg() != 1 {
return cli.ShowSubcommandHelp(clx)
}
name := clx.Args().First()
if name == k3kcluster.ClusterInvalidName {
return errors.New("invalid cluster name")
}
@@ -166,7 +165,7 @@ func createAction(appCtx *AppContext, config *CreateConfig) cli.ActionFunc {
return err
}
return writeKubeconfigFile(cluster, kubeconfig)
return writeKubeconfigFile(cluster, kubeconfig, "")
}
}
@@ -185,10 +184,10 @@ func newCluster(name, namespace string, config *CreateConfig) *v1alpha1.Cluster
Agents: ptr.To(int32(config.agents)),
ClusterCIDR: config.clusterCIDR,
ServiceCIDR: config.serviceCIDR,
ServerArgs: config.serverArgs.Value(),
AgentArgs: config.agentArgs.Value(),
ServerEnvs: env(config.serverEnvs.Value()),
AgentEnvs: env(config.agentEnvs.Value()),
ServerArgs: config.serverArgs,
AgentArgs: config.agentArgs,
ServerEnvs: env(config.serverEnvs),
AgentEnvs: env(config.agentEnvs),
Version: config.version,
Mode: v1alpha1.ClusterMode(config.mode),
Persistence: v1alpha1.PersistenceConfig{
+48 -124
View File
@@ -3,134 +3,58 @@ package cmds
import (
"errors"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
)
func newCreateFlags(config *CreateConfig) []cli.Flag {
return []cli.Flag{
&cli.IntFlag{
Name: "servers",
Usage: "number of servers",
Destination: &config.servers,
Value: 1,
Action: func(ctx *cli.Context, value int) error {
if value <= 0 {
return errors.New("invalid number of servers")
}
return nil
},
},
&cli.IntFlag{
Name: "agents",
Usage: "number of agents",
Destination: &config.agents,
},
&cli.StringFlag{
Name: "token",
Usage: "token of the cluster",
Destination: &config.token,
},
&cli.StringFlag{
Name: "cluster-cidr",
Usage: "cluster CIDR",
Destination: &config.clusterCIDR,
},
&cli.StringFlag{
Name: "service-cidr",
Usage: "service CIDR",
Destination: &config.serviceCIDR,
},
&cli.BoolFlag{
Name: "mirror-host-nodes",
Usage: "Mirror Host Cluster Nodes",
Destination: &config.mirrorHostNodes,
},
&cli.StringFlag{
Name: "persistence-type",
Usage: "persistence mode for the nodes (dynamic, ephemeral, static)",
Value: string(v1alpha1.DynamicPersistenceMode),
Destination: &config.persistenceType,
Action: func(ctx *cli.Context, value string) error {
switch v1alpha1.PersistenceMode(value) {
case v1alpha1.EphemeralPersistenceMode, v1alpha1.DynamicPersistenceMode:
return nil
default:
return errors.New(`persistence-type should be one of "dynamic", "ephemeral" or "static"`)
}
},
},
&cli.StringFlag{
Name: "storage-class-name",
Usage: "storage class name for dynamic persistence type",
Destination: &config.storageClassName,
},
&cli.StringFlag{
Name: "storage-request-size",
Usage: "storage size for dynamic persistence type",
Destination: &config.storageRequestSize,
Action: func(ctx *cli.Context, value string) error {
if _, err := resource.ParseQuantity(value); err != nil {
return errors.New(`invalid storage size, should be a valid resource quantity e.g "10Gi"`)
}
return nil
},
},
&cli.StringSliceFlag{
Name: "server-args",
Usage: "servers extra arguments",
Destination: &config.serverArgs,
},
&cli.StringSliceFlag{
Name: "agent-args",
Usage: "agents extra arguments",
Destination: &config.agentArgs,
},
&cli.StringSliceFlag{
Name: "server-envs",
Usage: "servers extra Envs",
Destination: &config.serverEnvs,
},
&cli.StringSliceFlag{
Name: "agent-envs",
Usage: "agents extra Envs",
Destination: &config.agentEnvs,
},
&cli.StringFlag{
Name: "version",
Usage: "k3s version",
Destination: &config.version,
},
&cli.StringFlag{
Name: "mode",
Usage: "k3k mode type (shared, virtual)",
Destination: &config.mode,
Value: "shared",
Action: func(ctx *cli.Context, value string) error {
switch value {
case string(v1alpha1.VirtualClusterMode), string(v1alpha1.SharedClusterMode):
return nil
default:
return errors.New(`mode should be one of "shared" or "virtual"`)
}
},
},
&cli.StringFlag{
Name: "kubeconfig-server",
Usage: "override the kubeconfig server host",
Destination: &config.kubeconfigServerHost,
},
&cli.StringFlag{
Name: "policy",
Usage: "The policy to create the cluster in",
Destination: &config.policy,
},
&cli.StringFlag{
Name: "custom-certs",
Usage: "The path for custom certificate directory",
Destination: &config.customCertsPath,
},
func createFlags(cmd *cobra.Command, cfg *CreateConfig) {
cmd.Flags().IntVar(&cfg.servers, "servers", 1, "number of servers")
cmd.Flags().IntVar(&cfg.agents, "agents", 0, "number of agents")
cmd.Flags().StringVar(&cfg.token, "token", "", "token of the cluster")
cmd.Flags().StringVar(&cfg.clusterCIDR, "cluster-cidr", "", "cluster CIDR")
cmd.Flags().StringVar(&cfg.serviceCIDR, "service-cidr", "", "service CIDR")
cmd.Flags().BoolVar(&cfg.mirrorHostNodes, "mirror-host-nodes", false, "Mirror Host Cluster Nodes")
cmd.Flags().StringVar(&cfg.persistenceType, "persistence-type", string(v1alpha1.DynamicPersistenceMode), "persistence mode for the nodes (dynamic, ephemeral, static)")
cmd.Flags().StringVar(&cfg.storageClassName, "storage-class-name", "", "storage class name for dynamic persistence type")
cmd.Flags().StringVar(&cfg.storageRequestSize, "storage-request-size", "", "storage size for dynamic persistence type")
cmd.Flags().StringSliceVar(&cfg.serverArgs, "server-args", []string{}, "servers extra arguments")
cmd.Flags().StringSliceVar(&cfg.agentArgs, "agent-args", []string{}, "agents extra arguments")
cmd.Flags().StringSliceVar(&cfg.serverEnvs, "server-envs", []string{}, "servers extra Envs")
cmd.Flags().StringSliceVar(&cfg.agentEnvs, "agent-envs", []string{}, "agents extra Envs")
cmd.Flags().StringVar(&cfg.version, "version", "", "k3s version")
cmd.Flags().StringVar(&cfg.mode, "mode", "shared", "k3k mode type (shared, virtual)")
cmd.Flags().StringVar(&cfg.kubeconfigServerHost, "kubeconfig-server", "", "override the kubeconfig server host")
cmd.Flags().StringVar(&cfg.policy, "policy", "", "The policy to create the cluster in")
}
func validateCreateConfig(cfg *CreateConfig) error {
if cfg.servers <= 0 {
return errors.New("invalid number of servers")
}
if cfg.persistenceType != "" {
switch v1alpha1.PersistenceMode(cfg.persistenceType) {
case v1alpha1.EphemeralPersistenceMode, v1alpha1.DynamicPersistenceMode:
return nil
default:
return errors.New(`persistence-type should be one of "dynamic", "ephemeral" or "static"`)
}
}
if _, err := resource.ParseQuantity(cfg.storageRequestSize); err != nil {
return errors.New(`invalid storage size, should be a valid resource quantity e.g "10Gi"`)
}
if cfg.mode != "" {
switch cfg.mode {
case string(v1alpha1.VirtualClusterMode), string(v1alpha1.SharedClusterMode):
return nil
default:
return errors.New(`mode should be one of "shared" or "virtual"`)
}
}
return nil
}
+16 -26
View File
@@ -5,7 +5,7 @@ import (
"errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -21,37 +21,27 @@ import (
var keepData bool
func NewClusterDeleteCmd(appCtx *AppContext) *cli.Command {
flags := CommonFlags(appCtx)
flags = append(flags, FlagNamespace(appCtx))
flags = append(flags,
&cli.BoolFlag{
Name: "keep-data",
Usage: "keeps persistence volumes created for the cluster after deletion",
Destination: &keepData,
},
)
return &cli.Command{
Name: "delete",
Usage: "Delete an existing cluster",
UsageText: "k3kcli cluster delete [command options] NAME",
Action: delete(appCtx),
Flags: flags,
HideHelpCommand: true,
func NewClusterDeleteCmd(appCtx *AppContext) *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Short: "Delete an existing cluster",
Example: "k3kcli cluster delete [command options] NAME",
RunE: delete(appCtx),
Args: cobra.ExactArgs(1),
}
CobraFlagNamespace(appCtx, cmd.Flags())
cmd.Flags().BoolVar(&keepData, "keep-data", false, "keeps persistence volumes created for the cluster after deletion")
return cmd
}
func delete(appCtx *AppContext) cli.ActionFunc {
return func(clx *cli.Context) error {
func delete(appCtx *AppContext) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client := appCtx.Client
name := args[0]
if clx.NArg() != 1 {
return cli.ShowSubcommandHelp(clx)
}
name := clx.Args().First()
if name == k3kcluster.ClusterInvalidName {
return errors.New("invalid cluster name")
}
+15 -19
View File
@@ -3,7 +3,7 @@ package cmds
import (
"context"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/printers"
@@ -13,29 +13,25 @@ import (
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
)
func NewClusterListCmd(appCtx *AppContext) *cli.Command {
flags := CommonFlags(appCtx)
flags = append(flags, FlagNamespace(appCtx))
return &cli.Command{
Name: "list",
Usage: "List all the existing cluster",
UsageText: "k3kcli cluster list [command options]",
Action: list(appCtx),
Flags: flags,
HideHelpCommand: true,
func NewClusterListCmd(appCtx *AppContext) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List all the existing cluster",
Example: "k3kcli cluster list [command options]",
RunE: list(appCtx),
Args: cobra.NoArgs,
}
CobraFlagNamespace(appCtx, cmd.Flags())
return cmd
}
func list(appCtx *AppContext) cli.ActionFunc {
return func(clx *cli.Context) error {
func list(appCtx *AppContext) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client := appCtx.Client
if clx.NArg() > 0 {
return cli.ShowSubcommandHelp(clx)
}
var clusters v1alpha1.ClusterList
if err := client.List(ctx, &clusters, ctrlclient.InNamespace(appCtx.namespace)); err != nil {
return err
@@ -51,6 +47,6 @@ func list(appCtx *AppContext) cli.ActionFunc {
printer := printers.NewTablePrinter(printers.PrintOptions{WithNamespace: true})
return printer.PrintObj(table, clx.App.Writer)
return printer.PrintObj(table, cmd.OutOrStdout())
}
}
+55 -87
View File
@@ -9,7 +9,7 @@ import (
"time"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/tools/clientcmd"
@@ -24,91 +24,63 @@ import (
"github.com/rancher/k3k/pkg/controller/kubeconfig"
)
var (
type GenerateKubeconfigConfig struct {
name string
cn string
org cli.StringSlice
altNames cli.StringSlice
expirationDays int64
configName string
cn string
org []string
altNames []string
expirationDays int64
kubeconfigServerHost string
)
func newGenerateKubeconfigFlags(appCtx *AppContext) []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: "cluster name",
Destination: &name,
},
&cli.StringFlag{
Name: "config-name",
Usage: "the name of the generated kubeconfig file",
Destination: &configName,
},
&cli.StringFlag{
Name: "cn",
Usage: "Common name (CN) of the generated certificates for the kubeconfig",
Destination: &cn,
Value: controller.AdminCommonName,
},
&cli.StringSliceFlag{
Name: "org",
Usage: "Organization name (ORG) of the generated certificates for the kubeconfig",
Value: &org,
},
&cli.StringSliceFlag{
Name: "altNames",
Usage: "altNames of the generated certificates for the kubeconfig",
Value: &altNames,
},
&cli.Int64Flag{
Name: "expiration-days",
Usage: "Expiration date of the certificates used for the kubeconfig",
Destination: &expirationDays,
Value: 356,
},
&cli.StringFlag{
Name: "kubeconfig-server",
Usage: "override the kubeconfig server host",
Destination: &kubeconfigServerHost,
Value: "",
},
}
}
func NewKubeconfigCmd(appCtx *AppContext) *cli.Command {
return &cli.Command{
Name: "kubeconfig",
Usage: "Manage kubeconfig for clusters",
Subcommands: []*cli.Command{
NewKubeconfigGenerateCmd(appCtx),
},
func NewKubeconfigCmd(appCtx *AppContext) *cobra.Command {
cmd := &cobra.Command{
Use: "kubeconfig",
Short: "Manage kubeconfig for clusters",
}
cmd.AddCommand(
NewKubeconfigGenerateCmd(appCtx),
)
return cmd
}
func NewKubeconfigGenerateCmd(appCtx *AppContext) *cli.Command {
flags := CommonFlags(appCtx)
flags = append(flags, FlagNamespace(appCtx))
flags = append(flags, newGenerateKubeconfigFlags(appCtx)...)
func NewKubeconfigGenerateCmd(appCtx *AppContext) *cobra.Command {
cfg := &GenerateKubeconfigConfig{}
return &cli.Command{
Name: "generate",
Usage: "Generate kubeconfig for clusters",
SkipFlagParsing: false,
Action: generate(appCtx),
Flags: flags,
cmd := &cobra.Command{
Use: "generate",
Short: "Generate kubeconfig for clusters",
RunE: generate(appCtx, cfg),
Args: cobra.NoArgs,
}
CobraFlagNamespace(appCtx, cmd.Flags())
generateKubeconfigFlags(cmd, cfg)
return cmd
}
func generate(appCtx *AppContext) cli.ActionFunc {
return func(clx *cli.Context) error {
func generateKubeconfigFlags(cmd *cobra.Command, cfg *GenerateKubeconfigConfig) {
cmd.Flags().StringVar(&cfg.name, "name", "", "cluster name")
cmd.Flags().StringVar(&cfg.configName, "config-name", "", "the name of the generated kubeconfig file")
cmd.Flags().StringVar(&cfg.cn, "cn", controller.AdminCommonName, "Common name (CN) of the generated certificates for the kubeconfig")
cmd.Flags().StringSliceVar(&cfg.org, "org", nil, "Organization name (ORG) of the generated certificates for the kubeconfig")
cmd.Flags().StringSliceVar(&cfg.altNames, "altNames", nil, "altNames of the generated certificates for the kubeconfig")
cmd.Flags().Int64Var(&cfg.expirationDays, "expiration-days", 365, "Expiration date of the certificates used for the kubeconfig")
cmd.Flags().StringVar(&cfg.kubeconfigServerHost, "kubeconfig-server", "", "override the kubeconfig server host")
}
func generate(appCtx *AppContext, cfg *GenerateKubeconfigConfig) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client := appCtx.Client
clusterKey := types.NamespacedName{
Name: name,
Namespace: appCtx.Namespace(name),
Name: cfg.name,
Namespace: appCtx.Namespace(cfg.name),
}
var cluster v1alpha1.Cluster
@@ -123,25 +95,21 @@ func generate(appCtx *AppContext) cli.ActionFunc {
}
host := strings.Split(url.Host, ":")
if kubeconfigServerHost != "" {
host = []string{kubeconfigServerHost}
if err := altNames.Set(kubeconfigServerHost); err != nil {
return err
}
if cfg.kubeconfigServerHost != "" {
host = []string{cfg.kubeconfigServerHost}
cfg.altNames = append(cfg.altNames, cfg.kubeconfigServerHost)
}
certAltNames := certs.AddSANs(altNames.Value())
certAltNames := certs.AddSANs(cfg.altNames)
orgs := org.Value()
if orgs == nil {
orgs = []string{user.SystemPrivilegedGroup}
if len(cfg.org) == 0 {
cfg.org = []string{user.SystemPrivilegedGroup}
}
cfg := kubeconfig.KubeConfig{
CN: cn,
ORG: orgs,
ExpiryDate: time.Hour * 24 * time.Duration(expirationDays),
kubeCfg := kubeconfig.KubeConfig{
CN: cfg.cn,
ORG: cfg.org,
ExpiryDate: time.Hour * 24 * time.Duration(cfg.expirationDays),
AltNames: certAltNames,
}
@@ -150,17 +118,17 @@ func generate(appCtx *AppContext) cli.ActionFunc {
var kubeconfig *clientcmdapi.Config
if err := retry.OnError(controller.Backoff, apierrors.IsNotFound, func() error {
kubeconfig, err = cfg.Generate(ctx, client, &cluster, host[0])
kubeconfig, err = kubeCfg.Generate(ctx, client, &cluster, host[0])
return err
}); err != nil {
return err
}
return writeKubeconfigFile(&cluster, kubeconfig)
return writeKubeconfigFile(&cluster, kubeconfig, cfg.configName)
}
}
func writeKubeconfigFile(cluster *v1alpha1.Cluster, kubeconfig *clientcmdapi.Config) error {
func writeKubeconfigFile(cluster *v1alpha1.Cluster, kubeconfig *clientcmdapi.Config, configName string) error {
if configName == "" {
configName = cluster.Namespace + "-" + cluster.Name + "-kubeconfig.yaml"
}
+13 -10
View File
@@ -1,17 +1,20 @@
package cmds
import (
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
)
func NewPolicyCmd(appCtx *AppContext) *cli.Command {
return &cli.Command{
Name: "policy",
Usage: "policy command",
Subcommands: []*cli.Command{
NewPolicyCreateCmd(appCtx),
NewPolicyDeleteCmd(appCtx),
NewPolicyListCmd(appCtx),
},
func NewPolicyCmd(appCtx *AppContext) *cobra.Command {
cmd := &cobra.Command{
Use: "policy",
Short: "policy command",
}
cmd.AddCommand(
NewPolicyCreateCmd(appCtx),
NewPolicyDeleteCmd(appCtx),
NewPolicyListCmd(appCtx),
)
return cmd
}
+22 -34
View File
@@ -5,7 +5,7 @@ import (
"errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -21,47 +21,35 @@ type VirtualClusterPolicyCreateConfig struct {
mode string
}
func NewPolicyCreateCmd(appCtx *AppContext) *cli.Command {
func NewPolicyCreateCmd(appCtx *AppContext) *cobra.Command {
config := &VirtualClusterPolicyCreateConfig{}
flags := CommonFlags(appCtx)
flags = append(flags,
&cli.StringFlag{
Name: "mode",
Usage: "The allowed mode type of the policy",
Destination: &config.mode,
Value: "shared",
Action: func(ctx *cli.Context, value string) error {
switch value {
case string(v1alpha1.VirtualClusterMode), string(v1alpha1.SharedClusterMode):
return nil
default:
return errors.New(`mode should be one of "shared" or "virtual"`)
}
},
cmd := &cobra.Command{
Use: "create",
Short: "Create new policy",
Example: "k3kcli policy create [command options] NAME",
PreRunE: func(cmd *cobra.Command, args []string) error {
switch config.mode {
case string(v1alpha1.VirtualClusterMode), string(v1alpha1.SharedClusterMode):
return nil
default:
return errors.New(`mode should be one of "shared" or "virtual"`)
}
},
)
return &cli.Command{
Name: "create",
Usage: "Create new policy",
UsageText: "k3kcli policy create [command options] NAME",
Action: policyCreateAction(appCtx, config),
Flags: flags,
HideHelpCommand: true,
RunE: policyCreateAction(appCtx, config),
Args: cobra.ExactArgs(1),
}
cmd.Flags().StringVar(&config.mode, "mode", "shared", "The allowed mode type of the policy")
return cmd
}
func policyCreateAction(appCtx *AppContext, config *VirtualClusterPolicyCreateConfig) cli.ActionFunc {
return func(clx *cli.Context) error {
func policyCreateAction(appCtx *AppContext, config *VirtualClusterPolicyCreateConfig) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client := appCtx.Client
if clx.NArg() != 1 {
return cli.ShowSubcommandHelp(clx)
}
policyName := clx.Args().First()
policyName := args[0]
_, err := createPolicy(ctx, client, v1alpha1.ClusterMode(config.mode), policyName)
+11 -17
View File
@@ -4,34 +4,28 @@ import (
"context"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
)
func NewPolicyDeleteCmd(appCtx *AppContext) *cli.Command {
return &cli.Command{
Name: "delete",
Usage: "Delete an existing policy",
UsageText: "k3kcli policy delete [command options] NAME",
Action: policyDeleteAction(appCtx),
Flags: CommonFlags(appCtx),
HideHelpCommand: true,
func NewPolicyDeleteCmd(appCtx *AppContext) *cobra.Command {
return &cobra.Command{
Use: "delete",
Short: "Delete an existing policy",
Example: "k3kcli policy delete [command options] NAME",
RunE: policyDeleteAction(appCtx),
Args: cobra.ExactArgs(1),
}
}
func policyDeleteAction(appCtx *AppContext) cli.ActionFunc {
return func(clx *cli.Context) error {
func policyDeleteAction(appCtx *AppContext) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client := appCtx.Client
if clx.NArg() != 1 {
return cli.ShowSubcommandHelp(clx)
}
name := clx.Args().First()
name := args[0]
policy := &v1alpha1.VirtualClusterPolicy{}
policy.Name = name
+11 -16
View File
@@ -3,7 +3,7 @@ package cmds
import (
"context"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/printers"
@@ -12,26 +12,21 @@ import (
"github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1"
)
func NewPolicyListCmd(appCtx *AppContext) *cli.Command {
return &cli.Command{
Name: "list",
Usage: "List all the existing policies",
UsageText: "k3kcli policy list [command options]",
Action: policyList(appCtx),
Flags: CommonFlags(appCtx),
HideHelpCommand: true,
func NewPolicyListCmd(appCtx *AppContext) *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List all the existing policies",
Example: "k3kcli policy list [command options]",
RunE: policyList(appCtx),
Args: cobra.NoArgs,
}
}
func policyList(appCtx *AppContext) cli.ActionFunc {
return func(clx *cli.Context) error {
func policyList(appCtx *AppContext) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
client := appCtx.Client
if clx.NArg() > 0 {
return cli.ShowSubcommandHelp(clx)
}
var policies v1alpha1.VirtualClusterPolicyList
if err := client.List(ctx, &policies); err != nil {
return err
@@ -47,6 +42,6 @@ func policyList(appCtx *AppContext) cli.ActionFunc {
printer := printers.NewTablePrinter(printers.PrintOptions{})
return printer.PrintObj(table, clx.App.Writer)
return printer.PrintObj(table, cmd.OutOrStdout())
}
}
+49 -61
View File
@@ -2,9 +2,12 @@ package cmds
import (
"fmt"
"strings"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
@@ -27,52 +30,53 @@ type AppContext struct {
namespace string
}
func NewApp() *cli.App {
func NewRootCmd() *cobra.Command {
appCtx := &AppContext{}
app := cli.NewApp()
app.Name = "k3kcli"
app.Usage = "CLI for K3K"
app.Flags = CommonFlags(appCtx)
rootCmd := &cobra.Command{
Use: "k3kcli",
Short: "CLI for K3K",
Version: buildinfo.Version,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
InitializeConfig(cmd)
app.Before = func(clx *cli.Context) error {
if appCtx.Debug {
logrus.SetLevel(logrus.DebugLevel)
}
if appCtx.Debug {
logrus.SetLevel(logrus.DebugLevel)
}
restConfig, err := loadRESTConfig(appCtx.Kubeconfig)
if err != nil {
return err
}
restConfig, err := loadRESTConfig(appCtx.Kubeconfig)
if err != nil {
return err
}
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
_ = v1alpha1.AddToScheme(scheme)
_ = apiextensionsv1.AddToScheme(scheme)
scheme := runtime.NewScheme()
_ = clientgoscheme.AddToScheme(scheme)
_ = v1alpha1.AddToScheme(scheme)
_ = apiextensionsv1.AddToScheme(scheme)
ctrlClient, err := client.New(restConfig, client.Options{Scheme: scheme})
if err != nil {
return err
}
ctrlClient, err := client.New(restConfig, client.Options{Scheme: scheme})
if err != nil {
return err
}
appCtx.RestConfig = restConfig
appCtx.Client = ctrlClient
appCtx.RestConfig = restConfig
appCtx.Client = ctrlClient
return nil
return nil
},
DisableAutoGenTag: true,
}
app.Version = buildinfo.Version
cli.VersionPrinter = func(cCtx *cli.Context) {
fmt.Println("k3kcli Version: " + buildinfo.Version)
}
rootCmd.PersistentFlags().StringVar(&appCtx.Kubeconfig, "kubeconfig", "", "kubeconfig path ($HOME/.kube/config or $KUBECONFIG if set)")
rootCmd.PersistentFlags().BoolVar(&appCtx.Debug, "debug", false, "Turn on debug logs")
app.Commands = []*cli.Command{
rootCmd.AddCommand(
NewClusterCmd(appCtx),
NewPolicyCmd(appCtx),
NewKubeconfigCmd(appCtx),
}
)
return app
return rootCmd
}
func (ctx *AppContext) Namespace(name string) string {
@@ -96,36 +100,20 @@ func loadRESTConfig(kubeconfig string) (*rest.Config, error) {
return kubeConfig.ClientConfig()
}
func CommonFlags(appCtx *AppContext) []cli.Flag {
return []cli.Flag{
FlagDebug(appCtx),
FlagKubeconfig(appCtx),
}
func CobraFlagNamespace(appCtx *AppContext, flag *pflag.FlagSet) {
flag.StringVarP(&appCtx.namespace, "namespace", "n", "", "namespace of the k3k cluster")
}
func FlagDebug(appCtx *AppContext) *cli.BoolFlag {
return &cli.BoolFlag{
Name: "debug",
Usage: "Turn on debug logs",
Destination: &appCtx.Debug,
EnvVars: []string{"K3K_DEBUG"},
}
}
func InitializeConfig(cmd *cobra.Command) {
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.AutomaticEnv()
func FlagKubeconfig(appCtx *AppContext) *cli.StringFlag {
return &cli.StringFlag{
Name: "kubeconfig",
Usage: "kubeconfig path",
Destination: &appCtx.Kubeconfig,
DefaultText: "$HOME/.kube/config or $KUBECONFIG if set",
}
}
func FlagNamespace(appCtx *AppContext) *cli.StringFlag {
return &cli.StringFlag{
Name: "namespace",
Usage: "namespace of the k3k cluster",
Aliases: []string{"n"},
Destination: &appCtx.namespace,
}
// Bind the current command's flags to viper
cmd.Flags().VisitAll(func(f *pflag.Flag) {
// Apply the viper config value to the flag when the flag is not set and viper has a value
if !f.Changed && viper.IsSet(f.Name) {
val := viper.Get(f.Name)
_ = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val))
}
})
}
+2 -4
View File
@@ -1,16 +1,14 @@
package main
import (
"os"
"github.com/sirupsen/logrus"
"github.com/rancher/k3k/cli/cmds"
)
func main() {
app := cmds.NewApp()
if err := app.Run(os.Args); err != nil {
app := cmds.NewRootCmd()
if err := app.Execute(); err != nil {
logrus.Fatal(err)
}
}