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:
@@ -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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
197
cmd/control/os.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@@ -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 {
|
@@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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"`
|
||||||
|
2
main.go
2
main.go
@@ -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() {
|
||||||
|
@@ -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)"
|
||||||
|
Reference in New Issue
Block a user