mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
198 lines
6.1 KiB
Go
198 lines
6.1 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/go-kit/log/level"
|
|
jsoniter "github.com/json-iterator/go"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/trace"
|
|
|
|
"github.com/grafana/tempo/pkg/util/log"
|
|
"github.com/grafana/tempo/tempodb/backend"
|
|
azure "github.com/grafana/tempo/tempodb/backend/azure"
|
|
"github.com/grafana/tempo/tempodb/backend/gcs"
|
|
"github.com/grafana/tempo/tempodb/backend/local"
|
|
"github.com/grafana/tempo/tempodb/backend/s3"
|
|
)
|
|
|
|
const (
|
|
OverridesKeyPath = "overrides"
|
|
OverridesFileName = "overrides.json"
|
|
)
|
|
|
|
var tracer = otel.Tracer("modules/overrides/userconfigurable/client")
|
|
|
|
var (
|
|
metricList = promauto.NewCounter(prometheus.CounterOpts{
|
|
Namespace: "tempo",
|
|
Name: "overrides_user_configurable_overrides_list_total",
|
|
Help: "How often the user-configurable overrides was listed",
|
|
})
|
|
metricFetch = promauto.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: "tempo",
|
|
Name: "overrides_user_configurable_overrides_fetch_total",
|
|
Help: "How often the user-configurable overrides was fetched for this tenant",
|
|
}, []string{"tenant"})
|
|
metricFetchFailed = promauto.NewCounterVec(prometheus.CounterOpts{
|
|
Namespace: "tempo",
|
|
Name: "overrides_user_configurable_overrides_fetch_failed_total",
|
|
Help: "How often fetching the user-configurable overrides failed for this tenant",
|
|
}, []string{"tenant"})
|
|
)
|
|
|
|
type Config struct {
|
|
Backend string `yaml:"backend"`
|
|
|
|
// ConfirmVersioning is enabled when creating the backend client. If versioning is disabled no
|
|
// checks against concurrent writes will be performed.
|
|
ConfirmVersioning bool `yaml:"confirm_versioning"`
|
|
|
|
Local *local.Config `yaml:"local"`
|
|
GCS *gcs.Config `yaml:"gcs"`
|
|
S3 *s3.Config `yaml:"s3"`
|
|
Azure *azure.Config `yaml:"azure"`
|
|
}
|
|
|
|
func (c *Config) RegisterFlagsAndApplyDefaults(*flag.FlagSet) {
|
|
c.ConfirmVersioning = true
|
|
|
|
// pass in a dummy flagset because we don't want to set any flags for this module
|
|
dummyFlagSet := &flag.FlagSet{}
|
|
|
|
c.Local = &local.Config{}
|
|
c.Local.RegisterFlagsAndApplyDefaults("", dummyFlagSet)
|
|
c.GCS = &gcs.Config{}
|
|
c.GCS.RegisterFlagsAndApplyDefaults("", dummyFlagSet)
|
|
c.S3 = &s3.Config{}
|
|
c.S3.RegisterFlagsAndApplyDefaults("", dummyFlagSet)
|
|
c.Azure = &azure.Config{}
|
|
c.Azure.RegisterFlagsAndApplyDefaults("", dummyFlagSet)
|
|
}
|
|
|
|
// Client is a collection of methods to manage overrides on a backend.
|
|
type Client interface {
|
|
// List tenants that have user-configurable overrides.
|
|
List(ctx context.Context) ([]string, error)
|
|
// Get the user-configurable overrides. Returns backend.ErrDoesNotExist if no limits are set.
|
|
Get(context.Context, string) (*Limits, backend.Version, error)
|
|
// Set the user-configurable overrides. Returns backend.ErrVersionDoesNotMatch if the backend
|
|
// has a newer version.
|
|
Set(context.Context, string, *Limits, backend.Version) (backend.Version, error)
|
|
// Delete the user-configurable overrides.
|
|
Delete(context.Context, string, backend.Version) error
|
|
// Shutdown the client.
|
|
Shutdown()
|
|
}
|
|
|
|
type clientImpl struct {
|
|
rw backend.VersionedReaderWriter
|
|
}
|
|
|
|
var _ Client = (*clientImpl)(nil)
|
|
|
|
func New(cfg *Config) (Client, error) {
|
|
rw, err := initBackend(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &clientImpl{rw}, nil
|
|
}
|
|
|
|
func (o *clientImpl) Shutdown() {
|
|
o.rw.Shutdown()
|
|
}
|
|
|
|
func initBackend(cfg *Config) (rw backend.VersionedReaderWriter, err error) {
|
|
switch cfg.Backend {
|
|
case backend.Local:
|
|
r, w, _, err := local.New(cfg.Local)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Create overrides directory with necessary permissions
|
|
err = os.MkdirAll(path.Join(cfg.Local.Path, OverridesKeyPath), 0o700)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rw = backend.NewFakeVersionedReaderWriter(r, w)
|
|
case backend.GCS:
|
|
rw, err = gcs.NewVersionedReaderWriter(cfg.GCS, cfg.ConfirmVersioning)
|
|
case backend.S3:
|
|
rw, err = s3.NewVersionedReaderWriter(cfg.S3)
|
|
case backend.Azure:
|
|
rw, err = azure.NewVersionedReaderWriter(cfg.Azure)
|
|
default:
|
|
err = fmt.Errorf("unknown backend %s", cfg.Backend)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if cfg.Backend == backend.Local || cfg.Backend == backend.S3 || cfg.Backend == backend.Azure {
|
|
level.Warn(log.Logger).Log(
|
|
"msg", "versioned backend requests are best-effort for the configured backend, concurrent requests modifying user-configurable overrides might cause data races",
|
|
"backend", cfg.Backend,
|
|
)
|
|
}
|
|
return rw, nil
|
|
}
|
|
|
|
func (o *clientImpl) List(ctx context.Context) ([]string, error) {
|
|
ctx, span := tracer.Start(ctx, "clientImpl.List")
|
|
defer span.End()
|
|
|
|
metricList.Inc()
|
|
|
|
return o.rw.List(ctx, []string{OverridesKeyPath})
|
|
}
|
|
|
|
func (o *clientImpl) Get(ctx context.Context, userID string) (tenantLimits *Limits, version backend.Version, err error) {
|
|
ctx, span := tracer.Start(ctx, "clientImpl.Get", trace.WithAttributes(attribute.String("tenant", userID)))
|
|
defer span.End()
|
|
|
|
metricFetch.WithLabelValues(userID).Inc()
|
|
defer func() {
|
|
if err != nil {
|
|
metricFetchFailed.WithLabelValues(userID).Inc()
|
|
}
|
|
}()
|
|
|
|
reader, version, err := o.rw.ReadVersioned(ctx, OverridesFileName, []string{OverridesKeyPath, userID})
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
defer reader.Close()
|
|
|
|
d := json.NewDecoder(reader)
|
|
err = d.Decode(&tenantLimits)
|
|
return
|
|
}
|
|
|
|
func (o *clientImpl) Set(ctx context.Context, userID string, limits *Limits, version backend.Version) (backend.Version, error) {
|
|
ctx, span := tracer.Start(ctx, "clientImpl.Set", trace.WithAttributes(attribute.String("tenant", userID)))
|
|
defer span.End()
|
|
|
|
data, err := jsoniter.Marshal(limits)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return o.rw.WriteVersioned(ctx, OverridesFileName, []string{OverridesKeyPath, userID}, bytes.NewReader(data), int64(len(data)), version)
|
|
}
|
|
|
|
func (o *clientImpl) Delete(ctx context.Context, userID string, version backend.Version) error {
|
|
ctx, span := tracer.Start(ctx, "clientImpl.Delete", trace.WithAttributes(attribute.String("tenant", userID)))
|
|
defer span.End()
|
|
|
|
return o.rw.DeleteVersioned(ctx, OverridesFileName, []string{OverridesKeyPath, userID}, version)
|
|
}
|