diff --git a/libnetwork/bridge/bridge.go b/libnetwork/bridge/bridge.go index d5f380f8b6..2afe066246 100644 --- a/libnetwork/bridge/bridge.go +++ b/libnetwork/bridge/bridge.go @@ -1,22 +1,27 @@ package bridge -import "github.com/docker/libnetwork" +import ( + "net" + + "github.com/docker/libnetwork" +) const networkType = "bridgednetwork" -func init() { - libnetwork.RegisterNetworkType(networkType, Create) +type bridgeConfiguration struct { + Subnet net.IPNet } -func Create(options libnetwork.DriverParams) (libnetwork.Network, error) { - return &bridgeNetwork{}, nil +func init() { + libnetwork.RegisterNetworkType(networkType, Create, bridgeConfiguration{}) +} + +func Create(config *bridgeConfiguration) (libnetwork.Network, error) { + return &bridgeNetwork{Config: *config}, nil } type bridgeNetwork struct { -} - -func (b *bridgeNetwork) Name() string { - return "" + Config bridgeConfiguration } func (b *bridgeNetwork) Type() string { diff --git a/libnetwork/cmd/test/main.go b/libnetwork/cmd/test/main.go new file mode 100644 index 0000000000..151c2e7191 --- /dev/null +++ b/libnetwork/cmd/test/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "log" + "net" + + "github.com/docker/libnetwork" + _ "github.com/docker/libnetwork/bridge" +) + +func main() { + _, net, _ := net.ParseCIDR("192.168.100.1/24") + + options := libnetwork.DriverParams{"Subnet": *net} + netw, err := libnetwork.NewNetwork("bridgednetwork", options) + if err != nil { + log.Fatal(err) + } + fmt.Printf("Network=%#v\n", netw) +} diff --git a/libnetwork/drivers.go b/libnetwork/drivers.go index ca8845e3df..a079c755b5 100644 --- a/libnetwork/drivers.go +++ b/libnetwork/drivers.go @@ -1,19 +1,55 @@ package libnetwork -import "fmt" +import ( + "fmt" + "reflect" -type DriverParams map[string]interface{} -type DriverConstructor func(DriverParams) (Network, error) + "github.com/docker/libnetwork/pkg/options" +) -var drivers = map[string]DriverConstructor{} +type DriverParams options.Generic + +var drivers = map[string]struct { + ctor interface{} + config interface{} +}{} // RegisterNetworkType associates a textual identifier with a way to create a // new network. It is called by the various network implementations, and used // upon invokation of the libnetwork.NetNetwork function. -func RegisterNetworkType(name string, ctor DriverConstructor) error { +func RegisterNetworkType(name string, ctor interface{}, config interface{}) error { if _, ok := drivers[name]; ok { return fmt.Errorf("a driver for network type %q is already registed", name) } - drivers[name] = ctor + drivers[name] = struct { + ctor interface{} + config interface{} + }{ctor, config} return nil } + +func createNetwork(name string, generic DriverParams) (Network, error) { + d, ok := drivers[name] + if !ok { + return nil, fmt.Errorf("unknown driver %q", name) + } + + config, err := options.GenerateFromModel(options.Generic(generic), d.config) + if err != nil { + return nil, fmt.Errorf("failed to generate driver config: %v", err) + } + + arg := []reflect.Value{reflect.ValueOf(config)} + res := reflect.ValueOf(d.ctor).Call(arg) + return makeCreateResult(res) +} + +func makeCreateResult(res []reflect.Value) (net Network, err error) { + if !res[0].IsNil() { + net = res[0].Interface().(Network) + } + if !res[1].IsNil() { + err = res[1].Interface().(error) + } + return +} diff --git a/libnetwork/network.go b/libnetwork/network.go index 5fc2797202..dd2cce297b 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -28,15 +28,13 @@ // return err // } // } +// package libnetwork -import "fmt" - // A Network represents a logical connectivity zone that containers may // ulteriorly join using the Link method. A Network is managed by a specific // driver. type Network interface { - Name() string Type() string Link(name string) ([]*Interface, error) } @@ -63,10 +61,6 @@ type Namespace interface { AddInterface(*Interface) error } -// TODO Figure out the proper options type func NewNetwork(networkType string, options DriverParams) (Network, error) { - if ctor, ok := drivers[networkType]; ok { - return ctor(options) - } - return nil, fmt.Errorf("Unknown network type %q", networkType) + return createNetwork(networkType, options) } diff --git a/libnetwork/pkg/options/options.go b/libnetwork/pkg/options/options.go new file mode 100644 index 0000000000..09966e6ad2 --- /dev/null +++ b/libnetwork/pkg/options/options.go @@ -0,0 +1,47 @@ +// The options package provides a way to pass unstructured sets of options to a +// component expecting a strongly-typed configuration structure. +package options + +import ( + "fmt" + "reflect" +) + +type NoSuchFieldError struct { + Field string + Type string +} + +func (e NoSuchFieldError) Error() string { + return fmt.Sprintf("no field %q in type %q", e.Field, e.Type) +} + +type CannotSetFieldError struct { + Field string + Type string +} + +func (e CannotSetFieldError) Error() string { + return fmt.Sprintf("cannot set field %q of type %q", e.Field, e.Type) +} + +type Generic map[string]interface{} + +func NewGeneric() Generic { + return make(Generic) +} + +func GenerateFromModel(options Generic, model interface{}) (interface{}, error) { + res := reflect.New(reflect.TypeOf(model)) + for name, value := range options { + field := res.Elem().FieldByName(name) + if !field.IsValid() { + return nil, NoSuchFieldError{name, reflect.TypeOf(model).Name()} + } + if !field.CanSet() { + return nil, CannotSetFieldError{name, reflect.TypeOf(model).Name()} + } + field.Set(reflect.ValueOf(value)) + } + return res.Interface(), nil +} diff --git a/libnetwork/pkg/options/options_test.go b/libnetwork/pkg/options/options_test.go new file mode 100644 index 0000000000..36c5060b00 --- /dev/null +++ b/libnetwork/pkg/options/options_test.go @@ -0,0 +1,57 @@ +package options + +import ( + "reflect" + "testing" +) + +func TestGenerate(t *testing.T) { + gen := NewGeneric() + gen["Int"] = 1 + gen["Rune"] = 'b' + gen["Float64"] = 2.0 + + type Model struct { + Int int + Rune rune + Float64 float64 + } + + result, err := GenerateFromModel(gen, Model{}) + + if err != nil { + t.Fatal(err) + } + + cast, ok := result.(*Model) + if !ok { + t.Fatalf("result has unexpected type %s", reflect.TypeOf(result)) + } + if expected := 1; cast.Int != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Int) + } + if expected := 'b'; cast.Rune != expected { + t.Fatalf("wrong value for field Rune: expected %v, got %v", expected, cast.Rune) + } + if expected := 2.0; cast.Float64 != expected { + t.Fatalf("wrong value for field Int: expected %v, got %v", expected, cast.Float64) + } +} + +func TestGenerateMissingField(t *testing.T) { + type Model struct{} + _, err := GenerateFromModel(Generic{"foo": "bar"}, Model{}) + + if _, ok := err.(NoSuchFieldError); !ok { + t.Fatalf("expected NoSuchFieldError, got %#v", err) + } +} + +func TestFieldCannotBeSet(t *testing.T) { + type Model struct{ foo int } + _, err := GenerateFromModel(Generic{"foo": "bar"}, Model{}) + + if _, ok := err.(CannotSetFieldError); !ok { + t.Fatalf("expected CannotSetFieldError, got %#v", err) + } +}