go-dockerclient icon indicating copy to clipboard operation
go-dockerclient copied to clipboard

testing.NewServer doesn't implement /events endpoint

Open CybotTM opened this issue 3 months ago • 0 comments

Summary

The mock server provided by testing.NewServer() doesn't implement the Docker /events endpoint, causing tests that use AddEventListener / AddEventListenerWithOptions to hang forever.

Reproduction

package main

import (
    "testing"
    "time"
    
    docker "github.com/fsouza/go-dockerclient"
    "github.com/fsouza/go-dockerclient/testing"
)

func TestEventsWithMockServer(t *testing.T) {
    server, err := testing.NewServer("127.0.0.1:0", nil, nil)
    if err != nil {
        t.Fatal(err)
    }
    defer server.Stop()

    client, err := docker.NewClient(server.URL())
    if err != nil {
        t.Fatal(err)
    }

    events := make(chan *docker.APIEvents, 10)
    
    // This works - listener is added
    err = client.AddEventListenerWithOptions(docker.EventsOptions{
        Filters: map[string][]string{"type": {"container"}},
    }, events)
    if err != nil {
        t.Fatal(err)
    }

    // Create a container that the mock server will track
    container, err := client.CreateContainer(docker.CreateContainerOptions{
        Name: "test",
        Config: &docker.Config{Image: "test-image"},
    })
    if err != nil {
        t.Fatal(err)
    }

    // Start and stop the container
    client.StartContainer(container.ID, nil)
    server.MutateContainer(container.ID, docker.State{Running: false, ExitCode: 0})

    // This will hang forever because the mock server never sends events
    select {
    case <-events:
        t.Log("Received event")
    case <-time.After(5 * time.Second):
        t.Fatal("Timeout waiting for event - mock server doesn't implement /events")
    }
}

Expected Behavior

When container state changes via MutateContainer(), the mock server should send appropriate Docker events (die, stop, etc.) to registered event listeners.

Actual Behavior

The mock server accepts AddEventListener calls but never sends any events. Code that waits for container events hangs indefinitely.

Workaround

We added a polling fallback in our container monitoring code that periodically calls InspectContainer alongside event listening:

pollTicker := time.NewTicker(100 * time.Millisecond)
defer pollTicker.Stop()

for {
    select {
    case event := <-eventChan:
        // Handle event
    case <-pollTicker.C:
        // Fallback: check container status directly
        container, _ := client.InspectContainer(containerID)
        if !container.State.Running {
            return &container.State, nil
        }
    }
}

Impact

This limitation makes it difficult to test code that relies on Docker events for container lifecycle monitoring without adding workarounds that also affect production code.

Environment

  • go-dockerclient version: v1.12.2
  • Go version: 1.23.x

CybotTM avatar Nov 26 '25 08:11 CybotTM