20 Commits

Author SHA1 Message Date
yo
f33b3cfec4 v0.42h: finish previous commit 2024-11-21 20:36:19 +01:00
yo
1006207fc2 v0.42h: Add static-macs option for bridged vnet setup with mac flapping 2024-11-21 18:47:55 +01:00
yo
6acea0d25b better handling of version/patch especiallly for snapshots, use updateWorkDir 2024-10-19 09:57:36 +02:00
yo
fb4010378f ZFS Snapshot release before updating 2024-09-22 21:35:54 +02:00
yo
d0646c51b3 Update README 2024-09-22 19:47:37 +02:00
yo
c6e0a93ed8 Rename "gocage list properties" to "gocage properties" 2024-09-22 19:46:22 +02:00
yo
1e2f937cb5 Update README 2024-09-22 19:39:42 +02:00
yo
b72c432a47 Update README 2024-09-22 19:37:31 +02:00
yo
ca1c7bea5f Update README 2024-09-22 19:35:46 +02:00
yo
fb5c4690bf Update README 2024-09-22 19:32:07 +02:00
yo
5d4c54f5fa Update README 2024-09-22 19:30:43 +02:00
yo
c79ac4ac30 Update release with -r and -d flags 2024-09-22 19:30:39 +02:00
yo
76c720354c freebsd-update work directory is now configurable in gocage.conf.yml 2024-09-22 19:30:35 +02:00
yo
f3f348164a Update README 2024-09-22 17:49:27 +02:00
yo
dcec35459c Update README 2024-09-22 17:48:47 +02:00
yo
e4d699e228 Update README 2024-09-22 17:39:00 +02:00
yo
48a0357a3f add init command files 2024-09-22 17:37:36 +02:00
yo
5b2f3a2f0a Update README to reflect change in fetch flags and new -p flag for create 2024-09-22 17:37:03 +02:00
yo
2df22b10b1 Add -p flag to create command to specify jail configuration items in k=v format 2024-09-22 17:34:56 +02:00
yo
c19e40b139 add getBridgeMembers(), Fix Vnet on by default 2024-09-22 17:34:50 +02:00
10 changed files with 397 additions and 116 deletions

111
README.md
View File

