mirror of
https://github.com/rancher/os.git
synced 2025-09-02 15:24:32 +00:00
get started on the new cli
Signed-off-by: Sven Dowideit <SvenDowideit@home.org.au>
This commit is contained in:
@@ -53,21 +53,31 @@ func Main() {
|
|||||||
HideHelp: true,
|
HideHelp: true,
|
||||||
Action: listServices,
|
Action: listServices,
|
||||||
}, {
|
}, {
|
||||||
Name: "add, install, upgrade",
|
Name: "install",
|
||||||
// TODO: add an --apply or --up ...
|
// TODO: add an --apply or --up ...
|
||||||
// TODO: also support the repo-name prefix
|
// TODO: also support the repo-name prefix
|
||||||
ShortName: "",
|
ShortName: "",
|
||||||
Usage: "install/upgrade service / RancherOS",
|
Usage: "install/upgrade service / RancherOS",
|
||||||
HideHelp: true,
|
HideHelp: true,
|
||||||
Action: service.Enable,
|
Action: service.Enable,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "apply",
|
||||||
|
Usage: "Switch console/engine, or start service.",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "force",
|
||||||
|
Usage: "Don't ask questions.",
|
||||||
|
},
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
Name: "remove, delete",
|
Name: "remove",
|
||||||
ShortName: "",
|
ShortName: "",
|
||||||
Usage: "remove service",
|
Usage: "remove service",
|
||||||
HideHelp: true,
|
HideHelp: true,
|
||||||
Action: service.Del,
|
Action: service.Del,
|
||||||
}, {
|
}, {
|
||||||
Name: "logs, log",
|
Name: "logs",
|
||||||
Usage: "View output from containers",
|
Usage: "View output from containers",
|
||||||
//Before: verifyOneOrMoreServices,
|
//Before: verifyOneOrMoreServices,
|
||||||
Action: composeApp.WithProject(factory, serviceApp.ProjectLog),
|
Action: composeApp.WithProject(factory, serviceApp.ProjectLog),
|
||||||
@@ -238,21 +248,9 @@ func GetAllServices() map[string]map[string]*libcomposeConfig.ServiceConfigV1 {
|
|||||||
}
|
}
|
||||||
for serviceType, serviceList := range services {
|
for serviceType, serviceList := range services {
|
||||||
for _, serviceLongName := range serviceList {
|
for _, serviceLongName := range serviceList {
|
||||||
servicePath := fmt.Sprintf("%s/%s.yml", repoName, serviceLongName)
|
p, err := service.LoadService(repoName, serviceLongName)
|
||||||
//log.Infof("loading %s", serviceLongName)
|
|
||||||
content, err := network.CacheLookup(servicePath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to load %s: %v", servicePath, err)
|
log.Errorf("Failed to load %s/%s : %v", repoName, serviceLongName, err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
if content, err = ComposeToCloudConfig(content); err != nil {
|
|
||||||
log.Errorf("Failed to convert compose to cloud-config syntax: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := config.ReadConfig(content, true)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to load %s : %v", servicePath, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// yes, the serviceLongName is really only the yml file name
|
// yes, the serviceLongName is really only the yml file name
|
||||||
@@ -274,21 +272,6 @@ func GetAllServices() map[string]map[string]*libcomposeConfig.ServiceConfigV1 {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: copied from cloudinitsave, move to config.
|
|
||||||
func ComposeToCloudConfig(bytes []byte) ([]byte, error) {
|
|
||||||
compose := make(map[interface{}]interface{})
|
|
||||||
err := yaml.Unmarshal(bytes, &compose)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return yaml.Marshal(map[interface{}]interface{}{
|
|
||||||
"rancher": map[interface{}]interface{}{
|
|
||||||
"services": compose,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var originalCli = []cli.Command{
|
var originalCli = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "config",
|
Name: "config",
|
||||||
|
3
cmd/control/console.go
Normal file → Executable file
3
cmd/control/console.go
Normal file → Executable file
@@ -66,7 +66,7 @@ func consoleSwitch(c *cli.Context) error {
|
|||||||
1. destroy the current console container
|
1. destroy the current console container
|
||||||
2. log you out
|
2. log you out
|
||||||
3. restart Docker`)
|
3. restart Docker`)
|
||||||
if !yes("Continue") {
|
if !util.Yes("Continue") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,6 +111,7 @@ func consoleEnable(c *cli.Context) error {
|
|||||||
cfg := config.LoadConfig()
|
cfg := config.LoadConfig()
|
||||||
validateConsole(newConsole, cfg)
|
validateConsole(newConsole, cfg)
|
||||||
|
|
||||||
|
//TODO: why does default not need to be staged?
|
||||||
if newConsole != "default" {
|
if newConsole != "default" {
|
||||||
if err := compose.StageServices(cfg, newConsole); err != nil {
|
if err := compose.StageServices(cfg, newConsole); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@@ -153,7 +153,7 @@ func installAction(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !kexec && reboot && (force || yes("Continue with reboot")) {
|
if !kexec && reboot && (force || util.Yes("Continue with reboot")) {
|
||||||
log.Info("Rebooting")
|
log.Info("Rebooting")
|
||||||
power.Reboot()
|
power.Reboot()
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ func runInstall(image, installType, cloudConfig, device, kappend string, force,
|
|||||||
fmt.Printf("Installing from %s\n", image)
|
fmt.Printf("Installing from %s\n", image)
|
||||||
|
|
||||||
if !force {
|
if !force {
|
||||||
if util.IsRunningInTty() && !yes("Continue") {
|
if util.IsRunningInTty() && !util.Yes("Continue") {
|
||||||
log.Infof("Not continuing with installation due to user not saying 'yes'")
|
log.Infof("Not continuing with installation due to user not saying 'yes'")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/rancher/os/compose"
|
"github.com/rancher/os/compose"
|
||||||
"github.com/rancher/os/config"
|
"github.com/rancher/os/config"
|
||||||
"github.com/rancher/os/docker"
|
"github.com/rancher/os/docker"
|
||||||
|
"github.com/rancher/os/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Images struct {
|
type Images struct {
|
||||||
@@ -226,7 +227,7 @@ func startUpgradeContainer(image string, stage, force, reboot, kexec bool, upgra
|
|||||||
if len(imageSplit) > 1 && imageSplit[1] == config.Version+config.Suffix {
|
if len(imageSplit) > 1 && imageSplit[1] == config.Version+config.Suffix {
|
||||||
confirmation = fmt.Sprintf("Already at version %s. Continue anyway", imageSplit[1])
|
confirmation = fmt.Sprintf("Already at version %s. Continue anyway", imageSplit[1])
|
||||||
}
|
}
|
||||||
if !force && !yes(confirmation) {
|
if !force && !util.Yes(confirmation) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +277,7 @@ func startUpgradeContainer(image string, stage, force, reboot, kexec bool, upgra
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if reboot && (force || yes("Continue with reboot")) {
|
if reboot && (force || util.Yes("Continue with reboot")) {
|
||||||
log.Info("Rebooting")
|
log.Info("Rebooting")
|
||||||
power.Reboot()
|
power.Reboot()
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,17 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
yaml "github.com/cloudfoundry-incubator/candiedyaml"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
dockerApp "github.com/docker/libcompose/cli/docker/app"
|
dockerApp "github.com/docker/libcompose/cli/docker/app"
|
||||||
|
composeConfig "github.com/docker/libcompose/config"
|
||||||
|
|
||||||
|
"github.com/docker/libcompose/project/options"
|
||||||
|
|
||||||
"github.com/docker/libcompose/project"
|
"github.com/docker/libcompose/project"
|
||||||
"github.com/rancher/os/cmd/control/service/command"
|
"github.com/rancher/os/cmd/control/service/command"
|
||||||
"github.com/rancher/os/compose"
|
"github.com/rancher/os/compose"
|
||||||
@@ -135,20 +141,125 @@ func Del(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: copied from cloudinitsave, move to config.
|
||||||
|
func ComposeToCloudConfig(bytes []byte) ([]byte, error) {
|
||||||
|
compose := make(map[interface{}]interface{})
|
||||||
|
err := yaml.Unmarshal(bytes, &compose)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return yaml.Marshal(map[interface{}]interface{}{
|
||||||
|
"rancher": map[interface{}]interface{}{
|
||||||
|
"services": compose,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should move to something like config/service.go?
|
||||||
|
// WARNING: this can contain more than one service - Josh and I aren't sure this is worth it
|
||||||
|
func LoadService(repoName, serviceLongName string) (*config.CloudConfig, error) {
|
||||||
|
servicePath := fmt.Sprintf("%s/%s.yml", repoName, serviceLongName)
|
||||||
|
//log.Infof("loading %s", serviceLongName)
|
||||||
|
content, err := network.CacheLookup(servicePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to load %s: %v", servicePath, err)
|
||||||
|
}
|
||||||
|
if content, err = ComposeToCloudConfig(content); err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to convert compose to cloud-config syntax: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := config.ReadConfig(content, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to load %s : %v", servicePath, err)
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should move to something like config/service.go?
|
||||||
|
func IsConsole(serviceCfg *config.CloudConfig) bool {
|
||||||
|
//the service is called console, and has an io.rancher.os.console label.
|
||||||
|
for serviceName, service := range serviceCfg.Rancher.Services {
|
||||||
|
if serviceName == "console" {
|
||||||
|
for k := range service.Labels {
|
||||||
|
if k == "io.rancher.os.console" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should move to something like config/service.go?
|
||||||
|
func IsEngine(serviceCfg *config.CloudConfig) bool {
|
||||||
|
//the service is called docker, and the command is "ros user-docker"
|
||||||
|
for serviceName, service := range serviceCfg.Rancher.Services {
|
||||||
|
log.Infof("serviceName == %s", serviceName)
|
||||||
|
if serviceName == "docker" {
|
||||||
|
cmd := strings.Join(service.Command, " ")
|
||||||
|
log.Infof("service command == %s", cmd)
|
||||||
|
if cmd == "ros user-docker" {
|
||||||
|
log.Infof("yes, its a docker engine")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func Enable(c *cli.Context) error {
|
func Enable(c *cli.Context) error {
|
||||||
cfg := config.LoadConfig()
|
cfg := config.LoadConfig()
|
||||||
|
|
||||||
var enabledServices []string
|
var enabledServices []string
|
||||||
|
var consoleService, engineService string
|
||||||
|
var errorServices []string
|
||||||
|
serviceMap := make(map[string]*config.CloudConfig)
|
||||||
|
|
||||||
for _, service := range c.Args() {
|
for _, service := range c.Args() {
|
||||||
validateService(service, cfg)
|
//validateService(service, cfg)
|
||||||
|
//log.Infof("start4")
|
||||||
|
// TODO: need to search for the service in all the repos.
|
||||||
|
// TODO: also need to deal with local file paths and URLs
|
||||||
|
serviceConfig, err := LoadService("core", service)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to load %s: %s", service, err)
|
||||||
|
errorServices = append(errorServices, service)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
serviceMap[service] = serviceConfig
|
||||||
|
}
|
||||||
|
if len(serviceMap) == 0 {
|
||||||
|
log.Fatalf("No valid Services found")
|
||||||
|
}
|
||||||
|
if len(errorServices) > 0 {
|
||||||
|
if c.Bool("force") || !util.Yes("Some services failed to load, Continue?") {
|
||||||
|
log.Fatalf("Services failed to load: %v", errorServices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for service, serviceConfig := range serviceMap {
|
||||||
if val, ok := cfg.Rancher.ServicesInclude[service]; !ok || !val {
|
if val, ok := cfg.Rancher.ServicesInclude[service]; !ok || !val {
|
||||||
if isLocal(service) && !strings.HasPrefix(service, "/var/lib/rancher/conf") {
|
if isLocal(service) && !strings.HasPrefix(service, "/var/lib/rancher/conf") {
|
||||||
log.Fatalf("ERROR: Service should be in path /var/lib/rancher/conf")
|
log.Fatalf("ERROR: Service should be in path /var/lib/rancher/conf")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.Rancher.ServicesInclude[service] = true
|
if IsConsole(serviceConfig) {
|
||||||
|
log.Debugf("Enabling the %s console", service)
|
||||||
|
if err := config.Set("rancher.console", service); err != nil {
|
||||||
|
log.Errorf("Failed to update 'rancher.console': %v", err)
|
||||||
|
}
|
||||||
|
consoleService = service
|
||||||
|
|
||||||
|
} else if IsEngine(serviceConfig) {
|
||||||
|
log.Debugf("Enabling the %s user engine", service)
|
||||||
|
if err := config.Set("rancher.docker.engine", service); err != nil {
|
||||||
|
log.Errorf("Failed to update 'rancher.docker.engine': %v", err)
|
||||||
|
}
|
||||||
|
engineService = service
|
||||||
|
} else {
|
||||||
|
cfg.Rancher.ServicesInclude[service] = true
|
||||||
|
}
|
||||||
enabledServices = append(enabledServices, service)
|
enabledServices = append(enabledServices, service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,6 +274,64 @@ func Enable(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: fix the case where the user is applying both a new console and a new docker engine
|
||||||
|
if consoleService != "" && c.Bool("apply") {
|
||||||
|
//ros console switch.
|
||||||
|
if !c.Bool("force") {
|
||||||
|
fmt.Println(`Switching consoles will
|
||||||
|
1. destroy the current console container
|
||||||
|
2. log you out
|
||||||
|
3. restart Docker`)
|
||||||
|
if !util.Yes("Continue") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switchService, err := compose.CreateService(nil, "switch-console", &composeConfig.ServiceConfigV1{
|
||||||
|
LogDriver: "json-file",
|
||||||
|
Privileged: true,
|
||||||
|
Net: "host",
|
||||||
|
Pid: "host",
|
||||||
|
Image: config.OsBase,
|
||||||
|
Labels: map[string]string{
|
||||||
|
config.ScopeLabel: config.System,
|
||||||
|
},
|
||||||
|
Command: []string{"/usr/bin/ros", "switch-console", consoleService},
|
||||||
|
VolumesFrom: []string{"all-volumes"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = switchService.Delete(context.Background(), options.Delete{}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = switchService.Up(context.Background(), options.Up{}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = switchService.Log(context.Background(), true); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if engineService != "" && c.Bool("apply") {
|
||||||
|
log.Info("Starting the %s engine", engineService)
|
||||||
|
project, err := compose.GetProject(cfg, true, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = project.Stop(context.Background(), 10, "docker"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = compose.LoadSpecialService(project, cfg, "docker", engineService); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = project.Up(context.Background(), options.Up{}, "docker"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package control
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/rancher/os/log"
|
"github.com/rancher/os/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func yes(question string) bool {
|
func Yes(question string) bool {
|
||||||
fmt.Printf("%s [y/N]: ", question)
|
fmt.Printf("%s [y/N]: ", question)
|
||||||
in := bufio.NewReader(os.Stdin)
|
in := bufio.NewReader(os.Stdin)
|
||||||
line, err := in.ReadString('\n')
|
line, err := in.ReadString('\n')
|
Reference in New Issue
Block a user