feat: add logging to Docker and iptables operations, fix iptables path
continuous-integration/drone/push Build is passing
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:
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user