feat: add logging to Docker and iptables operations, fix iptables path
continuous-integration/drone/push Build is passing

- Create /var/log/network-go directory in Dockerfile for log storage
- Add comprehensive logging to Docker client creation, network management, and container operations
- Add logging to iptables rule management (list, delete, etc.)
- Fix iptables executable path resolution in deleteMatchingLinesInContainer to use configured binary path
This commit is contained in:
gyurix
2026-06-15 17:05:53 +02:00
parent 3172023254
commit 27607d1a2e
8 changed files with 401 additions and 66 deletions
+41 -2
View File
@@ -16,6 +16,7 @@ import (
"github.com/docker/docker/client"
"firewall_containers/network-go/config"
"firewall_containers/network-go/logger"
)
// DockerAPI defines the interface for Docker operations, enabling mock implementations for testing
@@ -42,20 +43,29 @@ var _ DockerAPI = (*Client)(nil)
// NewClient creates a new Docker client
func NewClient() (*Client, error) {
logger.Info("DOCKER: creating Docker client (using DOCKER_HOST env)")
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return nil, fmt.Errorf("failed to create Docker client: %w", err)
}
ping, err := cli.Ping(context.Background())
if err != nil {
logger.Warn("DOCKER: Docker daemon ping failed: %v", err)
} else {
logger.Info("DOCKER: connected to Docker daemon (API version=%s, OS=%s)", ping.APIVersion, ping.OSType)
}
return &Client{cli: cli}, nil
}
// Close closes the Docker client
func (c *Client) Close() error {
logger.Debug("DOCKER: closing Docker client")
return c.cli.Close()
}
// EnsureNetwork creates a Docker network if it does not already exist
func (c *Client) EnsureNetwork(ctx context.Context, netCfg config.NetworkConfig) error {
logger.Debug("DOCKER: checking if network %q exists", netCfg.NetworkName)
existingNetworks, err := c.cli.NetworkList(ctx, network.ListOptions{
Filters: filters.NewArgs(filters.Arg("name", netCfg.NetworkName)),
})
@@ -65,6 +75,7 @@ func (c *Client) EnsureNetwork(ctx context.Context, netCfg config.NetworkConfig)
for _, n := range existingNetworks {
if n.Name == netCfg.NetworkName {
logger.Debug("DOCKER: network %q already exists", netCfg.NetworkName)
return nil
}
}
@@ -95,26 +106,30 @@ func (c *Client) EnsureNetwork(ctx context.Context, netCfg config.NetworkConfig)
Attachable: true,
}
logger.Info("DOCKER: creating network %q (subnet=%s, gateway=%s)", netCfg.NetworkName, netCfg.Subnet, netCfg.Gateway)
resp, err := c.cli.NetworkCreate(ctx, netCfg.NetworkName, createOpts)
if err != nil {
return fmt.Errorf("failed to create network %s: %w", netCfg.NetworkName, err)
}
_ = resp
logger.Info("DOCKER: network %q created (ID=%s)", netCfg.NetworkName, resp.ID)
return nil
}
// RemoveNetwork removes a Docker network
func (c *Client) RemoveNetwork(ctx context.Context, networkName string) error {
logger.Info("DOCKER: removing network %q", networkName)
err := c.cli.NetworkRemove(ctx, networkName)
if err != nil {
return fmt.Errorf("failed to remove network %s: %w", networkName, err)
}
logger.Info("DOCKER: network %q removed", networkName)
return nil
}
// ConnectContainer connects a container to a network with a specific IP
func (c *Client) ConnectContainer(ctx context.Context, containerName, networkName, ip string) error {
logger.Info("DOCKER: connecting container %q to network %q with IP %s", containerName, networkName, ip)
endpointSettings := &network.EndpointSettings{
IPAMConfig: &network.EndpointIPAMConfig{
IPv4Address: ip,
@@ -125,33 +140,41 @@ func (c *Client) ConnectContainer(ctx context.Context, containerName, networkNam
if err != nil {
// "endpoint with name ... already exists" is expected on re-reconciliation
if strings.Contains(err.Error(), "already exists") {
logger.Info("DOCKER: container %q already connected to network %q", containerName, networkName)
return nil
}
return fmt.Errorf("failed to connect container %s to network %s: %w", containerName, networkName, err)
}
logger.Info("DOCKER: container %q connected to network %q with IP %s", containerName, networkName, ip)
return nil
}
// DisconnectContainer disconnects a container from a network
func (c *Client) DisconnectContainer(ctx context.Context, containerName, networkName string) error {
logger.Info("DOCKER: disconnecting container %q from network %q", containerName, networkName)
err := c.cli.NetworkDisconnect(ctx, networkName, containerName, true)
if err != nil {
return fmt.Errorf("failed to disconnect container %s from network %s: %w", containerName, networkName, err)
}
logger.Info("DOCKER: container %q disconnected from network %q", containerName, networkName)
return nil
}
// InspectContainer returns the container's details
func (c *Client) InspectContainer(ctx context.Context, containerName string) (*types.ContainerJSON, error) {
logger.Debug("DOCKER: inspecting container %q", containerName)
container, err := c.cli.ContainerInspect(ctx, containerName)
if err != nil {
return nil, fmt.Errorf("failed to inspect container %s: %w", containerName, err)
}
logger.Debug("DOCKER: container %q inspected (status=%s, PID=%d)",
containerName, container.State.Status, container.State.Pid)
return &container, nil
}
// WaitForContainerRunning waits until a container is in running state, with a timeout
func (c *Client) WaitForContainerRunning(ctx context.Context, containerName string, timeout time.Duration) error {
logger.Info("DOCKER: waiting for container %q to be running (timeout=%s)", containerName, timeout)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
@@ -165,17 +188,21 @@ func (c *Client) WaitForContainerRunning(ctx context.Context, containerName stri
case <-ticker.C:
container, err := c.cli.ContainerInspect(ctx, containerName)
if err != nil {
logger.Debug("DOCKER: container %q inspect failed (not ready yet): %v", containerName, err)
continue
}
if container.State != nil && container.State.Running {
logger.Info("DOCKER: container %q is now running", containerName)
return nil
}
logger.Debug("DOCKER: container %q status=%s, not running yet", containerName, container.State.Status)
}
}
}
// GetContainerPID returns the PID of a container for nsenter operations
func (c *Client) GetContainerPID(ctx context.Context, containerName string) (int, error) {
logger.Info("DOCKER: getting PID for container %q", containerName)
cont, err := c.cli.ContainerInspect(ctx, containerName)
if err != nil {
return 0, fmt.Errorf("failed to inspect container %s: %w", containerName, err)
@@ -183,6 +210,7 @@ func (c *Client) GetContainerPID(ctx context.Context, containerName string) (int
if cont.State == nil || !cont.State.Running {
return 0, fmt.Errorf("container %s is not running", containerName)
}
logger.Info("DOCKER: container %q PID=%d", containerName, cont.State.Pid)
return cont.State.Pid, nil
}
@@ -193,6 +221,7 @@ func (c *Client) AddRouteInContainer(ctx context.Context, containerName, network
return fmt.Errorf("failed to get PID for container %s: %w", containerName, err)
}
logger.Info("DOCKER: adding route in container %q (PID=%d): %s via %s", containerName, pid, network, gateway)
args := []string{
"-t", fmt.Sprintf("%d", pid),
"-n", "--",
@@ -204,6 +233,8 @@ func (c *Client) AddRouteInContainer(ctx context.Context, containerName, network
if err != nil {
return fmt.Errorf("failed to add route in container %s: %w\noutput: %s", containerName, err, string(output))
}
logger.Info("DOCKER: route added in container %q: %s via %s", containerName, network, gateway)
logger.Debug("DOCKER: nsenter output: %s", strings.TrimSpace(string(output)))
return nil
}
@@ -211,6 +242,8 @@ func (c *Client) AddRouteInContainer(ctx context.Context, containerName, network
// First tries exact name match, then exact selector, then prefix matching
// (matching the old shell script's grep $D"-" behavior).
func (c *Client) FindContainerName(ctx context.Context, name, selector string) (string, error) {
logger.Info("DOCKER: finding container: name=%q selector=%q", name, selector)
// Try exact name match using ContainerList with a name filter
// Docker API name filter does an exact match by default
containers, err := c.cli.ContainerList(ctx, container.ListOptions{
@@ -221,11 +254,13 @@ func (c *Client) FindContainerName(ctx context.Context, name, selector string) (
})
if err == nil && len(containers) > 0 {
cName := strings.TrimPrefix(containers[0].Names[0], "/")
logger.Info("DOCKER: found container by exact name match: %q", cName)
return cName, nil
}
// Try exact selector match
if selector != "" && selector != name {
logger.Debug("DOCKER: trying selector match: %q", selector)
containers, err = c.cli.ContainerList(ctx, container.ListOptions{
Filters: filters.NewArgs(
filters.Arg("name", "^/?"+regexp.QuoteMeta(selector)+"$"),
@@ -234,6 +269,7 @@ func (c *Client) FindContainerName(ctx context.Context, name, selector string) (
})
if err == nil && len(containers) > 0 {
cName := strings.TrimPrefix(containers[0].Names[0], "/")
logger.Info("DOCKER: found container by selector match: %q", cName)
return cName, nil
}
}
@@ -249,6 +285,7 @@ func (c *Client) FindContainerName(ctx context.Context, name, selector string) (
if strings.Contains(candidate, "-") {
prefix = candidate[:strings.Index(candidate, "-")]
}
logger.Debug("DOCKER: trying prefix match: candidate=%q prefix=%q", candidate, prefix)
containers, err = c.cli.ContainerList(ctx, container.ListOptions{
Filters: filters.NewArgs(
@@ -257,16 +294,18 @@ func (c *Client) FindContainerName(ctx context.Context, name, selector string) (
),
})
if err != nil {
logger.Debug("DOCKER: prefix list failed: %v", err)
continue
}
for _, c := range containers {
for _, cName := range c.Names {
cName = strings.TrimPrefix(cName, "/")
logger.Info("DOCKER: found container by prefix match %q-: %q", prefix, cName)
return cName, nil
}
}
}
return "", fmt.Errorf("no running container found matching name=%q selector=%q", name, selector)
}
}