-
Notifications
You must be signed in to change notification settings - Fork 18.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
daemon: Improve NetworkingConfig & EndpointSettings validation #46183
Conversation
ee0eae0
to
b227bc4
Compare
) | ||
|
||
// Test case for 35752 | ||
func TestVerifyNetworkingConfig(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer not to delete this test, and I'd even prefer to expand it to test interesting cases. But given that verifyNetworkingConfig
is now a method of daemon
, it's quite hard. I'd have to instantiate a daemon with a working libnetwork controller.
To make it easier to test, I thought about keeping verifyNetworkingConfig
as a function and add a new networkFinder func(string) *libnetwork.Network
parameter to it. It'd take daemon.FindNetwork
in the daemon code, and a mock in this test. However, I'm not sure if that's a common / acceptable pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some quick comments from having a first glance.
daemon/container_operations.go
Outdated
if !hasUserDefinedIPAddress(epConfig.IPAMConfig) { | ||
return errors.Join(errs...) | ||
} | ||
if epConfig.IPAMConfig.IPv4Address != "" && net.ParseIP(epConfig.IPAMConfig.IPv4Address).To4() == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a bunch of these are also done in buildCreateEndpointOptions
. Wondering if we can extract some of these checks, and make both use the same validation 🤔
Lines 808 to 838 in 220fba0
if epConfig != nil { | |
if ipam := epConfig.IPAMConfig; ipam != nil { | |
var ipList []net.IP | |
for _, ips := range ipam.LinkLocalIPs { | |
linkIP := net.ParseIP(ips) | |
if linkIP == nil && ips != "" { | |
return nil, fmt.Errorf("invalid link-local IP address: %s", ipam.LinkLocalIPs) | |
} | |
ipList = append(ipList, linkIP) | |
} | |
ip := net.ParseIP(ipam.IPv4Address) | |
if ip == nil && ipam.IPv4Address != "" { | |
return nil, fmt.Errorf("invalid IPv4 address: %s", ipam.IPv4Address) | |
} | |
ip6 := net.ParseIP(ipam.IPv6Address) | |
if ip6 == nil && ipam.IPv6Address != "" { | |
return nil, fmt.Errorf("invalid IPv6 address: %s", ipam.IPv6Address) | |
} | |
createOptions = append(createOptions, libnetwork.CreateOptionIpam(ip, ip6, ipList, nil)) | |
} | |
for _, alias := range epConfig.Aliases { | |
createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias)) | |
} | |
for k, v := range epConfig.DriverOpts { | |
createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(options.Generic{k: v})) | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not sure what to do with these errors. For me, there're more like defensive checks than data validation. And there're needed because we actually need "strong-type".
From that perspective we could potentially do a quick check on well-formed-ness of options (we should really look if we can do such parsing/conversions earlier, and get to a "strong-type" ASAP in the process, as we're sometimes parsing the same string multiple times), and only if we get that far, proceed with the heavier lifting.
IMHO the best way to solve that would be to create an intermediate representation that's stored in-memory (and on-disk?) and not exposed on the API. Then, the validation step could be made responsible of instantiating these "stronger types". But that's a non-trivial chunk of work, and this PR is required by docker/cli#4493 (which in turn is required by docker/cli#4419, itself required by the other PRs adding proper support for multi --network parameters).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As shared privately, for now we can't really drop the validation steps in buildCreateEndpointOptions
. We first need to make some refactorings to make sure user-supplied data are correctly sanitized and transformed into useful structs instead of keeping literals everywhere.
This will be addressed in a follow-up PR.
daemon/container_operations.go
Outdated
} | ||
} | ||
|
||
if nw == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This validation is called from 2 places;
In Daemon.updateNetworkConfig()
;
if err := validateEndpointSettings(n, n.Name(), endpointConfig); err != nil {
return err
}
In Daemon.validateNetworkingConfig()
;
// The referenced network k might not exist when the container is created, so don't fail if that's the case.
// If any other error happen, it's not a validation error, so better early exit.
nw, err := daemon.FindNetwork(k)
if err != nil && !errdefs.IsNotFound(err) {
return err
}
if err := validateEndpointSettings(nw, k, v); err != nil {
errs = append(errs, fmt.Errorf("invalid endpoint config for network %s: %w", k, err))
}
- in both cases, I think we'd already barf out if the network is not to be found
- and, I think in both cases
nwName
is effectively equivalent tonw.Name()
(although I'm not 100% sure for the second case; can it use network ID instead of name? does that matter for this case?)
So I'm wondering if we should continue to bail out early if there's no network (instead of continuing with other checks), as we probably would never reach this validation
Also slightly looking if we should split "heavy" and "lightweight" validation; e.g. validating for invalid / invalidly formatted IP-addresses is something that's relatively lightweight, whereas fetching the network (knowing libnetwork ...) may "not be". From that perspective we could potentially do a quick check on well-formed-ness of options (we should really look if we can do such parsing/conversions earlier, and get to a "strong-type" ASAP in the process, as we're sometimes parsing the same string multiple times), and only if we get that far, proceed with the heavier lifting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in both cases, I think we'd already barf out if the network is not to be found
Nope, we actually accept a ContainerCreate
request with a reference to a non-existant network:
moby/daemon/container_operations.go
Lines 577 to 580 in b227bc4
// validateEndpointSettings checks whether the given epConfig is valid. The nw parameter might be nil as a container | |
// can be created with a reference to a network that don't exist yet. In that case, only partial validation will be | |
// done. | |
func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *networktypes.EndpointSettings) error { |
It's not a hypothetical case, it's how things are working right now 😉
$ docker network inspect foobar
[]
Error response from daemon: network foobar not found
$ docker container create --network foobar busybox
6697a506ee56c76d770218b49794255b8059a0f6deec836a0c26778379612b6a
we should really look if we can do such parsing/conversions earlier, and get to a "strong-type" ASAP in the process
Yeah, I looked into this a bit already. I'd like to have netip.*
instead of strings, as it'd remove the need of constantly parsing the same thing. But that imply quite a bunch of work. I didn't had time to create the CLI PR that depends on this one, but it all have to do with the ability to provide multiple --network
flags, hence I'd prefer to keep this one simple.
EDIT: For the record, here's the CLI PR that requires this one: docker/cli#4493
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, we actually accept a ContainerCreate request with a reference to a non-existant network:
For context here (I stumbled upon that recently); I think this is the commit that changed that behavior; 99a98cc (part of #25962). In that commit, it's no longer checked if the network exists, with the assumption that it may be a dynamically rolled-out (but "attachable") network, and will be created "lazily"".
(also related: #26449, #27466)
Honestly, looking at some of that, I'm not sure I'm a "fan". Perhaps we should indeed;
- Always have the network "available" on every node (but it could be a "definition-only" for dynamically rolled-out networks)
- Or, for
attachable
networks, and a container attaching to it, we could still consider to initialize it when attaching
But this definitely requires some digging into the internals / underlying mechanisms to see if that's possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the digging!
- Always have the network "available" on every node (but it could be a "definition-only" for dynamically rolled-out networks)
- Or, for attachable networks, and a container attaching to it, we could still consider to initialize it when attaching
Once this PR is merged, I'll submit a follow-up refactor PR to move attachment requests out of findAndAttachNetwork
to a dedicated method called before doing anything else in initializeNetworking
. With that change:
- On
ContainerCreate
, only soundness checks will be done ; - On
ContainerStart
(andNetworkConnect
), we'll determine whether referenced network exists locally, attach it if it's swarm scoped, or bail out because if it doesn't. And then, we can normalize and validate the settings thoroughly ;
I started doing some work in that direction here: fba50d6.
I think it'd be "conceptually" better to go back to the original semantic: you can only attach to a network that exists locally; but it'd be more work to implement and I guess we'd probably need to gate that behind an API version check, so anyway... I think we can get the same result by shuffling a bit the order of operations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be "conceptually" better to go back to the original semantic: you can only attach to a network that exists locally; but it'd be more work to implement and I guess we'd probably need to gate that behind an API version check, so anyway... I think we can get the same result by shuffling a bit the order of operations.
Yes, we need to look at that! I guess overall the tricky bit is that we must assume that there's potentially many nodes and many networks, and not all of those networks to be used on each node.
- For "attachable" networks, it would be good to have a ("stub?") network on each node, so that
docker network attache
can be used for containers; we could probably still make it a stub (so that it doesn't take up resources on all notes in the swarm), and make it an actual network when attaching. - For "non-attachable" networks, I guess it's TBD; do we want to show stubs for those? (if it's a worker node, the worker itself wouldn't have much purpose for it until a swarm task lands on the node).
- For managers it would be more useful (as you could run
docker network rm <some swarm-scope network>
)
Either way; all of that is for a follow-up discussion. We could create a ticket for it though (perhaps copy/paste some of the above)
daemon/container_operations.go
Outdated
|
||
var errs []error | ||
if !containertypes.NetworkMode(nwName).IsUserDefined() { | ||
// On Linux, user specified IP address is accepted only for networks with user specified subnets. | ||
if hasUserDefinedIPAddress(epConfig.IPAMConfig) && !enableIPOnPredefinedNetwork() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an observation here;
hasUserDefinedIPAddress()
will always befalse
if there's no ipamConfig, which may already invalidate other checks.- ^^ slightly leaning toward inlining that function here, and just make it a
hasUserDefinedIPAddress
variable that we set (and use), as that function is only used inside this check, and we're now potentially calling it twice with the same results - BOTH
enableIPOnPredefinedNetwork
andserviceDiscoveryOnDefaultNetwork
are effectivelyif runtime.GOOS == "windows"
) - ^^ wondering if we need to split the validation there, instead of having two utility functions that effectively means "different validation on windows vs linux" (not sure)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, most of these would be better handled in their own PR. Also, the next step once we get proper support for multi --network
flags, is to make the default bridge just a "normal" bridge (ie. no more links, no more artificial restriction, etc...). So better keep these refactorings for that time (unless you want to go ahead and do this beforehand).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- BOTH
enableIPOnPredefinedNetwork
andserviceDiscoveryOnDefaultNetwork
are effectively if runtime.GOOS == "windows")- ^^ wondering if we need to split the validation there, instead of having two utility functions that effectively means "different validation on windows vs linux" (not sure)
I added a TODO
to remind us we should remove these functions once we remove legacy networking mode from the default bridge. As such, I think we shouldn't make the validation OS-dependent here, as it's going to be addressed (hopefully) soon.
Following flags are silently ignored when they're passed with no `--network` specified (ie. when the default network is used): - `--network-alias` - `--ip` - `--ip6` - `--link-local-ip` This is not really an issue right now since the first 3 parameters are not allowed on the default bridge network. However, with [moby/moby#45905][1], the container-wide MacAddress parameter will be deprecated and dismissed. Because of that, with [docker#4419][2], it's currently not possible to use the `--mac-address` flag with no default network specified. Morever, `docker network connect --link-local-ip ...` works properly, so it should also work on `docker container create`. This also lay the ground for making the default bridge network just a "normal" network. Since the 3 parameters in the list above aren't ignored anymore, if users provide them, moby's ContainerStart endpoint will complain about those. To provide better UX, [moby/moby#46183][3] make sure these invalid parameters lead to a proper error message on `docker container create` / `docker run`. [1]: moby/moby#45905 [2]: docker#4419 [3]: moby/moby#46183 Signed-off-by: Albin Kerouanton <albinker@gmail.com>
b227bc4
to
08c7515
Compare
08c7515
to
2daa7e4
Compare
daemon/create.go
Outdated
|
||
// The referenced network k might not exist when the container is created, so don't fail if that's the case. | ||
nw, err := daemon.FindNetwork(k) | ||
if err != nil && !errdefs.IsNotFound(err) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test TestCreateWithDuplicateNetworkNames
from the service
suite is failing because this condition will return a validation error if two networks with conflicting names are created.
Instead of working around this, I drop TestCreateWithDuplicateNetworkNames
in #46251.
47d1652
to
cbe536e
Compare
Following flags are silently ignored when they're passed with no `--network` specified (ie. when the default network is used): - `--network-alias` - `--ip` - `--ip6` - `--link-local-ip` This is not really an issue right now since the first 3 parameters are not allowed on the default bridge network. However, with [moby/moby#45905][1], the container-wide MacAddress parameter will be deprecated and dismissed. Because of that, with [docker#4419][2], it's currently not possible to use the `--mac-address` flag with no default network specified. Morever, `docker network connect --link-local-ip ...` works properly, so it should also work on `docker container create`. This also lay the ground for making the default bridge network just a "normal" network. Since the 3 parameters in the list above aren't ignored anymore, if users provide them, moby's ContainerStart endpoint will complain about those. To provide better UX, [moby/moby#46183][3] make sure these invalid parameters lead to a proper error message on `docker container create` / `docker run`. [1]: moby/moby#45905 [2]: docker#4419 [3]: moby/moby#46183 Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Following flags are silently ignored when they're passed with no `--network` specified (ie. when the default network is used): - `--network-alias` - `--ip` - `--ip6` - `--link-local-ip` This is not really an issue right now since the first 3 parameters are not allowed on the default bridge network. However, with [moby/moby#45905][1], the container-wide MacAddress parameter will be deprecated and dismissed. Because of that, with [docker#4419][2], it's currently not possible to use the `--mac-address` flag with no default network specified. Morever, `docker network connect --link-local-ip ...` works properly, so it should also work on `docker container create`. This also lay the ground for making the default bridge network just a "normal" network. Since the 3 parameters in the list above aren't ignored anymore, if users provide them, moby's ContainerStart endpoint will complain about those. To provide better UX, [moby/moby#46183][3] make sure these invalid parameters lead to a proper error message on `docker container create` / `docker run`. [1]: moby/moby#45905 [2]: docker#4419 [3]: moby/moby#46183 Signed-off-by: Albin Kerouanton <albinker@gmail.com>
270d738
to
a285549
Compare
08ed707
to
d8173af
Compare
|
||
// TODO(aker): add a proper multierror.Append | ||
if err := ipamConfig.Validate(); err != nil { | ||
errs = append(errs, err.(interface{ Unwrap() []error }).Unwrap()...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this, we need a proper multierror.Append
instead. But I think it's ok to address that in a follow-up PR. @thaJeztah Any thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, not against having a utility to add it.
I guess in that case we also may be able to skip the multierrors.Join()
? (i.e., an nil
vs empty slice), and perhaps just return errs
in that case?
I'm fine with improving in a follow-up though.
4f3bf30
to
2fc216b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall looks good; there were some changes that landed in the wrong commit, so left comments about those, and I left a comment about a commit (renaming cfg => ipamConfig
) that I think would be ok to squash with the commit after it)
nw, _ := daemon.FindNetwork(k) | ||
if err := validateEndpointSettings(nw, k, v); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're passing both k
, and v
as arguments, and nw
may not be needed if we fail early inside the function.
Would it make sense to
- remove the
nw, _ := daemon.FindNetwork(k)
here - make
validateEndpointSettings
a method ondaemon
(func (daemon *daemon) validateEndpointSettings( nwName string, epConfig *networktypes.EndpointSettings)
- perform looking up the network inside the function (if we reach that point)
However, it may still be an indicator that this function is trying to do too much, and maybe we should split parts of it (i.e. being able to validate the endpoint settings themselves, and settings that depend on the network's setting)
FWIW, not necessarily a blocker, but mostly considering "what's the overhead of getting the network"? (could this be trying plugins etc?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, it may still be an indicator that this function is trying to do too much
Mh, I have fixed feelings about whether it does too much things or not. But once the refactor PR I mentioned previously is merged, attachSwarmNetworks
will happen first and we'll be able to take for granted that all networks exist when we start validation. This should make the code easier to follow, and help us draw a line between the "soundness checks" I mentioned and in-depth validation.
As some of this is already addressed in daemon: move most of validateEndpointSettings into api/t/net
, I'll keep the code as-is in this commit.
daemon/container_operations.go
Outdated
} | ||
} | ||
|
||
if nw == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, we actually accept a ContainerCreate request with a reference to a non-existant network:
For context here (I stumbled upon that recently); I think this is the commit that changed that behavior; 99a98cc (part of #25962). In that commit, it's no longer checked if the network exists, with the assumption that it may be a dynamically rolled-out (but "attachable") network, and will be created "lazily"".
(also related: #26449, #27466)
Honestly, looking at some of that, I'm not sure I'm a "fan". Perhaps we should indeed;
- Always have the network "available" on every node (but it could be a "definition-only" for dynamically rolled-out networks)
- Or, for
attachable
networks, and a container attaching to it, we could still consider to initialize it when attaching
But this definitely requires some digging into the internals / underlying mechanisms to see if that's possible.
api/types/network/endpoint.go
Outdated
func (ipamConfig *EndpointIPAMConfig) Copy() *EndpointIPAMConfig { | ||
cfgCopy := *ipamConfig | ||
cfgCopy.LinkLocalIPs = make([]string, 0, len(ipamConfig.LinkLocalIPs)) | ||
cfgCopy.LinkLocalIPs = append(cfgCopy.LinkLocalIPs, ipamConfig.LinkLocalIPs...) | ||
return &cfgCopy | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit on the fence on this one; generally, "the closer" a variable is define to where it's used, the shorter the name should be. For receivers, it's very common to use a single letter (as all code generally uses it very nearby)
If this was to prevent a conflict, perhaps just c
works (although I don't think the original cfg
was very ambiguous).
never mind, I see the follow-up commit, and there's some functions that are pretty long, so I guess it somewhat makes sense.
(I'd be OK with combining those in a single commit though, so that it's clear why you're renaming!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went the other way around and dropped this commit as I think it's okay to stick to cfg
.
|
||
// TODO(aker): add a proper multierror.Append | ||
if err := ipamConfig.Validate(); err != nil { | ||
errs = append(errs, err.(interface{ Unwrap() []error }).Unwrap()...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, not against having a utility to add it.
I guess in that case we also may be able to skip the multierrors.Join()
? (i.e., an nil
vs empty slice), and perhaps just return errs
in that case?
I'm fine with improving in a follow-up though.
api/types/network/endpoint.go
Outdated
if net.ParseIP(addr) == nil { | ||
errs = append(errs, fmt.Errorf("invalid link-local IP address %s", addr)) | ||
errs = append(errs, fmt.Errorf("invalid link-local IP address: %s", addr)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to suggest: could we use the underlying netip.Parse
instead, so that we can return an actual error with details "what is invalid"??
... only to realise that's new in Go 1.21 https://github.com/golang/go/blob/2c1e5b05fe39fc5e6c730dd60e82946b8e67c6ba/src/net/ip.go#L489-L502
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
... only to realise that's new in Go 1.21
netip.ParseAddr
is actually available since go 1.18, so we can perfectly use it. But looking at the git blame, it looks like net
parsers will use netip
ones starting with go 1.21.
As a consequence, we might start accepting IPv6 addresses with scope zone (eg. fe80::1cc0:3e8c:119f:c2e1%ens18
), whereas it's invalid. I need to double-check but we probably need a proper error for that.
EDIT: re-reading the diff, net.ParseIP
will continue to return a nil if the IPv6 address includes a zone, so no need to worry about that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, that's what errors netip.ParseAddr(...)
produces: ParseAddr("foo"): unable to parse IP
. So, it'd look like this if we use it here:
* invalid IPv4 address foo: ParseAddr("foo"): unable to parse IP
* invalid IPv6 address bar: ParseAddr("bar"): unable to parse IP
* invalid link-local IP address baz: ParseAddr("baz"): unable to parse IP
* invalid link-local IP address foobar: ParseAddr("foobar"): unable to parse IP
I think we should rather stick with net.ParseIP
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, that doesn't provide much more context indeed. I was hoping it would give the user more context what's wrong. Thanks for looking!
@@ -173,7 +173,7 @@ type Network struct { | |||
created time.Time | |||
scope string // network data scope | |||
labels map[string]string | |||
ipamType string | |||
ipamType string // ipamType is the name of the IPAM driver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should rename this to ipamDriver
at some point 😂
libnetwork/network.go
Outdated
// IpamConf contains all the ipam related configurations for a network | ||
// TODO(aker): use proper net/* structs instead of string literals. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're updating, can you add an empty line in between? ISTR GoDoc can hide "TODO" comments in the generated documentation as long as there's an empty comment-line in between, so;
// IpamConf contains all the ipam related configurations for a network.
//
// TODO(aker): use proper net/* structs instead of string literals.
(LOL, also this struct should really be named IPAMConf
(to be properly camelcase, but that's a different topic)
So far, only a subset of NetworkingConfig was validated when calling ContainerCreate. Other parameters would be validated when the container was started. And the same goes for EndpointSettings on NetworkConnect. This commit adds two validation steps: 1. Check if the IP addresses set in endpoint's IPAMConfig are valid, when ContainerCreate and ConnectToNetwork is called ; 2. Check if the network allows static IP addresses, only on ConnectToNetwork as we need the libnetwork's Network for that and it might not exist until NetworkAttachment requests are sent to the Swarm leader (which happens only when starting the container) ; Signed-off-by: Albin Kerouanton <albinker@gmail.com>
…tSettings 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>
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This issue wasn't caught on ContainerCreate or NetworkConnect (when container wasn't started yet). Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2fc216b
to
87d611f
Compare
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
87d611f
to
e19e541
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Depends on:
- What I did
So far, only a subset of
NetworkingConfig
was validated when callingContainerCreate
. Other parameters would be validated when the container was started. And the same goes forEndpointSettings
onNetworkConnect
.Also, the validation code was early exiting as soon as it caught an error. It now tries to validate as much as it can, and returns all the errors.
- How to verify it
Testing with the CLI won't work because it has its own client-side layer of validation (although incomplete). Some Go code has to be written to test all validation cases.
- Description for the changelog
NetworkConnect
andContainerCreate
have improved data validation, and now return all validation errors at once.