From 5e9adddb6f19a8d18d568871559495ea51b401ae Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 28 Feb 2017 16:39:25 -0500 Subject: Reload license from DB for all cluster app servers (#5525) * Reload license from DB for all cluster app servers * Increase test timeout --- Makefile | 2 +- api/license.go | 95 ++----------------------------------------- api/user_test.go | 15 ++++--- app/admin.go | 1 + app/license.go | 99 +++++++++++++++++++++++++++++++++++++++++++++ cmd/platform/init.go | 3 +- cmd/platform/license.go | 4 +- cmd/platform/oldcommands.go | 4 +- cmd/platform/server.go | 2 +- model/license.go | 5 +++ 10 files changed, 125 insertions(+), 105 deletions(-) create mode 100644 app/license.go diff --git a/Makefile b/Makefile index c99767a3a..af4302642 100644 --- a/Makefile +++ b/Makefile @@ -202,7 +202,7 @@ test-server: start-docker prepare-enterprise rm -f cover.out echo "mode: count" > cover.out - $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=650s -covermode=count -coverprofile=capi.out ./api || exit 1 + $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=1050s -covermode=count -coverprofile=capi.out ./api || exit 1 $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=650s -covermode=count -coverprofile=capi4.out ./api4 || exit 1 $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=60s -covermode=count -coverprofile=capp.out ./app || exit 1 $(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=60s -covermode=count -coverprofile=cmodel.out ./model || exit 1 diff --git a/api/license.go b/api/license.go index 41cba914d..ea5de20d4 100644 --- a/api/license.go +++ b/api/license.go @@ -7,7 +7,6 @@ import ( "bytes" "io" "net/http" - "strings" l4g "github.com/alecthomas/log4go" "github.com/mattermost/platform/app" @@ -15,11 +14,6 @@ import ( "github.com/mattermost/platform/utils" ) -const ( - EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error" - INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error" -) - func InitLicense() { l4g.Debug(utils.T("api.license.init.debug")) @@ -28,26 +22,6 @@ func InitLicense() { BaseRoutes.License.Handle("/client_config", ApiAppHandler(getClientLicenceConfig)).Methods("GET") } -func LoadLicense() { - licenseId := "" - if result := <-app.Srv.Store.System().Get(); result.Err == nil { - props := result.Data.(model.StringMap) - licenseId = props[model.SYSTEM_ACTIVE_LICENSE_ID] - } - - if len(licenseId) != 26 { - l4g.Info(utils.T("mattermost.load_license.find.warn")) - return - } - - if result := <-app.Srv.Store.License().Get(licenseId); result.Err == nil { - record := result.Data.(*model.LicenseRecord) - utils.LoadLicense([]byte(record.Bytes)) - } else { - l4g.Info(utils.T("mattermost.load_license.find.warn")) - } -} - func addLicense(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("attempt") err := r.ParseMultipartForm(*utils.Cfg.FileSettings.MaxFileSize) @@ -83,10 +57,10 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) { buf := bytes.NewBuffer(nil) io.Copy(buf, file) - if license, err := SaveLicense(buf.Bytes()); err != nil { - if err.Id == EXPIRED_LICENSE_ERROR { + if license, err := app.SaveLicense(buf.Bytes()); err != nil { + if err.Id == model.EXPIRED_LICENSE_ERROR { c.LogAudit("failed - expired or non-started license") - } else if err.Id == INVALID_LICENSE_ERROR { + } else if err.Id == model.INVALID_LICENSE_ERROR { c.LogAudit("failed - invalid license") } else { c.LogAudit("failed - unable to save license") @@ -99,56 +73,10 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) { } } -func SaveLicense(licenseBytes []byte) (*model.License, *model.AppError) { - var license *model.License - - if success, licenseStr := utils.ValidateLicense(licenseBytes); success { - license = model.LicenseFromJson(strings.NewReader(licenseStr)) - - if result := <-app.Srv.Store.User().AnalyticsUniqueUserCount(""); result.Err != nil { - return nil, model.NewLocAppError("addLicense", "api.license.add_license.invalid_count.app_error", nil, result.Err.Error()) - } else { - uniqueUserCount := result.Data.(int64) - - if uniqueUserCount > int64(*license.Features.Users) { - return nil, model.NewLocAppError("addLicense", "api.license.add_license.unique_users.app_error", map[string]interface{}{"Users": *license.Features.Users, "Count": uniqueUserCount}, "") - } - } - - if ok := utils.SetLicense(license); !ok { - return nil, model.NewLocAppError("addLicense", EXPIRED_LICENSE_ERROR, nil, "") - } - - record := &model.LicenseRecord{} - record.Id = license.Id - record.Bytes = string(licenseBytes) - rchan := app.Srv.Store.License().Save(record) - - sysVar := &model.System{} - sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID - sysVar.Value = license.Id - schan := app.Srv.Store.System().SaveOrUpdate(sysVar) - - if result := <-rchan; result.Err != nil { - RemoveLicense() - return nil, model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "err="+result.Err.Error()) - } - - if result := <-schan; result.Err != nil { - RemoveLicense() - return nil, model.NewLocAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "") - } - } else { - return nil, model.NewLocAppError("addLicense", INVALID_LICENSE_ERROR, nil, "") - } - - return license, nil -} - func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) { c.LogAudit("") - if err := RemoveLicense(); err != nil { + if err := app.RemoveLicense(); err != nil { c.Err = err return } @@ -158,21 +86,6 @@ func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(model.MapToJson(rdata))) } -func RemoveLicense() *model.AppError { - utils.RemoveLicense() - - sysVar := &model.System{} - sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID - sysVar.Value = "" - - if result := <-app.Srv.Store.System().SaveOrUpdate(sysVar); result.Err != nil { - utils.RemoveLicense() - return result.Err - } - - return nil -} - func getClientLicenceConfig(c *Context, w http.ResponseWriter, r *http.Request) { useSanitizedLicense := !app.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) diff --git a/api/user_test.go b/api/user_test.go index 68472c6d1..01784fadf 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -1823,16 +1823,19 @@ func TestUpdateMfa(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient - if utils.License.Features.MFA == nil { - utils.License.Features.MFA = new(bool) - } - + isLicensed := utils.IsLicensed + license := utils.License enableMfa := *utils.Cfg.ServiceSettings.EnableMultifactorAuthentication defer func() { - utils.IsLicensed = false - *utils.License.Features.MFA = false + utils.IsLicensed = isLicensed + utils.License = license *utils.Cfg.ServiceSettings.EnableMultifactorAuthentication = enableMfa }() + utils.IsLicensed = false + utils.License = &model.License{Features: &model.Features{}} + if utils.License.Features.MFA == nil { + utils.License.Features.MFA = new(bool) + } team := model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN} rteam, _ := Client.CreateTeam(&team) diff --git a/app/admin.go b/app/admin.go index 00d60a802..c551da50c 100644 --- a/app/admin.go +++ b/app/admin.go @@ -90,6 +90,7 @@ func InvalidateAllCachesSkipSend() { store.ClearUserCaches() store.ClearPostCaches() store.ClearWebhookCaches() + LoadLicense() } func GetConfig() *model.Config { diff --git a/app/license.go b/app/license.go new file mode 100644 index 000000000..87b2d1b05 --- /dev/null +++ b/app/license.go @@ -0,0 +1,99 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "strings" + + l4g "github.com/alecthomas/log4go" + "github.com/mattermost/platform/model" + "github.com/mattermost/platform/utils" +) + +func LoadLicense() { + utils.RemoveLicense() + + licenseId := "" + if result := <-Srv.Store.System().Get(); result.Err == nil { + props := result.Data.(model.StringMap) + licenseId = props[model.SYSTEM_ACTIVE_LICENSE_ID] + } + + if len(licenseId) != 26 { + l4g.Info(utils.T("mattermost.load_license.find.warn")) + return + } + + if result := <-Srv.Store.License().Get(licenseId); result.Err == nil { + record := result.Data.(*model.LicenseRecord) + utils.LoadLicense([]byte(record.Bytes)) + } else { + l4g.Info(utils.T("mattermost.load_license.find.warn")) + } +} + +func SaveLicense(licenseBytes []byte) (*model.License, *model.AppError) { + var license *model.License + + if success, licenseStr := utils.ValidateLicense(licenseBytes); success { + license = model.LicenseFromJson(strings.NewReader(licenseStr)) + + if result := <-Srv.Store.User().AnalyticsUniqueUserCount(""); result.Err != nil { + return nil, model.NewLocAppError("addLicense", "api.license.add_license.invalid_count.app_error", nil, result.Err.Error()) + } else { + uniqueUserCount := result.Data.(int64) + + if uniqueUserCount > int64(*license.Features.Users) { + return nil, model.NewLocAppError("addLicense", "api.license.add_license.unique_users.app_error", map[string]interface{}{"Users": *license.Features.Users, "Count": uniqueUserCount}, "") + } + } + + if ok := utils.SetLicense(license); !ok { + return nil, model.NewLocAppError("addLicense", model.EXPIRED_LICENSE_ERROR, nil, "") + } + + record := &model.LicenseRecord{} + record.Id = license.Id + record.Bytes = string(licenseBytes) + rchan := Srv.Store.License().Save(record) + + sysVar := &model.System{} + sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID + sysVar.Value = license.Id + schan := Srv.Store.System().SaveOrUpdate(sysVar) + + if result := <-rchan; result.Err != nil { + RemoveLicense() + return nil, model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "err="+result.Err.Error()) + } + + if result := <-schan; result.Err != nil { + RemoveLicense() + return nil, model.NewLocAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "") + } + } else { + return nil, model.NewLocAppError("addLicense", model.INVALID_LICENSE_ERROR, nil, "") + } + + InvalidateAllCaches() + + return license, nil +} + +func RemoveLicense() *model.AppError { + utils.RemoveLicense() + + sysVar := &model.System{} + sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID + sysVar.Value = "" + + if result := <-Srv.Store.System().SaveOrUpdate(sysVar); result.Err != nil { + utils.RemoveLicense() + return result.Err + } + + InvalidateAllCaches() + + return nil +} diff --git a/cmd/platform/init.go b/cmd/platform/init.go index eb842d302..cbfdb58ce 100644 --- a/cmd/platform/init.go +++ b/cmd/platform/init.go @@ -3,7 +3,6 @@ package main import ( "fmt" - "github.com/mattermost/platform/api" "github.com/mattermost/platform/app" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" @@ -41,6 +40,6 @@ func initDBCommandContext(configFileLocation string) { app.NewServer() app.InitStores() if model.BuildEnterpriseReady == "true" { - api.LoadLicense() + app.LoadLicense() } } diff --git a/cmd/platform/license.go b/cmd/platform/license.go index 776f62856..dedc4d306 100644 --- a/cmd/platform/license.go +++ b/cmd/platform/license.go @@ -6,7 +6,7 @@ import ( "errors" "io/ioutil" - "github.com/mattermost/platform/api" + "github.com/mattermost/platform/app" "github.com/spf13/cobra" ) @@ -40,7 +40,7 @@ func uploadLicenseCmdF(cmd *cobra.Command, args []string) error { return err } - if _, err := api.SaveLicense(fileBytes); err != nil { + if _, err := app.SaveLicense(fileBytes); err != nil { return err } diff --git a/cmd/platform/oldcommands.go b/cmd/platform/oldcommands.go index 15ebb25ba..518dcbaaf 100644 --- a/cmd/platform/oldcommands.go +++ b/cmd/platform/oldcommands.go @@ -77,7 +77,7 @@ func doLegacyCommands() { web.InitWeb() if model.BuildEnterpriseReady == "true" { - api.LoadLicense() + app.LoadLicense() } runCmds() @@ -1002,7 +1002,7 @@ func cmdUploadLicense() { flushLogAndExit(1) } - if _, err := api.SaveLicense(fileBytes); err != nil { + if _, err := app.SaveLicense(fileBytes); err != nil { l4g.Error("%v", err) flushLogAndExit(1) } else { diff --git a/cmd/platform/server.go b/cmd/platform/server.go index 00cdb1429..0cb718bcc 100644 --- a/cmd/platform/server.go +++ b/cmd/platform/server.go @@ -74,7 +74,7 @@ func runServer(configFileLocation string) { web.InitWeb() if model.BuildEnterpriseReady == "true" { - api.LoadLicense() + app.LoadLicense() } if !utils.IsLicensed && len(utils.Cfg.SqlSettings.DataSourceReplicas) > 1 { diff --git a/model/license.go b/model/license.go index 7115aa1ac..09da61eb7 100644 --- a/model/license.go +++ b/model/license.go @@ -8,6 +8,11 @@ import ( "io" ) +const ( + EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error" + INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error" +) + type LicenseRecord struct { Id string `json:"id"` CreateAt int64 `json:"create_at"` -- cgit v1.2.3-1-g7c22