mirror of
https://github.com/rancher/os.git
synced 2025-08-01 06:59:05 +00:00
reboot --kexec almost works
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
This commit is contained in:
parent
5e4b5975a9
commit
c5d4cb91c3
@ -152,7 +152,7 @@ func installAction(c *cli.Context) error {
|
||||
if cloudConfig == "" {
|
||||
if installType != "upgrade" {
|
||||
// TODO: I wonder if its plausible to merge a new cloud-config into an existing one on upgrade - so for now, i'm only turning off the warning
|
||||
log.Warn("Cloud-config not provided: you might need to provide cloud-config on bootDir with ssh_authorized_keys")
|
||||
log.Warn("Cloud-config not provided: you might need to provide cloud-config on boot with ssh_authorized_keys")
|
||||
}
|
||||
} else {
|
||||
os.MkdirAll("/opt", 0755)
|
||||
@ -410,7 +410,6 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
||||
//cloudConfig := SCRIPTS_DIR + "/conf/empty.yml" //${cloudConfig:-"${SCRIPTS_DIR}/conf/empty.yml"}
|
||||
CONSOLE := "tty0"
|
||||
baseName := "/mnt/new_img"
|
||||
bootDir := "boot/"
|
||||
kernelArgs := "printk.devkmsg=on rancher.state.dev=LABEL=RANCHER_STATE rancher.state.wait" // console="+CONSOLE
|
||||
if statedir != "" {
|
||||
kernelArgs = kernelArgs + " rancher.state.directory=" + statedir
|
||||
@ -432,12 +431,12 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
||||
case "generic":
|
||||
log.Debugf("formatAndMount")
|
||||
var err error
|
||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
||||
device, partition, err = formatAndMount(baseName, device, partition)
|
||||
if err != nil {
|
||||
log.Errorf("formatAndMount %s", err)
|
||||
return err
|
||||
}
|
||||
err = installSyslinux(device, baseName, bootDir, diskType)
|
||||
err = installSyslinux(device, baseName, diskType)
|
||||
if err != nil {
|
||||
log.Errorf("installSyslinux %s", err)
|
||||
return err
|
||||
@ -449,7 +448,7 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
||||
}
|
||||
case "arm":
|
||||
var err error
|
||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
||||
device, partition, err = formatAndMount(baseName, device, partition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -459,45 +458,45 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
||||
case "amazon-ebs-hvm":
|
||||
CONSOLE = "ttyS0"
|
||||
var err error
|
||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
||||
device, partition, err = formatAndMount(baseName, device, partition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if installType == "amazon-ebs-hvm" {
|
||||
installSyslinux(device, baseName, bootDir, diskType)
|
||||
installSyslinux(device, baseName, diskType)
|
||||
}
|
||||
//# AWS Networking recommends disabling.
|
||||
seedData(baseName, cloudConfig, FILES)
|
||||
case "googlecompute":
|
||||
CONSOLE = "ttyS0"
|
||||
var err error
|
||||
device, partition, err = formatAndMount(baseName, bootDir, device, partition)
|
||||
device, partition, err = formatAndMount(baseName, device, partition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installSyslinux(device, baseName, bootDir, diskType)
|
||||
installSyslinux(device, baseName, diskType)
|
||||
seedData(baseName, cloudConfig, FILES)
|
||||
case "noformat":
|
||||
var err error
|
||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
||||
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installSyslinux(device, baseName, bootDir, diskType)
|
||||
installSyslinux(device, baseName, diskType)
|
||||
if err := os.MkdirAll(filepath.Join(baseName, statedir), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
case "raid":
|
||||
var err error
|
||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
||||
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installSyslinux(device, baseName, bootDir, diskType)
|
||||
installSyslinux(device, baseName, diskType)
|
||||
case "bootstrap":
|
||||
CONSOLE = "ttyS0"
|
||||
var err error
|
||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, true)
|
||||
device, partition, err = install.MountDevice(baseName, device, partition, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -507,39 +506,39 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
||||
fallthrough
|
||||
case "upgrade":
|
||||
var err error
|
||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
||||
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("upgrading - %s, %s, %s, %s", device, baseName, bootDir, diskType)
|
||||
log.Debugf("upgrading - %s, %s, %s, %s", device, baseName, diskType)
|
||||
// TODO: detect pv-grub, and don't kill it with syslinux
|
||||
upgradeBootloader(device, baseName, bootDir, diskType)
|
||||
upgradeBootloader(device, baseName, diskType)
|
||||
default:
|
||||
return fmt.Errorf("unexpected install type %s", installType)
|
||||
}
|
||||
kernelArgs = kernelArgs + " console=" + CONSOLE
|
||||
|
||||
if kappend == "" {
|
||||
preservedAppend, _ := ioutil.ReadFile(filepath.Join(baseName, bootDir+"append"))
|
||||
preservedAppend, _ := ioutil.ReadFile(filepath.Join(baseName, install.BootDir+"append"))
|
||||
kappend = string(preservedAppend)
|
||||
} else {
|
||||
ioutil.WriteFile(filepath.Join(baseName, bootDir+"append"), []byte(kappend), 0644)
|
||||
ioutil.WriteFile(filepath.Join(baseName, install.BootDir+"append"), []byte(kappend), 0644)
|
||||
}
|
||||
|
||||
if installType == "amazon-ebs-pv" {
|
||||
menu := install.BootVars{
|
||||
BaseName: baseName,
|
||||
BootDir: bootDir,
|
||||
BootDir: install.BootDir,
|
||||
Timeout: 0,
|
||||
Fallback: 0, // need to be conditional on there being a 'rollback'?
|
||||
Entries: []install.MenuEntry{
|
||||
install.MenuEntry{"RancherOS-current", bootDir, VERSION, kernelArgs, kappend},
|
||||
install.MenuEntry{"RancherOS-current", install.BootDir, VERSION, kernelArgs, kappend},
|
||||
},
|
||||
}
|
||||
install.PvGrubConfig(menu)
|
||||
}
|
||||
log.Debugf("installRancher")
|
||||
currentCfg, err := installRancher(baseName, bootDir, VERSION, DIST, kernelArgs+" "+kappend)
|
||||
currentCfg, err := installRancher(baseName, VERSION, DIST, kernelArgs+" "+kappend)
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
@ -548,55 +547,12 @@ func layDownOS(image, installType, cloudConfig, device, partition, statedir, kap
|
||||
|
||||
// Used by upgrade
|
||||
if kexec {
|
||||
vmlinuzFile, initrdFile, err := readSyslinuxCfg(currentCfg)
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
// kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="${kernelArgs} ${APPEND}" -f
|
||||
cmd := exec.Command(
|
||||
"kexec",
|
||||
"-l", DIST+"/"+vmlinuzFile,
|
||||
"--initrd", DIST+"/"+initrdFile,
|
||||
"--append", "'"+kernelArgs+" "+kappend+"'",
|
||||
"-f")
|
||||
log.Debugf("Run(%#v)", cmd)
|
||||
cmd.Stderr = os.Stderr
|
||||
if _, err := cmd.Output(); err != nil {
|
||||
log.Errorf("Failed to kexec: %s", err)
|
||||
return err
|
||||
}
|
||||
log.Infof("kexec'd to new install")
|
||||
power.Kexec(currentCfg, kernelArgs+" "+kappend)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readSyslinuxCfg(currentCfg string) (string, string, error) {
|
||||
vmlinuzFile := ""
|
||||
initrdFile := ""
|
||||
// Need to parse currentCfg for the lines:
|
||||
// KERNEL ../vmlinuz-4.9.18-rancher^M
|
||||
// INITRD ../initrd-41e02e6-dirty^M
|
||||
buf, err := ioutil.ReadFile(currentCfg)
|
||||
if err != nil {
|
||||
return vmlinuzFile, initrdFile, err
|
||||
}
|
||||
s := bufio.NewScanner(bytes.NewReader(buf))
|
||||
for s.Scan() {
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if strings.HasPrefix(line, "KERNEL") {
|
||||
vmlinuzFile = strings.TrimSpace(strings.TrimPrefix(line, "KERNEL"))
|
||||
vmlinuzFile = filepath.Base(vmlinuzFile)
|
||||
}
|
||||
if strings.HasPrefix(line, "INITRD") {
|
||||
initrdFile = strings.TrimSpace(strings.TrimPrefix(line, "INITRD"))
|
||||
initrdFile = filepath.Base(initrdFile)
|
||||
}
|
||||
}
|
||||
return vmlinuzFile, initrdFile, err
|
||||
}
|
||||
|
||||
// files is an array of 'sourcefile:destination' - but i've not seen any examples of it being used.
|
||||
func seedData(baseName, cloudData string, files []string) error {
|
||||
log.Debugf("seedData")
|
||||
@ -772,59 +728,7 @@ func formatdevice(device, partition string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func mountdevice(baseName, bootDir, device, partition string, raw bool) (string, string, error) {
|
||||
log.Debugf("mountdevice %s, raw %v", partition, raw)
|
||||
|
||||
if partition == "" {
|
||||
if raw {
|
||||
log.Debugf("util.Mount (raw) %s, %s", partition, baseName)
|
||||
|
||||
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
||||
log.Debugf("Run(%v)", cmd)
|
||||
cmd.Stderr = os.Stderr
|
||||
device := ""
|
||||
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
||||
if out, err := cmd.Output(); err == nil {
|
||||
device = "/dev/" + strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
log.Debugf("mountdevice return -> d: %s, p: %s", device, partition)
|
||||
return device, partition, util.Mount(partition, baseName, "", "")
|
||||
}
|
||||
|
||||
//rootfs := partition
|
||||
// Don't use ResolveDevice - it can fail, whereas `blkid -L LABEL` works more often
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
if d, _ := util.Blkid("RANCHER_BOOT"); d != "" {
|
||||
partition = d
|
||||
baseName = filepath.Join(baseName, "boot")
|
||||
} else {
|
||||
if dev := util.ResolveDevice(cfg.Rancher.State.Dev); dev != "" {
|
||||
// try the rancher.state.dev setting
|
||||
partition = dev
|
||||
} else {
|
||||
if d, _ := util.Blkid("RANCHER_STATE"); d != "" {
|
||||
partition = d
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
||||
log.Debugf("Run(%v)", cmd)
|
||||
cmd.Stderr = os.Stderr
|
||||
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
||||
if out, err := cmd.Output(); err == nil {
|
||||
device = "/dev/" + strings.TrimSpace(string(out))
|
||||
}
|
||||
}
|
||||
os.MkdirAll(baseName, 0755)
|
||||
cmd := exec.Command("mount", partition, baseName)
|
||||
//cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
log.Debugf("mountdevice return2 -> d: %s, p: %s", device, partition)
|
||||
return device, partition, cmd.Run()
|
||||
}
|
||||
|
||||
func formatAndMount(baseName, bootDir, device, partition string) (string, string, error) {
|
||||
func formatAndMount(baseName, device, partition string) (string, string, error) {
|
||||
log.Debugf("formatAndMount")
|
||||
|
||||
err := formatdevice(device, partition)
|
||||
@ -832,31 +736,14 @@ func formatAndMount(baseName, bootDir, device, partition string) (string, string
|
||||
log.Errorf("formatdevice %s", err)
|
||||
return device, partition, err
|
||||
}
|
||||
device, partition, err = mountdevice(baseName, bootDir, device, partition, false)
|
||||
device, partition, err = install.MountDevice(baseName, device, partition, false)
|
||||
if err != nil {
|
||||
log.Errorf("mountdevice %s", err)
|
||||
return device, partition, err
|
||||
}
|
||||
//err = createbootDirs(baseName, bootDir)
|
||||
//if err != nil {
|
||||
// log.Errorf("createbootDirs %s", err)
|
||||
// return bootDir, err
|
||||
//}
|
||||
return device, partition, nil
|
||||
}
|
||||
|
||||
func NOPEcreatebootDir(baseName, bootDir string) error {
|
||||
log.Debugf("createbootDirs")
|
||||
|
||||
if err := os.MkdirAll(filepath.Join(baseName, bootDir+"grub"), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(baseName, bootDir+"syslinux"), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBootable(device, diskType string) error {
|
||||
// TODO make conditional - if there is a bootable device already, don't break it
|
||||
// TODO: make RANCHER_BOOT bootable - it might not be device 1
|
||||
@ -875,10 +762,10 @@ func setBootable(device, diskType string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
||||
func upgradeBootloader(device, baseName, diskType string) error {
|
||||
log.Debugf("start upgradeBootloader")
|
||||
|
||||
grubDir := filepath.Join(baseName, bootDir+"grub")
|
||||
grubDir := filepath.Join(baseName, install.BootDir+"grub")
|
||||
if _, err := os.Stat(grubDir); os.IsNotExist(err) {
|
||||
log.Debugf("%s does not exist - no need to upgrade bootloader", grubDir)
|
||||
// we've already upgraded
|
||||
@ -886,12 +773,12 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
||||
return nil
|
||||
}
|
||||
// deal with systems which were previously upgraded, then rolled back, and are now being re-upgraded
|
||||
grubBackup := filepath.Join(baseName, bootDir+"grub_backup")
|
||||
grubBackup := filepath.Join(baseName, install.BootDir+"grub_backup")
|
||||
if err := os.RemoveAll(grubBackup); err != nil {
|
||||
log.Errorf("RemoveAll (%s): %s", grubBackup, err)
|
||||
return err
|
||||
}
|
||||
backupSyslinuxDir := filepath.Join(baseName, bootDir+"syslinux_backup")
|
||||
backupSyslinuxDir := filepath.Join(baseName, install.BootDir+"syslinux_backup")
|
||||
if _, err := os.Stat(backupSyslinuxDir); !os.IsNotExist(err) {
|
||||
backupSyslinuxLdlinuxSys := filepath.Join(backupSyslinuxDir, "ldlinux.sys")
|
||||
if _, err := os.Stat(backupSyslinuxLdlinuxSys); !os.IsNotExist(err) {
|
||||
@ -914,7 +801,7 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
syslinuxDir := filepath.Join(baseName, bootDir+"syslinux")
|
||||
syslinuxDir := filepath.Join(baseName, install.BootDir+"syslinux")
|
||||
// it seems that v0.5.0 didn't have a syslinux dir, while 0.7 does
|
||||
if _, err := os.Stat(syslinuxDir); !os.IsNotExist(err) {
|
||||
if err := os.Rename(syslinuxDir, backupSyslinuxDir); err != nil {
|
||||
@ -935,15 +822,15 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
||||
|
||||
cfg = strings.Replace(cfg, "current", "previous", -1)
|
||||
// TODO consider removing the APPEND line - as the global.cfg should have the same result
|
||||
ioutil.WriteFile(filepath.Join(baseName, bootDir, "linux-current.cfg"), []byte(cfg), 0644)
|
||||
ioutil.WriteFile(filepath.Join(baseName, install.BootDir, "linux-current.cfg"), []byte(cfg), 0644)
|
||||
|
||||
lines := strings.Split(cfg, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "APPEND") {
|
||||
log.Errorf("write new (%s) %s", filepath.Join(baseName, bootDir, "global.cfg"), err)
|
||||
log.Errorf("write new (%s) %s", filepath.Join(baseName, install.BootDir, "global.cfg"), err)
|
||||
// TODO: need to append any extra's the user specified
|
||||
ioutil.WriteFile(filepath.Join(baseName, bootDir, "global.cfg"), []byte(cfg), 0644)
|
||||
ioutil.WriteFile(filepath.Join(baseName, install.BootDir, "global.cfg"), []byte(cfg), 0644)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -951,10 +838,10 @@ func upgradeBootloader(device, baseName, bootDir, diskType string) error {
|
||||
}
|
||||
}
|
||||
|
||||
return installSyslinux(device, baseName, bootDir, diskType)
|
||||
return installSyslinux(device, baseName, diskType)
|
||||
}
|
||||
|
||||
func installSyslinux(device, baseName, bootDir, diskType string) error {
|
||||
func installSyslinux(device, baseName, diskType string) error {
|
||||
log.Debugf("installSyslinux(%s)", device)
|
||||
|
||||
mbrFile := "mbr.bin"
|
||||
@ -1004,7 +891,7 @@ func installSyslinux(device, baseName, bootDir, diskType string) error {
|
||||
}
|
||||
}
|
||||
|
||||
sysLinuxDir := filepath.Join(baseName, bootDir, "syslinux")
|
||||
sysLinuxDir := filepath.Join(baseName, install.BootDir, "syslinux")
|
||||
if err := os.MkdirAll(sysLinuxDir, 0755); err != nil {
|
||||
log.Errorf("MkdirAll(%s)): %s", sysLinuxDir, err)
|
||||
//return err
|
||||
@ -1037,13 +924,13 @@ func installSyslinux(device, baseName, bootDir, diskType string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func installRancher(baseName, bootDir, VERSION, DIST, kappend string) (string, error) {
|
||||
func installRancher(baseName, VERSION, DIST, kappend string) (string, error) {
|
||||
log.Debugf("installRancher")
|
||||
|
||||
// detect if there already is a linux-current.cfg, if so, move it to linux-previous.cfg,
|
||||
currentCfg := filepath.Join(baseName, bootDir, "linux-current.cfg")
|
||||
currentCfg := filepath.Join(baseName, install.BootDir, "linux-current.cfg")
|
||||
if _, err := os.Stat(currentCfg); !os.IsNotExist(err) {
|
||||
previousCfg := filepath.Join(baseName, bootDir, "linux-previous.cfg")
|
||||
previousCfg := filepath.Join(baseName, install.BootDir, "linux-previous.cfg")
|
||||
if _, err := os.Stat(previousCfg); !os.IsNotExist(err) {
|
||||
if err := os.Remove(previousCfg); err != nil {
|
||||
return currentCfg, err
|
||||
@ -1059,19 +946,19 @@ func installRancher(baseName, bootDir, VERSION, DIST, kappend string) (string, e
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
if err := dfs.CopyFile(filepath.Join(DIST, file.Name()), filepath.Join(baseName, bootDir), file.Name()); err != nil {
|
||||
if err := dfs.CopyFile(filepath.Join(DIST, file.Name()), filepath.Join(baseName, install.BootDir), file.Name()); err != nil {
|
||||
log.Errorf("copy %s: %s", file.Name(), err)
|
||||
//return err
|
||||
}
|
||||
}
|
||||
// the general INCLUDE syslinuxcfg
|
||||
if err := dfs.CopyFile(filepath.Join(DIST, "isolinux", "isolinux.cfg"), filepath.Join(baseName, bootDir, "syslinux"), "syslinux.cfg"); err != nil {
|
||||
if err := dfs.CopyFile(filepath.Join(DIST, "isolinux", "isolinux.cfg"), filepath.Join(baseName, install.BootDir, "syslinux"), "syslinux.cfg"); err != nil {
|
||||
log.Errorf("copy global syslinux.cfgS%s: %s", "syslinux.cfg", err)
|
||||
//return err
|
||||
}
|
||||
|
||||
// The global.cfg INCLUDE - useful for over-riding the APPEND line
|
||||
globalFile := filepath.Join(filepath.Join(baseName, bootDir), "global.cfg")
|
||||
globalFile := filepath.Join(filepath.Join(baseName, install.BootDir), "global.cfg")
|
||||
if _, err := os.Stat(globalFile); !os.IsNotExist(err) {
|
||||
err := ioutil.WriteFile(globalFile, []byte("APPEND "+kappend), 0644)
|
||||
if err != nil {
|
||||
|
@ -1,5 +1,18 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/os/config"
|
||||
"github.com/rancher/os/log"
|
||||
"github.com/rancher/os/util"
|
||||
)
|
||||
|
||||
const BootDir = "boot/"
|
||||
|
||||
type MenuEntry struct {
|
||||
Name, BootDir, Version, KernelArgs, Append string
|
||||
}
|
||||
@ -9,3 +22,55 @@ type BootVars struct {
|
||||
Fallback int
|
||||
Entries []MenuEntry
|
||||
}
|
||||
|
||||
func MountDevice(baseName, device, partition string, raw bool) (string, string, error) {
|
||||
log.Debugf("mountdevice %s, raw %v", partition, raw)
|
||||
|
||||
if partition == "" {
|
||||
if raw {
|
||||
log.Debugf("util.Mount (raw) %s, %s", partition, baseName)
|
||||
|
||||
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
||||
log.Debugf("Run(%v)", cmd)
|
||||
cmd.Stderr = os.Stderr
|
||||
device := ""
|
||||
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
||||
if out, err := cmd.Output(); err == nil {
|
||||
device = "/dev/" + strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
log.Debugf("mountdevice return -> d: %s, p: %s", device, partition)
|
||||
return device, partition, util.Mount(partition, baseName, "", "")
|
||||
}
|
||||
|
||||
//rootfs := partition
|
||||
// Don't use ResolveDevice - it can fail, whereas `blkid -L LABEL` works more often
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
if d, _ := util.Blkid("RANCHER_BOOT"); d != "" {
|
||||
partition = d
|
||||
baseName = filepath.Join(baseName, BootDir)
|
||||
} else {
|
||||
if dev := util.ResolveDevice(cfg.Rancher.State.Dev); dev != "" {
|
||||
// try the rancher.state.dev setting
|
||||
partition = dev
|
||||
} else {
|
||||
if d, _ := util.Blkid("RANCHER_STATE"); d != "" {
|
||||
partition = d
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd := exec.Command("lsblk", "-no", "pkname", partition)
|
||||
log.Debugf("Run(%v)", cmd)
|
||||
cmd.Stderr = os.Stderr
|
||||
// TODO: out can == "" - this is used to "detect software RAID" which is terrible
|
||||
if out, err := cmd.Output(); err == nil {
|
||||
device = "/dev/" + strings.TrimSpace(string(out))
|
||||
}
|
||||
}
|
||||
os.MkdirAll(baseName, 0755)
|
||||
cmd := exec.Command("mount", partition, baseName)
|
||||
//cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
|
||||
log.Debugf("mountdevice return2 -> d: %s, p: %s", device, partition)
|
||||
return device, partition, cmd.Run()
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/os/log"
|
||||
)
|
||||
@ -43,3 +47,48 @@ DEFAULT RancherOS-current
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadGlobalCfg(globalCfg string) (string, error) {
|
||||
append := ""
|
||||
buf, err := ioutil.ReadFile(globalCfg)
|
||||
if err != nil {
|
||||
return append, err
|
||||
}
|
||||
|
||||
s := bufio.NewScanner(bytes.NewReader(buf))
|
||||
for s.Scan() {
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if strings.HasPrefix(line, "APPEND") {
|
||||
append = strings.TrimSpace(strings.TrimPrefix(line, "APPEND"))
|
||||
}
|
||||
}
|
||||
return append, nil
|
||||
}
|
||||
|
||||
func ReadSyslinuxCfg(currentCfg string) (string, string, error) {
|
||||
vmlinuzFile := ""
|
||||
initrdFile := ""
|
||||
// Need to parse currentCfg for the lines:
|
||||
// KERNEL ../vmlinuz-4.9.18-rancher^M
|
||||
// INITRD ../initrd-41e02e6-dirty^M
|
||||
buf, err := ioutil.ReadFile(currentCfg)
|
||||
if err != nil {
|
||||
return vmlinuzFile, initrdFile, err
|
||||
}
|
||||
|
||||
DIST := filepath.Dir(currentCfg)
|
||||
|
||||
s := bufio.NewScanner(bytes.NewReader(buf))
|
||||
for s.Scan() {
|
||||
line := strings.TrimSpace(s.Text())
|
||||
if strings.HasPrefix(line, "KERNEL") {
|
||||
vmlinuzFile = strings.TrimSpace(strings.TrimPrefix(line, "KERNEL"))
|
||||
vmlinuzFile = filepath.Join(DIST, filepath.Base(vmlinuzFile))
|
||||
}
|
||||
if strings.HasPrefix(line, "INITRD") {
|
||||
initrdFile = strings.TrimSpace(strings.TrimPrefix(line, "INITRD"))
|
||||
initrdFile = filepath.Join(DIST, filepath.Base(initrdFile))
|
||||
}
|
||||
}
|
||||
return vmlinuzFile, initrdFile, err
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ import (
|
||||
"github.com/rancher/os/util"
|
||||
)
|
||||
|
||||
// You can't shutdown the system from a process in console because we want to stop the console container.
|
||||
// If you do that you kill yourself. So we spawn a separate container to do power operations
|
||||
// This can up because on shutdown we want ssh to gracefully die, terminating ssh connections and not just hanging tcp session
|
||||
func runDocker(name string) error {
|
||||
if os.ExpandEnv("${IN_DOCKER}") == "true" {
|
||||
return nil
|
||||
@ -100,40 +103,24 @@ func runDocker(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func common(name string) {
|
||||
func reboot(name string, force bool, code uint) {
|
||||
if os.Geteuid() != 0 {
|
||||
log.Fatalf("%s: Need to be root", os.Args[0])
|
||||
}
|
||||
|
||||
if err := runDocker(name); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Off() {
|
||||
common("poweroff")
|
||||
reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
|
||||
}
|
||||
|
||||
func Reboot() {
|
||||
common("reboot")
|
||||
reboot(syscall.LINUX_REBOOT_CMD_RESTART)
|
||||
}
|
||||
|
||||
func Halt() {
|
||||
common("halt")
|
||||
reboot(syscall.LINUX_REBOOT_CMD_HALT)
|
||||
}
|
||||
|
||||
func reboot(code uint) {
|
||||
err := shutDownContainers()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
if !force {
|
||||
if err := runDocker(name); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err := shutDownContainers()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
syscall.Sync()
|
||||
|
||||
err = syscall.Reboot(int(code))
|
||||
err := syscall.Reboot(int(code))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -1,14 +1,28 @@
|
||||
package power
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/rancher/os/cmd/control/install"
|
||||
"github.com/rancher/os/config"
|
||||
"github.com/rancher/os/log"
|
||||
"github.com/rancher/os/util"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
var (
|
||||
haltFlag bool
|
||||
poweroffFlag bool
|
||||
rebootFlag bool
|
||||
forceFlag bool
|
||||
kexecFlag bool
|
||||
)
|
||||
|
||||
func Shutdown() {
|
||||
log.InitLogger()
|
||||
app := cli.NewApp()
|
||||
|
||||
@ -16,33 +30,181 @@ func Main() {
|
||||
app.Usage = "Control and configure RancherOS"
|
||||
app.Version = config.Version
|
||||
app.Author = "Rancher Labs, Inc."
|
||||
app.Email = "sid@rancher.com"
|
||||
app.EnableBashCompletion = true
|
||||
app.Action = shutdown
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "r, R",
|
||||
Usage: "reboot after shutdown",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "h",
|
||||
Usage: "halt the system",
|
||||
// --no-wall
|
||||
// Do not send wall message before halt, power-off,
|
||||
// reboot.
|
||||
|
||||
// halt, poweroff, reboot ONLY
|
||||
// -f, --force
|
||||
// Force immediate halt, power-off, reboot. Do not
|
||||
// contact the init system.
|
||||
cli.BoolFlag{
|
||||
Name: "f, force",
|
||||
Usage: "Force immediate halt, power-off, reboot. Do not contact the init system.",
|
||||
Destination: &forceFlag,
|
||||
},
|
||||
|
||||
// -w, --wtmp-only
|
||||
// Only write wtmp shutdown entry, do not actually
|
||||
// halt, power-off, reboot.
|
||||
|
||||
// -d, --no-wtmp
|
||||
// Do not write wtmp shutdown entry.
|
||||
|
||||
// -n, --no-sync
|
||||
// Don't sync hard disks/storage media before halt,
|
||||
// power-off, reboot.
|
||||
|
||||
// shutdown ONLY
|
||||
// -h
|
||||
// Equivalent to --poweroff, unless --halt is
|
||||
// specified.
|
||||
|
||||
// -k
|
||||
// Do not halt, power-off, reboot, just write wall
|
||||
// message.
|
||||
|
||||
// -c
|
||||
// Cancel a pending shutdown. This may be used
|
||||
// cancel the effect of an invocation of shutdown
|
||||
// with a time argument that is not "+0" or "now".
|
||||
|
||||
}
|
||||
app.HideHelp = true
|
||||
// -H, --halt
|
||||
// Halt the machine.
|
||||
if app.Name == "halt" {
|
||||
app.Flags = append(app.Flags, cli.BoolTFlag{
|
||||
Name: "H, halt",
|
||||
Usage: "halt the machine",
|
||||
Destination: &haltFlag,
|
||||
})
|
||||
} else {
|
||||
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||
Name: "H, halt",
|
||||
Usage: "halt the machine",
|
||||
Destination: &haltFlag,
|
||||
})
|
||||
}
|
||||
// -P, --poweroff
|
||||
// Power-off the machine (the default for shutdown cmd).
|
||||
if app.Name == "poweroff" {
|
||||
app.Flags = append(app.Flags, cli.BoolTFlag{
|
||||
Name: "P, poweroff",
|
||||
Usage: "halt the machine",
|
||||
Destination: &poweroffFlag,
|
||||
})
|
||||
} else {
|
||||
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||
Name: "P, poweroff",
|
||||
Usage: "halt the machine",
|
||||
Destination: &poweroffFlag,
|
||||
})
|
||||
}
|
||||
// -r, --reboot
|
||||
// Reboot the machine.
|
||||
if app.Name == "reboot" {
|
||||
app.Flags = append(app.Flags, cli.BoolTFlag{
|
||||
Name: "r, reboot",
|
||||
Usage: "reboot after shutdown",
|
||||
Destination: &rebootFlag,
|
||||
})
|
||||
// OR? maybe implement it as a `kexec` cli tool?
|
||||
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||
Name: "kexec",
|
||||
Usage: "kexec the default kernel",
|
||||
Destination: &kexecFlag,
|
||||
})
|
||||
} else {
|
||||
app.Flags = append(app.Flags, cli.BoolFlag{
|
||||
Name: "r, reboot",
|
||||
Usage: "reboot after shutdown",
|
||||
Destination: &rebootFlag,
|
||||
})
|
||||
}
|
||||
//TODO: add the time and msg flags...
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
func shutdown(c *cli.Context) error {
|
||||
common("")
|
||||
reboot := c.String("r")
|
||||
poweroff := c.String("h")
|
||||
|
||||
if reboot == "now" {
|
||||
Reboot()
|
||||
} else if poweroff == "now" {
|
||||
Off()
|
||||
func Kexec(bootDir, append string) error {
|
||||
cfgFile := filepath.Join(bootDir, "linux-current.cfg")
|
||||
vmlinuzFile, initrdFile, err := install.ReadSyslinuxCfg(cfgFile)
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
globalCfgFile := filepath.Join(bootDir, "global.cfg")
|
||||
if append == "" {
|
||||
append, err = install.ReadGlobalCfg(globalCfgFile)
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// TODO: read global.cfg if append == ""
|
||||
// kexec -l ${DIST}/vmlinuz --initrd=${DIST}/initrd --append="${kernelArgs} ${APPEND}" -f
|
||||
cmd := exec.Command(
|
||||
"kexec",
|
||||
"-l", vmlinuzFile,
|
||||
"--initrd", initrdFile,
|
||||
"--append", "'"+append+"'",
|
||||
"-f")
|
||||
log.Debugf("Run(%#v)", cmd)
|
||||
cmd.Stderr = os.Stderr
|
||||
if _, err := cmd.Output(); err != nil {
|
||||
log.Errorf("Failed to kexec: %s", err)
|
||||
return err
|
||||
}
|
||||
log.Infof("kexec'd to new install")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reboot is used by installation / upgrade
|
||||
// TODO: add kexec option
|
||||
func Reboot() {
|
||||
reboot("reboot", false, syscall.LINUX_REBOOT_CMD_RESTART)
|
||||
}
|
||||
|
||||
func shutdown(c *cli.Context) error {
|
||||
if kexecFlag {
|
||||
if os.Geteuid() != 0 {
|
||||
log.Fatalf("%s: Need to be root", os.Args[0])
|
||||
}
|
||||
|
||||
// need to mount boot dir, or `system-docker run -v /:/host -w /host/boot` ?
|
||||
baseName := "/mnt/new_img"
|
||||
_, _, err := install.MountDevice(baseName, "", "", false)
|
||||
if err != nil {
|
||||
log.Errorf("ERROR: can't Kexec: %s", err)
|
||||
return nil
|
||||
}
|
||||
defer util.Unmount(baseName)
|
||||
return Kexec(filepath.Join(baseName, install.BootDir), "")
|
||||
}
|
||||
// the shutdown command's default is poweroff
|
||||
var powerCmd uint
|
||||
powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
|
||||
if rebootFlag {
|
||||
powerCmd = syscall.LINUX_REBOOT_CMD_RESTART
|
||||
} else if poweroffFlag {
|
||||
powerCmd = syscall.LINUX_REBOOT_CMD_POWER_OFF
|
||||
} else if haltFlag {
|
||||
powerCmd = syscall.LINUX_REBOOT_CMD_HALT
|
||||
}
|
||||
|
||||
timeArg := c.Args().Get(0)
|
||||
if c.App.Name == "shutdown" && timeArg != "" {
|
||||
if timeArg != "now" {
|
||||
err := fmt.Errorf("Sorry, can't parse '%s' as time value (only 'now' supported)", timeArg)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
// TODO: if there are more params, LOG them
|
||||
}
|
||||
|
||||
reboot(c.App.Name, forceFlag, powerCmd)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
12
main.go
12
main.go
@ -29,19 +29,21 @@ var entrypoints = map[string]func(){
|
||||
"console.sh": control.ConsoleInitMain,
|
||||
"docker": docker.Main,
|
||||
"dockerlaunch": dfs.Main,
|
||||
"halt": power.Halt,
|
||||
"init": osInit.MainInit,
|
||||
"netconf": network.Main,
|
||||
"poweroff": power.Off,
|
||||
"reboot": power.Reboot,
|
||||
"respawn": respawn.Main,
|
||||
"ros-sysinit": sysinit.Main,
|
||||
"shutdown": power.Main,
|
||||
"system-docker": systemdocker.Main,
|
||||
"wait-for-docker": wait.Main,
|
||||
"cni-glue": glue.Main,
|
||||
"bridge": bridge.Main,
|
||||
"host-local": hostlocal.Main,
|
||||
"respawn": respawn.Main,
|
||||
|
||||
// Power commands
|
||||
"halt": power.Shutdown,
|
||||
"poweroff": power.Shutdown,
|
||||
"reboot": power.Shutdown,
|
||||
"shutdown": power.Shutdown,
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
Loading…
Reference in New Issue
Block a user