mirror of
https://github.com/rancher/os.git
synced 2025-08-31 14:23:11 +00:00
Merge pull request #1931 from SvenDowideit/autologin-and-recovery-console
WIP Autologin and recovery console
This commit is contained in:
103
cmd/control/autologin.go
Normal file
103
cmd/control/autologin.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/rancher/os/config"
|
||||
"github.com/rancher/os/log"
|
||||
)
|
||||
|
||||
func AutologinMain() {
|
||||
log.InitLogger()
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Name = os.Args[0]
|
||||
app.Usage = "autologin console"
|
||||
app.Version = config.Version
|
||||
app.Author = "Rancher Labs, Inc."
|
||||
app.Email = "sven@rancher.com"
|
||||
app.EnableBashCompletion = true
|
||||
app.Action = autologinAction
|
||||
app.HideHelp = true
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
func autologinAction(c *cli.Context) error {
|
||||
cmd := exec.Command("/bin/stty", "sane")
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
usertty := ""
|
||||
user := "root"
|
||||
tty := ""
|
||||
if c.NArg() > 0 {
|
||||
usertty = c.Args().Get(0)
|
||||
s := strings.SplitN(usertty, ":", 2)
|
||||
user = s[0]
|
||||
if len(s) > 1 {
|
||||
tty = s[1]
|
||||
}
|
||||
}
|
||||
|
||||
mode := filepath.Base(os.Args[0])
|
||||
console := CurrentConsole()
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
// replace \n and \l
|
||||
banner := config.Banner
|
||||
banner = strings.Replace(banner, "\\v", config.Version, -1)
|
||||
banner = strings.Replace(banner, "\\s", "RancherOS "+runtime.GOARCH, -1)
|
||||
banner = strings.Replace(banner, "\\r", config.GetKernelVersion(), -1)
|
||||
banner = strings.Replace(banner, "\\n", cfg.Hostname, -1)
|
||||
banner = strings.Replace(banner, "\\l", tty, -1)
|
||||
banner = strings.Replace(banner, "\\\\", "\\", -1)
|
||||
banner = banner + "\n"
|
||||
banner = banner + "Autologin " + console + "\n"
|
||||
fmt.Printf(banner)
|
||||
|
||||
loginBin := ""
|
||||
args := []string{}
|
||||
if console == "centos" || console == "fedora" ||
|
||||
mode == "recovery" {
|
||||
// For some reason, centos and fedora ttyS0 and tty1 don't work with `login -f rancher`
|
||||
// until I make time to read their source, lets just give us a way to get work done
|
||||
loginBin = "bash"
|
||||
args = append(args, "--login")
|
||||
os.Setenv("PROMPT_COMMAND", `echo "[`+fmt.Sprintf("Recovery console %s@%s:${PWD}", user, cfg.Hostname)+`]"`)
|
||||
} else {
|
||||
loginBin = "login"
|
||||
args = append(args, "-f", user)
|
||||
// TODO: add a PROMPT_COMMAND if we haven't switch-rooted
|
||||
}
|
||||
|
||||
loginBinPath, err := exec.LookPath(loginBin)
|
||||
if err != nil {
|
||||
fmt.Printf("error finding %s in path: %s", cmd.Args[0], err)
|
||||
return err
|
||||
}
|
||||
os.Setenv("TERM", "linux")
|
||||
|
||||
// Causes all sorts of issues
|
||||
//return syscall.Exec(loginBinPath, args, os.Environ())
|
||||
cmd = exec.Command(loginBinPath, args...)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "SVEN", "MORE")
|
||||
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Errorf("\nError starting %s: %s", cmd.Args[0], err)
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -101,6 +101,13 @@ func Main() {
|
||||
SkipFlagParsing: true,
|
||||
Action: preloadImagesAction,
|
||||
},
|
||||
{
|
||||
Name: "recovery-init",
|
||||
Hidden: true,
|
||||
HideHelp: true,
|
||||
SkipFlagParsing: true,
|
||||
Action: recoveryInitAction,
|
||||
},
|
||||
{
|
||||
Name: "switch-console",
|
||||
Hidden: true,
|
||||
|
@@ -225,7 +225,15 @@ func merge(c *cli.Context) error {
|
||||
}
|
||||
|
||||
if err = config.Merge(bytes); err != nil {
|
||||
log.Fatal(err)
|
||||
log.Error(err)
|
||||
validationErrors, err := config.ValidateBytes(bytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, validationError := range validationErrors.Errors() {
|
||||
log.Error(validationError)
|
||||
}
|
||||
log.Fatal("EXITING: Failed to parse configuration")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -255,7 +263,7 @@ func validate(c *cli.Context) error {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
validationErrors, err := config.Validate(bytes)
|
||||
validationErrors, err := config.ValidateBytes(bytes)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@@ -2,18 +2,19 @@ package control
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/docker/reference"
|
||||
composeConfig "github.com/docker/libcompose/config"
|
||||
"github.com/docker/libcompose/project/options"
|
||||
"github.com/rancher/os/cmd/control/service"
|
||||
"github.com/rancher/os/compose"
|
||||
"github.com/rancher/os/config"
|
||||
"github.com/rancher/os/docker"
|
||||
"github.com/rancher/os/log"
|
||||
"github.com/rancher/os/util"
|
||||
"github.com/rancher/os/util/network"
|
||||
@@ -57,7 +58,7 @@ func consoleSwitch(c *cli.Context) error {
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
validateConsole(newConsole, cfg)
|
||||
if newConsole == currentConsole() {
|
||||
if newConsole == CurrentConsole() {
|
||||
log.Warnf("Console is already set to %s", newConsole)
|
||||
}
|
||||
|
||||
@@ -127,10 +128,10 @@ func consoleEnable(c *cli.Context) error {
|
||||
func consoleList(c *cli.Context) error {
|
||||
cfg := config.LoadConfig()
|
||||
consoles := availableConsoles(cfg)
|
||||
currentConsole := currentConsole()
|
||||
CurrentConsole := CurrentConsole()
|
||||
|
||||
for _, console := range consoles {
|
||||
if console == currentConsole {
|
||||
if console == CurrentConsole {
|
||||
fmt.Printf("current %s\n", console)
|
||||
} else if console == cfg.Rancher.Console {
|
||||
fmt.Printf("enabled %s\n", console)
|
||||
@@ -159,12 +160,32 @@ func availableConsoles(cfg *config.CloudConfig) []string {
|
||||
return consoles
|
||||
}
|
||||
|
||||
func currentConsole() (console string) {
|
||||
consoleBytes, err := ioutil.ReadFile("/run/console-done")
|
||||
if err == nil {
|
||||
console = strings.TrimSpace(string(consoleBytes))
|
||||
} else {
|
||||
// CurrentConsole gets the name of the console that's running
|
||||
func CurrentConsole() (console string) {
|
||||
// TODO: replace this docker container look up with a libcompose service lookup?
|
||||
|
||||
// sudo system-docker inspect --format "{{.Config.Image}}" console
|
||||
client, err := docker.NewSystemClient()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to detect current console: %v", err)
|
||||
return
|
||||
}
|
||||
info, err := client.ContainerInspect(context.Background(), "console")
|
||||
if err != nil {
|
||||
log.Warnf("Failed to detect current console: %v", err)
|
||||
return
|
||||
}
|
||||
// parse image name, then remove os- prefix and the console suffix
|
||||
image, err := reference.ParseNamed(info.Config.Image)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to detect current console(%s): %v", info.Config.Image, err)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(image.Name(), "os-console") {
|
||||
console = "default"
|
||||
return
|
||||
}
|
||||
console = strings.TrimPrefix(strings.TrimSuffix(image.Name(), "console"), "rancher/os-")
|
||||
return
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/SvenDowideit/cpuid"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/rancher/os/cmd/cloudinitexecute"
|
||||
"github.com/rancher/os/config"
|
||||
@@ -82,7 +81,7 @@ func consoleInitFunc() error {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
if err := writeRespawn(); err != nil {
|
||||
if err := writeRespawn("rancher", cfg.Rancher.SSH.Daemon, false); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
@@ -109,17 +108,7 @@ func consoleInitFunc() error {
|
||||
}
|
||||
|
||||
// font backslashes need to be escaped for when issue is output! (but not the others..)
|
||||
if err := ioutil.WriteFile("/etc/issue", []byte(`
|
||||
, , ______ _ _____ _____TM
|
||||
,------------|'------'| | ___ \\ | | / _ / ___|
|
||||
/ . '-' |- | |_/ /__ _ _ __ ___| |__ ___ _ __ | | | \\ '--.
|
||||
\\/| | | | // _' | '_ \\ / __| '_ \\ / _ \\ '__' | | | |'--. \\
|
||||
| .________.'----' | |\\ \\ (_| | | | | (__| | | | __/ | | \\_/ /\\__/ /
|
||||
| | | | \\_| \\_\\__,_|_| |_|\\___|_| |_|\\___|_| \\___/\\____/
|
||||
\\___/ \\___/ \s \r
|
||||
|
||||
RancherOS `+config.Version+` \n \l `+cpuid.CPU.HypervisorName+`
|
||||
`), 0644); err != nil {
|
||||
if err := ioutil.WriteFile("/etc/issue", []byte(config.Banner), 0644); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
@@ -137,7 +126,7 @@ func consoleInitFunc() error {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(consoleDone, []byte(cfg.Rancher.Console), 0644); err != nil {
|
||||
if err := ioutil.WriteFile(consoleDone, []byte(CurrentConsole()), 0644); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
@@ -155,15 +144,20 @@ func consoleInitFunc() error {
|
||||
return syscall.Exec(respawnBinPath, []string{"respawn", "-f", "/etc/respawn.conf"}, os.Environ())
|
||||
}
|
||||
|
||||
func generateRespawnConf(cmdline string) string {
|
||||
func generateRespawnConf(cmdline, user string, sshd, recovery bool) string {
|
||||
var respawnConf bytes.Buffer
|
||||
|
||||
autologinBin := "/usr/bin/autologin"
|
||||
if recovery {
|
||||
autologinBin = "/usr/bin/recovery"
|
||||
}
|
||||
|
||||
for i := 1; i < 7; i++ {
|
||||
tty := fmt.Sprintf("tty%d", i)
|
||||
|
||||
respawnConf.WriteString(gettyCmd)
|
||||
if strings.Contains(cmdline, fmt.Sprintf("rancher.autologin=%s", tty)) {
|
||||
respawnConf.WriteString(" --autologin rancher")
|
||||
respawnConf.WriteString(fmt.Sprintf(" -n -l %s -o %s:tty%d", autologinBin, user, i))
|
||||
}
|
||||
respawnConf.WriteString(fmt.Sprintf(" --noclear %s linux\n", tty))
|
||||
}
|
||||
@@ -175,23 +169,25 @@ func generateRespawnConf(cmdline string) string {
|
||||
|
||||
respawnConf.WriteString(gettyCmd)
|
||||
if strings.Contains(cmdline, fmt.Sprintf("rancher.autologin=%s", tty)) {
|
||||
respawnConf.WriteString(" --autologin rancher")
|
||||
respawnConf.WriteString(fmt.Sprintf(" -n -l %s -o %s:%s", autologinBin, user, tty))
|
||||
}
|
||||
respawnConf.WriteString(fmt.Sprintf(" %s\n", tty))
|
||||
}
|
||||
|
||||
respawnConf.WriteString("/usr/sbin/sshd -D")
|
||||
if sshd {
|
||||
respawnConf.WriteString("/usr/sbin/sshd -D")
|
||||
}
|
||||
|
||||
return respawnConf.String()
|
||||
}
|
||||
|
||||
func writeRespawn() error {
|
||||
func writeRespawn(user string, sshd, recovery bool) error {
|
||||
cmdline, err := ioutil.ReadFile("/proc/cmdline")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
respawn := generateRespawnConf(string(cmdline))
|
||||
respawn := generateRespawnConf(string(cmdline), user, sshd, recovery)
|
||||
|
||||
files, err := ioutil.ReadDir("/etc/respawn.conf.d")
|
||||
if err == nil {
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/rancher/os/config"
|
||||
"github.com/rancher/os/log"
|
||||
"github.com/rancher/os/util"
|
||||
)
|
||||
@@ -22,6 +21,7 @@ const (
|
||||
)
|
||||
|
||||
func dockerInitAction(c *cli.Context) error {
|
||||
// TODO: this should be replaced by a "Console ready event watcher"
|
||||
for {
|
||||
if _, err := os.Stat(consoleDone); err == nil {
|
||||
break
|
||||
@@ -69,9 +69,8 @@ func dockerInitAction(c *cli.Context) error {
|
||||
fmt.Sprintf(`[ -e %s ] && source %s; exec /usr/bin/dockerlaunch %s %s $DOCKER_OPTS >> %s 2>&1`, dockerConf, dockerConf, dockerBin, strings.Join(c.Args(), " "), dockerLog),
|
||||
}
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
|
||||
if err := ioutil.WriteFile(dockerDone, []byte(cfg.Rancher.Docker.Engine), 0644); err != nil {
|
||||
// TODO: this should be replaced by a "Docker ready event watcher"
|
||||
if err := ioutil.WriteFile(dockerDone, []byte(CurrentEngine()), 0644); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
|
@@ -2,17 +2,18 @@ package control
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/libcompose/project/options"
|
||||
"github.com/rancher/os/cmd/control/service"
|
||||
"github.com/rancher/os/compose"
|
||||
"github.com/rancher/os/config"
|
||||
"github.com/rancher/os/docker"
|
||||
"github.com/rancher/os/log"
|
||||
"github.com/rancher/os/util"
|
||||
"github.com/rancher/os/util/network"
|
||||
@@ -104,7 +105,7 @@ func engineEnable(c *cli.Context) error {
|
||||
func engineList(c *cli.Context) error {
|
||||
cfg := config.LoadConfig()
|
||||
engines := availableEngines(cfg)
|
||||
currentEngine := currentEngine()
|
||||
currentEngine := CurrentEngine()
|
||||
|
||||
for _, engine := range engines {
|
||||
if engine == currentEngine {
|
||||
@@ -135,12 +136,33 @@ func availableEngines(cfg *config.CloudConfig) []string {
|
||||
return engines
|
||||
}
|
||||
|
||||
func currentEngine() (engine string) {
|
||||
engineBytes, err := ioutil.ReadFile(dockerDone)
|
||||
if err == nil {
|
||||
engine = strings.TrimSpace(string(engineBytes))
|
||||
} else {
|
||||
log.Warnf("Failed to detect current Docker engine: %v", err)
|
||||
// CurrentEngine gets the name of the docker that's running
|
||||
func CurrentEngine() (engine string) {
|
||||
// sudo system-docker inspect --format "{{.Config.Image}}" docker
|
||||
client, err := docker.NewSystemClient()
|
||||
if err != nil {
|
||||
log.Warnf("Failed to detect current docker: %v", err)
|
||||
return
|
||||
}
|
||||
info, err := client.ContainerInspect(context.Background(), "docker")
|
||||
if err != nil {
|
||||
log.Warnf("Failed to detect current docker: %v", err)
|
||||
return
|
||||
}
|
||||
// parse image name, then remove os- prefix and the engine suffix
|
||||
image, err := reference.ParseNamed(info.Config.Image)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to detect current docker(%s): %v", info.Config.Image, err)
|
||||
return
|
||||
}
|
||||
if t, ok := image.(reference.NamedTagged); ok {
|
||||
tag := t.Tag()
|
||||
if !strings.HasPrefix(tag, "1.") {
|
||||
// TODO: this assumes we only do Docker ce :/
|
||||
tag = tag + "-ce"
|
||||
}
|
||||
return "docker-" + tag
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@@ -79,6 +79,8 @@ func writeFiles(cfg *config.CloudConfig) error {
|
||||
|
||||
func setupCommandSymlinks() {
|
||||
for _, link := range []symlink{
|
||||
{config.RosBin, "/usr/bin/autologin"},
|
||||
{config.RosBin, "/usr/bin/recovery"},
|
||||
{config.RosBin, "/usr/bin/cloud-init-execute"},
|
||||
{config.RosBin, "/usr/bin/cloud-init-save"},
|
||||
{config.RosBin, "/usr/bin/dockerlaunch"},
|
||||
|
23
cmd/control/recovery_init.go
Normal file
23
cmd/control/recovery_init.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func recoveryInitAction(c *cli.Context) error {
|
||||
if err := writeRespawn("root", false, true); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
respawnBinPath, err := exec.LookPath("respawn")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return syscall.Exec(respawnBinPath, []string{"respawn", "-f", "/etc/respawn.conf"}, os.Environ())
|
||||
}
|
@@ -1,10 +1,25 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||
"github.com/rancher/os/util"
|
||||
)
|
||||
|
||||
const Banner = `
|
||||
, , ______ _ _____ _____TM
|
||||
,------------|'------'| | ___ \\ | | / _ / ___|
|
||||
/ . '-' |- | |_/ /__ _ _ __ ___| |__ ___ _ __ | | | \\ '--.
|
||||
\\/| | | | // _' | '_ \\ / __| '_ \\ / _ \\ '__' | | | |'--. \\
|
||||
| .________.'----' | |\\ \\ (_| | | | | (__| | | | __/ | | \\_/ /\\__/ /
|
||||
| | | | \\_| \\_\\__,_|_| |_|\\___|_| |_|\\___|_| \\___/\\____/
|
||||
\\___/ \\___/ \s \r
|
||||
|
||||
RancherOS \v \n \l
|
||||
`
|
||||
|
||||
func Merge(bytes []byte) error {
|
||||
data, err := readConfigs(bytes, false, true)
|
||||
if err != nil {
|
||||
@@ -60,3 +75,12 @@ func Set(key string, value interface{}) error {
|
||||
|
||||
return WriteToFile(modified, CloudConfigFile)
|
||||
}
|
||||
|
||||
func GetKernelVersion() string {
|
||||
b, err := ioutil.ReadFile("/proc/version")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
elem := strings.Split(string(b), " ")
|
||||
return elem[2]
|
||||
}
|
||||
|
@@ -71,8 +71,19 @@ func LoadConfigWithPrefix(dirPrefix string) *CloudConfig {
|
||||
|
||||
cfg := &CloudConfig{}
|
||||
if err := util.Convert(rawCfg, cfg); err != nil {
|
||||
log.Errorf("Failed to parse configuration: %s", err)
|
||||
log.Errorf("EXITING: Failed to parse configuration: %s", err)
|
||||
log.Debugf("Bad cfg:\n%v\n", rawCfg)
|
||||
// no point returning {}, it'll just sit there broken
|
||||
// TODO: print some context around what failed..
|
||||
validationErrors, err := ValidateRawCfg(rawCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, validationError := range validationErrors.Errors() {
|
||||
log.Error(validationError)
|
||||
}
|
||||
// TODO: I'd love to panic & recover(), for issues on boot, but it doesn't work yet
|
||||
os.Exit(-1)
|
||||
return &CloudConfig{}
|
||||
}
|
||||
cfg = amendNils(cfg)
|
||||
|
@@ -37,6 +37,7 @@ var schema = `{
|
||||
"no_sharedroot": {"type": "boolean"},
|
||||
"log": {"type": "boolean"},
|
||||
"force_console_rebuild": {"type": "boolean"},
|
||||
"recovery": {"type": "boolean"},
|
||||
"disable": {"$ref": "#/definitions/list_of_strings"},
|
||||
"services_include": {"type": "object"},
|
||||
"modules": {"$ref": "#/definitions/list_of_strings"},
|
||||
@@ -144,7 +145,8 @@ var schema = `{
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"keys": {"type": "object"}
|
||||
"keys": {"type": "object"},
|
||||
"daemon": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -113,6 +113,7 @@ type RancherConfig struct {
|
||||
NoSharedRoot bool `yaml:"no_sharedroot,omitempty"`
|
||||
Log bool `yaml:"log,omitempty"`
|
||||
ForceConsoleRebuild bool `yaml:"force_console_rebuild,omitempty"`
|
||||
Recovery bool `yaml:"recovery,omitempty"`
|
||||
Disable []string `yaml:"disable,omitempty"`
|
||||
ServicesInclude map[string]bool `yaml:"services_include,omitempty"`
|
||||
Modules []string `yaml:"modules,omitempty"`
|
||||
@@ -174,7 +175,8 @@ type DockerConfig struct {
|
||||
}
|
||||
|
||||
type SSHConfig struct {
|
||||
Keys map[string]string `yaml:"keys,omitempty"`
|
||||
Keys map[string]string `yaml:"keys,omitempty"`
|
||||
Daemon bool `yaml:"daemon,omitempty"`
|
||||
}
|
||||
|
||||
type StateConfig struct {
|
||||
|
@@ -31,11 +31,15 @@ func ConvertKeysToStrings(item interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func Validate(bytes []byte) (*gojsonschema.Result, error) {
|
||||
func ValidateBytes(bytes []byte) (*gojsonschema.Result, error) {
|
||||
var rawCfg map[string]interface{}
|
||||
if err := yaml.Unmarshal([]byte(bytes), &rawCfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ValidateRawCfg(rawCfg)
|
||||
}
|
||||
|
||||
func ValidateRawCfg(rawCfg interface{}) (*gojsonschema.Result, error) {
|
||||
rawCfg = ConvertKeysToStrings(rawCfg).(map[string]interface{})
|
||||
loader := gojsonschema.NewGoLoader(rawCfg)
|
||||
schemaLoader := gojsonschema.NewStringLoader(schema)
|
||||
|
@@ -2,7 +2,6 @@ package docker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
composeConfig "github.com/docker/libcompose/config"
|
||||
@@ -43,11 +42,9 @@ func environmentFromCloudConfig(cfg *config.CloudConfig) map[string]string {
|
||||
environment["no_proxy"] = cfg.Rancher.Network.NoProxy
|
||||
environment["NO_PROXY"] = cfg.Rancher.Network.NoProxy
|
||||
}
|
||||
b, err := ioutil.ReadFile("/proc/version")
|
||||
if err == nil {
|
||||
elem := strings.Split(string(b), " ")
|
||||
environment["KERNEL_VERSION"] = elem[2]
|
||||
log.Debugf("Using /proc/version to set rancher.environment.KERNEL_VERSION = %s", elem[2])
|
||||
if v := config.GetKernelVersion(); v != "" {
|
||||
environment["KERNEL_VERSION"] = v
|
||||
log.Debugf("Using /proc/version to set rancher.environment.KERNEL_VERSION = %s", v)
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
@@ -4,9 +4,11 @@ layout: os-default
|
||||
|
||||
---
|
||||
|
||||
## Kernel parameters
|
||||
## Kernel boot parameters
|
||||
|
||||
There are two ways to edit the kernel parameters, in-place (editing the file and reboot) or during installation to disk.
|
||||
RancherOS parses the Linux kernel boot cmdline to add any keys it understands to its configuration. This allows you to modify what cloud-init sources it will use on boot, to enable `rancher.debug` logging, or to almost any other configuration setting.
|
||||
|
||||
There are two ways to set or modify persistent kernel parameters, in-place (editing the file and reboot) or during installation to disk.
|
||||
|
||||
### In-place editing
|
||||
|
||||
@@ -14,10 +16,6 @@ To edit the kernel boot parameters of an already installed RancherOS system, use
|
||||
|
||||
> To activate this setting, you will need to reboot.
|
||||
|
||||
#### Graphical boot screen
|
||||
|
||||
RancherOS v1.1.0 added a syslinux boot menu, which on desktop systems can be switched to graphical mode by adding `UI vesamenu.c32` to a new line in `global.cfg` (use `sudo ros config syslinux` to edit the file).
|
||||
|
||||
### During installation
|
||||
|
||||
If you want to set the extra kernel parameters when you are [Installing RancherOS to Disk]({{site.baseurl}}/os/running-rancheros/server/install-to-disk/) please use the `--append` parameter.
|
||||
@@ -25,3 +23,28 @@ If you want to set the extra kernel parameters when you are [Installing RancherO
|
||||
```bash
|
||||
$ sudo ros install -d /dev/sda --append "rancheros.autologin=tty1"
|
||||
```
|
||||
|
||||
### Graphical boot screen
|
||||
|
||||
RancherOS v1.1.0 added a Syslinux boot menu, which allows you to temporarily edit the boot paramters, or to select "Debug logging", "Autologin", both "Debug logging & Autologin" and "Recovery Console".
|
||||
|
||||
|
||||
On desktop systems the Syslinux boot menu can be switched to graphical mode by adding `UI vesamenu.c32` to a new line in `global.cfg` (use `sudo ros config syslinux` to edit the file).
|
||||
|
||||
### Useful RancherOS cloud-init or boot settings
|
||||
|
||||
#### Recovery console
|
||||
|
||||
`rancher.recovery=true` will start a single user `root` bash session as easily in the boot process, with no network, or persitent filesystem mounted. This can be used to fix disk problems, or to debug your system.
|
||||
|
||||
#### Enable/Disable sshd
|
||||
|
||||
`rancher.ssh.daemon=false` (its enabled in the os-config) can be used to start your RancherOS with no sshd daemon. This can be used to futher reduce the ports that your system is listening on.
|
||||
|
||||
#### Enable debug logging
|
||||
|
||||
`rancher.debug=true` will log everything to the console for debugging.
|
||||
|
||||
#### Autologin console
|
||||
|
||||
`rancher.autologin=<tty...>` will automatically log in the sepcified console - common values are `tty1`, `ttyS0` and `ttyAMA0` - depending on your platform.
|
||||
|
16
init/init.go
16
init/init.go
@@ -94,6 +94,13 @@ func sysInit(c *config.CloudConfig) (*config.CloudConfig, error) {
|
||||
|
||||
func MainInit() {
|
||||
log.InitDeferedLogger()
|
||||
// TODO: this breaks and does nothing if the cfg is invalid (or is it due to threading?)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Printf("Starting Recovery console: %v\n", r)
|
||||
recovery(nil)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := RunInit(); err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -263,6 +270,12 @@ func RunInit() error {
|
||||
return cfg, nil
|
||||
}},
|
||||
config.CfgFuncData{"load modules", loadModules},
|
||||
config.CfgFuncData{"recovery console", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
|
||||
if cfg.Rancher.Recovery {
|
||||
recovery(nil)
|
||||
}
|
||||
return cfg, nil
|
||||
}},
|
||||
config.CfgFuncData{"b2d env", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
|
||||
if dev := util.ResolveDevice("LABEL=B2D_STATE"); dev != "" {
|
||||
boot2DockerEnvironment = true
|
||||
@@ -411,7 +424,7 @@ func RunInit() error {
|
||||
|
||||
cfg, err := config.ChainCfgFuncs(nil, initFuncs)
|
||||
if err != nil {
|
||||
return err
|
||||
recovery(err)
|
||||
}
|
||||
|
||||
launchConfig, args := getLaunchConfig(cfg, &cfg.Rancher.SystemDocker)
|
||||
@@ -422,6 +435,7 @@ func RunInit() error {
|
||||
_, err = dfs.LaunchDocker(launchConfig, config.SystemDockerBin, args...)
|
||||
if err != nil {
|
||||
log.Errorf("Error Launching System Docker: %s", err)
|
||||
recovery(err)
|
||||
return err
|
||||
}
|
||||
// Code never gets here - rancher.system_docker.exec=true
|
||||
|
96
init/recovery.go
Normal file
96
init/recovery.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
composeConfig "github.com/docker/libcompose/config"
|
||||
"github.com/docker/libcompose/yaml"
|
||||
"github.com/rancher/os/compose"
|
||||
"github.com/rancher/os/config"
|
||||
"github.com/rancher/os/netconf"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// TODO: move this into the os-config file so it can be customised.
|
||||
recoveryDockerService = composeConfig.ServiceConfigV1{
|
||||
Image: config.OsBase,
|
||||
Command: yaml.Command{
|
||||
"ros",
|
||||
"recovery-init",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
config.DetachLabel: "false",
|
||||
config.ScopeLabel: "system",
|
||||
},
|
||||
LogDriver: "json-file",
|
||||
Net: "host",
|
||||
Uts: "host",
|
||||
Pid: "host",
|
||||
Ipc: "host",
|
||||
Privileged: true,
|
||||
Volumes: []string{
|
||||
"/dev:/host/dev",
|
||||
"/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt.rancher",
|
||||
"/lib/modules:/lib/modules",
|
||||
"/lib/firmware:/lib/firmware",
|
||||
"/usr/bin/ros:/usr/bin/ros:ro",
|
||||
"/usr/bin/ros:/usr/bin/cloud-init-save",
|
||||
"/usr/bin/ros:/usr/bin/respawn:ro",
|
||||
"/usr/share/ros:/usr/share/ros:ro",
|
||||
"/var/lib/rancher:/var/lib/rancher",
|
||||
"/var/lib/rancher/conf:/var/lib/rancher/conf",
|
||||
"/var/run:/var/run",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func recoveryServices(cfg *config.CloudConfig) (*config.CloudConfig, error) {
|
||||
_, err := compose.RunServiceSet("recovery", cfg, map[string]*composeConfig.ServiceConfigV1{
|
||||
"recovery": &recoveryDockerService,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func recovery(initFailure error) {
|
||||
if initFailure != nil {
|
||||
log.Errorf("RancherOS has failed to boot: %v", initFailure)
|
||||
}
|
||||
log.Info("Launching recovery console")
|
||||
|
||||
var recoveryConfig config.CloudConfig
|
||||
recoveryConfig.Rancher.Defaults = config.Defaults{
|
||||
Network: netconf.NetworkConfig{
|
||||
DNS: netconf.DNSConfig{
|
||||
Nameservers: []string{
|
||||
"8.8.8.8",
|
||||
"8.8.4.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
recoveryConfig.Rancher.BootstrapDocker = config.DockerConfig{
|
||||
EngineOpts: config.EngineOpts{
|
||||
Bridge: "none",
|
||||
StorageDriver: "overlay",
|
||||
Restart: &[]bool{false}[0],
|
||||
Graph: "/var/lib/recovery-docker",
|
||||
Group: "root",
|
||||
Host: []string{"unix:///var/run/system-docker.sock"},
|
||||
UserlandProxy: &[]bool{false}[0],
|
||||
},
|
||||
}
|
||||
|
||||
_, err := startDocker(&recoveryConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = config.ChainCfgFuncs(&recoveryConfig,
|
||||
[]config.CfgFuncData{
|
||||
config.CfgFuncData{"loadImages", loadImages},
|
||||
config.CfgFuncData{"recovery console", recoveryServices},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
2
main.go
2
main.go
@@ -23,6 +23,7 @@ import (
|
||||
)
|
||||
|
||||
var entrypoints = map[string]func(){
|
||||
"autologin": control.AutologinMain,
|
||||
"cloud-init-execute": cloudinitexecute.Main,
|
||||
"cloud-init-save": cloudinitsave.Main,
|
||||
"console": control.ConsoleInitMain,
|
||||
@@ -31,6 +32,7 @@ var entrypoints = map[string]func(){
|
||||
"dockerlaunch": dfs.Main,
|
||||
"init": osInit.MainInit,
|
||||
"netconf": network.Main,
|
||||
"recovery": control.AutologinMain,
|
||||
"ros-sysinit": sysinit.Main,
|
||||
"system-docker": systemdocker.Main,
|
||||
"wait-for-docker": wait.Main,
|
||||
|
@@ -14,6 +14,8 @@ rancher:
|
||||
network:
|
||||
dns:
|
||||
nameservers: [8.8.8.8, 8.8.4.4]
|
||||
ssh:
|
||||
daemon: true
|
||||
bootstrap:
|
||||
bootstrap:
|
||||
image: {{.OS_REPO}}/os-bootstrap:{{.VERSION}}{{.SUFFIX}}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Add `UI vesamenu.c32` to a new line in `global.cfg` to switch to GUI bootmenu (use `sudo ros config syslinux`)
|
||||
UI menu.c32
|
||||
TIMEOUT 20 #2s
|
||||
TIMEOUT 30 #3s
|
||||
PROMPT 0
|
||||
|
||||
# doesn't appear to work here?
|
||||
|
@@ -9,23 +9,30 @@ LABEL rancheros-${LABEL}
|
||||
|
||||
LABEL rancheros-${LABEL}-autologin
|
||||
SAY rancheros-${LABEL}-autologin: autologin RancherOS ${VERSION} ${KERNEL_VERSION}
|
||||
MENU LABEL rancher.autologin
|
||||
MENU LABEL Autologin on tty1 and ttyS0
|
||||
MENU INDENT 2
|
||||
COM32 cmd.c32
|
||||
APPEND rancheros-${LABEL} rancher.autologin=tty1 rancher.autologin=ttyS0
|
||||
|
||||
LABEL rancheros-${LABEL}-debug
|
||||
SAY rancheros-${LABEL}-debug: debug RancherOS ${VERSION} ${KERNEL_VERSION}
|
||||
MENU LABEL rancher.debug=true
|
||||
MENU LABEL Debug logging
|
||||
MENU INDENT 2
|
||||
COM32 cmd.c32
|
||||
APPEND rancheros-${LABEL} rancher.debug=true
|
||||
|
||||
LABEL rancheros-${LABEL}-debug-autologin
|
||||
SAY rancheros-${LABEL}-debug-autolgin: debug and autologin RancherOS ${VERSION} ${KERNEL_VERSION}
|
||||
MENU LABEL rancher.debug and rancher.autologin
|
||||
MENU LABEL Autologin on tty1 and ttyS0 plus Debug logging
|
||||
MENU INDENT 2
|
||||
COM32 cmd.c32
|
||||
APPEND rancheros-${LABEL} rancher.autologin=tty1 rancher.autologin=ttyS0 rancher.debug=true
|
||||
|
||||
LABEL rancheros-${LABEL}-recovery
|
||||
SAY rancheros-${LABEL}-recovery: recovery console RancherOS ${VERSION} ${KERNEL_VERSION}
|
||||
MENU LABEL Recovery console
|
||||
MENU INDENT 2
|
||||
COM32 cmd.c32
|
||||
APPEND rancheros-${LABEL} rancher.recovery=true
|
||||
|
||||
MENU SEPARATOR
|
||||
|
@@ -171,7 +171,7 @@ if [ "$APPEND_INIT" != "" ]; then
|
||||
fi
|
||||
|
||||
if [ "$BOOT_PXE" == "1" ]; then
|
||||
KERNEL_ARGS="console=tty1 rancher.console=tty1 rancher.autologin=tty1 ${KERNEL_ARGS}"
|
||||
KERNEL_ARGS="console=tty1 rancher.autologin=tty1 ${KERNEL_ARGS}"
|
||||
set -ex
|
||||
PIXIECORE=$(which pixiecore)
|
||||
sudo -E $PIXIECORE boot \
|
||||
@@ -311,6 +311,7 @@ if [ "$QEMU" == "1" ]; then
|
||||
$(eval "${hd["$ARCH"]} ${HD}") \
|
||||
${SECOND_DRIVE_ENABLE} \
|
||||
-smp 1 \
|
||||
-device virtio-rng-pci \
|
||||
${CLOUD_CONFIG_DISK} \
|
||||
-fsdev local,security_model=none,id=fsdev1,path=${HOME} \
|
||||
-device virtio-9p-pci,id=fs1,fsdev=fsdev1,mount_tag=home \
|
||||
@@ -338,6 +339,7 @@ elif [ "$BOOT_ISO" == "1" ] ||
|
||||
$(eval "${hd["$ARCH"]} ${HD}") \
|
||||
${SECOND_DRIVE_ENABLE} \
|
||||
-smp 1 \
|
||||
-device virtio-rng-pci \
|
||||
${ISO_OPTS} \
|
||||
"${@}"
|
||||
elif [ "$QIND" == "1" ]; then
|
||||
|
@@ -52,4 +52,4 @@ if [ "$ENGINE_REGISTRY_MIRROR" != "" ]; then
|
||||
REGISTRY_MIRROR="rancher.bootstrap_docker.registry_mirror=${ENGINE_REGISTRY_MIRROR} rancher.system_docker.registry_mirror=${ENGINE_REGISTRY_MIRROR} rancher.docker.registry_mirror=${ENGINE_REGISTRY_MIRROR}"
|
||||
fi
|
||||
|
||||
DEFAULT_KERNEL_ARGS="printk.devkmsg=on rancher.debug=true rancher.password=rancher console=${TTYCONS} rancher.autologin=${TTYCONS} ${REGISTRY_MIRROR} "
|
||||
DEFAULT_KERNEL_ARGS="printk.devkmsg=on rancher.debug=true rancher.password=rancher console=tty1 rancher.autologin=tty1 console=${TTYCONS} rancher.autologin=${TTYCONS} ${REGISTRY_MIRROR} "
|
||||
|
@@ -35,6 +35,7 @@
|
||||
"no_sharedroot": {"type": "boolean"},
|
||||
"log": {"type": "boolean"},
|
||||
"force_console_rebuild": {"type": "boolean"},
|
||||
"recovery": {"type": "boolean"},
|
||||
"disable": {"$ref": "#/definitions/list_of_strings"},
|
||||
"services_include": {"type": "object"},
|
||||
"modules": {"$ref": "#/definitions/list_of_strings"},
|
||||
@@ -142,7 +143,8 @@
|
||||
"additionalProperties": false,
|
||||
|
||||
"properties": {
|
||||
"keys": {"type": "object"}
|
||||
"keys": {"type": "object"},
|
||||
"daemon": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
|
||||
|
Reference in New Issue
Block a user