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"
"net"
"os/exec"
"strings"
"time"
"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/network"
"github.com/docker/docker/client"
@@ -26,6 +28,7 @@ type DockerAPI interface {
WaitForContainerRunning(ctx context.Context, containerName string, timeout time.Duration) error
GetContainerPID(ctx context.Context, containerName string) (int, 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
@@ -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 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
}
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)
if err := o.dockerClient.WaitForContainerRunning(waitCtx, ipCfg.ContainerName, 10*time.Second); err != nil {
log.Printf("FIREWALL: WARNING container %s not running yet: %v, connecting anyway", ipCfg.ContainerName, err)
if err := o.dockerClient.WaitForContainerRunning(waitCtx, containerName, 10*time.Second); err != nil {
log.Printf("FIREWALL: WARNING container %s not running yet: %v, connecting anyway", containerName, err)
}
cancel()
if err := o.dockerClient.ConnectContainer(ctx, ipCfg.ContainerName, networkName, ipStr); err != nil {
log.Printf("FIREWALL: ERROR connecting container %s to %s: %v", ipCfg.ContainerName, networkName, err)
if err := o.dockerClient.ConnectContainer(ctx, containerName, networkName, ipStr); err != nil {
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
AddRouteErr error
FindContainerNameCalled bool
FindContainerNameResult string
FindContainerNameErr error
InspectContainerErr error
RemoveNetworkErr error
DisconnectContainerErr error
@@ -94,6 +98,14 @@ func (m *MockDockerClient) AddRouteInContainer(ctx context.Context, containerNam
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
type MockIPTablesManager struct {
BinaryResult string