@ -8,36 +8,30 @@ Gocage can handle multiple datastores, so you can have jails on HDD storage and
From v0.33b, due to multi ZFS pool support, gocage is no longer 100% compatible with iocage.
Zfs datasets now should be specified with the ZFS pool. e.g. :
<pre><code>
Config.Jail_zfs = 1
<pre><code>Config.Jail_zfs = 1
Config.Jail_zfs_dataset = myzfspool/poudriere
Config.Jail_zfs_mountpoint = none
</code></pre>
Create jails
------------
For now, we can't pass config at creation time. We have to define config after creation:
<pre><code>
gocage create jail1 -r 13.2-RELEASE
gocage set Config.Ip4_addr="vnet0|192.168.1.91/24" Config.Vnet=1 jail1
You need to specify release, and optional configuration:
<pre><code>gocage create jail1 -r 13.2-RELEASE -p "Config.Ip4_addr='vnet0|192.168.1.91/24',Config.Ip6=none,Config.Boot=1"
</code></pre>
Create basejail (jail based on a release, system will be nullfs read-only mounted from the release directory):
<pre><code>
gocage create -b -r 14.0-RELEASE basejail1
Create basejail. A basejail is a jail based on a release: system will be nullfs read-only mounted from the release directory. Main advantage is that release updates will immediately apply to jails based on this release. Another advantage is that jail system is mounted read-only, a plus from a security perspective:
<pre><code>gocage create -b -r 14.0-RELEASE basejail1
</code></pre>
List jails
----------
Nothing fancy, just use
`gocage list`
<pre><code>gocage list</code></pre>
### Specify fields to display
Use -o to specify which fields you want to display:
<pre><code>
gocage list -o JID,Name,Running,Config.Boot,Config.Comment
<pre><code>gocage list -o JID,Name,Running,Config.Boot,Config.Comment
+=====+==========+=========+=============+================+
| JID | Name | Running | Config.Boot | Config.Comment |
+=====+==========+=========+=============+================+
@ -51,7 +45,7 @@ gocage list -o JID,Name,Running,Config.Boot,Config.Comment
+-----+----------+---------+-------------+----------------+
</code></pre>
See [cmd/struct.go](https://git.nosd.in/yo/gocage/src/branch/master/cmd/struct.go) for field names.
Use `gocage properties`to list available fields.
Filter jails
@ -59,8 +53,7 @@ Filter jails
### By name
Just add name on gocage list command :
<pre><code>
gocage list srv-bdd srv-web
<pre><code>gocage list srv-bdd srv-web
+=====+=========+=================+=======================+=========+
| JID | Name | Config.Release | Config.Ip4_addr | Running |
+=====+=========+=================+=======================+=========+
@ -68,12 +61,11 @@ gocage list srv-bdd srv-web
+-----+---------+-----------------+-----------------------+---------+
| 41 | srv-web | 13.0-RELEASE-p4 | vnet0|192.168.1.26/24 | true |
+-----+---------+-----------------+-----------------------+---------+
</pre></code>
</code></pre>
### By field value
You can filter jails with -f option, followed by key=value. Suppose you want to see only active at boot jails:
<pre><code>
gocage list -f Config.Boot=1 -o JID,Name,Running,Config.Boot,Config.Comment
<pre><code>gocage list -f Config.Boot=1 -o JID,Name,Running,Config.Boot,Config.Comment
+=====+==========+=========+=============+================+
| JID | Name | Running | Config.Boot | Config.Comment |
+=====+==========+=========+=============+================+
@ -85,11 +77,10 @@ gocage list -f Config.Boot=1 -o JID,Name,Running,Config.Boot,Config.Comment
+-----+----------+---------+-------------+----------------+
| 22 | srv-dns1 | true | 1 | |
+-----+----------+---------+-------------+----------------+
</pre></code>
</code></pre>
Now, only active at boot and running :
<pre><code>
gocage list -f Config.Boot=1,Running=true -o JID,Name,Running,Config.Boot
<pre><code>gocage list -f Config.Boot=1,Running=true -o JID,Name,Running,Config.Boot
+=====+==========+=========+=============+
| JID | Name | Running | Config.Boot |
+=====+==========+=========+=============+
@ -99,13 +90,12 @@ gocage list -f Config.Boot=1,Running=true -o JID,Name,Running,Config.Boot
+-----+----------+---------+-------------+
| 22 | srv-dns1 | true | 1 |
+-----+----------+---------+-------------+
</pre></code>
</code></pre>
Sort jails
----------
Use -s switch followed by sort criteria. Criteria is a field name, prefixed with + or - for sort order (increase/decrease):
<pre><code>
gocage list -f Config.Boot=1,Running=true -o JID,Name,Running,Config.Boot -s +JID
<pre><code>gocage list -f Config.Boot=1,Running=true -o JID,Name,Running,Config.Boot -s +JID
+=====+==========+=========+=============+
| JID | Name | Running | Config.Boot |
+=====+==========+=========+=============+
@ -115,12 +105,11 @@ gocage list -f Config.Boot=1,Running=true -o JID,Name,Running,Config.Boot -s +JI
+-----+----------+---------+-------------+
| 183 | test | true | 1 |
+-----+----------+---------+-------------+
</pre></code>
</code></pre>
You can use up to 3 criteria, delimited with comma.
As an example, you want to list boot priorities of automatically starting jails:
<pre><code>
gocage list -o JID,Name,Config.Ip4_addr,Config.Priority,Config.Boot,Running -s -Config.Priority,-Config.Boot -f Running=true
<pre><code>gocage list -o JID,Name,Config.Ip4_addr,Config.Priority,Config.Boot,Running -s -Config.Priority,-Config.Boot -f Running=true
+=====+==============+=======================+=================+=============+=========+
| JID | Name | Config.Ip4_addr | Config.Priority | Config.Boot | Running |
+=====+==============+=======================+=================+=============+=========+
@ -132,47 +121,62 @@ gocage list -o JID,Name,Config.Ip4_addr,Config.Priority,Config.Boot,Running -s -
+-----+--------------+-----------------------+-----------------+-------------+---------+
| 4 | coincoin | vnet0|192.168.1.9/24 | 20 | 0 | true |
+-----+--------------+-----------------------+-----------------+-------------+---------+
</pre></code>
</code></pre>
Stop jails
----------
`gocage stop test`
<pre><code>gocage stop test</code></pre>
Update jails
----------
To update jail patch version, use gocage update :
`gocage update test`
<pre><code>gocage update test</code></pre>
Update basejails/releases
----------
To update basejails, you need to update the release they are based on. Specify release with -r, and the datastore storing concerned release with -d :
<pre><code>gocage update -d fastgocage -r 14.1-RELEASE</code></pre>
Upgrade jails
----------
To upgrade jail to newer release, use gocage upgrade :
`gocage upgrade -r 13.2-RELEASE test`
<pre><code>gocage upgrade -r 13.2-RELEASE test</code></pre>
A pre-upgrade snapshot wil be made so we can rollback if needed.
A pre-upgrade snapshot wil be made so you can rollback if needed.
Upgrading basejail/release
----------
Upgrading basejails currently needs to be done manually, for each jail.
The idea is to stop the jail, change the content of its fstab file to point to the new release, then start jail.
If one change the fstab while the jail is running, its system directories won't be unmounted at stop time and this will provoke stop errors.
To minimize downtime, the change could be scripted:
<pre><code>gocage stop jail1
sed -i .bak 's/14.0-RELEASE/14.1-RELEASE/' /iocage/jails/jail1/fstab
# Avoid race-condition by waiting for the update in fstab
until grep -q 14.1-RELEASE /iocage/jails/jail1/fstab; do sleep 0.2; done
gocage start jail1
</code></pre>
You can now update ports.
Delete jails
----------
`gocage destroy test`
<pre><code>gocage destroy test</code></pre>
Multi datastore
----------
A datastore is a ZFS dataset mounted. It should be declared in gocage.conf.yml, specifying its ZFS mountpoint :
<pre><code>
datastore:
<pre><code>datastore:
- /iocage
- /fastiocage
</pre></code>
</code></pre>
In gocage commands, datastore name is the mountpoint without its "/" prefix.
### List datastores
<pre><code>
gocage datastore list
<pre><code>gocage datastore list
+============+=============+============+===========+==========+============+
| Name | Mountpoint | ZFSDataset | Available | Used | Referenced |
+============+=============+============+===========+==========+============+
@ -180,23 +184,21 @@ gocage datastore list
+------------+-------------+------------+-----------+----------+------------+
| fastiocage | /fastiocage | ssd/iocage | 1.5 TB | 65.3 KB | 34.6 KB |
+------------+-------------+------------+-----------+----------+------------+
</pre></code>
</code></pre>
### Filter datastores
As with jails and snapshots, you can filter by name:
<pre><code>
gocage datastore list iocage
<pre><code>gocage datastore list iocage
+============+=============+============+===========+==========+============+
| Name | Mountpoint | ZFSDataset | Available | Used | Referenced |
+============+=============+============+===========+==========+============+
| iocage | /iocage | hdd/iocage | 1.6 TB | 414.9 GB | 27.5 KB |
+------------+-------------+------------+-----------+----------+------------+
</pre></code>
</code></pre>
### Sort datastores
You can sort datastores:
<pre><code>
gocage datastore list -s -Available
<pre><code>gocage datastore list -s -Available
+============+=============+============+===========+==========+============+
| Name | Mountpoint | ZFSDataset | Available | Used | Referenced |
+============+=============+============+===========+==========+============+
@ -204,9 +206,9 @@ gocage datastore list -s -Available
+------------+-------------+------------+-----------+----------+------------+
| fastiocage | /fastiocage | ssd/iocage | 1.5 TB | 65.3 KB | 34.6 KB |
+------------+-------------+------------+-----------+----------+------------+
</pre></code>
</code></pre>
See [cmd/struct.go](https://git.nosd.in/yo/gocage/src/branch/master/cmd/struct.go) for field names.
Use `gocage properties`to list available fields.
Migrating jails
----------
@ -219,26 +221,23 @@ Be aware the moment you migrate a jail to another datastore than /iocage default
Then you need to disable iocage service, and enable gocage so the jails will start automatically at boot.
Also make sure, if you don't destroy source jail, that it won't have the "boot" property set or you will have the 2 jails up at boot.
<pre><code>
gocage migrate -d fastiocage srv-random
<pre><code>gocage migrate -d fastiocage srv-random
Snapshot data/iocage/jails/srv-random: Done
Snapshot data/iocage/jails/srv-random/root: Done
Migrate jail config dataset to fastdata/iocage/jails/srv-random: Done
Migrate jail filesystem dataset to fastdata/iocage/jails/srv-random/root: Done
</pre></code>
</code></pre>
Fetch
----------
Files can be fetched from custom repository, or from local directory with "from" option.
For example if you destroyed releases/12.3-RELEASE and still have the downloaded files in /iocage/download/12.3-RELEASE:
<pre><code>
gocage fetch -r 12.3 -o iocage --from file:/iocage/download
</pre></code>
<pre><code>gocage fetch -r 12.3 -d iocage -f file:/iocage/download
</code></pre>
TODO
----------
gocage create from templates
gocage init
create default pool with defaults.json

View File

@ -44,7 +44,7 @@ func CreateJail(args []string) {
var ds *Datastore
if len(gCreateArgs.Datastore) > 0 {
fmt.Printf("DEBUG: Use %s datastore\n", gCreateArgs.Datastore)
log.Debugf("Use %s datastore\n", gCreateArgs.Datastore)
ds, err = getDatastoreFromArray(gCreateArgs.Datastore, gDatastores)
if err != nil {
fmt.Printf("ERROR Getting datastore: %s\n", gCreateArgs.Datastore, err.Error())
@ -272,7 +272,6 @@ func CreateJail(args []string) {
j.Config.Host_hostname = jname
j.Config.Host_hostuuid = jname
j.Config.Jailtype = "jail"
j.WriteConfigToDisk(false)
///////////////////////////////////////////////////////////////////////
@ -285,5 +284,17 @@ func CreateJail(args []string) {
defer fstabHandle.Close()
fmt.Printf(" > Jail created!\n")
}
var cmdline []string
for _, props := range strings.Split(gCreateArgs.Properties, ",") {
cmdline = append(cmdline, props)
}
// Reload jail list so SetJailProperties will see it
ListJails(nil, false)
cmdline = append(cmdline, jname)
log.Debugf("cmdline: \"%v\"", cmdline)
SetJailProperties(cmdline)
}
}

153
cmd/init.go Normal file
View File

@ -0,0 +1,153 @@
package cmd
import (
"os"
"fmt"
"strings"
"github.com/spf13/viper"
log "github.com/sirupsen/logrus"
)
/********************************************************************************
* Initialize datastore(s) /iocage, /iocage/jails
* Put defaults.json,
* Update it with hostid, interfaces, and maybe other necessary fields
* Initialize bridge
*******************************************************************************/
func InitGoCage(args []string) {
// Create datastores
for _, dstore := range viper.GetStringSlice("datastore") {
log.Debugf("Ranging over %v\n", dstore)
dset, err := zfsGetDatasetByMountpoint(dstore)
if err != nil && strings.HasSuffix(err.Error(), "No such file or directory\"") {
if len(gZPool) == 0 {
log.Errorf("Datastore mountpoint \"%s\" does not exist. Specify a pool if you want to create it.", dstore)
return
}
// Create dataset /iocage
rootDSName := fmt.Sprintf("%s%s", gZPool, dstore)
log.Debugf("Creating dataset %s mounted on %s\n", rootDSName, dstore)
if err = zfsCreateDataset(rootDSName, dstore, ""); err != nil {
log.Errorf("Error creating dataset %s: %v\n", rootDSName, err)
return
}
// Create /iocage/jail, releases, templates
for _, l := range []string{"jails","releases","templates"} {
cds := fmt.Sprintf("%s/%s", rootDSName, l)
cmp := fmt.Sprintf("%s/%s", dstore, l)
log.Debugf("Creating dataset %s mounted on %s\n", cds, cmp)
if err = zfsCreateDataset(cds, cmp, ""); err != nil {
log.Errorf("Error creating dataset %s: %v\n", cds, err)
return
}
}
// Create /iocage/defaults.json
exists, err := doFileExist(fmt.Sprintf("%s/defaults.json", dstore))
if err != nil {
log.Errorf("Error checking defaults.json: %v\n", err)
return
}
if !exists {
if err = createDefaultsJson(dstore, gBridge); err != nil {
log.Errorf("%v\n", err)
}
}
} else if err != nil {
log.Errorf("Error checking datastore existence: %v\n", err)
return
} else {
log.Debugf("Datastore dataset exist: %s\n", dset)
}
}
// Check and create bridge
// FIXME: What if bridge name is invalid, as we already wrote it in defaults.json in dstore loop?
if len(gBridge) > 0 && len(gInterface) > 0 {
if err := initBridge(); err != nil {
log.Errorf("%v\n", err)
}
}
}
func createDefaultsJson(rootDirectory string, bridge string) error {
hostid, err := os.ReadFile("/etc/hostid")
if err != nil {
log.Fatalf("Unable to read /etc/hostid: %v\n", err)
}
json := strings.Replace(gDefaultsJson, "TO-BE-REPLACED-WITH-HOSTID", strings.Trim(string(hostid), "\n"), 1)
json = strings.Replace(json, "TO-BE-REPLACED-WITH-BRIDGE", bridge, 1)
if err := os.WriteFile(fmt.Sprintf("%s/defaults.json", rootDirectory), []byte(json), 0640); err != nil {
log.Fatal(err)
}
return nil
}
func createInterface(iface string) error {
log.Debugf("creating interface \"%s\"\n", iface)
cmd := fmt.Sprintf("/sbin/ifconfig %s create", iface)
_, err := executeCommand(cmd)
if err != nil {
return err
}
return nil
}
func bringUpInterface(iface string) error {
log.Debugf("bringing up interface \"%s\"\n", iface)
cmd := fmt.Sprintf("/sbin/ifconfig %s up", iface)
_, err := executeCommand(cmd)
if err != nil {
return err
}
return nil
}
func addMemberToBridge(bridge string, iface string) error {
log.Debugf("adding member interface \"%s\" to bridge \"%s\"\n", iface, bridge)
cmd := fmt.Sprintf("/sbin/ifconfig %s addm %s", bridge, iface)
_, err := executeCommand(cmd)
if err != nil {
return err
}
return nil
}
func initBridge() error {
hostInt, err := gJailHost.GetInterfaces()
if err != nil {
return fmt.Errorf("Error listing interfaces: %v\n", err)
}
if !isStringInArray(hostInt, gInterface) {
return fmt.Errorf("Interface not found: %s\n", gInterface)
}
if !isStringInArray(hostInt, gBridge) {
if err := createInterface(gBridge); err != nil {
return fmt.Errorf("Error creating bridge: %v\n", err)
}
if err := bringUpInterface(gBridge); err != nil {
return fmt.Errorf("Error bringing up bridge: %v\n", err)
}
log.Infof("bridge was created, but it won't persist reboot. Configure rc.conf to persist. See https://docs.freebsd.org/en/books/handbook/advanced-networking/#network-bridging\n")
log.Infof("It is strongly suggested you move interface %s IP to bridge %s\n", gInterface, gBridge)
}
// FIXME: Need to check if not already member
members, err := getBridgeMembers(gBridge)
if err != nil {
return fmt.Errorf("Error getting bridge members: %v\n", err)
}
// Return if interface already member of the bridge
for _, m := range members {
log.Debugf("Bridge member: %s\n", m)
if strings.EqualFold(m, gInterface) {
return nil
}
}
if err := addMemberToBridge(gBridge, gInterface); err != nil {
return fmt.Errorf("Error adding interface to bridge: %v\n", err)
}
return nil
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"net"
"regexp"
"strconv"
"strings"
"io/ioutil"
"golang.org/x/net/route"
@ -190,34 +189,17 @@ func getArch() (string, error) {
func getFreeBSDVersion() (FreeBSDVersion, error) {
var version FreeBSDVersion
regex := `([0-9]{1,2})(\.)?([0-9]{1,2})?\-([^\-]*)(\-)?(p[0-9]{1,2})?`
re := regexp.MustCompile(regex)
out, err := executeCommand("/bin/freebsd-version")
if err != nil {
return version, fmt.Errorf("Error executing \"/bin/freebsd-version\": %v", err)
}
if re.MatchString(out) {
version.major, err = strconv.Atoi(re.FindStringSubmatch(out)[1])
version, err = freebsdVersionToStruct(out)
if err != nil {
return version, err
}
version.minor, err = strconv.Atoi(re.FindStringSubmatch(out)[3])
if err != nil {
return version, err
}
version.flavor = strings.Trim(re.FindStringSubmatch(out)[4], "\n")
// Skip the 'p' starting patch level
if len(re.FindStringSubmatch(out)[6]) > 0 {
version.patchLevel, err = strconv.Atoi(re.FindStringSubmatch(out)[6][1:])
if err != nil {
return version, err
}
}
}
return version, nil
}

View File

@ -14,7 +14,7 @@ import (
)
const (
gVersion = "0.42b"
gVersion = "0.42h"
// TODO : Get from $jail_zpool/defaults.json
MIN_DYN_DEVFS_RULESET = 1000
@ -25,6 +25,7 @@ type createArgs struct {
BaseJail bool
Datastore string
JailType string
Properties string
}
var (
@ -51,6 +52,7 @@ var (
gNoJailLineSep bool
gNoSnapLineSep bool
gNoDSLineSep bool
gBridgeStaticMac bool
gHostVersion float64
@ -67,6 +69,8 @@ var (
gFetchIntoDS string
gFetchFrom string
gUpgradeRelease string
gUpdateRelease string
gUpdateReleaseDS string
// For a based jail, these are directories binded to basejail
gBaseDirs = []string{"bin", "boot", "lib", "libexec", "rescue", "sbin", "usr/bin", "usr/include",
@ -96,7 +100,11 @@ It support iocage jails and can coexist with iocage.`,
Long: `Let this show you how much fail I had to get this *cough* perfect`,
Run: func(cmd *cobra.Command, args []string) {
fv, _ := getFreeBSDVersion()
if fv.patchLevel > 0 {
fmt.Printf("GoCage v.%s on FreeBSD %d.%d-%s-p%d\n", gVersion, fv.major, fv.minor, fv.flavor, fv.patchLevel)
} else {
fmt.Printf("GoCage v.%s on FreeBSD %d.%d-%s\n", gVersion, fv.major, fv.minor, fv.flavor)
}
},
}
@ -379,6 +387,7 @@ func init() {
initCmd.Flags().StringVarP(&gZPool, "pool", "p", "", "ZFS pool to create datastore on")
initCmd.Flags().StringVarP(&gBridge, "bridge", "b", "", "bridge to create for jails networking")
initCmd.Flags().StringVarP(&gInterface, "interface", "i", "", "interface to add as bridge member. This should be your main interface")
initCmd.MarkFlagRequired("bridge")
initCmd.MarkFlagsRequiredTogether("bridge", "interface")
// We reuse these flags in "gocage snapshot list myjail" and 'gocage datastore list" commands
@ -412,23 +421,28 @@ func init() {
migrateCmd.MarkFlagRequired("datastore")
fetchCmd.Flags().StringVarP(&gFetchRelease, "release", "r", "", "Release to fetch (e.g.: \"13.1-RELEASE\"")
fetchCmd.Flags().StringVarP(&gFetchIntoDS, "datastore", "o", "", "Datastore release will be saved to")
fetchCmd.Flags().StringVarP(&gFetchFrom, "from", "d", "", "Repository to download from. Should contain XY.Z-RELEASE. File protocol supported")
fetchCmd.Flags().StringVarP(&gFetchIntoDS, "datastore", "d", "", "Datastore release will be saved to")
fetchCmd.Flags().StringVarP(&gFetchFrom, "from", "f", "", "Repository to download from. Should contain XY.Z-RELEASE. File protocol supported")
fetchCmd.MarkFlagRequired("release")
fetchCmd.MarkFlagRequired("datastore")
upgradeCmd.Flags().StringVarP(&gUpgradeRelease, "release", "r", "", "Release to upgrade to (e.g.: \"13.1-RELEASE\"")
upgradeCmd.MarkFlagRequired("release")
updateCmd.Flags().StringVarP(&gUpdateRelease, "release", "r", "", "Release to update (e.g.: \"13.1-RELEASE\"")
updateCmd.Flags().StringVarP(&gUpdateReleaseDS, "datastore", "d", "", "Datastore release is stored on")
updateCmd.MarkFlagsRequiredTogether("release", "datastore")
createCmd.Flags().StringVarP(&gCreateArgs.Release, "release", "r", "", "Release for the jail (e.g.: \"13.1-RELEASE\"")
createCmd.Flags().BoolVarP(&gCreateArgs.BaseJail, "basejail", "b", false, "Basejail. This will create a jail mounted read only from a release, so every up(date|grade) made to this release 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.")
createCmd.Flags().StringVarP(&gCreateArgs.Properties, "configuration", "p", "", "Configuration properties with format k1=v1,k2=v2 (Ex: \"Config.Ip4_addr=vnet0|192.168.1.2,Config.Ip6=none\")")
// Now declare commands
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(versionCmd)
rootCmd.AddCommand(listCmd)
listCmd.AddCommand(listPropsCmd)
rootCmd.AddCommand(listPropsCmd)
rootCmd.AddCommand(stopCmd)
rootCmd.AddCommand(startCmd)
rootCmd.AddCommand(restartCmd)
@ -443,7 +457,6 @@ func init() {
rootCmd.AddCommand(updateCmd)
rootCmd.AddCommand(upgradeCmd)
rootCmd.AddCommand(createCmd)
rootCmd.AddCommand(apiCmd)
rootCmd.AddCommand(testCmd)
snapshotCmd.AddCommand(snapshotListCmd)
@ -507,6 +520,8 @@ func initConfig() {
os.Exit(1)
}
gBridgeStaticMac = viper.GetBool("static-macs")
if gDebug {
log.SetLevel(log.DebugLevel)
log.Debugf("Debug mode enabled\n")

View File

@ -1007,6 +1007,19 @@ func setupVnetInterfaceJailSide(jail *Jail, hostepairs []string) error {
if err != nil {
return fmt.Errorf("Error adding member %s to %s: %v: %s\n", nic, bridge, err, out)
}
// Fix mac flapping and instabilities with bridged vnet network
if gBridgeStaticMac {
printablemac := hex.EncodeToString(jsmac)
for i := 2 ; i < len(printablemac) ; i += 3 {
printablemac = printablemac[:i] + ":" + printablemac[i:]
}
log.Debugf("Set %s.%d as static to %s in %s address cache\n", nic, jail.JID, printablemac, bridge)
cmd = fmt.Sprintf("/sbin/ifconfig %s static %s.%d %s", bridge, nic, jail.JID, printablemac)
out, err := executeCommand(cmd)
if err != nil {
return fmt.Errorf("Error setting %s.%d static with %s on %s: %v: %s\n", nic, jail.JID, printablemac, bridge, err, out)
}
}
}
// Check we have an IP for the nic, and set it into jail

View File

@ -6,33 +6,36 @@ import (
//"log"
"time"
"strings"
"github.com/spf13/viper"
)
// Internal usage only
func updateJail(jail *Jail) error {
func updateJail(jail *Jail, doUpdateVersion bool) error {
// Create default config as temporary file
cfgFile, err := os.CreateTemp("", "gocage-jail-update-")
if err != nil {
return err
}
cfgFile.Write([]byte(fbsdUpdateConfig))
defer cfgFile.Close()
defer os.Remove(cfgFile.Name())
// Folder containing update/upgrade temporary files. Common so we save bandwith when upgrading multiple jails
// TODO: Variabilize /iocage/freebsd-update
_, err = os.Stat("/iocage/freebsd-update")
// Folder containing update/upgrade temporary files. Mutualized so we save bandwith when upgrading multiple jails
uwd := viper.GetString("updateWorkDir")
if len(uwd) == 0 {
return fmt.Errorf("updateWorkDir not set in configuration")
}
_, err = os.Stat(uwd)
if os.IsNotExist(err) {
if err := os.Mkdir("/iocage/freebsd-update", 0755); err != nil {
if err := os.Mkdir(uwd, 0755); err != nil {
return err
}
}
cfgFile.Write([]byte(strings.Replace(fbsdUpdateConfig, "TO-BE-REPLACED-WITH-UPDATEWORKDIR", uwd, 1)))
defer cfgFile.Close()
defer os.Remove(cfgFile.Name())
cmd := fmt.Sprintf("/usr/sbin/freebsd-update --not-running-from-cron -f %s -b %s --currently-running %s fetch",
cfgFile.Name(), jail.RootPath, jail.Config.Release)
err = executeCommandWithOutputToStdout(cmd)
if err != nil {
return err
@ -45,8 +48,10 @@ func updateJail(jail *Jail) error {
return err
}
// Get and write new release into config.json
// Get and write new release into config.json. Don't do that for fake jail (aka release updating)
if doUpdateVersion {
updateVersion(jail)
}
return nil
}
@ -56,6 +61,52 @@ func UpdateJail(args []string) {
var cj *Jail
var err error
// User is updateing a release, fake a jail
if len(gUpdateRelease) > 0 {
// get datastore mountpoing from datastore name
ds, err := getDatastoreFromArray(gUpdateReleaseDS, gDatastores)
if err != nil {
fmt.Printf("Error getting datastore %s: %v\n", gUpdateReleaseDS, err)
return
}
rp := fmt.Sprintf("%s/releases/%s/root", ds.Mountpoint, gUpdateRelease)
fakeJail := Jail{RootPath: rp}
v, err := getVersion(&fakeJail)
if err != nil {
fmt.Printf("Error getting version of release %s: %v\n", gUpdateRelease, err)
return
}
fakeJail.Config.Release = v
// Remove patch level from Release
fv, err := freebsdVersionToStruct(fakeJail.Config.Release)
if err != nil {
fmt.Printf("Error converting release %s: %v\n", fakeJail.Config.Release, err)
return
}
release := fmt.Sprintf("%d.%d-%s", fv.major, fv.minor, fv.flavor)
// Snapshot before updating
dt := time.Now()
curDate := fmt.Sprintf("%s", dt.Format("2006-01-02_15-04-05"))
snapshotName := fmt.Sprintf("gocage_update_%s_%s", v, curDate)
err = zfsSnapshot(fmt.Sprintf("%s/releases/%s", ds.ZFSDataset, release), snapshotName)
if err != nil {
fmt.Printf("Error snapshoting release %s: %v\n", gUpdateRelease, err)
return
}
err = zfsSnapshot(fmt.Sprintf("%s/releases/%s/root", ds.ZFSDataset, release), snapshotName)
if err != nil {
fmt.Printf("Error snapshoting release %s: %v\n", gUpdateRelease, err)
} else {
fmt.Printf("Release %s was snapshoted with success: %s\n", gUpdateRelease, snapshotName)
}
if err = updateJail(&fakeJail, false); err != nil {
fmt.Printf("Error updating release %s: %v\n", gUpdateRelease, err)
}
return
}
for _, a := range args {
// Check if jail exist and is distinctly named
cj, err = getJailFromArray(a, []string{""}, gJails)
@ -74,7 +125,7 @@ func UpdateJail(args []string) {
// Set snapshot name
dt := time.Now()
curDate := fmt.Sprintf("%s", dt.Format("2006-01-02_15-04-05"))
gSnapshotName = fmt.Sprintf("goc_update_%s_%s", cj.Config.Release, curDate)
gSnapshotName = fmt.Sprintf("gocage_update_%s_%s", cj.Config.Release, curDate)
err := createJailSnapshot(*cj)
if err != nil {
fmt.Printf(" > Snapshot jail %s: ERROR: %s\n", cj.Name, err.Error())
@ -83,7 +134,7 @@ func UpdateJail(args []string) {
fmt.Printf(" > Snapshot jail %s: OK\n", cj.Name)
fmt.Printf(" > Update jail %s\n", cj.Name)
err = updateJail(cj)
err = updateJail(cj, true)
if err != nil {
fmt.Printf("ERROR: %s\n", err.Error())
} else {

View File

@ -8,6 +8,7 @@ import (
//"log"
"time"
"strings"
"github.com/spf13/viper"
)
// Internal usage only
@ -18,19 +19,20 @@ func upgradeJail(jail *Jail, version string) error {
return err
}
cfgFile.Write([]byte(fbsdUpdateConfig))
defer cfgFile.Close()
defer os.Remove(cfgFile.Name())
// Folder containing update/uipgrade temporary files. Common so we save bandwith when upgrading multiple jails
// TODO: Variabilize /iocage/freebsd-update
_, err = os.Stat("/iocage/freebsd-update")
// Folder containing update/upgrade temporary files. Mutualized so we save bandwith when upgrading multiple jails
uwd := viper.GetString("updateWorkDir")
if len(uwd) == 0 {
return fmt.Errorf("updateWorkDir not set in configuration")
}
_, err = os.Stat(uwd)
if os.IsNotExist(err) {
if err := os.Mkdir("/iocage/freebsd-update", 0755); err != nil {
if err := os.Mkdir(uwd, 0755); err != nil {
return err
}
}
cfgFile.Write([]byte(strings.Replace(fbsdUpdateConfig, "TO-BE-REPLACED-WITH-UPDATEWORKDIR", uwd, 1)))
defer cfgFile.Close()
defer os.Remove(cfgFile.Name())
// Get current version. Won't work on stopped jail.
fbsdvers, err := executeCommandInJail(jail, "/bin/freebsd-version")
@ -39,7 +41,6 @@ func upgradeJail(jail *Jail, version string) error {
return err
}
fbsdvers = strings.TrimRight(fbsdvers, "\n")
//fbsdvers := jail.Config.Release
cmd := fmt.Sprintf("/usr/sbin/freebsd-update -f %s -b %s --currently-running %s -r %s upgrade",
cfgFile.Name(), jail.RootPath, fbsdvers, version)

View File

@ -8,6 +8,7 @@ import (
"sort"
"bufio"
"errors"
"regexp"
"os/exec"
"reflect"
"strconv"
@ -68,7 +69,7 @@ MergeChanges /etc/
# Directory in which to store downloaded updates and temporary
# files used by FreeBSD Update.
WorkDir /iocage/freebsd-update
WorkDir TO-BE-REPLACED-WITH-UPDATEWORKDIR
# Destination to send output of "freebsd-update cron" if an error
# occurs or updates have been downloaded.
@ -228,7 +229,7 @@ CreateBootEnv no
"type": "jail",
"used": "readonly",
"vmemoryuse": "off",
"vnet": 0,
"vnet": 1,
"vnet0_mac": "none",
"vnet1_mac": "none",
"vnet2_mac": "none",
@ -751,6 +752,30 @@ func executeScript(script string) (string, error) {
return string(out), err
}
/*****************************************************************************
*
* Network related operations
*
*****************************************************************************/
func getBridgeMembers(bridge string) ([]string, error) {
var members []string
cmd := fmt.Sprintf("/sbin/ifconfig %s", bridge)
out, err := executeCommand(cmd)
if err != nil {
return members, errors.New(fmt.Sprintf("%v; command returned \"%s\"", err, out))
}
for _, line := range strings.Split(out, "\n") {
if strings.HasPrefix(strings.TrimLeft(line, " \t"), "member:") {
m := strings.Split(strings.TrimLeft(strings.Split(line, ":")[1], " "), " ")[0]
log.Debugf("%s is member of %s\n", m, bridge)
members = append(members, m)
}
}
return members, nil
}
/*****************************************************************************
*
* ZFS datasets/pools operations
@ -1033,6 +1058,7 @@ func addRcKeyValue(rcconfpath string, key string, value string) error {
}
return nil
}
/*****************************************************************************
* Parse an fstab file, returning an array of Mount
*****************************************************************************/
@ -1085,13 +1111,10 @@ func getDevfsRuleset(ruleset int) []string {
if err != nil {
return []string{}
}
// Get rid of the last "\n"
return strings.Split(out, "\n")[:len(strings.Split(out, "\n"))-1]
return strings.Split(out, "\n")[:len(strings.Split(out, "\n"))]
}
func copyDevfsRuleset(ruleset int, srcrs int) error {
// Resulting ruleset as an array of line
//var result []string
out := getDevfsRuleset(srcrs)
for _, line := range out {
//fields := strings.Fields(line)
@ -1262,6 +1285,36 @@ func setJailConfigUpdated(jail *Jail) error {
return nil
}
func freebsdVersionToStruct(rawVersion string) (FreeBSDVersion, error) {
var version FreeBSDVersion
var err error
regex := `([0-9]{1,2})(\.)?([0-9]{1,2})?\-([^\-]*)(\-)?(p[0-9]{1,2})?`
re := regexp.MustCompile(regex)
if re.MatchString(rawVersion) {
version.major, err = strconv.Atoi(re.FindStringSubmatch(rawVersion)[1])
if err != nil {
return version, err
}
version.minor, err = strconv.Atoi(re.FindStringSubmatch(rawVersion)[3])
if err != nil {
return version, err
}
version.flavor = strings.Trim(re.FindStringSubmatch(rawVersion)[4], "\n")
// Skip the 'p' starting patch level
if len(re.FindStringSubmatch(rawVersion)[6]) > 0 {
version.patchLevel, err = strconv.Atoi(re.FindStringSubmatch(rawVersion)[6][1:])
if err != nil {
return version, err
}
}
}
return version, nil
}
func getVersion(jail *Jail) (string, error) {
cvers, err := executeCommand(fmt.Sprintf("%s/bin/freebsd-version", jail.RootPath))
if err != nil {

View File

@ -5,6 +5,9 @@ datastore:
# Prefix all commands with sudo
sudo: false
# Directory used to store update temporary files. Mutualized so we save bandwith
updateWorkDir: /iocage/freebsd-updates
# Columns to display when "gocage list". Column names are struct fields, see cmd/struct.go
outcol: 'JID,Name,Config.Release,Config.Ip4_addr,Running'