Explorar el Código

Register plugins explicitly

Fixes #45
Do not use `init()` magic to register plugins, do it explicitly. This is
done by requiring plugins to declare a populated `Plugin` symbol of type
`plugins.Plugin`.

Signed-off-by: Andrea Barberio <insomniac@slackware.it>
Andrea Barberio hace 5 años
padre
commit
8bd5261895

+ 2 - 2
cmds/coredhcp-generator/core-plugins.txt

@@ -1,7 +1,7 @@
 github.com/coredhcp/coredhcp/plugins/dns
 github.com/coredhcp/coredhcp/plugins/file
-github.com/coredhcp/coredhcp/plugins/lease_time
+github.com/coredhcp/coredhcp/plugins/leasetime
 github.com/coredhcp/coredhcp/plugins/netmask
 github.com/coredhcp/coredhcp/plugins/range
 github.com/coredhcp/coredhcp/plugins/router
-github.com/coredhcp/coredhcp/plugins/server_id
+github.com/coredhcp/coredhcp/plugins/serverid

+ 15 - 1
cmds/coredhcp-generator/coredhcp.go.template

@@ -11,8 +11,9 @@ import (
 	"github.com/coredhcp/coredhcp"
 	"github.com/coredhcp/coredhcp/config"
 	"github.com/coredhcp/coredhcp/logger"
+	"github.com/coredhcp/coredhcp/plugins"
 {{range $plugin := .}}
-	_ "{{$plugin}}"
+	{{if eq $plugin "github.com/coredhcp/coredhcp/plugins/range"}}rangepl "{{$plugin}}"{{else}}"{{$plugin}}"{{end}}
 {{end}}
 	"github.com/sirupsen/logrus"
 )
@@ -23,6 +24,12 @@ var (
 	flagDebug       = flag.Bool("debug", false, "Enable debug output")
 )
 
