Adjust ipam errors

- Remove from contract predefined errors which are no longer
  valid (ex. ErrInvalidIpamService, ErrInvalidIpamConfigService)

- Do not use network driver error for ipam load failure in controller.go

- Bitseq to expose two well-known errors (no more bit available, bit is already set)

- Default ipam to report proper well-known error on RequestAddress()
  based on bitseq returned error

- Default ipam errors to comply with types error interface

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2015-10-20 17:05:01 -07:00
parent 6f3c0e34f3
commit dc4285b9a4
7 changed files with 60 additions and 55 deletions

View file

@ -24,7 +24,10 @@ const (
)
var (
errNoBitAvailable = fmt.Errorf("no bit available")
// ErrNoBitAvailable is returned when no more bits are available to set
ErrNoBitAvailable = fmt.Errorf("no bit available")
// ErrBitAllocated is returned when the specific bit requested is already set
ErrBitAllocated = fmt.Errorf("requested bit is already allocated")
)
// Handle contains the sequece representing the bitmask and its identifier
@ -94,7 +97,7 @@ func (s *sequence) toString() string {
// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence
func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) {
if s.block == blockMAX || s.count == 0 {
return invalidPos, invalidPos, errNoBitAvailable
return invalidPos, invalidPos, ErrNoBitAvailable
}
bits := from
bitSel := blockFirstBit >> from
@ -197,7 +200,7 @@ func (h *Handle) SetAnyInRange(start, end uint64) (uint64, error) {
return invalidPos, fmt.Errorf("invalid bit range [%d, %d]", start, end)
}
if h.Unselected() == 0 {
return invalidPos, errNoBitAvailable
return invalidPos, ErrNoBitAvailable
}
return h.set(0, start, end, true, false)
}
@ -205,7 +208,7 @@ func (h *Handle) SetAnyInRange(start, end uint64) (uint64, error) {
// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal
func (h *Handle) SetAny() (uint64, error) {
if h.Unselected() == 0 {
return invalidPos, errNoBitAvailable
return invalidPos, ErrNoBitAvailable
}
return h.set(0, 0, h.bits-1, true, false)
}
@ -269,7 +272,7 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool) (uint64
bytePos, bitPos, err = getFirstAvailable(h.head, start)
ret = posToOrdinal(bytePos, bitPos)
if end < ret {
err = errNoBitAvailable
err = ErrNoBitAvailable
}
} else {
bytePos, bitPos, err = checkIfAvailable(h.head, ordinal)
@ -449,7 +452,7 @@ func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
byteOffset += current.count * blockBytes
current = current.next
}
return invalidPos, invalidPos, errNoBitAvailable
return invalidPos, invalidPos, ErrNoBitAvailable
}
// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset
@ -467,7 +470,7 @@ func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) {
}
}
return invalidPos, invalidPos, fmt.Errorf("requested bit is not available")
return invalidPos, invalidPos, ErrBitAllocated
}
// Given the byte position and the sequences list head, return the pointer to the

View file

