From 735a052790565f3dc9fae0318354eda8a9988696 Mon Sep 17 00:00:00 2001 From: mgorripa Date: Thu, 25 Sep 2025 15:05:57 -0400 Subject: [PATCH 1/4] Improve subnet overlap error message for Docker mgmt network Previously, when a requested subnet overlapped with an existing Docker network, containerlab returned Docker's raw error: Error response from daemon: invalid pool request: Pool overlaps ... Now, containerlab detects the overlap and returns a clearer error: Subnet 172.20.0.0/16 already in use by Docker network "testnet". See https://containerlab.dev/manual/network/ This helps users quickly identify the conflicting network. --- runtime/docker/docker.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/runtime/docker/docker.go b/runtime/docker/docker.go index b6e34fe24..65b8aa1a0 100644 --- a/runtime/docker/docker.go +++ b/runtime/docker/docker.go @@ -340,7 +340,27 @@ func (d *DockerRuntime) createMgmtBridge( //nolint: funlen netCreateResponse, err := d.Client.NetworkCreate(nctx, d.mgmt.Network, opts) if err != nil { - return "", err + // Handle subnet overlap error + if strings.Contains(err.Error(), "Pool overlaps") || strings.Contains(err.Error(), "subnet") { + nets, _ := d.Client.NetworkList(nctx, networkapi.ListOptions{}) + for _, n := range nets { + for _, cfg := range n.IPAM.Config { + if cfg.Subnet == d.mgmt.IPv4Subnet { + return "", fmt.Errorf( + "subnet %s already in use by Docker network %q. See https://containerlab.dev/manual/network/", + cfg.Subnet, n.Name, + ) + } + } + } + // fallback: no exact match, but clarify + return "", fmt.Errorf( + "requested subnet %s overlaps an existing Docker network. Original error: %v. See https://containerlab.dev/manual/network/", + d.mgmt.IPv4Subnet, err, + ) + } + + return "", err } if len(netCreateResponse.ID) < 12 { @@ -355,8 +375,7 @@ func (d *DockerRuntime) createMgmtBridge( //nolint: funlen } // getMgmtBridgeIPs gets the management bridge v4/6 addresses. -func getMgmtBridgeIPs( - bridgeName string, +func getMgmtBridgeIPs( bridgeName string, netResource *networkapi.Inspect, ) (v4, v6 string, err error) { if v4, v6, err = clabutils.FirstLinkIPs(bridgeName); err != nil { From 0e9f342d00caf3f6e0e38f077e72ace306e8d596 Mon Sep 17 00:00:00 2001 From: hellt Date: Fri, 26 Sep 2025 09:39:24 +0200 Subject: [PATCH 2/4] check for v4 and v6 match list existing networks --- runtime/docker/docker.go | 50 +++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/runtime/docker/docker.go b/runtime/docker/docker.go index 65b8aa1a0..ead6e2b95 100644 --- a/runtime/docker/docker.go +++ b/runtime/docker/docker.go @@ -341,26 +341,33 @@ func (d *DockerRuntime) createMgmtBridge( //nolint: funlen netCreateResponse, err := d.Client.NetworkCreate(nctx, d.mgmt.Network, opts) if err != nil { // Handle subnet overlap error - if strings.Contains(err.Error(), "Pool overlaps") || strings.Contains(err.Error(), "subnet") { - nets, _ := d.Client.NetworkList(nctx, networkapi.ListOptions{}) - for _, n := range nets { - for _, cfg := range n.IPAM.Config { - if cfg.Subnet == d.mgmt.IPv4Subnet { - return "", fmt.Errorf( - "subnet %s already in use by Docker network %q. See https://containerlab.dev/manual/network/", - cfg.Subnet, n.Name, - ) - } - } - } - // fallback: no exact match, but clarify - return "", fmt.Errorf( - "requested subnet %s overlaps an existing Docker network. Original error: %v. See https://containerlab.dev/manual/network/", - d.mgmt.IPv4Subnet, err, - ) - } - - return "", err + if strings.Contains(strings.ToLower(err.Error()), strings.ToLower("Pool overlaps")) || + strings.Contains(strings.ToLower(err.Error()), strings.ToLower("subnet")) { + networksAndAddresses := map[string][]string{} + nets, _ := d.Client.NetworkList(nctx, networkapi.ListOptions{}) + for _, n := range nets { + for _, cfg := range n.IPAM.Config { + // store existing networks and their subnets + networksAndAddresses[n.Name] = append(networksAndAddresses[n.Name], cfg.Subnet) + if cfg.Subnet == d.mgmt.IPv4Subnet || cfg.Subnet == d.mgmt.IPv6Subnet { + return "", fmt.Errorf( + "subnet %s already in use by Docker network %q. See https://containerlab.dev/manual/network/", + cfg.Subnet, + n.Name, + ) + } + } + } + // fallback: no exact match, but clarify + return "", fmt.Errorf( + "requested subnet %s overlaps an existing Docker network. Existing networks: %v. Original error: %w. See https://containerlab.dev/manual/network/", + d.mgmt.IPv4Subnet, + networksAndAddresses, + err, + ) + } + + return "", err } if len(netCreateResponse.ID) < 12 { @@ -375,7 +382,8 @@ func (d *DockerRuntime) createMgmtBridge( //nolint: funlen } // getMgmtBridgeIPs gets the management bridge v4/6 addresses. -func getMgmtBridgeIPs( bridgeName string, +func getMgmtBridgeIPs( + bridgeName string, netResource *networkapi.Inspect, ) (v4, v6 string, err error) { if v4, v6, err = clabutils.FirstLinkIPs(bridgeName); err != nil { From bb13e92270c4093d09dc4acaae9c78b4515c4b48 Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Fri, 26 Sep 2025 09:41:58 +0200 Subject: [PATCH 3/4] Update runtime/docker/docker.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- runtime/docker/docker.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/docker/docker.go b/runtime/docker/docker.go index ead6e2b95..5d78d3a4c 100644 --- a/runtime/docker/docker.go +++ b/runtime/docker/docker.go @@ -344,7 +344,14 @@ func (d *DockerRuntime) createMgmtBridge( //nolint: funlen if strings.Contains(strings.ToLower(err.Error()), strings.ToLower("Pool overlaps")) || strings.Contains(strings.ToLower(err.Error()), strings.ToLower("subnet")) { networksAndAddresses := map[string][]string{} - nets, _ := d.Client.NetworkList(nctx, networkapi.ListOptions{}) + nets, listErr := d.Client.NetworkList(nctx, networkapi.ListOptions{}) + if listErr != nil { + return "", fmt.Errorf( + "failed to list Docker networks while handling subnet overlap error: %w (original error: %v)", + listErr, + err, + ) + } for _, n := range nets { for _, cfg := range n.IPAM.Config { // store existing networks and their subnets From 23d547d7f0dda346f15c4730c3373c0670179bc3 Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Fri, 26 Sep 2025 09:43:01 +0200 Subject: [PATCH 4/4] Update runtime/docker/docker.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- runtime/docker/docker.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/runtime/docker/docker.go b/runtime/docker/docker.go index 5d78d3a4c..3f97ec01a 100644 --- a/runtime/docker/docker.go +++ b/runtime/docker/docker.go @@ -366,9 +366,17 @@ func (d *DockerRuntime) createMgmtBridge( //nolint: funlen } } // fallback: no exact match, but clarify + // Show both IPv4 and IPv6 subnets in the error message if present + requestedSubnets := d.mgmt.IPv4Subnet + if d.mgmt.IPv6Subnet != "" { + if requestedSubnets != "" { + requestedSubnets += ", " + } + requestedSubnets += d.mgmt.IPv6Subnet + } return "", fmt.Errorf( - "requested subnet %s overlaps an existing Docker network. Existing networks: %v. Original error: %w. See https://containerlab.dev/manual/network/", - d.mgmt.IPv4Subnet, + "requested subnet(s) %s overlap an existing Docker network. Existing networks: %v. Original error: %w. See https://containerlab.dev/manual/network/", + requestedSubnets, networksAndAddresses, err, )