mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
The Prometheus dependency upgrade from v0.304.2 to v0.307.3 (PR prometheus/prometheus#16928) moved label name validation from a global to a per-config NameValidationScheme field on relabel.Config. This field must be initialized by calling Validate() before use, otherwise relabeling panics with "Invalid name validation scheme requested: unset". Tempo constructs RemoteWriteConfig programmatically rather than going through Prometheus's config.Load() path, so Validate() was never called. Call RemoteWriteConfig.Validate(model.UTF8Validation) once at startup in storage.Config.Validate(), which initializes NameValidationScheme on all write relabel configs before any tenant storage instances are created. Also fix a pre-existing bug in watchOverrides where cached state was updated before ApplyConfig succeeded, preventing retries on failure.
198 lines
6.6 KiB
Go
198 lines
6.6 KiB
Go
package storage
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/url"
|
|
"os"
|
|
"testing"
|
|
|
|
prometheus_common_config "github.com/prometheus/common/config"
|
|
"github.com/prometheus/common/model"
|
|
prometheus_config "github.com/prometheus/prometheus/config"
|
|
"github.com/prometheus/prometheus/model/labels"
|
|
"github.com/prometheus/prometheus/model/relabel"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/tempo/pkg/util"
|
|
)
|
|
|
|
func Test_generateTenantRemoteWriteConfigs(t *testing.T) {
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
|
|
|
original := []prometheus_config.RemoteWriteConfig{
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-1/api/prom/push")},
|
|
Headers: map[string]string{},
|
|
},
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-2/api/prom/push")},
|
|
Headers: map[string]string{
|
|
"foo": "bar",
|
|
"x-scope-orgid": "fake-tenant",
|
|
},
|
|
},
|
|
}
|
|
|
|
addOrgIDHeader := true
|
|
|
|
result := generateTenantRemoteWriteConfigs(original, "my-tenant", nil, addOrgIDHeader, logger, false)
|
|
|
|
// First case doesn't have a header, gets set.
|
|
assert.Equal(t, original[0].URL, result[0].URL)
|
|
assert.Equal(t, map[string]string{}, original[0].Headers, "Original headers have been modified")
|
|
assert.Equal(t, map[string]string{"X-Scope-OrgID": "my-tenant"}, result[0].Headers)
|
|
|
|
// Second case already contains header, not overwritten
|
|
// Also checks case-insensitivity
|
|
assert.Equal(t, original[1].URL, result[1].URL)
|
|
assert.Equal(t, map[string]string{"foo": "bar", "x-scope-orgid": "fake-tenant"}, original[1].Headers, "Original headers have been modified")
|
|
assert.Equal(t, map[string]string{"foo": "bar", "x-scope-orgid": "fake-tenant"}, result[1].Headers, "Existing header was incorrectly overwritten")
|
|
}
|
|
|
|
func Test_generateTenantRemoteWriteConfigs_singleTenant(t *testing.T) {
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
|
|
|
original := []prometheus_config.RemoteWriteConfig{
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-1/api/prom/push")},
|
|
Headers: map[string]string{},
|
|
},
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-2/api/prom/push")},
|
|
Headers: map[string]string{
|
|
"x-scope-orgid": "my-custom-tenant-id",
|
|
},
|
|
},
|
|
}
|
|
|
|
addOrgIDHeader := true
|
|
|
|
result := generateTenantRemoteWriteConfigs(original, util.FakeTenantID, nil, addOrgIDHeader, logger, false)
|
|
|
|
assert.Equal(t, original[0].URL, result[0].URL)
|
|
|
|
assert.Equal(t, original[0].URL, result[0].URL)
|
|
assert.Equal(t, map[string]string{}, original[0].Headers, "Original headers have been modified")
|
|
// X-Scope-OrgID has not been injected
|
|
assert.Equal(t, map[string]string{}, result[0].Headers)
|
|
|
|
assert.Equal(t, original[1].URL, result[1].URL)
|
|
assert.Equal(t, map[string]string{"x-scope-orgid": "my-custom-tenant-id"}, original[1].Headers, "Original headers have been modified")
|
|
// X-Scope-OrgID has not been modified
|
|
assert.Equal(t, map[string]string{"x-scope-orgid": "my-custom-tenant-id"}, result[1].Headers)
|
|
}
|
|
|
|
func Test_generateTenantRemoteWriteConfigs_addOrgIDHeader(t *testing.T) {
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
|
|
|
original := []prometheus_config.RemoteWriteConfig{
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-1/api/prom/push")},
|
|
Headers: map[string]string{},
|
|
},
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-2/api/prom/push")},
|
|
Headers: map[string]string{
|
|
"foo": "bar",
|
|
"x-scope-orgid": "fake-tenant",
|
|
},
|
|
},
|
|
}
|
|
|
|
addOrgIDHeader := false
|
|
|
|
result := generateTenantRemoteWriteConfigs(original, "my-tenant", nil, addOrgIDHeader, logger, false)
|
|
|
|
assert.Equal(t, original[0].URL, result[0].URL)
|
|
assert.Empty(t, original[0].Headers, "X-Scope-OrgID header is not added")
|
|
|
|
assert.Equal(t, original[1].URL, result[1].URL)
|
|
assert.Equal(t, map[string]string{"foo": "bar", "x-scope-orgid": "fake-tenant"}, result[1].Headers, "Original headers not modified")
|
|
}
|
|
|
|
func Test_generateTenantRemoteWriteConfigs_sendNativeHistograms(t *testing.T) {
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
|
|
|
original := []prometheus_config.RemoteWriteConfig{
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-1/api/prom/push")},
|
|
Headers: map[string]string{},
|
|
},
|
|
}
|
|
|
|
result := generateTenantRemoteWriteConfigs(original, "my-tenant", nil, false, logger, true)
|
|
assert.Equal(t, true, result[0].SendNativeHistograms, "SendNativeHistograms should be true")
|
|
|
|
result = generateTenantRemoteWriteConfigs(original, "my-tenant", nil, false, logger, false)
|
|
assert.Equal(t, false, result[0].SendNativeHistograms, "SendNativeHistograms should be true")
|
|
}
|
|
|
|
func Test_copyMap(t *testing.T) {
|
|
original := map[string]string{
|
|
"k1": "v1",
|
|
"k2": "v2",
|
|
}
|
|
|
|
copied := copyMap(original)
|
|
|
|
assert.Equal(t, original, copied)
|
|
|
|
copied["k2"] = "other value"
|
|
copied["k3"] = "v3"
|
|
|
|
assert.Len(t, original, 2)
|
|
assert.Equal(t, "v2", original["k2"])
|
|
assert.Equal(t, "", original["k3"])
|
|
}
|
|
|
|
func Test_generateTenantRemoteWriteConfigs_writeRelabelConfigs(t *testing.T) {
|
|
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
|
|
|
cfg := Config{
|
|
RemoteWrite: []prometheus_config.RemoteWriteConfig{
|
|
{
|
|
URL: &prometheus_common_config.URL{URL: urlMustParse("http://prometheus-1/api/prom/push")},
|
|
Headers: map[string]string{},
|
|
WriteRelabelConfigs: []*relabel.Config{
|
|
{
|
|
SourceLabels: model.LabelNames{"deployment_environment"},
|
|
Separator: relabel.DefaultRelabelConfig.Separator,
|
|
Regex: relabel.DefaultRelabelConfig.Regex,
|
|
Replacement: relabel.DefaultRelabelConfig.Replacement,
|
|
Action: relabel.Replace,
|
|
TargetLabel: "client_deployment_environment",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Validate once at startup, as the generator does in Config.Validate().
|
|
require.NoError(t, cfg.Validate())
|
|
|
|
result := generateTenantRemoteWriteConfigs(cfg.RemoteWrite, "my-tenant", nil, false, logger, false)
|
|
|
|
// Simulate what the Prometheus remote write queue manager does: run the
|
|
// relabel configs against a label set. Before the fix this panics with
|
|
// "Invalid name validation scheme requested: unset" because
|
|
// NameValidationScheme was never initialized on the relabel configs.
|
|
lbls := labels.FromStrings("deployment_environment", "production")
|
|
|
|
var newLbls labels.Labels
|
|
var keep bool
|
|
assert.NotPanics(t, func() {
|
|
newLbls, keep = relabel.Process(lbls, result[0].WriteRelabelConfigs...)
|
|
})
|
|
assert.True(t, keep)
|
|
assert.Equal(t, "production", newLbls.Get("client_deployment_environment"))
|
|
}
|
|
|
|
func urlMustParse(urlStr string) *url.URL {
|
|
url, err := url.Parse(urlStr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return url
|
|
}
|