gocage/cmd/root.go

525 lines
17 KiB
Go
Raw Normal View History

2021-12-18 13:13:25 +01:00
package cmd
import (
2022-04-24 16:49:54 +02:00
"os"
"fmt"
"sync"
2021-12-20 22:10:38 +01:00
"strings"
"io/ioutil"
2021-12-18 13:13:25 +01:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
2022-04-24 16:49:54 +02:00
// TODO : Use log
log "github.com/sirupsen/logrus"
2021-12-18 13:13:25 +01:00
)
const (
2023-08-05 19:49:59 +02:00
gVersion = "0.36d"
2022-06-18 11:08:03 +02:00
// TODO : Get from $jail_zpool/defaults.json
MIN_DYN_DEVFS_RULESET = 1000
2021-12-18 13:13:25 +01:00
)
2023-08-05 19:49:59 +02:00
type createArgs struct {
Release string
BaseTemplate string
Datastore string
JailType string
}
2021-12-18 13:13:25 +01:00
var (
2022-06-19 13:55:07 +02:00
gJailHost JailHost
gJails []Jail
gDatastores []Datastore
2022-04-24 16:49:54 +02:00
gUseSudo bool
2022-09-25 13:45:40 +02:00
gForce bool
gDebug bool
2023-08-05 19:49:59 +02:00
gCreateArgs createArgs
gConfigFile string
gDisplayJColumns string
gDisplaySColumns string
gDisplayDColumns string
gFilterJails string
gFilterSnaps string
gFilterDS string
gSortJailFields string
gSortSnapFields string
gSortDSFields string
gNoJailLineSep bool
gNoSnapLineSep bool
gNoDSLineSep bool
2021-12-19 13:05:30 +01:00
2022-04-24 16:49:54 +02:00
gHostVersion float64
2022-04-24 16:49:54 +02:00
gTimeZone string
gSnapshotName string
2021-12-18 13:13:25 +01:00
gMigrateDestDatastore string
gYesToAll bool
2022-10-16 15:19:51 +02:00
gFetchRelease string
gFetchIntoDS string
2022-11-06 16:34:52 +01:00
gFetchFrom string
2023-07-23 15:13:16 +02:00
gUpgradeRelease string
2022-10-16 15:19:51 +02:00
gMdevfs sync.Mutex
2022-10-16 15:19:51 +02:00
2022-04-24 16:49:54 +02:00
rootCmd = &cobra.Command{
Use: "gocage",
Short: "GoCage is a FreeBSD Jail management tool",
Long: `GoCage is a jail management tool. It support VNET, host-only, NAT networks. Provides snapshots and cloning.
2021-12-18 13:13:25 +01:00
It support iocage jails and can coexist with iocage.`,
2022-04-02 17:11:54 +02:00
Run: func(cmd *cobra.Command, args []string) {
2022-07-10 14:16:39 +02:00
fv, _ := getFreeBSDVersion()
fmt.Printf("GoCage v.%s on FreeBSD %d.%d-%s\n", gVersion, fv.major, fv.minor, fv.flavor)
fmt.Printf("Use -h flag to display help\n")
2022-04-02 17:11:54 +02:00
},
2021-12-18 13:13:25 +01:00
}
2022-04-24 16:49:54 +02:00
versionCmd = &cobra.Command{
2022-04-02 17:11:54 +02:00
Use: "version",
Short: "Print the version number of GoCage",
Long: `Let this show you how much fail I had to get this *cough* perfect`,
Run: func(cmd *cobra.Command, args []string) {
2022-07-10 14:16:39 +02:00
fv, _ := getFreeBSDVersion()
fmt.Printf("GoCage v.%s on FreeBSD %d.%d-%s\n", gVersion, fv.major, fv.minor, fv.flavor)
2022-04-24 16:49:54 +02:00
},
2021-12-18 13:13:25 +01:00
}
2023-08-05 19:49:59 +02:00
/* TODO
Initialize datastore(s) /iocage, /iocage/jails
Put defaults.json, update it with hostid,interfaces, and maybe other necessary fields
Initialize bridge
initCmd = &cobra.Command{
Use: "init",
Short: "Initialize GoCage",
//Long: `Let this show you how much fail I had to get this *cough* perfect`,
Run: func(cmd *cobra.Command, args []string) {
fv, _ := getFreeBSDVersion()
fmt.Printf("GoCage v.%s on FreeBSD %d.%d-%s\n", gVersion, fv.major, fv.minor, fv.flavor)
},
}*/
2021-12-18 13:13:25 +01:00
2022-04-24 16:49:54 +02:00
listCmd = &cobra.Command{
2021-12-18 13:13:25 +01:00
Use: "list",
Short: "Print jails",
2022-04-24 16:49:54 +02:00
Long: `Display jails, their IP and OS.
Jail list can be restricted by adding name on command line
ex: gocage list srv-db srv-web`,
Run: func(cmd *cobra.Command, args []string) {
ListJails(args, true)
},
}
2022-04-24 16:49:54 +02:00
listPropsCmd = &cobra.Command{
Use: "properties",
Short: "Print jails properties",
Long: "Display jails properties. You can use properties to filter, get or set them.",
Run: func(cmd *cobra.Command, args []string) {
ListJailsProps(args)
},
}
2023-07-09 13:42:24 +02:00
destroyCmd = &cobra.Command{
2022-09-25 13:45:40 +02:00
Use: "destroy",
Short: "destroy jails",
Long: `Destroy jail filesystem, snapshots and configuration file.`,
Run: func(cmd *cobra.Command, args []string) {
ListJails(args, false)
DestroyJails(args)
},
}
2023-07-09 13:42:24 +02:00
2022-04-24 16:49:54 +02:00
stopCmd = &cobra.Command{
Use: "stop",
Short: "stop jail",
Long: "shutdown jail",
2021-12-18 13:13:25 +01:00
Run: func(cmd *cobra.Command, args []string) {
2022-04-03 14:27:26 +02:00
// Load inventory
ListJails(args, false)
if len(args) == 0 {
StopAllRunningJails()
} else {
StopJail(args)
}
2021-12-18 13:13:25 +01:00
},
}
2021-12-21 20:48:15 +01:00
2022-04-24 16:49:54 +02:00
startCmd = &cobra.Command{
2021-12-21 20:48:15 +01:00
Use: "start",
Short: "start jail",
Run: func(cmd *cobra.Command, args []string) {
2022-04-03 14:27:26 +02:00
// Load inventory
2021-12-21 20:48:15 +01:00
ListJails(args, false)
if len(args) == 0 {
StartJailsAtBoot()
} else {
StartJail(args)
}
2021-12-21 20:48:15 +01:00
},
}
2023-07-23 15:13:16 +02:00
2022-07-14 10:57:57 +02:00
restartCmd = &cobra.Command{
Use: "restart",
Short: "restart jail",
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
StopJail(args)
StartJail(args)
},
}
2023-07-23 15:13:16 +02:00
2022-06-18 11:08:03 +02:00
shellCmd = &cobra.Command {
Use: "console",
Short: "Execute shell on jail",
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
ShellJail(args)
},
}
2023-07-23 15:13:16 +02:00
2022-04-24 16:49:54 +02:00
setCmd = &cobra.Command{
2022-04-02 17:11:54 +02:00
Use: "set",
Short: "Set a jail property",
2022-04-24 16:49:54 +02:00
Long: `Set jail property value. Specify property=value, end command with jail name.
2022-04-02 17:11:54 +02:00
Multiples properties can be specified, separated with space (Ex: gocage set allow_mlock=1 boot=1 myjail)`,
Run: func(cmd *cobra.Command, args []string) {
2022-04-03 14:27:26 +02:00
// Load inventory
2022-04-02 17:11:54 +02:00
ListJails(args, false)
SetJailProperties(args)
},
}
2022-04-24 16:49:54 +02:00
getCmd = &cobra.Command{
Use: "get",
2022-04-03 14:27:26 +02:00
Short: "Get a jail property",
2022-04-24 16:49:54 +02:00
Long: `Get jail property value. Specify property, end command with jail name.
2022-04-03 11:04:01 +02:00
Multiples properties can be specified, separated with space (Ex: gocage get allow_mlock boot myjail)
For all properties specify "all" (Ex: gocage get all myjail)`,
2022-04-03 14:27:26 +02:00
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
GetJailProperties(args)
},
}
2022-04-24 16:49:54 +02:00
snapshotCmd = &cobra.Command{
2022-04-03 14:27:26 +02:00
Use: "snapshot",
Short: "snapshot jail",
Long: "Commands to manage jail snapshots. If no arguments given, ",
Run: func(cmd *cobra.Command, args []string) {
},
}
2022-04-24 16:49:54 +02:00
snapshotListCmd = &cobra.Command{
2022-04-03 14:27:26 +02:00
Use: "list",
Short: "list snapshots",
2022-04-24 16:49:54 +02:00
Long: `List snapshots of a jail by specifying its name.
2022-04-03 14:27:26 +02:00
List all snapshots if no jail name specified.
You can specify multiple jails.`,
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
ListJailsSnapshots(args)
},
}
2022-04-24 16:49:54 +02:00
snapshotCreateCmd = &cobra.Command{
Use: "create",
Short: "create snapshots",
Long: `Create snapshot of a jail by specifying snapshot name and jail name.`,
2022-04-24 16:49:54 +02:00
// You can specify multiple jails.`,
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
CreateJailSnapshot(args)
},
}
2022-04-24 16:49:54 +02:00
snapshotRollbackCmd = &cobra.Command{
Use: "rollback",
Short: "Rollback snapshots",
Long: `Rollback jail to specifyed snapshot.`,
2022-04-24 16:49:54 +02:00
// You can specify multiple jails.`,
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
RollbackJailSnapshot(args)
},
}
2022-04-24 16:49:54 +02:00
snapshotDeleteCmd = &cobra.Command{
Use: "destroy",
Short: "destroy snapshots",
Long: `Destroy snapshot of a jail by specifying snapshot name and jail name.
You can specify multiple snapshots separated by comma.`,
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
DeleteJailSnapshot(args)
2022-04-03 14:27:26 +02:00
},
}
migrateCmd = &cobra.Command{
Use: "migrate",
Short: "Migrate jail to another datastore",
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
MigrateJail(args)
},
}
migrateCleanCmd = &cobra.Command{
Use: "clean",
Short: "Clean previous aborted/in error jail migration",
Run: func(cmd *cobra.Command, args []string) {
// Load inventory
ListJails(args, false)
err := CleanMigrateMess(args)
if err != nil {
fmt.Printf("%v", err)
}
},
}
datastoreCmd = &cobra.Command{
Use: "datastore",
Short: "list datastores",
Long: "Commands to manage datastores. If no arguments given, list them.",
Run: func(cmd *cobra.Command, args []string) {
ListDatastores(args, true)
},
}
datastoreListCmd = &cobra.Command{
Use: "list",
Short: "list datastores",
Long: `List datastore by specifying its name.
List all datastores if no name specified.
You can specify multiple datastores.`,
Run: func(cmd *cobra.Command, args []string) {
ListDatastores(args, true)
},
}
2023-07-23 15:13:16 +02:00
2022-10-16 15:19:51 +02:00
fetchCmd = &cobra.Command{
Use: "fetch",
Short: "Fetch FreeBSD release to local datastore",
Run: func(cmd *cobra.Command, args []string) {
2022-11-06 16:34:52 +01:00
err := fetchRelease(gFetchRelease, "http", gJailHost.arch, gFetchIntoDS, gFetchFrom)
if err != nil {
fmt.Printf("%v\n", err)
} else {
extractRelease(gFetchRelease, gFetchIntoDS)
}
2023-07-23 15:13:16 +02:00
},
2022-10-16 15:19:51 +02:00
}
2023-07-23 15:13:16 +02:00
updateCmd = &cobra.Command{
2022-11-20 20:20:06 +01:00
Use: "update",
2023-07-23 15:13:16 +02:00
Short: "Update FreeBSD release",
Run: func(cmd *cobra.Command, args []string) {
ListJails(args, false)
UpdateJail(args)
},
2022-11-20 20:20:06 +01:00
}
2023-07-23 15:13:16 +02:00
upgradeCmd = &cobra.Command{
Use: "upgrade",
Short: "Upgrade FreeBSD release",
Run: func(cmd *cobra.Command, args []string) {
ListJails(args, false)
UpgradeJail(args)
},
}
2023-08-05 19:49:59 +02:00
createCmd = &cobra.Command{
Use: "create",
Short: "Create jail",
Run: func(cmd *cobra.Command, args []string) {
ListJails(args, false)
CreateJail(args)
},
}
2022-06-18 20:09:32 +02:00
testCmd = &cobra.Command{
Use: "test",
Short: "temporary command to test some code snippet",
Run: func(cmd *cobra.Command, args []string) {
2022-06-26 20:02:29 +02:00
fmt.Printf("Nope\n")
2022-06-18 20:09:32 +02:00
},
}
2021-12-18 13:13:25 +01:00
)
2022-04-24 16:49:54 +02:00
// TODO : Init log level and log output
2021-12-18 13:13:25 +01:00
func init() {
2022-06-19 13:55:07 +02:00
var err error
2023-08-05 19:49:59 +02:00
2022-04-24 16:49:54 +02:00
cobra.OnInitialize(initConfig)
2021-12-18 13:13:25 +01:00
2022-04-24 16:49:54 +02:00
// Global switches
2022-04-02 17:11:54 +02:00
rootCmd.PersistentFlags().StringVarP(&gConfigFile, "config", "c", "/usr/local/etc/gocage.conf.yml", "GoCage configuration file")
rootCmd.PersistentFlags().BoolVarP(&gUseSudo, "sudo", "u", false, "Use sudo to run commands")
2022-04-03 14:27:26 +02:00
rootCmd.PersistentFlags().StringVarP(&gTimeZone, "timezone", "t", "", "Specify timezone. Will get from /var/db/zoneinfo if not set.")
2022-10-16 15:19:51 +02:00
rootCmd.PersistentFlags().BoolVar(&gDebug, "debug", false, "Debug mode")
2021-12-18 13:13:25 +01:00
2022-04-24 16:49:54 +02:00
// Command dependant switches
// We reuse these flags in "gocage snapshot list myjail" and 'gocage datastore list" commands
listCmd.Flags().StringVarP(&gDisplayJColumns, "outcol", "o", "JID,Name,Config.Release,Config.Ip4_addr,Running", "Show these columns in output")
listCmd.Flags().BoolVarP(&gNoJailLineSep, "nolinesep", "l", false, "Do not display line separator between jails")
listCmd.Flags().StringVarP(&gFilterJails, "filter", "f", "none", "Only display jails with these values. Ex: \"gocage list -f Config.Boot=1\" will only list started on boot jails")
listCmd.Flags().StringVarP(&gSortJailFields, "sort", "s", "none", "Display jails sorted by field values. Ex: \"gocage list -s +Name,-Config.Priority\" will sort jails by their decreasing name, then increasing start priority. 3 critera max supported.")
2022-09-25 13:45:40 +02:00
2023-07-09 13:42:24 +02:00
destroyCmd.Flags().BoolVarP(&gForce, "force", "f", false, "Force stop jail if running")
2022-04-24 16:49:54 +02:00
snapshotListCmd.Flags().StringVarP(&gDisplaySColumns, "outcol", "o", "Jailname,Name,Creation,Referenced,Used", "Show these columns in output")
snapshotListCmd.Flags().BoolVarP(&gNoSnapLineSep, "nolinesep", "l", false, "Do not display line separator between snapshots")
snapshotListCmd.Flags().StringVarP(&gFilterSnaps, "filter", "f", "none", "Only display snapshots with these values. Ex: \"gocage snapshot list -f Config.Boot=1\" will only list started on boot jails")
snapshotListCmd.Flags().StringVarP(&gSortSnapFields, "sort", "s", "none", "Display snapshots sorted by field values. Ex: \"gocage snapshot list -s +Jailname,-Used\" will sort snapshots by jail decreasing name, then increasing used space. 3 critera max supported.")
2023-08-05 19:49:59 +02:00
datastoreListCmd.Flags().StringVarP(&gDisplayDColumns, "outcol", "o", "Name,Mountpoint,ZFSDataset,Available,Used,Referenced", "Show these columns in output")
datastoreListCmd.Flags().BoolVarP(&gNoDSLineSep, "nolinesep", "l", false, "Do not display line separator between datastores")
datastoreListCmd.Flags().StringVarP(&gFilterDS, "filter", "f", "none", "Only display datastores with these values. Ex: \"gocage datastore list -f Config.Boot=1\" will only list started on boot jails")
datastoreListCmd.Flags().StringVarP(&gSortDSFields, "sort", "s", "none", "Display datastores sorted by field values. Ex: \"gocage datastore list -s +Jailname,-Used\" will sort snapshots by jail decreasing name, then increasing used space. 3 critera max supported.")
2023-08-05 19:49:59 +02:00
// This is local flag : Only available to gocage snapshot create command
snapshotCreateCmd.Flags().StringVarP(&gSnapshotName, "snapname", "n", "", "Name of the snapshot to create")
snapshotCreateCmd.MarkFlagRequired("snapname")
snapshotDeleteCmd.Flags().StringVarP(&gSnapshotName, "snapname", "n", "", "Name of the snapshot to destroy")
snapshotDeleteCmd.MarkFlagRequired("snapname")
snapshotRollbackCmd.Flags().StringVarP(&gSnapshotName, "snapname", "n", "", "Name of the snapshot to rollback to")
snapshotRollbackCmd.MarkFlagRequired("snapname")
2022-04-24 16:49:54 +02:00
migrateCmd.Flags().StringVarP(&gMigrateDestDatastore, "datastore", "d", "", "Path of destination datastore for jail (Ex: \"/iocage\")")
2022-06-18 11:08:03 +02:00
migrateCmd.Flags().BoolVarP(&gYesToAll, "yes", "y", false, "Answer yes to all questions")
migrateCmd.MarkFlagRequired("datastore")
2023-08-05 19:49:59 +02:00
fetchCmd.Flags().StringVarP(&gFetchRelease, "release", "r", "", "Release to fetch (e.g.: \"13.1-RELEASE\"")
2022-10-16 15:19:51 +02:00
fetchCmd.Flags().StringVarP(&gFetchIntoDS, "datastore", "o", "", "Datastore release will be saved to")
2022-11-06 16:34:52 +01:00
fetchCmd.Flags().StringVarP(&gFetchFrom, "from", "d", "", "Repository to download from. Should contain XY.Z-RELEASE. File protocol supported")
2022-10-16 15:19:51 +02:00
fetchCmd.MarkFlagRequired("release")
fetchCmd.MarkFlagRequired("datastore")
2023-08-05 19:49:59 +02:00
2023-07-23 15:13:16 +02:00
upgradeCmd.Flags().StringVarP(&gUpgradeRelease, "release", "r", "", "Release to upgrade to (e.g.: \"13.1-RELEASE\"")
upgradeCmd.MarkFlagRequired("release")
2023-08-05 19:49:59 +02:00
createCmd.Flags().StringVarP(&gCreateArgs.Release, "release", "r", "", "Release for the jail (e.g.: \"13.1-RELEASE\"")
createCmd.Flags().StringVarP(&gCreateArgs.BaseTemplate, "basetpl", "b", "", "Base template. This will create a jail based on basetpl, so every up(date|grade) made to basetpl will immediately propagate to new jail\n")
createCmd.Flags().StringVarP(&gCreateArgs.Datastore, "datastore", "d", "", "Datastore to create the jail on. Defaults to first declared in config.")
2022-04-24 16:49:54 +02:00
// Now declare commands
2022-04-02 17:11:54 +02:00
rootCmd.AddCommand(versionCmd)
rootCmd.AddCommand(listCmd)
listCmd.AddCommand(listPropsCmd)
2022-04-02 17:11:54 +02:00
rootCmd.AddCommand(stopCmd)
rootCmd.AddCommand(startCmd)
2022-07-14 10:57:57 +02:00
rootCmd.AddCommand(restartCmd)
2023-07-09 13:42:24 +02:00
rootCmd.AddCommand(destroyCmd)
2022-06-18 11:08:03 +02:00
rootCmd.AddCommand(shellCmd)
rootCmd.AddCommand(getCmd)
2022-04-02 17:11:54 +02:00
rootCmd.AddCommand(setCmd)
2022-04-03 14:27:26 +02:00
rootCmd.AddCommand(snapshotCmd)
rootCmd.AddCommand(migrateCmd)
rootCmd.AddCommand(datastoreCmd)
2022-10-16 15:19:51 +02:00
rootCmd.AddCommand(fetchCmd)
2023-07-23 15:13:16 +02:00
rootCmd.AddCommand(updateCmd)
rootCmd.AddCommand(upgradeCmd)
2023-08-05 19:49:59 +02:00
rootCmd.AddCommand(createCmd)
2022-06-18 20:09:32 +02:00
rootCmd.AddCommand(testCmd)
2022-04-03 14:27:26 +02:00
snapshotCmd.AddCommand(snapshotListCmd)
snapshotCmd.AddCommand(snapshotCreateCmd)
snapshotCmd.AddCommand(snapshotDeleteCmd)
snapshotCmd.AddCommand(snapshotRollbackCmd)
migrateCmd.AddCommand(migrateCleanCmd)
datastoreCmd.AddCommand(datastoreListCmd)
2022-04-24 16:49:54 +02:00
2022-06-19 13:55:07 +02:00
// Get FreeBSD version, hostname, hostid
gJailHost, err = NewJailHost()
if err != nil {
2022-06-19 13:55:07 +02:00
fmt.Printf("Error initializing JailHost properties: %v\n", err)
os.Exit(1)
}
2021-12-18 13:13:25 +01:00
}
func initConfig() {
if gConfigFile == "" {
fmt.Println("No config file!")
os.Exit(1)
2021-12-18 13:13:25 +01:00
}
viper.SetConfigFile(gConfigFile)
if err := viper.ReadInConfig(); err != nil {
fmt.Printf("ERROR reading config file %s : %s\n", gConfigFile, err.Error())
os.Exit(1)
2021-12-18 13:13:25 +01:00
}
// Load default configs from datastores
err := ListDatastores(viper.GetStringSlice("datastore"), false)
if err != nil {
fmt.Printf("ERROR: error checking datastores: %v\n", err)
os.Exit(1)
}
2021-12-18 13:13:25 +01:00
2022-04-24 16:49:54 +02:00
// fmt.Println("Using config file:", viper.ConfigFileUsed())
// fmt.Printf("datastore in config : %s\n", viper.GetStringSlice("datastore"))
// fmt.Printf("datastore.0 in config : %s\n", viper.GetStringSlice("datastore.0"))
2021-12-19 13:05:30 +01:00
// Command line flags have priority on config file
if rootCmd.Flags().Lookup("sudo") != nil && false == rootCmd.Flags().Lookup("sudo").Changed {
2021-12-19 13:05:30 +01:00
gUseSudo = viper.GetBool("sudo")
}
2022-04-03 14:27:26 +02:00
if rootCmd.Flags().Lookup("timezone") != nil && false == rootCmd.Flags().Lookup("timezone").Changed {
gTimeZone = viper.GetString("timezone")
}
// If neither on cmdline nor config file, get from /var/db/zoneinfo
if len(gTimeZone) == 0 {
tz, err := ioutil.ReadFile("/var/db/zoneinfo")
if err != nil {
fmt.Println("Error reading /var/db/zoneinfo: %s\n", err.Error())
os.Exit(1)
}
gTimeZone = strings.Trim(string(tz), "\n")
}
if listCmd.Flags().Lookup("outcol") != nil && false == listCmd.Flags().Lookup("outcol").Changed {
gDisplayJColumns = viper.GetString("outcol")
2021-12-19 13:05:30 +01:00
}
if listCmd.Flags().Lookup("nolinesep") != nil && false == listCmd.Flags().Lookup("nolinesep").Changed {
gNoJailLineSep = viper.GetBool("nolinesep")
}
2021-12-19 16:49:07 +01:00
if listCmd.Flags().Lookup("filter") != nil && false == listCmd.Flags().Lookup("filter").Changed {
gFilterJails = viper.GetString("filter")
}
if listCmd.Flags().Lookup("sort") != nil && false == listCmd.Flags().Lookup("sort").Changed {
gSortJailFields = viper.GetString("sort")
2021-12-19 16:49:07 +01:00
}
if len(strings.Split(gSortJailFields, ",")) > 3 {
fmt.Printf("More than 3 sort criteria is not supported!\n")
2021-12-20 22:10:38 +01:00
os.Exit(1)
}
if gDebug {
log.SetLevel(log.DebugLevel)
log.Debugf("Debug mode enabled\n")
}
2021-12-18 13:13:25 +01:00
}
2022-11-20 20:20:06 +01:00
2021-12-18 13:13:25 +01:00
func Execute() {
2022-04-02 17:11:54 +02:00
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
2021-12-18 13:13:25 +01:00
}