+var desiredPlugins = []*plugins.Plugin{
+{{range $plugin := .}}
+{{$import := importname $plugin}}&{{if eq $import "range"}}rangepl{{else}}{{$import}}{{end}}.Plugin,
+{{end}}
+}
+
 func main() {
 	flag.Parse()
 	log := logger.GetLogger("main")
@@ -42,6 +49,13 @@ func main() {
 	if err != nil {
 		log.Fatal(err)
 	}
+	// register plugins
+	for _, plugin := range desiredPlugins {
+		if err := plugins.RegisterPlugin(plugin); err != nil {
+			log.Fatalf("Failed to register plugin '%s': %v", plugin.Name, err)
+		}
+	}
+	// start server
 	server := coredhcp.NewServer(config)
 	if err := server.Start(); err != nil {
 		log.Fatal(err)

+ 12 - 1
cmds/coredhcp-generator/main.go

@@ -7,6 +7,7 @@ package main
 import (
 	"bufio"
 	"flag"
+	"fmt"
 	"html/template"
 	"io/ioutil"
 	"log"
@@ -25,13 +26,23 @@ var (
 	flagFromFile = flag.String("from", "", "Optional file name to get the plugin list from, one import path per line")
 )
 
+var funcMap = template.FuncMap{
+	"importname": func(importPath string) (string, error) {
+		parts := strings.Split(importPath, "/")
+		if len(parts) < 1 {
+			return "", fmt.Errorf("no components found in import path '%s'", importPath)
+		}
+		return parts[len(parts)-1], nil
+	},
+}
+
 func main() {
 	flag.Parse()
 	data, err := ioutil.ReadFile(*flagTemplate)
 	if err != nil {
 		log.Fatalf("Failed to read template file '%s': %v", *flagTemplate, err)
 	}
-	t, err := template.New("coredhcp").Parse(string(data))
+	t, err := template.New("coredhcp").Funcs(funcMap).Parse(string(data))
 	if err != nil {
 		log.Fatalf("Template parsing failed: %v", err)
 	}

+ 30 - 10
cmds/coredhcp/main.go

@@ -11,14 +11,15 @@ import (
 	"github.com/coredhcp/coredhcp"
 	"github.com/coredhcp/coredhcp/config"
 	"github.com/coredhcp/coredhcp/logger"
-	_ "github.com/coredhcp/coredhcp/plugins/dns"
-	_ "github.com/coredhcp/coredhcp/plugins/file"
-	_ "github.com/coredhcp/coredhcp/plugins/lease_time"
-	_ "github.com/coredhcp/coredhcp/plugins/nbp"
-	_ "github.com/coredhcp/coredhcp/plugins/netmask"
-	_ "github.com/coredhcp/coredhcp/plugins/range"
-	_ "github.com/coredhcp/coredhcp/plugins/router"
-	_ "github.com/coredhcp/coredhcp/plugins/server_id"
+	"github.com/coredhcp/coredhcp/plugins"
+	"github.com/coredhcp/coredhcp/plugins/dns"
+	"github.com/coredhcp/coredhcp/plugins/file"
+	"github.com/coredhcp/coredhcp/plugins/leasetime"
+	"github.com/coredhcp/coredhcp/plugins/nbp"
+	"github.com/coredhcp/coredhcp/plugins/netmask"
+	rangepl "github.com/coredhcp/coredhcp/plugins/range"
+	"github.com/coredhcp/coredhcp/plugins/router"
+	"github.com/coredhcp/coredhcp/plugins/serverid"
 	"github.com/sirupsen/logrus"
 )
 
@@ -28,6 +29,17 @@ var (
 	flagDebug       = flag.Bool("debug", false, "Enable debug output")
 )
 
+var desiredPlugins = []*plugins.Plugin{
+	&dns.Plugin,
+	&file.Plugin,
+	&leasetime.Plugin,
+	&nbp.Plugin,
+	&netmask.Plugin,
+	&rangepl.Plugin,
+	&router.Plugin,
+	&serverid.Plugin,
+}
+
 func main() {
 	flag.Parse()
 	log := logger.GetLogger("main")
@@ -45,11 +57,19 @@ func main() {
 	}
 	config, err := config.Load()
 	if err != nil {
-		log.Fatal(err)
+		log.Fatalf("Failed to load configuration: %v", err)
 	}
+	// register plugins
+	for _, plugin := range desiredPlugins {
+		if err := plugins.RegisterPlugin(plugin); err != nil {
+			log.Fatalf("Failed to register plugin '%s': %v", plugin.Name, err)
+		}
+	}
+
+	// start server
 	server := coredhcp.NewServer(config)
 	if err := server.Start(); err != nil {
-		log.Fatal(err)
+		log.Fatalf("Failed to start server: %v", err)
 	}
 	if err := server.Wait(); err != nil {
 		log.Error(err)

+ 7 - 4
plugins/dns/plugin.go

@@ -17,8 +17,11 @@ import (
 
 var log = logger.GetLogger("plugins/dns")
 
-func init() {
-	plugins.RegisterPlugin("dns", setupDNS6, setupDNS4)
+// Plugin wraps the DNS plugin information.
+var Plugin = plugins.Plugin{
+	Name:   "dns",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
 var (
@@ -26,7 +29,7 @@ var (
 	dnsServers4 []net.IP
 )
 
-func setupDNS6(args ...string) (handler.Handler6, error) {
+func setup6(args ...string) (handler.Handler6, error) {
 	if len(args) < 1 {
 		return nil, errors.New("need at least one DNS server")
 	}
@@ -41,7 +44,7 @@ func setupDNS6(args ...string) (handler.Handler6, error) {
 	return Handler6, nil
 }
 
-func setupDNS4(args ...string) (handler.Handler4, error) {
+func setup4(args ...string) (handler.Handler4, error) {
 	log.Printf("loaded plugin for DHCPv4.")
 	if len(args) < 1 {
 		return nil, errors.New("need at least one DNS server")

+ 32 - 17
plugins/example/plugin.go

@@ -23,24 +23,37 @@ import (
 // information in the docstring of the logger package.
 var log = logger.GetLogger("plugins/example")
 
-// In the main package, you need to register your plugin at import time. To do
-// this, just do a blank import of this package, e.g.
+// Plugin wraps the information necessary to register a plugin.
+// In the main package, you need to export a `plugins.Plugin` object called
+// `Plugin`, so it can be registered into the plugin registry.
+// Just import your plugin, and fill the structure with plugin name and setup
+// functions:
+//
 // import (
-//     _ "github.com/coredhcp/coredhcp/plugins/example"
+//     "github.com/coredhcp/coredhcp/plugins"
+//     "github.com/coredhcp/coredhcp/plugins/example"
 // )
 //
-// This guarantees that `init` will be called at import time, and your plugin
-// is correctly registered.
+// var Plugin = plugins.Plugin{
+//     Name: "example",
+//     Setup6: setup6,
+//     Setup4: setup4,
+// }
+//
+// Name is simply the name used to register the plugin. It must be unique to
+// other registered plugins, or the operation will fail. In other words, don't
+// declare plugins with colliding names.
 //
-// The `init` function then should call `plugins.RegisterPlugin`, specifying the
-// plugin name, the setup function for DHCPv6 packets, and the setup function
-// for DHCPv4 packets. The setup functions must implement the
-// `plugin.SetupFunc6` and `plugin.Setup4` interfaces.
+// Setup6 and Setup4 are the setup functions for DHCPv6 and DHCPv4 traffic
+// handlers. They conform to the `plugins.SetupFunc6` and `plugins.SetupFunc4`
+// interfaces, so they must return a `plugins.Handler6` and a `plugins.Handler4`
+// respectively.
 // A `nil` setup function means that that protocol won't be handled by this
 // plugin.
 //
-// Note that importing the plugin is not enough: you have to explicitly specify
-// its use in the `config.yml` file, in the plugins section. For example:
+// Note that importing the plugin is not enough to use it: you have to
+// explicitly specify the intention to use it in the `config.yml` file, in the
+// plugins section. For example:
 //
 // server6:
 //   listen: '[::]547'
@@ -48,25 +61,27 @@ var log = logger.GetLogger("plugins/example")
 //   - server_id: LL aa:bb:cc:dd:ee:ff
 //   - file: "leases.txt"
 //
-func init() {
-	plugins.RegisterPlugin("example", setupExample6, setupExample4)
+var Plugin = plugins.Plugin{
+	Name:   "example",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
-// setupExample6 is the setup function to initialize the handler for DHCPv6
+// setup6 is the setup function to initialize the handler for DHCPv6
 // traffic. This function implements the `plugin.SetupFunc6` interface.
 // This function returns a `handler.Handler6` function, and an error if any.
 // In this example we do very little in the setup function, and just return the
 // `exampleHandler6` function. Such function will be called for every DHCPv6
 // packet that the server receives. Remember that a handler may not be called
 // for each packet, if the handler chain is interrupted before reaching it.
-func setupExample6(args ...string) (handler.Handler6, error) {
+func setup6(args ...string) (handler.Handler6, error) {
 	log.Printf("loaded plugin for DHCPv6.")
 	return exampleHandler6, nil
 }
 
-// setupExample4 behaves like setupExample6, but for DHCPv4 packets. It
+// setup4 behaves like setupExample6, but for DHCPv4 packets. It
 // implements the `plugin.SetupFunc4` interface.
-func setupExample4(args ...string) (handler.Handler4, error) {
+func setup4(args ...string) (handler.Handler4, error) {
 	log.Printf("loaded plugin for DHCPv4.")
 	return exampleHandler4, nil
 }

+ 7 - 4
plugins/file/plugin.go

@@ -42,8 +42,11 @@ import (
 
 var log = logger.GetLogger("plugins/file")
 
-func init() {
-	plugins.RegisterPlugin("file", setupFile6, setupFile4)
+// Plugin wraps plugin registration information
+var Plugin = plugins.Plugin{
+	Name:   "file",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
 // StaticRecords holds a MAC -> IP address mapping
@@ -163,12 +166,12 @@ func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
 	return resp, true
 }
 
-func setupFile6(args ...string) (handler.Handler6, error) {
+func setup6(args ...string) (handler.Handler6, error) {
 	h6, _, err := setupFile(true, args...)
 	return h6, err
 }
 
-func setupFile4(args ...string) (handler.Handler4, error) {
+func setup4(args ...string) (handler.Handler4, error) {
 	_, h4, err := setupFile(false, args...)
 	return h4, err
 }

+ 7 - 3
plugins/lease_time/plugin.go → plugins/leasetime/plugin.go

@@ -14,8 +14,12 @@ import (
 	"github.com/insomniacslk/dhcp/dhcpv4"
 )
 
-func init() {
-	plugins.RegisterPlugin("lease_time", nil, setupLeaseTime4)
+// Plugin wraps plugin registration information
+var Plugin = plugins.Plugin{
+	Name: "lease_time",
+	// currently not supported for DHCPv6
+	Setup6: nil,
+	Setup4: setup4,
 }
 
 var (
@@ -35,7 +39,7 @@ func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
 	return resp, false
 }
 
-func setupLeaseTime4(args ...string) (handler.Handler4, error) {
+func setup4(args ...string) (handler.Handler4, error) {
 	log.Print("loading `lease_time` plugin for DHCPv4")
 	if len(args) < 1 {
 		log.Error("No default lease time provided")

+ 7 - 4
plugins/nbp/nbp.go

@@ -41,8 +41,11 @@ import (
 
 var log = logger.GetLogger("plugins/nbp")
 
-func init() {
-	plugins.RegisterPlugin("nbp", setupNBP6, setupNBP4)
+// Plugin wraps plugin registration information
+var Plugin = plugins.Plugin{
+	Name:   "nbp",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
 var (
@@ -57,7 +60,7 @@ func parseArgs(args ...string) (*url.URL, error) {
 	return url.Parse(args[0])
 }
 
-func setupNBP6(args ...string) (handler.Handler6, error) {
+func setup6(args ...string) (handler.Handler6, error) {
 	u, err := parseArgs(args...)
 	if err != nil {
 		return nil, err
@@ -74,7 +77,7 @@ func setupNBP6(args ...string) (handler.Handler6, error) {
 	return nbpHandler6, nil
 }
 
-func setupNBP4(args ...string) (handler.Handler4, error) {
+func setup4(args ...string) (handler.Handler4, error) {
 	u, err := parseArgs(args...)
 	if err != nil {
 		return nil, err

+ 7 - 4
plugins/netmask/plugin.go

@@ -18,21 +18,24 @@ import (
 
 var log = logger.GetLogger("plugins/netmask")
 
-func init() {
-	plugins.RegisterPlugin("netmask", setupNetmask6, setupNetmask4)
+// Plugin wraps plugin registration information
+var Plugin = plugins.Plugin{
+	Name:   "netmask",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
 var (
 	netmask net.IPMask
 )
 
-func setupNetmask6(args ...string) (handler.Handler6, error) {
+func setup6(args ...string) (handler.Handler6, error) {
 	// TODO setup function for IPv6
 	log.Warning("not implemented for IPv6")
 	return Handler6, nil
 }
 
-func setupNetmask4(args ...string) (handler.Handler4, error) {
+func setup4(args ...string) (handler.Handler4, error) {
 	log.Printf("loaded plugin for DHCPv4.")
 	if len(args) != 1 {
 		return nil, errors.New("need at least one netmask IP address")

+ 12 - 11
plugins/plugin.go

@@ -5,6 +5,8 @@
 package plugins
 
 import (
+	"errors"
+
 	"github.com/coredhcp/coredhcp/handler"
 	"github.com/coredhcp/coredhcp/logger"
 )
@@ -29,18 +31,17 @@ type SetupFunc6 func(args ...string) (handler.Handler6, error)
 // SetupFunc4 defines a plugin setup function for DHCPv6
 type SetupFunc4 func(args ...string) (handler.Handler4, error)
 
-// RegisterPlugin registers a plugin by its name and setup functions.
-func RegisterPlugin(name string, setup6 SetupFunc6, setup4 SetupFunc4) {
-	log.Printf("Registering plugin \"%s\"", name)
-	if _, ok := RegisteredPlugins[name]; ok {
+// RegisterPlugin registers a plugin.
+func RegisterPlugin(plugin *Plugin) error {
+	if plugin == nil {
+		return errors.New("cannot register nil plugin")
+	}
+	log.Printf("Registering plugin '%s'", plugin.Name)
+	if _, ok := RegisteredPlugins[plugin.Name]; ok {
 		// TODO this highlights that asking the plugins to register themselves
 		// is not the right approach. Need to register them in the main program.
-		log.Panicf("Plugin '%s' is already registered", name)
-	}
-	plugin := Plugin{
-		Name:   name,
-		Setup6: setup6,
-		Setup4: setup4,
+		log.Panicf("Plugin '%s' is already registered", plugin.Name)
 	}
-	RegisteredPlugins[name] = &plugin
+	RegisteredPlugins[plugin.Name] = plugin
+	return nil
 }

+ 7 - 4
plugins/range/plugin.go

@@ -25,8 +25,11 @@ import (
 
 var log = logger.GetLogger("plugins/range")
 
-func init() {
-	plugins.RegisterPlugin("range", setupRange6, setupRange4)
+// Plugin wraps plugin registration information
+var Plugin = plugins.Plugin{
+	Name:   "range",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
 //Record holds an IP lease record
@@ -113,13 +116,13 @@ func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
 	return resp, false
 }
 
-func setupRange6(args ...string) (handler.Handler6, error) {
+func setup6(args ...string) (handler.Handler6, error) {
 	// TODO setup function for IPv6
 	log.Warning("not implemented for IPv6")
 	return Handler6, nil
 }
 
-func setupRange4(args ...string) (handler.Handler4, error) {
+func setup4(args ...string) (handler.Handler4, error) {
 	_, h4, err := setupRange(false, args...)
 	return h4, err
 }

+ 9 - 6
plugins/router/plugin.go

@@ -17,22 +17,25 @@ import (
 
 var log = logger.GetLogger("plugins/router")
 
-func init() {
-	plugins.RegisterPlugin("router", setupRouter6, setupRouter4)
+// Plugin wraps plugin registration information
+var Plugin = plugins.Plugin{
+	Name:   "router",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
 var (
 	routers []net.IP
 )
 
-func setupRouter6(args ...string) (handler.Handler6, error) {
+func setup6(args ...string) (handler.Handler6, error) {
 	// TODO setup function for IPv6
-	log.Warning("not implemented for IPv6")
+	log.Warning("Not implemented for IPv6")
 	return Handler6, nil
 }
 
-func setupRouter4(args ...string) (handler.Handler4, error) {
-	log.Printf("loaded plugin for DHCPv4.")
+func setup4(args ...string) (handler.Handler4, error) {
+	log.Printf("Loaded plugin for DHCPv4.")
 	if len(args) < 1 {
 		return nil, errors.New("need at least one router IP address")
 	}

+ 9 - 6
plugins/server_id/plugin.go → plugins/serverid/plugin.go

@@ -19,8 +19,11 @@ import (
 
 var log = logger.GetLogger("plugins/server_id")
 
-func init() {
-	plugins.RegisterPlugin("server_id", setupServerID6, setupServerID4)
+// Plugin wraps plugin registration information
+var Plugin = plugins.Plugin{
+	Name:   "server_id",
+	Setup6: setup6,
+	Setup4: setup4,
 }
 
 // v6ServerID is the DUID of the v6 server
@@ -95,8 +98,8 @@ func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
 	return resp, false
 }
 
-func setupServerID4(args ...string) (handler.Handler4, error) {
-	log.Print("loading `server_id` plugin for DHCPv4")
+func setup4(args ...string) (handler.Handler4, error) {
+	log.Printf("loading `server_id` plugin for DHCPv4 with args: %v", args)
 	if len(args) < 1 {
 		return nil, errors.New("need an argument")
 	}
@@ -111,8 +114,8 @@ func setupServerID4(args ...string) (handler.Handler4, error) {
 	return Handler4, nil
 }
 
-func setupServerID6(args ...string) (handler.Handler6, error) {
-	log.Print("loading `server_id` plugin for DHCPv6")
+func setup6(args ...string) (handler.Handler6, error) {
+	log.Printf("loading `server_id` plugin for DHCPv6 with args: %v", args)
 	if len(args) < 2 {
 		return nil, errors.New("need a DUID type and value")
 	}

+ 0 - 0
plugins/server_id/plugin_test.go → plugins/serverid/plugin_test.go