@ -657,7 +657,7 @@ func TestSetInRange(t *testing.T) {
if err == nil {
t.Fatalf("Expected failure. Got success with ordinal:%d", o)
}
if err != errNoBitAvailable {
if err != ErrNoBitAvailable {
t.Fatalf("Unexpected error: %v", err)
}
@ -673,7 +673,7 @@ func TestSetInRange(t *testing.T) {
if err == nil {
t.Fatalf("Expected failure. Got success with ordinal:%d", o)
}
if err != errNoBitAvailable {
if err != ErrNoBitAvailable {
t.Fatalf("Unexpected error: %v", err)
}
@ -687,7 +687,7 @@ func TestSetInRange(t *testing.T) {
if err == nil {
t.Fatalf("Expected failure. Got success with ordinal:%d", o)
}
if err != errNoBitAvailable {
if err != ErrNoBitAvailable {
t.Fatalf("Unexpected error: %v", err)
}
}

View file

@ -315,17 +315,17 @@ func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error
_, ok := c.ipamDrivers[name]
c.Unlock()
if ok {
return driverapi.ErrActiveRegistration(name)
return types.ForbiddenErrorf("ipam driver %q already registered", name)
}
locAS, glbAS, err := driver.GetDefaultAddressSpaces()
if err != nil {
return fmt.Errorf("ipam driver %s failed to return default address spaces: %v", name, err)
return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err)
}
c.Lock()
c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS}
c.Unlock()
log.Debugf("Registering ipam provider: %s", name)
log.Debugf("Registering ipam driver: %q", name)
return nil
}
@ -667,7 +667,7 @@ func (c *controller) loadIpamDriver(name string) (*ipamData, error) {
id, ok := c.ipamDrivers[name]
c.Unlock()
if !ok {
return nil, ErrInvalidNetworkDriver(name)
return nil, types.BadRequestErrorf("invalid ipam driver: %q", name)
}
return id, nil
}

View file

@ -76,8 +76,7 @@ func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
func (a *Allocator) refresh(as string) error {
aSpace, err := a.getAddressSpaceFromStore(as)
if err != nil {
return fmt.Errorf("error getting pools config from store during init: %v",
err)
return types.InternalErrorf("error getting pools config from store: %v", err)
}
if aSpace == nil {
@ -239,7 +238,7 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
store := a.getStore(key.AddressSpace)
if store == nil {
return fmt.Errorf("could not find store for address space %s while inserting bit mask", key.AddressSpace)
return types.InternalErrorf("could not find store for address space %s while inserting bit mask", key.AddressSpace)
}
ipVer := getAddressVersion(pool.IP)
@ -279,7 +278,7 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle,
if !ok {
log.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String())
if err := a.insertBitMask(k, n); err != nil {
return nil, fmt.Errorf("could not find bitmask in datastore for %s", k.String())
return nil, types.InternalErrorf("could not find bitmask in datastore for %s", k.String())
}
a.Lock()
bm = a.addresses[k]
@ -306,7 +305,7 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
}
if as != localAddressSpace && as != globalAddressSpace {
return nil, fmt.Errorf("no default pool available for non-default address spaces")
return nil, types.NotImplementedErrorf("no default pool availbale for non-default addresss spaces")
}
aSpace, err := a.getAddrSpace(as)
@ -378,7 +377,7 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s
bm, err := a.retrieveBitmask(k, c.Pool)
if err != nil {
return nil, nil, fmt.Errorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
return nil, nil, types.InternalErrorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
k.String(), prefAddress, poolID, err)
}
ip, err := a.getAddress(p.Pool, bm, prefAddress, p.Range)
@ -410,12 +409,12 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
p, ok := aSpace.subnets[k]
if !ok {
aSpace.Unlock()
return ipamapi.ErrBadPool
return types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID)
}
if address == nil {
aSpace.Unlock()
return ipamapi.ErrInvalidRequest
return types.BadRequestErrorf("invalid address: nil")
}
if !p.Pool.Contains(address) {
@ -434,12 +433,12 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
h, err := types.GetHostPartIP(address, mask)
if err != nil {
return fmt.Errorf("failed to release address %s: %v", address.String(), err)
return types.InternalErrorf("failed to release address %s: %v", address.String(), err)
}
bm, err := a.retrieveBitmask(k, c.Pool)
if err != nil {
return fmt.Errorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
return types.InternalErrorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
k.String(), address, poolID, err)
}
@ -463,19 +462,25 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres
} else if prefAddress != nil {
hostPart, e := types.GetHostPartIP(prefAddress, base.Mask)
if e != nil {
return nil, fmt.Errorf("failed to allocate preferred address %s: %v", prefAddress.String(), e)
return nil, types.InternalErrorf("failed to allocate preferred address %s: %v", prefAddress.String(), e)
}
ordinal = ipToUint64(types.GetMinimalIP(hostPart))
err = bitmask.Set(ordinal)
} else {
ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End)
}
if err != nil {
return nil, ipamapi.ErrNoAvailableIPs
}
// Convert IP ordinal for this subnet into IP address
return generateAddress(ordinal, base), nil
switch err {
case nil:
// Convert IP ordinal for this subnet into IP address
return generateAddress(ordinal, base), nil
case bitseq.ErrBitAllocated:
return nil, ipamapi.ErrIPAlreadyAllocated
case bitseq.ErrNoBitAvailable:
return nil, ipamapi.ErrNoAvailableIPs
default:
return nil, err
}
}
// DumpDatabase dumps the internal info

View file

