gocage/cmd/create.go

290 lines
9.4 KiB
Go

package cmd
import (
"os"
"fmt"
//"log"
"time"
"errors"
"strings"
cp "github.com/otiai10/copy"
log "github.com/sirupsen/logrus"
)
// TODO : Add a flag to specify which parts of freebsd base we want : Slim jail only need base.txz, neither lib32 nor src.txz
func CreateJail(args []string) {
var err error
var jtype []string
if gCreateArgs.BaseJail && gCreateArgs.Release == "" {
fmt.Println("Release should be set when creating basejail")
os.Exit(1)
}
if len(gCreateArgs.JailType) > 0 {
jtype = []string{gCreateArgs.JailType}
}
for _, jname := range args {
// Check if jail exist and is distinctly named
_, err = getJailFromArray(jname, jtype, gJails)
if err != nil {
if strings.EqualFold(err.Error(), "Jail not found") {
} else {
fmt.Printf("ERROR: %s\n", err.Error())
return
}
} else {
fmt.Printf("Jail exist: %s\n", jname)
continue
}
fmt.Printf(" > create jail %s\n", jname)
var ds *Datastore
if len(gCreateArgs.Datastore) > 0 {
fmt.Printf("DEBUG: 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())
return
}
} else {
ds = &gDatastores[0]
}
// Get base template if specified
if gCreateArgs.BaseJail {
/**************************************************************************
* Create based jail from a template
*/
log.Debugf("Jail will be created read-only from release %s\n", gCreateArgs.Release)
// First check if we got release on the same datastore
releasePath := fmt.Sprintf("%s/releases/%s/root", ds.Mountpoint, gCreateArgs.Release)
_, err := os.Stat(releasePath)
if os.IsNotExist(err) {
fmt.Printf("ERROR: Release locally not available. Run \"gocage fetch\"\n")
return
}
// Create jail datasets
dstDset := fmt.Sprintf("%s/jails/%s", ds.ZFSDataset, jname)
fmt.Printf(" > Initialize dataset %s\n", dstDset)
err = zfsCreateDataset(dstDset, "", "")
if err != nil {
fmt.Printf("ERROR creating dataset %s: %s\n", dstDset, err.Error())
return
}
// Create jail root datasets
dstRootDset := fmt.Sprintf("%s/jails/%s/root", ds.ZFSDataset, jname)
fmt.Printf(" > Initialize dataset %s\n", dstRootDset)
err = zfsCreateDataset(dstRootDset, "", "")
if err != nil {
fmt.Printf("ERROR creating dataset %s: %s\n", dstRootDset, err.Error())
return
}
// Create needed directories with basejail permissions
fmt.Printf(" > Create base read-only directories\n")
dstRootDir := fmt.Sprintf("%s/jails/%s/root", ds.Mountpoint, jname)
for _, d := range append(gBaseDirs, gEmptyDirs...) {
dstPath := dstRootDir
srcPath := releasePath
for _, cd := range strings.Split(d, "/") {
srcPath = fmt.Sprintf("%s/%s", srcPath, cd)
dstPath = fmt.Sprintf("%s/%s", dstPath, cd)
_, err := os.Stat(dstPath)
if errors.Is(err, os.ErrNotExist) {
srcPerm, err := getPermissions(srcPath)
if err != nil {
fmt.Printf("ERROR getting permissions of %s: %s\n", srcPath, err.Error())
return
}
err = os.Mkdir(dstPath, srcPerm.Mode().Perm())
if err != nil {
fmt.Printf("ERROR creating directory %s: %s\n", dstPath, err.Error())
return
}
}
}
}
// Copy these from basejail
fmt.Printf(" > Create base writable directories\n")
for _, d := range gCopyDirs {
err := cp.Copy(fmt.Sprintf("%s/%s", releasePath, d), fmt.Sprintf("%s/%s", dstRootDir, d))
if err != nil {
fmt.Printf("ERROR copying %s to %s: %s\n", fmt.Sprintf("%s/%s", releasePath, d),
fmt.Sprintf("%s/%s", dstRootDir, d), err.Error())
return
}
}
///////////////////////////////////////////////////////////////////////
// Copy defaults.json...
jailConfPath := fmt.Sprintf("%s/jails/%s/config.json", ds.Mountpoint, jname)
err = copyFile(fmt.Sprintf("%s/defaults.json", ds.Mountpoint),
jailConfPath)
if err != nil {
fmt.Printf("ERROR creating config.json: %s\n", err.Error())
return
}
///////////////////////////////////////////////////////////////////////
// ... and update it
jailConf, err := getJailConfig(jailConfPath)
if err != nil {
log.Println("ERROR reading jail config from %s", jailConfPath)
}
// Build jail object from config
jailRootPath := fmt.Sprintf("%s/jails/%s/%s", ds.Mountpoint, jname, "root")
j := Jail{
Name: jailConf.Host_hostuuid,
Config: jailConf,
ConfigPath: jailConfPath,
Datastore: ds.Name,
RootPath: jailRootPath,
Running: false,
}
// We need to store the basejail template. We could :
// 1. Use "origin" ?
// 2. Add a json item to config ("basejail_template" p.e.), but iocage would delete it once jail is started from iocage
// 3. Add a gocage specific config ("config.gocage.json" p.e.)
j.Config.Jailtype = "basejail"
j.Config.Origin = gCreateArgs.Release
j.Config.Host_hostname = jname
j.Config.Host_hostuuid = jname
j.WriteConfigToDisk(false)
///////////////////////////////////////////////////////////////////////
// Create fstab
fstabHandle, err := os.Create(fmt.Sprintf("%s/jails/%s/fstab", ds.Mountpoint, jname))
if err != nil {
fmt.Printf("ERROR creating fstab: %s", err.Error())
return
}
defer fstabHandle.Close()
for _, d := range gBaseDirs {
fmt.Fprintf(fstabHandle, "%s\t%s\tnullfs\tro\t0\t0\n", fmt.Sprintf("%s/%s", releasePath, d), fmt.Sprintf("%s/%s", dstRootDir, d))
}
fmt.Printf(" > Jail created!\n")
} else {
/**************************************************************************
* Create normal jail with its own freebsd base
*/
log.Debugf("Creating jail with its own freebsd base\n")
// First check if we got release on the same datastore
_, err := os.Stat(fmt.Sprintf("%s/releases/%s/root", ds.Mountpoint, gCreateArgs.Release))
if os.IsNotExist(err) {
fmt.Printf("ERROR: Release locally not available. Run \"gocage fetch\"\n")
return
}
///////////////////////////////////////////////////////////////////////
// Create and populate jail filesystem from release
dstDset := fmt.Sprintf("%s/jails/%s", ds.ZFSDataset, jname)
fmt.Printf(" > Initialize dataset %s\n", dstDset)
sNow := time.Now().Format("20060102150405")
reldset := fmt.Sprintf("%s/releases/%s", ds.ZFSDataset, gCreateArgs.Release)
err = zfsSnapshot(reldset, sNow)
if err != nil {
fmt.Printf("ERROR Creating snapshot of %s: %s\n", reldset, err.Error())
return
}
err = zfsCopy(fmt.Sprintf("%s@%s", reldset, sNow), dstDset)
if err != nil {
fmt.Printf("ERROR sending snapshot to %s: %s\n", dstDset, err.Error())
return
}
// Remove snapshot of release, then snapshot of destination dataset
err = zfsDestroy(fmt.Sprintf("%s@%s", reldset, sNow))
if err != nil {
fmt.Printf("ERROR destroying snapshot %s: %s\n", reldset, err.Error())
return
}
err = zfsDestroy(fmt.Sprintf("%s@%s", dstDset, sNow))
if err != nil {
fmt.Printf("ERROR destroying snapshot %s: %s\n", dstDset, err.Error())
return
}
dstRootDset := fmt.Sprintf("%s/jails/%s/root", ds.ZFSDataset, jname)
fmt.Printf(" > Initialize dataset %s\n", dstRootDset)
relrootdset := fmt.Sprintf("%s/releases/%s/root", ds.ZFSDataset, gCreateArgs.Release)
err = zfsSnapshot(relrootdset, sNow)
if err != nil {
fmt.Printf("ERROR Creating snapshot of %s: %s\n", relrootdset, err.Error())
return
}
err = zfsCopy(fmt.Sprintf("%s@%s", relrootdset, sNow), dstRootDset)
if err != nil {
fmt.Printf("ERROR sending snapshot to %s: %s\n", dstRootDset, err.Error())
return
}
// Remove snapshot of release, then snapshot of destination dataset
err = zfsDestroy(fmt.Sprintf("%s@%s", relrootdset, sNow))
if err != nil {
fmt.Printf("ERROR destroying snapshot %s: %s\n", relrootdset, err.Error())
return
}
err = zfsDestroy(fmt.Sprintf("%s@%s", dstRootDset, sNow))
if err != nil {
fmt.Printf("ERROR destroying snapshot %s: %s\n", dstRootDset, err.Error())
return
}
///////////////////////////////////////////////////////////////////////
// Copy defaults.json...
jailConfPath := fmt.Sprintf("%s/jails/%s/config.json", ds.Mountpoint, jname)
err = copyFile(fmt.Sprintf("%s/defaults.json", ds.Mountpoint),
jailConfPath)
if err != nil {
fmt.Printf("ERROR creating config.json: %s\n", err.Error())
return
}
///////////////////////////////////////////////////////////////////////
// ... and update it
jailConf, err := getJailConfig(jailConfPath)
if err != nil {
log.Println("ERROR reading jail config from %s", jailConfPath)
}
// Build jail object from config
jailRootPath := fmt.Sprintf("%s/jails/%s/%s", ds.Mountpoint, jname, "root")
j := Jail{
Name: jailConf.Host_hostuuid,
Config: jailConf,
ConfigPath: jailConfPath,
Datastore: ds.Name,
RootPath: jailRootPath,
Running: false,
}
j.Config.Release = gCreateArgs.Release
j.Config.Host_hostname = jname
j.Config.Host_hostuuid = jname
j.Config.Jailtype = "jail"
j.WriteConfigToDisk(false)
///////////////////////////////////////////////////////////////////////
// Create fstab
fstabHandle, err := os.Create(fmt.Sprintf("%s/jails/%s/fstab", ds.Mountpoint, jname))
if err != nil {
fmt.Printf("ERROR creating fstab: %s", err.Error())
return
}
defer fstabHandle.Close()
fmt.Printf(" > Jail created!\n")
}
}
}