package cmd import ( "os" "fmt" "strconv" "strings" "encoding/json" "github.com/spf13/cobra" "github.com/spf13/viper" ) const ( gVersion = "0.022b" ) var ( gJails []Jail gUseSudo bool gConfigFile string gDisplayColumns string gFilterJails string gSortFields string gNoLineSep bool gHostVersion float64 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. It support iocage jails and can coexist with iocage.`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("Here we are in the Run") cleanAfterRun() }, } versionCmd = &cobra.Command{ 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) { fmt.Printf("GoCage v.%s on FreeBSD %.1f\n", gVersion, gHostVersion) cleanAfterRun() }, } listCmd = &cobra.Command{ Use: "list", Short: "Print jails", 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) cleanAfterRun() }, } 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) cleanAfterRun() }, } stopCmd = &cobra.Command{ Use: "stop", Short: "stop jail", Long: "shutdown jail", Run: func(cmd *cobra.Command, args []string) { // Get the inventory ListJails(args, false) StopJail(args) cleanAfterRun() }, } startCmd = &cobra.Command{ Use: "start", Short: "start jail", Run: func(cmd *cobra.Command, args []string) { // Get the inventory ListJails(args, false) StartJail(args) cleanAfterRun() }, } ) func init() { cobra.OnInitialize(initConfig) // Global switches 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") // Command dependant switches listCmd.PersistentFlags().StringVarP(&gDisplayColumns, "outcol", "o", "JID,Name,Config.Release,Config.Ip4_addr,Running", "Show these columns in output") listCmd.PersistentFlags().BoolVarP(&gNoLineSep, "nolinesep", "l", false, "Do not display line separator between jails") listCmd.PersistentFlags().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.PersistentFlags().StringVarP(&gSortFields, "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.") // Now declare commands rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(listCmd) listCmd.AddCommand(listPropsCmd) rootCmd.AddCommand(stopCmd) rootCmd.AddCommand(startCmd) // Get FreeBSD version out, err := executeCommand("freebsd-version") if err != nil { fmt.Printf("Error running \"freebsd-version\": %s", err.Error()) os.Exit(1) } gHostVersion, _ = strconv.ParseFloat(strings.Split(out, "-")[0], 32) } func initConfig() { if gConfigFile == "" { fmt.Println("No config file!") os.Exit(1) } // fmt.Printf("We are in initConfig(), with config file %s\n", gConfigFile) viper.SetConfigFile(gConfigFile) if err := viper.ReadInConfig(); err != nil { fmt.Printf("ERROR reading config file %s : %s\n", gConfigFile, err.Error()) os.Exit(1) } // 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")) // Command line flags have priority on config file if rootCmd.Flags().Lookup("sudo") != nil && false == rootCmd.Flags().Lookup("sudo").Changed { gUseSudo = viper.GetBool("sudo") } if listCmd.Flags().Lookup("outcol") != nil && false == listCmd.Flags().Lookup("outcol").Changed { gDisplayColumns = viper.GetString("outcol") } if listCmd.Flags().Lookup("nolinesep") != nil && false == listCmd.Flags().Lookup("nolinesep").Changed { gNoLineSep = viper.GetBool("nolinesep") } 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 { gSortFields = viper.GetString("sort") } if len(strings.Split(gSortFields, ",")) > 3 { fmt.Printf("More than 3 sort criteria, this is not supported!\n") os.Exit(1) } } // Called after execution func cleanAfterRun() { for _, j := range gJails { if j.ConfigUpdated { // TODO : Marshall to disk fmt.Printf("Config for jail %s will be updated\n", j.Name) marshaled, err := json.MarshalIndent(j.Config, "", " ") if err != nil { fmt.Printf("ERROR marshaling config: %s\n", err.Error()) } fmt.Printf(string(marshaled)) } } } func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }