diff --git a/Makefile b/Makefile index e78cdee4..c88be9cc 100644 --- a/Makefile +++ b/Makefile @@ -9,10 +9,12 @@ CONTROLLER_TOOLS_VERSION ?= v0.14.0 GINKGO_VERSION ?= v2.21.0 ENVTEST_VERSION ?= latest ENVTEST_K8S_VERSION := 1.31.0 +CRD_REF_DOCS_VER ?= v0.1.0 GOLANGCI_LINT ?= go run github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) CONTROLLER_GEN ?= go run sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) GINKGO ?= go run github.com/onsi/ginkgo/v2/ginkgo@$(GINKGO_VERSION) +CRD_REF_DOCS := go run github.com/elastic/crd-ref-docs@$(CRD_REF_DOCS_VER) ENVTEST ?= go run sigs.k8s.io/controller-runtime/tools/setup-envtest@$(ENVTEST_VERSION) ENVTEST_DIR ?= $(shell pwd)/.envtest @@ -75,8 +77,9 @@ build-crds: ## Build the CRDs specs output:crd:dir=./charts/k3k/crds .PHONY: docs -docs: ## Build the CRDs docs - $(MAKE) -C docs/crds +docs: ## Build the CRDs and CLI docs + $(CRD_REF_DOCS) --config=./docs/crds/config.yaml --renderer=markdown --source-path=./pkg/apis/k3k.io/v1alpha1 --output-path=./docs/crds/crd-docs.md + @go run ./docs/cli/genclidoc.go .PHONY: lint lint: ## Find any linting issues in the project diff --git a/cli/cmds/cluster/cluster.go b/cli/cmds/cluster.go similarity index 59% rename from cli/cmds/cluster/cluster.go rename to cli/cmds/cluster.go index d39c5a6f..6bbbc6d0 100644 --- a/cli/cmds/cluster/cluster.go +++ b/cli/cmds/cluster.go @@ -1,16 +1,16 @@ -package cluster +package cmds import ( "github.com/urfave/cli/v2" ) -func NewCommand() *cli.Command { +func NewClusterCommand() *cli.Command { return &cli.Command{ Name: "cluster", Usage: "cluster command", Subcommands: []*cli.Command{ - NewCreateCmd(), - NewDeleteCmd(), + NewClusterCreateCmd(), + NewClusterDeleteCmd(), }, } } diff --git a/cli/cmds/cluster/create.go b/cli/cmds/cluster_create.go similarity index 89% rename from cli/cmds/cluster/create.go rename to cli/cmds/cluster_create.go index d3ec03d8..61c4c05b 100644 --- a/cli/cmds/cluster/create.go +++ b/cli/cmds/cluster_create.go @@ -1,4 +1,4 @@ -package cluster +package cmds import ( "context" @@ -9,7 +9,6 @@ import ( "strings" "time" - "github.com/rancher/k3k/cli/cmds" "github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1" k3kcluster "github.com/rancher/k3k/pkg/controller/cluster" "github.com/rancher/k3k/pkg/controller/kubeconfig" @@ -18,9 +17,7 @@ import ( v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/util/retry" @@ -28,13 +25,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -var Scheme = runtime.NewScheme() - -func init() { - _ = clientgoscheme.AddToScheme(Scheme) - _ = v1alpha1.AddToScheme(Scheme) -} - type CreateConfig struct { token string clusterCIDR string @@ -50,7 +40,7 @@ type CreateConfig struct { kubeconfigServerHost string } -func NewCreateCmd() *cli.Command { +func NewClusterCreateCmd() *cli.Command { createConfig := &CreateConfig{} createFlags := NewCreateFlags(createConfig) @@ -59,7 +49,7 @@ func NewCreateCmd() *cli.Command { Usage: "Create new cluster", UsageText: "k3kcli cluster create [command options] NAME", Action: createAction(createConfig), - Flags: append(cmds.CommonFlags, createFlags...), + Flags: append(CommonFlags, createFlags...), HideHelpCommand: true, } } @@ -77,7 +67,7 @@ func createAction(config *CreateConfig) cli.ActionFunc { return errors.New("invalid cluster name") } - restConfig, err := clientcmd.BuildConfigFromFlags("", cmds.Kubeconfig) + restConfig, err := clientcmd.BuildConfigFromFlags("", Kubeconfig) if err != nil { return err } @@ -97,8 +87,7 @@ func createAction(config *CreateConfig) cli.ActionFunc { if config.token != "" { logrus.Infof("Creating cluster token secret") - - obj := k3kcluster.TokenSecretObj(config.token, name, cmds.Namespace()) + obj := k3kcluster.TokenSecretObj(config.token, name, Namespace()) if err := ctrlClient.Create(ctx, &obj); err != nil { return err } @@ -106,7 +95,7 @@ func createAction(config *CreateConfig) cli.ActionFunc { logrus.Infof("Creating a new cluster [%s]", name) - cluster := newCluster(name, cmds.Namespace(), config) + cluster := newCluster(name, Namespace(), config) cluster.Spec.Expose = &v1alpha1.ExposeConfig{ NodePort: &v1alpha1.NodePortConfig{}, @@ -117,12 +106,10 @@ func createAction(config *CreateConfig) cli.ActionFunc { if err != nil { return err } - host := strings.Split(url.Host, ":") if config.kubeconfigServerHost != "" { host = []string{config.kubeconfigServerHost} } - cluster.Spec.TLSSANs = []string{host[0]} if err := ctrlClient.Create(ctx, cluster); err != nil { @@ -147,7 +134,6 @@ func createAction(config *CreateConfig) cli.ActionFunc { cfg := kubeconfig.New() var kubeconfig *clientcmdapi.Config - if err := retry.OnError(availableBackoff, apierrors.IsNotFound, func() error { kubeconfig, err = cfg.Extract(ctx, ctrlClient, cluster, host[0]) return err @@ -203,7 +189,6 @@ func newCluster(name, namespace string, config *CreateConfig) *v1alpha1.Cluster if config.storageClassName == "" { cluster.Spec.Persistence.StorageClassName = nil } - if config.token != "" { cluster.Spec.TokenSecretRef = &v1.SecretReference{ Name: k3kcluster.TokenSecretName(name), diff --git a/cli/cmds/cluster/create_flags.go b/cli/cmds/cluster_create_flags.go similarity index 99% rename from cli/cmds/cluster/create_flags.go rename to cli/cmds/cluster_create_flags.go index 942b315e..1c7bb687 100644 --- a/cli/cmds/cluster/create_flags.go +++ b/cli/cmds/cluster_create_flags.go @@ -1,4 +1,4 @@ -package cluster +package cmds import ( "errors" diff --git a/cli/cmds/cluster/delete.go b/cli/cmds/cluster_delete.go similarity index 83% rename from cli/cmds/cluster/delete.go rename to cli/cmds/cluster_delete.go index ec56b6e6..e61f3942 100644 --- a/cli/cmds/cluster/delete.go +++ b/cli/cmds/cluster_delete.go @@ -1,10 +1,9 @@ -package cluster +package cmds import ( "context" "errors" - "github.com/rancher/k3k/cli/cmds" "github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1" k3kcluster "github.com/rancher/k3k/pkg/controller/cluster" "github.com/sirupsen/logrus" @@ -14,13 +13,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func NewDeleteCmd() *cli.Command { +func NewClusterDeleteCmd() *cli.Command { return &cli.Command{ Name: "delete", Usage: "Delete an existing cluster", UsageText: "k3kcli cluster delete [command options] NAME", Action: delete, - Flags: cmds.CommonFlags, + Flags: CommonFlags, HideHelpCommand: true, } } @@ -37,7 +36,7 @@ func delete(clx *cli.Context) error { return errors.New("invalid cluster name") } - restConfig, err := clientcmd.BuildConfigFromFlags("", cmds.Kubeconfig) + restConfig, err := clientcmd.BuildConfigFromFlags("", Kubeconfig) if err != nil { return err } @@ -54,9 +53,8 @@ func delete(clx *cli.Context) error { cluster := v1alpha1.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: cmds.Namespace(), + Namespace: Namespace(), }, } - return ctrlClient.Delete(ctx, &cluster) } diff --git a/cli/cmds/kubeconfig/kubeconfig.go b/cli/cmds/kubeconfig.go similarity index 88% rename from cli/cmds/kubeconfig/kubeconfig.go rename to cli/cmds/kubeconfig.go index 8e080d37..2eb61972 100644 --- a/cli/cmds/kubeconfig/kubeconfig.go +++ b/cli/cmds/kubeconfig.go @@ -1,4 +1,4 @@ -package kubeconfig +package cmds import ( "context" @@ -8,7 +8,6 @@ import ( "strings" "time" - "github.com/rancher/k3k/cli/cmds" "github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1" "github.com/rancher/k3k/pkg/controller" "github.com/rancher/k3k/pkg/controller/certs" @@ -16,23 +15,15 @@ import ( "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apiserver/pkg/authentication/user" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" ) -func init() { - _ = clientgoscheme.AddToScheme(Scheme) - _ = v1alpha1.AddToScheme(Scheme) -} - var ( - Scheme = runtime.NewScheme() name string cn string org cli.StringSlice @@ -88,11 +79,11 @@ var subcommands = []*cli.Command{ Usage: "Generate kubeconfig for clusters", SkipFlagParsing: false, Action: generate, - Flags: append(cmds.CommonFlags, generateKubeconfigFlags...), + Flags: append(CommonFlags, generateKubeconfigFlags...), }, } -func NewCommand() *cli.Command { +func NewKubeconfigCommand() *cli.Command { return &cli.Command{ Name: "kubeconfig", Usage: "Manage kubeconfig for clusters", @@ -102,10 +93,9 @@ func NewCommand() *cli.Command { func generate(clx *cli.Context) error { var cluster v1alpha1.Cluster - ctx := context.Background() - restConfig, err := clientcmd.BuildConfigFromFlags("", cmds.Kubeconfig) + restConfig, err := clientcmd.BuildConfigFromFlags("", Kubeconfig) if err != nil { return err } @@ -116,10 +106,9 @@ func generate(clx *cli.Context) error { if err != nil { return err } - clusterKey := types.NamespacedName{ Name: name, - Namespace: cmds.Namespace(), + Namespace: Namespace(), } if err := ctrlClient.Get(ctx, clusterKey, &cluster); err != nil { @@ -130,11 +119,9 @@ func generate(clx *cli.Context) error { if err != nil { return err } - host := strings.Split(url.Host, ":") if kubeconfigServerHost != "" { host = []string{kubeconfigServerHost} - if err := altNames.Set(kubeconfigServerHost); err != nil { return err } @@ -157,7 +144,6 @@ func generate(clx *cli.Context) error { logrus.Infof("waiting for cluster to be available..") var kubeconfig *clientcmdapi.Config - if err := retry.OnError(controller.Backoff, apierrors.IsNotFound, func() error { kubeconfig, err = cfg.Extract(ctx, ctrlClient, &cluster, host[0]) return err diff --git a/cli/cmds/root.go b/cli/cmds/root.go index 189e2fbf..f015d402 100644 --- a/cli/cmds/root.go +++ b/cli/cmds/root.go @@ -1,10 +1,15 @@ package cmds import ( + "fmt" "os" + "github.com/rancher/k3k/pkg/apis/k3k.io/v1alpha1" + "github.com/rancher/k3k/pkg/buildinfo" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" + "k8s.io/apimachinery/pkg/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" ) const ( @@ -12,9 +17,12 @@ const ( ) var ( - debug bool - Kubeconfig string - namespace string + Scheme = runtime.NewScheme() + + debug bool + Kubeconfig string + namespace string + CommonFlags = []cli.Flag{ &cli.StringFlag{ Name: "kubeconfig", @@ -31,6 +39,11 @@ var ( } ) +func init() { + _ = clientgoscheme.AddToScheme(Scheme) + _ = v1alpha1.AddToScheme(Scheme) +} + func NewApp() *cli.App { app := cli.NewApp() app.Name = "k3kcli" @@ -48,10 +61,19 @@ func NewApp() *cli.App { if debug { logrus.SetLevel(logrus.DebugLevel) } - return nil } + app.Version = buildinfo.Version + cli.VersionPrinter = func(cCtx *cli.Context) { + fmt.Println("k3kcli Version: " + buildinfo.Version) + } + + app.Commands = []*cli.Command{ + NewClusterCommand(), + NewKubeconfigCommand(), + } + return app } @@ -59,6 +81,5 @@ func Namespace() string { if namespace == "" { return defaultNamespace } - return namespace } diff --git a/cli/main.go b/cli/main.go index 20b8946d..355dfcd2 100644 --- a/cli/main.go +++ b/cli/main.go @@ -1,29 +1,14 @@ package main import ( - "fmt" "os" "github.com/rancher/k3k/cli/cmds" - "github.com/rancher/k3k/cli/cmds/cluster" - "github.com/rancher/k3k/cli/cmds/kubeconfig" - "github.com/rancher/k3k/pkg/buildinfo" "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" ) func main() { app := cmds.NewApp() - app.Version = buildinfo.Version - cli.VersionPrinter = func(cCtx *cli.Context) { - fmt.Println("k3kcli Version: " + buildinfo.Version) - } - - app.Commands = []*cli.Command{ - cluster.NewCommand(), - kubeconfig.NewCommand(), - } - if err := app.Run(os.Args); err != nil { logrus.Fatal(err) } diff --git a/docs/cli/cli-docs.md b/docs/cli/cli-docs.md new file mode 100644 index 00000000..1d3a75b2 --- /dev/null +++ b/docs/cli/cli-docs.md @@ -0,0 +1,98 @@ +# NAME + +k3kcli - CLI for K3K + +# SYNOPSIS + +k3kcli + +``` +[--debug] +``` + +**Usage**: + +``` +k3kcli [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...] +``` + +# GLOBAL OPTIONS + +**--debug**: Turn on debug logs + + +# COMMANDS + +## cluster + +cluster command + +### create + +Create new cluster + +>k3kcli cluster create [command options] NAME + +**--agent-args**="": agents extra arguments + +**--agents**="": number of agents (default: 0) + +**--cluster-cidr**="": cluster CIDR + +**--kubeconfig**="": kubeconfig path (default: "/home/jpgouin/.kube/config") + +**--kubeconfig-server**="": override the kubeconfig server host + +**--mode**="": k3k mode type (shared, virtual) (default: "shared") + +**--namespace**="": namespace to create the k3k cluster in + +**--persistence-type**="": persistence mode for the nodes (ephemeral, static, dynamic) (default: "dynamic") + +**--server-args**="": servers extra arguments + +**--servers**="": number of servers (default: 1) + +**--service-cidr**="": service CIDR + +**--storage-class-name**="": storage class name for dynamic persistence type + +**--token**="": token of the cluster + +**--version**="": k3s version + +### delete + +Delete an existing cluster + +>k3kcli cluster delete [command options] NAME + +**--kubeconfig**="": kubeconfig path (default: "/home/jpgouin/.kube/config") + +**--namespace**="": namespace to create the k3k cluster in + +## kubeconfig + +Manage kubeconfig for clusters + +### generate + +Generate kubeconfig for clusters + +**--altNames**="": altNames of the generated certificates for the kubeconfig + +**--cn**="": Common name (CN) of the generated certificates for the kubeconfig (default: "system:admin") + +**--config-name**="": the name of the generated kubeconfig file + +**--expiration-days**="": Expiration date of the certificates used for the kubeconfig (default: 356) + +**--kubeconfig**="": kubeconfig path (default: "/home/jpgouin/.kube/config") + +**--kubeconfig-server**="": override the kubeconfig server host + +**--name**="": cluster name + +**--namespace**="": namespace to create the k3k cluster in + +**--org**="": Organization name (ORG) of the generated certificates for the kubeconfig diff --git a/docs/cli/genclidoc.go b/docs/cli/genclidoc.go new file mode 100644 index 00000000..e80cfab5 --- /dev/null +++ b/docs/cli/genclidoc.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "os" + "path" + + "github.com/rancher/k3k/cli/cmds" +) + +func main() { + // Instantiate the CLI application + app := cmds.NewApp() + + // Generate the Markdown documentation + md, err := app.ToMarkdown() + if err != nil { + fmt.Println("Error generating documentation:", err) + os.Exit(1) + } + + wd, err := os.Getwd() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + outputFile := path.Join(wd, "docs/cli/cli-docs.md") + err = os.WriteFile(outputFile, []byte(md), 0644) + if err != nil { + fmt.Println("Error generating documentation:", err) + os.Exit(1) + } + + fmt.Println("Documentation generated at " + outputFile) +} diff --git a/docs/crds/Makefile b/docs/crds/Makefile deleted file mode 100644 index 93df3d7c..00000000 --- a/docs/crds/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -CRD_REF_DOCS_VER := v0.1.0 -CRD_REF_DOCS := go run github.com/elastic/crd-ref-docs@$(CRD_REF_DOCS_VER) - -.PHONY: generate -generate: - $(CRD_REF_DOCS) --config=config.yaml --renderer=markdown --source-path=../../pkg/apis/k3k.io/v1alpha1 --output-path=crd-docs.md