From 50964c994831c3a960d306ff130ff42983e2fe4a Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Thu, 14 May 2015 14:56:15 -0700 Subject: [PATCH] Provide interface to categorize errors - Package types to define the interfaces libnetwork errors may implement, so that caller can categorize them. Signed-off-by: Alessandro Boch --- api/api.go | 28 +- api/api_test.go | 93 +++++- controller.go | 15 +- driverapi/driverapi.go | 21 -- driverapi/errors.go | 56 ++++ drivers/bridge/bridge.go | 38 +-- drivers/bridge/error.go | 201 ------------ drivers/bridge/errors.go | 341 +++++++++++++++++++++ drivers/bridge/link.go | 2 +- drivers/bridge/network_test.go | 4 +- drivers/bridge/setup_fixedcidrv4.go | 6 +- drivers/bridge/setup_fixedcidrv6.go | 6 +- drivers/bridge/setup_ip_forwarding.go | 2 +- drivers/bridge/setup_ip_forwarding_test.go | 2 +- drivers/bridge/setup_ip_tables.go | 2 +- drivers/bridge/setup_ipv4.go | 4 +- drivers/bridge/setup_ipv6.go | 6 +- drivers/bridge/setup_verify.go | 8 +- drivers/remote/driver.go | 16 +- endpoint.go | 12 +- error.go | 124 ++++++-- errors_test.go | 51 +++ iptables/iptables_test.go | 2 +- libnetwork_test.go | 49 +-- network.go | 10 +- types/types.go | 174 ++++++++++- types/types_test.go | 99 ++++++ 27 files changed, 1025 insertions(+), 347 deletions(-) create mode 100644 driverapi/errors.go delete mode 100644 drivers/bridge/error.go create mode 100644 drivers/bridge/errors.go create mode 100644 errors_test.go create mode 100644 types/types_test.go diff --git a/api/api.go b/api/api.go index 0eebf4d1..a4a51559 100644 --- a/api/api.go +++ b/api/api.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/docker/libnetwork" + "github.com/docker/libnetwork/types" "github.com/gorilla/mux" ) @@ -434,7 +435,7 @@ func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.N panic(fmt.Sprintf("unexpected selector for network search: %d", by)) } if err != nil { - if err == libnetwork.ErrNoSuchNetwork { + if _, ok := err.(libnetwork.ErrNoSuchNetwork); ok { return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound} } return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} @@ -460,7 +461,7 @@ func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy)) } if err != nil { - if err == libnetwork.ErrNoSuchEndpoint { + if _, ok := err.(libnetwork.ErrNoSuchEndpoint); ok { return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound} } return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} @@ -469,9 +470,26 @@ func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) } func convertNetworkError(err error) *responseStatus { - // No real libnetwork error => http error code conversion for now. - // Will came in later when new interface for libnetwork error is vailable - return &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} + var code int + switch err.(type) { + case types.BadRequestError: + code = http.StatusBadRequest + case types.ForbiddenError: + code = http.StatusForbidden + case types.NotFoundError: + code = http.StatusNotFound + case types.TimeoutError: + code = http.StatusRequestTimeout + case types.NotImplementedError: + code = http.StatusNotImplemented + case types.NoServiceError: + code = http.StatusServiceUnavailable + case types.InternalError: + code = http.StatusInternalServerError + default: + code = http.StatusInternalServerError + } + return &responseStatus{Status: err.Error(), StatusCode: code} } func writeJSON(w http.ResponseWriter, code int, v interface{}) error { diff --git a/api/api_test.go b/api/api_test.go index 069afb7c..01d7256b 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -193,8 +193,8 @@ func TestCreateDeleteNetwork(t *testing.T) { if errRsp == &createdResponse { t.Fatalf("Expected to fail but succeeded") } - if errRsp.StatusCode != http.StatusBadRequest { - t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode) + if errRsp.StatusCode != http.StatusNotFound { + t.Fatalf("Expected StatusNotFound status code, got: %v", errRsp) } ops := make(map[string]interface{}) @@ -1305,3 +1305,92 @@ func TestHttpHandlerGood(t *testing.T) { t.Fatalf("Incongruent resource found") } } + +type bre struct{} + +func (b *bre) Error() string { + return "I am a bad request error" +} +func (b *bre) BadRequest() {} + +type nfe struct{} + +func (n *nfe) Error() string { + return "I am a not found error" +} +func (n *nfe) NotFound() {} + +type forb struct{} + +func (f *forb) Error() string { + return "I am a bad request error" +} +func (f *forb) Forbidden() {} + +type notimpl struct{} + +func (nip *notimpl) Error() string { + return "I am a not implemented error" +} +func (nip *notimpl) NotImplemented() {} + +type inter struct{} + +func (it *inter) Error() string { + return "I am a internal error" +} +func (it *inter) Internal() {} + +type tout struct{} + +func (to *tout) Error() string { + return "I am a timeout error" +} +func (to *tout) Timeout() {} + +type noserv struct{} + +func (nos *noserv) Error() string { + return "I am a no service error" +} +func (nos *noserv) NoService() {} + +type notclassified struct{} + +func (noc *notclassified) Error() string { + return "I am a non classified error" +} + +func TestErrorConversion(t *testing.T) { + if convertNetworkError(new(bre)).StatusCode != http.StatusBadRequest { + t.Fatalf("Failed to recognize BadRequest error") + } + + if convertNetworkError(new(nfe)).StatusCode != http.StatusNotFound { + t.Fatalf("Failed to recognize NotFound error") + } + + if convertNetworkError(new(forb)).StatusCode != http.StatusForbidden { + t.Fatalf("Failed to recognize Forbidden error") + } + + if convertNetworkError(new(notimpl)).StatusCode != http.StatusNotImplemented { + t.Fatalf("Failed to recognize NotImplemented error") + } + + if convertNetworkError(new(inter)).StatusCode != http.StatusInternalServerError { + t.Fatalf("Failed to recognize Internal error") + } + + if convertNetworkError(new(tout)).StatusCode != http.StatusRequestTimeout { + t.Fatalf("Failed to recognize Timeout error") + } + + if convertNetworkError(new(noserv)).StatusCode != http.StatusServiceUnavailable { + t.Fatalf("Failed to recognize No Service error") + } + + if convertNetworkError(new(notclassified)).StatusCode != http.StatusInternalServerError { + t.Fatalf("Failed to recognize not classified error as Internal error") + } +} diff --git a/controller.go b/controller.go index a64d6e52..442473eb 100644 --- a/controller.go +++ b/controller.go @@ -134,7 +134,7 @@ func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver) // are network specific and modeled in a generic way. func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) { if name == "" { - return nil, ErrInvalidName + return nil, ErrInvalidName(name) } // Check if a driver for the specified network type is available c.Lock() @@ -203,7 +203,7 @@ func (c *controller) WalkNetworks(walker NetworkWalker) { func (c *controller) NetworkByName(name string) (Network, error) { if name == "" { - return nil, ErrInvalidName + return nil, ErrInvalidName(name) } var n Network @@ -218,7 +218,7 @@ func (c *controller) NetworkByName(name string) (Network, error) { c.WalkNetworks(s) if n == nil { - return nil, ErrNoSuchNetwork + return nil, ErrNoSuchNetwork(name) } return n, nil @@ -226,14 +226,14 @@ func (c *controller) NetworkByName(name string) (Network, error) { func (c *controller) NetworkByID(id string) (Network, error) { if id == "" { - return nil, ErrInvalidID + return nil, ErrInvalidID(id) } c.Lock() defer c.Unlock() if n, ok := c.networks[types.UUID(id)]; ok { return n, nil } - return nil, ErrNoSuchNetwork + return nil, ErrNoSuchNetwork(id) } func (c *controller) sandboxAdd(key string, create bool) (sandbox.Sandbox, error) { @@ -286,13 +286,16 @@ func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) { // As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available. _, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType) if err != nil { + if err == plugins.ErrNotFound { + return nil, types.NotFoundErrorf(err.Error()) + } return nil, err } c.Lock() defer c.Unlock() d, ok := c.drivers[networkType] if !ok { - return nil, ErrInvalidNetworkDriver + return nil, ErrInvalidNetworkDriver(networkType) } return d, nil } diff --git a/driverapi/driverapi.go b/driverapi/driverapi.go index 35467daf..8ae986f8 100644 --- a/driverapi/driverapi.go +++ b/driverapi/driverapi.go @@ -1,24 +1,11 @@ package driverapi import ( - "errors" - "fmt" "net" "github.com/docker/libnetwork/types" ) -var ( - // ErrEndpointExists is returned if more than one endpoint is added to the network - ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)") - // ErrNoNetwork is returned if no network with the specified id exists - ErrNoNetwork = errors.New("No network exists") - // ErrNoEndpoint is returned if no endpoint with the specified id exists - ErrNoEndpoint = errors.New("No endpoint exists") - // ErrNotImplemented is returned when a Driver has not implemented an API yet - ErrNotImplemented = errors.New("The API is not implemented yet") -) - // NetworkPluginEndpointType represents the Endpoint Type used by Plugin system const NetworkPluginEndpointType = "NetworkDriver" @@ -124,14 +111,6 @@ type JoinInfo interface { SetResolvConfPath(string) error } -// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered -type ErrActiveRegistration string - -// Error interface for ErrActiveRegistration -func (ar ErrActiveRegistration) Error() string { - return fmt.Sprintf("Driver already registered for type %q", string(ar)) -} - // DriverCallback provides a Callback interface for Drivers into LibNetwork type DriverCallback interface { // RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a driver instance diff --git a/driverapi/errors.go b/driverapi/errors.go new file mode 100644 index 00000000..041ef415 --- /dev/null +++ b/driverapi/errors.go @@ -0,0 +1,56 @@ +package driverapi + +import ( + "fmt" +) + +// ErrNoNetwork is returned if no network with the specified id exists +type ErrNoNetwork string + +func (enn ErrNoNetwork) Error() string { + return fmt.Sprintf("No network (%s) exists", string(enn)) +} + +// NotFound denotes the type of this error +func (enn ErrNoNetwork) NotFound() {} + +// ErrEndpointExists is returned if more than one endpoint is added to the network +type ErrEndpointExists string + +func (ee ErrEndpointExists) Error() string { + return fmt.Sprintf("Endpoint (%s) already exists (Only one endpoint allowed)", string(ee)) +} + +// Forbidden denotes the type of this error +func (ee ErrEndpointExists) Forbidden() {} + +// ErrNotImplemented is returned when a Driver has not implemented an API yet +type ErrNotImplemented struct{} + +func (eni *ErrNotImplemented) Error() string { + return "The API is not implemented yet" +} + +// NotImplemented denotes the type of this error +func (eni *ErrNotImplemented) NotImplemented() {} + +// ErrNoEndpoint is returned if no endpoint with the specified id exists +type ErrNoEndpoint string + +func (ene ErrNoEndpoint) Error() string { + return fmt.Sprintf("No endpoint (%s) exists", string(ene)) +} + +// NotFound denotes the type of this error +func (ene ErrNoEndpoint) NotFound() {} + +// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered +type ErrActiveRegistration string + +// Error interface for ErrActiveRegistration +func (ar ErrActiveRegistration) Error() string { + return fmt.Sprintf("Driver already registered for type %q", string(ar)) +} + +// Forbidden denotes the type of this error +func (ar ErrActiveRegistration) Forbidden() {} diff --git a/drivers/bridge/bridge.go b/drivers/bridge/bridge.go index a9868608..1681e126 100644 --- a/drivers/bridge/bridge.go +++ b/drivers/bridge/bridge.go @@ -109,7 +109,7 @@ func Init(dc driverapi.DriverCallback) error { // Whatever can be assessed a priori before attempting any programming. func (c *NetworkConfiguration) Validate() error { if c.Mtu < 0 { - return ErrInvalidMtu + return ErrInvalidMtu(c.Mtu) } // If bridge v4 subnet is specified @@ -118,19 +118,19 @@ func (c *NetworkConfiguration) Validate() error { if c.FixedCIDR != nil { // Check Network address if !c.AddressIPv4.Contains(c.FixedCIDR.IP) { - return ErrInvalidContainerSubnet + return &ErrInvalidContainerSubnet{} } // Check it is effectively a subset brNetLen, _ := c.AddressIPv4.Mask.Size() cnNetLen, _ := c.FixedCIDR.Mask.Size() if brNetLen > cnNetLen { - return ErrInvalidContainerSubnet + return &ErrInvalidContainerSubnet{} } } // If default gw is specified, it must be part of bridge subnet if c.DefaultGatewayIPv4 != nil { if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) { - return ErrInvalidGateway + return &ErrInvalidGateway{} } } } @@ -138,7 +138,7 @@ func (c *NetworkConfiguration) Validate() error { // If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil { if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) { - return ErrInvalidGateway + return &ErrInvalidGateway{} } } @@ -167,7 +167,7 @@ func (d *driver) Config(option map[string]interface{}) error { defer d.Unlock() if d.config != nil { - return ErrConfigExists + return &ErrConfigExists{} } genericData, ok := option[netlabel.GenericData] @@ -182,7 +182,7 @@ func (d *driver) Config(option map[string]interface{}) error { case *Configuration: config = opt default: - return ErrInvalidDriverConfig + return &ErrInvalidDriverConfig{} } d.config = config @@ -220,7 +220,7 @@ func parseNetworkOptions(option options.Generic) (*NetworkConfiguration, error) case *NetworkConfiguration: config = opt default: - return nil, ErrInvalidNetworkConfig + return nil, &ErrInvalidNetworkConfig{} } if err := config.Validate(); err != nil { @@ -247,7 +247,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err // Sanity checks if d.network != nil { d.Unlock() - return ErrNetworkExists + return &ErrNetworkExists{} } // Create and set network handler in driver @@ -361,7 +361,7 @@ func (d *driver) DeleteNetwork(nid types.UUID) error { // Sanity check if n == nil { - err = driverapi.ErrNoNetwork + err = driverapi.ErrNoNetwork(nid) return err } @@ -397,7 +397,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn config := n.config d.Unlock() if n == nil { - return driverapi.ErrNoNetwork + return driverapi.ErrNoNetwork(nid) } // Sanity check @@ -416,7 +416,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn // Endpoint with that id exists either on desired or other sandbox if ep != nil { - return driverapi.ErrEndpointExists + return driverapi.ErrEndpointExists(eid) } // Try to convert the options to endpoint configuration @@ -578,7 +578,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { config := n.config d.Unlock() if n == nil { - return driverapi.ErrNoNetwork + return driverapi.ErrNoNetwork(nid) } // Sanity Check @@ -648,7 +648,7 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, n := d.network d.Unlock() if n == nil { - return nil, driverapi.ErrNoNetwork + return nil, driverapi.ErrNoNetwork(nid) } // Sanity check @@ -665,7 +665,7 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, return nil, err } if ep == nil { - return nil, driverapi.ErrNoEndpoint + return nil, driverapi.ErrNoEndpoint(eid) } m := make(map[string]interface{}) @@ -856,7 +856,7 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat if mac, ok := opt.(net.HardwareAddr); ok { ec.MacAddress = mac } else { - return nil, ErrInvalidEndpointConfig + return nil, &ErrInvalidEndpointConfig{} } } @@ -864,7 +864,7 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat if bs, ok := opt.([]types.PortBinding); ok { ec.PortBindings = bs } else { - return nil, ErrInvalidEndpointConfig + return nil, &ErrInvalidEndpointConfig{} } } @@ -872,7 +872,7 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat if ports, ok := opt.([]types.TransportPort); ok { ec.ExposedPorts = ports } else { - return nil, ErrInvalidEndpointConfig + return nil, &ErrInvalidEndpointConfig{} } } @@ -924,5 +924,5 @@ func generateIfaceName() (string, error) { return "", err } } - return "", ErrIfaceName + return "", &ErrIfaceName{} } diff --git a/drivers/bridge/error.go b/drivers/bridge/error.go deleted file mode 100644 index 5f149c46..00000000 --- a/drivers/bridge/error.go +++ /dev/null @@ -1,201 +0,0 @@ -package bridge - -import ( - "errors" - "fmt" - "net" -) - -var ( - // ErrConfigExists error is returned when driver already has a config applied. - ErrConfigExists = errors.New("configuration already exists, bridge configuration can be applied only once") - - // ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config - ErrInvalidDriverConfig = errors.New("Invalid configuration passed to Bridge Driver") - - // ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config. - ErrInvalidNetworkConfig = errors.New("trying to create a network on a driver without valid config") - - // ErrInvalidContainerConfig error is returned when a endpoint create is attempted with an invalid configuration. - ErrInvalidContainerConfig = errors.New("Error in joining a container due to invalid configuration") - - // ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration. - ErrInvalidEndpointConfig = errors.New("trying to create an endpoint with an invalid endpoint configuration") - - // ErrNetworkExists error is returned when a network already exists and another network is created. - ErrNetworkExists = errors.New("network already exists, bridge can only have one network") - - // ErrIfaceName error is returned when a new name could not be generated. - ErrIfaceName = errors.New("failed to find name for new interface") - - // ErrNoIPAddr error is returned when bridge has no IPv4 address configured. - ErrNoIPAddr = errors.New("bridge has no IPv4 address configured") - - // ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid. - ErrInvalidGateway = errors.New("default gateway ip must be part of the network") - - // ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid. - ErrInvalidContainerSubnet = errors.New("container subnet must be a subset of bridge network") - - // ErrInvalidMtu is returned when the user provided MTU is not valid. - ErrInvalidMtu = errors.New("invalid MTU number") - - // ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration - // not enabled. - ErrIPFwdCfg = errors.New("unexpected request to enable IP Forwarding") -) - -// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid. -type ErrInvalidPort string - -func (ip ErrInvalidPort) Error() string { - return fmt.Sprintf("invalid transport port: %s", string(ip)) -} - -// ErrUnsupportedAddressType is returned when the specified address type is not supported. -type ErrUnsupportedAddressType string - -func (uat ErrUnsupportedAddressType) Error() string { - return fmt.Sprintf("unsupported address type: %s", string(uat)) -} - -// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid. -type ErrInvalidAddressBinding string - -func (iab ErrInvalidAddressBinding) Error() string { - return fmt.Sprintf("invalid host address in port binding: %s", string(iab)) -} - -// ActiveEndpointsError is returned when there are -// still active endpoints in the network being deleted. -type ActiveEndpointsError string - -func (aee ActiveEndpointsError) Error() string { - return fmt.Sprintf("network %s has active endpoint", string(aee)) -} - -// InvalidNetworkIDError is returned when the passed -// network id for an existing network is not a known id. -type InvalidNetworkIDError string - -func (inie InvalidNetworkIDError) Error() string { - return fmt.Sprintf("invalid network id %s", string(inie)) -} - -// InvalidEndpointIDError is returned when the passed -// endpoint id is not valid. -type InvalidEndpointIDError string - -func (ieie InvalidEndpointIDError) Error() string { - return fmt.Sprintf("invalid endpoint id: %s", string(ieie)) -} - -// InvalidSandboxIDError is returned when the passed -// sandbox id valid. -type InvalidSandboxIDError string - -func (isie InvalidSandboxIDError) Error() string { - return fmt.Sprintf("invalid sanbox id: %s", string(isie)) -} - -// EndpointNotFoundError is returned when the no endpoint -// with the passed endpoint id is found. -type EndpointNotFoundError string - -func (enfe EndpointNotFoundError) Error() string { - return fmt.Sprintf("endpoint not found: %s", string(enfe)) -} - -// NonDefaultBridgeExistError is returned when a non-default -// bridge config is passed but it does not already exist. -type NonDefaultBridgeExistError string - -func (ndbee NonDefaultBridgeExistError) Error() string { - return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee)) -} - -// FixedCIDRv4Error is returned when fixed-cidrv4 configuration -// failed. -type FixedCIDRv4Error struct { - net *net.IPNet - subnet *net.IPNet - err error -} - -func (fcv4 *FixedCIDRv4Error) Error() string { - return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.subnet, fcv4.net, fcv4.err) -} - -// FixedCIDRv6Error is returned when fixed-cidrv6 configuration -// failed. -type FixedCIDRv6Error struct { - net *net.IPNet - err error -} - -func (fcv6 *FixedCIDRv6Error) Error() string { - return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.net, fcv6.net, fcv6.err) -} - -type ipTableCfgError string - -func (name ipTableCfgError) Error() string { - return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name)) -} - -type invalidIPTablesCfgError string - -func (action invalidIPTablesCfgError) Error() string { - return fmt.Sprintf("Invalid IPTables action '%s'", string(action)) -} - -// IPv4AddrRangeError is returned when a valid IP address range couldn't be found. -type IPv4AddrRangeError string - -func (name IPv4AddrRangeError) Error() string { - return fmt.Sprintf("can't find an address range for interface %q", string(name)) -} - -// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge. -type IPv4AddrAddError struct { - ip *net.IPNet - err error -} - -func (ipv4 *IPv4AddrAddError) Error() string { - return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.ip, ipv4.err) -} - -// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge. -type IPv6AddrAddError struct { - ip *net.IPNet - err error -} - -func (ipv6 *IPv6AddrAddError) Error() string { - return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.ip, ipv6.err) -} - -// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured. -type IPv4AddrNoMatchError struct { - ip net.IP - cfgIP net.IP -} - -func (ipv4 *IPv4AddrNoMatchError) Error() string { - return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.ip, ipv4.cfgIP) -} - -// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured. -type IPv6AddrNoMatchError net.IPNet - -func (ipv6 *IPv6AddrNoMatchError) Error() string { - return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String()) -} - -// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address -type InvalidLinkIPAddrError string - -func (address InvalidLinkIPAddrError) Error() string { - return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address)) -} diff --git a/drivers/bridge/errors.go b/drivers/bridge/errors.go new file mode 100644 index 00000000..d22912c5 --- /dev/null +++ b/drivers/bridge/errors.go @@ -0,0 +1,341 @@ +package bridge + +import ( + "fmt" + "net" +) + +// ErrConfigExists error is returned when driver already has a config applied. +type ErrConfigExists struct{} + +func (ece *ErrConfigExists) Error() string { + return "configuration already exists, bridge configuration can be applied only once" +} + +// Forbidden denotes the type of this error +func (ece *ErrConfigExists) Forbidden() {} + +// ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config +type ErrInvalidDriverConfig struct{} + +func (eidc *ErrInvalidDriverConfig) Error() string { + return "Invalid configuration passed to Bridge Driver" +} + +// BadRequest denotes the type of this error +func (eidc *ErrInvalidDriverConfig) BadRequest() {} + +// ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config. +type ErrInvalidNetworkConfig struct{} + +func (einc *ErrInvalidNetworkConfig) Error() string { + return "trying to create a network on a driver without valid config" +} + +// Forbidden denotes the type of this error +func (einc *ErrInvalidNetworkConfig) Forbidden() {} + +// ErrInvalidContainerConfig error is returned when a endpoint create is attempted with an invalid configuration. +type ErrInvalidContainerConfig struct{} + +func (eicc *ErrInvalidContainerConfig) Error() string { + return "Error in joining a container due to invalid configuration" +} + +// BadRequest denotes the type of this error +func (eicc *ErrInvalidContainerConfig) BadRequest() {} + +// ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration. +type ErrInvalidEndpointConfig struct{} + +func (eiec *ErrInvalidEndpointConfig) Error() string { + return "trying to create an endpoint with an invalid endpoint configuration" +} + +// BadRequest denotes the type of this error +func (eiec *ErrInvalidEndpointConfig) BadRequest() {} + +// ErrNetworkExists error is returned when a network already exists and another network is created. +type ErrNetworkExists struct{} + +func (ene *ErrNetworkExists) Error() string { + return "network already exists, bridge can only have one network" +} + +// Forbidden denotes the type of this error +func (ene *ErrNetworkExists) Forbidden() {} + +// ErrIfaceName error is returned when a new name could not be generated. +type ErrIfaceName struct{} + +func (ein *ErrIfaceName) Error() string { + return "failed to find name for new interface" +} + +// InternalError denotes the type of this error +func (ein *ErrIfaceName) InternalError() {} + +// ErrNoIPAddr error is returned when bridge has no IPv4 address configured. +type ErrNoIPAddr struct{} + +func (enip *ErrNoIPAddr) Error() string { + return "bridge has no IPv4 address configured" +} + +// InternalError denotes the type of this error +func (enip *ErrNoIPAddr) InternalError() {} + +// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid. +type ErrInvalidGateway struct{} + +func (eig *ErrInvalidGateway) Error() string { + return "default gateway ip must be part of the network" +} + +// BadRequest denotes the type of this error +func (eig *ErrInvalidGateway) BadRequest() {} + +// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid. +type ErrInvalidContainerSubnet struct{} + +func (eis *ErrInvalidContainerSubnet) Error() string { + return "container subnet must be a subset of bridge network" +} + +// BadRequest denotes the type of this error +func (eis *ErrInvalidContainerSubnet) BadRequest() {} + +// ErrInvalidMtu is returned when the user provided MTU is not valid. +type ErrInvalidMtu int + +func (eim ErrInvalidMtu) Error() string { + return fmt.Sprintf("invalid MTU number: %d", int(eim)) +} + +// BadRequest denotes the type of this error +func (eim ErrInvalidMtu) BadRequest() {} + +// ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration +// not enabled. +type ErrIPFwdCfg struct{} + +func (eipf *ErrIPFwdCfg) Error() string { + return "unexpected request to enable IP Forwarding" +} + +// BadRequest denotes the type of this error +func (eipf *ErrIPFwdCfg) BadRequest() {} + +// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid. +type ErrInvalidPort string + +func (ip ErrInvalidPort) Error() string { + return fmt.Sprintf("invalid transport port: %s", string(ip)) +} + +// BadRequest denotes the type of this error +func (ip ErrInvalidPort) BadRequest() {} + +// ErrUnsupportedAddressType is returned when the specified address type is not supported. +type ErrUnsupportedAddressType string + +func (uat ErrUnsupportedAddressType) Error() string { + return fmt.Sprintf("unsupported address type: %s", string(uat)) +} + +// BadRequest denotes the type of this error +func (uat ErrUnsupportedAddressType) BadRequest() {} + +// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid. +type ErrInvalidAddressBinding string + +func (iab ErrInvalidAddressBinding) Error() string { + return fmt.Sprintf("invalid host address in port binding: %s", string(iab)) +} + +// BadRequest denotes the type of this error +func (iab ErrInvalidAddressBinding) BadRequest() {} + +// ActiveEndpointsError is returned when there are +// still active endpoints in the network being deleted. +type ActiveEndpointsError string + +func (aee ActiveEndpointsError) Error() string { + return fmt.Sprintf("network %s has active endpoint", string(aee)) +} + +// Forbidden denotes the type of this error +func (aee ActiveEndpointsError) Forbidden() {} + +// InvalidNetworkIDError is returned when the passed +// network id for an existing network is not a known id. +type InvalidNetworkIDError string + +func (inie InvalidNetworkIDError) Error() string { + return fmt.Sprintf("invalid network id %s", string(inie)) +} + +// NotFound denotes the type of this error +func (inie InvalidNetworkIDError) NotFound() {} + +// InvalidEndpointIDError is returned when the passed +// endpoint id is not valid. +type InvalidEndpointIDError string + +func (ieie InvalidEndpointIDError) Error() string { + return fmt.Sprintf("invalid endpoint id: %s", string(ieie)) +} + +// BadRequest denotes the type of this error +func (ieie InvalidEndpointIDError) BadRequest() {} + +// InvalidSandboxIDError is returned when the passed +// sandbox id is not valid. +type InvalidSandboxIDError string + +func (isie InvalidSandboxIDError) Error() string { + return fmt.Sprintf("invalid sanbox id: %s", string(isie)) +} + +// BadRequest denotes the type of this error +func (isie InvalidSandboxIDError) BadRequest() {} + +// EndpointNotFoundError is returned when the no endpoint +// with the passed endpoint id is found. +type EndpointNotFoundError string + +func (enfe EndpointNotFoundError) Error() string { + return fmt.Sprintf("endpoint not found: %s", string(enfe)) +} + +// NotFound denotes the type of this error +func (enfe EndpointNotFoundError) NotFound() {} + +// NonDefaultBridgeExistError is returned when a non-default +// bridge config is passed but it does not already exist. +type NonDefaultBridgeExistError string + +func (ndbee NonDefaultBridgeExistError) Error() string { + return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee)) +} + +// Forbidden denotes the type of this error +func (ndbee NonDefaultBridgeExistError) Forbidden() {} + +// FixedCIDRv4Error is returned when fixed-cidrv4 configuration +// failed. +type FixedCIDRv4Error struct { + Net *net.IPNet + Subnet *net.IPNet + Err error +} + +func (fcv4 *FixedCIDRv4Error) Error() string { + return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.Subnet, fcv4.Net, fcv4.Err) +} + +// InternalError denotes the type of this error +func (fcv4 *FixedCIDRv4Error) InternalError() {} + +// FixedCIDRv6Error is returned when fixed-cidrv6 configuration +// failed. +type FixedCIDRv6Error struct { + Net *net.IPNet + Err error +} + +func (fcv6 *FixedCIDRv6Error) Error() string { + return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.Net, fcv6.Net, fcv6.Err) +} + +// InternalError denotes the type of this error +func (fcv6 *FixedCIDRv6Error) InternalError() {} + +// IPTableCfgError is returned when an unexpected ip tables configuration is entered +type IPTableCfgError string + +func (name IPTableCfgError) Error() string { + return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name)) +} + +// BadRequest denotes the type of this error +func (name IPTableCfgError) BadRequest() {} + +// InvalidIPTablesCfgError is returned when an invalid ip tables configuration is entered +type InvalidIPTablesCfgError string + +func (action InvalidIPTablesCfgError) Error() string { + return fmt.Sprintf("Invalid IPTables action '%s'", string(action)) +} + +// BadRequest denotes the type of this error +func (action InvalidIPTablesCfgError) BadRequest() {} + +// IPv4AddrRangeError is returned when a valid IP address range couldn't be found. +type IPv4AddrRangeError string + +func (name IPv4AddrRangeError) Error() string { + return fmt.Sprintf("can't find an address range for interface %q", string(name)) +} + +// BadRequest denotes the type of this error +func (name IPv4AddrRangeError) BadRequest() {} + +// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge. +type IPv4AddrAddError struct { + IP *net.IPNet + Err error +} + +func (ipv4 *IPv4AddrAddError) Error() string { + return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.IP, ipv4.Err) +} + +// InternalError denotes the type of this error +func (ipv4 *IPv4AddrAddError) InternalError() {} + +// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge. +type IPv6AddrAddError struct { + IP *net.IPNet + Err error +} + +func (ipv6 *IPv6AddrAddError) Error() string { + return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.IP, ipv6.Err) +} + +// InternalError denotes the type of this error +func (ipv6 *IPv6AddrAddError) InternalError() {} + +// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured. +type IPv4AddrNoMatchError struct { + IP net.IP + CfgIP net.IP +} + +func (ipv4 *IPv4AddrNoMatchError) Error() string { + return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.IP, ipv4.CfgIP) +} + +// BadRequest denotes the type of this error +func (ipv4 *IPv4AddrNoMatchError) BadRequest() {} + +// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured. +type IPv6AddrNoMatchError net.IPNet + +func (ipv6 *IPv6AddrNoMatchError) Error() string { + return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String()) +} + +// BadRequest denotes the type of this error +func (ipv6 *IPv6AddrNoMatchError) BadRequest() {} + +// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address +type InvalidLinkIPAddrError string + +func (address InvalidLinkIPAddrError) Error() string { + return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address)) +} + +// BadRequest denotes the type of this error +func (address InvalidLinkIPAddrError) BadRequest() {} diff --git a/drivers/bridge/link.go b/drivers/bridge/link.go index affe976c..4e4444e0 100644 --- a/drivers/bridge/link.go +++ b/drivers/bridge/link.go @@ -57,7 +57,7 @@ func linkContainers(action, parentIP, childIP string, ports []types.TransportPor case "-D": nfAction = iptables.Delete default: - return invalidIPTablesCfgError(action) + return InvalidIPTablesCfgError(action) } ip1 := net.ParseIP(parentIP) diff --git a/drivers/bridge/network_test.go b/drivers/bridge/network_test.go index abadc07e..20afea90 100644 --- a/drivers/bridge/network_test.go +++ b/drivers/bridge/network_test.go @@ -125,8 +125,8 @@ func TestLinkCreateTwo(t *testing.T) { te2 := &testEndpoint{ifaces: []*testInterface{}} err = d.CreateEndpoint("dummy", "ep", te2, nil) if err != nil { - if err != driverapi.ErrEndpointExists { - t.Fatalf("Failed with a wrong error :%s", err.Error()) + if _, ok := err.(driverapi.ErrEndpointExists); !ok { + t.Fatalf("Failed with a wrong error: %s", err.Error()) } } else { t.Fatalf("Expected to fail while trying to add same endpoint twice") diff --git a/drivers/bridge/setup_fixedcidrv4.go b/drivers/bridge/setup_fixedcidrv4.go index af6aecc7..7657aa33 100644 --- a/drivers/bridge/setup_fixedcidrv4.go +++ b/drivers/bridge/setup_fixedcidrv4.go @@ -1,6 +1,8 @@ package bridge -import log "github.com/Sirupsen/logrus" +import ( + log "github.com/Sirupsen/logrus" +) func setupFixedCIDRv4(config *NetworkConfiguration, i *bridgeInterface) error { addrv4, _, err := i.addresses() @@ -10,7 +12,7 @@ func setupFixedCIDRv4(config *NetworkConfiguration, i *bridgeInterface) error { log.Debugf("Using IPv4 subnet: %v", config.FixedCIDR) if err := ipAllocator.RegisterSubnet(addrv4.IPNet, config.FixedCIDR); err != nil { - return &FixedCIDRv4Error{subnet: config.FixedCIDR, net: addrv4.IPNet, err: err} + return &FixedCIDRv4Error{Subnet: config.FixedCIDR, Net: addrv4.IPNet, Err: err} } return nil diff --git a/drivers/bridge/setup_fixedcidrv6.go b/drivers/bridge/setup_fixedcidrv6.go index ce1718a3..ade465a1 100644 --- a/drivers/bridge/setup_fixedcidrv6.go +++ b/drivers/bridge/setup_fixedcidrv6.go @@ -1,11 +1,13 @@ package bridge -import log "github.com/Sirupsen/logrus" +import ( + log "github.com/Sirupsen/logrus" +) func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error { log.Debugf("Using IPv6 subnet: %v", config.FixedCIDRv6) if err := ipAllocator.RegisterSubnet(config.FixedCIDRv6, config.FixedCIDRv6); err != nil { - return &FixedCIDRv6Error{net: config.FixedCIDRv6, err: err} + return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err} } return nil diff --git a/drivers/bridge/setup_ip_forwarding.go b/drivers/bridge/setup_ip_forwarding.go index 85fddbca..1bc3416c 100644 --- a/drivers/bridge/setup_ip_forwarding.go +++ b/drivers/bridge/setup_ip_forwarding.go @@ -13,7 +13,7 @@ const ( func setupIPForwarding(config *Configuration) error { // Sanity Check if config.EnableIPForwarding == false { - return ErrIPFwdCfg + return &ErrIPFwdCfg{} } // Enable IPv4 forwarding diff --git a/drivers/bridge/setup_ip_forwarding_test.go b/drivers/bridge/setup_ip_forwarding_test.go index a555a393..7c4cfea2 100644 --- a/drivers/bridge/setup_ip_forwarding_test.go +++ b/drivers/bridge/setup_ip_forwarding_test.go @@ -47,7 +47,7 @@ func TestUnexpectedSetupIPForwarding(t *testing.T) { t.Fatal("Setup IP forwarding was expected to fail") } - if err != ErrIPFwdCfg { + if _, ok := err.(*ErrIPFwdCfg); !ok { t.Fatalf("Setup IP forwarding failed with unexpected error: %v", err) } } diff --git a/drivers/bridge/setup_ip_tables.go b/drivers/bridge/setup_ip_tables.go index e74ded74..3d461976 100644 --- a/drivers/bridge/setup_ip_tables.go +++ b/drivers/bridge/setup_ip_tables.go @@ -16,7 +16,7 @@ const ( func setupIPTables(config *NetworkConfiguration, i *bridgeInterface) error { // Sanity check. if config.EnableIPTables == false { - return ipTableCfgError(config.BridgeName) + return IPTableCfgError(config.BridgeName) } hairpinMode := !config.EnableUserlandProxy diff --git a/drivers/bridge/setup_ipv4.go b/drivers/bridge/setup_ipv4.go index 7a775a37..a0059c85 100644 --- a/drivers/bridge/setup_ipv4.go +++ b/drivers/bridge/setup_ipv4.go @@ -71,7 +71,7 @@ func setupBridgeIPv4(config *NetworkConfiguration, i *bridgeInterface) error { log.Debugf("Creating bridge interface %q with network %s", config.BridgeName, bridgeIPv4) if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv4}); err != nil { - return &IPv4AddrAddError{ip: bridgeIPv4, err: err} + return &IPv4AddrAddError{IP: bridgeIPv4, Err: err} } // Store bridge network and default gateway @@ -114,7 +114,7 @@ func electBridgeIPv4(config *NetworkConfiguration) (*net.IPNet, error) { func setupGatewayIPv4(config *NetworkConfiguration, i *bridgeInterface) error { if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) { - return ErrInvalidGateway + return &ErrInvalidGateway{} } if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil { return err diff --git a/drivers/bridge/setup_ipv6.go b/drivers/bridge/setup_ipv6.go index 81cbda1b..264e5b2a 100644 --- a/drivers/bridge/setup_ipv6.go +++ b/drivers/bridge/setup_ipv6.go @@ -37,7 +37,7 @@ func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error { // Add the default link local ipv6 address if it doesn't exist if !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) { if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil { - return &IPv6AddrAddError{ip: bridgeIPv6, err: err} + return &IPv6AddrAddError{IP: bridgeIPv6, Err: err} } } @@ -50,10 +50,10 @@ func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error { func setupGatewayIPv6(config *NetworkConfiguration, i *bridgeInterface) error { if config.FixedCIDRv6 == nil { - return ErrInvalidContainerSubnet + return &ErrInvalidContainerSubnet{} } if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) { - return ErrInvalidGateway + return &ErrInvalidGateway{} } if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil { return err diff --git a/drivers/bridge/setup_verify.go b/drivers/bridge/setup_verify.go index 9954ca85..46d025d1 100644 --- a/drivers/bridge/setup_verify.go +++ b/drivers/bridge/setup_verify.go @@ -1,6 +1,8 @@ package bridge -import "github.com/vishvananda/netlink" +import ( + "github.com/vishvananda/netlink" +) func setupVerifyAndReconcile(config *NetworkConfiguration, i *bridgeInterface) error { // Fetch a single IPv4 and a slice of IPv6 addresses from the bridge. @@ -11,12 +13,12 @@ func setupVerifyAndReconcile(config *NetworkConfiguration, i *bridgeInterface) e // Verify that the bridge does have an IPv4 address. if addrv4.IPNet == nil { - return ErrNoIPAddr + return &ErrNoIPAddr{} } // Verify that the bridge IPv4 address matches the requested configuration. if config.AddressIPv4 != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) { - return &IPv4AddrNoMatchError{ip: addrv4.IP, cfgIP: config.AddressIPv4.IP} + return &IPv4AddrNoMatchError{IP: addrv4.IP, CfgIP: config.AddressIPv4.IP} } // Verify that one of the bridge IPv6 addresses matches the requested diff --git a/drivers/remote/driver.go b/drivers/remote/driver.go index 0159fb40..0d4ee2ca 100644 --- a/drivers/remote/driver.go +++ b/drivers/remote/driver.go @@ -31,37 +31,37 @@ func Init(dc driverapi.DriverCallback) error { } func (d *driver) Config(option map[string]interface{}) error { - return driverapi.ErrNotImplemented + return &driverapi.ErrNotImplemented{} } func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { - return driverapi.ErrNotImplemented + return &driverapi.ErrNotImplemented{} } func (d *driver) DeleteNetwork(nid types.UUID) error { - return driverapi.ErrNotImplemented + return &driverapi.ErrNotImplemented{} } func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { - return driverapi.ErrNotImplemented + return &driverapi.ErrNotImplemented{} } func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { - return driverapi.ErrNotImplemented + return &driverapi.ErrNotImplemented{} } func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { - return nil, driverapi.ErrNotImplemented + return nil, &driverapi.ErrNotImplemented{} } // Join method is invoked when a Sandbox is attached to an endpoint. func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - return driverapi.ErrNotImplemented + return &driverapi.ErrNotImplemented{} } // Leave method is invoked when a Sandbox detaches from an endpoint. func (d *driver) Leave(nid, eid types.UUID) error { - return driverapi.ErrNotImplemented + return &driverapi.ErrNotImplemented{} } func (d *driver) Type() string { diff --git a/endpoint.go b/endpoint.go index 8f243d50..56252ab5 100644 --- a/endpoint.go +++ b/endpoint.go @@ -216,7 +216,7 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai ep.Lock() if ep.container != nil { ep.Unlock() - return nil, ErrInvalidJoin + return nil, ErrInvalidJoin{} } ep.container = &containerInfo{ @@ -334,7 +334,7 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error { if container == nil || container.id == "" || containerID == "" || container.id != containerID { if container == nil { - err = ErrNoContainer + err = ErrNoContainer{} } else { err = InvalidContainerIDError(containerID) } @@ -412,7 +412,7 @@ func (ep *endpoint) buildHostsFiles() error { ep.Unlock() if container == nil { - return ErrNoContainer + return ErrNoContainer{} } if container.config.hostsPath == "" { @@ -462,7 +462,7 @@ func (ep *endpoint) updateParentHosts() error { ep.Unlock() if container == nil { - return ErrNoContainer + return ErrNoContainer{} } for _, update := range container.config.parentUpdates { @@ -495,7 +495,7 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error { ep.Unlock() if container == nil { - return ErrNoContainer + return ErrNoContainer{} } oldHash := []byte{} @@ -573,7 +573,7 @@ func (ep *endpoint) setupDNS() error { ep.Unlock() if container == nil { - return ErrNoContainer + return ErrNoContainer{} } if container.config.resolvConfPath == "" { diff --git a/error.go b/error.go index 088bfbc8..a1cd01d6 100644 --- a/error.go +++ b/error.go @@ -1,34 +1,83 @@ package libnetwork import ( - "errors" "fmt" ) -var ( - // ErrNoSuchNetwork is returned when a network query finds no result - ErrNoSuchNetwork = errors.New("network not found") - // ErrNoSuchEndpoint is returned when a endpoint query finds no result - ErrNoSuchEndpoint = errors.New("endpoint not found") - // ErrNilNetworkDriver is returned if a nil network driver - // is passed to NewNetwork api. - ErrNilNetworkDriver = errors.New("nil NetworkDriver instance") - // ErrInvalidNetworkDriver is returned if an invalid driver - // instance is passed. - ErrInvalidNetworkDriver = errors.New("invalid driver bound to network") - // ErrInvalidJoin is returned if a join is attempted on an endpoint - // which already has a container joined. - ErrInvalidJoin = errors.New("a container has already joined the endpoint") - // ErrNoContainer is returned when the endpoint has no container - // attached to it. - ErrNoContainer = errors.New("no container attached to the endpoint") - // ErrInvalidID is returned when a query-by-id method is being invoked - // with an empty id parameter - ErrInvalidID = errors.New("invalid ID") - // ErrInvalidName is returned when a query-by-name or resource create method is - // invoked with an empty name parameter - ErrInvalidName = errors.New("invalid Name") -) +// ErrNoSuchNetwork is returned when a network query finds no result +type ErrNoSuchNetwork string + +func (nsn ErrNoSuchNetwork) Error() string { + return fmt.Sprintf("network %s not found", string(nsn)) +} + +// BadRequest denotes the type of this error +func (nsn ErrNoSuchNetwork) BadRequest() {} + +// ErrNoSuchEndpoint is returned when a endpoint query finds no result +type ErrNoSuchEndpoint string + +func (nse ErrNoSuchEndpoint) Error() string { + return fmt.Sprintf("endpoint %s not found", string(nse)) +} + +// BadRequest denotes the type of this error +func (nse ErrNoSuchEndpoint) BadRequest() {} + +// ErrInvalidNetworkDriver is returned if an invalid driver +// name is passed. +type ErrInvalidNetworkDriver string + +func (ind ErrInvalidNetworkDriver) Error() string { + return fmt.Sprintf("invalid driver bound to network: %s", string(ind)) +} + +// BadRequest denotes the type of this error +func (ind ErrInvalidNetworkDriver) BadRequest() {} + +// ErrInvalidJoin is returned if a join is attempted on an endpoint +// which already has a container joined. +type ErrInvalidJoin struct{} + +func (ij ErrInvalidJoin) Error() string { + return "a container has already joined the endpoint" +} + +// BadRequest denotes the type of this error +func (ij ErrInvalidJoin) BadRequest() {} + +// ErrNoContainer is returned when the endpoint has no container +// attached to it. +type ErrNoContainer struct{} + +func (nc ErrNoContainer) Error() string { + return "a container has already joined the endpoint" +} + +// Maskable denotes the type of this error +func (nc ErrNoContainer) Maskable() {} + +// ErrInvalidID is returned when a query-by-id method is being invoked +// with an empty id parameter +type ErrInvalidID string + +func (ii ErrInvalidID) Error() string { + return fmt.Sprintf("invalid id: %s", string(ii)) +} + +// BadRequest denotes the type of this error +func (ii ErrInvalidID) BadRequest() {} + +// ErrInvalidName is returned when a query-by-name or resource create method is +// invoked with an empty name parameter +type ErrInvalidName string + +func (in ErrInvalidName) Error() string { + return fmt.Sprintf("invalid name: %s", string(in)) +} + +// BadRequest denotes the type of this error +func (in ErrInvalidName) BadRequest() {} // NetworkTypeError type is returned when the network type string is not // known to libnetwork. @@ -38,13 +87,19 @@ func (nt NetworkTypeError) Error() string { return fmt.Sprintf("unknown driver %q", string(nt)) } +// NotFound denotes the type of this error +func (nt NetworkTypeError) NotFound() {} + // NetworkNameError is returned when a network with the same name already exists. type NetworkNameError string -func (name NetworkNameError) Error() string { - return fmt.Sprintf("network with name %s already exists", string(name)) +func (nnr NetworkNameError) Error() string { + return fmt.Sprintf("network with name %s already exists", string(nnr)) } +// Forbidden denotes the type of this error +func (nnr NetworkNameError) Forbidden() {} + // UnknownNetworkError is returned when libnetwork could not find in it's database // a network with the same name and id. type UnknownNetworkError struct { @@ -56,6 +111,9 @@ func (une *UnknownNetworkError) Error() string { return fmt.Sprintf("unknown network %s id %s", une.name, une.id) } +// NotFound denotes the type of this error +func (une *UnknownNetworkError) NotFound() {} + // ActiveEndpointsError is returned when a network is deleted which has active // endpoints in it. type ActiveEndpointsError struct { @@ -67,6 +125,9 @@ func (aee *ActiveEndpointsError) Error() string { return fmt.Sprintf("network with name %s id %s has active endpoints", aee.name, aee.id) } +// Forbidden denotes the type of this error +func (aee *ActiveEndpointsError) Forbidden() {} + // UnknownEndpointError is returned when libnetwork could not find in it's database // an endpoint with the same name and id. type UnknownEndpointError struct { @@ -78,6 +139,9 @@ func (uee *UnknownEndpointError) Error() string { return fmt.Sprintf("unknown endpoint %s id %s", uee.name, uee.id) } +// NotFound denotes the type of this error +func (uee *UnknownEndpointError) NotFound() {} + // ActiveContainerError is returned when an endpoint is deleted which has active // containers attached to it. type ActiveContainerError struct { @@ -89,6 +153,9 @@ func (ace *ActiveContainerError) Error() string { return fmt.Sprintf("endpoint with name %s id %s has active containers", ace.name, ace.id) } +// Forbidden denotes the type of this error +func (ace *ActiveContainerError) Forbidden() {} + // InvalidContainerIDError is returned when an invalid container id is passed // in Join/Leave type InvalidContainerIDError string @@ -96,3 +163,6 @@ type InvalidContainerIDError string func (id InvalidContainerIDError) Error() string { return fmt.Sprintf("invalid container id %s", string(id)) } + +// BadRequest denotes the type of this error +func (id InvalidContainerIDError) BadRequest() {} diff --git a/errors_test.go b/errors_test.go new file mode 100644 index 00000000..29bf6686 --- /dev/null +++ b/errors_test.go @@ -0,0 +1,51 @@ +package libnetwork + +import ( + "testing" + + "github.com/docker/libnetwork/types" +) + +func TestErrorInterfaces(t *testing.T) { + + badRequestErrorList := []error{ErrInvalidID(""), ErrInvalidName(""), ErrInvalidJoin{}, ErrInvalidNetworkDriver(""), InvalidContainerIDError(""), ErrNoSuchNetwork(""), ErrNoSuchEndpoint("")} + for _, err := range badRequestErrorList { + switch u := err.(type) { + case types.BadRequestError: + return + default: + t.Fatalf("Failed to detect err %v is of type BadRequestError. Got type: %T", err, u) + } + } + + maskableErrorList := []error{ErrNoContainer{}} + for _, err := range maskableErrorList { + switch u := err.(type) { + case types.MaskableError: + return + default: + t.Fatalf("Failed to detect err %v is of type MaskableError. Got type: %T", err, u) + } + } + + notFoundErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}} + for _, err := range notFoundErrorList { + switch u := err.(type) { + case types.NotFoundError: + return + default: + t.Fatalf("Failed to detect err %v is of type NotFoundError. Got type: %T", err, u) + } + } + + forbiddenErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}} + for _, err := range forbiddenErrorList { + switch u := err.(type) { + case types.ForbiddenError: + return + default: + t.Fatalf("Failed to detect err %v is of type ForbiddenError. Got type: %T", err, u) + } + } + +} diff --git a/iptables/iptables_test.go b/iptables/iptables_test.go index 4035b587..afb3587f 100644 --- a/iptables/iptables_test.go +++ b/iptables/iptables_test.go @@ -11,7 +11,7 @@ import ( _ "github.com/docker/libnetwork/netutils" ) -const chainName = "DOCKER-TEST" +const chainName = "DOCKEREST" var natChain *Chain var filterChain *Chain diff --git a/libnetwork_test.go b/libnetwork_test.go index 8bfa3417..9414f520 100644 --- a/libnetwork_test.go +++ b/libnetwork_test.go @@ -290,7 +290,7 @@ func TestNilRemoteDriver(t *testing.T) { t.Fatal("Expected to fail. But instead succeeded") } - if err != plugins.ErrNotFound { + if _, ok := err.(types.NotFoundError); !ok { t.Fatalf("Did not fail with expected error. Actual error: %v", err) } } @@ -338,8 +338,9 @@ func TestNetworkName(t *testing.T) { if err == nil { t.Fatal("Expected to fail. But instead succeeded") } - if err != libnetwork.ErrInvalidName { - t.Fatal("Expected to fail with ErrInvalidName error") + + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected to fail with ErrInvalidName error. Got %v", err) } networkName := "testnetwork" @@ -475,8 +476,8 @@ func TestUnknownEndpoint(t *testing.T) { if err == nil { t.Fatal("Expected to fail. But instead succeeded") } - if err != libnetwork.ErrInvalidName { - t.Fatal("Expected to fail with ErrInvalidName error") + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected to fail with ErrInvalidName error. Actual error: %v", err) } ep, err := network.CreateEndpoint("testep") @@ -613,15 +614,15 @@ func TestControllerQuery(t *testing.T) { if err == nil { t.Fatalf("NetworkByName() succeeded with invalid target name") } - if err != libnetwork.ErrInvalidName { - t.Fatalf("NetworkByName() failed with unexpected error: %v", err) + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected NetworkByName() to fail with ErrInvalidName error. Got: %v", err) } _, err = controller.NetworkByID("") if err == nil { t.Fatalf("NetworkByID() succeeded with invalid target id") } - if err != libnetwork.ErrInvalidID { + if _, ok := err.(libnetwork.ErrInvalidID); !ok { t.Fatalf("NetworkByID() failed with unexpected error: %v", err) } @@ -629,7 +630,7 @@ func TestControllerQuery(t *testing.T) { if err == nil { t.Fatalf("Unexpected success for NetworkByID(): %v", g) } - if err != libnetwork.ErrNoSuchNetwork { + if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok { t.Fatalf("NetworkByID() failed with unexpected error: %v", err) } @@ -695,15 +696,15 @@ func TestNetworkQuery(t *testing.T) { if err == nil { t.Fatalf("EndpointByName() succeeded with invalid target name") } - if err != libnetwork.ErrInvalidName { - t.Fatalf("EndpointByName() failed with unexpected error: %v", err) + if _, ok := err.(libnetwork.ErrInvalidName); !ok { + t.Fatalf("Expected EndpointByName() to fail with ErrInvalidName error. Got: %v", err) } e, err = net1.EndpointByName("IamNotAnEndpoint") if err == nil { t.Fatalf("EndpointByName() succeeded with unknown target name") } - if err != libnetwork.ErrNoSuchEndpoint { + if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok { t.Fatal(err) } if e != nil { @@ -722,7 +723,7 @@ func TestNetworkQuery(t *testing.T) { if err == nil { t.Fatalf("EndpointByID() succeeded with invalid target id") } - if err != libnetwork.ErrInvalidID { + if _, ok := err.(libnetwork.ErrInvalidID); !ok { t.Fatalf("EndpointByID() failed with unexpected error: %v", err) } } @@ -891,7 +892,7 @@ func TestEndpointMultipleJoins(t *testing.T) { t.Fatal("Expected to fail multiple joins for the same endpoint") } - if err != libnetwork.ErrInvalidJoin { + if _, ok := err.(libnetwork.ErrInvalidJoin); !ok { t.Fatalf("Failed for unexpected reason: %v", err) } } @@ -917,7 +918,7 @@ func TestEndpointInvalidLeave(t *testing.T) { } if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { - if err != libnetwork.ErrNoContainer { + if _, ok := err.(libnetwork.ErrNoContainer); !ok { t.Fatalf("Failed for unexpected reason: %v", err) } } @@ -1297,8 +1298,10 @@ func TestValidRemoteDriver(t *testing.T) { _, err = controller.NewNetwork("valid-network-driver", "dummy", libnetwork.NetworkOptionGeneric(getEmptyGenericOption())) - if err != nil && err != driverapi.ErrNotImplemented { - t.Fatal(err) + if err != nil { + if _, ok := err.(*driverapi.ErrNotImplemented); !ok { + t.Fatal(err) + } } } @@ -1371,8 +1374,10 @@ func parallelJoin(t *testing.T, ep libnetwork.Endpoint, thrNumber int) { _, err := ep.Join("racing_container") runtime.LockOSThread() if err != nil { - if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin { - t.Fatal(err) + if _, ok := err.(libnetwork.ErrNoContainer); !ok { + if _, ok := err.(libnetwork.ErrInvalidJoin); !ok { + t.Fatal(err) + } } debugf("JE%d(%v).", thrNumber, err) } @@ -1384,8 +1389,10 @@ func parallelLeave(t *testing.T, ep libnetwork.Endpoint, thrNumber int) { err := ep.Leave("racing_container") runtime.LockOSThread() if err != nil { - if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin { - t.Fatal(err) + if _, ok := err.(libnetwork.ErrNoContainer); !ok { + if _, ok := err.(libnetwork.ErrInvalidJoin); !ok { + t.Fatal(err) + } } debugf("LE%d(%v).", thrNumber, err) } diff --git a/network.go b/network.go index e2955c8d..36938a54 100644 --- a/network.go +++ b/network.go @@ -133,7 +133,7 @@ func (n *network) Delete() error { func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) { if name == "" { - return nil, ErrInvalidName + return nil, ErrInvalidName(name) } ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})} ep.id = types.UUID(stringid.GenerateRandomID()) @@ -173,7 +173,7 @@ func (n *network) WalkEndpoints(walker EndpointWalker) { func (n *network) EndpointByName(name string) (Endpoint, error) { if name == "" { - return nil, ErrInvalidName + return nil, ErrInvalidName(name) } var e Endpoint @@ -188,7 +188,7 @@ func (n *network) EndpointByName(name string) (Endpoint, error) { n.WalkEndpoints(s) if e == nil { - return nil, ErrNoSuchEndpoint + return nil, ErrNoSuchEndpoint(name) } return e, nil @@ -196,12 +196,12 @@ func (n *network) EndpointByName(name string) (Endpoint, error) { func (n *network) EndpointByID(id string) (Endpoint, error) { if id == "" { - return nil, ErrInvalidID + return nil, ErrInvalidID(id) } n.Lock() defer n.Unlock() if e, ok := n.endpoints[types.UUID(id)]; ok { return e, nil } - return nil, ErrNoSuchEndpoint + return nil, ErrNoSuchEndpoint(id) } diff --git a/types/types.go b/types/types.go index 801afd7b..3b83485f 100644 --- a/types/types.go +++ b/types/types.go @@ -11,13 +11,6 @@ import ( // UUID represents a globally unique ID of various resources like network and endpoint type UUID string -// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid. -type ErrInvalidProtocolBinding string - -func (ipb ErrInvalidProtocolBinding) Error() string { - return fmt.Sprintf("invalid transport protocol: %s", string(ipb)) -} - // TransportPort represent a local Layer 4 endpoint type TransportPort struct { Proto Protocol @@ -110,6 +103,13 @@ func (p *PortBinding) Equal(o *PortBinding) bool { return true } +// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid. +type ErrInvalidProtocolBinding string + +func (ipb ErrInvalidProtocolBinding) Error() string { + return fmt.Sprintf("invalid transport protocol: %s", string(ipb)) +} + const ( // ICMP is for the ICMP ip protocol ICMP = 1 @@ -183,3 +183,163 @@ func CompareIPNet(a, b *net.IPNet) bool { } return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask) } + +/****************************** + * Well-known Error Interfaces + ******************************/ + +// MaskableError is an interface for errors which can be ignored by caller +type MaskableError interface { + // Maskable makes implementer into MaskableError type + Maskable() +} + +// BadRequestError is an interface for errors originated by a bad request +type BadRequestError interface { + // BadRequest makes implementer into BadRequestError type + BadRequest() +} + +// NotFoundError is an interface for errors raised because a needed resource is not available +type NotFoundError interface { + // NotFound makes implementer into NotFoundError type + NotFound() +} + +// ForbiddenError is an interface for errors which denote an valid request that cannot be honored +type ForbiddenError interface { + // Forbidden makes implementer into ForbiddenError type + Forbidden() +} + +// NoServiceError is an interface for errors returned when the required service is not available +type NoServiceError interface { + // NoService makes implementer into NoServiceError type + NoService() +} + +// TimeoutError is an interface for errors raised because of timeout +type TimeoutError interface { + // Timeout makes implementer into TimeoutError type + Timeout() +} + +// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented +type NotImplementedError interface { + // NotImplemented makes implementer into NotImplementedError type + NotImplemented() +} + +// InternalError is an interface for errors raised because of an internal error +type InternalError interface { + // Internal makes implementer into InternalError type + Internal() +} + +/****************************** + * Weel-known Error Formatters + ******************************/ + +// BadRequestErrorf creates an instance of BadRequestError +func BadRequestErrorf(format string, params ...interface{}) error { + return badRequest(fmt.Sprintf(format, params...)) +} + +// NotFoundErrorf creates an instance of NotFoundError +func NotFoundErrorf(format string, params ...interface{}) error { + return notFound(fmt.Sprintf(format, params...)) +} + +// ForbiddenErrorf creates an instance of ForbiddenError +func ForbiddenErrorf(format string, params ...interface{}) error { + return forbidden(fmt.Sprintf(format, params...)) +} + +// NoServiceErrorf creates an instance of NoServiceError +func NoServiceErrorf(format string, params ...interface{}) error { + return noService(fmt.Sprintf(format, params...)) +} + +// NotImplementedErrorf creates an instance of NotImplementedError +func NotImplementedErrorf(format string, params ...interface{}) error { + return notImpl(fmt.Sprintf(format, params...)) +} + +// TimeoutErrorf creates an instance of TimeoutError +func TimeoutErrorf(format string, params ...interface{}) error { + return timeout(fmt.Sprintf(format, params...)) +} + +// InternalErrorf creates an instance of InternalError +func InternalErrorf(format string, params ...interface{}) error { + return internal(fmt.Sprintf(format, params...)) +} + +// InternalMaskableErrorf creates an instance of InternalError and MaskableError +func InternalMaskableErrorf(format string, params ...interface{}) error { + return maskInternal(fmt.Sprintf(format, params...)) +} + +/*********************** + * Internal Error Types + ***********************/ +type badRequest string + +func (br badRequest) Error() string { + return string(br) +} +func (br badRequest) BadRequest() {} + +type maskBadRequest string + +type notFound string + +func (nf notFound) Error() string { + return string(nf) +} +func (nf notFound) NotFound() {} + +type forbidden string + +func (frb forbidden) Error() string { + return string(frb) +} +func (frb forbidden) Forbidden() {} + +type noService string + +func (ns noService) Error() string { + return string(ns) +} +func (ns noService) NoService() {} + +type maskNoService string + +type timeout string + +func (to timeout) Error() string { + return string(to) +} +func (to timeout) Timeout() {} + +type notImpl string + +func (ni notImpl) Error() string { + return string(ni) +} +func (ni notImpl) NotImplemented() {} + +type internal string + +func (nt internal) Error() string { + return string(nt) +} +func (nt internal) Internal() {} + +type maskInternal string + +func (mnt maskInternal) Error() string { + return string(mnt) +} +func (mnt maskInternal) Internal() {} +func (mnt maskInternal) Maskable() {} diff --git a/types/types_test.go b/types/types_test.go new file mode 100644 index 00000000..9e96ea85 --- /dev/null +++ b/types/types_test.go @@ -0,0 +1,99 @@ +package types + +import ( + "testing" + + _ "github.com/docker/libnetwork/netutils" +) + +func TestErrorConstructors(t *testing.T) { + var err error + + err = BadRequestErrorf("Io ho %d uccello", 1) + if err.Error() != "Io ho 1 uccello" { + t.Fatal(err) + } + if _, ok := err.(BadRequestError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = NotFoundErrorf("Can't find the %s", "keys") + if err.Error() != "Can't find the keys" { + t.Fatal(err) + } + if _, ok := err.(NotFoundError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = ForbiddenErrorf("Can't open door %d", 2) + if err.Error() != "Can't open door 2" { + t.Fatal(err) + } + if _, ok := err.(ForbiddenError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = NotImplementedErrorf("Functionality %s is not implemented", "x") + if err.Error() != "Functionality x is not implemented" { + t.Fatal(err) + } + if _, ok := err.(NotImplementedError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = TimeoutErrorf("Process %s timed out", "abc") + if err.Error() != "Process abc timed out" { + t.Fatal(err) + } + if _, ok := err.(TimeoutError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = NoServiceErrorf("Driver %s is not available", "mh") + if err.Error() != "Driver mh is not available" { + t.Fatal(err) + } + if _, ok := err.(NoServiceError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = InternalErrorf("Not sure what happened") + if err.Error() != "Not sure what happened" { + t.Fatal(err) + } + if _, ok := err.(InternalError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); ok { + t.Fatal(err) + } + + err = InternalMaskableErrorf("Minor issue, it can be ignored") + if err.Error() != "Minor issue, it can be ignored" { + t.Fatal(err) + } + if _, ok := err.(InternalError); !ok { + t.Fatal(err) + } + if _, ok := err.(MaskableError); !ok { + t.Fatal(err) + } +}