1
0
mirror of https://github.com/rancher/os.git synced 2025-09-03 15:54:24 +00:00

Merge pull request #31 from sidharthamani/master

add upgrade framework for rancherctl os command
This commit is contained in:
Darren
2015-02-21 19:40:44 -07:00
8 changed files with 273 additions and 59 deletions

View File

@@ -45,20 +45,12 @@ func Main() {
{ {
Name: "os", Name: "os",
Usage: "operating system upgrade/downgrade", Usage: "operating system upgrade/downgrade",
Subcommands: []cli.Command{ Subcommands: osSubcommands(),
{ },
Name: "list", {
Usage: "list available RancherOS versions and state", Name: "tlsconf",
}, Usage: "setup tls configuration",
{ Subcommands: tlsConfCommands(),
Name: "update",
Usage: "download the latest new version of RancherOS",
},
{
Name: "activate",
Usage: "switch to a new version of RancherOS and reboot",
},
},
}, },
} }

View File

@@ -182,7 +182,7 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{
for i, part := range parts { for i, part := range parts {
val, ok := data[part] val, ok := data[part]
last := i+1 == len(parts) last := i+1 == len(parts)
if last && value != nil { if last && value != nil {
if s, ok := value.(string); ok { if s, ok := value.(string); ok {
value = config.DummyMarshall(s) value = config.DummyMarshall(s)

197
cmd/control/os.go Normal file
View File

@@ -0,0 +1,197 @@
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
}

View File

@@ -1,19 +1,46 @@
package tlsconf package control
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"github.com/codegangsta/cli"
machineUtil "github.com/docker/machine/utils" machineUtil "github.com/docker/machine/utils"
) )
func Main() { func tlsConfCommands() []cli.Command {
return []cli.Command {
{
Name: "create",
Usage: "use it to create a new set of tls configuration certs and keys or upload existing ones",
Action: tlsConfCreate,
Flags: []cli.Flag {
cli.StringFlag {
Name: "cakey",
Usage: "path to existing certificate authority key (only use with --generate)",
},
cli.StringFlag {
Name: "ca",
Usage: "path to existing certificate authority (only use with --genreate)",
},
cli.BoolFlag {
Name: "generate, g",
Usage: "generate the client key and client cert from existing ca and cakey",
},
cli.StringFlag {
Name: "outDir, o",
Usage: "the output directory to save the generated certs or keys",
},
},
},
}
}
func tlsConfCreate(c *cli.Context) {
name := "rancher" name := "rancher"
bits := 2048 bits := 2048
vargs := os.Args
caCertPath := "ca.pem" caCertPath := "ca.pem"
caKeyPath := "ca-key.pem" caKeyPath := "ca-key.pem"
outDir := "/etc/docker/tls/" outDir := "/etc/docker/tls/"
@@ -22,42 +49,28 @@ func Main() {
inputCaKey := "" inputCaKey := ""
inputCaCert := "" inputCaCert := ""
for index := range vargs { if val := c.String("outDir"); val != "" {
arg := vargs[index] outDir = val
if arg == "--help" || arg == "-h" {
fmt.Println("run tlsconfig with no args to generate ca, cakey, server-key and server-cert in /var/run \n")
fmt.Println("--help or -h\t print this help text")
fmt.Println("--cakey\t\t path to existing certificate authority key (only use with -g)")
fmt.Println("--ca\t\t path to existing certificate authority (only use with -g)")
fmt.Println("--g \t\t generates server key and server cert from existing ca and caKey")
fmt.Println("--outdir \t the output directory to save the generate certs or keys")
return
} else if arg == "--outdir" {
if len(vargs) > index+1 {
outDir = vargs[index+1]
} else {
fmt.Println("please specify a output directory")
}
} else if arg == "-g" {
generateCaCerts = false
} else if arg == "--cakey" {
if len(vargs) > index+1 {
inputCaKey = vargs[index+1]
} else {
fmt.Println("please specify a input ca-key file path")
}
} else if arg == "--ca" {
if len(vargs) > index+1 {
inputCaCert = vargs[index+1]
} else {
fmt.Println("please specify a input ca file path")
}
}
} }
if c.Bool("generate") {
generateCaCerts = false
}
if val := c.String("cakey"); val != "" {
inputCaKey = val
}
if val := c.String("ca"); val != "" {
inputCaCert = val
}
caCertPath = filepath.Join(outDir, caCertPath) caCertPath = filepath.Join(outDir, caCertPath)
caKeyPath = filepath.Join(outDir, caKeyPath) caKeyPath = filepath.Join(outDir, caKeyPath)
serverCertPath := "server-cert.pem"
serverKeyPath := "server-key.pem"
if generateCaCerts { if generateCaCerts {
if err := machineUtil.GenerateCACertificate(caCertPath, caKeyPath, name, bits); err != nil { if err := machineUtil.GenerateCACertificate(caCertPath, caKeyPath, name, bits); err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
@@ -83,12 +96,12 @@ func Main() {
} else { } else {
caCertPath = inputCaCert caCertPath = inputCaCert
} }
serverCertPath = "client-cert.pem"
serverKeyPath = "client-key.pem"
} }
serverCertPath := "server-cert.pem"
serverCertPath = filepath.Join(outDir, serverCertPath)
serverKeyPath := "server-key.pem" serverCertPath = filepath.Join(outDir, serverCertPath)
serverKeyPath = filepath.Join(outDir, serverKeyPath) serverKeyPath = filepath.Join(outDir, serverKeyPath)
if err := machineUtil.GenerateCert([]string{""}, serverCertPath, serverKeyPath, caCertPath, caKeyPath, name, bits); err != nil { if err := machineUtil.GenerateCert([]string{""}, serverCertPath, serverKeyPath, caCertPath, caKeyPath, name, bits); err != nil {

View File

@@ -9,6 +9,7 @@ import (
"syscall" "syscall"
"github.com/fsouza/go-dockerclient" "github.com/fsouza/go-dockerclient"
log "github.com/Sirupsen/logrus"
) )
const ( const (
@@ -17,16 +18,28 @@ const (
) )
func PowerOff() { func PowerOff() {
if os.Geteuid() != 0 {
log.Info("poweroff: Permission Denied")
return
}
syscall.Sync() syscall.Sync()
reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF) reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF)
} }
func Reboot() { func Reboot() {
if os.Geteuid() != 0 {
log.Info("reboot: Permission Denied")
return
}
syscall.Sync() syscall.Sync()
reboot(syscall.LINUX_REBOOT_CMD_RESTART) reboot(syscall.LINUX_REBOOT_CMD_RESTART)
} }
func Halt() { func Halt() {
if os.Geteuid() != 0 {
log.Info("reboot: Permission Denied")
return
}
syscall.Sync() syscall.Sync()
reboot(syscall.LINUX_REBOOT_CMD_HALT) reboot(syscall.LINUX_REBOOT_CMD_HALT)
} }
@@ -34,11 +47,11 @@ func Halt() {
func reboot(code int) { func reboot(code int) {
err := shutDownContainers() err := shutDownContainers()
if err != nil { if err != nil {
panic(err) log.Fatal(err)
} }
err = syscall.Reboot(code) err = syscall.Reboot(code)
if err != nil { if err != nil {
panic(err) log.Fatal(err)
} }
} }
@@ -59,7 +72,7 @@ func shutDownContainers() error {
} }
timeout = uint(t) timeout = uint(t)
} else { } else {
panic("please specify a timeout") log.Info("please specify a timeout")
} }
} }
} }

View File

@@ -46,6 +46,7 @@ type Config struct {
RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"` RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"`
State ConfigState `yaml:"state,omitempty"` State ConfigState `yaml:"state,omitempty"`
Userdocker UserDockerInfo `yaml:"userdocker,omitempty"` Userdocker UserDockerInfo `yaml:"userdocker,omitempty"`
OsUpgradeChannel string `yaml:"os_upgrade_channel,omitempty"`
SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"` SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"`
SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"` SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"`
Modules []string `yaml:"modules,omitempty"` Modules []string `yaml:"modules,omitempty"`

View File

@@ -12,7 +12,6 @@ import (
"github.com/rancherio/os/cmd/respawn" "github.com/rancherio/os/cmd/respawn"
"github.com/rancherio/os/cmd/sysinit" "github.com/rancherio/os/cmd/sysinit"
"github.com/rancherio/os/cmd/systemdocker" "github.com/rancherio/os/cmd/systemdocker"
"github.com/rancherio/os/cmd/tlsconf"
osInit "github.com/rancherio/os/init" osInit "github.com/rancherio/os/init"
) )
@@ -43,7 +42,6 @@ func main() {
registerCmd("/sbin/halt", power.Halt) registerCmd("/sbin/halt", power.Halt)
registerCmd("/usr/bin/respawn", respawn.Main) registerCmd("/usr/bin/respawn", respawn.Main)
registerCmd("/usr/sbin/rancherctl", control.Main) registerCmd("/usr/sbin/rancherctl", control.Main)
registerCmd("/usr/bin/tlsconf", tlsconf.Main)
registerCmd("/usr/bin/cloud-init", cloudinit.Main) registerCmd("/usr/bin/cloud-init", cloudinit.Main)
if !reexec.Init() { if !reexec.Init() {

View File

@@ -32,7 +32,7 @@ if [ "$USE_TLS" == "true" ]; then
echo "$TLS_SERVER_CERT" > $TLS_PATH/server-cert.pem echo "$TLS_SERVER_CERT" > $TLS_PATH/server-cert.pem
echo "$TLS_SERVER_KEY" > $TLS_PATH/server-key.pem echo "$TLS_SERVER_KEY" > $TLS_PATH/server-key.pem
else else
tlsconf rancherctl tlsconf create
TLS_CA_CERT="$(cat $TLS_PATH/ca.pem)" TLS_CA_CERT="$(cat $TLS_PATH/ca.pem)"
TLS_SERVER_CERT="$(cat $TLS_PATH/server-cert.pem)" TLS_SERVER_CERT="$(cat $TLS_PATH/server-cert.pem)"
TLS_SERVER_KEY="$(cat $TLS_PATH/server-key.pem)" TLS_SERVER_KEY="$(cat $TLS_PATH/server-key.pem)"