1
0
mirror of https://github.com/rancher/os.git synced 2025-06-09 23:15:32 +00:00
os/cmd/control/os.go

297 lines
6.3 KiB
Go
Raw Normal View History

package control
import (
"fmt"
"io/ioutil"
"net/http"
2015-03-18 12:34:23 +00:00
"net/url"
2015-03-19 21:56:41 +00:00
"os"
2015-03-18 12:34:23 +00:00
"strings"
2016-05-24 00:21:28 +00:00
"golang.org/x/net/context"
yaml "github.com/cloudfoundry-incubator/candiedyaml"
"github.com/rancher/os/log"
2015-03-18 12:34:23 +00:00
"github.com/codegangsta/cli"
2016-05-24 00:21:28 +00:00
dockerClient "github.com/docker/engine-api/client"
composeConfig "github.com/docker/libcompose/config"
"github.com/docker/libcompose/project/options"
"github.com/rancher/os/cmd/power"
"github.com/rancher/os/compose"
"github.com/rancher/os/config"
"github.com/rancher/os/docker"
)
2015-03-18 12:34:23 +00:00
type Images struct {
Current string `yaml:"current,omitempty"`
Available []string `yaml:"available,omitempty"`
}
func osSubcommands() []cli.Command {
return []cli.Command{
{
Name: "upgrade",
Usage: "upgrade to latest version",
Action: osUpgrade,
Flags: []cli.Flag{
cli.BoolFlag{
2015-02-23 19:00:33 +00:00
Name: "stage, s",
Usage: "Only stage the new upgrade, don't apply it",
},
cli.StringFlag{
2015-02-23 19:00:33 +00:00
Name: "image, i",
Usage: "upgrade to a certain image",
},
2015-03-19 21:56:41 +00:00
cli.BoolFlag{
Name: "force, f",
Usage: "do not prompt for input",
},
cli.BoolFlag{
Name: "no-reboot",
Usage: "do not reboot after upgrade",
},
2016-02-27 23:30:53 +00:00
cli.BoolFlag{
Name: "kexec",
Usage: "reboot using kexec",
},
cli.StringFlag{
Name: "append",
Usage: "append additional kernel parameters",
},
cli.BoolFlag{
Name: "upgrade-console",
Usage: "upgrade console even if persistent",
},
},
},
{
2015-02-23 19:00:33 +00:00
Name: "list",
Usage: "list the current available versions",
Action: osMetaDataGet,
},
2015-04-11 15:32:15 +00:00
{
Name: "version",
Usage: "show the currently installed version",
Action: osVersion,
},
}
}
// TODO: this and the getLatestImage should probably move to utils/network and be suitably cached.
2015-03-18 12:34:23 +00:00
func getImages() (*Images, error) {
2016-11-28 08:06:00 +00:00
upgradeURL, err := getUpgradeURL()
if err != nil {
2015-03-18 12:34:23 +00:00
return nil, err
}
2015-03-18 12:34:23 +00:00
var body []byte
2016-11-28 08:06:00 +00:00
if strings.HasPrefix(upgradeURL, "/") {
body, err = ioutil.ReadFile(upgradeURL)
2015-03-18 12:34:23 +00:00
if err != nil {
return nil, err
}
} else {
2016-11-28 08:06:00 +00:00
u, err := url.Parse(upgradeURL)
2015-03-18 12:34:23 +00:00
if err != nil {
return nil, err
}
q := u.Query()
2016-11-28 08:06:00 +00:00
q.Set("current", config.Version)
2015-03-18 12:34:23 +00:00
u.RawQuery = q.Encode()
2016-11-28 08:06:00 +00:00
upgradeURL = u.String()
2015-03-18 12:34:23 +00:00
2016-11-28 08:06:00 +00:00
resp, err := http.Get(upgradeURL)
2015-03-18 12:34:23 +00:00
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
}
2015-03-18 12:34:23 +00:00
return parseBody(body)
}
2015-02-23 19:00:33 +00:00
func osMetaDataGet(c *cli.Context) error {
2015-03-18 12:34:23 +00:00
images, err := getImages()
if err != nil {
log.Fatal(err)
}
2015-03-18 12:34:23 +00:00
client, err := docker.NewSystemClient()
if err != nil {
log.Fatal(err)
}
2015-03-18 12:34:23 +00:00
cfg := config.LoadConfig()
2016-11-28 08:06:00 +00:00
runningName := cfg.Rancher.Upgrade.Image + ":" + config.Version
foundRunning := false
for i := len(images.Available) - 1; i >= 0; i-- {
image := images.Available[i]
2016-05-24 00:21:28 +00:00
_, _, err := client.ImageInspectWithRaw(context.Background(), image, false)
local := "local"
2016-05-24 00:21:28 +00:00
if dockerClient.IsErrImageNotFound(err) {
local = "remote"
}
available := "available"
if image == images.Current {
available = "latest"
2015-03-18 12:34:23 +00:00
}
var running string
if image == runningName {
foundRunning = true
running = "running"
}
fmt.Println(image, local, available, running)
}
if !foundRunning {
2016-11-28 08:06:00 +00:00
fmt.Println(config.Version, "running")
2015-03-18 12:34:23 +00:00
}
return nil
}
2015-03-18 12:34:23 +00:00
func getLatestImage() (string, error) {
images, err := getImages()
if err != nil {
return "", err
}
2015-03-18 12:34:23 +00:00
return images.Current, nil
}
func osUpgrade(c *cli.Context) error {
image := c.String("image")
if image == "" {
var err error
2015-03-18 12:34:23 +00:00
image, err = getLatestImage()
if err != nil {
log.Fatal(err)
}
2015-03-18 12:34:23 +00:00
if image == "" {
log.Fatal("Failed to find latest image")
}
}
if c.Args().Present() {
log.Fatalf("invalid arguments %v", c.Args())
}
if err := startUpgradeContainer(image, c.Bool("stage"), c.Bool("force"), !c.Bool("no-reboot"), c.Bool("kexec"), c.Bool("upgrade-console"), c.String("append")); err != nil {
2015-08-04 21:45:38 +00:00
log.Fatal(err)
}
return nil
}
func osVersion(c *cli.Context) error {
2016-11-28 08:06:00 +00:00
fmt.Println(config.Version)
return nil
2015-04-11 15:32:15 +00:00
}
func startUpgradeContainer(image string, stage, force, reboot, kexec bool, upgradeConsole bool, kernelArgs string) error {
2016-02-27 23:30:53 +00:00
command := []string{
"-t", "rancher-upgrade",
2016-11-28 08:06:00 +00:00
"-r", config.Version,
2016-02-27 23:30:53 +00:00
}
if kexec {
command = append(command, "-k")
}
kernelArgs = strings.TrimSpace(kernelArgs)
if kernelArgs != "" {
command = append(command, "-a", kernelArgs)
2016-02-27 23:30:53 +00:00
}
if upgradeConsole {
2016-05-31 21:34:04 +00:00
if err := config.Set("rancher.force_console_rebuild", true); err != nil {
log.Fatal(err)
}
}
fmt.Printf("Upgrading to %s\n", image)
confirmation := "Continue"
imageSplit := strings.Split(image, ":")
2016-11-28 08:06:00 +00:00
if len(imageSplit) > 1 && imageSplit[1] == config.Version+config.Suffix {
confirmation = fmt.Sprintf("Already at version %s. Continue anyway", imageSplit[1])
}
if !force && !yes(confirmation) {
os.Exit(1)
}
2016-05-24 00:21:28 +00:00
container, err := compose.CreateService(nil, "os-upgrade", &composeConfig.ServiceConfigV1{
2015-08-04 21:45:38 +00:00
LogDriver: "json-file",
Privileged: true,
Net: "host",
2016-02-27 23:30:53 +00:00
Pid: "host",
2015-08-04 21:45:38 +00:00
Image: image,
2016-05-24 00:21:28 +00:00
Labels: map[string]string{
2016-11-28 08:06:00 +00:00
config.ScopeLabel: config.System,
2016-05-24 00:21:28 +00:00
},
Command: command,
2015-08-04 21:45:38 +00:00
})
if err != nil {
return err
}
client, err := docker.NewSystemClient()
if err != nil {
2015-08-04 21:45:38 +00:00
return err
}
// Only pull image if not found locally
2016-05-24 00:21:28 +00:00
if _, _, err := client.ImageInspectWithRaw(context.Background(), image, false); err != nil {
if err := container.Pull(context.Background()); err != nil {
return err
}
}
if !stage {
// If there is already an upgrade container, delete it
// Up() should to this, but currently does not due to a bug
2016-05-24 00:21:28 +00:00
if err := container.Delete(context.Background(), options.Delete{}); err != nil {
return err
}
2016-06-30 23:28:32 +00:00
if err := container.Up(context.Background(), options.Up{}); err != nil {
2015-08-04 21:45:38 +00:00
return err
}
2015-03-19 21:56:41 +00:00
2016-05-24 00:21:28 +00:00
if err := container.Log(context.Background(), true); err != nil {
2015-08-04 21:45:38 +00:00
return err
2015-03-19 21:56:41 +00:00
}
2016-05-24 00:21:28 +00:00
if err := container.Delete(context.Background(), options.Delete{}); err != nil {
2016-02-25 22:02:58 +00:00
return err
}
if reboot && (force || yes("Continue with reboot")) {
2015-08-04 21:45:38 +00:00
log.Info("Rebooting")
power.Reboot()
2015-03-19 21:56:41 +00:00
}
}
2015-08-04 21:45:38 +00:00
return nil
}
2015-03-18 12:34:23 +00:00
func parseBody(body []byte) (*Images, error) {
update := &Images{}
err := yaml.Unmarshal(body, update)
if err != nil {
2015-03-18 12:34:23 +00:00
return nil, err
}
2015-03-18 12:34:23 +00:00
return update, nil
}
2016-11-28 08:06:00 +00:00
func getUpgradeURL() (string, error) {
2016-06-02 01:41:55 +00:00
cfg := config.LoadConfig()
2016-11-28 08:06:00 +00:00
return cfg.Rancher.Upgrade.URL, nil
}