Wait out a failed lookup of the registry's name in a build, and say it was the resolver (issue 1748)
mesh/delivery delivered
mesh/check pass 0b312218f90f on c834e4f82137 | unit 1762 run, 0 kept, 4 skipped | integration 3040 run, 0 kept, 16 skipped | over bound: integration (…

This commit is contained in:
2026-10-23 13:03:17 +02:00
parent c834e4f821
commit 0b312218f9
2 changed files with 88 additions and 7 deletions
+65
View File
@@ -4,6 +4,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"net"
"net/http"
"strings"
@@ -152,3 +153,67 @@ func TestAnImagePushWaitsForARegistryHeldStill(t *testing.T) {
type errorString string
func (e errorString) Error() string { return string(e) }
// A resolver that does not answer for a moment (novox/hq issue 1748): a lookup of the registry's name that
// timed out failed the build at once, though the same name resolved a second later.
const lookupTimeout = "Head \"http://novox.internal:5100/v2/x/blobs/sha256:1\": dial tcp: lookup novox.internal on 10.10.0.3:53: " +
"read udp 10.10.0.3:44790->10.10.0.3:53: i/o timeout"
type lookupTimedOut struct{}
func (lookupTimedOut) Error() string { return lookupTimeout }
func TestABuildWaitsOutAResolverThatDoesNotAnswerAndGoesOn(t *testing.T) { // 1748/AC1
waitingFor(t, 5*time.Second)
said := saying(t)
calls := 0
err := waitForRegistry(context.Background(), "novox.internal:5100", "HEAD /v2/x", func() error {
calls++
if calls < 3 {
return lookupTimedOut{}
}
return nil
})
if err != nil {
t.Fatalf("a lookup timing out twice failed the build: %v", err)
}
if calls != 3 {
t.Errorf("tried %d times, want 3", calls)
}
log := strings.Join(said(), "\n")
if !strings.Contains(log, "resolver did not answer") || !strings.Contains(log, "answers again") {
t.Errorf("the build's log does not say the resolver was waited for, and that it came back:\n%s", log)
}
}
func TestABuildFailsNamingTheResolverWhenItDoesNotAnswerPastTheBound(t *testing.T) { // 1748/AC2
waitingFor(t, 100*time.Millisecond)
saying(t)
started := time.Now()
err := waitForRegistry(context.Background(), "novox.internal:5100", "HEAD /v2/x", func() error { return lookupTimedOut{} })
if err == nil {
t.Fatal("a resolver that never answered let the build go on")
}
if !strings.Contains(err.Error(), "resolver did not answer") || !strings.Contains(err.Error(), "i/o timeout") {
t.Errorf("the failure does not name the resolver: %v", err)
}
if strings.Contains(err.Error(), "refused every connection") {
t.Errorf("the failure blames a refusal: %v", err)
}
if waited := time.Since(started); waited < 100*time.Millisecond {
t.Errorf("failed after %s, before the bound", waited)
}
}
func TestAnErrorThatIsNeitherARefusalNorALookupFailsAtOnce(t *testing.T) { // 1748/AC3
waitingFor(t, 5*time.Second)
calls := 0
err := waitForRegistry(context.Background(), "novox.internal:5100", "HEAD /v2/x", func() error {
calls++
return errors.New("401 unauthorized")
})
if err == nil || calls != 1 {
t.Fatalf("err %v after %d calls, want a failure at once", err, calls)
}
}