app/vmauth: properly handle the case when zero backend hosts are resolved at SRV DNS

When zero backend hosts are resolved, then vmauth must return 'no backend hosts' error instead of crashing with panic

This is a follow-up for 590aeccd7d and 3a45bbb4e0

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6401
This commit is contained in:
Aliaksandr Valialkin 2024-07-17 11:30:15 +02:00
parent 7ee5797493
commit 7ed719b46a
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
3 changed files with 58 additions and 0 deletions

View file

@ -312,12 +312,18 @@ func (up *URLPrefix) getBackendsCount() int {
// getBackendURL returns the backendURL depending on the load balance policy. // getBackendURL returns the backendURL depending on the load balance policy.
// //
// It can return nil if there are no backend urls available at the moment.
//
// backendURL.put() must be called on the returned backendURL after the request is complete. // backendURL.put() must be called on the returned backendURL after the request is complete.
func (up *URLPrefix) getBackendURL() *backendURL { func (up *URLPrefix) getBackendURL() *backendURL {
up.discoverBackendAddrsIfNeeded() up.discoverBackendAddrsIfNeeded()
pbus := up.bus.Load() pbus := up.bus.Load()
bus := *pbus bus := *pbus
if len(bus) == 0 {
return nil
}
if up.loadBalancingPolicy == "first_available" { if up.loadBalancingPolicy == "first_available" {
return getFirstAvailableBackendURL(bus) return getFirstAvailableBackendURL(bus)
} }

View file

@ -208,6 +208,9 @@ func processRequest(w http.ResponseWriter, r *http.Request, ui *UserInfo) {
maxAttempts := up.getBackendsCount() maxAttempts := up.getBackendsCount()
for i := 0; i < maxAttempts; i++ { for i := 0; i < maxAttempts; i++ {
bu := up.getBackendURL() bu := up.getBackendURL()
if bu == nil {
break
}
targetURL := bu.url targetURL := bu.url
// Don't change path and add request_path query param for default route. // Don't change path and add request_path query param for default route.
if isDefault { if isDefault {

View file

@ -342,6 +342,55 @@ func TestUserInfoGetBackendURL_SRV(t *testing.T) {
f(ui, `/test`, "http://non-exist-dns-addr/test") f(ui, `/test`, "http://non-exist-dns-addr/test")
} }
func TestUserInfoGetBackendURL_SRVZeroBackends(t *testing.T) {
f := func(ui *UserInfo, requestURI string) {
t.Helper()
u, err := url.Parse(requestURI)
if err != nil {
t.Fatalf("cannot parse %q: %s", requestURI, err)
}
u = normalizeURL(u)
up, _ := ui.getURLPrefixAndHeaders(u, nil)
if up == nil {
t.Fatalf("cannot match available backend: %s", err)
}
bu := up.getBackendURL()
if bu != nil {
t.Fatalf("expecting nil backendURL; got %v", bu)
}
}
customResolver := &fakeResolver{
Resolver: &net.Resolver{},
lookupSRVResults: map[string][]*net.SRV{
"vmselect": {},
},
}
origResolver := netutil.Resolver
netutil.Resolver = customResolver
defer func() {
netutil.Resolver = origResolver
}()
allowed := true
ui := &UserInfo{
URLMaps: []URLMap{
{
SrcPaths: getRegexs([]string{"/select/.+"}),
URLPrefix: mustParseURL("http://srv+vmselect"),
},
},
DiscoverBackendIPs: &allowed,
URLPrefix: mustParseURL("http://non-exist-dns-addr"),
}
if err := ui.initURLs(); err != nil {
t.Fatalf("cannot initialize urls inside UserInfo: %s", err)
}
f(ui, `/select/0/prometheus/api/v1/query?query=up`)
}
func TestCreateTargetURLFailure(t *testing.T) { func TestCreateTargetURLFailure(t *testing.T) {
f := func(ui *UserInfo, requestURI string) { f := func(ui *UserInfo, requestURI string) {
t.Helper() t.Helper()