server: refactor to use Config struct
This paves the way for having a configuration file that is loaded rather than everything being set via the command-line. Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
parent
3f48986ea0
commit
7bf5110b76
5 changed files with 230 additions and 45 deletions
|
@ -20,46 +20,94 @@ const (
|
|||
pausePath = "/usr/libexec/ocid/pause"
|
||||
)
|
||||
|
||||
// DefaultConfig returns the default configuration for ocid.
|
||||
func DefaultConfig() *server.Config {
|
||||
return &server.Config{
|
||||
RootConfig: server.RootConfig{
|
||||
Root: ocidRoot,
|
||||
SandboxDir: filepath.Join(ocidRoot, "sandboxes"),
|
||||
ContainerDir: filepath.Join(ocidRoot, "containers"),
|
||||
},
|
||||
APIConfig: server.APIConfig{
|
||||
Listen: "/var/run/ocid.sock",
|
||||
},
|
||||
RuntimeConfig: server.RuntimeConfig{
|
||||
Runtime: "/usr/bin/runc",
|
||||
Conmon: conmonPath,
|
||||
SELinux: selinux.SelinuxEnabled(),
|
||||
},
|
||||
ImageConfig: server.ImageConfig{
|
||||
Pause: pausePath,
|
||||
ImageStore: filepath.Join(ocidRoot, "store"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mergeConfig(config *server.Config, ctx *cli.Context) error {
|
||||
// Override options set with the CLI.
|
||||
if ctx.GlobalIsSet("conmon") {
|
||||
config.Conmon = ctx.GlobalString("conmon")
|
||||
}
|
||||
if ctx.GlobalIsSet("pause") {
|
||||
config.Pause = ctx.GlobalString("pause")
|
||||
}
|
||||
if ctx.GlobalIsSet("root") {
|
||||
config.Root = ctx.GlobalString("root")
|
||||
}
|
||||
if ctx.GlobalIsSet("sandboxdir") {
|
||||
config.SandboxDir = ctx.GlobalString("sandboxdir")
|
||||
}
|
||||
if ctx.GlobalIsSet("containerdir") {
|
||||
config.ContainerDir = ctx.GlobalString("containerdir")
|
||||
}
|
||||
if ctx.GlobalIsSet("listen") {
|
||||
config.Listen = ctx.GlobalString("listen")
|
||||
}
|
||||
if ctx.GlobalIsSet("runtime") {
|
||||
config.Runtime = ctx.GlobalString("runtime")
|
||||
}
|
||||
if ctx.GlobalIsSet("selinux") {
|
||||
config.SELinux = ctx.GlobalBool("selinux")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "ocid"
|
||||
app.Usage = "ocid server"
|
||||
app.Version = "0.0.1"
|
||||
app.Metadata = map[string]interface{}{
|
||||
"config": DefaultConfig(),
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "conmon",
|
||||
Value: conmonPath,
|
||||
Usage: "path to the conmon executable",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pause",
|
||||
Value: pausePath,
|
||||
Usage: "path to the pause executable",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "root",
|
||||
Value: ocidRoot,
|
||||
Usage: "ocid root dir",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "sandboxdir",
|
||||
Value: filepath.Join(ocidRoot, "sandboxes"),
|
||||
Usage: "ocid pod sandbox dir",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "containerdir",
|
||||
Value: filepath.Join(ocidRoot, "containers"),
|
||||
Usage: "ocid container dir",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "listen",
|
||||
Value: "/var/run/ocid.sock",
|
||||
Usage: "path to ocid socket",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "runtime",
|
||||
Value: "/usr/bin/runc",
|
||||
Usage: "OCI runtime path",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
|
@ -83,12 +131,16 @@ func main() {
|
|||
}
|
||||
|
||||
app.Before = func(c *cli.Context) error {
|
||||
// Load the configuration file.
|
||||
config := c.App.Metadata["config"].(*server.Config)
|
||||
if err := mergeConfig(config, c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.GlobalBool("debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if !c.GlobalBool("selinux") {
|
||||
selinux.SetDisabled()
|
||||
}
|
||||
|
||||
if path := c.GlobalString("log"); path != "" {
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666)
|
||||
if err != nil {
|
||||
|
@ -96,10 +148,7 @@ func main() {
|
|||
}
|
||||
logrus.SetOutput(f)
|
||||
}
|
||||
if _, err := os.Stat(c.GlobalString("runtime")); os.IsNotExist(err) {
|
||||
// path to runtime does not exist
|
||||
return fmt.Errorf("invalid --runtime value %q", err)
|
||||
}
|
||||
|
||||
switch c.GlobalString("log-format") {
|
||||
case "text":
|
||||
// retain logrus's default.
|
||||
|
@ -113,25 +162,31 @@ func main() {
|
|||
}
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
socketPath := c.String("listen")
|
||||
config := c.App.Metadata["config"].(*server.Config)
|
||||
|
||||
if !config.SELinux {
|
||||
selinux.SetDisabled()
|
||||
}
|
||||
|
||||
if _, err := os.Stat(config.Runtime); os.IsNotExist(err) {
|
||||
// path to runtime does not exist
|
||||
return fmt.Errorf("invalid --runtime value %q", err)
|
||||
}
|
||||
|
||||
// Remove the socket if it already exists
|
||||
if _, err := os.Stat(socketPath); err == nil {
|
||||
if err := os.Remove(socketPath); err != nil {
|
||||
if _, err := os.Stat(config.Listen); err == nil {
|
||||
if err := os.Remove(config.Listen); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
lis, err := net.Listen("unix", socketPath)
|
||||
lis, err := net.Listen("unix", config.Listen)
|
||||
if err != nil {
|
||||
logrus.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
|
||||
s := grpc.NewServer()
|
||||
|
||||
containerDir := c.String("containerdir")
|
||||
sandboxDir := c.String("sandboxdir")
|
||||
conmonPath := c.String("conmon")
|
||||
pausePath := c.String("pause")
|
||||
service, err := server.New(c.String("runtime"), c.String("root"), sandboxDir, containerDir, conmonPath, pausePath)
|
||||
service, err := server.New(config)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
|
135
server/config.go
Normal file
135
server/config.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
// Config represents the entire set of configuration values that can be set for
|
||||
// the server. This is intended to be loaded from a toml-encoded config file.
|
||||
type Config struct {
|
||||
RootConfig
|
||||
APIConfig
|
||||
RuntimeConfig
|
||||
ImageConfig
|
||||
}
|
||||
|
||||
// This structure is necessary to fake the TOML tables when parsing,
|
||||
// while also not requiring a bunch of layered structs for no good
|
||||
// reason.
|
||||
|
||||
// RootConfig represents the root of the "ocid" TOML config table.
|
||||
type RootConfig struct {
|
||||
// Root is a path to the "root directory" where all information not
|
||||
// explicitly handled by other options will be stored.
|
||||
Root string `toml:"root"`
|
||||
|
||||
// SandboxDir is the directory where ocid will store all of its sandbox
|
||||
// state and other information.
|
||||
SandboxDir string `toml:"sandbox_dir"`
|
||||
|
||||
// ContainerDir is the directory where ocid will store all of its container
|
||||
// state and other information.
|
||||
ContainerDir string `toml:"container_dir"`
|
||||
}
|
||||
|
||||
// APIConfig represents the "ocid.api" TOML config table.
|
||||
type APIConfig struct {
|
||||
// Listen is the path to the AF_LOCAL socket on which cri-o will listen.
|
||||
// This may support proto://addr formats later, but currently this is just
|
||||
// a path.
|
||||
Listen string `toml:"listen"`
|
||||
}
|
||||
|
||||
// RuntimeConfig represents the "ocid.runtime" TOML config table.
|
||||
type RuntimeConfig struct {
|
||||
// Runtime is a path to the OCI runtime which ocid will be using. Currently
|
||||
// the only known working choice is runC, simply because the OCI has not
|
||||
// yet merged a CLI API (so we assume runC's API here).
|
||||
Runtime string `toml:"runtime"`
|
||||
|
||||
// Conmon is the path to conmon binary, used for managing the runtime.
|
||||
Conmon string `toml:"conmon"`
|
||||
|
||||
// SELinux determines whether or not SELinux is used for pod separation.
|
||||
SELinux bool `toml:"selinux"`
|
||||
}
|
||||
|
||||
// ImageConfig represents the "ocid.image" TOML config table.
|
||||
type ImageConfig struct {
|
||||
// Pause is the path to the statically linked pause container binary, used
|
||||
// as the entrypoint for infra containers.
|
||||
//
|
||||
// TODO(cyphar): This should be replaced with a path to an OCI image
|
||||
// bundle, once the OCI image/storage code has been implemented.
|
||||
Pause string `toml:"pause"`
|
||||
|
||||
// ImageStore is the directory where the ocid image store will be stored.
|
||||
ImageStore string
|
||||
}
|
||||
|
||||
// tomlConfig is another way of looking at a Config, which is
|
||||
// TOML-friendly (it has all of the explicit tables). It's just used for
|
||||
// conversions.
|
||||
type tomlConfig struct {
|
||||
Ocid struct {
|
||||
RootConfig
|
||||
API struct{ APIConfig } `toml:"api"`
|
||||
Runtime struct{ RuntimeConfig } `toml:"runtime"`
|
||||
Image struct{ ImageConfig } `toml:"image"`
|
||||
} `toml:"ocid"`
|
||||
}
|
||||
|
||||
func (t *tomlConfig) toConfig(c *Config) {
|
||||
c.RootConfig = t.Ocid.RootConfig
|
||||
c.APIConfig = t.Ocid.API.APIConfig
|
||||
c.RuntimeConfig = t.Ocid.Runtime.RuntimeConfig
|
||||
c.ImageConfig = t.Ocid.Image.ImageConfig
|
||||
}
|
||||
|
||||
func (t *tomlConfig) fromConfig(c *Config) {
|
||||
t.Ocid.RootConfig = c.RootConfig
|
||||
t.Ocid.API.APIConfig = c.APIConfig
|
||||
t.Ocid.Runtime.RuntimeConfig = c.RuntimeConfig
|
||||
t.Ocid.Image.ImageConfig = c.ImageConfig
|
||||
}
|
||||
|
||||
// FromFile populates the Config from the TOML-encoded file at the given path.
|
||||
// Returns errors encountered when reading or parsing the files, or nil
|
||||
// otherwise.
|
||||
func (c *Config) FromFile(path string) error {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := new(tomlConfig)
|
||||
t.fromConfig(c)
|
||||
|
||||
_, err = toml.Decode(string(data), t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.toConfig(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToFile outputs the given Config as a TOML-encoded file at the given path.
|
||||
// Returns errors encountered when generating or writing the file, or nil
|
||||
// otherwise.
|
||||
func (c *Config) ToFile(path string) error {
|
||||
var w bytes.Buffer
|
||||
e := toml.NewEncoder(&w)
|
||||
|
||||
t := new(tomlConfig)
|
||||
t.fromConfig(c)
|
||||
|
||||
if err := e.Encode(*t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(path, w.Bytes(), 0644)
|
||||
}
|
|
@ -55,10 +55,10 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err = os.Mkdir(filepath.Join(imageStore, tr.StringWithinTransport()), 0755); err != nil {
|
||||
if err = os.Mkdir(filepath.Join(s.config.ImageStore, tr.StringWithinTransport()), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir, err := directory.NewReference(filepath.Join(imageStore, tr.StringWithinTransport()))
|
||||
dir, err := directory.NewReference(filepath.Join(s.config.ImageStore, tr.StringWithinTransport()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podSandboxDir := filepath.Join(s.sandboxDir, id)
|
||||
podSandboxDir := filepath.Join(s.config.SandboxDir, id)
|
||||
if _, err = os.Stat(podSandboxDir); err == nil {
|
||||
return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
|||
// creates a spec Generator with the default spec.
|
||||
g := generate.New()
|
||||
|
||||
podInfraRootfs := filepath.Join(s.root, "graph/vfs/pause")
|
||||
podInfraRootfs := filepath.Join(s.config.Root, "graph/vfs/pause")
|
||||
// setup defaults for the pod sandbox
|
||||
g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs"))
|
||||
g.SetRootReadonly(true)
|
||||
|
@ -235,7 +235,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
|||
if _, err = os.Stat(podInfraRootfs); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// TODO: Replace by rootfs creation API when it is ready
|
||||
if err = utils.CreateInfraRootfs(podInfraRootfs, s.pausePath); err != nil {
|
||||
if err = utils.CreateInfraRootfs(podInfraRootfs, s.config.Pause); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
|
@ -361,7 +361,7 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR
|
|||
}
|
||||
|
||||
// Remove the files related to the sandbox
|
||||
podSandboxDir := filepath.Join(s.sandboxDir, sb.id)
|
||||
podSandboxDir := filepath.Join(s.config.SandboxDir, sb.id)
|
||||
if err := os.RemoveAll(podSandboxDir); err != nil {
|
||||
return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", sb.id, err)
|
||||
}
|
||||
|
|
|
@ -20,15 +20,12 @@ import (
|
|||
|
||||
const (
|
||||
runtimeAPIVersion = "v1alpha1"
|
||||
imageStore = "/var/lib/ocid/images"
|
||||
)
|
||||
|
||||
// Server implements the RuntimeService and ImageService
|
||||
type Server struct {
|
||||
root string
|
||||
config Config
|
||||
runtime *oci.Runtime
|
||||
sandboxDir string
|
||||
pausePath string
|
||||
stateLock sync.Mutex
|
||||
state *serverState
|
||||
netPlugin ocicni.CNIPlugin
|
||||
|
@ -82,7 +79,7 @@ func (s *Server) loadContainer(id string) error {
|
|||
}
|
||||
|
||||
func (s *Server) loadSandbox(id string) error {
|
||||
config, err := ioutil.ReadFile(filepath.Join(s.sandboxDir, id, "config.json"))
|
||||
config, err := ioutil.ReadFile(filepath.Join(s.config.SandboxDir, id, "config.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -113,7 +110,7 @@ func (s *Server) loadSandbox(id string) error {
|
|||
processLabel: processLabel,
|
||||
mountLabel: mountLabel,
|
||||
})
|
||||
sandboxPath := filepath.Join(s.sandboxDir, id)
|
||||
sandboxPath := filepath.Join(s.config.SandboxDir, id)
|
||||
|
||||
if err := label.ReserveLabel(processLabel); err != nil {
|
||||
return err
|
||||
|
@ -141,7 +138,7 @@ func (s *Server) loadSandbox(id string) error {
|
|||
}
|
||||
|
||||
func (s *Server) restore() {
|
||||
sandboxDir, err := ioutil.ReadDir(s.sandboxDir)
|
||||
sandboxDir, err := ioutil.ReadDir(s.config.SandboxDir)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
logrus.Warnf("could not read sandbox directory %s: %v", sandboxDir, err)
|
||||
}
|
||||
|
@ -207,7 +204,7 @@ func (s *Server) releaseContainerName(name string) {
|
|||
}
|
||||
|
||||
// New creates a new Server with options provided
|
||||
func New(runtimePath, root, sandboxDir, containerDir, conmonPath, pausePath string) (*Server, error) {
|
||||
func New(config *Config) (*Server, error) {
|
||||
// TODO: This will go away later when we have wrapper process or systemd acting as
|
||||
// subreaper.
|
||||
if err := utils.SetSubreaper(1); err != nil {
|
||||
|
@ -216,15 +213,15 @@ func New(runtimePath, root, sandboxDir, containerDir, conmonPath, pausePath stri
|
|||
|
||||
utils.StartReaper()
|
||||
|
||||
if err := os.MkdirAll(imageStore, 0755); err != nil {
|
||||
if err := os.MkdirAll(config.ImageStore, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(sandboxDir, 0755); err != nil {
|
||||
if err := os.MkdirAll(config.SandboxDir, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := oci.New(runtimePath, containerDir, conmonPath)
|
||||
r, err := oci.New(config.Runtime, config.ContainerDir, config.Conmon)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -235,11 +232,9 @@ func New(runtimePath, root, sandboxDir, containerDir, conmonPath, pausePath stri
|
|||
return nil, err
|
||||
}
|
||||
s := &Server{
|
||||
root: root,
|
||||
runtime: r,
|
||||
netPlugin: netPlugin,
|
||||
sandboxDir: sandboxDir,
|
||||
pausePath: pausePath,
|
||||
runtime: r,
|
||||
netPlugin: netPlugin,
|
||||
config: *config,
|
||||
state: &serverState{
|
||||
sandboxes: sandboxes,
|
||||
containers: containers,
|
||||
|
|
Loading…
Reference in a new issue