feat(licenses): sync between portainer and license-server (#41)
* feat(license): add sync service * feat(licenses): check license server * chore(deps): update liblicense * feat(license): revoke license if invalid * feat(license): log revokation * - removed retry logics - removed license sync logging * revert liblicense version * - remove not used field Co-authored-by: yi-portainer <yi.chen@portainer.io>
This commit is contained in:
@@ -79,6 +79,12 @@ func (service *Service) AddLicense(licenseKey string, license *liblicense.Portai
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateLicense updates a license.
|
||||
func (service *Service) UpdateLicense(licenseKey string, license *liblicense.PortainerLicense) error {
|
||||
identifier := []byte(licenseKey)
|
||||
return internal.UpdateObject(service.db, BucketName, identifier, license)
|
||||
}
|
||||
|
||||
// DeleteLicense deletes a License.
|
||||
func (service *Service) DeleteLicense(licenseKey string) error {
|
||||
identifier := []byte(licenseKey)
|
||||
|
||||
@@ -463,6 +463,11 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = licenseService.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("failed starting license service: %w", err)
|
||||
}
|
||||
|
||||
var server portainer.Server = &http.Server{
|
||||
ReverseTunnelService: reverseTunnelService,
|
||||
Status: applicationStatus,
|
||||
|
||||
+39
-4
@@ -2,22 +2,33 @@ package license
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/portainer/liblicense"
|
||||
"github.com/portainer/liblicense/master"
|
||||
"github.com/portainer/portainer/api"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/portainer/liblicense"
|
||||
"github.com/portainer/liblicense/master"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
// Service represents a service for managing portainer licenses
|
||||
type Service struct {
|
||||
repository portainer.LicenseRepository
|
||||
info *portainer.LicenseInfo
|
||||
stopSignal chan struct{}
|
||||
}
|
||||
|
||||
// NewService creates a new instance of Service
|
||||
func NewService(repository portainer.LicenseRepository) *Service {
|
||||
return &Service{repository: repository, info: nil}
|
||||
return &Service{
|
||||
repository: repository,
|
||||
info: nil,
|
||||
stopSignal: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the service
|
||||
func (service *Service) Start() error {
|
||||
return service.startSyncLoop()
|
||||
}
|
||||
|
||||
// Info returns aggregation of the information about the existing licenses
|
||||
@@ -120,6 +131,30 @@ func (service *Service) DeleteLicense(licenseKey string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// revokeLicense revokes the license
|
||||
func (service *Service) revokeLicense(licenseKey string) error {
|
||||
license, err := service.repository.License(licenseKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
license.Revoked = true
|
||||
|
||||
err = service.repository.UpdateLicense(licenseKey, license)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
licenses, err := service.Licenses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.info = aggregate(licenses)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func aggregate(licenses []liblicense.PortainerLicense) *portainer.LicenseInfo {
|
||||
nodes := 0
|
||||
var expiresAt time.Time
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package license
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/portainer/liblicense/master"
|
||||
)
|
||||
|
||||
const (
|
||||
syncInterval = 5 * time.Minute
|
||||
)
|
||||
|
||||
func (service *Service) startSyncLoop() error {
|
||||
if service.stopSignal != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := service.syncLicenses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.stopSignal = make(chan struct{})
|
||||
ticker := time.NewTicker(syncInterval)
|
||||
|
||||
go (func() {
|
||||
for {
|
||||
select {
|
||||
case <-service.stopSignal:
|
||||
ticker.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
service.syncLicenses()
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) syncLicenses() error {
|
||||
licenses, err := service.Licenses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
licensesToRemove := []string{}
|
||||
|
||||
for _, license := range licenses {
|
||||
valid, err := master.ValidateLicense(&license)
|
||||
if err != nil || !valid {
|
||||
licensesToRemove = append(licensesToRemove, license.LicenseKey)
|
||||
}
|
||||
}
|
||||
|
||||
for _, licenseKey := range licensesToRemove {
|
||||
err := service.revokeLicense(licenseKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1058,6 +1058,7 @@ type (
|
||||
Licenses() ([]liblicense.PortainerLicense, error)
|
||||
License(licenseKey string) (*liblicense.PortainerLicense, error)
|
||||
AddLicense(licenseKey string, license *liblicense.PortainerLicense) error
|
||||
UpdateLicense(licenseKey string, license *liblicense.PortainerLicense) error
|
||||
DeleteLicense(licenseKey string) error
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user