diff --git a/Dockerfile.dapper b/Dockerfile.dapper index b52dedc4..7df46347 100644 --- a/Dockerfile.dapper +++ b/Dockerfile.dapper @@ -5,7 +5,7 @@ ENV ARCH=${DAPPER_HOST_ARCH} ENV DAPPER_RUN_ARGS --privileged ENV DAPPER_ENV REPO TAG DRONE_TAG -ENV DAPPER_SOURCE /longhorn-preflight +ENV DAPPER_SOURCE /cli ENV DAPPER_OUTPUT ./bin ./spdk coverage.out ENV DAPPER_DOCKER_SOCKET true diff --git a/README.md b/README.md index f4a6be6c..9b15bd25 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,43 @@ -# Longhorn Preflight +# Longhorn Commandline Interface (longhornctl) -`longhorn-preflight` helps install, configure and check the prerequisites for Longhorn system. +This repository contains the source code for `longhornctl`, a CLI (command-line interface) designed to simplify Longhorn manual operations. -## Install +## What Can You Do With `longhornctl`? -### Deploy +- Install and verify prelight requirements. +- Execute one-time Longhorn operations. +- Gain inside into your Longhorn system. -Users can create `longhorn-preflight` DaemonSet for installing and configuring the prerequisites and the environment. +## Install `longhornctl` -``` -# kubectl -f deploy/install.yaml -``` +### Run From Container Image -### Tweak the Options +`To be updated` -#### General Options +### Using curl -- `UPDATE_PACKAGE_LIST`: Update package list before install required packages. +`To be updated` -#### SPDK Specific Options +### Build From Source -- `ENABLE_SPDK`: Enable installation of required packages, modules and setup. -- `HUGEMEM`: Hugepage size in MiB for SPDK. -- `PCI_ALLOWED`: Whitespace separated list of PCI devices. By default, block all PCI devices use a non-valid address. -- `DRIVER_OVERRIDE`: Bind devices to the given user space driver. +1. Clone repository + ```bash + git clone https://github.com/longhorn/cli.git + ``` +1. Build the `longhornctl` binary + ```bash + cd cli + make + ``` + > **Note:** This process will generate two binaries: + > - `longhornctl`: A command-line interface for remote Longhorn operations, designed to be run outside the Kubernetes cluster. It executes `longhornctl-local` for operations within the cluster. + > - `longhornctl-local`: A command-line interface to be used within a DaemonSet pod inside the Kubernetes cluster, handling in-cluster and host operations. +1. After the build process completes, find the `longhornctl` binary in the `./bin` directory. -## Check +## Getting Started -### Deploy +To begin, run `longhornctl --help` to access a list of available commands and options. -Users can create `longhorn-preflight` DaemonSet for checking the prerequisites and the environment. +### Command Reference -``` -# kubectl -f deploy/check.yaml -``` - -### Tweak the Options - -#### SPDK Specific Options - -- `ENABLE_SPDK`: Enable installation of required packages, modules and setup. -- `HUGEMEM`: Hugepage size in MiB for SPDK. -- `UIO_DRIVER`: Userspace IO driver. +`To be updated` \ No newline at end of file diff --git a/cmd/app/check.go b/cmd/app/check.go deleted file mode 100644 index 63ad12f8..00000000 --- a/cmd/app/check.go +++ /dev/null @@ -1,50 +0,0 @@ -package app - -import ( - "os" - - "github.com/sirupsen/logrus" - "github.com/urfave/cli" - - "github.com/longhorn/cli/pkg/checker" - "github.com/longhorn/cli/pkg/pkgmgr" -) - -func PreflightCheckCmd(pkgMgrType pkgmgr.PackageManagerType) cli.Command { - return cli.Command{ - Name: "check", - Flags: []cli.Flag{}, - Usage: "Check environment", - Action: func(c *cli.Context) { - if err := check(c, pkgMgrType); err != nil { - logrus.WithError(err).Fatalf("Failed to run command") - } - - }, - } -} - -func check(_ *cli.Context, pkgMgrType pkgmgr.PackageManagerType) error { - ckr, err := checker.NewChecker(pkgMgrType) - if err != nil { - return err - } - - ckr.CheckIscsidService() - ckr.CheckMultipathService() - ckr.CheckNFSv4Support() - ckr.CheckPackagesInstalled(false) - - if os.Getenv("ENABLE_SPDK") == "true" { - instructionSets := map[string][]string{ - "amd64": {"sse4_2"}, - } - ckr.CheckCpuInstructionSet(instructionSets) - - ckr.CheckHugePages() - ckr.CheckPackagesInstalled(true) - ckr.CheckModulesLoaded(true) - } - - return nil -} diff --git a/cmd/app/install.go b/cmd/app/install.go deleted file mode 100644 index c66ca15a..00000000 --- a/cmd/app/install.go +++ /dev/null @@ -1,50 +0,0 @@ -package app - -import ( - "os" - - "github.com/sirupsen/logrus" - "github.com/urfave/cli" - - "github.com/longhorn/cli/pkg/installer" - "github.com/longhorn/cli/pkg/pkgmgr" -) - -func PreflightInstallCmd(pkgMgrType pkgmgr.PackageManagerType) cli.Command { - return cli.Command{ - Name: "install", - Flags: []cli.Flag{}, - Usage: "Install and configure prerequisites", - Action: func(c *cli.Context) { - if err := install(c, pkgMgrType); err != nil { - logrus.WithError(err).Fatalf("Failed to run command") - } - }, - } -} - -func install(_ *cli.Context, pkgMgrType pkgmgr.PackageManagerType) error { - ins, err := installer.NewInstaller(pkgMgrType) - if err != nil { - return err - } - - if os.Getenv("UPDATE_PACKAGE_LIST") == "true" { - ins.UpdatePackageList() - } - - ins.StartServices() - ins.ProbeModules(false) - ins.InstallPackages(false) - - if os.Getenv("ENABLE_SPDK") == "true" { - ins.InstallPackages(true) - ins.ProbeModules(true) - err := ins.ConfigureSPDKEnv() - if err != nil { - return err - } - } - - return nil -} diff --git a/cmd/local/longhornctl-local.go b/cmd/local/longhornctl-local.go new file mode 100644 index 00000000..c10abf11 --- /dev/null +++ b/cmd/local/longhornctl-local.go @@ -0,0 +1,73 @@ +package main + +import ( + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "k8s.io/kubectl/pkg/util/templates" + + localsubcmd "github.com/longhorn/cli/cmd/local/subcmd" + remotesubcmd "github.com/longhorn/cli/cmd/remote/subcmd" + "github.com/longhorn/cli/pkg/consts" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func main() { + if err := newCmdLonghornctlLocal().Execute(); err != nil { + logrus.WithError(err).Fatal("Failed to execute command") + os.Exit(1) + } +} + +func newCmdLonghornctlLocal() *cobra.Command { + globalOpts := &types.GlobalCmdOptions{} + + cmd := &cobra.Command{ + Use: consts.CmdLonghornctlLocal, + Short: "Longhorn commandline interface.", + Long: "CLI (local) for troubleshooting and operations to be used in the Kubernetes cluster.", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + err := utils.SetLog(globalOpts.LogLevel) + if err != nil { + logrus.WithError(err).Warn("Failed to set log level") + } + }, + } + + cmd.CompletionOptions.HiddenDefaultCmd = true + + cmd.PersistentFlags().StringVarP(&globalOpts.LogLevel, consts.CmdOptLogLevel, "l", "info", "log level (trace, debug, info, warn, error, fatal, panic)") + + groups := templates.CommandGroups{ + { + Message: "Install And Uninstall Commands:", + Commands: []*cobra.Command{ + localsubcmd.NewCmdInstall(globalOpts), + }, + }, + { + Message: "Operation Commands:", + Commands: []*cobra.Command{ + localsubcmd.NewCmdTrim(globalOpts), + }, + }, + { + Message: "Troubleshoot Commands:", + Commands: []*cobra.Command{ + localsubcmd.NewCmdCheck(globalOpts), + localsubcmd.NewCmdGet(globalOpts), + }, + }, + } + groups.Add(cmd) + + cmd.AddCommand(remotesubcmd.NewCmdGlobalOptions()) + + filters := []string{"options"} + templates.ActsAsRootCommand(cmd, filters, groups...) + + return cmd +} diff --git a/cmd/local/subcmd/check.go b/cmd/local/subcmd/check.go new file mode 100644 index 00000000..253671b5 --- /dev/null +++ b/cmd/local/subcmd/check.go @@ -0,0 +1,71 @@ +package cmd + +import ( + "os" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/longhorn/cli/pkg/consts" + local "github.com/longhorn/cli/pkg/local/preflight" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func NewCmdCheck(globalOpts *types.GlobalCmdOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: consts.SubCmdCheck, + Short: "Longhorn checking operations", + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.AddCommand(newCmdCheckPreflight(globalOpts)) + + return cmd +} + +func newCmdCheckPreflight(globalOpts *types.GlobalCmdOptions) *cobra.Command { + var localChecker = local.Checker{} + + cmd := &cobra.Command{ + Use: consts.SubCmdPreflight, + Short: "Check preflight environment", + Long: `This command verifies your Kubernetes cluster environment. It performs a series of checks to ensure your cluster meets the requirements for Longhorn to function properly. +These checks can help to identify issues that might prevent Longhorn from functioning properly.`, + + PreRun: func(cmd *cobra.Command, args []string) { + localChecker.LogLevel = globalOpts.LogLevel + + if err := localChecker.Init(); err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to initialize preflight checker")) + } + }, + + Run: func(cmd *cobra.Command, args []string) { + if err := localChecker.Run(); err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to run preflight checker")) + } + + logrus.Info("Successfully checked preflight environment") + }, + + PostRun: func(cmd *cobra.Command, args []string) { + if err := localChecker.Output(); err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to output preflight checker collection")) + } + + logrus.Info("Successfully output preflight checker collection") + }, + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.Flags().StringVarP(&localChecker.OutputFilePath, consts.CmdOptOutputFile, "o", os.Getenv(consts.EnvOutputFilePath), "Output the result to a file, default to stdout.") + cmd.Flags().BoolVar(&localChecker.EnableSpdk, consts.CmdOptEnableSpdk, utils.ConvertStringToTypeOrDefault(os.Getenv(consts.EnvEnableSpdk), false), "Enable checking of SPDK required packages, modules, and setup.") + cmd.Flags().IntVar(&localChecker.HugePageSize, consts.CmdOptHugePageSize, utils.ConvertStringToTypeOrDefault(os.Getenv(consts.EnvHugePageSize), 1024), "Specify the huge page size in MiB for SPDK.") + cmd.Flags().StringVar(&localChecker.UioDriver, consts.CmdOptUioDriver, os.Getenv(consts.EnvUioDriver), "User space I/O driver for SPDK.") + + return cmd +} diff --git a/cmd/local/subcmd/get.go b/cmd/local/subcmd/get.go new file mode 100644 index 00000000..80402483 --- /dev/null +++ b/cmd/local/subcmd/get.go @@ -0,0 +1,80 @@ +package cmd + +import ( + "os" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/longhorn/cli/pkg/consts" + local "github.com/longhorn/cli/pkg/local/replica" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func NewCmdGet(globalOpts *types.GlobalCmdOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: consts.SubCmdGet, + Short: "Longhorn information gathering operations", + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.AddCommand(newCmdGetReplica(globalOpts)) + + return cmd +} + +func newCmdGetReplica(globalOpts *types.GlobalCmdOptions) *cobra.Command { + var localGetter = local.Getter{} + + cmd := &cobra.Command{ + Use: consts.SubCmdReplica, + Short: "Get Longhorn replica information", + Long: `This command retrieves detailed information about Longhorn replicas. +The information can be used for troubleshooting and gaining insights into the state of your Longhorn replicas. + +By default, the command retrieves information about all Longhorn replicas in the system. +You can optionally filter the results using the option flags to narrow down the information returned: +- --name: Specify a specific Longhorn replica name to retrieve details for. +- --volume-name: Filter replicas based on the volume they belong to.`, + + PreRun: func(cmd *cobra.Command, args []string) { + localGetter.LogLevel = globalOpts.LogLevel + + err := localGetter.Init() + if err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to initialize replica getter")) + } + }, + + Run: func(cmd *cobra.Command, args []string) { + err := localGetter.Run() + if err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to run replica getter")) + } + + logrus.Info("Successfully get replica information") + }, + + PostRun: func(cmd *cobra.Command, args []string) { + err := localGetter.Output() + if err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to output replica getter collection")) + } + + logrus.Info("Successfully output replica getter collection") + }, + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.Flags().StringVar(&localGetter.CurrentNodeID, consts.CmdOptNodeId, os.Getenv(consts.EnvCurrentNodeID), "Current node ID.") + cmd.Flags().StringVarP(&localGetter.OutputFilePath, consts.CmdOptOutputFile, "o", os.Getenv(consts.EnvOutputFilePath), "Output the result to a file, default to stdout.") + cmd.Flags().StringVar(&localGetter.ReplicaName, consts.CmdOptName, os.Getenv(consts.EnvLonghornReplicaName), "Specify the name of the replica to retrieve information.") + cmd.Flags().StringVar(&localGetter.VolumeName, consts.CmdOptLonghornVolumeName, os.Getenv(consts.EnvLonghornVolumeName), "Specify the name of the volume to retrieve replica information.") + cmd.Flags().StringVar(&localGetter.LonghornDataDirectory, consts.CmdOptLonghornDataDirectory, os.Getenv(consts.EnvLonghornDataDirectory), "Specify the Longhorn data directory. If not provided, the default will be attempted, or it will fall back to the directory of longhorn-disk.cfg.") + + return cmd +} diff --git a/cmd/local/subcmd/install.go b/cmd/local/subcmd/install.go new file mode 100644 index 00000000..97419505 --- /dev/null +++ b/cmd/local/subcmd/install.go @@ -0,0 +1,67 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/pkg/errors" + + "github.com/longhorn/cli/pkg/consts" + local "github.com/longhorn/cli/pkg/local/preflight" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func NewCmdInstall(globalOpts *types.GlobalCmdOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: consts.SubCmdInstall, + Short: "Longhorn installation operations", + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.AddCommand(newCmdInstallPreflight(globalOpts)) + + return cmd +} + +func newCmdInstallPreflight(globalOpts *types.GlobalCmdOptions) *cobra.Command { + var localInstaller = local.Installer{} + + cmd := &cobra.Command{ + Use: consts.SubCmdPreflight, + Short: "Install and configure prerequisites", + Long: `This command prepares your system for Longhorn deployment by installing the necessary dependencies. +This can help to ensure that your Kubernetes cluster is properly configured and meets the necessary requirements for Longhorn.`, + + PreRun: func(cmd *cobra.Command, args []string) { + localInstaller.LogLevel = globalOpts.LogLevel + + if err := localInstaller.Init(); err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to initialize preflight installer")) + } + }, + + Run: func(cmd *cobra.Command, args []string) { + if err := localInstaller.Run(); err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to run preflight installer")) + } + + logrus.Info("Successfully completed preflight installation") + }, + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.Flags().BoolVar(&localInstaller.UpdatePackages, consts.CmdOptUpdatePackages, utils.ConvertStringToTypeOrDefault(os.Getenv(consts.EnvUpdatePackageList), true), "Update packages before installing required dependencies.") + cmd.Flags().BoolVar(&localInstaller.EnableSpdk, consts.CmdOptEnableSpdk, utils.ConvertStringToTypeOrDefault(os.Getenv(consts.EnvEnableSpdk), false), "Enable installation of SPDK required packages, modules, and setup.") + cmd.Flags().StringVar(&localInstaller.SpdkOptions, consts.CmdOptSpdkOptions, os.Getenv(consts.EnvSpdkOptions), fmt.Sprintf("Specify a comma-separated (%s) list of custom options for configuring SPDK environment.", consts.CmdOptSeperator)) + cmd.Flags().IntVar(&localInstaller.HugePageSize, consts.CmdOptHugePageSize, utils.ConvertStringToTypeOrDefault(os.Getenv(consts.EnvHugePageSize), 2048), "Specify the huge page size in MiB for SPDK.") + cmd.Flags().StringVar(&localInstaller.AllowPci, consts.CmdOptAllowPci, os.Getenv(consts.EnvPciAllowed), fmt.Sprintf("Specify a comma-separated (%s) list of allowed PCI devices. By default, all PCI devices are blocked by a non-valid address.", consts.CmdOptSeperator)) + cmd.Flags().StringVar(&localInstaller.DriverOverride, consts.CmdOptDriverOverride, os.Getenv(consts.EnvDriverOverride), "User space driver for device bindings. Override default driver for PCI devices.") + + return cmd +} diff --git a/cmd/local/subcmd/trim.go b/cmd/local/subcmd/trim.go new file mode 100644 index 00000000..a2a7934c --- /dev/null +++ b/cmd/local/subcmd/trim.go @@ -0,0 +1,71 @@ +package cmd + +import ( + "os" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/longhorn/cli/pkg/consts" + local "github.com/longhorn/cli/pkg/local/volume" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func NewCmdTrim(globalOpts *types.GlobalCmdOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: consts.SubCmdTrim, + Short: "Longhorn trimming operations", + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.AddCommand(newCmdTrimVolume(globalOpts)) + + return cmd +} + +func newCmdTrimVolume(globalOpts *types.GlobalCmdOptions) *cobra.Command { + var localTrimmer = local.Trimmer{} + + cmd := &cobra.Command{ + Use: consts.SubCmdVolume, + Short: "Trim a Longhon volume", + Long: `This command helps to reclaim storage space on a Longhorn volume. It achieves this by removing unused data blocks associated with data that has been deleted from the volume. +This is useful after you've deleted files or applications from the volume but haven't seen a corresponding reduction in storage consumption. + +To use this command, you'll need to specify the following: +- --name: Specify a specific Longhorn volume you want to trim. + +By regularly trimming your Longhorn volumes, you can ensure efficient storage management with your system.`, + + PreRun: func(cmd *cobra.Command, args []string) { + localTrimmer.LogLevel = globalOpts.LogLevel + + utils.CheckErr(localTrimmer.Validate()) + + err := localTrimmer.Init() + if err != nil { + utils.CheckErr(errors.Wrapf(err, "Failed to initialize trimmer for volume %s", localTrimmer.VolumeName)) + } + }, + + Run: func(cmd *cobra.Command, args []string) { + err := localTrimmer.Run() + if err != nil { + utils.CheckErr(errors.Wrapf(err, "Failed to run trimmer for volume %s", localTrimmer.VolumeName)) + } + + logrus.Infof("Successfully trimmed volume %s", localTrimmer.VolumeName) + }, + } + + utils.SetGlobalOptionsLocal(cmd, globalOpts) + + cmd.Flags().StringVar(&localTrimmer.CurrentNodeID, consts.CmdOptNodeId, os.Getenv(consts.EnvCurrentNodeID), "Current node ID.") + cmd.Flags().StringVar(&localTrimmer.LonghornNamespace, consts.CmdOptLonghornNamespace, os.Getenv(consts.EnvLonghornNamespace), "Namespace where Longhorn is deployed within the Kubernetes cluster.") + cmd.Flags().StringVar(&localTrimmer.VolumeName, consts.CmdOptLonghornVolumeName, os.Getenv(consts.EnvLonghornVolumeName), "Name of the Longhorn volum to be trimmed.") + + return cmd +} diff --git a/cmd/remote/longhornctl.go b/cmd/remote/longhornctl.go new file mode 100644 index 00000000..6a0e8438 --- /dev/null +++ b/cmd/remote/longhornctl.go @@ -0,0 +1,75 @@ +package main + +import ( + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "k8s.io/kubectl/pkg/util/templates" + + "github.com/longhorn/cli/cmd/remote/subcmd" + "github.com/longhorn/cli/pkg/consts" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func main() { + if err := newCmdLonghornctl().Execute(); err != nil { + logrus.Fatal(err) + os.Exit(1) + } +} + +func newCmdLonghornctl() *cobra.Command { + globalOpts := &types.GlobalCmdOptions{} + + cmd := &cobra.Command{ + Use: consts.CmdLonghornctlRemote, + Short: "Longhorn commandline interface.", + Long: "CLI for Longhorn troubleshooting and operations.", + PersistentPreRun: func(cmd *cobra.Command, args []string) { + err := utils.SetLog(globalOpts.LogLevel) + if err != nil { + logrus.WithError(err).Warn("Failed to set log level") + } + }, + } + + cmd.CompletionOptions.HiddenDefaultCmd = true + + cmd.PersistentFlags().StringVarP(&globalOpts.LogLevel, consts.CmdOptLogLevel, "l", "info", "log level (trace, debug, info, warn, error, fatal, panic)") + cmd.PersistentFlags().StringVar(&globalOpts.KubeConfigPath, consts.CmdOptKubeConfigPath, os.Getenv(consts.EnvKubeConfigPath), "Kubernetes config (kubeconfig) path") + cmd.PersistentFlags().StringVar(&globalOpts.Image, consts.CmdOptImage, consts.ImageLonghornctl, "Image containing longhornctl-local") + + groups := templates.CommandGroups{ + { + Message: "Install And Uninstall Commands:", + Commands: []*cobra.Command{ + subcmd.NewCmdInstall(globalOpts), + }, + }, + { + Message: "Operation Commands:", + Commands: []*cobra.Command{ + subcmd.NewCmdTrim(globalOpts), + subcmd.NewCmdExport(globalOpts), + }, + }, + { + Message: "Troubleshoot Commands:", + Commands: []*cobra.Command{ + subcmd.NewCmdCheck(globalOpts), + subcmd.NewCmdGet(globalOpts), + }, + }, + } + groups.Add(cmd) + + cmd.AddCommand(subcmd.NewCmdGlobalOptions()) + + filters := []string{"options"} + templates.ActsAsRootCommand(cmd, filters, groups...) + + return cmd +} diff --git a/cmd/remote/subcmd/check.go b/cmd/remote/subcmd/check.go new file mode 100644 index 00000000..8a993a57 --- /dev/null +++ b/cmd/remote/subcmd/check.go @@ -0,0 +1,78 @@ +package subcmd + +import ( + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/longhorn/cli/pkg/consts" + "github.com/longhorn/cli/pkg/remote/preflight" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func NewCmdCheck(globalOpts *types.GlobalCmdOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: consts.SubCmdCheck, + Short: "Longhorn checking operations", + } + + utils.SetGlobalOptionsRemote(cmd, globalOpts) + + cmd.AddCommand(newCmdCheckPreflight(globalOpts)) + + return cmd +} + +func newCmdCheckPreflight(globalOpts *types.GlobalCmdOptions) *cobra.Command { + var preflightChecker = preflight.Checker{} + + cmd := &cobra.Command{ + Use: consts.SubCmdPreflight, + Short: "Check Longhorn preflight", + Long: `This command verifies your Kubernetes cluster environment. It performs a series of checks to ensure your cluster meets the requirements for Longhorn to function properly. +These checks can help to identify issues that might prevent Longhorn from functioning properly.`, + + PreRun: func(cmd *cobra.Command, args []string) { + preflightChecker.Image = globalOpts.Image + preflightChecker.KubeConfigPath = globalOpts.KubeConfigPath + + logrus.Info("Initializing preflight checker") + if err := preflightChecker.Init(); err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to initialize preflight checker")) + } + + logrus.Info("Cleaning up preflight checker") + if err := preflightChecker.Cleanup(); err != nil { + utils.CheckErr(errors.Wrapf(err, "Failed to cleanup preflight checker")) + } + }, + + Run: func(cmd *cobra.Command, args []string) { + logrus.Info("Running preflight checker") + output, err := preflightChecker.Run() + if err != nil { + utils.CheckErr(errors.Wrap(err, "Failed to run preflight checker")) + } + + logrus.Infof("Retrieved preflight checker result:\n%v", output) + }, + + PostRun: func(cmd *cobra.Command, args []string) { + logrus.Info("Cleaning up preflight checker") + if err := preflightChecker.Cleanup(); err != nil { + utils.CheckErr(errors.Wrapf(err, "Failed to cleanup preflight checker")) + } + + logrus.Info("Completed preflight checker") + }, + } + + utils.SetGlobalOptionsRemote(cmd, globalOpts) + + cmd.Flags().BoolVar(&preflightChecker.EnableSpdk, consts.CmdOptEnableSpdk, false, "Enable checking of SPDK required packages, modules, and setup.") + cmd.Flags().IntVar(&preflightChecker.HugePageSize, consts.CmdOptHugePageSize, 1024, "Specify the huge page size in MiB for SPDK.") + cmd.Flags().StringVar(&preflightChecker.UioDriver, consts.CmdOptUioDriver, "uio_pci_generic", "User space I/O driver for SPDK.") + + return cmd +} diff --git a/cmd/remote/subcmd/export.go b/cmd/remote/subcmd/export.go new file mode 100644 index 00000000..2a0f1eda --- /dev/null +++ b/cmd/remote/subcmd/export.go @@ -0,0 +1,131 @@ +package subcmd + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/longhorn/cli/pkg/consts" + "github.com/longhorn/cli/pkg/remote/replica" + "github.com/longhorn/cli/pkg/types" + "github.com/longhorn/cli/pkg/utils" +) + +func NewCmdExport(globalOpts *types.GlobalCmdOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: consts.SubCmdExport, + Short: "Export Longhorn resources", + } + + utils.SetGlobalOptionsRemote(cmd, globalOpts) + + cmd.AddCommand(newCmdExportReplica(globalOpts)) + + return cmd +} + +func newCmdExportReplica(globalOpts *types.GlobalCmdOptions) *cobra.Command { + var replicaExporter = replica.Exporter{} + + cmd := &cobra.Command{ + Use: consts.SubCmdReplica, + Short: "Export Longhorn replica", + Long: `This command exports the data from a specified Longhorn replica data directory to a directory on its host machine. +It provides data recovery capabilities when the Longhorn system is unavailable. + +To perform an export, provide the name of the replica data directory to the --name option. + +To find available replica data directory names, use the following command: +> longhornctl get replica + +After the export is completed, you can access the exported data at the specified location on the node provided in the output. + +To terminate the replica exporter and stop the replica export process, use the 'stop' subcommand with the original command. For example: +> longhornctl export replica stop`, + + PreRun: func(cmd *cobra.Command, args []string) { + replicaExporter.Image = globalOpts.Image + replicaExporter.KubeConfigPath = globalOpts.KubeConfigPath + + utils.CheckErr(replicaExporter.Validate()) + + logrus.Info("Initializing replica exporter") + if err := replicaExporter.Init(); err != nil { + utils.CheckErr(errors.Wrapf(err, "Failed to initialize replica exporter")) + } + }, + + Run: func(cmd *cobra.Command, args []string) { + logrus.Info("Running replica exporter") + result, err := replicaExporter.Run() + if err != nil { + utils.CheckErr(errors.Wrapf(err, "Failed to run replica exporter")) + } + + logrus.Infof("Exported replica:\n %v", result) + }, + + PostRun: func(cmd *cobra.Command, args []string) { + logrus.Infof("Completed replica exporter. Use '%s %s %s %s' to stop exporting replica.", consts.CmdLonghornctlRemote, consts.SubCmdExport, consts.SubCmdReplica, consts.SubCmdStop) + }, + } + + cmd.AddCommand(newCmdExportReplicaStop(globalOpts)) + + utils.SetGlobalOptionsRemote(cmd, globalOpts) + + // Use SetFlagHidden to include these option flags in the child subcommand. + // This allows the user to use `export replica --