feat(network-go): add fuzzy container name resolution for firewall connections
continuous-integration/drone/push Build is passing

Implement FindContainerName method on DockerAPI that attempts exact match
first, then falls back to prefix-based matching (e.g., extracting prefix
before dash like "service-" in "service-abc") to replicate the old shell
script's `grep $D"-"` behavior. Update firewall orchestrator to use this
resolution before connecting containers to networks, improving robustness
when container names vary from configured selectors.
This commit is contained in:
gyurix
2026-06-15 14:58:29 +02:00
parent c6ae1748cf
commit aac9b83576
3 changed files with 78 additions and 5 deletions
+53
View File
@@ -5,9 +5,11 @@ import (
"fmt" "fmt"
"net" "net"
"os/exec" "os/exec"
"strings"
"time" "time"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/client" "github.com/docker/docker/client"
@@ -26,6 +28,7 @@ type DockerAPI interface {
WaitForContainerRunning(ctx context.Context, containerName string, timeout time.Duration) error WaitForContainerRunning(ctx context.Context, containerName string, timeout time.Duration) error
GetContainerPID(ctx context.Context, containerName string) (int, error) GetContainerPID(ctx context.Context, containerName string) (int, error)
AddRouteInContainer(ctx context.Context, containerName, network, gateway string) error AddRouteInContainer(ctx context.Context, containerName, network, gateway string) error
FindContainerName(ctx context.Context, name, selector string) (string, error)
} }
// Client wraps the Docker SDK client // Client wraps the Docker SDK client
@@ -197,4 +200,54 @@ func (c *Client) AddRouteInContainer(ctx context.Context, containerName, network
return fmt.Errorf("failed to add route in container %s: %w\noutput: %s", containerName, err, string(output)) return fmt.Errorf("failed to add route in container %s: %w\noutput: %s", containerName, err, string(output))
} }
return nil return nil
}
// FindContainerName attempts to find a running container by name or selector.
// First tries the exact name, then tries listing running containers whose name
// starts with the selector prefix (or the name prefix), matching the old shell
// script's grep $D"-" behavior.
func (c *Client) FindContainerName(ctx context.Context, name, selector string) (string, error) {
// First try the exact name
cont, err := c.cli.ContainerInspect(ctx, name)
if err == nil && cont.State != nil && cont.State.Running {
return name, nil
}
// Try exact selector
if selector != "" && selector != name {
cont, err := c.cli.ContainerInspect(ctx, selector)
if err == nil && cont.State != nil && cont.State.Running {
return selector, nil
}
}
// Try prefix matching with selector (old shell script behavior: grep $D"-")
candidates := []string{name, selector}
for _, candidate := range candidates {
if candidate == "" {
continue
}
// Extract prefix before first dash if present
prefix := candidate
if strings.Contains(candidate, "-") {
prefix = candidate[:strings.Index(candidate, "-")]
}
containers, err := c.cli.ContainerList(ctx, container.ListOptions{})
if err != nil {
continue
}
for _, container := range containers {
// Remove leading / from container names
for _, cName := range container.Names {
cName = strings.TrimPrefix(cName, "/")
if strings.HasPrefix(cName, prefix+"-") && container.State == "running" {
return cName, nil
}
}
}
}
return "", fmt.Errorf("no running container found matching name=%q selector=%q", name, selector)
} }
+13 -5
View File
@@ -76,16 +76,24 @@ func (o *Orchestrator) reconcileIPs(ctx context.Context, cfg *config.NetworksCon
continue continue
} }
log.Printf("FIREWALL: connecting container %s to network %s with IP %s", ipCfg.ContainerName, networkName, ipStr) // Resolve the actual container name, with fallback to fuzzy matching
// (old shell script behavior: docker ps | grep $D"-")
containerName, err := o.dockerClient.FindContainerName(ctx, ipCfg.ContainerName, ipCfg.Selector)
if err != nil {
log.Printf("FIREWALL: WARNING container %s (selector=%s) not found: %v, trying connection anyway", ipCfg.ContainerName, ipCfg.Selector, err)
containerName = ipCfg.ContainerName
}
log.Printf("FIREWALL: connecting container %s to network %s with IP %s", containerName, networkName, ipStr)
waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second) waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
if err := o.dockerClient.WaitForContainerRunning(waitCtx, ipCfg.ContainerName, 10*time.Second); err != nil { if err := o.dockerClient.WaitForContainerRunning(waitCtx, containerName, 10*time.Second); err != nil {
log.Printf("FIREWALL: WARNING container %s not running yet: %v, connecting anyway", ipCfg.ContainerName, err) log.Printf("FIREWALL: WARNING container %s not running yet: %v, connecting anyway", containerName, err)
} }
cancel() cancel()
if err := o.dockerClient.ConnectContainer(ctx, ipCfg.ContainerName, networkName, ipStr); err != nil { if err := o.dockerClient.ConnectContainer(ctx, containerName, networkName, ipStr); err != nil {
log.Printf("FIREWALL: ERROR connecting container %s to %s: %v", ipCfg.ContainerName, networkName, err) log.Printf("FIREWALL: ERROR connecting container %s to %s: %v", containerName, networkName, err)
} }
} }
} }
+12
View File
@@ -41,6 +41,10 @@ type MockDockerClient struct {
AddRouteGateway string AddRouteGateway string
AddRouteErr error AddRouteErr error
FindContainerNameCalled bool
FindContainerNameResult string
FindContainerNameErr error
InspectContainerErr error InspectContainerErr error
RemoveNetworkErr error RemoveNetworkErr error
DisconnectContainerErr error DisconnectContainerErr error
@@ -94,6 +98,14 @@ func (m *MockDockerClient) AddRouteInContainer(ctx context.Context, containerNam
return m.AddRouteErr return m.AddRouteErr
} }
func (m *MockDockerClient) FindContainerName(ctx context.Context, name, selector string) (string, error) {
m.FindContainerNameCalled = true
if m.FindContainerNameResult != "" {
return m.FindContainerNameResult, m.FindContainerNameErr
}
return name, m.FindContainerNameErr
}
// MockIPTablesManager implements iptables.IPTablesAPI for testing // MockIPTablesManager implements iptables.IPTablesAPI for testing
type MockIPTablesManager struct { type MockIPTablesManager struct {
BinaryResult string BinaryResult string