diff --git a/.github/workflows/pr_verify_linked_issue.yml b/.github/workflows/pr_verify_linked_issue.yml deleted file mode 100644 index 927b46c216..0000000000 --- a/.github/workflows/pr_verify_linked_issue.yml +++ /dev/null @@ -1,19 +0,0 @@ -# This workflow will inspect a pull request to ensure there is a linked issue or a -# valid issue is mentioned in the body. If neither is present it fails the check and adds -# a comment alerting users of this missing requirement. -name: VerifyIssue - -on: - pull_request: - types: [edited, opened] - check_run: - -jobs: - verify_linked_issue: - runs-on: ubuntu-latest - name: Ensure Pull Request has a linked issue. - steps: - - name: Verify Linked Issue - uses: srikanthccv/verify-linked-issue-action@v0.71 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/ee/query-service/license/db.go b/ee/query-service/license/db.go index b663a5a2ee..4a87cfbdc9 100644 --- a/ee/query-service/license/db.go +++ b/ee/query-service/license/db.go @@ -32,19 +32,6 @@ func (r *Repo) InitDB(inputDB *sqlx.DB) error { return sqlite.InitDB(inputDB) } -func (r *Repo) GetLicenses(ctx context.Context) ([]model.License, error) { - licenses := []model.License{} - - query := "SELECT key, activationId, planDetails, validationMessage FROM licenses" - - err := r.db.Select(&licenses, query) - if err != nil { - return nil, fmt.Errorf("failed to get licenses from db: %v", err) - } - - return licenses, nil -} - func (r *Repo) GetLicensesV3(ctx context.Context) ([]*model.LicenseV3, error) { licensesData := []model.LicenseDB{} licenseV3Data := []*model.LicenseV3{} @@ -73,35 +60,6 @@ func (r *Repo) GetLicensesV3(ctx context.Context) ([]*model.LicenseV3, error) { return licenseV3Data, nil } -func (r *Repo) GetActiveLicenseV2(ctx context.Context) (*model.License, *basemodel.ApiError) { - var err error - licenses := []model.License{} - - query := "SELECT key, activationId, planDetails, validationMessage FROM licenses" - - err = r.db.Select(&licenses, query) - if err != nil { - return nil, basemodel.InternalError(fmt.Errorf("failed to get active licenses from db: %v", err)) - } - - var active *model.License - for _, l := range licenses { - l.ParsePlan() - if active == nil && - (l.ValidFrom != 0) && - (l.ValidUntil == -1 || l.ValidUntil > time.Now().Unix()) { - active = &l - } - if active != nil && - l.ValidFrom > active.ValidFrom && - (l.ValidUntil == -1 || l.ValidUntil > time.Now().Unix()) { - active = &l - } - } - - return active, nil -} - // GetActiveLicense fetches the latest active license from DB. // If the license is not present, expect a nil license and a nil error in the output. func (r *Repo) GetActiveLicense(ctx context.Context) (*model.License, *basemodel.ApiError) { @@ -156,50 +114,56 @@ func (r *Repo) GetActiveLicenseV3(ctx context.Context) (*model.LicenseV3, error) return active, nil } -// InsertLicense inserts a new license in db -func (r *Repo) InsertLicense(ctx context.Context, l *model.License) error { +// InsertLicenseV3 inserts a new license v3 in db +func (r *Repo) InsertLicenseV3(ctx context.Context, l *model.LicenseV3) *model.ApiError { - if l.Key == "" { - return fmt.Errorf("insert license failed: license key is required") + query := `INSERT INTO licenses_v3 (id, key, data) VALUES ($1, $2, $3)` + + // licsense is the entity of zeus so putting the entire license here without defining schema + licenseData, err := json.Marshal(l.Data) + if err != nil { + return &model.ApiError{Typ: basemodel.ErrorBadData, Err: err} } - query := `INSERT INTO licenses - (key, planDetails, activationId, validationmessage) - VALUES ($1, $2, $3, $4)` - - _, err := r.db.ExecContext(ctx, + _, err = r.db.ExecContext(ctx, query, + l.ID, l.Key, - l.PlanDetails, - l.ActivationId, - l.ValidationMessage) + string(licenseData), + ) if err != nil { + if sqliteErr, ok := err.(sqlite3.Error); ok { + if sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique { + zap.L().Error("error in inserting license data: ", zap.Error(sqliteErr)) + return &model.ApiError{Typ: model.ErrorConflict, Err: sqliteErr} + } + } zap.L().Error("error in inserting license data: ", zap.Error(err)) - return fmt.Errorf("failed to insert license in db: %v", err) + return &model.ApiError{Typ: basemodel.ErrorExec, Err: err} } return nil } -// UpdatePlanDetails writes new plan details to the db -func (r *Repo) UpdatePlanDetails(ctx context.Context, - key, - planDetails string) error { +// UpdateLicenseV3 updates a new license v3 in db +func (r *Repo) UpdateLicenseV3(ctx context.Context, l *model.LicenseV3) error { - if key == "" { - return fmt.Errorf("update plan details failed: license key is required") + // the key and id for the license can't change so only update the data here! + query := `UPDATE licenses_v3 SET data=$1 WHERE id=$2;` + + license, err := json.Marshal(l.Data) + if err != nil { + return fmt.Errorf("insert license failed: license marshal error") } - - query := `UPDATE licenses - SET planDetails = $1, - updatedAt = $2 - WHERE key = $3` - - _, err := r.db.ExecContext(ctx, query, planDetails, time.Now(), key) + _, err = r.db.ExecContext(ctx, + query, + license, + l.ID, + ) if err != nil { - zap.L().Error("error in updating license: ", zap.Error(err)) + zap.L().Error("error in updating license data: ", zap.Error(err)) return fmt.Errorf("failed to update license in db: %v", err) } @@ -281,59 +245,3 @@ func (r *Repo) InitFeatures(req basemodel.FeatureSet) error { } return nil } - -// InsertLicenseV3 inserts a new license v3 in db -func (r *Repo) InsertLicenseV3(ctx context.Context, l *model.LicenseV3) *model.ApiError { - - query := `INSERT INTO licenses_v3 (id, key, data) VALUES ($1, $2, $3)` - - // licsense is the entity of zeus so putting the entire license here without defining schema - licenseData, err := json.Marshal(l.Data) - if err != nil { - return &model.ApiError{Typ: basemodel.ErrorBadData, Err: err} - } - - _, err = r.db.ExecContext(ctx, - query, - l.ID, - l.Key, - string(licenseData), - ) - - if err != nil { - if sqliteErr, ok := err.(sqlite3.Error); ok { - if sqliteErr.ExtendedCode == sqlite3.ErrConstraintUnique { - zap.L().Error("error in inserting license data: ", zap.Error(sqliteErr)) - return &model.ApiError{Typ: model.ErrorConflict, Err: sqliteErr} - } - } - zap.L().Error("error in inserting license data: ", zap.Error(err)) - return &model.ApiError{Typ: basemodel.ErrorExec, Err: err} - } - - return nil -} - -// UpdateLicenseV3 updates a new license v3 in db -func (r *Repo) UpdateLicenseV3(ctx context.Context, l *model.LicenseV3) error { - - // the key and id for the license can't change so only update the data here! - query := `UPDATE licenses_v3 SET data=$1 WHERE id=$2;` - - license, err := json.Marshal(l.Data) - if err != nil { - return fmt.Errorf("insert license failed: license marshal error") - } - _, err = r.db.ExecContext(ctx, - query, - license, - l.ID, - ) - - if err != nil { - zap.L().Error("error in updating license data: ", zap.Error(err)) - return fmt.Errorf("failed to update license in db: %v", err) - } - - return nil -} diff --git a/ee/query-service/license/manager.go b/ee/query-service/license/manager.go index ccdfa2b651..f5f704006e 100644 --- a/ee/query-service/license/manager.go +++ b/ee/query-service/license/manager.go @@ -27,26 +27,19 @@ var LM *Manager var validationFrequency = 24 * 60 * time.Minute type Manager struct { - repo *Repo - mutex sync.Mutex - + repo *Repo + mutex sync.Mutex 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 - activeLicense *model.License activeLicenseV3 *model.LicenseV3 activeFeatures basemodel.FeatureSet } @@ -58,7 +51,6 @@ func StartManager(db *sqlx.DB, features ...basemodel.Feature) (*Manager, error) repo := NewLicenseRepo(db) err := repo.InitDB(db) - if err != nil { return nil, fmt.Errorf("failed to initiate license repo: %v", err) } @@ -66,10 +58,10 @@ func StartManager(db *sqlx.DB, features ...basemodel.Feature) (*Manager, error) m := &Manager{ repo: &repo, } - if err := m.start(features...); err != nil { return m, err } + LM = m return m, nil } @@ -119,6 +111,7 @@ func (lm *Manager) LoadActiveLicenseV3(features ...basemodel.Feature) error { if err != nil { return err } + if active != nil { lm.SetActiveV3(active, features...) } else { @@ -136,32 +129,6 @@ func (lm *Manager) LoadActiveLicenseV3(features ...basemodel.Feature) error { return nil } -func (lm *Manager) GetLicenses(ctx context.Context) (response []model.License, apiError *model.ApiError) { - - licenses, err := lm.repo.GetLicenses(ctx) - if err != nil { - return nil, model.InternalError(err) - } - - for _, l := range licenses { - l.ParsePlan() - - if lm.activeLicense != nil && l.Key == lm.activeLicense.Key { - l.IsCurrent = true - } - - 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 - } - - response = append(response, l) - } - - return -} - func (lm *Manager) GetLicensesV3(ctx context.Context) (response []*model.LicenseV3, apiError *model.ApiError) { licenses, err := lm.repo.GetLicensesV3(ctx) @@ -188,11 +155,11 @@ func (lm *Manager) GetLicensesV3(ctx context.Context) (response []*model.License func (lm *Manager) ValidatorV3(ctx context.Context) { zap.L().Info("ValidatorV3 started!") defer close(lm.terminated) + tick := time.NewTicker(validationFrequency) defer tick.Stop() lm.ValidateV3(ctx) - for { select { case <-lm.done: @@ -238,10 +205,27 @@ func (lm *Manager) ValidateV3(ctx context.Context) (reterr error) { lm.lastValidated = time.Now().Unix() if reterr != nil { zap.L().Error("License validation completed with error", zap.Error(reterr)) + atomic.AddUint64(&lm.failedAttempts, 1) + // 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 + } + telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_LICENSE_CHECK_FAILED, map[string]interface{}{"err": reterr.Error()}, "", true, false) } else { + // reset the failed attempts counter + atomic.StoreUint64(&lm.failedAttempts, 0) zap.L().Info("License validation completed with no errors") } diff --git a/ee/query-service/model/license.go b/ee/query-service/model/license.go index 706985f027..b96f6737a3 100644 --- a/ee/query-service/model/license.go +++ b/ee/query-service/model/license.go @@ -1,7 +1,6 @@ package model import ( - "encoding/base64" "encoding/json" "fmt" "reflect" @@ -61,37 +60,6 @@ type LicensePlan struct { Status string `json:"status"` } -func (l *License) ParsePlan() error { - l.LicensePlan = LicensePlan{} - - planData, err := base64.StdEncoding.DecodeString(l.PlanDetails) - if err != nil { - return err - } - - plan := LicensePlan{} - err = json.Unmarshal([]byte(planData), &plan) - if err != nil { - l.ValidationMessage = "failed to parse plan from license" - return errors.Wrap(err, "failed to parse plan from license") - } - - l.LicensePlan = plan - l.ParseFeatures() - return nil -} - -func (l *License) ParseFeatures() { - switch l.PlanKey { - case Pro: - l.FeatureSet = ProPlan - case Enterprise: - l.FeatureSet = EnterprisePlan - default: - l.FeatureSet = BasicPlan - } -} - type Licenses struct { TrialStart int64 `json:"trialStart"` TrialEnd int64 `json:"trialEnd"`