From 4bd055327480aa8cb7a4a5ab7f4c1ff1b97ffa2e Mon Sep 17 00:00:00 2001 From: Albin Kerouanton Date: Thu, 10 Aug 2023 01:42:35 +0200 Subject: [PATCH] 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 --- daemon/container_operations.go | 17 ++++++++++------- daemon/create.go | 11 +++++++++-- docs/api/version-history.md | 2 ++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/daemon/container_operations.go b/daemon/container_operations.go index 7e480a411b..42900d1155 100644 --- a/daemon/container_operations.go +++ b/daemon/container_operations.go @@ -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 diff --git a/daemon/create.go b/daemon/create.go index f593f59ddc..682b2b2a65 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -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 } diff --git a/docs/api/version-history.md b/docs/api/version-history.md index 44f0384187..904c212256 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -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