2015-02-12 23:22:48 +00:00
|
|
|
package power
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"errors"
|
|
|
|
"os"
|
2015-03-15 04:31:11 +00:00
|
|
|
"path/filepath"
|
2015-02-12 23:22:48 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2015-02-17 05:09:26 +00:00
|
|
|
"syscall"
|
2015-02-12 23:22:48 +00:00
|
|
|
|
2015-02-21 20:22:59 +00:00
|
|
|
log "github.com/Sirupsen/logrus"
|
2015-03-15 04:31:11 +00:00
|
|
|
dockerClient "github.com/fsouza/go-dockerclient"
|
|
|
|
|
2015-10-12 11:50:17 +00:00
|
|
|
"github.com/rancher/os/docker"
|
2015-02-12 23:22:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2015-03-15 04:31:11 +00:00
|
|
|
DOCKER_CGROUPS_FILE = "/proc/self/cgroup"
|
2015-02-12 23:22:48 +00:00
|
|
|
)
|
|
|
|
|
2015-03-19 22:36:40 +00:00
|
|
|
func runDocker(name string) error {
|
2015-03-15 04:31:11 +00:00
|
|
|
if os.ExpandEnv("${IN_DOCKER}") == "true" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
client, err := docker.NewSystemClient()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-03-19 22:36:40 +00:00
|
|
|
cmd := []string{name}
|
|
|
|
|
|
|
|
if name == "" {
|
|
|
|
name = filepath.Base(os.Args[0])
|
|
|
|
cmd = os.Args
|
|
|
|
}
|
|
|
|
|
2015-03-15 04:31:11 +00:00
|
|
|
exiting, err := client.InspectContainer(name)
|
|
|
|
if exiting != nil {
|
|
|
|
err := client.RemoveContainer(dockerClient.RemoveContainerOptions{
|
|
|
|
ID: exiting.ID,
|
|
|
|
Force: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
currentContainerId, err := getCurrentContainerId()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
currentContainer, err := client.InspectContainer(currentContainerId)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
powerContainer, err := client.CreateContainer(dockerClient.CreateContainerOptions{
|
|
|
|
Name: name,
|
|
|
|
Config: &dockerClient.Config{
|
|
|
|
Image: currentContainer.Config.Image,
|
2015-03-19 22:36:40 +00:00
|
|
|
Cmd: cmd,
|
2015-03-15 04:31:11 +00:00
|
|
|
Env: []string{
|
|
|
|
"IN_DOCKER=true",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
HostConfig: &dockerClient.HostConfig{
|
|
|
|
PidMode: "host",
|
|
|
|
VolumesFrom: []string{
|
|
|
|
currentContainer.ID,
|
|
|
|
},
|
|
|
|
Privileged: true,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
client.AttachToContainer(dockerClient.AttachToContainerOptions{
|
|
|
|
Container: powerContainer.ID,
|
|
|
|
OutputStream: os.Stdout,
|
|
|
|
ErrorStream: os.Stderr,
|
|
|
|
Stderr: true,
|
|
|
|
Stdout: true,
|
|
|
|
})
|
|
|
|
}()
|
|
|
|
|
|
|
|
err = client.StartContainer(powerContainer.ID, powerContainer.HostConfig)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = client.WaitContainer(powerContainer.ID)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
os.Exit(0)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-03-19 22:36:40 +00:00
|
|
|
func common(name string) {
|
2015-02-21 20:22:59 +00:00
|
|
|
if os.Geteuid() != 0 {
|
2015-03-15 04:31:11 +00:00
|
|
|
log.Fatalf("%s: Need to be root", os.Args[0])
|
2015-02-21 20:22:59 +00:00
|
|
|
}
|
2015-03-15 04:31:11 +00:00
|
|
|
|
2015-03-19 22:36:40 +00:00
|
|
|
if err := runDocker(name); err != nil {
|
2015-03-15 04:31:11 +00:00
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func PowerOff() {
|
2015-03-19 22:36:40 +00:00
|
|
|
common("poweroff")
|
2015-02-12 23:22:48 +00:00
|
|
|
reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Reboot() {
|
2015-03-19 22:36:40 +00:00
|
|
|
common("reboot")
|
2015-02-12 23:22:48 +00:00
|
|
|
reboot(syscall.LINUX_REBOOT_CMD_RESTART)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Halt() {
|
2015-03-19 22:36:40 +00:00
|
|
|
common("halt")
|
2015-02-12 23:22:48 +00:00
|
|
|
reboot(syscall.LINUX_REBOOT_CMD_HALT)
|
|
|
|
}
|
|
|
|
|
2015-09-10 07:33:15 +00:00
|
|
|
func reboot(code uint) {
|
2015-02-12 23:22:48 +00:00
|
|
|
err := shutDownContainers()
|
|
|
|
if err != nil {
|
2015-02-23 21:52:11 +00:00
|
|
|
log.Error(err)
|
2015-02-12 23:22:48 +00:00
|
|
|
}
|
2015-03-15 04:31:11 +00:00
|
|
|
|
|
|
|
syscall.Sync()
|
|
|
|
|
2015-09-10 07:33:15 +00:00
|
|
|
err = syscall.Reboot(int(code))
|
2015-02-12 23:22:48 +00:00
|
|
|
if err != nil {
|
2015-02-21 20:22:59 +00:00
|
|
|
log.Fatal(err)
|
2015-02-12 23:22:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func shutDownContainers() error {
|
|
|
|
var err error
|
|
|
|
shutDown := true
|
2015-03-15 04:31:11 +00:00
|
|
|
timeout := 2
|
|
|
|
for i, arg := range os.Args {
|
2015-02-12 23:22:48 +00:00
|
|
|
if arg == "-f" || arg == "--f" || arg == "--force" {
|
|
|
|
shutDown = false
|
|
|
|
}
|
|
|
|
if arg == "-t" || arg == "--t" || arg == "--timeout" {
|
|
|
|
if len(os.Args) > i+1 {
|
2015-03-15 04:31:11 +00:00
|
|
|
t, err := strconv.Atoi(os.Args[i+1])
|
|
|
|
if err != nil {
|
2015-02-12 23:22:48 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-03-15 04:31:11 +00:00
|
|
|
timeout = t
|
2015-02-12 23:22:48 +00:00
|
|
|
} else {
|
2015-03-15 04:31:11 +00:00
|
|
|
log.Error("please specify a timeout")
|
2015-02-12 23:22:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !shutDown {
|
|
|
|
return nil
|
|
|
|
}
|
2015-03-15 04:31:11 +00:00
|
|
|
client, err := docker.NewSystemClient()
|
2015-02-17 05:09:26 +00:00
|
|
|
|
2015-02-12 23:22:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-03-15 04:31:11 +00:00
|
|
|
opts := dockerClient.ListContainersOptions{
|
|
|
|
All: true,
|
|
|
|
Filters: map[string][]string{
|
2015-06-15 20:58:16 +00:00
|
|
|
"status": {"running"},
|
2015-03-15 04:31:11 +00:00
|
|
|
},
|
|
|
|
}
|
2015-02-12 23:22:48 +00:00
|
|
|
|
2015-03-15 04:31:11 +00:00
|
|
|
containers, err := client.ListContainers(opts)
|
2015-02-12 23:22:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
currentContainerId, err := getCurrentContainerId()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var stopErrorStrings []string
|
|
|
|
|
2015-03-15 04:31:11 +00:00
|
|
|
for _, container := range containers {
|
|
|
|
if container.ID == currentContainerId {
|
2015-02-12 23:22:48 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-03-15 04:31:11 +00:00
|
|
|
|
|
|
|
log.Infof("Stopping %s : %v", container.ID[:12], container.Names)
|
|
|
|
stopErr := client.StopContainer(container.ID, uint(timeout))
|
2015-02-12 23:22:48 +00:00
|
|
|
if stopErr != nil {
|
2015-03-15 04:31:11 +00:00
|
|
|
stopErrorStrings = append(stopErrorStrings, " ["+container.ID+"] "+stopErr.Error())
|
2015-02-12 23:22:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var waitErrorStrings []string
|
|
|
|
|
2015-03-15 04:31:11 +00:00
|
|
|
for _, container := range containers {
|
|
|
|
if container.ID == currentContainerId {
|
2015-02-12 23:22:48 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-03-15 04:31:11 +00:00
|
|
|
_, waitErr := client.WaitContainer(container.ID)
|
2015-02-12 23:22:48 +00:00
|
|
|
if waitErr != nil {
|
2015-03-15 04:31:11 +00:00
|
|
|
waitErrorStrings = append(waitErrorStrings, " ["+container.ID+"] "+waitErr.Error())
|
2015-02-12 23:22:48 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-17 05:09:26 +00:00
|
|
|
|
2015-02-12 23:22:48 +00:00
|
|
|
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) {
|
2015-03-15 04:31:11 +00:00
|
|
|
file, err := os.Open(DOCKER_CGROUPS_FILE)
|
2015-02-12 23:22:48 +00:00
|
|
|
|
|
|
|
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, "/")
|
|
|
|
|
2015-02-17 05:09:26 +00:00
|
|
|
for len(parts) != 3 {
|
2015-02-12 23:22:48 +00:00
|
|
|
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
|
|
|
|
}
|