Skip to content

Commit

Permalink
Config-Based Module Support
Browse files Browse the repository at this point in the history
This patch adds support for configuration modules via the REX-Ray
configuration source. For example, the following configuration is loaded
by default:

    rexray:
        modules:
            default-admin:
                type: admin
                desc: The default admin module.
                host: tcp://127.0.0.1:7979
            default-docker:
                type: docker
                desc: The default docker module.
                host: unix:///run/docker/plugins/rexray.sock
                spec: /etc/docker/plugins/rexray.spec

The above configuration indicates that there are two modules that need
to be initialized and started:

  1. default-admin
  2. default-docker

Default modules can be overridden in a custom configuration file by
simply using the same names as above.

If the `host` or `spec` properties are not defined for a docker module,
then the sanitized name of the module is used to build the paths to a
socket and spec file. For example:

    rexray:
        modules:
            "isilon 2":
                type: docker

The above configuration would create a Docker module hosted using a
socket file at `unix:///run/docker/plugins/isilon-2.sock` and spec file
at `/etc/docker/plugins/isilon-2.spec`.

If the configuration file is used to override part of the
`default-docker` module's configuration, for example its description,
it's also possible to omit the `default-docker` module's `host` and
`spec` properties as well. The difference is that the `default-docker`
module's `host` and `spec` properties will default to values based not
on the module name but `unix:///run/docker/plugins/rexray` and
`/etc/docker/plugins/rexray.spec`.
  • Loading branch information
akutz committed Feb 3, 2016
1 parent 5e00171 commit 6c3fb0e
Show file tree
Hide file tree
Showing 9 changed files with 240 additions and 196 deletions.
2 changes: 1 addition & 1 deletion daemon/daemon_modules.go
Expand Up @@ -5,6 +5,6 @@ package daemon
import (
// load the modules
_ "github.com/emccode/rexray/daemon/module/admin"
_ "github.com/emccode/rexray/daemon/module/docker/remotevolumedriver"
// _ "github.com/emccode/rexray/daemon/module/docker/remotevolumedriver"
_ "github.com/emccode/rexray/daemon/module/docker/volumedriver"
)
85 changes: 29 additions & 56 deletions daemon/module/admin/admin.go
Expand Up @@ -20,13 +20,10 @@ import (
)

const (
modPort = 7979
modName = "AdminModule"
modDescription = "The REX-Ray admin module"
modName = "admin"
)

