diff --git a/config/config.go b/config/config.go index 19318db3..7c2c760d 100644 --- a/config/config.go +++ b/config/config.go @@ -41,6 +41,7 @@ type Config struct { SystemDockerArgs []string `json:"systemDockerArgs,omitempty"` UserContainers []ContainerConfig `json:"userContainser,omitempty"` UserInit string `json:"userInit,omitempty"` + DockerBin string `json:"dockerBin,omitempty"` Modules []string `json:"modules,omitempty"` Respawn []string `json:"respawn,omitempty"` } @@ -70,6 +71,7 @@ func LoadConfig() (*Config, error) { func NewConfig() *Config { return &Config{ ConsoleContainer: "console", + DockerBin: "/usr/bin/docker", Debug: true, DockerEndpoint: "unix:/var/run/docker.sock", Dns: []string{ @@ -79,7 +81,7 @@ func NewConfig() *Config { ImagesPath: "/", ImagesPattern: "images*.tar", StateRequired: false, - StateDev: "/dev/vda", + StateDev: "LABEL=RANCHER_STATE", StateDevFSType: "ext4", SysInit: "/sbin/init-sys", SystemDockerArgs: []string{"docker", "-d", "-s", "overlay", "-b", "none"}, @@ -141,6 +143,9 @@ func NewConfig() *Config { "--volume", "/init:/usr/bin/system-docker:ro", "--volume", "/init:/usr/bin/respawn:ro", "--volume", "/var/run/docker.sock:/var/run/system-docker.sock:ro", + "--volume", "/sbin/poweroff:/sbin/poweroff:ro", + "--volume", "/sbin/reboot:/sbin/reboot:ro", + "--volume", "/sbin/halt:/sbin/halt:ro", "--volumes-from", "system-state", "--net", "host", "--pid", "host", diff --git a/init/init.go b/init/init.go index 524a3572..3d9d0c65 100644 --- a/init/init.go +++ b/init/init.go @@ -55,6 +55,9 @@ var ( "/sbin/modprobe": "/busybox", "/var/run": "/run", DOCKER: "/docker", + "/sbin/poweroff": "/init", + "/sbin/halt": "/init", + "/sbin/reboot": "/init", } ) @@ -213,17 +216,36 @@ func MainInit() { func mountState(cfg *config.Config) error { var err error - if len(cfg.StateDev) == 0 { - log.Debugf("State will not be persisted") - err = util.Mount("none", STATE, "tmpfs", "") - } else { - log.Debugf("Mounting state device %s to %s", cfg.StateDev, STATE) - err = util.Mount(cfg.StateDev, STATE, cfg.StateDevFSType, "") + + dev := util.ResolveDevice(cfg.StateDev) + log.Debugf("Mounting state device %s", dev) + + fsType := cfg.StateDevFSType + log.Debugf("FsType has been set to %s", fsType) + if fsType == "auto" { + actualFsType, fsErr := util.GetFsType(dev) + if fsErr != nil { + return fsErr + } + fsType = actualFsType + } + err = util.Mount(dev, STATE, fsType, "") + + if err != nil { + if cfg.StateRequired { + return err + } else { + log.Debugf("State will not be persisted") + err = util.Mount("none", STATE, "tmpfs", "") + if err != nil { + return err + } + } } - //if err != nil { - return err - //} + if err != nil { + return err + } //for _, i := range []string{"docker", "images"} { // dir := path.Join(STATE, i) diff --git a/main.go b/main.go index ef6bb34b..42963ea8 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/reexec" osInit "github.com/rancherio/os/init" + "github.com/rancherio/os/power" "github.com/rancherio/os/respawn" "github.com/rancherio/os/sysinit" "github.com/rancherio/os/user" @@ -18,8 +19,10 @@ func main() { reexec.Register("/sbin/init-sys", sysinit.SysInit) reexec.Register("/usr/bin/system-docker", user.SystemDocker) reexec.Register("system-docker", user.SystemDocker) + reexec.Register("poweroff", power.PowerOff) + reexec.Register("reboot", power.Reboot) + reexec.Register("halt", power.Halt) reexec.Register("respawn", respawn.Main) - if !reexec.Init() { log.Fatalf("Failed to find an entry point for %s", os.Args[0]) } diff --git a/power/power.go b/power/power.go new file mode 100644 index 00000000..58409941 --- /dev/null +++ b/power/power.go @@ -0,0 +1,151 @@ +package power + +import ( + "bufio" + "errors" + "os" + "strconv" + "syscall" + "strings" + + "github.com/fsouza/go-dockerclient" +) + + +const ( + dockerPath = "unix:///var/run/system-docker.sock" + dockerCGroupsFile = "/proc/self/cgroup" +) + +func PowerOff() { + reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF) +} + +func Reboot() { + reboot(syscall.LINUX_REBOOT_CMD_RESTART) +} + +func Halt() { + reboot(syscall.LINUX_REBOOT_CMD_HALT) +} + +func reboot(code int) { + err := shutDownContainers() + if err != nil { + panic(err) + } + err = syscall.Reboot(code) + if err != nil { + panic(err) + } +} + +func shutDownContainers() error { + var err error + shutDown := true + timeout := uint(0) + for i := range os.Args { + arg := os.Args[i] + if arg == "-f" || arg == "--f" || arg == "--force" { + shutDown = false + } + if arg == "-t" || arg == "--t" || arg == "--timeout" { + if len(os.Args) > i+1 { + t, er := strconv.Atoi(os.Args[i+1]) + if er != nil { + return err + } + timeout = uint(t) + } else { + panic("please specify a timeout") + } + } + } + if !shutDown { + return nil + } + client, err := docker.NewClient(dockerPath) + + if err != nil { + return err + } + + opts := docker.ListContainersOptions{All: true, Filters: map[string][]string{"status": []string{"running"}}} + var containers []docker.APIContainers + + containers, err = client.ListContainers(opts) + + if err != nil { + return err + } + + currentContainerId, err := getCurrentContainerId() + + if err != nil { + return err + } + + var stopErrorStrings []string + + for i := range containers { + if containers[i].ID == currentContainerId { + continue + } + stopErr := client.StopContainer(containers[i].ID, timeout) + if stopErr != nil { + stopErrorStrings = append(stopErrorStrings, " [" + containers[i].ID + "] " +stopErr.Error()) + } + } + + var waitErrorStrings []string + + for i := range containers { + if containers[i].ID == currentContainerId { + continue + } + _, waitErr := client.WaitContainer(containers[i].ID) + if waitErr != nil { + waitErrorStrings = append(waitErrorStrings, " [" + containers[i].ID + "] " + waitErr.Error()) + } + } + + if len(waitErrorStrings) != 0 || len(stopErrorStrings) != 0 { + return errors.New("error while stopping \n1. STOP Errors [" + strings.Join(stopErrorStrings, ",") + "] \n2. WAIT Errors [" + strings.Join(waitErrorStrings, ",") + "]") + } + + return nil +} + + +func getCurrentContainerId() (string, error) { + file, err := os.Open(dockerCGroupsFile) + + if err != nil { + return "", err + } + + fileReader := bufio.NewScanner(file) + if !fileReader.Scan() { + return "", errors.New("Empty file /proc/self/cgroup") + } + line := fileReader.Text() + parts := strings.Split(line, "/") + + while len(parts) != 3 { + if !fileReader.Scan() { + return "", errors.New("Found no docker cgroups") + } + line = fileReader.Text() + parts = strings.Split(line, "/") + if len(parts) == 3 { + if strings.HasSuffix(parts[1], "docker") { + break + } else { + parts = nil + } + } + } + + return parts[len(parts)-1:][0], nil +} + diff --git a/scripts/bootstrap b/scripts/bootstrap index d460226d..3abb7e85 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -5,7 +5,7 @@ set -x cd $(dirname $0)/.. apt-get update -apt-get install -y curl rsync build-essential syslinux xorriso +apt-get install -y curl rsync build-essential syslinux xorriso libblkid-dev curl -sL https://github.com/ibuildthecloud/docker/releases/download/v1.5.0-rancher-2/docker-1.5.0 > /usr/bin/docker diff --git a/util/cutil.go b/util/cutil.go new file mode 100644 index 00000000..1261b44a --- /dev/null +++ b/util/cutil.go @@ -0,0 +1,41 @@ +package util + +/* +#cgo LDFLAGS: -lmount -lblkid -luuid +#include +#include +#include +*/ +import "C" +import "unsafe" + +import ( + "errors" +) + +func ResolveDevice(spec string) string { + cSpec := C.CString(spec) + defer C.free(unsafe.Pointer(cSpec)) + cString := C.blkid_evaluate_spec(cSpec, nil) + defer C.free(unsafe.Pointer(cString)) + return C.GoString(cString) +} + +func GetFsType(device string) (string, error) { + var ambi * C.int + cDevice := C.CString(device) + defer C.free(unsafe.Pointer(cDevice)) + cString := C.mnt_get_fstype(cDevice, ambi, nil) + defer C.free(unsafe.Pointer(cString)) + if cString != nil { + return C.GoString(cString), nil + } + return "", errors.New("Error while getting fstype") +} + +func intToBool(value C.int) bool { + if value == 0 { + return false + } + return true +}