diff --git a/Makefile b/Makefile index e78cdee..c88be9c 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/README.md b/README.md index c47512f..7f38d0e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,10 @@ This section provides instructions on how to install K3k and the `k3kcli`. ### Prerequisites * [Helm](https://helm.sh) must be installed to use the charts. Please refer to Helm's [documentation](https://helm.sh/docs) to get started. +* An existing [RKE2](https://docs.rke2.io/install/quickstart) Kubernetes cluster (recommended). +* A configured storage provider with a default storage class. +**Note:** If you do not have a storage provider, you can configure the cluster to use ephemeral or static storage. Please consult the [k3kcli advance usage](./docs/advanced-usage.md#using-the-cli) for instructions on using these options. ### Install the K3k controller 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 d39c5a6..6bbbc6d 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 d3ec03d..b6c9239 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 } @@ -98,7 +88,8 @@ 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 +97,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{}, 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 942b315..1c7bb68 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 ec56b6e..aa15e7b 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,7 +53,7 @@ func delete(clx *cli.Context) error { cluster := v1alpha1.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: name, - Namespace: cmds.Namespace(), + Namespace: Namespace(), }, } diff --git a/cli/cmds/kubeconfig/kubeconfig.go b/cli/cmds/kubeconfig.go similarity index 89% rename from cli/cmds/kubeconfig/kubeconfig.go rename to cli/cmds/kubeconfig.go index 8e080d3..47e4272 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", @@ -105,7 +96,7 @@ func generate(clx *cli.Context) error { ctx := context.Background() - restConfig, err := clientcmd.BuildConfigFromFlags("", cmds.Kubeconfig) + restConfig, err := clientcmd.BuildConfigFromFlags("", Kubeconfig) if err != nil { return err } @@ -119,7 +110,7 @@ func generate(clx *cli.Context) error { clusterKey := types.NamespacedName{ Name: name, - Namespace: cmds.Namespace(), + Namespace: Namespace(), } if err := ctrlClient.Get(ctx, clusterKey, &cluster); err != nil { diff --git a/cli/cmds/root.go b/cli/cmds/root.go index 189e2fb..dc0a26f 100644 --- a/cli/cmds/root.go +++ b/cli/cmds/root.go @@ -1,10 +1,14 @@ package cmds import ( - "os" + "fmt" + "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,16 +16,19 @@ const ( ) var ( - debug bool - Kubeconfig string - namespace string + Scheme = runtime.NewScheme() + + debug bool + Kubeconfig string + namespace string + CommonFlags = []cli.Flag{ &cli.StringFlag{ Name: "kubeconfig", EnvVars: []string{"KUBECONFIG"}, Usage: "kubeconfig path", Destination: &Kubeconfig, - Value: os.Getenv("HOME") + "/.kube/config", + Value: "$HOME/.kube/config", }, &cli.StringFlag{ Name: "namespace", @@ -31,6 +38,11 @@ var ( } ) +func init() { + _ = clientgoscheme.AddToScheme(Scheme) + _ = v1alpha1.AddToScheme(Scheme) +} + func NewApp() *cli.App { app := cli.NewApp() app.Name = "k3kcli" @@ -52,6 +64,16 @@ func NewApp() *cli.App { 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 } diff --git a/cli/main.go b/cli/main.go index 20b8946..355dfcd 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/advanced-usage.md b/docs/advanced-usage.md index dfd220e..468066e 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -6,7 +6,7 @@ This document provides advanced usage information for k3k, including detailed us The `Cluster` resource provides a variety of fields for customizing the behavior of your virtual clusters. You can check the [CRD documentation](./crds/crd-docs.md) for the full specs. -**Note:** Most of these customization options can also be configured using the `k3kcli` tool. Refer to the `k3kcli` documentation for more details. +**Note:** Most of these customization options can also be configured using the `k3kcli` tool. Refer to the [k3kcli](./cli/cli-docs.md) documentation for more details. @@ -112,3 +112,21 @@ The `clusterDNS` field specifies the IP address for the CoreDNS service. It need ### `serverArgs` The `serverArgs` field allows you to specify additional arguments to be passed to the K3s server pods. + +## Using the cli + +You can check the [k3kcli documentation](./cli/cli-docs.md) for the full specs. + +### No storage provider: + +* Ephemeral Storage: + + ```bash + k3kcli cluster create my-cluster --persistence-type ephemeral + ``` + +*Important Notes:* + +* Using `--persistence-type ephemeral` will result in data loss if the nodes are restarted. + +* It is highly recommended to use `--persistence-type dynamic` with a configured storage class. \ No newline at end of file diff --git a/docs/cli/cli-docs.md b/docs/cli/cli-docs.md new file mode 100644 index 0000000..642aa7e --- /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/.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 (dynamic, ephemeral, static) (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/.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/.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 0000000..7fd6649 --- /dev/null +++ b/docs/cli/genclidoc.go @@ -0,0 +1,37 @@ +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 93df3d7..0000000 --- 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