@ -2,7 +2,6 @@ package ipam
import (
"encoding/json"
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/datastore"
@ -84,7 +83,7 @@ func (a *Allocator) getStore(as string) datastore.DataStore {
func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
store := a.getStore(as)
if store == nil {
return nil, fmt.Errorf("store for address space %s not found", as)
return nil, types.InternalErrorf("store for address space %s not found", as)
}
pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a}
@ -93,7 +92,7 @@ func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
return nil, nil
}
return nil, fmt.Errorf("could not get pools config from store: %v", err)
return nil, types.InternalErrorf("could not get pools config from store: %v", err)
}
return pc, nil
@ -102,7 +101,7 @@ func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
func (a *Allocator) writeToStore(aSpace *addrSpace) error {
store := aSpace.store()
if store == nil {
return fmt.Errorf("invalid store while trying to write %s address space", aSpace.DataScope())
return types.InternalErrorf("invalid store while trying to write %s address space", aSpace.DataScope())
}
err := store.PutObjectAtomic(aSpace)
@ -116,7 +115,7 @@ func (a *Allocator) writeToStore(aSpace *addrSpace) error {
func (a *Allocator) deleteFromStore(aSpace *addrSpace) error {
store := aSpace.store()
if store == nil {
return fmt.Errorf("invalid store while trying to delete %s address space", aSpace.DataScope())
return types.InternalErrorf("invalid store while trying to delete %s address space", aSpace.DataScope())
}
return store.DeleteObjectAtomic(aSpace)

View file

@ -88,12 +88,12 @@ func (s *SubnetKey) String() string {
// FromString populate the SubnetKey object reading it from string
func (s *SubnetKey) FromString(str string) error {
if str == "" || !strings.Contains(str, "/") {
return fmt.Errorf("invalid string form for subnetkey: %s", str)
return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
}
p := strings.Split(str, "/")
if len(p) != 3 && len(p) != 5 {
return fmt.Errorf("invalid string form for subnetkey: %s", str)
return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
}
s.AddressSpace = p[0]
s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
@ -317,7 +317,7 @@ func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) (func() error, error
return func() error {
bm, err := aSpace.alloc.retrieveBitmask(k, c.Pool)
if err != nil {
return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
return types.InternalErrorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
}
return bm.Destroy()
}, nil

View file

@ -2,8 +2,9 @@
package ipamapi
import (
"errors"
"net"
"github.com/docker/libnetwork/types"
)
/********************
@ -29,22 +30,19 @@ type Callback interface {
// Weel-known errors returned by IPAM
var (
ErrInvalidIpamService = errors.New("Invalid IPAM Service")
ErrInvalidIpamConfigService = errors.New("Invalid IPAM Config Service")
ErrIpamNotAvailable = errors.New("IPAM Service not available")
ErrIpamInternalError = errors.New("IPAM Internal Error")
ErrInvalidAddressSpace = errors.New("Invalid Address Space")
ErrInvalidPool = errors.New("Invalid Address Pool")
ErrInvalidSubPool = errors.New("Invalid Address SubPool")
ErrInvalidRequest = errors.New("Invalid Request")
ErrPoolNotFound = errors.New("Address Pool not found")
ErrOverlapPool = errors.New("Address pool overlaps with existing pool on this address space")
ErrNoAvailablePool = errors.New("No available pool")
ErrNoAvailableIPs = errors.New("No available addresses on this pool")
ErrIPAlreadyAllocated = errors.New("Address already in use")
ErrIPOutOfRange = errors.New("Requested address is out of range")
ErrPoolOverlap = errors.New("Pool overlaps with other one on this address space")
ErrBadPool = errors.New("Address space does not contain specified address pool")
ErrIpamInternalError = types.InternalErrorf("IPAM Internal Error")
ErrInvalidAddressSpace = types.BadRequestErrorf("Invalid Address Space")
ErrInvalidPool = types.BadRequestErrorf("Invalid Address Pool")
ErrInvalidSubPool = types.BadRequestErrorf("Invalid Address SubPool")
ErrInvalidRequest = types.BadRequestErrorf("Invalid Request")
ErrPoolNotFound = types.BadRequestErrorf("Address Pool not found")
ErrOverlapPool = types.ForbiddenErrorf("Address pool overlaps with existing pool on this address space")
ErrNoAvailablePool = types.NoServiceErrorf("No available pool")
ErrNoAvailableIPs = types.NoServiceErrorf("No available addresses on this pool")
ErrIPAlreadyAllocated = types.ForbiddenErrorf("Address already in use")
ErrIPOutOfRange = types.BadRequestErrorf("Requested address is out of range")
ErrPoolOverlap = types.ForbiddenErrorf("Pool overlaps with other one on this address space")
ErrBadPool = types.BadRequestErrorf("Address space does not contain specified address pool")
)
/*******************************