mirror of
https://github.com/rancher/os.git
synced 2025-06-10 23:45:36 +00:00
198 lines
3.9 KiB
Go
198 lines
3.9 KiB
Go
|
package control
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
|
||
|
log "github.com/Sirupsen/logrus"
|
||
|
|
||
|
"github.com/codegangsta/cli"
|
||
|
"github.com/rancherio/os/cmd/power"
|
||
|
"github.com/rancherio/os/config"
|
||
|
"github.com/rancherio/os/docker"
|
||
|
)
|
||
|
|
||
|
var osChannels map[string]string
|
||
|
|
||
|
const (
|
||
|
osVersionsFile = "/var/lib/rancher/versions"
|
||
|
)
|
||
|
|
||
|
func osSubcommands() []cli.Command {
|
||
|
return []cli.Command{
|
||
|
{
|
||
|
Name: "upgrade",
|
||
|
Usage: "upgrade to latest version",
|
||
|
Action: osUpgrade,
|
||
|
Flags: []cli.Flag{
|
||
|
cli.BoolFlag{
|
||
|
Name: "stage, s",
|
||
|
Usage: "Only stage the new upgrade, don't apply it",
|
||
|
},
|
||
|
cli.StringFlag{
|
||
|
Name: "image, i",
|
||
|
Usage: "upgrade to a certain image",
|
||
|
},
|
||
|
cli.StringFlag{
|
||
|
Name: "channel, c",
|
||
|
Usage: "upgrade to the latest in a specific channel",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
Name: "list",
|
||
|
Usage: "list the current available versions",
|
||
|
Action: osMetaDataGet,
|
||
|
},
|
||
|
{
|
||
|
Name: "rollback",
|
||
|
Usage: "rollback to the previous version",
|
||
|
Action: osRollback,
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func osRollback(c *cli.Context) {
|
||
|
file, err := os.Open(osVersionsFile)
|
||
|
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
fileReader := bufio.NewScanner(file)
|
||
|
line := " "
|
||
|
for ; line[len(line)-1:] != "*"; {
|
||
|
if !fileReader.Scan() {
|
||
|
log.Error("Current version not indicated in "+ osVersionsFile)
|
||
|
}
|
||
|
line = fileReader.Text()
|
||
|
}
|
||
|
if !fileReader.Scan() {
|
||
|
log.Error("already at earliest version, please choose a version specifically using upgrade --image")
|
||
|
}
|
||
|
line = fileReader.Text()
|
||
|
//TODO: process string if required
|
||
|
|
||
|
startUpgradeContainer(line, false)
|
||
|
}
|
||
|
|
||
|
func osMetaDataGet(c *cli.Context) {
|
||
|
osChannel, ok := getChannelUrl("meta"); if !ok {
|
||
|
log.Fatal("unrecognized channel meta")
|
||
|
}
|
||
|
resp, err := http.Get(osChannel)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
body, err := ioutil.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
fmt.Print(parseBody(body, osChannel))
|
||
|
}
|
||
|
|
||
|
func osUpgrade(c *cli.Context) {
|
||
|
channel := c.String("channel")
|
||
|
|
||
|
image := c.String("image")
|
||
|
|
||
|
if image == "" {
|
||
|
var err error
|
||
|
image, err = getLatestImage(channel)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
startUpgradeContainer(image, c.Bool("stage"))
|
||
|
}
|
||
|
|
||
|
func startUpgradeContainer(image string, stage bool) {
|
||
|
container := docker.NewContainer(config.DOCKER_SYSTEM_HOST, &config.ContainerConfig{
|
||
|
Cmd: "--name=upgrade " +
|
||
|
"--privileged " +
|
||
|
"--net=host " +
|
||
|
"--ipc=host " +
|
||
|
"--pid=host " +
|
||
|
"-v=/var:/var " +
|
||
|
"--volumes-from=system-volumes " +
|
||
|
image,
|
||
|
}).Stage()
|
||
|
|
||
|
if container.Err != nil {
|
||
|
log.Fatal(container.Err)
|
||
|
}
|
||
|
|
||
|
if !stage {
|
||
|
container.StartAndWait()
|
||
|
if container.Err != nil {
|
||
|
log.Fatal(container.Err)
|
||
|
}
|
||
|
power.Reboot()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getLatestImage(channel string) (string, error) {
|
||
|
data, err := getConfigData()
|
||
|
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
var pivot string
|
||
|
|
||
|
if pivot == "" {
|
||
|
val := getOrSetVal("os_upgrade_channel", data, nil)
|
||
|
|
||
|
if val == nil {
|
||
|
return "", errors.New("os_upgrade_channel is not set")
|
||
|
}
|
||
|
|
||
|
switch currentChannel := val.(type) {
|
||
|
case string:
|
||
|
pivot = currentChannel
|
||
|
default:
|
||
|
return "", errors.New("invalid format of rancherctl config get os_upgrade_channel")
|
||
|
}
|
||
|
} else {
|
||
|
pivot = channel
|
||
|
}
|
||
|
osChannel, ok := getChannelUrl(pivot); if !ok {
|
||
|
return "", errors.New("unrecognized channel " + pivot)
|
||
|
}
|
||
|
resp, err := http.Get(osChannel)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
body, err := ioutil.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return parseBody(body, osChannel), nil
|
||
|
}
|
||
|
|
||
|
func parseBody(body []byte, channel string) string {
|
||
|
// just going to assume that the response is the image name
|
||
|
// can change it later based on server response design
|
||
|
return string(body)
|
||
|
}
|
||
|
|
||
|
func getChannelUrl(channel string) (string, bool) {
|
||
|
if osChannels == nil {
|
||
|
osChannels = map[string]string {
|
||
|
"stable" : "",
|
||
|
"alpha" : "",
|
||
|
"beta" : "",
|
||
|
"meta" : "",
|
||
|
}
|
||
|
}
|
||
|
channel, ok := osChannels[channel];
|
||
|
return channel, ok
|
||
|
}
|
||
|
|