feat(network-go): add fuzzy container name resolution for firewall connections
continuous-integration/drone/push Build is passing
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:
@@ -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
|
||||
@@ -198,3 +201,53 @@ func (c *Client) AddRouteInContainer(ctx context.Context, containerName, network
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user