diff --git a/cli/info.go b/cli/info.go index f5e1dfa96..cd39bf212 100644 --- a/cli/info.go +++ b/cli/info.go @@ -68,13 +68,13 @@ func prettyPrintInfo(cli *Cli, info *types.SystemInfo) error { fmt.Fprintln(os.Stdout, "Logging Driver:", info.LoggingDriver) fmt.Fprintln(os.Stdout, "Volume Drivers:", info.VolumeDrivers) fmt.Fprintln(os.Stdout, "Cgroup Driver:", info.CgroupDriver) + fmt.Fprintln(os.Stdout, "Default Runtime:", info.DefaultRuntime) if len(info.Runtimes) > 0 { - fmt.Fprintln(os.Stdout, "Runtimes:") + fmt.Fprintf(os.Stdout, "Runtimes:") for name := range info.Runtimes { - fmt.Fprintf(os.Stdout, "%s", name) + fmt.Fprintf(os.Stdout, " %s", name) } fmt.Fprint(os.Stdout, "\n") - fmt.Fprintln(os.Stdout, "Default Runtime:", info.DefaultRuntime) } fmt.Fprintln(os.Stdout, "runc:", info.RuncCommit) fmt.Fprintln(os.Stdout, "containerd:", info.ContainerdCommit) diff --git a/config/opt/runtime.go b/config/opt/runtime.go new file mode 100644 index 000000000..7244bfbe0 --- /dev/null +++ b/config/opt/runtime.go @@ -0,0 +1,59 @@ +package opt + +import ( + "fmt" + "strings" + + "github.com/alibaba/pouch/apis/types" +) + +// Runtime defines runtimes information +type Runtime struct { + values *map[string]types.Runtime +} + +// NewRuntime initials a Runtime struct +func NewRuntime(rts *map[string]types.Runtime) *Runtime { + if rts == nil { + rts = &map[string]types.Runtime{} + } + + if *rts == nil { + *rts = map[string]types.Runtime{} + } + + rt := &Runtime{values: rts} + return rt +} + +// Set implement Runtime as pflag.Value interface +func (r *Runtime) Set(val string) error { + splits := strings.Split(val, "=") + if len(splits) != 2 || splits[0] == "" || splits[1] == "" { + return fmt.Errorf("invalid runtime %s, correct format must be runtime=path", val) + } + + name := splits[0] + path := splits[1] + if _, exist := (*r.values)[name]; exist { + return fmt.Errorf("runtime %s already registers to daemon", name) + } + + (*r.values)[name] = types.Runtime{Path: path} + return nil +} + +// String implement Runtime as pflag.Value interface +func (r *Runtime) String() string { + var str []string + for k := range *r.values { + str = append(str, fmt.Sprintf("%s", k)) + } + + return fmt.Sprintf("%v", str) +} + +// Type implement Runtime as pflag.Value interface +func (r *Runtime) Type() string { + return "value" +} diff --git a/config/opt/runtime_test.go b/config/opt/runtime_test.go new file mode 100644 index 000000000..f380ea388 --- /dev/null +++ b/config/opt/runtime_test.go @@ -0,0 +1,25 @@ +package opt + +import ( + "testing" + + "github.com/alibaba/pouch/apis/types" + "github.com/stretchr/testify/assert" +) + +func TestNewRuntime(t *testing.T) { + assert := assert.New(t) + + for _, r := range []*map[string]types.Runtime{ + nil, + {}, + { + "a": {}, + "b": {Path: "foo"}, + }, + } { + runtime := NewRuntime(r) + // just test no panic here + assert.NoError(runtime.Set("foo=bar")) + } +} diff --git a/daemon/config/config.go b/daemon/config/config.go index 159cd6476..9da65799e 100644 --- a/daemon/config/config.go +++ b/daemon/config/config.go @@ -108,6 +108,11 @@ type Config struct { // oom_score_adj for the daemon OOMScoreAdjust int `json:"oom-score-adjust,omitempty"` + + // runtimes config + // TODO(Ace-Tang): runtime args is not support, since containerd is not support, + // add a resolution later if it needed. + Runtimes map[string]types.Runtime `json:"add-runtime,omitempty"` } // Validate validates the user input config. @@ -128,6 +133,16 @@ func (cfg *Config) Validate() error { // TODO: add config validation + // validates runtimes config + if len(cfg.Runtimes) == 0 { + cfg.Runtimes = make(map[string]types.Runtime) + } + if _, exist := cfg.Runtimes[cfg.DefaultRuntime]; exist { + return fmt.Errorf("default runtime %s cannot be re-register", cfg.DefaultRuntime) + } + // add default runtime + cfg.Runtimes[cfg.DefaultRuntime] = types.Runtime{Path: cfg.DefaultRuntime} + return nil } @@ -141,17 +156,16 @@ func (cfg *Config) MergeConfigurations(flagSet *pflag.FlagSet) error { return fmt.Errorf("failed to read contents from config file %s: %s", cfg.ConfigFile, err) } - var origin map[string]interface{} - if err = json.NewDecoder(bytes.NewReader(contents)).Decode(&origin); err != nil { + var fileFlags map[string]interface{} + if err = json.NewDecoder(bytes.NewReader(contents)).Decode(&fileFlags); err != nil { return fmt.Errorf("failed to decode json: %s", err) } - if len(origin) == 0 { + if len(fileFlags) == 0 { return nil } - fileFlags := make(map[string]interface{}, 0) - iterateConfig(origin, fileFlags) + transferTLSConfig(fileFlags) // check if invalid or unknown flag exist in config file if err = getUnknownFlags(flagSet, fileFlags); err != nil { @@ -211,6 +225,29 @@ func (cfg *Config) delValue(flagSet *pflag.FlagSet, fileFlags map[string]interfa return cfg } +// transferTLSConfig fetch key value from tls config +// { +// "tlscert": "...", +// "tlscacert": "..." +// } +// add this transfer logic since no flag named TLS, but tlscert, tlscert... +// we should fetch them to do unknown flags and conflict flags check +func transferTLSConfig(config map[string]interface{}) { + v, exist := config["TLS"] + if !exist { + return + } + + var tlscofig map[string]interface{} + iterateConfig(map[string]interface{}{ + "TLS": v, + }, tlscofig) + + for k, v := range tlscofig { + config[k] = v + } +} + // iterateConfig resolves key-value from config file iteratly. func iterateConfig(origin map[string]interface{}, config map[string]interface{}) { for k, v := range origin { diff --git a/daemon/mgr/container.go b/daemon/mgr/container.go index b2e617ecd..f3dc83059 100644 --- a/daemon/mgr/container.go +++ b/daemon/mgr/container.go @@ -267,6 +267,10 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty config.HostConfig.Runtime = mgr.Config.DefaultRuntime } + if _, exist := mgr.Config.Runtimes[config.HostConfig.Runtime]; !exist { + return nil, fmt.Errorf("unknown runtime %s", config.HostConfig.Runtime) + } + config.Image = primaryRef.String() // create a snapshot with image. if err := mgr.Client.CreateSnapshot(ctx, id, config.Image); err != nil { diff --git a/daemon/mgr/system.go b/daemon/mgr/system.go index ef87c9a22..8dede7d16 100644 --- a/daemon/mgr/system.go +++ b/daemon/mgr/system.go @@ -140,7 +140,7 @@ func (mgr *SystemManager) Info() (types.SystemInfo, error) { PouchRootDir: mgr.config.HomeDir, RegistryConfig: &mgr.config.RegistryService, // RuncCommit: , - // Runtimes: , + Runtimes: mgr.config.Runtimes, // SecurityOptions: , ServerVersion: version.Version, ListenAddresses: mgr.config.Listen, diff --git a/hack/make.sh b/hack/make.sh index a2dcebdea..2304a2c64 100755 --- a/hack/make.sh +++ b/hack/make.sh @@ -260,10 +260,10 @@ function target # start pouch daemon echo "start pouch daemon" if stat /usr/bin/lxcfs ; then - $POUCHD --debug --enable-lxcfs=true \ + $POUCHD --debug --enable-lxcfs=true --add-runtime runv=runv \ --lxcfs=/usr/bin/lxcfs > "$TMP/log" 2>&1 & else - $POUCHD --debug > "$TMP/log" 2>&1 & + $POUCHD --debug --add-runtime runv=runv > "$TMP/log" 2>&1 & fi # wait until pouch daemon is ready diff --git a/main.go b/main.go index 1a8b3e338..50cafc7bf 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "syscall" "time" + "github.com/alibaba/pouch/config/opt" "github.com/alibaba/pouch/daemon" "github.com/alibaba/pouch/daemon/config" "github.com/alibaba/pouch/lxcfs" @@ -42,17 +43,11 @@ func main() { Args: cobra.NoArgs, SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - return runDaemon() + return runDaemon(cmd) }, } setupFlags(cmdServe) - parseFlags(cmdServe, os.Args[1:]) - if err := loadDaemonFile(cfg, cmdServe.Flags()); err != nil { - logrus.Errorf("failed to load daemon file: %s", err) - os.Exit(1) - } - if err := cmdServe.Execute(); err != nil { logrus.Error(err) os.Exit(1) @@ -116,21 +111,16 @@ func setupFlags(cmd *cobra.Command) { flagSet.BoolVar(&cfg.EnableProfiler, "enable-profiler", false, "Set if pouchd setup profiler") flagSet.StringVar(&cfg.Pidfile, "pidfile", "/var/run/pouch.pid", "Save daemon pid") flagSet.IntVar(&cfg.OOMScoreAdjust, "oom-score-adj", -500, "Set the oom_score_adj for the daemon") -} + flagSet.Var(opt.NewRuntime(&cfg.Runtimes), "add-runtime", "register a OCI runtime to daemon") -// parse flags -func parseFlags(cmd *cobra.Command, flags []string) { - err := cmd.Flags().Parse(flags) - if err == nil || err == pflag.ErrHelp { - return - } - - cmd.SetOutput(os.Stderr) - cmd.Usage() } // runDaemon prepares configs, setups essential details and runs pouchd daemon. -func runDaemon() error { +func runDaemon(cmd *cobra.Command) error { + if err := loadDaemonFile(cfg, cmd.Flags()); err != nil { + return fmt.Errorf("failed to load daemon file: %s", err) + } + //user specifies --version or -v, print version and return. if printVersion { fmt.Printf("pouchd version: %s, build: %s, build at: %s\n", version.Version, version.GitCommit, version.BuildTime) diff --git a/test/z_cli_daemon_test.go b/test/z_cli_daemon_test.go index 838deb98b..22e7b965e 100644 --- a/test/z_cli_daemon_test.go +++ b/test/z_cli_daemon_test.go @@ -451,3 +451,18 @@ func (suite *PouchDaemonSuite) TestDaemonStartOverOneTimes(c *check.C) { c.Assert(err, check.NotNil) } + +// TestDaemonWithMultiRuntimes tests start daemon with multiple runtimes +func (suite *PouchDaemonSuite) TestDaemonWithMultiRuntimes(c *check.C) { + dcfg1, err := StartDefaultDaemonDebug( + "--add-runtime", "foo=bar") + c.Assert(err, check.IsNil) + dcfg1.KillDaemon() + + // should fail if runtime name equal + dcfg2, err := StartDefaultDaemonDebug( + "--add-runtime", "runa=runa", + "--add-runtime", "runa=runa") + c.Assert(err, check.NotNil) + dcfg2.KillDaemon() +}