daemon: Return all validation errors for NetworkingConfig and EndpointSettings

Thus far, validation code would stop as soon as a bad value was found.
Now, we try to validate as much as we can, to return all errors to the
API client.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
Albin Kerouanton 2023-08-10 01:42:35 +02:00
parent ff503882f7
commit 4bd0553274
No known key found for this signature in database
GPG key ID: 630B8E1DCBDB1864
3 changed files with 21 additions and 9 deletions

View file

@ -18,6 +18,7 @@ import (
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/daemon/network"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/internal/multierror"
"github.com/docker/docker/libnetwork"
"github.com/docker/docker/libnetwork/netlabel"
"github.com/docker/docker/libnetwork/options"
@ -575,30 +576,32 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
ipamConfig = epConfig.IPAMConfig
}
var errs []error
if !containertypes.NetworkMode(nwName).IsUserDefined() {
hasStaticAddresses := ipamConfig.IPv4Address != "" || ipamConfig.IPv6Address != ""
// On Linux, user specified IP address is accepted only by networks with user specified subnets.
if hasStaticAddresses && !enableIPOnPredefinedNetwork() {
return runconfig.ErrUnsupportedNetworkAndIP
errs = append(errs, runconfig.ErrUnsupportedNetworkAndIP)
}
if len(epConfig.Aliases) > 0 && !serviceDiscoveryOnDefaultNetwork() {
return runconfig.ErrUnsupportedNetworkAndAlias
errs = append(errs, runconfig.ErrUnsupportedNetworkAndAlias)
}
}
if ipamConfig.IPv4Address != "" {
if addr := net.ParseIP(ipamConfig.IPv4Address); addr == nil || addr.To4() == nil || addr.IsUnspecified() {
return fmt.Errorf("invalid IPv4 address: %s", ipamConfig.IPv4Address)
errs = append(errs, fmt.Errorf("invalid IPv4 address: %s", ipamConfig.IPv4Address))
}
}
if ipamConfig.IPv6Address != "" {
if addr := net.ParseIP(ipamConfig.IPv6Address); addr == nil || addr.To4() != nil || addr.IsUnspecified() {
return fmt.Errorf("invalid IPv6 address: %s", ipamConfig.IPv6Address)
errs = append(errs, fmt.Errorf("invalid IPv6 address: %s", ipamConfig.IPv6Address))
}
}
if nw == nil {
return nil
return multierror.Join(errs...)
}
_, _, nwIPv4Configs, nwIPv6Configs := nw.IpamConfig()
@ -624,12 +627,12 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
}
}
if !foundSubnet {
return runconfig.ErrUnsupportedNetworkNoSubnetAndIP
errs = append(errs, runconfig.ErrUnsupportedNetworkNoSubnetAndIP)
}
}
}
return nil
return multierror.Join(errs...)
}
// cleanOperationalData resets the operational data from the passed endpoint settings

View file

@ -20,6 +20,7 @@ import (
"github.com/docker/docker/daemon/images"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
"github.com/docker/docker/internal/multierror"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/runconfig"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@ -326,18 +327,24 @@ func (daemon *Daemon) validateNetworkingConfig(nwConfig *networktypes.Networking
return nil
}
var errs []error
for k, v := range nwConfig.EndpointsConfig {
if v == nil {
return fmt.Errorf("no EndpointSettings for %s", k)
errs = append(errs, fmt.Errorf("invalid config for network %s: EndpointsConfig is nil", k))
continue
}
// The referenced network k might not exist when the container is created, so just ignore the error in that case.
nw, _ := daemon.FindNetwork(k)
if err := validateEndpointSettings(nw, k, v); err != nil {
return err
errs = append(errs, fmt.Errorf("invalid config for network %s: %w", k, err))
}
}
if len(errs) > 0 {
return errdefs.InvalidParameter(multierror.Join(errs...))
}
return nil
}

View file

@ -49,6 +49,8 @@ keywords: "API, Docker, rcli, REST, documentation"
`NetworkingConfig.EndpointSettings`.
* `POST /containers/create` and `POST /networks/{id}/connect` will now catch
validation errors that were previously only returned during `POST /containers/{id}/start`.
These endpoints will also return the full set of validation errors they find,
instead of returning only the first one.
Note that this change is _unversioned_ and applies to all API versions.
## v1.43 API changes