From 594db8fd8c4941a86d71cc1bdc119b914f50a6f5 Mon Sep 17 00:00:00 2001 From: sidharthamani Date: Fri, 20 Feb 2015 22:35:42 -0800 Subject: [PATCH 1/3] add upgrade framework for rancherctl os command --- cmd/control/cli.go | 15 +--- cmd/control/config.go | 2 +- cmd/control/os.go | 197 ++++++++++++++++++++++++++++++++++++++++++ config/config.go | 1 + 4 files changed, 200 insertions(+), 15 deletions(-) create mode 100644 cmd/control/os.go diff --git a/cmd/control/cli.go b/cmd/control/cli.go index 68f182c0..ab3a09b7 100644 --- a/cmd/control/cli.go +++ b/cmd/control/cli.go @@ -45,20 +45,7 @@ func Main() { { Name: "os", Usage: "operating system upgrade/downgrade", - Subcommands: []cli.Command{ - { - Name: "list", - Usage: "list available RancherOS versions and state", - }, - { - Name: "update", - Usage: "download the latest new version of RancherOS", - }, - { - Name: "activate", - Usage: "switch to a new version of RancherOS and reboot", - }, - }, + Subcommands: osSubcommands(), }, } diff --git a/cmd/control/config.go b/cmd/control/config.go index 09a489d6..81110914 100644 --- a/cmd/control/config.go +++ b/cmd/control/config.go @@ -186,7 +186,7 @@ func getOrSetVal(args string, data map[interface{}]interface{}, value interface{ for i, part := range parts { val, ok := data[part] last := i+1 == len(parts) - + if last && value != nil { if s, ok := value.(string); ok { value = config.DummyMarshall(s) diff --git a/cmd/control/os.go b/cmd/control/os.go new file mode 100644 index 00000000..f75f915b --- /dev/null +++ b/cmd/control/os.go @@ -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 +} + diff --git a/config/config.go b/config/config.go index 040ef376..c30930a4 100644 --- a/config/config.go +++ b/config/config.go @@ -46,6 +46,7 @@ type Config struct { RescueContainer *ContainerConfig `yaml:"rescue_container,omitempty"` State ConfigState `yaml:"state,omitempty"` Userdocker UserDockerInfo `yaml:"userdocker,omitempty"` + OsUpgradeChannel string `yaml:"os_upgrade_channel,omitempty"` SystemContainers []ContainerConfig `yaml:"system_containers,omitempty"` SystemDockerArgs []string `yaml:"system_docker_args,flow,omitempty"` Modules []string `yaml:"modules,omitempty"` From 33d636930b0b8fb24e6c50541cbbc1a7a5165f36 Mon Sep 17 00:00:00 2001 From: sidharthamani Date: Sat, 21 Feb 2015 12:22:59 -0800 Subject: [PATCH 2/3] refactor power and check for permissions --- cmd/power/power.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/cmd/power/power.go b/cmd/power/power.go index f2bffb5c..17d3e66c 100644 --- a/cmd/power/power.go +++ b/cmd/power/power.go @@ -9,6 +9,7 @@ import ( "syscall" "github.com/fsouza/go-dockerclient" + log "github.com/Sirupsen/logrus" ) const ( @@ -17,16 +18,28 @@ const ( ) func PowerOff() { + if os.Geteuid() != 0 { + log.Info("poweroff: Permission Denied") + return + } syscall.Sync() reboot(syscall.LINUX_REBOOT_CMD_POWER_OFF) } func Reboot() { + if os.Geteuid() != 0 { + log.Info("reboot: Permission Denied") + return + } syscall.Sync() reboot(syscall.LINUX_REBOOT_CMD_RESTART) } func Halt() { + if os.Geteuid() != 0 { + log.Info("reboot: Permission Denied") + return + } syscall.Sync() reboot(syscall.LINUX_REBOOT_CMD_HALT) } @@ -34,11 +47,11 @@ func Halt() { func reboot(code int) { err := shutDownContainers() if err != nil { - panic(err) + log.Fatal(err) } err = syscall.Reboot(code) if err != nil { - panic(err) + log.Fatal(err) } } @@ -59,7 +72,7 @@ func shutDownContainers() error { } timeout = uint(t) } else { - panic("please specify a timeout") + log.Info("please specify a timeout") } } } From 0b31dc7e3b8aa54d12630a384a5fcba66747e066 Mon Sep 17 00:00:00 2001 From: sidharthamani Date: Sat, 21 Feb 2015 13:31:10 -0800 Subject: [PATCH 3/3] move tlsconf to rancherctl --- cmd/control/cli.go | 5 ++ cmd/{tlsconf => control}/tlsconf.go | 89 +++++++++++++++----------- main.go | 2 - scripts/dockerimages/scripts/docker.sh | 2 +- 4 files changed, 57 insertions(+), 41 deletions(-) rename cmd/{tlsconf => control}/tlsconf.go (52%) diff --git a/cmd/control/cli.go b/cmd/control/cli.go index ab3a09b7..9ce765ad 100644 --- a/cmd/control/cli.go +++ b/cmd/control/cli.go @@ -47,6 +47,11 @@ func Main() { Usage: "operating system upgrade/downgrade", Subcommands: osSubcommands(), }, + { + Name: "tlsconf", + Usage: "setup tls configuration", + Subcommands: tlsConfCommands(), + }, } app.Run(os.Args) diff --git a/cmd/tlsconf/tlsconf.go b/cmd/control/tlsconf.go similarity index 52% rename from cmd/tlsconf/tlsconf.go rename to cmd/control/tlsconf.go index ecfc0e7f..c74768cd 100644 --- a/cmd/tlsconf/tlsconf.go +++ b/cmd/control/tlsconf.go @@ -1,19 +1,46 @@ -package tlsconf +package control import ( "fmt" "os" "path/filepath" + "github.com/codegangsta/cli" 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" bits := 2048 - vargs := os.Args - caCertPath := "ca.pem" caKeyPath := "ca-key.pem" outDir := "/etc/docker/tls/" @@ -22,42 +49,28 @@ func Main() { inputCaKey := "" inputCaCert := "" - for index := range vargs { - arg := vargs[index] - 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 val := c.String("outDir"); val != "" { + outDir = val } + + 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) caKeyPath = filepath.Join(outDir, caKeyPath) + serverCertPath := "server-cert.pem" + serverKeyPath := "server-key.pem" + if generateCaCerts { if err := machineUtil.GenerateCACertificate(caCertPath, caKeyPath, name, bits); err != nil { fmt.Println(err.Error()) @@ -83,12 +96,12 @@ func Main() { } else { 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) if err := machineUtil.GenerateCert([]string{""}, serverCertPath, serverKeyPath, caCertPath, caKeyPath, name, bits); err != nil { diff --git a/main.go b/main.go index 24495a3a..db882353 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,6 @@ import ( "github.com/rancherio/os/cmd/respawn" "github.com/rancherio/os/cmd/sysinit" "github.com/rancherio/os/cmd/systemdocker" - "github.com/rancherio/os/cmd/tlsconf" osInit "github.com/rancherio/os/init" ) @@ -43,7 +42,6 @@ func main() { registerCmd("/sbin/halt", power.Halt) registerCmd("/usr/bin/respawn", respawn.Main) registerCmd("/usr/sbin/rancherctl", control.Main) - registerCmd("/usr/bin/tlsconf", tlsconf.Main) registerCmd("/usr/bin/cloud-init", cloudinit.Main) if !reexec.Init() { diff --git a/scripts/dockerimages/scripts/docker.sh b/scripts/dockerimages/scripts/docker.sh index 21a149fa..4623e128 100755 --- a/scripts/dockerimages/scripts/docker.sh +++ b/scripts/dockerimages/scripts/docker.sh @@ -32,7 +32,7 @@ if [ "$USE_TLS" == "true" ]; then echo "$TLS_SERVER_CERT" > $TLS_PATH/server-cert.pem echo "$TLS_SERVER_KEY" > $TLS_PATH/server-key.pem else - tlsconf + rancherctl tlsconf create TLS_CA_CERT="$(cat $TLS_PATH/ca.pem)" TLS_SERVER_CERT="$(cat $TLS_PATH/server-cert.pem)" TLS_SERVER_KEY="$(cat $TLS_PATH/server-key.pem)"