type mod struct {
id int32
name string
addr string
desc string
Expand All @@ -38,26 +35,17 @@ type jsonError struct {
}

func init() {
addr := fmt.Sprintf("tcp://:%d", modPort)
mc := &module.Config{
Address: addr,
}
module.RegisterModule(modName, false, newModule, []*module.Config{mc})
module.RegisterModule(modName, newModule)
}

func newModule(id int32, config *module.Config) (module.Module, error) {
func newModule(c *module.Config) (module.Module, error) {
return &mod{
id: id,
name: modName,
desc: modDescription,
addr: config.Address,
name: c.Name,
desc: c.Description,
addr: c.Address,
}, nil
}

func (m *mod) ID() int32 {
return m.id
}

func loadAsset(path, defaultValue string) string {

devPath := fmt.Sprintf(
Expand Down Expand Up @@ -158,16 +146,18 @@ func moduleInstGetHandler(w http.ResponseWriter, req *http.Request) {
}

func moduleInstPostHandler(w http.ResponseWriter, req *http.Request) {
typeID := req.FormValue("typeId")
name := req.FormValue("name")
typeName := req.FormValue("typeName")
address := req.FormValue("address")
cfgJSON := req.FormValue("config")
start := req.FormValue("start")

log.WithFields(log.Fields{
"typeId": typeID,
"address": address,
"start": start,
"config": cfgJSON,
"name": name,
"typeName": typeName,
"address": address,
"start": start,
"config": cfgJSON,
}).Debug("received module instance post request")

cfg, cfgErr := gofig.FromJSON(cfgJSON)
Expand All @@ -178,28 +168,21 @@ func moduleInstPostHandler(w http.ResponseWriter, req *http.Request) {
}

modConfig := &module.Config{
Name: name,
Type: typeName,
Address: address,
Config: cfg,
}

w.Header().Set("Content-Type", "application/json; charset=UTF-8")

if typeID == "" || address == "" {
w.Write(getJSONError("Fields typeId and address are required", nil))
log.Printf("Fields typeId and address are required\n")
return
}

typeIDInt, typeIDIntErr := strconv.ParseInt(typeID, 10, 32)
if typeIDIntErr != nil {
w.Write(getJSONError("Error parsing typeId", typeIDIntErr))
log.Printf("Error parsing typeId ERR: %v\n", typeIDIntErr)
if typeName == "" || address == "" {
w.Write(getJSONError("Fields typeName and address are required", nil))
log.Printf("Fields typeName and address are required\n")
return
}

typeIDInt32 := int32(typeIDInt)

modInst, initErr := module.InitializeModule(typeIDInt32, modConfig)
modInst, initErr := module.InitializeModule(modConfig)
if initErr != nil {
w.WriteHeader(http.StatusBadRequest)
log.Printf("Error initializing module ERR: %v\n", initErr)
Expand All @@ -219,7 +202,7 @@ func moduleInstPostHandler(w http.ResponseWriter, req *http.Request) {
}

if startBool {
startErr := module.StartModule(modInst.ID)
startErr := module.StartModule(modInst.Name)
if startErr != nil {
w.Write(getJSONError("Error starting module", startErr))
log.Printf("Error starting module ERR: %v\n", startErr)
Expand Down Expand Up @@ -255,27 +238,17 @@ func moduleInstStartHandler(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")

vars := mux.Vars(req)
id := vars["id"]
if id == "" {
w.Write(getJSONError("The URL should include the module instance ID", nil))
log.Printf("The URL should include the module instance ID\n")
return
}

idInt, idIntErr := strconv.ParseInt(id, 10, 32)

if idIntErr != nil {
w.Write(getJSONError("Error parsing id", idIntErr))
log.Printf("Error parsing id ERR: %v\n", idIntErr)
name := vars["name"]
if name == "" {
w.Write(getJSONError("The URL should include the module instance name", nil))
log.Printf("The URL should include the module instance name\n")
return
}

idInt32 := int32(idInt)

modInst, modInstErr := module.GetModuleInstance(idInt32)
modInst, modInstErr := module.GetModuleInstance(name)
if modInstErr != nil {
w.Write(getJSONError("Unknown module id", modInstErr))
log.Printf("Unknown module id ERR: %v\n", modInstErr)
w.Write(getJSONError("Unknown module name", modInstErr))
log.Printf("Unknown module name ERR: %v\n", modInstErr)
return
}

Expand All @@ -291,7 +264,7 @@ func moduleInstStartHandler(w http.ResponseWriter, req *http.Request) {
return
}

startErr := module.StartModule(idInt32)
startErr := module.StartModule(name)

if startErr != nil {
w.Write(getJSONError("Error starting moudle", startErr))
Expand Down Expand Up @@ -330,7 +303,7 @@ func (m *mod) Start() error {

r.Handle("/r/module/instances",
handlers.LoggingHandler(stdOut, http.HandlerFunc(moduleInstHandler)))
r.Handle("/r/module/instances/{id}/start",
r.Handle("/r/module/instances/{name}/start",
handlers.LoggingHandler(stdOut, http.HandlerFunc(moduleInstStartHandler)))
r.Handle("/r/module/types",
handlers.LoggingHandler(stdOut, http.HandlerFunc(moduleTypeHandler)))
Expand Down
1 change: 1 addition & 0 deletions daemon/module/docker/remotevolumedriver/notests.go
@@ -0,0 +1 @@
package remotevolumedriver
2 changes: 2 additions & 0 deletions daemon/module/docker/remotevolumedriver/remvoldriver.go
@@ -1,3 +1,5 @@
// +build ignore

package remotevolumedriver

import (
Expand Down
87 changes: 54 additions & 33 deletions daemon/module/docker/volumedriver/voldriver.go
Expand Up @@ -9,10 +9,10 @@ import (
"os"
"path/filepath"
"regexp"
"strings"
"time"

log "github.com/Sirupsen/logrus"
"github.com/akutz/gofig"
"github.com/akutz/goof"
"github.com/akutz/gotil"

Expand All @@ -21,54 +21,58 @@ import (
)

const (
modAddress = "unix:///run/docker/plugins/rexray.sock"
modPort = 7980
modName = "DockerVolumeDriverModule"
modDescription = "The REX-Ray Docker VolumeDriver module"
modName = "docker"
)

type mod struct {
id int32
r *core.RexRay
name string
addr string
desc string
}

var (
separators = regexp.MustCompile(`[ &_=+:]`)
dashes = regexp.MustCompile(`[\-]+`)
illegalPath = regexp.MustCompile(`[^[:alnum:]\~\-\./]`)
)

func init() {
//tcpAddr := fmt.Sprintf("tcp://:%d", ModPort)
module.RegisterModule(modName, newModule)
}

_, fsPath, parseAddrErr := gotil.ParseAddress(modAddress)
if parseAddrErr != nil {
panic(parseAddrErr)
}
func newModule(c *module.Config) (module.Module, error) {

fsPathDir := filepath.Dir(fsPath)
os.MkdirAll(fsPathDir, 0755)
host := strings.Trim(c.Address, " ")

mc := &module.Config{
Address: modAddress,
Config: gofig.New(),
if host == "" {
if c.Name == "default-docker" {
host = "unix:///run/docker/plugins/rexray.sock"
} else {
fname := cleanName(c.Name)
host = fmt.Sprintf("unix:///run/docker/plugins/%s.sock", fname)
}
}

module.RegisterModule(modName, true, newMod, []*module.Config{mc})
}

func (m *mod) ID() int32 {
return m.id
}
c.Address = host

func newMod(id int32, cfg *module.Config) (module.Module, error) {
return &mod{
id: id,
r: core.New(cfg.Config),
name: modName,
desc: modDescription,
addr: cfg.Address,
r: core.New(c.Config),
name: c.Name,
desc: c.Description,
addr: host,
}, nil
}

const driverName = "dockervolumedriver"
func cleanName(s string) string {
s = strings.Trim(strings.ToLower(s), " ")
s = separators.ReplaceAllString(s, "-")
s = illegalPath.ReplaceAllString(s, "")
s = dashes.ReplaceAllString(s, "-")
return s
}

const driverName = "docker"

var (
errMissingHost = goof.New("Missing host parameter")
Expand All @@ -88,6 +92,11 @@ func (m *mod) Start() error {
return parseAddrErr
}

if proto == "unix" {
dir := filepath.Dir(addr)
os.MkdirAll(dir, 0755)
}

const validProtoPatt = "(?i)^unix|tcp$"
isProtoValid, matchProtoErr := regexp.MatchString(validProtoPatt, proto)
if matchProtoErr != nil {
Expand Down Expand Up @@ -158,10 +167,22 @@ func (m *mod) Start() error {
}
}()

writeSpecErr := ioutil.WriteFile(
"/etc/docker/plugins/rexray.spec", []byte(specPath), 0644)
if writeSpecErr != nil {
return writeSpecErr
spec := m.r.Config.GetString("spec")
if spec == "" {
if m.name == "default-docker" {
spec = "/etc/docker/plugins/rexray.spec"
} else {
fname := cleanName(m.name)
spec = fmt.Sprintf("/etc/docker/plugins/%s.spec", fname)
}
}

log.WithField("path", spec).Debug("docker voldriver spec file")

if !gotil.FileExists(spec) {
if err := ioutil.WriteFile(spec, []byte(specPath), 0644); err != nil {
return err
}
}

return nil
Expand Down

0 comments on commit 6c3fb0e

Please sign in to comment.