Compare commits
27 Commits
remove-dea
...
licensing
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4af0503a89 | ||
|
|
fa2f63bc0d | ||
|
|
084def0ba9 | ||
|
|
b77bca7b71 | ||
|
|
a6bd5f9e33 | ||
|
|
2ae34cbcae | ||
|
|
46e8182ab1 | ||
|
|
7bc2a614f9 | ||
|
|
88227c6992 | ||
|
|
6aa8f18018 | ||
|
|
a389901b8d | ||
|
|
30e2581bbc | ||
|
|
ff1e46766f | ||
|
|
8e48e58f9b | ||
|
|
bb7301bc9f | ||
|
|
7b5eae84d5 | ||
|
|
0aeb1009c6 | ||
|
|
ab3b250629 | ||
|
|
765354b1ee | ||
|
|
a3daf43186 | ||
|
|
1649c0e26f | ||
|
|
142ad8adc4 | ||
|
|
cda94d0325 | ||
|
|
51ae2df8d5 | ||
|
|
ea4e9988a5 | ||
|
|
74489efeef | ||
|
|
834d75fbbd |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -83,3 +83,4 @@ queries.active
|
|||||||
|
|
||||||
# .devenv tmp files
|
# .devenv tmp files
|
||||||
.devenv/**/tmp/**
|
.devenv/**/tmp/**
|
||||||
|
.qodo
|
||||||
|
|||||||
31
ee/licensing/licensingserver/config.go
Normal file
31
ee/licensing/licensingserver/config.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package licensingserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
PollingConfig PollingConfig `mapstructure:"polling"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PollingConfig struct {
|
||||||
|
Interval time.Duration `mapstructure:"interval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig() Config {
|
||||||
|
return Config{
|
||||||
|
PollingConfig: PollingConfig{
|
||||||
|
Interval: 24 * time.Hour,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigFromLicensingConfig(config licensing.Config) Config {
|
||||||
|
return Config{
|
||||||
|
PollingConfig: PollingConfig{
|
||||||
|
Interval: config.PollingConfig.Interval,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
72
ee/licensing/licensingserver/server.go
Normal file
72
ee/licensing/licensingserver/server.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package licensingserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
logger *slog.Logger
|
||||||
|
|
||||||
|
cfg Config
|
||||||
|
|
||||||
|
orgID valuer.UUID
|
||||||
|
|
||||||
|
zeus zeus.Zeus
|
||||||
|
|
||||||
|
store licensetypes.Store
|
||||||
|
|
||||||
|
license licensetypes.License
|
||||||
|
|
||||||
|
mtx sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(logger *slog.Logger, config Config, orgID valuer.UUID, zeus zeus.Zeus, store licensetypes.Store) *Server {
|
||||||
|
return &Server{
|
||||||
|
logger: logger,
|
||||||
|
cfg: config,
|
||||||
|
orgID: orgID,
|
||||||
|
zeus: zeus,
|
||||||
|
store: store,
|
||||||
|
license: licensetypes.NewNoop(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) Fetch(ctx context.Context) error {
|
||||||
|
license, err := server.store.GetLatest(ctx, server.orgID)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Ast(err, errors.TypeNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchedLicense, err := server.zeus.GetLicense(ctx, license.Key())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.SetLicense(ctx, fetchedLicense)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) SetLicense(ctx context.Context, license licensetypes.License) error {
|
||||||
|
server.mtx.Lock()
|
||||||
|
defer server.mtx.Unlock()
|
||||||
|
|
||||||
|
server.license = license
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (server *Server) GetLicense(ctx context.Context) licensetypes.License {
|
||||||
|
server.mtx.RLock()
|
||||||
|
defer server.mtx.RUnlock()
|
||||||
|
|
||||||
|
return server.license
|
||||||
|
}
|
||||||
35
ee/licensing/licensingstore/sqllicensingstore/store.go
Normal file
35
ee/licensing/licensingstore/sqllicensingstore/store.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package sqllicensingstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type store struct {
|
||||||
|
sqlstore sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStore(sqlstore sqlstore.SQLStore) licensetypes.Store {
|
||||||
|
return &store{
|
||||||
|
sqlstore: sqlstore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) Set(ctx context.Context, license licensetypes.License) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) Get(ctx context.Context, orgID valuer.UUID) ([]licensetypes.License, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) GetLatest(ctx context.Context, orgID valuer.UUID) (licensetypes.License, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *store) ListOrgs(ctx context.Context) ([]valuer.UUID, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
109
ee/licensing/pollinglicensing/provider.go
Normal file
109
ee/licensing/pollinglicensing/provider.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package pollinglicensing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/ee/licensing/licensingstore/sqllicensingstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type provider struct {
|
||||||
|
config licensing.Config
|
||||||
|
settings factory.ScopedProviderSettings
|
||||||
|
zeus zeus.Zeus
|
||||||
|
service *Service
|
||||||
|
store licensetypes.Store
|
||||||
|
stopC chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactory(zeus zeus.Zeus, sqlstore sqlstore.SQLStore) factory.ProviderFactory[licensing.Licensing, licensing.Config] {
|
||||||
|
return factory.NewProviderFactory(factory.MustNewName("sql"), func(ctx context.Context, providerSettings factory.ProviderSettings, config licensing.Config) (licensing.Licensing, error) {
|
||||||
|
return New(ctx, providerSettings, config, zeus, sqlstore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, providerSettings factory.ProviderSettings, config licensing.Config, zeus zeus.Zeus, sqlstore sqlstore.SQLStore) (licensing.Licensing, error) {
|
||||||
|
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/licensing/pollinglicensing")
|
||||||
|
store := sqllicensingstore.NewStore(sqlstore)
|
||||||
|
|
||||||
|
return &provider{
|
||||||
|
config: config,
|
||||||
|
settings: settings,
|
||||||
|
zeus: zeus,
|
||||||
|
service: NewService(ctx, settings, config, store, zeus),
|
||||||
|
stopC: make(chan struct{}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Start(ctx context.Context) error {
|
||||||
|
if err := provider.service.SyncServers(ctx); err != nil {
|
||||||
|
provider.settings.Logger().ErrorContext(ctx, "failed to sync licensing servers", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(provider.config.PollingConfig.Interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-provider.stopC:
|
||||||
|
return nil
|
||||||
|
case <-ticker.C:
|
||||||
|
if err := provider.service.SyncServers(ctx); err != nil {
|
||||||
|
provider.settings.Logger().ErrorContext(ctx, "failed to sync licensing servers", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) GetLatestLicense(ctx context.Context, orgID valuer.UUID) (licensetypes.License, error) {
|
||||||
|
server, err := provider.service.getServer(orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.GetLicense(ctx), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) GetLicenses(ctx context.Context, orgID valuer.UUID, params licensetypes.GettableLicenseParams) (licensetypes.GettableLicenses, error) {
|
||||||
|
if params.Active != nil {
|
||||||
|
if *params.Active {
|
||||||
|
license, err := provider.GetLatestLicense(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return licensetypes.GettableLicenses{license}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
licenses, err := provider.store.Get(ctx, orgID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return licenses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) SetLicense(ctx context.Context, orgID valuer.UUID, key string) error {
|
||||||
|
license, err := provider.zeus.GetLicense(ctx, key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := provider.store.Set(ctx, license); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider.service.SyncOrgServer(ctx, orgID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Stop(ctx context.Context) error {
|
||||||
|
close(provider.stopC)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
103
ee/licensing/pollinglicensing/service.go
Normal file
103
ee/licensing/pollinglicensing/service.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package pollinglicensing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/ee/licensing/licensingserver"
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
// config is the config for the licensing service
|
||||||
|
config licensing.Config
|
||||||
|
|
||||||
|
// store is the store for the licensing service
|
||||||
|
store licensetypes.Store
|
||||||
|
|
||||||
|
// zeus
|
||||||
|
zeus zeus.Zeus
|
||||||
|
|
||||||
|
// settings is the settings for the licensing service
|
||||||
|
settings factory.ScopedProviderSettings
|
||||||
|
|
||||||
|
// Map of organization id to alertmanager server
|
||||||
|
servers map[valuer.UUID]*licensingserver.Server
|
||||||
|
|
||||||
|
// Mutex to protect the servers map
|
||||||
|
serversMtx sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewService(ctx context.Context, settings factory.ScopedProviderSettings, config licensing.Config, store licensetypes.Store, zeus zeus.Zeus) *Service {
|
||||||
|
service := &Service{
|
||||||
|
config: config,
|
||||||
|
store: store,
|
||||||
|
zeus: zeus,
|
||||||
|
settings: settings,
|
||||||
|
servers: make(map[valuer.UUID]*licensingserver.Server),
|
||||||
|
serversMtx: sync.RWMutex{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) SyncServers(ctx context.Context) error {
|
||||||
|
orgIDs, err := service.store.ListOrgs(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
service.serversMtx.Lock()
|
||||||
|
for _, orgID := range orgIDs {
|
||||||
|
// If the server is not present, create it and sync the config
|
||||||
|
if _, ok := service.servers[orgID]; !ok {
|
||||||
|
server := licensingserver.NewServer(service.settings.Logger(), licensingserver.NewConfigFromLicensingConfig(service.config), orgID, service.zeus, service.store)
|
||||||
|
service.servers[orgID] = server
|
||||||
|
}
|
||||||
|
|
||||||
|
err = service.servers[orgID].Fetch(ctx)
|
||||||
|
if err != nil {
|
||||||
|
service.settings.Logger().Error("failed to fetch license for licensing server", "orgID", orgID, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service.serversMtx.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) SyncOrgServer(ctx context.Context, orgID valuer.UUID) error {
|
||||||
|
service.serversMtx.Lock()
|
||||||
|
defer service.serversMtx.Unlock()
|
||||||
|
|
||||||
|
_, ok := service.servers[orgID]
|
||||||
|
if !ok {
|
||||||
|
server := licensingserver.NewServer(service.settings.Logger(), licensingserver.NewConfigFromLicensingConfig(service.config), orgID, service.zeus, service.store)
|
||||||
|
service.servers[orgID] = server
|
||||||
|
}
|
||||||
|
|
||||||
|
err := service.servers[orgID].Fetch(ctx)
|
||||||
|
if err != nil {
|
||||||
|
service.settings.Logger().Error("failed to fetch license for licensing server", "orgID", orgID, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (service *Service) getServer(orgID valuer.UUID) (*licensingserver.Server, error) {
|
||||||
|
service.serversMtx.RLock()
|
||||||
|
defer service.serversMtx.RUnlock()
|
||||||
|
|
||||||
|
server, ok := service.servers[orgID]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Newf(errors.TypeNotFound, licensing.ErrCodeLicensingServerNotFound, "server not found for %s", orgID.StringValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/SigNoz/signoz/ee/query-service/dao"
|
"github.com/SigNoz/signoz/ee/query-service/dao"
|
||||||
"github.com/SigNoz/signoz/ee/query-service/integrations/gateway"
|
"github.com/SigNoz/signoz/ee/query-service/integrations/gateway"
|
||||||
"github.com/SigNoz/signoz/ee/query-service/interfaces"
|
|
||||||
"github.com/SigNoz/signoz/ee/query-service/license"
|
"github.com/SigNoz/signoz/ee/query-service/license"
|
||||||
"github.com/SigNoz/signoz/ee/query-service/usage"
|
"github.com/SigNoz/signoz/ee/query-service/usage"
|
||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
@@ -29,7 +28,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type APIHandlerOptions struct {
|
type APIHandlerOptions struct {
|
||||||
DataConnector interfaces.DataConnector
|
DataConnector baseint.Reader
|
||||||
SkipConfig *basemodel.SkipConfig
|
SkipConfig *basemodel.SkipConfig
|
||||||
PreferSpanMetrics bool
|
PreferSpanMetrics bool
|
||||||
AppDao dao.ModelDao
|
AppDao dao.ModelDao
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start the usagemanager
|
// start the usagemanager
|
||||||
usageManager, err := usage.New(modelDao, lm.GetRepo(), serverOptions.SigNoz.TelemetryStore.ClickhouseDB(), serverOptions.Config.TelemetryStore.Clickhouse.DSN)
|
usageManager, err := usage.New(lm.GetRepo(), serverOptions.SigNoz.TelemetryStore, serverOptions.SigNoz.Zeus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package interfaces
|
|
||||||
|
|
||||||
import (
|
|
||||||
baseint "github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Connector defines methods for interaction
|
|
||||||
// with o11y data. for example - clickhouse
|
|
||||||
type DataConnector interface {
|
|
||||||
baseint.Reader
|
|
||||||
}
|
|
||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
"github.com/SigNoz/signoz/pkg/types"
|
"github.com/SigNoz/signoz/pkg/types"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
|
||||||
validate "github.com/SigNoz/signoz/ee/query-service/integrations/signozio"
|
|
||||||
"github.com/SigNoz/signoz/ee/query-service/model"
|
"github.com/SigNoz/signoz/ee/query-service/model"
|
||||||
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
|
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
|
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
|
||||||
@@ -29,6 +29,7 @@ var validationFrequency = 24 * 60 * time.Minute
|
|||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
repo *Repo
|
repo *Repo
|
||||||
|
zeus zeus.Zeus
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
validatorRunning bool
|
validatorRunning bool
|
||||||
// end the license validation, this is important to gracefully
|
// end the license validation, this is important to gracefully
|
||||||
@@ -45,7 +46,7 @@ type Manager struct {
|
|||||||
activeFeatures basemodel.FeatureSet
|
activeFeatures basemodel.FeatureSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartManager(db *sqlx.DB, store sqlstore.SQLStore, features ...basemodel.Feature) (*Manager, error) {
|
func StartManager(db *sqlx.DB, store sqlstore.SQLStore, zeus zeus.Zeus, features ...basemodel.Feature) (*Manager, error) {
|
||||||
if LM != nil {
|
if LM != nil {
|
||||||
return LM, nil
|
return LM, nil
|
||||||
}
|
}
|
||||||
@@ -53,6 +54,7 @@ func StartManager(db *sqlx.DB, store sqlstore.SQLStore, features ...basemodel.Fe
|
|||||||
repo := NewLicenseRepo(db, store)
|
repo := NewLicenseRepo(db, store)
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
repo: &repo,
|
repo: &repo,
|
||||||
|
zeus: zeus,
|
||||||
}
|
}
|
||||||
if err := m.start(features...); err != nil {
|
if err := m.start(features...); err != nil {
|
||||||
return m, err
|
return m, err
|
||||||
@@ -173,14 +175,12 @@ func (lm *Manager) ValidatorV3(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lm *Manager) RefreshLicense(ctx context.Context) *model.ApiError {
|
func (lm *Manager) RefreshLicense(ctx context.Context) *model.ApiError {
|
||||||
|
license, err := lm.zeus.GetLicense(ctx, lm.activeLicenseV3.Key)
|
||||||
license, apiError := validate.ValidateLicenseV3(lm.activeLicenseV3.Key)
|
if err != nil {
|
||||||
if apiError != nil {
|
return model.BadRequest(errors.Wrap(err, "failed to get license"))
|
||||||
zap.L().Error("failed to validate license", zap.Error(apiError.Err))
|
|
||||||
return apiError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := lm.repo.UpdateLicenseV3(ctx, license)
|
err = lm.repo.UpdateLicenseV3(ctx, license)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.BadRequest(errors.Wrap(err, "failed to update the new license"))
|
return model.BadRequest(errors.Wrap(err, "failed to update the new license"))
|
||||||
}
|
}
|
||||||
@@ -247,10 +247,9 @@ func (lm *Manager) ActivateV3(ctx context.Context, licenseKey string) (licenseRe
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
license, apiError := validate.ValidateLicenseV3(licenseKey)
|
license, errv2 := lm.zeus.GetLicense(ctx, lm.activeLicenseV3.Key)
|
||||||
if apiError != nil {
|
if errv2 != nil {
|
||||||
zap.L().Error("failed to get the license", zap.Error(apiError.Err))
|
return nil, model.BadRequest(errors.Wrap(errv2, "failed to get license"))
|
||||||
return nil, apiError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the new license to the sqlite db
|
// insert the new license to the sqlite db
|
||||||
|
|||||||
@@ -1,246 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type License struct {
|
|
||||||
Key string `json:"key" db:"key"`
|
|
||||||
ActivationId string `json:"activationId" db:"activationId"`
|
|
||||||
CreatedAt time.Time `db:"created_at"`
|
|
||||||
|
|
||||||
// PlanDetails contains the encrypted plan info
|
|
||||||
PlanDetails string `json:"planDetails" db:"planDetails"`
|
|
||||||
|
|
||||||
// stores parsed license details
|
|
||||||
LicensePlan
|
|
||||||
|
|
||||||
FeatureSet basemodel.FeatureSet
|
|
||||||
|
|
||||||
// populated in case license has any errors
|
|
||||||
ValidationMessage string `db:"validationMessage"`
|
|
||||||
|
|
||||||
// used only for sending details to front-end
|
|
||||||
IsCurrent bool `json:"isCurrent"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *License) MarshalJSON() ([]byte, error) {
|
|
||||||
|
|
||||||
return json.Marshal(&struct {
|
|
||||||
Key string `json:"key" db:"key"`
|
|
||||||
ActivationId string `json:"activationId" db:"activationId"`
|
|
||||||
ValidationMessage string `db:"validationMessage"`
|
|
||||||
IsCurrent bool `json:"isCurrent"`
|
|
||||||
PlanKey string `json:"planKey"`
|
|
||||||
ValidFrom time.Time `json:"ValidFrom"`
|
|
||||||
ValidUntil time.Time `json:"ValidUntil"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
}{
|
|
||||||
Key: l.Key,
|
|
||||||
ActivationId: l.ActivationId,
|
|
||||||
IsCurrent: l.IsCurrent,
|
|
||||||
PlanKey: l.PlanKey,
|
|
||||||
ValidFrom: time.Unix(l.ValidFrom, 0),
|
|
||||||
ValidUntil: time.Unix(l.ValidUntil, 0),
|
|
||||||
Status: l.Status,
|
|
||||||
ValidationMessage: l.ValidationMessage,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type LicensePlan struct {
|
|
||||||
PlanKey string `json:"planKey"`
|
|
||||||
ValidFrom int64 `json:"validFrom"`
|
|
||||||
ValidUntil int64 `json:"validUntil"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Licenses struct {
|
|
||||||
TrialStart int64 `json:"trialStart"`
|
|
||||||
TrialEnd int64 `json:"trialEnd"`
|
|
||||||
OnTrial bool `json:"onTrial"`
|
|
||||||
WorkSpaceBlock bool `json:"workSpaceBlock"`
|
|
||||||
TrialConvertedToSubscription bool `json:"trialConvertedToSubscription"`
|
|
||||||
GracePeriodEnd int64 `json:"gracePeriodEnd"`
|
|
||||||
Licenses []License `json:"licenses"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubscriptionServerResp struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
Data Licenses `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Plan struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LicenseDB struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
Data string `json:"data"`
|
|
||||||
}
|
|
||||||
type LicenseV3 struct {
|
|
||||||
ID string
|
|
||||||
Key string
|
|
||||||
Data map[string]interface{}
|
|
||||||
PlanName string
|
|
||||||
Features basemodel.FeatureSet
|
|
||||||
Status string
|
|
||||||
IsCurrent bool
|
|
||||||
ValidFrom int64
|
|
||||||
ValidUntil int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractKeyFromMapStringInterface[T any](data map[string]interface{}, key string) (T, error) {
|
|
||||||
var zeroValue T
|
|
||||||
if val, ok := data[key]; ok {
|
|
||||||
if value, ok := val.(T); ok {
|
|
||||||
return value, nil
|
|
||||||
}
|
|
||||||
return zeroValue, fmt.Errorf("%s key is not a valid %s", key, reflect.TypeOf(zeroValue))
|
|
||||||
}
|
|
||||||
return zeroValue, fmt.Errorf("%s key is missing", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLicenseV3(data map[string]interface{}) (*LicenseV3, error) {
|
|
||||||
var features basemodel.FeatureSet
|
|
||||||
|
|
||||||
// extract id from data
|
|
||||||
licenseID, err := extractKeyFromMapStringInterface[string](data, "id")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
delete(data, "id")
|
|
||||||
|
|
||||||
// extract key from data
|
|
||||||
licenseKey, err := extractKeyFromMapStringInterface[string](data, "key")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
delete(data, "key")
|
|
||||||
|
|
||||||
// extract status from data
|
|
||||||
status, err := extractKeyFromMapStringInterface[string](data, "status")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
planMap, err := extractKeyFromMapStringInterface[map[string]any](data, "plan")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
planName, err := extractKeyFromMapStringInterface[string](planMap, "name")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// if license status is invalid then default it to basic
|
|
||||||
if status == LicenseStatusInvalid {
|
|
||||||
planName = PlanNameBasic
|
|
||||||
}
|
|
||||||
|
|
||||||
featuresFromZeus := basemodel.FeatureSet{}
|
|
||||||
if _features, ok := data["features"]; ok {
|
|
||||||
featuresData, err := json.Marshal(_features)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to marshal features data")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(featuresData, &featuresFromZeus); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to unmarshal features data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch planName {
|
|
||||||
case PlanNameTeams:
|
|
||||||
features = append(features, ProPlan...)
|
|
||||||
case PlanNameEnterprise:
|
|
||||||
features = append(features, EnterprisePlan...)
|
|
||||||
case PlanNameBasic:
|
|
||||||
features = append(features, BasicPlan...)
|
|
||||||
default:
|
|
||||||
features = append(features, BasicPlan...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(featuresFromZeus) > 0 {
|
|
||||||
for _, feature := range featuresFromZeus {
|
|
||||||
exists := false
|
|
||||||
for i, existingFeature := range features {
|
|
||||||
if existingFeature.Name == feature.Name {
|
|
||||||
features[i] = feature // Replace existing feature
|
|
||||||
exists = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
features = append(features, feature) // Append if it doesn't exist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data["features"] = features
|
|
||||||
|
|
||||||
_validFrom, err := extractKeyFromMapStringInterface[float64](data, "valid_from")
|
|
||||||
if err != nil {
|
|
||||||
_validFrom = 0
|
|
||||||
}
|
|
||||||
validFrom := int64(_validFrom)
|
|
||||||
|
|
||||||
_validUntil, err := extractKeyFromMapStringInterface[float64](data, "valid_until")
|
|
||||||
if err != nil {
|
|
||||||
_validUntil = 0
|
|
||||||
}
|
|
||||||
validUntil := int64(_validUntil)
|
|
||||||
|
|
||||||
return &LicenseV3{
|
|
||||||
ID: licenseID,
|
|
||||||
Key: licenseKey,
|
|
||||||
Data: data,
|
|
||||||
PlanName: planName,
|
|
||||||
Features: features,
|
|
||||||
ValidFrom: validFrom,
|
|
||||||
ValidUntil: validUntil,
|
|
||||||
Status: status,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLicenseV3WithIDAndKey(id string, key string, data map[string]interface{}) (*LicenseV3, error) {
|
|
||||||
licenseDataWithIdAndKey := data
|
|
||||||
licenseDataWithIdAndKey["id"] = id
|
|
||||||
licenseDataWithIdAndKey["key"] = key
|
|
||||||
return NewLicenseV3(licenseDataWithIdAndKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConvertLicenseV3ToLicenseV2(l *LicenseV3) *License {
|
|
||||||
planKeyFromPlanName, ok := MapOldPlanKeyToNewPlanName[l.PlanName]
|
|
||||||
if !ok {
|
|
||||||
planKeyFromPlanName = Basic
|
|
||||||
}
|
|
||||||
return &License{
|
|
||||||
Key: l.Key,
|
|
||||||
ActivationId: "",
|
|
||||||
PlanDetails: "",
|
|
||||||
FeatureSet: l.Features,
|
|
||||||
ValidationMessage: "",
|
|
||||||
IsCurrent: l.IsCurrent,
|
|
||||||
LicensePlan: LicensePlan{
|
|
||||||
PlanKey: planKeyFromPlanName,
|
|
||||||
ValidFrom: l.ValidFrom,
|
|
||||||
ValidUntil: l.ValidUntil,
|
|
||||||
Status: l.Status},
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type CheckoutRequest struct {
|
|
||||||
SuccessURL string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PortalRequest struct {
|
|
||||||
SuccessURL string `json:"url"`
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewLicenseV3(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
data []byte
|
|
||||||
pass bool
|
|
||||||
expected *LicenseV3
|
|
||||||
error error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Error for missing license id",
|
|
||||||
data: []byte(`{}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("id key is missing"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for license id not being a valid string",
|
|
||||||
data: []byte(`{"id": 10}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("id key is not a valid string"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for missing license key",
|
|
||||||
data: []byte(`{"id":"does-not-matter"}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("key key is missing"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for invalid string license key",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key":10}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("key key is not a valid string"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for missing license status",
|
|
||||||
data: []byte(`{"id":"does-not-matter", "key": "does-not-matter","category":"FREE"}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("status key is missing"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for invalid string license status",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key": "does-not-matter", "category":"FREE", "status":10}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("status key is not a valid string"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for missing license plan",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE"}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("plan key is missing"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for invalid json license plan",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":10}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("plan key is not a valid map[string]interface {}"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Error for invalid license plan",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{}}`),
|
|
||||||
pass: false,
|
|
||||||
error: errors.New("name key is missing"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Parse the entire license properly",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{"name":"TEAMS"},"valid_from": 1730899309,"valid_until": -1}`),
|
|
||||||
pass: true,
|
|
||||||
expected: &LicenseV3{
|
|
||||||
ID: "does-not-matter",
|
|
||||||
Key: "does-not-matter-key",
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"plan": map[string]interface{}{
|
|
||||||
"name": "TEAMS",
|
|
||||||
},
|
|
||||||
"category": "FREE",
|
|
||||||
"status": "ACTIVE",
|
|
||||||
"valid_from": float64(1730899309),
|
|
||||||
"valid_until": float64(-1),
|
|
||||||
},
|
|
||||||
PlanName: PlanNameTeams,
|
|
||||||
ValidFrom: 1730899309,
|
|
||||||
ValidUntil: -1,
|
|
||||||
Status: "ACTIVE",
|
|
||||||
IsCurrent: false,
|
|
||||||
Features: model.FeatureSet{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Fallback to basic plan if license status is invalid",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"INVALID","plan":{"name":"TEAMS"},"valid_from": 1730899309,"valid_until": -1}`),
|
|
||||||
pass: true,
|
|
||||||
expected: &LicenseV3{
|
|
||||||
ID: "does-not-matter",
|
|
||||||
Key: "does-not-matter-key",
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"plan": map[string]interface{}{
|
|
||||||
"name": "TEAMS",
|
|
||||||
},
|
|
||||||
"category": "FREE",
|
|
||||||
"status": "INVALID",
|
|
||||||
"valid_from": float64(1730899309),
|
|
||||||
"valid_until": float64(-1),
|
|
||||||
},
|
|
||||||
PlanName: PlanNameBasic,
|
|
||||||
ValidFrom: 1730899309,
|
|
||||||
ValidUntil: -1,
|
|
||||||
Status: "INVALID",
|
|
||||||
IsCurrent: false,
|
|
||||||
Features: model.FeatureSet{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "fallback states for validFrom and validUntil",
|
|
||||||
data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{"name":"TEAMS"},"valid_from":1234.456,"valid_until":5678.567}`),
|
|
||||||
pass: true,
|
|
||||||
expected: &LicenseV3{
|
|
||||||
ID: "does-not-matter",
|
|
||||||
Key: "does-not-matter-key",
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"plan": map[string]interface{}{
|
|
||||||
"name": "TEAMS",
|
|
||||||
},
|
|
||||||
"valid_from": 1234.456,
|
|
||||||
"valid_until": 5678.567,
|
|
||||||
"category": "FREE",
|
|
||||||
"status": "ACTIVE",
|
|
||||||
},
|
|
||||||
PlanName: PlanNameTeams,
|
|
||||||
ValidFrom: 1234,
|
|
||||||
ValidUntil: 5678,
|
|
||||||
Status: "ACTIVE",
|
|
||||||
IsCurrent: false,
|
|
||||||
Features: model.FeatureSet{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
var licensePayload map[string]interface{}
|
|
||||||
err := json.Unmarshal(tc.data, &licensePayload)
|
|
||||||
require.NoError(t, err)
|
|
||||||
license, err := NewLicenseV3(licensePayload)
|
|
||||||
if license != nil {
|
|
||||||
license.Features = make(model.FeatureSet, 0)
|
|
||||||
delete(license.Data, "features")
|
|
||||||
}
|
|
||||||
|
|
||||||
if tc.pass {
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, license)
|
|
||||||
assert.Equal(t, tc.expected, license)
|
|
||||||
} else {
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.EqualError(t, err, tc.error.Error())
|
|
||||||
require.Nil(t, license)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
|
||||||
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
|
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,7 +23,6 @@ var (
|
|||||||
LicenseStatusInvalid = "INVALID"
|
LicenseStatusInvalid = "INVALID"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DisableUpsell = "DISABLE_UPSELL"
|
|
||||||
const Onboarding = "ONBOARDING"
|
const Onboarding = "ONBOARDING"
|
||||||
const ChatSupport = "CHAT_SUPPORT"
|
const ChatSupport = "CHAT_SUPPORT"
|
||||||
const Gateway = "GATEWAY"
|
const Gateway = "GATEWAY"
|
||||||
@@ -38,83 +36,6 @@ var BasicPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.OSS,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: DisableUpsell,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.CustomMetricsFunction,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.QueryBuilderPanels,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.QueryBuilderAlerts,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelSlack,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelWebhook,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelPagerduty,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelOpsgenie,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelEmail,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelMsTeams,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.UseSpanMetrics,
|
Name: basemodel.UseSpanMetrics,
|
||||||
Active: false,
|
Active: false,
|
||||||
@@ -143,135 +64,6 @@ var BasicPlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.HostsInfraMonitoring,
|
|
||||||
Active: constants.EnableHostsInfraMonitoring(),
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.TraceFunnels,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProPlan = basemodel.FeatureSet{
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: SSO,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.OSS,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.CustomMetricsFunction,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.QueryBuilderPanels,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.QueryBuilderAlerts,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelSlack,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelWebhook,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelPagerduty,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelOpsgenie,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelEmail,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelMsTeams,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.UseSpanMetrics,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: Gateway,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: PremiumSupport,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AnomalyDetection,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.HostsInfraMonitoring,
|
|
||||||
Active: constants.EnableHostsInfraMonitoring(),
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.TraceFunnels,
|
Name: basemodel.TraceFunnels,
|
||||||
Active: false,
|
Active: false,
|
||||||
@@ -289,76 +81,6 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.OSS,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.CustomMetricsFunction,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.QueryBuilderPanels,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.QueryBuilderAlerts,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelSlack,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelWebhook,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelPagerduty,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelOpsgenie,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelEmail,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.AlertChannelMsTeams,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.UseSpanMetrics,
|
Name: basemodel.UseSpanMetrics,
|
||||||
Active: false,
|
Active: false,
|
||||||
@@ -401,13 +123,6 @@ var EnterprisePlan = basemodel.FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
basemodel.Feature{
|
|
||||||
Name: basemodel.HostsInfraMonitoring,
|
|
||||||
Active: constants.EnableHostsInfraMonitoring(),
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
basemodel.Feature{
|
basemodel.Feature{
|
||||||
Name: basemodel.TraceFunnels,
|
Name: basemodel.TraceFunnels,
|
||||||
Active: false,
|
Active: false,
|
||||||
|
|||||||
@@ -4,22 +4,21 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ClickHouse/clickhouse-go/v2"
|
|
||||||
"github.com/go-co-op/gocron"
|
"github.com/go-co-op/gocron"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/ee/query-service/dao"
|
|
||||||
licenseserver "github.com/SigNoz/signoz/ee/query-service/integrations/signozio"
|
licenseserver "github.com/SigNoz/signoz/ee/query-service/integrations/signozio"
|
||||||
"github.com/SigNoz/signoz/ee/query-service/license"
|
"github.com/SigNoz/signoz/ee/query-service/license"
|
||||||
"github.com/SigNoz/signoz/ee/query-service/model"
|
"github.com/SigNoz/signoz/ee/query-service/model"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/utils/encryption"
|
"github.com/SigNoz/signoz/pkg/query-service/utils/encryption"
|
||||||
|
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -34,35 +33,20 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
clickhouseConn clickhouse.Conn
|
telemetryStore telemetrystore.TelemetryStore
|
||||||
|
licenseRepo *license.Repo
|
||||||
licenseRepo *license.Repo
|
scheduler *gocron.Scheduler
|
||||||
|
zeus zeus.Zeus
|
||||||
scheduler *gocron.Scheduler
|
|
||||||
|
|
||||||
modelDao dao.ModelDao
|
|
||||||
|
|
||||||
tenantID string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(modelDao dao.ModelDao, licenseRepo *license.Repo, clickhouseConn clickhouse.Conn, chUrl string) (*Manager, error) {
|
func New(licenseRepo *license.Repo, telemetryStore telemetrystore.TelemetryStore, zeus zeus.Zeus) (*Manager, error) {
|
||||||
hostNameRegex := regexp.MustCompile(`tcp://(?P<hostname>.*):`)
|
|
||||||
hostNameRegexMatches := hostNameRegex.FindStringSubmatch(chUrl)
|
|
||||||
|
|
||||||
tenantID := ""
|
|
||||||
if len(hostNameRegexMatches) == 2 {
|
|
||||||
tenantID = hostNameRegexMatches[1]
|
|
||||||
tenantID = strings.TrimSuffix(tenantID, "-clickhouse")
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
// repository: repo,
|
|
||||||
clickhouseConn: clickhouseConn,
|
|
||||||
licenseRepo: licenseRepo,
|
licenseRepo: licenseRepo,
|
||||||
|
telemetryStore: telemetryStore,
|
||||||
|
zeus: zeus,
|
||||||
scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC
|
scheduler: gocron.NewScheduler(time.UTC).Every(1).Day().At("00:00"), // send usage every at 00:00 UTC
|
||||||
modelDao: modelDao,
|
|
||||||
tenantID: tenantID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +104,7 @@ func (lm *Manager) UploadUsage() {
|
|||||||
|
|
||||||
for _, db := range dbs {
|
for _, db := range dbs {
|
||||||
dbusages := []model.UsageDB{}
|
dbusages := []model.UsageDB{}
|
||||||
err := lm.clickhouseConn.Select(ctx, &dbusages, fmt.Sprintf(query, db, db), time.Now().Add(-(24 * time.Hour)))
|
err := lm.telemetryStore.ClickhouseDB().Select(ctx, &dbusages, fmt.Sprintf(query, db, db), time.Now().Add(-(24 * time.Hour)))
|
||||||
if err != nil && !strings.Contains(err.Error(), "doesn't exist") {
|
if err != nil && !strings.Contains(err.Error(), "doesn't exist") {
|
||||||
zap.L().Error("failed to get usage from clickhouse: %v", zap.Error(err))
|
zap.L().Error("failed to get usage from clickhouse: %v", zap.Error(err))
|
||||||
return
|
return
|
||||||
@@ -136,17 +120,6 @@ func (lm *Manager) UploadUsage() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
zap.L().Info("uploading usage data")
|
|
||||||
|
|
||||||
orgName := ""
|
|
||||||
orgNames, orgError := lm.modelDao.GetOrgs(ctx)
|
|
||||||
if orgError != nil {
|
|
||||||
zap.L().Error("failed to get org data: %v", zap.Error(orgError))
|
|
||||||
}
|
|
||||||
if len(orgNames) == 1 {
|
|
||||||
orgName = orgNames[0].Name
|
|
||||||
}
|
|
||||||
|
|
||||||
usagesPayload := []model.Usage{}
|
usagesPayload := []model.Usage{}
|
||||||
for _, usage := range usages {
|
for _, usage := range usages {
|
||||||
usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data))
|
usageDataBytes, err := encryption.Decrypt([]byte(usage.ExporterID[:32]), []byte(usage.Data))
|
||||||
@@ -166,8 +139,8 @@ func (lm *Manager) UploadUsage() {
|
|||||||
usageData.ExporterID = usage.ExporterID
|
usageData.ExporterID = usage.ExporterID
|
||||||
usageData.Type = usage.Type
|
usageData.Type = usage.Type
|
||||||
usageData.Tenant = "default"
|
usageData.Tenant = "default"
|
||||||
usageData.OrgName = orgName
|
usageData.OrgName = "default"
|
||||||
usageData.TenantId = lm.tenantID
|
usageData.TenantId = "default"
|
||||||
usagesPayload = append(usagesPayload, usageData)
|
usagesPayload = append(usagesPayload, usageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +149,7 @@ func (lm *Manager) UploadUsage() {
|
|||||||
LicenseKey: key,
|
LicenseKey: key,
|
||||||
Usage: usagesPayload,
|
Usage: usagesPayload,
|
||||||
}
|
}
|
||||||
|
|
||||||
lm.UploadUsageWithExponentalBackOff(ctx, payload)
|
lm.UploadUsageWithExponentalBackOff(ctx, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
33
ee/types/featuretypes/enterprise.go
Normal file
33
ee/types/featuretypes/enterprise.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package featuretypes
|
||||||
|
|
||||||
|
import "github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
|
|
||||||
|
var (
|
||||||
|
SingleSignOn = featuretypes.MustNewName("SingleSignOn")
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewEnterpriseRegistry() (featuretypes.Registry, error) {
|
||||||
|
enterpriseRegistry, err := featuretypes.NewRegistry(
|
||||||
|
&featuretypes.Feature{
|
||||||
|
Name: SingleSignOn,
|
||||||
|
Kind: featuretypes.KindBoolean,
|
||||||
|
Description: "Enable single sign on.",
|
||||||
|
Stage: featuretypes.StageStable,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return enterpriseRegistry.MergeOrOverride(featuretypes.MustNewCommunityRegistry()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustNewEnterpriseRegistry() featuretypes.Registry {
|
||||||
|
enterpriseRegistry, err := NewEnterpriseRegistry()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return enterpriseRegistry
|
||||||
|
}
|
||||||
1
ee/types/licensetypes/feature.go
Normal file
1
ee/types/licensetypes/feature.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package licensetypes
|
||||||
245
ee/types/licensetypes/license.go
Normal file
245
ee/types/licensetypes/license.go
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
package licensetypes
|
||||||
|
|
||||||
|
import "github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
|
|
||||||
|
type License struct {
|
||||||
|
ID string
|
||||||
|
Key string
|
||||||
|
Contents map[string]any
|
||||||
|
OrgFeatures []*featuretypes.StorableOrgFeature
|
||||||
|
}
|
||||||
|
|
||||||
|
// type License struct {
|
||||||
|
// Key string `json:"key" db:"key"`
|
||||||
|
// ActivationId string `json:"activationId" db:"activationId"`
|
||||||
|
// CreatedAt time.Time `db:"created_at"`
|
||||||
|
|
||||||
|
// // PlanDetails contains the encrypted plan info
|
||||||
|
// PlanDetails string `json:"planDetails" db:"planDetails"`
|
||||||
|
|
||||||
|
// // stores parsed license details
|
||||||
|
// LicensePlan
|
||||||
|
|
||||||
|
// FeatureSet basemodel.FeatureSet
|
||||||
|
|
||||||
|
// // populated in case license has any errors
|
||||||
|
// ValidationMessage string `db:"validationMessage"`
|
||||||
|
|
||||||
|
// // used only for sending details to front-end
|
||||||
|
// IsCurrent bool `json:"isCurrent"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (l *License) MarshalJSON() ([]byte, error) {
|
||||||
|
|
||||||
|
// return json.Marshal(&struct {
|
||||||
|
// Key string `json:"key" db:"key"`
|
||||||
|
// ActivationId string `json:"activationId" db:"activationId"`
|
||||||
|
// ValidationMessage string `db:"validationMessage"`
|
||||||
|
// IsCurrent bool `json:"isCurrent"`
|
||||||
|
// PlanKey string `json:"planKey"`
|
||||||
|
// ValidFrom time.Time `json:"ValidFrom"`
|
||||||
|
// ValidUntil time.Time `json:"ValidUntil"`
|
||||||
|
// Status string `json:"status"`
|
||||||
|
// }{
|
||||||
|
// Key: l.Key,
|
||||||
|
// ActivationId: l.ActivationId,
|
||||||
|
// IsCurrent: l.IsCurrent,
|
||||||
|
// PlanKey: l.PlanKey,
|
||||||
|
// ValidFrom: time.Unix(l.ValidFrom, 0),
|
||||||
|
// ValidUntil: time.Unix(l.ValidUntil, 0),
|
||||||
|
// Status: l.Status,
|
||||||
|
// ValidationMessage: l.ValidationMessage,
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type LicensePlan struct {
|
||||||
|
// PlanKey string `json:"planKey"`
|
||||||
|
// ValidFrom int64 `json:"validFrom"`
|
||||||
|
// ValidUntil int64 `json:"validUntil"`
|
||||||
|
// Status string `json:"status"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type Licenses struct {
|
||||||
|
// TrialStart int64 `json:"trialStart"`
|
||||||
|
// TrialEnd int64 `json:"trialEnd"`
|
||||||
|
// OnTrial bool `json:"onTrial"`
|
||||||
|
// WorkSpaceBlock bool `json:"workSpaceBlock"`
|
||||||
|
// TrialConvertedToSubscription bool `json:"trialConvertedToSubscription"`
|
||||||
|
// GracePeriodEnd int64 `json:"gracePeriodEnd"`
|
||||||
|
// Licenses []License `json:"licenses"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type SubscriptionServerResp struct {
|
||||||
|
// Status string `json:"status"`
|
||||||
|
// Data Licenses `json:"data"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type Plan struct {
|
||||||
|
// Name string `json:"name"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type LicenseDB struct {
|
||||||
|
// ID string `json:"id"`
|
||||||
|
// Key string `json:"key"`
|
||||||
|
// Data string `json:"data"`
|
||||||
|
// }
|
||||||
|
// type LicenseV3 struct {
|
||||||
|
// ID string
|
||||||
|
// Key string
|
||||||
|
// Data map[string]interface{}
|
||||||
|
// PlanName string
|
||||||
|
// Features basemodel.FeatureSet
|
||||||
|
// Status string
|
||||||
|
// IsCurrent bool
|
||||||
|
// ValidFrom int64
|
||||||
|
// ValidUntil int64
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func extractKeyFromMapStringInterface[T any](data map[string]interface{}, key string) (T, error) {
|
||||||
|
// var zeroValue T
|
||||||
|
// if val, ok := data[key]; ok {
|
||||||
|
// if value, ok := val.(T); ok {
|
||||||
|
// return value, nil
|
||||||
|
// }
|
||||||
|
// return zeroValue, fmt.Errorf("%s key is not a valid %s", key, reflect.TypeOf(zeroValue))
|
||||||
|
// }
|
||||||
|
// return zeroValue, fmt.Errorf("%s key is missing", key)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func NewLicenseV3(data map[string]interface{}) (*LicenseV3, error) {
|
||||||
|
// var features basemodel.FeatureSet
|
||||||
|
|
||||||
|
// // extract id from data
|
||||||
|
// licenseID, err := extractKeyFromMapStringInterface[string](data, "id")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// delete(data, "id")
|
||||||
|
|
||||||
|
// // extract key from data
|
||||||
|
// licenseKey, err := extractKeyFromMapStringInterface[string](data, "key")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// delete(data, "key")
|
||||||
|
|
||||||
|
// // extract status from data
|
||||||
|
// status, err := extractKeyFromMapStringInterface[string](data, "status")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// planMap, err := extractKeyFromMapStringInterface[map[string]any](data, "plan")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// planName, err := extractKeyFromMapStringInterface[string](planMap, "name")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// // if license status is invalid then default it to basic
|
||||||
|
// if status == LicenseStatusInvalid {
|
||||||
|
// planName = PlanNameBasic
|
||||||
|
// }
|
||||||
|
|
||||||
|
// featuresFromZeus := basemodel.FeatureSet{}
|
||||||
|
// if _features, ok := data["features"]; ok {
|
||||||
|
// featuresData, err := json.Marshal(_features)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, errors.Wrap(err, "failed to marshal features data")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err := json.Unmarshal(featuresData, &featuresFromZeus); err != nil {
|
||||||
|
// return nil, errors.Wrap(err, "failed to unmarshal features data")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// switch planName {
|
||||||
|
// case PlanNameTeams:
|
||||||
|
// features = append(features, ProPlan...)
|
||||||
|
// case PlanNameEnterprise:
|
||||||
|
// features = append(features, EnterprisePlan...)
|
||||||
|
// case PlanNameBasic:
|
||||||
|
// features = append(features, BasicPlan...)
|
||||||
|
// default:
|
||||||
|
// features = append(features, BasicPlan...)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if len(featuresFromZeus) > 0 {
|
||||||
|
// for _, feature := range featuresFromZeus {
|
||||||
|
// exists := false
|
||||||
|
// for i, existingFeature := range features {
|
||||||
|
// if existingFeature.Name == feature.Name {
|
||||||
|
// features[i] = feature // Replace existing feature
|
||||||
|
// exists = true
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if !exists {
|
||||||
|
// features = append(features, feature) // Append if it doesn't exist
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// data["features"] = features
|
||||||
|
|
||||||
|
// _validFrom, err := extractKeyFromMapStringInterface[float64](data, "valid_from")
|
||||||
|
// if err != nil {
|
||||||
|
// _validFrom = 0
|
||||||
|
// }
|
||||||
|
// validFrom := int64(_validFrom)
|
||||||
|
|
||||||
|
// _validUntil, err := extractKeyFromMapStringInterface[float64](data, "valid_until")
|
||||||
|
// if err != nil {
|
||||||
|
// _validUntil = 0
|
||||||
|
// }
|
||||||
|
// validUntil := int64(_validUntil)
|
||||||
|
|
||||||
|
// return &LicenseV3{
|
||||||
|
// ID: licenseID,
|
||||||
|
// Key: licenseKey,
|
||||||
|
// Data: data,
|
||||||
|
// PlanName: planName,
|
||||||
|
// Features: features,
|
||||||
|
// ValidFrom: validFrom,
|
||||||
|
// ValidUntil: validUntil,
|
||||||
|
// Status: status,
|
||||||
|
// }, nil
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func NewLicenseV3WithIDAndKey(id string, key string, data map[string]interface{}) (*LicenseV3, error) {
|
||||||
|
// licenseDataWithIdAndKey := data
|
||||||
|
// licenseDataWithIdAndKey["id"] = id
|
||||||
|
// licenseDataWithIdAndKey["key"] = key
|
||||||
|
// return NewLicenseV3(licenseDataWithIdAndKey)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func ConvertLicenseV3ToLicenseV2(l *LicenseV3) *License {
|
||||||
|
// planKeyFromPlanName, ok := MapOldPlanKeyToNewPlanName[l.PlanName]
|
||||||
|
// if !ok {
|
||||||
|
// planKeyFromPlanName = Basic
|
||||||
|
// }
|
||||||
|
// return &License{
|
||||||
|
// Key: l.Key,
|
||||||
|
// ActivationId: "",
|
||||||
|
// PlanDetails: "",
|
||||||
|
// FeatureSet: l.Features,
|
||||||
|
// ValidationMessage: "",
|
||||||
|
// IsCurrent: l.IsCurrent,
|
||||||
|
// LicensePlan: LicensePlan{
|
||||||
|
// PlanKey: planKeyFromPlanName,
|
||||||
|
// ValidFrom: l.ValidFrom,
|
||||||
|
// ValidUntil: l.ValidUntil,
|
||||||
|
// Status: l.Status},
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type CheckoutRequest struct {
|
||||||
|
// SuccessURL string `json:"url"`
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type PortalRequest struct {
|
||||||
|
// SuccessURL string `json:"url"`
|
||||||
|
// }
|
||||||
160
ee/types/licensetypes/license_test.go
Normal file
160
ee/types/licensetypes/license_test.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package licensetypes
|
||||||
|
|
||||||
|
// func TestNewLicenseV3(t *testing.T) {
|
||||||
|
// testCases := []struct {
|
||||||
|
// name string
|
||||||
|
// data []byte
|
||||||
|
// pass bool
|
||||||
|
// expected *LicenseV3
|
||||||
|
// error error
|
||||||
|
// }{
|
||||||
|
// {
|
||||||
|
// name: "Error for missing license id",
|
||||||
|
// data: []byte(`{}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("id key is missing"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for license id not being a valid string",
|
||||||
|
// data: []byte(`{"id": 10}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("id key is not a valid string"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for missing license key",
|
||||||
|
// data: []byte(`{"id":"does-not-matter"}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("key key is missing"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for invalid string license key",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key":10}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("key key is not a valid string"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for missing license status",
|
||||||
|
// data: []byte(`{"id":"does-not-matter", "key": "does-not-matter","category":"FREE"}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("status key is missing"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for invalid string license status",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key": "does-not-matter", "category":"FREE", "status":10}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("status key is not a valid string"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for missing license plan",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE"}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("plan key is missing"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for invalid json license plan",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":10}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("plan key is not a valid map[string]interface {}"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Error for invalid license plan",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{}}`),
|
||||||
|
// pass: false,
|
||||||
|
// error: errors.New("name key is missing"),
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Parse the entire license properly",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{"name":"TEAMS"},"valid_from": 1730899309,"valid_until": -1}`),
|
||||||
|
// pass: true,
|
||||||
|
// expected: &LicenseV3{
|
||||||
|
// ID: "does-not-matter",
|
||||||
|
// Key: "does-not-matter-key",
|
||||||
|
// Data: map[string]interface{}{
|
||||||
|
// "plan": map[string]interface{}{
|
||||||
|
// "name": "TEAMS",
|
||||||
|
// },
|
||||||
|
// "category": "FREE",
|
||||||
|
// "status": "ACTIVE",
|
||||||
|
// "valid_from": float64(1730899309),
|
||||||
|
// "valid_until": float64(-1),
|
||||||
|
// },
|
||||||
|
// PlanName: PlanNameTeams,
|
||||||
|
// ValidFrom: 1730899309,
|
||||||
|
// ValidUntil: -1,
|
||||||
|
// Status: "ACTIVE",
|
||||||
|
// IsCurrent: false,
|
||||||
|
// Features: model.FeatureSet{},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "Fallback to basic plan if license status is invalid",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"INVALID","plan":{"name":"TEAMS"},"valid_from": 1730899309,"valid_until": -1}`),
|
||||||
|
// pass: true,
|
||||||
|
// expected: &LicenseV3{
|
||||||
|
// ID: "does-not-matter",
|
||||||
|
// Key: "does-not-matter-key",
|
||||||
|
// Data: map[string]interface{}{
|
||||||
|
// "plan": map[string]interface{}{
|
||||||
|
// "name": "TEAMS",
|
||||||
|
// },
|
||||||
|
// "category": "FREE",
|
||||||
|
// "status": "INVALID",
|
||||||
|
// "valid_from": float64(1730899309),
|
||||||
|
// "valid_until": float64(-1),
|
||||||
|
// },
|
||||||
|
// PlanName: PlanNameBasic,
|
||||||
|
// ValidFrom: 1730899309,
|
||||||
|
// ValidUntil: -1,
|
||||||
|
// Status: "INVALID",
|
||||||
|
// IsCurrent: false,
|
||||||
|
// Features: model.FeatureSet{},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: "fallback states for validFrom and validUntil",
|
||||||
|
// data: []byte(`{"id":"does-not-matter","key":"does-not-matter-key","category":"FREE","status":"ACTIVE","plan":{"name":"TEAMS"},"valid_from":1234.456,"valid_until":5678.567}`),
|
||||||
|
// pass: true,
|
||||||
|
// expected: &LicenseV3{
|
||||||
|
// ID: "does-not-matter",
|
||||||
|
// Key: "does-not-matter-key",
|
||||||
|
// Data: map[string]interface{}{
|
||||||
|
// "plan": map[string]interface{}{
|
||||||
|
// "name": "TEAMS",
|
||||||
|
// },
|
||||||
|
// "valid_from": 1234.456,
|
||||||
|
// "valid_until": 5678.567,
|
||||||
|
// "category": "FREE",
|
||||||
|
// "status": "ACTIVE",
|
||||||
|
// },
|
||||||
|
// PlanName: PlanNameTeams,
|
||||||
|
// ValidFrom: 1234,
|
||||||
|
// ValidUntil: 5678,
|
||||||
|
// Status: "ACTIVE",
|
||||||
|
// IsCurrent: false,
|
||||||
|
// Features: model.FeatureSet{},
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for _, tc := range testCases {
|
||||||
|
// var licensePayload map[string]interface{}
|
||||||
|
// err := json.Unmarshal(tc.data, &licensePayload)
|
||||||
|
// require.NoError(t, err)
|
||||||
|
// license, err := NewLicenseV3(licensePayload)
|
||||||
|
// if license != nil {
|
||||||
|
// license.Features = make(model.FeatureSet, 0)
|
||||||
|
// delete(license.Data, "features")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if tc.pass {
|
||||||
|
// require.NoError(t, err)
|
||||||
|
// require.NotNil(t, license)
|
||||||
|
// assert.Equal(t, tc.expected, license)
|
||||||
|
// } else {
|
||||||
|
// require.Error(t, err)
|
||||||
|
// assert.EqualError(t, err, tc.error.Error())
|
||||||
|
// require.Nil(t, license)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
31
ee/zeus/config.go
Normal file
31
ee/zeus/config.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package zeus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
neturl "net/url"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This will be set via ldflags at build time.
|
||||||
|
var (
|
||||||
|
url string = "<unset>"
|
||||||
|
once sync.Once
|
||||||
|
GlobalConfig zeus.Config
|
||||||
|
)
|
||||||
|
|
||||||
|
// init initializes and validates the Zeus configuration
|
||||||
|
func init() {
|
||||||
|
once.Do(func() {
|
||||||
|
parsedURL, err := neturl.Parse(url)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("invalid zeus URL: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalConfig = zeus.Config{URL: parsedURL}
|
||||||
|
if err := GlobalConfig.Validate(); err != nil {
|
||||||
|
panic(fmt.Errorf("invalid zeus config: %w", err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
61
ee/zeus/implzeus/provider.go
Normal file
61
ee/zeus/implzeus/provider.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package implzeus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/http/client"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/metertypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Provider struct {
|
||||||
|
settings factory.ScopedProviderSettings
|
||||||
|
config zeus.Config
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProviderFactory() factory.ProviderFactory[zeus.Zeus, zeus.Config] {
|
||||||
|
return factory.NewProviderFactory(factory.MustNewName("impl"), func(ctx context.Context, providerSettings factory.ProviderSettings, config zeus.Config) (zeus.Zeus, error) {
|
||||||
|
return New(ctx, providerSettings, config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, providerSettings factory.ProviderSettings, config zeus.Config) (zeus.Zeus, error) {
|
||||||
|
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/ee/zeus/implzeus")
|
||||||
|
|
||||||
|
httpClient := client.New(
|
||||||
|
settings.Logger(),
|
||||||
|
providerSettings.TracerProvider,
|
||||||
|
providerSettings.MeterProvider,
|
||||||
|
client.WithRequestResponseLog(true),
|
||||||
|
client.WithRetryCount(3),
|
||||||
|
)
|
||||||
|
|
||||||
|
return &Provider{
|
||||||
|
settings: settings,
|
||||||
|
config: config,
|
||||||
|
client: httpClient,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetLicense(ctx context.Context, key string) (*licensetypes.License, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetCheckoutURL(ctx context.Context, key string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetPortalURL(ctx context.Context, key string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetDeployment(ctx context.Context, key string) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) PutMeters(ctx context.Context, key string, meters metertypes.Meters) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,26 +1,12 @@
|
|||||||
// keep this consistent with backend constants.go
|
// keep this consistent with backend constants.go
|
||||||
export enum FeatureKeys {
|
export enum FeatureKeys {
|
||||||
SSO = 'SSO',
|
SSO = 'SSO',
|
||||||
ENTERPRISE_PLAN = 'ENTERPRISE_PLAN',
|
|
||||||
BASIC_PLAN = 'BASIC_PLAN',
|
|
||||||
ALERT_CHANNEL_SLACK = 'ALERT_CHANNEL_SLACK',
|
|
||||||
ALERT_CHANNEL_WEBHOOK = 'ALERT_CHANNEL_WEBHOOK',
|
|
||||||
ALERT_CHANNEL_PAGERDUTY = 'ALERT_CHANNEL_PAGERDUTY',
|
|
||||||
ALERT_CHANNEL_OPSGENIE = 'ALERT_CHANNEL_OPSGENIE',
|
|
||||||
ALERT_CHANNEL_MSTEAMS = 'ALERT_CHANNEL_MSTEAMS',
|
|
||||||
CUSTOM_METRICS_FUNCTION = 'CUSTOM_METRICS_FUNCTION',
|
|
||||||
QUERY_BUILDER_PANELS = 'QUERY_BUILDER_PANELS',
|
|
||||||
QUERY_BUILDER_ALERTS = 'QUERY_BUILDER_ALERTS',
|
|
||||||
DISABLE_UPSELL = 'DISABLE_UPSELL',
|
|
||||||
USE_SPAN_METRICS = 'USE_SPAN_METRICS',
|
USE_SPAN_METRICS = 'USE_SPAN_METRICS',
|
||||||
OSS = 'OSS',
|
|
||||||
ONBOARDING = 'ONBOARDING',
|
ONBOARDING = 'ONBOARDING',
|
||||||
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
||||||
GATEWAY = 'GATEWAY',
|
GATEWAY = 'GATEWAY',
|
||||||
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
|
PREMIUM_SUPPORT = 'PREMIUM_SUPPORT',
|
||||||
QUERY_BUILDER_SEARCH_V2 = 'QUERY_BUILDER_SEARCH_V2',
|
|
||||||
ANOMALY_DETECTION = 'ANOMALY_DETECTION',
|
ANOMALY_DETECTION = 'ANOMALY_DETECTION',
|
||||||
AWS_INTEGRATION = 'AWS_INTEGRATION',
|
|
||||||
ONBOARDING_V3 = 'ONBOARDING_V3',
|
ONBOARDING_V3 = 'ONBOARDING_V3',
|
||||||
THIRD_PARTY_API = 'THIRD_PARTY_API',
|
THIRD_PARTY_API = 'THIRD_PARTY_API',
|
||||||
TRACE_FUNNELS = 'TRACE_FUNNELS',
|
TRACE_FUNNELS = 'TRACE_FUNNELS',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Form, FormInstance, Input, Select, Switch, Typography } from 'antd';
|
import { Form, FormInstance, Input, Select, Switch, Typography } from 'antd';
|
||||||
import { Store } from 'antd/lib/form/interface';
|
import { Store } from 'antd/lib/form/interface';
|
||||||
import { FeatureKeys } from 'constants/features';
|
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
ChannelType,
|
||||||
@@ -11,11 +10,8 @@ import {
|
|||||||
WebhookChannel,
|
WebhookChannel,
|
||||||
} from 'container/CreateAlertChannels/config';
|
} from 'container/CreateAlertChannels/config';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { useAppContext } from 'providers/App/App';
|
|
||||||
import { Dispatch, ReactElement, SetStateAction } from 'react';
|
import { Dispatch, ReactElement, SetStateAction } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FeatureFlagProps } from 'types/api/features/getFeaturesFlags';
|
|
||||||
import { isFeatureKeys } from 'utils/app';
|
|
||||||
|
|
||||||
import EmailSettings from './Settings/Email';
|
import EmailSettings from './Settings/Email';
|
||||||
import MsTeamsSettings from './Settings/MsTeams';
|
import MsTeamsSettings from './Settings/MsTeams';
|
||||||
@@ -39,17 +35,6 @@ function FormAlertChannels({
|
|||||||
editing = false,
|
editing = false,
|
||||||
}: FormAlertChannelsProps): JSX.Element {
|
}: FormAlertChannelsProps): JSX.Element {
|
||||||
const { t } = useTranslation('channels');
|
const { t } = useTranslation('channels');
|
||||||
const { featureFlags } = useAppContext();
|
|
||||||
|
|
||||||
const feature = `ALERT_CHANNEL_${type.toUpperCase()}`;
|
|
||||||
|
|
||||||
const featureKey = isFeatureKeys(feature)
|
|
||||||
? feature
|
|
||||||
: FeatureKeys.ALERT_CHANNEL_SLACK;
|
|
||||||
|
|
||||||
const hasFeature = featureFlags?.find(
|
|
||||||
(flag: FeatureFlagProps) => flag.name === featureKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderSettings = (): ReactElement | null => {
|
const renderSettings = (): ReactElement | null => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -146,7 +131,7 @@ function FormAlertChannels({
|
|||||||
|
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button
|
<Button
|
||||||
disabled={savingState || !hasFeature}
|
disabled={savingState}
|
||||||
loading={savingState}
|
loading={savingState}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={(): void => onSaveHandler(type)}
|
onClick={(): void => onSaveHandler(type)}
|
||||||
@@ -154,7 +139,7 @@ function FormAlertChannels({
|
|||||||
{t('button_save_channel')}
|
{t('button_save_channel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={testingState || !hasFeature}
|
disabled={testingState}
|
||||||
loading={testingState}
|
loading={testingState}
|
||||||
onClick={(): void => onTestHandler(type)}
|
onClick={(): void => onTestHandler(type)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -467,10 +467,6 @@ function FormAlertRules({
|
|||||||
panelType,
|
panelType,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isAlertAvailable =
|
|
||||||
!featureFlags?.find((flag) => flag.name === FeatureKeys.QUERY_BUILDER_ALERTS)
|
|
||||||
?.active || false;
|
|
||||||
|
|
||||||
const saveRule = useCallback(async () => {
|
const saveRule = useCallback(async () => {
|
||||||
if (!isFormValid()) {
|
if (!isFormValid()) {
|
||||||
return;
|
return;
|
||||||
@@ -689,7 +685,6 @@ function FormAlertRules({
|
|||||||
const isAlertNameMissing = !formInstance.getFieldValue('alert');
|
const isAlertNameMissing = !formInstance.getFieldValue('alert');
|
||||||
|
|
||||||
const isAlertAvailableToSave =
|
const isAlertAvailableToSave =
|
||||||
isAlertAvailable &&
|
|
||||||
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
||||||
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
alertType !== AlertTypes.METRICS_BASED_ALERT;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { WarningOutlined } from '@ant-design/icons';
|
|||||||
import { Button, Flex, Modal, Space, Typography } from 'antd';
|
import { Button, Flex, Modal, Space, Typography } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||||
import { FeatureKeys } from 'constants/features';
|
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import {
|
import {
|
||||||
initialQueriesMap,
|
initialQueriesMap,
|
||||||
@@ -27,7 +26,6 @@ import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
|||||||
import { cloneDeep, defaultTo, isEmpty, isUndefined } from 'lodash-es';
|
import { cloneDeep, defaultTo, isEmpty, isUndefined } from 'lodash-es';
|
||||||
import { Check, X } from 'lucide-react';
|
import { Check, X } from 'lucide-react';
|
||||||
import { DashboardWidgetPageParams } from 'pages/DashboardWidget';
|
import { DashboardWidgetPageParams } from 'pages/DashboardWidget';
|
||||||
import { useAppContext } from 'providers/App/App';
|
|
||||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||||
import {
|
import {
|
||||||
getNextWidgets,
|
getNextWidgets,
|
||||||
@@ -79,8 +77,6 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
|
|
||||||
const { t } = useTranslation(['dashboard']);
|
const { t } = useTranslation(['dashboard']);
|
||||||
|
|
||||||
const { featureFlags } = useAppContext();
|
|
||||||
|
|
||||||
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -566,12 +562,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isQueryBuilderActive =
|
|
||||||
!featureFlags?.find((flag) => flag.name === FeatureKeys.QUERY_BUILDER_PANELS)
|
|
||||||
?.active || false;
|
|
||||||
|
|
||||||
const isNewTraceLogsAvailable =
|
const isNewTraceLogsAvailable =
|
||||||
isQueryBuilderActive &&
|
|
||||||
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
currentQuery.queryType === EQueryType.QUERY_BUILDER &&
|
||||||
currentQuery.builder.queryData.find(
|
currentQuery.builder.queryData.find(
|
||||||
(query) => query.dataSource !== DataSource.METRICS,
|
(query) => query.dataSource !== DataSource.METRICS,
|
||||||
|
|||||||
@@ -13,11 +13,7 @@ function OrganizationSettings(): JSX.Element {
|
|||||||
const isNotSSO =
|
const isNotSSO =
|
||||||
!featureFlags?.find((flag) => flag.name === FeatureKeys.SSO)?.active || false;
|
!featureFlags?.find((flag) => flag.name === FeatureKeys.SSO)?.active || false;
|
||||||
|
|
||||||
const isNoUpSell =
|
const isAuthDomain = !isNotSSO;
|
||||||
!featureFlags?.find((flag) => flag.name === FeatureKeys.DISABLE_UPSELL)
|
|
||||||
?.active || false;
|
|
||||||
|
|
||||||
const isAuthDomain = !isNoUpSell || (isNoUpSell && !isNotSSO);
|
|
||||||
|
|
||||||
if (!org) {
|
if (!org) {
|
||||||
return <div />;
|
return <div />;
|
||||||
|
|||||||
@@ -186,76 +186,6 @@ export function getAppContextMock(
|
|||||||
usage_limit: -1,
|
usage_limit: -1,
|
||||||
route: '',
|
route: '',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: FeatureKeys.OSS,
|
|
||||||
active: false,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.DISABLE_UPSELL,
|
|
||||||
active: false,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.CUSTOM_METRICS_FUNCTION,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.QUERY_BUILDER_PANELS,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.QUERY_BUILDER_ALERTS,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.ALERT_CHANNEL_SLACK,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.ALERT_CHANNEL_WEBHOOK,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.ALERT_CHANNEL_PAGERDUTY,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.ALERT_CHANNEL_OPSGENIE,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: FeatureKeys.ALERT_CHANNEL_MSTEAMS,
|
|
||||||
active: true,
|
|
||||||
usage: 0,
|
|
||||||
usage_limit: -1,
|
|
||||||
route: '',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: FeatureKeys.USE_SPAN_METRICS,
|
name: FeatureKeys.USE_SPAN_METRICS,
|
||||||
active: false,
|
active: false,
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -181,6 +181,7 @@ require (
|
|||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||||
github.com/oklog/run v1.1.0 // indirect
|
github.com/oklog/run v1.1.0 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
|
github.com/open-feature/go-sdk v1.14.1 // indirect
|
||||||
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.111.0 // indirect
|
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.111.0 // indirect
|
||||||
github.com/paulmach/orb v0.11.1 // indirect
|
github.com/paulmach/orb v0.11.1 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -717,6 +717,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
|||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
|
github.com/open-feature/go-sdk v1.14.1 h1:jcxjCIG5Up3XkgYwWN5Y/WWfc6XobOhqrIwjyDBsoQo=
|
||||||
|
github.com/open-feature/go-sdk v1.14.1/go.mod h1:t337k0VB/t/YxJ9S0prT30ISUHwYmUd/jhUZgFcOvGg=
|
||||||
github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g=
|
github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g=
|
||||||
github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M=
|
github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M=
|
||||||
github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.111.0 h1:n1p2DedLvPEN1XEx26s1PR1PCuXTgCY4Eo+kDTq7q0s=
|
github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.111.0 h1:n1p2DedLvPEN1XEx26s1PR1PCuXTgCY4Eo+kDTq7q0s=
|
||||||
|
|||||||
1
pkg/flagger/api.go
Normal file
1
pkg/flagger/api.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package flagger
|
||||||
35
pkg/flagger/config.go
Normal file
35
pkg/flagger/config.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package flagger
|
||||||
|
|
||||||
|
import "github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
|
||||||
|
var _ factory.Config = Config{}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Provider string `json:"provider"`
|
||||||
|
Memory Memory `json:"memory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Memory struct {
|
||||||
|
Boolean Boolean `json:"boolean"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Boolean struct {
|
||||||
|
Enabled []string `json:"enabled"`
|
||||||
|
Disabled []string `json:"disabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigFactory() factory.ConfigFactory {
|
||||||
|
return factory.NewConfigFactory(factory.MustNewName("flagger"), newConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConfig() factory.Config {
|
||||||
|
return &Config{
|
||||||
|
Provider: "memory",
|
||||||
|
Memory: Memory{},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Validate() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
27
pkg/flagger/flagger.go
Normal file
27
pkg/flagger/flagger.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package flagger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/open-feature/go-sdk/openfeature"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Provider = openfeature.FeatureProvider
|
||||||
|
|
||||||
|
type FlaggerHook = openfeature.Hook
|
||||||
|
|
||||||
|
type Flagger = openfeature.IClient
|
||||||
|
|
||||||
|
type flagger struct {
|
||||||
|
*openfeature.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(provider Provider, hooks ...FlaggerHook) (Flagger, error) {
|
||||||
|
client := openfeature.NewClient("signoz")
|
||||||
|
|
||||||
|
if err := openfeature.SetNamedProviderAndWait("signoz", provider); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client.AddHooks(hooks...)
|
||||||
|
|
||||||
|
return &flagger{Client: client}, nil
|
||||||
|
}
|
||||||
241
pkg/flagger/memoryprovider/provider.go
Normal file
241
pkg/flagger/memoryprovider/provider.go
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
package memoryprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/flagger"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
|
"github.com/open-feature/go-sdk/openfeature"
|
||||||
|
)
|
||||||
|
|
||||||
|
type provider struct {
|
||||||
|
config flagger.Config
|
||||||
|
settings factory.ScopedProviderSettings
|
||||||
|
featureValues map[featuretypes.Name]featuretypes.FeatureValue
|
||||||
|
registry featuretypes.Registry
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactory(registry featuretypes.Registry) factory.ProviderFactory[flagger.Provider, flagger.Config] {
|
||||||
|
return factory.NewProviderFactory(factory.MustNewName("memory"), func(ctx context.Context, providerSettings factory.ProviderSettings, config flagger.Config) (flagger.Provider, error) {
|
||||||
|
return New(ctx, providerSettings, config, registry)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, providerSettings factory.ProviderSettings, config flagger.Config, registry featuretypes.Registry) (flagger.Provider, error) {
|
||||||
|
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/flagger/memoryprovider")
|
||||||
|
|
||||||
|
featureValues := make(map[featuretypes.Name]featuretypes.FeatureValue)
|
||||||
|
for _, flag := range config.Memory.Boolean.Enabled {
|
||||||
|
name, err := featuretypes.NewName(flag)
|
||||||
|
if err != nil {
|
||||||
|
settings.Logger().Error("invalid flag name encountered", "flag", flag, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
featureValues[name] = featuretypes.FeatureValue{
|
||||||
|
Name: name,
|
||||||
|
Variant: featuretypes.KindBooleanVariantEnabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, flag := range config.Memory.Boolean.Disabled {
|
||||||
|
name, err := featuretypes.NewName(flag)
|
||||||
|
if err != nil {
|
||||||
|
settings.Logger().Error("invalid flag name encountered", "flag", flag, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := featureValues[name]; ok {
|
||||||
|
settings.Logger().Error("flag already exists and has been enabled", "flag", flag)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
featureValues[name] = featuretypes.FeatureValue{
|
||||||
|
Name: name,
|
||||||
|
Variant: featuretypes.KindBooleanVariantDisabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &provider{
|
||||||
|
config: config,
|
||||||
|
settings: settings,
|
||||||
|
featureValues: featureValues,
|
||||||
|
registry: registry,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Metadata() openfeature.Metadata {
|
||||||
|
return openfeature.Metadata{
|
||||||
|
Name: "memory",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) BooleanEvaluation(ctx context.Context, flag string, defaultValue bool, evalCtx openfeature.FlattenedContext) openfeature.BoolResolutionDetail {
|
||||||
|
feature, detail, err := provider.registry.Get(flag)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.BoolResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if featureValue, ok := provider.featureValues[feature.Name]; ok {
|
||||||
|
value, detail, err := featuretypes.GetVariantValue[bool](feature, featureValue.Variant)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.BoolResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.BoolResolutionDetail{
|
||||||
|
Value: value,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.BoolResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
|
||||||
|
Reason: openfeature.StaticReason,
|
||||||
|
Variant: feature.DefaultVariant,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) StringEvaluation(ctx context.Context, flag string, defaultValue string, evalCtx openfeature.FlattenedContext) openfeature.StringResolutionDetail {
|
||||||
|
feature, detail, err := provider.registry.Get(flag)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.StringResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if featureValue, ok := provider.featureValues[feature.Name]; ok {
|
||||||
|
value, detail, err := featuretypes.GetVariantValue[string](feature, featureValue.Variant)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.StringResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.StringResolutionDetail{
|
||||||
|
Value: value,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.StringResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
|
||||||
|
Reason: openfeature.StaticReason,
|
||||||
|
Variant: feature.DefaultVariant,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) FloatEvaluation(ctx context.Context, flag string, defaultValue float64, evalCtx openfeature.FlattenedContext) openfeature.FloatResolutionDetail {
|
||||||
|
feature, detail, err := provider.registry.Get(flag)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.FloatResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if featureValue, ok := provider.featureValues[feature.Name]; ok {
|
||||||
|
value, detail, err := featuretypes.GetVariantValue[float64](feature, featureValue.Variant)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.FloatResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.FloatResolutionDetail{
|
||||||
|
Value: value,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.FloatResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
|
||||||
|
Reason: openfeature.StaticReason,
|
||||||
|
Variant: feature.DefaultVariant,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) IntEvaluation(ctx context.Context, flag string, defaultValue int64, evalCtx openfeature.FlattenedContext) openfeature.IntResolutionDetail {
|
||||||
|
feature, detail, err := provider.registry.Get(flag)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.IntResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if featureValue, ok := provider.featureValues[feature.Name]; ok {
|
||||||
|
value, detail, err := featuretypes.GetVariantValue[int64](feature, featureValue.Variant)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.IntResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.IntResolutionDetail{
|
||||||
|
Value: value,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.IntResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
|
||||||
|
Reason: openfeature.StaticReason,
|
||||||
|
Variant: feature.DefaultVariant,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) ObjectEvaluation(ctx context.Context, flag string, defaultValue interface{}, evalCtx openfeature.FlattenedContext) openfeature.InterfaceResolutionDetail {
|
||||||
|
feature, detail, err := provider.registry.Get(flag)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.InterfaceResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if featureValue, ok := provider.featureValues[feature.Name]; ok {
|
||||||
|
value, detail, err := featuretypes.GetVariantValue[interface{}](feature, featureValue.Variant)
|
||||||
|
if err != nil {
|
||||||
|
return openfeature.InterfaceResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.InterfaceResolutionDetail{
|
||||||
|
Value: value,
|
||||||
|
ProviderResolutionDetail: detail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return openfeature.InterfaceResolutionDetail{
|
||||||
|
Value: defaultValue,
|
||||||
|
ProviderResolutionDetail: openfeature.ProviderResolutionDetail{
|
||||||
|
Reason: openfeature.StaticReason,
|
||||||
|
Variant: feature.DefaultVariant,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Hooks() []openfeature.Hook {
|
||||||
|
return []openfeature.Hook{}
|
||||||
|
}
|
||||||
34
pkg/flagger/registry.go
Normal file
34
pkg/flagger/registry.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package flagger
|
||||||
|
|
||||||
|
import "github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
|
|
||||||
|
var (
|
||||||
|
FeatureUseTracesNewSchema = featuretypes.MustNewName("UseTracesNewSchema")
|
||||||
|
FeatureUseLogsNewSchema = featuretypes.MustNewName("UseLogsNewSchema")
|
||||||
|
)
|
||||||
|
|
||||||
|
func MustNewRegistry() featuretypes.Registry {
|
||||||
|
registry, err := featuretypes.NewRegistry(
|
||||||
|
&featuretypes.Feature{
|
||||||
|
Name: FeatureUseTracesNewSchema,
|
||||||
|
Kind: featuretypes.KindBoolean,
|
||||||
|
Description: "Use new traces schema.",
|
||||||
|
Stage: featuretypes.StageStable,
|
||||||
|
DefaultVariant: featuretypes.KindBooleanVariantDisabled,
|
||||||
|
Variants: featuretypes.NewBooleanFeatureVariants(),
|
||||||
|
},
|
||||||
|
&featuretypes.Feature{
|
||||||
|
Name: FeatureUseLogsNewSchema,
|
||||||
|
Kind: featuretypes.KindBoolean,
|
||||||
|
Description: "Use new logs schema.",
|
||||||
|
Stage: featuretypes.StageStable,
|
||||||
|
DefaultVariant: featuretypes.KindBooleanVariantDisabled,
|
||||||
|
Variants: featuretypes.NewBooleanFeatureVariants(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return registry
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ type Client struct {
|
|||||||
netc *http.Client
|
netc *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(logger *slog.Logger, tracerProvider trace.TracerProvider, meterProvider metric.MeterProvider, opts ...Option) (*Client, error) {
|
func New(logger *slog.Logger, tracerProvider trace.TracerProvider, meterProvider metric.MeterProvider, opts ...Option) *Client {
|
||||||
clientOpts := options{
|
clientOpts := options{
|
||||||
retryCount: 3,
|
retryCount: 3,
|
||||||
requestResponseLog: false,
|
requestResponseLog: false,
|
||||||
@@ -46,7 +46,7 @@ func New(logger *slog.Logger, tracerProvider trace.TracerProvider, meterProvider
|
|||||||
return &Client{
|
return &Client{
|
||||||
netc: netc,
|
netc: netc,
|
||||||
c: c,
|
c: c,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Do(request *http.Request) (*http.Response, error) {
|
func (c *Client) Do(request *http.Request) (*http.Response, error) {
|
||||||
|
|||||||
16
pkg/licensing/api.go
Normal file
16
pkg/licensing/api.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package licensing
|
||||||
|
|
||||||
|
// type API interface {
|
||||||
|
// GetLicenses(context.Context, valuer.UUID, licensetypes.GettableLicenseParams) (licensetypes.GettableLicenses, error)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func Get(){
|
||||||
|
// //
|
||||||
|
// return 501
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func Put() {
|
||||||
|
// //400 bad request
|
||||||
|
|
||||||
|
// //501
|
||||||
|
// }
|
||||||
17
pkg/licensing/config.go
Normal file
17
pkg/licensing/config.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package licensing
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Provider string `mapstructure:"provider"`
|
||||||
|
|
||||||
|
PollingConfig PollingConfig `mapstructure:"polling"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PollingConfig struct {
|
||||||
|
Interval time.Duration `mapstructure:"interval"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Validate() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
27
pkg/licensing/licensing.go
Normal file
27
pkg/licensing/licensing.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package licensing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCodeLicensingServerNotFound = errors.MustNewCode("licensing_server_not_found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Licensing interface {
|
||||||
|
factory.Service
|
||||||
|
|
||||||
|
// GetLicenses gets the licenses for the organization.
|
||||||
|
GetLicenses(context.Context, valuer.UUID, licensetypes.GettableLicenseParams) (licensetypes.GettableLicenses, error)
|
||||||
|
|
||||||
|
// GetLatestLicense gets the latest license for the organization.
|
||||||
|
GetLatestLicense(context.Context, valuer.UUID) (licensetypes.License, error)
|
||||||
|
|
||||||
|
// SetLicense sets the license for the organization.
|
||||||
|
SetLicense(context.Context, valuer.UUID, string) error
|
||||||
|
}
|
||||||
50
pkg/licensing/nooplicensing/provider.go
Normal file
50
pkg/licensing/nooplicensing/provider.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package nooplicensing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/licensing"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type provider struct {
|
||||||
|
stopC chan struct{}
|
||||||
|
license licensetypes.License
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactory() factory.ProviderFactory[licensing.Licensing, licensing.Config] {
|
||||||
|
return factory.NewProviderFactory(factory.MustNewName("noop"), func(ctx context.Context, providerSettings factory.ProviderSettings, config licensing.Config) (licensing.Licensing, error) {
|
||||||
|
return New(ctx, providerSettings, config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, providerSettings factory.ProviderSettings, config licensing.Config) (licensing.Licensing, error) {
|
||||||
|
return &provider{
|
||||||
|
stopC: make(chan struct{}),
|
||||||
|
license: licensetypes.NewNoop(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Start(ctx context.Context) error {
|
||||||
|
<-provider.stopC
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) GetLicenses(ctx context.Context, orgID valuer.UUID, params licensetypes.GettableLicenseParams) (licensetypes.GettableLicenses, error) {
|
||||||
|
return licensetypes.GettableLicenses{provider.license}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) GetLatestLicense(ctx context.Context, orgID valuer.UUID) (licensetypes.License, error) {
|
||||||
|
return provider.license, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) SetLicense(ctx context.Context, orgID valuer.UUID, key string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Stop(ctx context.Context) error {
|
||||||
|
close(provider.stopC)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
||||||
"github.com/SigNoz/signoz/pkg/cache"
|
"github.com/SigNoz/signoz/pkg/cache"
|
||||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
@@ -143,9 +144,6 @@ type ClickHouseReader struct {
|
|||||||
liveTailRefreshSeconds int
|
liveTailRefreshSeconds int
|
||||||
cluster string
|
cluster string
|
||||||
|
|
||||||
useLogsNewSchema bool
|
|
||||||
useTraceNewSchema bool
|
|
||||||
|
|
||||||
logsTableName string
|
logsTableName string
|
||||||
logsLocalTableName string
|
logsLocalTableName string
|
||||||
|
|
||||||
@@ -166,8 +164,6 @@ func NewReader(
|
|||||||
telemetryStore telemetrystore.TelemetryStore,
|
telemetryStore telemetrystore.TelemetryStore,
|
||||||
prometheus prometheus.Prometheus,
|
prometheus prometheus.Prometheus,
|
||||||
cluster string,
|
cluster string,
|
||||||
useLogsNewSchema bool,
|
|
||||||
useTraceNewSchema bool,
|
|
||||||
fluxIntervalForTraceDetail time.Duration,
|
fluxIntervalForTraceDetail time.Duration,
|
||||||
cache cache.Cache,
|
cache cache.Cache,
|
||||||
) *ClickHouseReader {
|
) *ClickHouseReader {
|
||||||
@@ -181,13 +177,13 @@ func NewReaderFromClickhouseConnection(
|
|||||||
telemetryStore telemetrystore.TelemetryStore,
|
telemetryStore telemetrystore.TelemetryStore,
|
||||||
prometheus prometheus.Prometheus,
|
prometheus prometheus.Prometheus,
|
||||||
cluster string,
|
cluster string,
|
||||||
useLogsNewSchema bool,
|
|
||||||
useTraceNewSchema bool,
|
|
||||||
fluxIntervalForTraceDetail time.Duration,
|
fluxIntervalForTraceDetail time.Duration,
|
||||||
cache cache.Cache,
|
cache cache.Cache,
|
||||||
) *ClickHouseReader {
|
) *ClickHouseReader {
|
||||||
logsTableName := options.primary.LogsTable
|
logsTableName := options.primary.LogsTable
|
||||||
logsLocalTableName := options.primary.LogsLocalTable
|
logsLocalTableName := options.primary.LogsLocalTable
|
||||||
|
|
||||||
|
useLogsNewSchema, _ := featureControl.Boolean(context.Background(), valuer.UUID{}, featuretypes.UseLogsNewSchema)
|
||||||
if useLogsNewSchema {
|
if useLogsNewSchema {
|
||||||
logsTableName = options.primary.LogsTableV2
|
logsTableName = options.primary.LogsTableV2
|
||||||
logsLocalTableName = options.primary.LogsLocalTableV2
|
logsLocalTableName = options.primary.LogsLocalTableV2
|
||||||
@@ -195,6 +191,7 @@ func NewReaderFromClickhouseConnection(
|
|||||||
|
|
||||||
traceTableName := options.primary.IndexTable
|
traceTableName := options.primary.IndexTable
|
||||||
traceLocalTableName := options.primary.LocalIndexTable
|
traceLocalTableName := options.primary.LocalIndexTable
|
||||||
|
useTraceNewSchema, _ := featureControl.Boolean(context.Background(), valuer.UUID{}, featuretypes.UseTracesNewSchema)
|
||||||
if useTraceNewSchema {
|
if useTraceNewSchema {
|
||||||
traceTableName = options.primary.TraceIndexTableV3
|
traceTableName = options.primary.TraceIndexTableV3
|
||||||
traceLocalTableName = options.primary.TraceLocalTableNameV3
|
traceLocalTableName = options.primary.TraceLocalTableNameV3
|
||||||
@@ -290,7 +287,7 @@ func (r *ClickHouseReader) GetServicesList(ctx context.Context) (*[]string, erro
|
|||||||
services := []string{}
|
services := []string{}
|
||||||
query := fmt.Sprintf(`SELECT DISTINCT serviceName FROM %s.%s WHERE toDate(timestamp) > now() - INTERVAL 1 DAY`, r.TraceDB, r.traceTableName)
|
query := fmt.Sprintf(`SELECT DISTINCT serviceName FROM %s.%s WHERE toDate(timestamp) > now() - INTERVAL 1 DAY`, r.TraceDB, r.traceTableName)
|
||||||
|
|
||||||
if r.useTraceNewSchema {
|
if ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema); ok {
|
||||||
query = fmt.Sprintf(`SELECT DISTINCT serviceName FROM %s.%s WHERE ts_bucket_start > (toUnixTimestamp(now() - INTERVAL 1 DAY) - 1800) AND toDate(timestamp) > now() - INTERVAL 1 DAY`, r.TraceDB, r.traceTableName)
|
query = fmt.Sprintf(`SELECT DISTINCT serviceName FROM %s.%s WHERE ts_bucket_start > (toUnixTimestamp(now() - INTERVAL 1 DAY) - 1800) AND toDate(timestamp) > now() - INTERVAL 1 DAY`, r.TraceDB, r.traceTableName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,7 +535,7 @@ func (r *ClickHouseReader) GetServicesV2(ctx context.Context, queryParams *model
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.GetServicesParams, skipConfig *model.SkipConfig) (*[]model.ServiceItem, *model.ApiError) {
|
func (r *ClickHouseReader) GetServices(ctx context.Context, queryParams *model.GetServicesParams, skipConfig *model.SkipConfig) (*[]model.ServiceItem, *model.ApiError) {
|
||||||
if r.useTraceNewSchema {
|
if ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema); ok {
|
||||||
return r.GetServicesV2(ctx, queryParams, skipConfig)
|
return r.GetServicesV2(ctx, queryParams, skipConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -892,7 +889,7 @@ func (r *ClickHouseReader) GetTopOperationsV2(ctx context.Context, queryParams *
|
|||||||
|
|
||||||
func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) {
|
func (r *ClickHouseReader) GetTopOperations(ctx context.Context, queryParams *model.GetTopOperationsParams) (*[]model.TopOperationsItem, *model.ApiError) {
|
||||||
|
|
||||||
if r.useTraceNewSchema {
|
if ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema); ok {
|
||||||
return r.GetTopOperationsV2(ctx, queryParams)
|
return r.GetTopOperationsV2(ctx, queryParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2680,7 +2677,7 @@ func (r *ClickHouseReader) GetSpansInLastHeartBeatInterval(ctx context.Context,
|
|||||||
var spansInLastHeartBeatInterval uint64
|
var spansInLastHeartBeatInterval uint64
|
||||||
|
|
||||||
queryStr := fmt.Sprintf("SELECT count() from %s.%s where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d));", signozTraceDBName, signozSpansTable, int(interval.Minutes()))
|
queryStr := fmt.Sprintf("SELECT count() from %s.%s where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d));", signozTraceDBName, signozSpansTable, int(interval.Minutes()))
|
||||||
if r.useTraceNewSchema {
|
if ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema); ok {
|
||||||
queryStr = fmt.Sprintf("SELECT count() from %s.%s where ts_bucket_start >= toUInt64(toUnixTimestamp(now() - toIntervalMinute(%d))) - 1800 and timestamp > toUnixTimestamp(now()-toIntervalMinute(%d));", signozTraceDBName, r.traceTableName, int(interval.Minutes()), int(interval.Minutes()))
|
queryStr = fmt.Sprintf("SELECT count() from %s.%s where ts_bucket_start >= toUInt64(toUnixTimestamp(now() - toIntervalMinute(%d))) - 1800 and timestamp > toUnixTimestamp(now()-toIntervalMinute(%d));", signozTraceDBName, r.traceTableName, int(interval.Minutes()), int(interval.Minutes()))
|
||||||
}
|
}
|
||||||
r.db.QueryRow(ctx, queryStr).Scan(&spansInLastHeartBeatInterval)
|
r.db.QueryRow(ctx, queryStr).Scan(&spansInLastHeartBeatInterval)
|
||||||
@@ -2820,7 +2817,7 @@ func (r *ClickHouseReader) GetTagsInfoInLastHeartBeatInterval(ctx context.Contex
|
|||||||
where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d))
|
where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d))
|
||||||
group by serviceName, env, language;`, r.TraceDB, r.traceTableName, int(interval.Minutes()))
|
group by serviceName, env, language;`, r.TraceDB, r.traceTableName, int(interval.Minutes()))
|
||||||
|
|
||||||
if r.useTraceNewSchema {
|
if ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema); ok {
|
||||||
queryStr = fmt.Sprintf(`select serviceName, resources_string['deployment.environment'] as env,
|
queryStr = fmt.Sprintf(`select serviceName, resources_string['deployment.environment'] as env,
|
||||||
resources_string['telemetry.sdk.language'] as language from %s.%s
|
resources_string['telemetry.sdk.language'] as language from %s.%s
|
||||||
where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d))
|
where timestamp > toUnixTimestamp(now()-toIntervalMinute(%d))
|
||||||
@@ -2925,8 +2922,9 @@ func (r *ClickHouseReader) extractSelectedAndInterestingFields(tableStatement st
|
|||||||
if overrideFieldType != "" {
|
if overrideFieldType != "" {
|
||||||
field.Type = overrideFieldType
|
field.Type = overrideFieldType
|
||||||
}
|
}
|
||||||
|
ok, _ := r.featureControl.Boolean(context.Background(), valuer.UUID{}, featuretypes.UseLogsNewSchema)
|
||||||
// all static fields are assumed to be selected as we don't allow changing them
|
// all static fields are assumed to be selected as we don't allow changing them
|
||||||
if isColumn(r.useLogsNewSchema, tableStatement, field.Type, field.Name, field.DataType) {
|
if isColumn(ok, tableStatement, field.Type, field.Name, field.DataType) {
|
||||||
response.Selected = append(response.Selected, field)
|
response.Selected = append(response.Selected, field)
|
||||||
} else {
|
} else {
|
||||||
response.Interesting = append(response.Interesting, field)
|
response.Interesting = append(response.Interesting, field)
|
||||||
@@ -3007,7 +3005,8 @@ func (r *ClickHouseReader) UpdateLogField(ctx context.Context, field *model.Upda
|
|||||||
return &model.ApiError{Err: err, Typ: model.ErrorBadData}
|
return &model.ApiError{Err: err, Typ: model.ErrorBadData}
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.useLogsNewSchema {
|
ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseLogsNewSchema)
|
||||||
|
if ok {
|
||||||
return r.UpdateLogFieldV2(ctx, field)
|
return r.UpdateLogFieldV2(ctx, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3896,6 +3895,7 @@ func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v
|
|||||||
var tagKey string
|
var tagKey string
|
||||||
var dataType string
|
var dataType string
|
||||||
var attType string
|
var attType string
|
||||||
|
ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseLogsNewSchema)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
if err := rows.Scan(&tagKey, &attType, &dataType); err != nil {
|
if err := rows.Scan(&tagKey, &attType, &dataType); err != nil {
|
||||||
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
|
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
|
||||||
@@ -3904,7 +3904,7 @@ func (r *ClickHouseReader) GetLogAggregateAttributes(ctx context.Context, req *v
|
|||||||
Key: tagKey,
|
Key: tagKey,
|
||||||
DataType: v3.AttributeKeyDataType(dataType),
|
DataType: v3.AttributeKeyDataType(dataType),
|
||||||
Type: v3.AttributeKeyType(attType),
|
Type: v3.AttributeKeyType(attType),
|
||||||
IsColumn: isColumn(r.useLogsNewSchema, statements[0].Statement, attType, tagKey, dataType),
|
IsColumn: isColumn(ok, statements[0].Statement, attType, tagKey, dataType),
|
||||||
}
|
}
|
||||||
response.AttributeKeys = append(response.AttributeKeys, key)
|
response.AttributeKeys = append(response.AttributeKeys, key)
|
||||||
}
|
}
|
||||||
@@ -3950,6 +3950,7 @@ func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.Filt
|
|||||||
var attributeKey string
|
var attributeKey string
|
||||||
var attributeDataType string
|
var attributeDataType string
|
||||||
var tagType string
|
var tagType string
|
||||||
|
ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseLogsNewSchema)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
if err := rows.Scan(&attributeKey, &tagType, &attributeDataType); err != nil {
|
if err := rows.Scan(&attributeKey, &tagType, &attributeDataType); err != nil {
|
||||||
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
|
return nil, fmt.Errorf("error while scanning rows: %s", err.Error())
|
||||||
@@ -3959,7 +3960,7 @@ func (r *ClickHouseReader) GetLogAttributeKeys(ctx context.Context, req *v3.Filt
|
|||||||
Key: attributeKey,
|
Key: attributeKey,
|
||||||
DataType: v3.AttributeKeyDataType(attributeDataType),
|
DataType: v3.AttributeKeyDataType(attributeDataType),
|
||||||
Type: v3.AttributeKeyType(tagType),
|
Type: v3.AttributeKeyType(tagType),
|
||||||
IsColumn: isColumn(r.useLogsNewSchema, statements[0].Statement, tagType, attributeKey, attributeDataType),
|
IsColumn: isColumn(ok, statements[0].Statement, tagType, attributeKey, attributeDataType),
|
||||||
}
|
}
|
||||||
|
|
||||||
response.AttributeKeys = append(response.AttributeKeys, key)
|
response.AttributeKeys = append(response.AttributeKeys, key)
|
||||||
@@ -4690,7 +4691,8 @@ func (r *ClickHouseReader) GetTraceAggregateAttributes(ctx context.Context, req
|
|||||||
}
|
}
|
||||||
|
|
||||||
fields := constants.NewStaticFieldsTraces
|
fields := constants.NewStaticFieldsTraces
|
||||||
if !r.useTraceNewSchema {
|
ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema)
|
||||||
|
if !ok {
|
||||||
fields = constants.DeprecatedStaticFieldsTraces
|
fields = constants.DeprecatedStaticFieldsTraces
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4754,7 +4756,8 @@ func (r *ClickHouseReader) GetTraceAttributeKeys(ctx context.Context, req *v3.Fi
|
|||||||
|
|
||||||
// remove this later just to have NewStaticFieldsTraces in the response
|
// remove this later just to have NewStaticFieldsTraces in the response
|
||||||
fields := constants.NewStaticFieldsTraces
|
fields := constants.NewStaticFieldsTraces
|
||||||
if !r.useTraceNewSchema {
|
ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema)
|
||||||
|
if !ok {
|
||||||
fields = constants.DeprecatedStaticFieldsTraces
|
fields = constants.DeprecatedStaticFieldsTraces
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4818,7 +4821,7 @@ func (r *ClickHouseReader) GetTraceAttributeValues(ctx context.Context, req *v3.
|
|||||||
|
|
||||||
// TODO(nitya): remove 24 hour limit in future after checking the perf/resource implications
|
// TODO(nitya): remove 24 hour limit in future after checking the perf/resource implications
|
||||||
where := "timestamp >= toDateTime64(now() - INTERVAL 48 HOUR, 9)"
|
where := "timestamp >= toDateTime64(now() - INTERVAL 48 HOUR, 9)"
|
||||||
if r.useTraceNewSchema {
|
if ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema); ok {
|
||||||
where += " AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 48 HOUR))"
|
where += " AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 48 HOUR))"
|
||||||
}
|
}
|
||||||
query = fmt.Sprintf("SELECT DISTINCT %s FROM %s.%s WHERE %s AND %s ILIKE $1 LIMIT $2", selectKey, r.TraceDB, r.traceTableName, where, filterValueColumnWhere)
|
query = fmt.Sprintf("SELECT DISTINCT %s FROM %s.%s WHERE %s AND %s ILIKE $1 LIMIT $2", selectKey, r.TraceDB, r.traceTableName, where, filterValueColumnWhere)
|
||||||
@@ -4916,7 +4919,8 @@ func (r *ClickHouseReader) GetSpanAttributeKeysV2(ctx context.Context) (map[stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *ClickHouseReader) GetSpanAttributeKeys(ctx context.Context) (map[string]v3.AttributeKey, error) {
|
func (r *ClickHouseReader) GetSpanAttributeKeys(ctx context.Context) (map[string]v3.AttributeKey, error) {
|
||||||
if r.useTraceNewSchema {
|
ok, _ := r.featureControl.Boolean(ctx, valuer.UUID{}, featuretypes.UseTracesNewSchema)
|
||||||
|
if ok {
|
||||||
return r.GetSpanAttributeKeysV2(ctx)
|
return r.GetSpanAttributeKeysV2(ctx)
|
||||||
}
|
}
|
||||||
var query string
|
var query string
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ type querier struct {
|
|||||||
fluxInterval time.Duration
|
fluxInterval time.Duration
|
||||||
|
|
||||||
builder *queryBuilder.QueryBuilder
|
builder *queryBuilder.QueryBuilder
|
||||||
|
|
||||||
// used for testing
|
// used for testing
|
||||||
// TODO(srikanthccv): remove this once we have a proper mock
|
// TODO(srikanthccv): remove this once we have a proper mock
|
||||||
testingMode bool
|
testingMode bool
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
metricsV3 "github.com/SigNoz/signoz/pkg/query-service/app/metrics/v3"
|
metricsV3 "github.com/SigNoz/signoz/pkg/query-service/app/metrics/v3"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/cache"
|
"github.com/SigNoz/signoz/pkg/query-service/cache"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/interfaces"
|
|
||||||
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
@@ -46,8 +45,7 @@ type prepareLogsQueryFunc func(start, end int64, queryType v3.QueryType, panelTy
|
|||||||
type prepareMetricQueryFunc func(start, end int64, queryType v3.QueryType, panelType v3.PanelType, bq *v3.BuilderQuery, options metricsV3.Options) (string, error)
|
type prepareMetricQueryFunc func(start, end int64, queryType v3.QueryType, panelType v3.PanelType, bq *v3.BuilderQuery, options metricsV3.Options) (string, error)
|
||||||
|
|
||||||
type QueryBuilder struct {
|
type QueryBuilder struct {
|
||||||
options QueryBuilderOptions
|
options QueryBuilderOptions
|
||||||
featureFlags interfaces.FeatureLookup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryBuilderOptions struct {
|
type QueryBuilderOptions struct {
|
||||||
|
|||||||
@@ -68,10 +68,6 @@ func UseMetricsPreAggregation() bool {
|
|||||||
return GetOrDefaultEnv("USE_METRICS_PRE_AGGREGATION", "true") == "true"
|
return GetOrDefaultEnv("USE_METRICS_PRE_AGGREGATION", "true") == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnableHostsInfraMonitoring() bool {
|
|
||||||
return GetOrDefaultEnv("ENABLE_INFRA_METRICS", "true") == "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
var KafkaSpanEval = GetOrDefaultEnv("KAFKA_SPAN_EVAL", "false")
|
var KafkaSpanEval = GetOrDefaultEnv("KAFKA_SPAN_EVAL", "false")
|
||||||
|
|
||||||
var DEFAULT_FEATURE_SET = model.FeatureSet{
|
var DEFAULT_FEATURE_SET = model.FeatureSet{
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package featureManager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/constants"
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FeatureManager struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func StartManager() *FeatureManager {
|
|
||||||
fM := &FeatureManager{}
|
|
||||||
return fM
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckFeature will be internally used by backend routines
|
|
||||||
// for feature gating
|
|
||||||
func (fm *FeatureManager) CheckFeature(featureKey string) error {
|
|
||||||
|
|
||||||
feature, err := fm.GetFeatureFlag(featureKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if feature.Active {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return model.ErrFeatureUnavailable{Key: featureKey}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFeatureFlags returns current features
|
|
||||||
func (fm *FeatureManager) GetFeatureFlags() (model.FeatureSet, error) {
|
|
||||||
features := append(constants.DEFAULT_FEATURE_SET, model.Feature{
|
|
||||||
Name: model.OSS,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
})
|
|
||||||
return features, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fm *FeatureManager) InitFeatures(req model.FeatureSet) error {
|
|
||||||
zap.L().Error("InitFeatures not implemented in OSS")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fm *FeatureManager) UpdateFeatureFlag(req model.Feature) error {
|
|
||||||
zap.L().Error("UpdateFeatureFlag not implemented in OSS")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fm *FeatureManager) GetFeatureFlag(key string) (model.Feature, error) {
|
|
||||||
features, err := fm.GetFeatureFlags()
|
|
||||||
if err != nil {
|
|
||||||
return model.Feature{}, err
|
|
||||||
}
|
|
||||||
for _, feature := range features {
|
|
||||||
if feature.Name == key {
|
|
||||||
return feature, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return model.Feature{}, model.ErrFeatureUnavailable{Key: key}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package interfaces
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/SigNoz/signoz/pkg/query-service/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FeatureLookup interface {
|
|
||||||
CheckFeature(f string) error
|
|
||||||
GetFeatureFlags() (model.FeatureSet, error)
|
|
||||||
GetFeatureFlag(f string) (model.Feature, error)
|
|
||||||
UpdateFeatureFlag(features model.Feature) error
|
|
||||||
InitFeatures(features model.FeatureSet) error
|
|
||||||
}
|
|
||||||
@@ -9,58 +9,11 @@ type Feature struct {
|
|||||||
Route string `db:"route" json:"route"`
|
Route string `db:"route" json:"route"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const CustomMetricsFunction = "CUSTOM_METRICS_FUNCTION"
|
|
||||||
const DisableUpsell = "DISABLE_UPSELL"
|
|
||||||
const OSS = "OSS"
|
|
||||||
const QueryBuilderPanels = "QUERY_BUILDER_PANELS"
|
|
||||||
const QueryBuilderAlerts = "QUERY_BUILDER_ALERTS"
|
|
||||||
const UseSpanMetrics = "USE_SPAN_METRICS"
|
const UseSpanMetrics = "USE_SPAN_METRICS"
|
||||||
const AlertChannelSlack = "ALERT_CHANNEL_SLACK"
|
|
||||||
const AlertChannelWebhook = "ALERT_CHANNEL_WEBHOOK"
|
|
||||||
const AlertChannelPagerduty = "ALERT_CHANNEL_PAGERDUTY"
|
|
||||||
const AlertChannelMsTeams = "ALERT_CHANNEL_MSTEAMS"
|
|
||||||
const AlertChannelOpsgenie = "ALERT_CHANNEL_OPSGENIE"
|
|
||||||
const AlertChannelEmail = "ALERT_CHANNEL_EMAIL"
|
|
||||||
const AnomalyDetection = "ANOMALY_DETECTION"
|
const AnomalyDetection = "ANOMALY_DETECTION"
|
||||||
const HostsInfraMonitoring = "HOSTS_INFRA_MONITORING"
|
|
||||||
const TraceFunnels = "TRACE_FUNNELS"
|
const TraceFunnels = "TRACE_FUNNELS"
|
||||||
|
|
||||||
var BasicPlan = FeatureSet{
|
var BasicPlan = FeatureSet{
|
||||||
Feature{
|
|
||||||
Name: OSS,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: DisableUpsell,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: CustomMetricsFunction,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: QueryBuilderPanels,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: QueryBuilderAlerts,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
Feature{
|
||||||
Name: UseSpanMetrics,
|
Name: UseSpanMetrics,
|
||||||
Active: false,
|
Active: false,
|
||||||
@@ -68,48 +21,6 @@ var BasicPlan = FeatureSet{
|
|||||||
UsageLimit: -1,
|
UsageLimit: -1,
|
||||||
Route: "",
|
Route: "",
|
||||||
},
|
},
|
||||||
Feature{
|
|
||||||
Name: AlertChannelSlack,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: AlertChannelWebhook,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: AlertChannelPagerduty,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: AlertChannelOpsgenie,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: AlertChannelEmail,
|
|
||||||
Active: true,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
|
||||||
Name: AlertChannelMsTeams,
|
|
||||||
Active: false,
|
|
||||||
Usage: 0,
|
|
||||||
UsageLimit: -1,
|
|
||||||
Route: "",
|
|
||||||
},
|
|
||||||
Feature{
|
Feature{
|
||||||
Name: AnomalyDetection,
|
Name: AnomalyDetection,
|
||||||
Active: false,
|
Active: false,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/alertmanager"
|
"github.com/SigNoz/signoz/pkg/alertmanager"
|
||||||
"github.com/SigNoz/signoz/pkg/cache"
|
"github.com/SigNoz/signoz/pkg/cache"
|
||||||
"github.com/SigNoz/signoz/pkg/factory"
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/featurecontrol"
|
||||||
"github.com/SigNoz/signoz/pkg/instrumentation"
|
"github.com/SigNoz/signoz/pkg/instrumentation"
|
||||||
"github.com/SigNoz/signoz/pkg/prometheus"
|
"github.com/SigNoz/signoz/pkg/prometheus"
|
||||||
"github.com/SigNoz/signoz/pkg/sqlmigration"
|
"github.com/SigNoz/signoz/pkg/sqlmigration"
|
||||||
@@ -13,6 +14,7 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||||
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
"github.com/SigNoz/signoz/pkg/telemetrystore"
|
||||||
"github.com/SigNoz/signoz/pkg/version"
|
"github.com/SigNoz/signoz/pkg/version"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
|
||||||
"github.com/SigNoz/signoz/pkg/web"
|
"github.com/SigNoz/signoz/pkg/web"
|
||||||
)
|
)
|
||||||
@@ -25,6 +27,8 @@ type SigNoz struct {
|
|||||||
TelemetryStore telemetrystore.TelemetryStore
|
TelemetryStore telemetrystore.TelemetryStore
|
||||||
Prometheus prometheus.Prometheus
|
Prometheus prometheus.Prometheus
|
||||||
Alertmanager alertmanager.Alertmanager
|
Alertmanager alertmanager.Alertmanager
|
||||||
|
Zeus zeus.Zeus
|
||||||
|
FeatureControl featurecontrol.FeatureControl
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func (migration *updatePatAndOrgDomains) Up(ctx context.Context, db *bun.DB) err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := updateOrgId(ctx, tx, "org_domains"); err != nil {
|
if err := updateOrgId(ctx, tx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ func (migration *updatePatAndOrgDomains) Down(ctx context.Context, db *bun.DB) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateOrgId(ctx context.Context, tx bun.Tx, table string) error {
|
func updateOrgId(ctx context.Context, tx bun.Tx) error {
|
||||||
if _, err := tx.NewCreateTable().
|
if _, err := tx.NewCreateTable().
|
||||||
Model(&struct {
|
Model(&struct {
|
||||||
bun.BaseModel `bun:"table:org_domains_new"`
|
bun.BaseModel `bun:"table:org_domains_new"`
|
||||||
|
|||||||
35
pkg/types/featuretypes/enum.go
Normal file
35
pkg/types/featuretypes/enum.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package featuretypes
|
||||||
|
|
||||||
|
import "github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
|
||||||
|
const (
|
||||||
|
KindBooleanVariantEnabled = "enabled"
|
||||||
|
KindBooleanVariantDisabled = "disabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
KindBoolean Kind = Kind{valuer.NewString("bool")}
|
||||||
|
KindString Kind = Kind{valuer.NewString("string")}
|
||||||
|
KindInt Kind = Kind{valuer.NewString("int")}
|
||||||
|
KindFloat Kind = Kind{valuer.NewString("float")}
|
||||||
|
KindObject Kind = Kind{valuer.NewString("object")}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Kind is the kind of the feature flag. Inspired from https://github.com/open-feature/go-sdk/blob/main/openfeature/interfaces.go
|
||||||
|
type Kind struct{ valuer.String }
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Is the feature experimental?
|
||||||
|
StageExperimental = Stage{valuer.NewString("experimental")}
|
||||||
|
|
||||||
|
// Does the feature work but is not ready for production?
|
||||||
|
StagePreview = Stage{valuer.NewString("preview")}
|
||||||
|
|
||||||
|
// Is the feature stable and ready for production?
|
||||||
|
StageStable = Stage{valuer.NewString("stable")}
|
||||||
|
|
||||||
|
// Is the feature deprecated and will be removed in the future?
|
||||||
|
StageDeprecated = Stage{valuer.NewString("deprecated")}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Stage struct{ valuer.String }
|
||||||
78
pkg/types/featuretypes/feature.go
Normal file
78
pkg/types/featuretypes/feature.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package featuretypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/open-feature/go-sdk/openfeature"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrCodeFeatureNotFound = errors.MustNewCode("feature_not_found")
|
||||||
|
ErrCodeFeatureKindMismatch = errors.MustNewCode("feature_kind_mismatch")
|
||||||
|
ErrCodeFeatureVariantNotFound = errors.MustNewCode("feature_variant_not_found")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Feature struct {
|
||||||
|
// Name is the name of the feature flag.
|
||||||
|
Name Name `json:"name"`
|
||||||
|
|
||||||
|
// Kind is the kind of the feature flag.
|
||||||
|
Kind Kind `json:"kind"`
|
||||||
|
|
||||||
|
// Description is the description of the feature flag.
|
||||||
|
Description string `json:"description"`
|
||||||
|
|
||||||
|
// Stage is the stage of the feature flag.
|
||||||
|
Stage Stage `json:"stage"`
|
||||||
|
|
||||||
|
// DefaultVariant is the default variant of the feature flag.
|
||||||
|
DefaultVariant string `json:"default_variant"`
|
||||||
|
|
||||||
|
// Variants is the variants of the feature flag.
|
||||||
|
Variants map[string]any `json:"variants"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FeatureValue struct {
|
||||||
|
// Name is the name of the feature flag.
|
||||||
|
Name Name `json:"name"`
|
||||||
|
|
||||||
|
// Value is the value of the feature flag.
|
||||||
|
Variant string `json:"variant"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBooleanFeatureVariants() map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
KindBooleanVariantEnabled: true,
|
||||||
|
KindBooleanVariantDisabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVariantValue[T any](feature *Feature, variant string) (t T, detail openfeature.ProviderResolutionDetail, err error) {
|
||||||
|
value, ok := feature.Variants[variant]
|
||||||
|
if !ok {
|
||||||
|
err = errors.Newf(errors.TypeNotFound, ErrCodeFeatureVariantNotFound, "variant %s not found for feature %s", variant, feature.Name.String())
|
||||||
|
detail = openfeature.ProviderResolutionDetail{
|
||||||
|
ResolutionError: openfeature.NewGeneralResolutionError(err.Error()),
|
||||||
|
Reason: openfeature.ErrorReason,
|
||||||
|
Variant: feature.DefaultVariant,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t, ok = value.(T)
|
||||||
|
if !ok {
|
||||||
|
err = errors.Newf(errors.TypeInvalidInput, ErrCodeFeatureKindMismatch, "variant %s has type %T, expected %T", variant, value, t)
|
||||||
|
detail = openfeature.ProviderResolutionDetail{
|
||||||
|
ResolutionError: openfeature.NewTypeMismatchResolutionError(err.Error()),
|
||||||
|
Reason: openfeature.ErrorReason,
|
||||||
|
Variant: feature.DefaultVariant,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
detail = openfeature.ProviderResolutionDetail{
|
||||||
|
Reason: openfeature.StaticReason,
|
||||||
|
Variant: variant,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
35
pkg/types/featuretypes/name.go
Normal file
35
pkg/types/featuretypes/name.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package featuretypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nameRegex = regexp.MustCompile(`^[A-Z][a-zA-Z]+$`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Name struct {
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewName(s string) (Name, error) {
|
||||||
|
if !nameRegex.MatchString(s) {
|
||||||
|
return Name{}, fmt.Errorf("invalid feature name: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Name{s: s}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustNewName(s string) Name {
|
||||||
|
name, err := NewName(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n Name) String() string {
|
||||||
|
return n.s
|
||||||
|
}
|
||||||
134
pkg/types/featuretypes/registry.go
Normal file
134
pkg/types/featuretypes/registry.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package featuretypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/open-feature/go-sdk/openfeature"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Registry interface {
|
||||||
|
// Merge merges the given registry with the current registry and returns a new registry.
|
||||||
|
// The input registry takes precedence over the current registry.
|
||||||
|
MergeOrOverride(Registry) Registry
|
||||||
|
|
||||||
|
// Get returns the feature with the given name.
|
||||||
|
Get(string) (*Feature, openfeature.ProviderResolutionDetail, error)
|
||||||
|
|
||||||
|
// List returns all the features in the registry.
|
||||||
|
List() []*Feature
|
||||||
|
}
|
||||||
|
|
||||||
|
type registry struct {
|
||||||
|
features map[Name]*Feature
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRegistry(features ...*Feature) (Registry, error) {
|
||||||
|
registry := ®istry{features: make(map[Name]*Feature)}
|
||||||
|
|
||||||
|
for _, feature := range features {
|
||||||
|
// Name must be unique
|
||||||
|
if _, ok := registry.features[feature.Name]; ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, duplicate name %q found", feature.Name.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default variant must be present in the variants
|
||||||
|
if _, ok := feature.Variants[feature.DefaultVariant]; !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, default variant %q not found in variants %v", feature.DefaultVariant, feature.Variants)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the type of the default variant and the variants match the kind of the feature
|
||||||
|
switch feature.Kind {
|
||||||
|
case KindBoolean:
|
||||||
|
if _, ok := feature.Variants[feature.DefaultVariant].(bool); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, default variant %q is not a boolean", feature.DefaultVariant)
|
||||||
|
}
|
||||||
|
for variant, value := range feature.Variants {
|
||||||
|
if _, ok := value.(bool); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, variant %q is not a boolean", variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KindString:
|
||||||
|
if _, ok := feature.Variants[feature.DefaultVariant].(string); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, default variant %q is not a string", feature.DefaultVariant)
|
||||||
|
}
|
||||||
|
for variant, value := range feature.Variants {
|
||||||
|
if _, ok := value.(string); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, variant %q is not a string", variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KindInt:
|
||||||
|
if _, ok := feature.Variants[feature.DefaultVariant].(int); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, default variant %q is not an int", feature.DefaultVariant)
|
||||||
|
}
|
||||||
|
for variant, value := range feature.Variants {
|
||||||
|
if _, ok := value.(int); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, variant %q is not an int", variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KindFloat:
|
||||||
|
if _, ok := feature.Variants[feature.DefaultVariant].(float64); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, default variant %q is not a float", feature.DefaultVariant)
|
||||||
|
}
|
||||||
|
for variant, value := range feature.Variants {
|
||||||
|
if _, ok := value.(float64); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, variant %q is not a float", variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KindObject:
|
||||||
|
if _, ok := feature.Variants[feature.DefaultVariant].(map[string]any); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, default variant %q is not an object", feature.DefaultVariant)
|
||||||
|
}
|
||||||
|
for variant, value := range feature.Variants {
|
||||||
|
if _, ok := value.(map[string]any); !ok {
|
||||||
|
return nil, fmt.Errorf("cannot build registry, variant %q is not an object", variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registry.features[feature.Name] = feature
|
||||||
|
}
|
||||||
|
|
||||||
|
return registry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (registry *registry) MergeOrOverride(other Registry) Registry {
|
||||||
|
for _, feature := range other.List() {
|
||||||
|
if _, ok := registry.features[feature.Name]; ok {
|
||||||
|
registry.features[feature.Name] = feature
|
||||||
|
}
|
||||||
|
|
||||||
|
registry.features[feature.Name] = feature
|
||||||
|
}
|
||||||
|
|
||||||
|
return registry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (registry *registry) Get(flag string) (*Feature, openfeature.ProviderResolutionDetail, error) {
|
||||||
|
name, err := NewName(flag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, openfeature.ProviderResolutionDetail{
|
||||||
|
ResolutionError: openfeature.NewFlagNotFoundResolutionError(err.Error()),
|
||||||
|
Reason: openfeature.ErrorReason,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
feature, ok := registry.features[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, openfeature.ProviderResolutionDetail{
|
||||||
|
ResolutionError: openfeature.NewFlagNotFoundResolutionError(errors.Newf(errors.TypeNotFound, ErrCodeFeatureNotFound, "feature %s not found in registry", name.String()).Error()),
|
||||||
|
Reason: openfeature.ErrorReason,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return feature, openfeature.ProviderResolutionDetail{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (registry *registry) List() []*Feature {
|
||||||
|
features := make([]*Feature, 0, len(registry.features))
|
||||||
|
for _, feature := range registry.features {
|
||||||
|
features = append(features, feature)
|
||||||
|
}
|
||||||
|
|
||||||
|
return features
|
||||||
|
}
|
||||||
73
pkg/types/licensetypes/license.go
Normal file
73
pkg/types/licensetypes/license.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package licensetypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GettableLicenseParams struct {
|
||||||
|
Active *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type GettableLicenses = []License
|
||||||
|
|
||||||
|
type License interface {
|
||||||
|
// ID returns the unique identifier for the license
|
||||||
|
ID() valuer.UUID
|
||||||
|
|
||||||
|
// OrgID returns the organization ID for the license
|
||||||
|
OrgID() valuer.UUID
|
||||||
|
|
||||||
|
// Contents returns the raw data for the license
|
||||||
|
Contents() []byte
|
||||||
|
|
||||||
|
// Key returns the key for the license
|
||||||
|
Key() string
|
||||||
|
|
||||||
|
// CreatedAt returns the creation time for the license
|
||||||
|
CreatedAt() time.Time
|
||||||
|
|
||||||
|
// UpdatedAt returns the last update time for the license
|
||||||
|
UpdatedAt() time.Time
|
||||||
|
|
||||||
|
// FeatureValues returns the feature values for the license
|
||||||
|
FeatureValues() []*featuretypes.FeatureValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGettableLicenseParams(req *http.Request) (GettableLicenseParams, error) {
|
||||||
|
params := GettableLicenseParams{
|
||||||
|
Active: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
queryValues := req.URL.Query()
|
||||||
|
|
||||||
|
if active := queryValues.Get("active"); active != "" {
|
||||||
|
activeBool, err := strconv.ParseBool(active)
|
||||||
|
if err != nil {
|
||||||
|
return GettableLicenseParams{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
params.Active = &activeBool
|
||||||
|
}
|
||||||
|
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Store interface {
|
||||||
|
// Set creates or updates a license.
|
||||||
|
Set(context.Context, License) error
|
||||||
|
|
||||||
|
// Get returns the license for the given orgID
|
||||||
|
Get(context.Context, valuer.UUID) ([]License, error)
|
||||||
|
|
||||||
|
// GetLatest returns the latest license for the given orgID
|
||||||
|
GetLatest(context.Context, valuer.UUID) (License, error)
|
||||||
|
|
||||||
|
// ListOrgs returns the list of orgs
|
||||||
|
ListOrgs(context.Context) ([]valuer.UUID, error)
|
||||||
|
}
|
||||||
42
pkg/types/licensetypes/noop.go
Normal file
42
pkg/types/licensetypes/noop.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package licensetypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/featuretypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/valuer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Noop struct{}
|
||||||
|
|
||||||
|
func NewNoop() License {
|
||||||
|
return &Noop{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Noop) ID() valuer.UUID {
|
||||||
|
return valuer.UUID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Noop) OrgID() valuer.UUID {
|
||||||
|
return valuer.UUID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Noop) Contents() []byte {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Noop) Key() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Noop) CreatedAt() time.Time {
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Noop) UpdatedAt() time.Time {
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Noop) FeatureValues() []*featuretypes.FeatureValue {
|
||||||
|
return []*featuretypes.FeatureValue{}
|
||||||
|
}
|
||||||
5
pkg/types/metertypes/meter.go
Normal file
5
pkg/types/metertypes/meter.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package metertypes
|
||||||
|
|
||||||
|
type Meter struct{}
|
||||||
|
|
||||||
|
type Meters = []*Meter
|
||||||
65
pkg/valuer/string.go
Normal file
65
pkg/valuer/string.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package valuer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Valuer = (*String)(nil)
|
||||||
|
|
||||||
|
type String struct {
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewString(val string) String {
|
||||||
|
return String{val: strings.ToLower(strings.TrimSpace(val))}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum String) IsZero() bool {
|
||||||
|
return enum.val == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum String) StringValue() string {
|
||||||
|
return enum.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum String) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(enum.StringValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum *String) UnmarshalJSON(data []byte) error {
|
||||||
|
var str string
|
||||||
|
if err := json.Unmarshal(data, &str); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*enum = NewString(str)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum String) Value() (driver.Value, error) {
|
||||||
|
return enum.StringValue(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enum *String) Scan(val interface{}) error {
|
||||||
|
if enum == nil {
|
||||||
|
return fmt.Errorf("string: (nil \"%s\")", reflect.TypeOf(enum).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if val == nil {
|
||||||
|
// Return an empty string
|
||||||
|
*enum = NewString("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
str, ok := val.(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("string: (non-string \"%s\")", reflect.TypeOf(val).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
*enum = NewString(str)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
17
pkg/zeus/config.go
Normal file
17
pkg/zeus/config.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package zeus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ factory.Config = (*Config)(nil)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
URL *url.URL `mapstructure:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) Validate() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
43
pkg/zeus/noopzeus/provider.go
Normal file
43
pkg/zeus/noopzeus/provider.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package noopzeus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/SigNoz/signoz/pkg/factory"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/metertypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/zeus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Provider struct{}
|
||||||
|
|
||||||
|
func NewProviderFactory() factory.ProviderFactory[zeus.Zeus, zeus.Config] {
|
||||||
|
return factory.NewProviderFactory(factory.MustNewName("noop"), func(ctx context.Context, providerSettings factory.ProviderSettings, config zeus.Config) (zeus.Zeus, error) {
|
||||||
|
return New(ctx, providerSettings, config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(_ context.Context, _ factory.ProviderSettings, _ zeus.Config) (zeus.Zeus, error) {
|
||||||
|
return &Provider{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetLicense(_ context.Context, _ string) (*licensetypes.License, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "this operation is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetCheckoutURL(ctx context.Context, key string) (string, error) {
|
||||||
|
return "", errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "this operation is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetPortalURL(ctx context.Context, key string) (string, error) {
|
||||||
|
return "", errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "this operation is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) GetDeployment(ctx context.Context, key string) ([]byte, error) {
|
||||||
|
return nil, errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "this operation is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) PutMeters(ctx context.Context, key string, meters metertypes.Meters) error {
|
||||||
|
return errors.New(errors.TypeUnsupported, errors.CodeUnsupported, "this operation is not supported")
|
||||||
|
}
|
||||||
25
pkg/zeus/zeus.go
Normal file
25
pkg/zeus/zeus.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package zeus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/licensetypes"
|
||||||
|
"github.com/SigNoz/signoz/pkg/types/metertypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Zeus interface {
|
||||||
|
// Returns the license for the given key.
|
||||||
|
GetLicense(context.Context, string) (licensetypes.License, error)
|
||||||
|
|
||||||
|
// Returns the checkout URL for the given license key.
|
||||||
|
GetCheckoutURL(context.Context, string) (string, error)
|
||||||
|
|
||||||
|
// Returns the portal URL for the given license key.
|
||||||
|
GetPortalURL(context.Context, string) (string, error)
|
||||||
|
|
||||||
|
// Returns the deployment for the given license key.
|
||||||
|
GetDeployment(context.Context, string) ([]byte, error)
|
||||||
|
|
||||||
|
// Puts the usage for the given license key.
|
||||||
|
PutMeters(context.Context, string, metertypes.Meters) error
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user