Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 76 additions & 16 deletions environment/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package environment

import (
"context"
"strings"
"strconv"
"sync"

Expand Down Expand Up @@ -48,6 +49,12 @@ func ConfigureDocker(ctx context.Context) error {
if err := createDockerNetwork(ctx, cli); err != nil {
return err
}

// Re-inspect the network after creation to get the actual configuration
resource, err = cli.NetworkInspect(ctx, nw.Name, network.InspectOptions{})
if err != nil {
return errors.Wrap(err, "environment/docker: failed to inspect newly created network")
}
}

config.Update(func(c *config.Configuration) {
Expand All @@ -64,26 +71,61 @@ func ConfigureDocker(ctx context.Context) error {
default:
c.Docker.Network.ISPN = false
}

// Update the interface configuration with the actual assigned values from Docker
// Skip IPAM processing for special drivers that don't have normal IPAM configs
if c.Docker.Network.Driver != "host" && c.Docker.Network.Driver != "overlay" && c.Docker.Network.Driver != "weavemesh" {
for _, ipamCfg := range resource.IPAM.Config {
if ipamCfg.Subnet == "" {
continue
}
// IPv6 subnets contain colons
if strings.Contains(ipamCfg.Subnet, ":") {
c.Docker.Network.Interfaces.V6.Subnet = ipamCfg.Subnet
if ipamCfg.Gateway != "" {
c.Docker.Network.Interfaces.V6.Gateway = ipamCfg.Gateway
}
} else {
c.Docker.Network.Interfaces.V4.Subnet = ipamCfg.Subnet
if ipamCfg.Gateway != "" {
c.Docker.Network.Interfaces.V4.Gateway = ipamCfg.Gateway
c.Docker.Network.Interface = ipamCfg.Gateway
}
}
}
}
})
return nil
}

// Creates a new network on the machine if one does not exist already.
// If the configured subnet conflicts with existing networks, it will automatically
// retry with Docker auto-assigning the subnet to avoid "Pool overlaps" errors.
func createDockerNetwork(ctx context.Context, cli *client.Client) error {
nw := config.Get().Docker.Network
enableIPv6 := nw.IPv6 // get the value from the config file, todo add some logic to the IPAM interaface for that.
_, err := cli.NetworkCreate(ctx, nw.Name, network.CreateOptions{
enableIPv6 := nw.IPv6

// Build IPAM config with the configured subnets
ipamConfigs := []network.IPAMConfig{}
if nw.Interfaces.V4.Subnet != "" {
ipamConfigs = append(ipamConfigs, network.IPAMConfig{
Subnet: nw.Interfaces.V4.Subnet,
Gateway: nw.Interfaces.V4.Gateway,
})
}
if enableIPv6 && nw.Interfaces.V6.Subnet != "" {
ipamConfigs = append(ipamConfigs, network.IPAMConfig{
Subnet: nw.Interfaces.V6.Subnet,
Gateway: nw.Interfaces.V6.Gateway,
})
}

createOpts := network.CreateOptions{
Driver: nw.Driver,
EnableIPv6: &enableIPv6,
Internal: nw.IsInternal,
IPAM: &network.IPAM{
Config: []network.IPAMConfig{{
Subnet: nw.Interfaces.V4.Subnet,
Gateway: nw.Interfaces.V4.Gateway,
}, {
Subnet: nw.Interfaces.V6.Subnet,
Gateway: nw.Interfaces.V6.Gateway,
}},
Config: ipamConfigs,
},
Options: map[string]string{
"encryption": "false",
Expand All @@ -94,14 +136,32 @@ func createDockerNetwork(ctx context.Context, cli *client.Client) error {
"com.docker.network.bridge.name": "pelican0",
"com.docker.network.driver.mtu": strconv.FormatInt(nw.NetworkMTU, 10),
},
})
if err != nil {
return err
}
if nw.Driver != "host" && nw.Driver != "overlay" && nw.Driver != "weavemesh" {
config.Update(func(c *config.Configuration) {
c.Docker.Network.Interface = c.Docker.Network.Interfaces.V4.Gateway
})

// Try to create the network with the configured subnet
_, err := cli.NetworkCreate(ctx, nw.Name, createOpts)
if err != nil {
// Check if the error is a pool overlap issue
errStr := err.Error()
if strings.Contains(errStr, "Pool overlaps") || strings.Contains(errStr, "invalid pool request") {
log.Warn("configured subnet conflicts with existing network, letting Docker auto-assign subnet...")

// Retry without specifying IPAM config - let Docker auto-assign
createOpts.IPAM = &network.IPAM{
Driver: "default",
// Don't specify Config - let Docker choose available subnets
}

_, err = cli.NetworkCreate(ctx, nw.Name, createOpts)
if err != nil {
return errors.Wrap(err, "environment/docker: failed to create network even with auto-assigned subnet")
}

log.Info("network created successfully with Docker auto-assigned subnet")
} else {
return errors.Wrap(err, "environment/docker: failed to create network")
}
}

return nil
}
Loading