2022-10-06 20:13:30 +05:30
package license
import (
"context"
"sync/atomic"
"time"
"github.com/jmoiron/sqlx"
2024-11-12 01:40:10 +05:30
"github.com/pkg/errors"
2022-10-06 20:13:30 +05:30
"sync"
2022-11-09 08:30:00 +05:30
baseconstants "go.signoz.io/signoz/pkg/query-service/constants"
2025-02-17 18:16:41 +05:30
"go.signoz.io/signoz/pkg/types/authtypes"
2022-11-09 08:30:00 +05:30
2022-10-06 20:13:30 +05:30
validate "go.signoz.io/signoz/ee/query-service/integrations/signozio"
"go.signoz.io/signoz/ee/query-service/model"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
"go.signoz.io/signoz/pkg/query-service/telemetry"
"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
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-01-22 12:49:38 +05:30
func StartManager ( db * sqlx . DB , features ... basemodel . Feature ) ( * Manager , error ) {
2022-10-06 20:13:30 +05:30
if LM != nil {
return LM , nil
}
2024-12-16 15:53:23 +05:30
repo := NewLicenseRepo ( db )
2022-10-06 20:13:30 +05:30
m := & Manager {
repo : & repo ,
}
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 ( )
lm . ValidateV3 ( ctx )
for {
select {
case <- lm . done :
return
default :
select {
case <- lm . done :
return
case <- tick . C :
lm . ValidateV3 ( ctx )
}
}
}
}
func ( lm * Manager ) RefreshLicense ( ctx context . Context ) * model . ApiError {
license , apiError := validate . ValidateLicenseV3 ( lm . activeLicenseV3 . Key )
if apiError != nil {
zap . L ( ) . Error ( "failed to validate license" , zap . Error ( apiError . Err ) )
return apiError
}
err := lm . repo . UpdateLicenseV3 ( ctx , license )
if err != nil {
return model . BadRequest ( errors . Wrap ( err , "failed to update the new license" ) )
}
lm . SetActiveV3 ( license )
return nil
}
func ( lm * Manager ) ValidateV3 ( ctx context . Context ) ( reterr error ) {
zap . L ( ) . Info ( "License validation started" )
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
}
func ( lm * Manager ) ActivateV3 ( ctx context . Context , licenseKey string ) ( licenseResponse * model . LicenseV3 , errResponse * model . ApiError ) {
defer func ( ) {
if errResponse != nil {
2025-02-17 18:16:41 +05:30
claims , ok := authtypes . ClaimsFromContext ( ctx )
if ok {
2024-11-12 01:40:10 +05:30
telemetry . GetInstance ( ) . SendEvent ( telemetry . TELEMETRY_LICENSE_ACT_FAILED ,
2025-02-17 18:16:41 +05:30
map [ string ] interface { } { "err" : errResponse . Err . Error ( ) } , claims . Email , true , false )
2024-11-12 01:40:10 +05:30
}
}
} ( )
license , apiError := validate . ValidateLicenseV3 ( licenseKey )
if apiError != nil {
zap . L ( ) . Error ( "failed to get the license" , zap . Error ( apiError . Err ) )
return nil , apiError
}
// insert the new license to the sqlite db
err := lm . repo . InsertLicenseV3 ( ctx , license )
if err != nil {
zap . L ( ) . Error ( "failed to activate license" , zap . Error ( err ) )
2024-11-13 00:25:00 +05:30
return nil , err
2024-11-12 01:40:10 +05:30
}
// license is valid, activate it
lm . SetActiveV3 ( license )
return license , nil
}
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 {
return lm . repo . InitFeatures ( features )
}
func ( lm * Manager ) UpdateFeatureFlag ( feature basemodel . Feature ) error {
return lm . repo . UpdateFeature ( feature )
}
func ( lm * Manager ) GetFeatureFlag ( key string ) ( basemodel . Feature , error ) {
return lm . repo . GetFeature ( key )
2022-10-06 20:13:30 +05:30
}
// GetRepo return the license repo
func ( lm * Manager ) GetRepo ( ) * Repo {
return lm . repo
}