mirror of
https://github.com/moby/moby.git
synced 2026-08-03 22:51:03 +00:00
85 lines
2.6 KiB
Go
85 lines
2.6 KiB
Go
//go:build windows
|
|
|
|
package windows
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/containerd/log"
|
|
"github.com/moby/moby/v2/daemon/libnetwork/portallocator"
|
|
"github.com/moby/moby/v2/daemon/libnetwork/types"
|
|
)
|
|
|
|
const maxAllocatePortAttempts = 10
|
|
|
|
// AllocatePorts allocates ports specified in bindings from the port allocator.
|
|
func AllocatePorts(pa *portallocator.OSAllocator, bindings []types.PortBinding) ([]types.PortBinding, error) {
|
|
bs := make([]types.PortBinding, 0, len(bindings))
|
|
for _, c := range bindings {
|
|
b, err := allocatePort(pa, c)
|
|
if err != nil {
|
|
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
|
|
if cuErr := ReleasePorts(pa, bs); cuErr != nil {
|
|
log.G(context.TODO()).Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
|
|
}
|
|
return nil, err
|
|
}
|
|
bs = append(bs, b)
|
|
}
|
|
return bs, nil
|
|
}
|
|
|
|
func allocatePort(pa *portallocator.OSAllocator, bnd types.PortBinding) (types.PortBinding, error) {
|
|
// Windows does not support a host ip for port bindings (this is validated in ConvertPortBindings()).
|
|
// If the HostIP is nil, force it to be 0.0.0.0 for use as the key in the port allocator.
|
|
if bnd.HostIP == nil {
|
|
bnd.HostIP = net.IPv4zero
|
|
}
|
|
|
|
// Adjust HostPortEnd if this is not a range.
|
|
if bnd.HostPortEnd == 0 {
|
|
bnd.HostPortEnd = bnd.HostPort
|
|
}
|
|
|
|
// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
|
|
var allocatedPort int
|
|
var err error
|
|
for i := range maxAllocatePortAttempts {
|
|
allocatedPort, err = pa.AllocateHostPort(bnd.HostIP, bnd.Proto, int(bnd.HostPort), int(bnd.HostPortEnd))
|
|
if err == nil {
|
|
break
|
|
}
|
|
// There is no point in immediately retrying to map an explicitly chosen port.
|
|
if bnd.HostPort != 0 {
|
|
log.G(context.TODO()).Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
|
|
break
|
|
}
|
|
log.G(context.TODO()).Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
|
|
}
|
|
if err != nil {
|
|
return types.PortBinding{}, err
|
|
}
|
|
|
|
bnd.HostPort = uint16(allocatedPort)
|
|
bnd.HostPortEnd = uint16(allocatedPort)
|
|
|
|
return bnd, nil
|
|
}
|
|
|
|
// ReleasePorts releases ports specified in bindings from the portAlloc
|
|
func ReleasePorts(pa *portallocator.OSAllocator, bindings []types.PortBinding) error {
|
|
var errs []error
|
|
|
|
// Attempt to release all port bindings, do not stop on failure
|
|
for _, m := range bindings {
|
|
if err := pa.Deallocate(m.HostIP, m.Proto, int(m.HostPort)); err != nil {
|
|
errs = append(errs, fmt.Errorf("could not release %v because of %v", m, err))
|
|
}
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
}
|