2021-12-18 13:13:25 +01:00
package cmd
import (
"os"
"fmt"
2022-04-02 14:18:50 +02:00
"strconv"
2021-12-20 22:10:38 +01:00
"strings"
2022-04-03 14:27:26 +02:00
"io/ioutil"
2022-04-02 14:18:50 +02:00
"encoding/json"
2021-12-18 13:13:25 +01:00
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
2022-04-04 21:00:44 +02:00
gVersion = "0.25"
2021-12-18 13:13:25 +01:00
)
var (
2021-12-19 13:05:30 +01:00
gJails [ ] Jail
2021-12-18 21:34:11 +01:00
2021-12-19 13:05:30 +01:00
gUseSudo bool
gConfigFile string
gDisplayColumns string
2021-12-19 16:49:07 +01:00
gFilterJails string
gSortFields string
2021-12-19 14:31:51 +01:00
gNoLineSep bool
2021-12-18 21:34:11 +01:00
2022-04-02 14:18:50 +02:00
gHostVersion float64
2022-04-03 14:27:26 +02:00
gTimeZone string
2022-04-04 21:00:44 +02:00
gSnapshotName string
2021-12-18 13:13:25 +01:00
2022-04-03 14:27:26 +02:00
rootCmd = & cobra . Command {
2022-04-02 17:11:54 +02:00
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 ) {
fmt . Println ( "Here we are in the Run" )
2022-04-02 14:18:50 +02:00
cleanAfterRun ( )
2022-04-02 17:11:54 +02:00
} ,
2021-12-18 13:13:25 +01:00
}
2022-04-02 17:11:54 +02:00
2022-04-03 14:27:26 +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 ) {
fmt . Printf ( "GoCage v.%s on FreeBSD %.1f\n" , gVersion , gHostVersion )
2022-04-02 14:18:50 +02:00
cleanAfterRun ( )
2021-12-18 13:13:25 +01:00
} ,
}
2022-04-03 14:27:26 +02:00
listCmd = & cobra . Command {
2021-12-18 13:13:25 +01:00
Use : "list" ,
Short : "Print jails" ,
2021-12-18 21:34:11 +01: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 ) {
2021-12-19 12:41:54 +01:00
ListJails ( args , true )
2022-04-02 14:18:50 +02:00
cleanAfterRun ( )
2021-12-18 21:34:11 +01:00
} ,
}
2022-04-03 14:27:26 +02:00
listPropsCmd = & cobra . Command {
2022-04-02 15:40:04 +02:00
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 ( )
} ,
}
2022-04-03 14:27:26 +02:00
stopCmd = & cobra . Command {
2021-12-18 21:34:11 +01:00
Use : "stop" ,
Short : "stop jail" ,
2022-04-02 15:40:04 +02:00
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
2021-12-19 12:41:54 +01:00
ListJails ( args , false )
StopJail ( args )
2022-04-02 14:18:50 +02:00
cleanAfterRun ( )
2021-12-18 13:13:25 +01:00
} ,
}
2021-12-21 20:48:15 +01:00
2022-04-03 14:27:26 +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 )
StartJail ( args )
2022-04-02 14:18:50 +02:00
cleanAfterRun ( )
2021-12-21 20:48:15 +01:00
} ,
}
2022-04-02 17:11:54 +02:00
2022-04-05 20:58:11 +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 )
} ,
}
* /
2022-04-03 14:27:26 +02:00
setCmd = & cobra . Command {
2022-04-02 17:11:54 +02:00
Use : "set" ,
Short : "Set a jail property" ,
Long : ` Set jail property value . Specify property = value , end command with jail name .
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 )
cleanAfterRun ( )
} ,
}
2022-04-03 10:35:48 +02:00
2022-04-03 14:27:26 +02:00
getCmd = & cobra . Command {
2022-04-03 10:35:48 +02:00
Use : "get" ,
2022-04-03 14:27:26 +02:00
Short : "Get a jail property" ,
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 )
cleanAfterRun ( )
} ,
}
snapshotCmd = & cobra . Command {
Use : "snapshot" ,
Short : "snapshot jail" ,
Long : "Commands to manage jail snapshots. If no arguments given, " ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
} ,
}
snapshotListCmd = & cobra . Command {
Use : "list" ,
Short : "list snapshots" ,
Long : ` List snapshots of a jail by specifying its name .
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-04 21:00:44 +02:00
} ,
}
2022-04-05 20:58:11 +02:00
snapshotCreateCmd = & cobra . Command {
2022-04-04 21:00:44 +02:00
Use : "create" ,
Short : "create snapshots" ,
Long : ` Create snapshot of a jail by specifying snapshot name and jail name. ` ,
// You can specify multiple jails.`,
Run : func ( cmd * cobra . Command , args [ ] string ) {
// Load inventory
ListJails ( args , false )
CreateJailSnapshot ( args )
} ,
}
2022-04-05 20:58:11 +02:00
snapshotRollbackCmd = & cobra . Command {
Use : "rollback" ,
Short : "Rollback snapshots" ,
Long : ` Rollback jail to specifyed snapshot. ` ,
// You can specify multiple jails.`,
Run : func ( cmd * cobra . Command , args [ ] string ) {
// Load inventory
ListJails ( args , false )
RollbackJailSnapshot ( args )
} ,
}
2022-04-04 21:00:44 +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 jails.`,
Run : func ( cmd * cobra . Command , args [ ] string ) {
// Load inventory
ListJails ( args , false )
DeleteJailSnapshot ( args )
2022-04-03 14:27:26 +02:00
} ,
2022-04-03 10:35:48 +02:00
}
2021-12-18 13:13:25 +01:00
)
func init ( ) {
cobra . OnInitialize ( initConfig )
// 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." )
2021-12-18 13:13:25 +01:00
// Command dependant switches
2022-04-04 21:00:44 +02:00
// These are persistent so we can reuse them in "gocage list snapshot myjail" command (TODO)
2022-04-02 17:11:54 +02:00
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." )
2022-04-04 21:00:44 +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" )
2022-04-05 20:58:11 +02:00
snapshotRollbackCmd . Flags ( ) . StringVarP ( & gSnapshotName , "snapname" , "n" , "" , "Name of the snapshot to rollback to" )
snapshotRollbackCmd . MarkFlagRequired ( "snapname" )
2021-12-18 13:13:25 +01:00
// Now declare commands
2022-04-02 17:11:54 +02:00
rootCmd . AddCommand ( versionCmd )
rootCmd . AddCommand ( listCmd )
2022-04-02 15:40:04 +02:00
listCmd . AddCommand ( listPropsCmd )
2022-04-02 17:11:54 +02:00
rootCmd . AddCommand ( stopCmd )
rootCmd . AddCommand ( startCmd )
2022-04-03 10:35:48 +02:00
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 )
snapshotCmd . AddCommand ( snapshotListCmd )
2022-04-04 21:00:44 +02:00
snapshotCmd . AddCommand ( snapshotCreateCmd )
snapshotCmd . AddCommand ( snapshotDeleteCmd )
2022-04-05 20:58:11 +02:00
snapshotCmd . AddCommand ( snapshotRollbackCmd )
2022-04-04 21:00:44 +02:00
2022-04-02 14:18:50 +02:00
// 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 )
2022-04-02 17:11:54 +02:00
2021-12-18 13:13:25 +01:00
}
func initConfig ( ) {
if gConfigFile == "" {
fmt . Println ( "No config file!" )
2021-12-18 21:34:11 +01:00
os . Exit ( 1 )
2021-12-18 13:13:25 +01:00
}
// 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 ( ) )
2021-12-18 21:34:11 +01:00
os . Exit ( 1 )
2021-12-18 13:13:25 +01: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
2021-12-19 14:45:12 +01:00
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" )
}
2021-12-19 14:45:12 +01:00
if listCmd . Flags ( ) . Lookup ( "outcol" ) != nil && false == listCmd . Flags ( ) . Lookup ( "outcol" ) . Changed {
2021-12-19 13:05:30 +01:00
gDisplayColumns = viper . GetString ( "outcol" )
}
2021-12-19 14:45:12 +01:00
if listCmd . Flags ( ) . Lookup ( "nolinesep" ) != nil && false == listCmd . Flags ( ) . Lookup ( "nolinesep" ) . Changed {
2021-12-19 14:31:51 +01:00
gNoLineSep = 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 {
gSortFields = viper . GetString ( "sort" )
}
2021-12-20 22:10:38 +01:00
if len ( strings . Split ( gSortFields , "," ) ) > 3 {
fmt . Printf ( "More than 3 sort criteria, this is not supported!\n" )
os . Exit ( 1 )
}
2021-12-18 13:13:25 +01:00
}
2022-04-02 14:18:50 +02:00
// Called after execution
func cleanAfterRun ( ) {
for _ , j := range gJails {
if j . ConfigUpdated {
marshaled , err := json . MarshalIndent ( j . Config , "" , " " )
if err != nil {
fmt . Printf ( "ERROR marshaling config: %s\n" , err . Error ( ) )
}
2022-04-02 21:38:54 +02:00
//fmt.Printf(string(marshaled))
if os . WriteFile ( j . ConfigPath , [ ] byte ( marshaled ) , 0644 ) ; err != nil {
fmt . Printf ( "Error writing config file %s: %v\n" , j . ConfigPath , err )
os . Exit ( 1 )
}
2022-04-02 14:18:50 +02: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
}