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"
|
"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)
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user