2022-10-06 20:13:30 +05:30
package license
import (
"context"
"sync/atomic"
"time"
"github.com/jmoiron/sqlx"
"sync"
2025-03-20 21:01:41 +05:30
baseconstants "github.com/SigNoz/signoz/pkg/query-service/constants"
2025-03-24 14:54:20 +05:30
"github.com/SigNoz/signoz/pkg/sqlstore"
2025-03-20 21:01:41 +05:30
"github.com/SigNoz/signoz/pkg/types"
2025-04-28 19:50:47 +05:30
"github.com/SigNoz/signoz/pkg/zeus"
2025-03-20 21:01:41 +05:30
validate "github.com/SigNoz/signoz/ee/query-service/integrations/signozio"
"github.com/SigNoz/signoz/ee/query-service/model"
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
"github.com/SigNoz/signoz/pkg/query-service/telemetry"
2022-10-06 20:13:30 +05:30
"go.uber.org/zap"
)
var LM * Manager
// validate and update license every 24 hours
var validationFrequency = 24 * 60 * time . Minute
type Manager struct {
2025-01-29 23:47:50 +05:30
repo * Repo
2025-04-28 19:50:47 +05:30
zeus zeus . Zeus
2025-01-29 23:47:50 +05:30
mutex sync . Mutex
2022-10-06 20:13:30 +05:30
validatorRunning bool
// end the license validation, this is important to gracefully
// stopping validation and protect in-consistent updates
done chan struct { }
// terminated waits for the validate go routine to end
terminated chan struct { }
// last time the license was validated
lastValidated int64
// keep track of validation failure attempts
failedAttempts uint64
// keep track of active license and features
2024-11-12 01:40:10 +05:30
activeLicenseV3 * model . LicenseV3
activeFeatures basemodel . FeatureSet
2022-10-06 20:13:30 +05:30
}
2025-04-28 19:50:47 +05:30
func StartManager ( db * sqlx . DB , store sqlstore . SQLStore , zeus zeus . Zeus , features ... basemodel . Feature ) ( * Manager , error ) {
2022-10-06 20:13:30 +05:30
if LM != nil {
return LM , nil
}
2025-03-24 14:54:20 +05:30
repo := NewLicenseRepo ( db , store )
2022-10-06 20:13:30 +05:30
m := & Manager {
repo : & repo ,
2025-04-28 19:50:47 +05:30
zeus : zeus ,
2022-10-06 20:13:30 +05:30
}
2024-12-16 15:53:23 +05:30
if err := m . start ( features ... ) ; err != nil {
2022-10-06 20:13:30 +05:30
return m , err
}
2025-01-29 23:47:50 +05:30
2022-10-06 20:13:30 +05:30
LM = m
return m , nil
}
// start loads active license in memory and initiates validator
2024-12-16 15:53:23 +05:30
func ( lm * Manager ) start ( features ... basemodel . Feature ) error {
return lm . LoadActiveLicenseV3 ( features ... )
2022-10-06 20:13:30 +05:30
}
func ( lm * Manager ) Stop ( ) {
close ( lm . done )
<- lm . terminated
}
2024-11-12 01:40:10 +05:30
func ( lm * Manager ) SetActiveV3 ( l * model . LicenseV3 , features ... basemodel . Feature ) {
lm . mutex . Lock ( )
defer lm . mutex . Unlock ( )
if l == nil {
return
}
lm . activeLicenseV3 = l
lm . activeFeatures = append ( l . Features , features ... )
// set default features
setDefaultFeatures ( lm )
err := lm . InitFeatures ( lm . activeFeatures )
if err != nil {
zap . L ( ) . Panic ( "Couldn't activate features" , zap . Error ( err ) )
}
if ! lm . validatorRunning {
// we want to make sure only one validator runs,
// we already have lock() so good to go
lm . validatorRunning = true
go lm . ValidatorV3 ( context . Background ( ) )
}
2022-10-06 20:13:30 +05:30
}
2022-11-09 08:30:00 +05:30
func setDefaultFeatures ( lm * Manager ) {
2023-05-17 16:10:43 +05:30
lm . activeFeatures = append ( lm . activeFeatures , baseconstants . DEFAULT_FEATURE_SET ... )
2022-11-09 08:30:00 +05:30
}
2024-11-12 01:40:10 +05:30
func ( lm * Manager ) LoadActiveLicenseV3 ( features ... basemodel . Feature ) error {
active , err := lm . repo . GetActiveLicenseV3 ( context . Background ( ) )
if err != nil {
return err
}
2025-01-29 23:47:50 +05:30
2024-11-12 01:40:10 +05:30
if active != nil {
lm . SetActiveV3 ( active , features ... )
} else {
zap . L ( ) . Info ( "No active license found, defaulting to basic plan" )
// if no active license is found, we default to basic(free) plan with all default features
lm . activeFeatures = model . BasicPlan
setDefaultFeatures ( lm )
err := lm . InitFeatures ( lm . activeFeatures )
if err != nil {
zap . L ( ) . Error ( "Couldn't initialize features" , zap . Error ( err ) )
return err
}
}
return nil
}
func ( lm * Manager ) GetLicensesV3 ( ctx context . Context ) ( response [ ] * model . LicenseV3 , apiError * model . ApiError ) {
licenses , err := lm . repo . GetLicensesV3 ( ctx )
if err != nil {
return nil , model . InternalError ( err )
}
for _ , l := range licenses {
if lm . activeLicenseV3 != nil && l . Key == lm . activeLicenseV3 . Key {
l . IsCurrent = true
}
2024-11-19 19:10:59 +05:30
if l . ValidUntil == - 1 {
// for subscriptions, there is no end-date as such
// but for showing user some validity we default one year timespan
l . ValidUntil = l . ValidFrom + 31556926
}
2024-11-12 01:40:10 +05:30
response = append ( response , l )
}
return response , nil
}
// Validator validates license after an epoch of time
func ( lm * Manager ) ValidatorV3 ( ctx context . Context ) {
2024-11-25 23:51:45 +05:30
zap . L ( ) . Info ( "ValidatorV3 started!" )
2024-11-12 01:40:10 +05:30
defer close ( lm . terminated )
2025-01-29 23:47:50 +05:30
2024-11-12 01:40:10 +05:30
tick := time . NewTicker ( validationFrequency )
defer tick . Stop ( )
2025-03-18 14:01:48 +05:30
_ = lm . ValidateV3 ( ctx )
2024-11-12 01:40:10 +05:30
for {
select {
case <- lm . done :
return
default :
select {
case <- lm . done :
return
case <- tick . C :
2025-03-18 14:01:48 +05:30
_ = lm . ValidateV3 ( ctx )
2024-11-12 01:40:10 +05:30
}
}
}
}
2025-04-28 19:50:47 +05:30
func ( lm * Manager ) RefreshLicense ( ctx context . Context ) error {
license , err := validate . ValidateLicenseV3 ( ctx , lm . activeLicenseV3 . Key , lm . zeus )
if err != nil {
return err
2024-11-12 01:40:10 +05:30
}
2025-04-28 19:50:47 +05:30
err = lm . repo . UpdateLicenseV3 ( ctx , license )
2024-11-12 01:40:10 +05:30
if err != nil {
2025-04-28 19:50:47 +05:30
return err
2024-11-12 01:40:10 +05:30
}
lm . SetActiveV3 ( license )
return nil
}
func ( lm * Manager ) ValidateV3 ( ctx context . Context ) ( reterr error ) {
if lm . activeLicenseV3 == nil {
return nil
}
defer func ( ) {
lm . mutex . Lock ( )
lm . lastValidated = time . Now ( ) . Unix ( )
if reterr != nil {
zap . L ( ) . Error ( "License validation completed with error" , zap . Error ( reterr ) )
2025-01-29 23:47:50 +05:30
2024-11-12 01:40:10 +05:30
atomic . AddUint64 ( & lm . failedAttempts , 1 )
2025-01-29 23:47:50 +05:30
// default to basic plan if validation fails for three consecutive times
if atomic . LoadUint64 ( & lm . failedAttempts ) > 3 {
zap . L ( ) . Error ( "License validation completed with error for three consecutive times, defaulting to basic plan" , zap . String ( "license_id" , lm . activeLicenseV3 . ID ) , zap . Bool ( "license_validation" , false ) )
lm . activeLicenseV3 = nil
lm . activeFeatures = model . BasicPlan
setDefaultFeatures ( lm )
err := lm . InitFeatures ( lm . activeFeatures )
if err != nil {
zap . L ( ) . Error ( "Couldn't initialize features" , zap . Error ( err ) )
}
lm . done <- struct { } { }
lm . validatorRunning = false
}
2024-11-12 01:40:10 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_LICENSE_CHECK_FAILED ,
map [ string ] interface { } { "err" : reterr . Error ( ) } , "" , true , false )
} else {
2025-01-29 23:47:50 +05:30
// reset the failed attempts counter
atomic . StoreUint64 ( & lm . failedAttempts , 0 )
2024-11-12 01:40:10 +05:30
zap . L ( ) . Info ( "License validation completed with no errors" )
}
lm . mutex . Unlock ( )
} ( )
err := lm . RefreshLicense ( ctx )
if err != nil {
return err
}
return nil
}
2025-04-28 19:50:47 +05:30
func ( lm * Manager ) ActivateV3 ( ctx context . Context , licenseKey string ) ( * model . LicenseV3 , error ) {
license , err := validate . ValidateLicenseV3 ( ctx , licenseKey , lm . zeus )
if err != nil {
return nil , err
2024-11-12 01:40:10 +05:30
}
// insert the new license to the sqlite db
2025-04-28 19:50:47 +05:30
modelErr := lm . repo . InsertLicenseV3 ( ctx , license )
if modelErr != nil {
zap . L ( ) . Error ( "failed to activate license" , zap . Error ( modelErr ) )
return nil , modelErr
2024-11-12 01:40:10 +05:30
}
// license is valid, activate it
lm . SetActiveV3 ( license )
return license , nil
}
2025-03-17 15:22:04 +05:30
func ( lm * Manager ) GetActiveLicense ( ) * model . LicenseV3 {
return lm . activeLicenseV3
}
2022-10-06 20:13:30 +05:30
// CheckFeature will be internally used by backend routines
// for feature gating
func ( lm * Manager ) CheckFeature ( featureKey string ) error {
2023-05-17 16:10:43 +05:30
feature , err := lm . repo . GetFeature ( featureKey )
if err != nil {
return err
}
if feature . Active {
return nil
2022-10-06 20:13:30 +05:30
}
return basemodel . ErrFeatureUnavailable { Key : featureKey }
}
// GetFeatureFlags returns current active features
2023-05-17 16:10:43 +05:30
func ( lm * Manager ) GetFeatureFlags ( ) ( basemodel . FeatureSet , error ) {
return lm . repo . GetAllFeatures ( )
}
func ( lm * Manager ) InitFeatures ( features basemodel . FeatureSet ) error {
2025-02-18 11:08:09 +05:30
featureStatus := make ( [ ] types . FeatureStatus , len ( features ) )
for i , f := range features {
featureStatus [ i ] = types . FeatureStatus {
Name : f . Name ,
Active : f . Active ,
Usage : int ( f . Usage ) ,
UsageLimit : int ( f . UsageLimit ) ,
Route : f . Route ,
}
}
return lm . repo . InitFeatures ( featureStatus )
2023-05-17 16:10:43 +05:30
}
func ( lm * Manager ) UpdateFeatureFlag ( feature basemodel . Feature ) error {
2025-02-18 11:08:09 +05:30
return lm . repo . UpdateFeature ( types . FeatureStatus {
Name : feature . Name ,
Active : feature . Active ,
Usage : int ( feature . Usage ) ,
UsageLimit : int ( feature . UsageLimit ) ,
Route : feature . Route ,
} )
2023-05-17 16:10:43 +05:30
}
func ( lm * Manager ) GetFeatureFlag ( key string ) ( basemodel . Feature , error ) {
2025-02-18 11:08:09 +05:30
featureStatus , err := lm . repo . GetFeature ( key )
if err != nil {
return basemodel . Feature { } , err
}
return basemodel . Feature {
Name : featureStatus . Name ,
Active : featureStatus . Active ,
Usage : int64 ( featureStatus . Usage ) ,
UsageLimit : int64 ( featureStatus . UsageLimit ) ,
Route : featureStatus . Route ,
} , nil
2022-10-06 20:13:30 +05:30
}
// GetRepo return the license repo
func ( lm * Manager ) GetRepo ( ) * Repo {
return lm . repo
}