mirror of https://github.com/portainer/portainer
fix(extensions): fix an issue with extensions with expired licenses (#2628)
* fix(extensions): fix an issue with extensions with expired licenses * fix(api): fix invalid log call * fix(api): allow to re-enable an extensionfix2626-endpoint-management-cmd-line
parent
62eb47b3cb
commit
54163e3b92
|
@ -139,6 +139,7 @@ func (store *Store) MigrateData() error {
|
||||||
DatabaseVersion: version,
|
DatabaseVersion: version,
|
||||||
EndpointGroupService: store.EndpointGroupService,
|
EndpointGroupService: store.EndpointGroupService,
|
||||||
EndpointService: store.EndpointService,
|
EndpointService: store.EndpointService,
|
||||||
|
ExtensionService: store.ExtensionService,
|
||||||
ResourceControlService: store.ResourceControlService,
|
ResourceControlService: store.ResourceControlService,
|
||||||
SettingsService: store.SettingsService,
|
SettingsService: store.SettingsService,
|
||||||
StackService: store.StackService,
|
StackService: store.StackService,
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package migrator
|
||||||
|
|
||||||
|
func (m *Migrator) updateExtensionsToDBVersion17() error {
|
||||||
|
legacyExtensions, err := m.extensionService.Extensions()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, extension := range legacyExtensions {
|
||||||
|
extension.License.Valid = true
|
||||||
|
|
||||||
|
err = m.extensionService.Persist(&extension)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/portainer/portainer"
|
"github.com/portainer/portainer"
|
||||||
"github.com/portainer/portainer/bolt/endpoint"
|
"github.com/portainer/portainer/bolt/endpoint"
|
||||||
"github.com/portainer/portainer/bolt/endpointgroup"
|
"github.com/portainer/portainer/bolt/endpointgroup"
|
||||||
|
"github.com/portainer/portainer/bolt/extension"
|
||||||
"github.com/portainer/portainer/bolt/resourcecontrol"
|
"github.com/portainer/portainer/bolt/resourcecontrol"
|
||||||
"github.com/portainer/portainer/bolt/settings"
|
"github.com/portainer/portainer/bolt/settings"
|
||||||
"github.com/portainer/portainer/bolt/stack"
|
"github.com/portainer/portainer/bolt/stack"
|
||||||
|
@ -20,6 +21,7 @@ type (
|
||||||
db *bolt.DB
|
db *bolt.DB
|
||||||
endpointGroupService *endpointgroup.Service
|
endpointGroupService *endpointgroup.Service
|
||||||
endpointService *endpoint.Service
|
endpointService *endpoint.Service
|
||||||
|
extensionService *extension.Service
|
||||||
resourceControlService *resourcecontrol.Service
|
resourceControlService *resourcecontrol.Service
|
||||||
settingsService *settings.Service
|
settingsService *settings.Service
|
||||||
stackService *stack.Service
|
stackService *stack.Service
|
||||||
|
@ -35,6 +37,7 @@ type (
|
||||||
DatabaseVersion int
|
DatabaseVersion int
|
||||||
EndpointGroupService *endpointgroup.Service
|
EndpointGroupService *endpointgroup.Service
|
||||||
EndpointService *endpoint.Service
|
EndpointService *endpoint.Service
|
||||||
|
ExtensionService *extension.Service
|
||||||
ResourceControlService *resourcecontrol.Service
|
ResourceControlService *resourcecontrol.Service
|
||||||
SettingsService *settings.Service
|
SettingsService *settings.Service
|
||||||
StackService *stack.Service
|
StackService *stack.Service
|
||||||
|
@ -52,6 +55,7 @@ func NewMigrator(parameters *Parameters) *Migrator {
|
||||||
currentDBVersion: parameters.DatabaseVersion,
|
currentDBVersion: parameters.DatabaseVersion,
|
||||||
endpointGroupService: parameters.EndpointGroupService,
|
endpointGroupService: parameters.EndpointGroupService,
|
||||||
endpointService: parameters.EndpointService,
|
endpointService: parameters.EndpointService,
|
||||||
|
extensionService: parameters.ExtensionService,
|
||||||
resourceControlService: parameters.ResourceControlService,
|
resourceControlService: parameters.ResourceControlService,
|
||||||
settingsService: parameters.SettingsService,
|
settingsService: parameters.SettingsService,
|
||||||
templateService: parameters.TemplateService,
|
templateService: parameters.TemplateService,
|
||||||
|
@ -210,5 +214,12 @@ func (m *Migrator) Migrate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.currentDBVersion < 17 {
|
||||||
|
err := m.updateExtensionsToDBVersion17()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return m.versionService.StoreDBVersion(portainer.DBVersion)
|
return m.versionService.StoreDBVersion(portainer.DBVersion)
|
||||||
}
|
}
|
||||||
|
|
|
@ -486,7 +486,10 @@ func initExtensionManager(fileService portainer.FileService, extensionService po
|
||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
err := extensionManager.EnableExtension(&extension, extension.License.LicenseKey)
|
err := extensionManager.EnableExtension(&extension, extension.License.LicenseKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
log.Printf("Unable to enable extension: %s [extension: %s]", err.Error(), extension.Name)
|
||||||
|
extension.Enabled = false
|
||||||
|
extension.License.Valid = false
|
||||||
|
extensionService.Persist(&extension)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ func (manager *ExtensionManager) EnableExtension(extension *portainer.Extension,
|
||||||
LicenseKey: licenseKey,
|
LicenseKey: licenseKey,
|
||||||
Company: licenseDetails[0],
|
Company: licenseDetails[0],
|
||||||
Expiration: licenseDetails[1],
|
Expiration: licenseDetails[1],
|
||||||
|
Valid: true,
|
||||||
}
|
}
|
||||||
extension.Version = licenseDetails[2]
|
extension.Version = licenseDetails[2]
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (handler *Handler) extensionCreate(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, existingExtension := range extensions {
|
for _, existingExtension := range extensions {
|
||||||
if existingExtension.ID == extensionID {
|
if existingExtension.ID == extensionID && existingExtension.Enabled {
|
||||||
return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", portainer.ErrExtensionAlreadyEnabled}
|
return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", portainer.ErrExtensionAlreadyEnabled}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ func associateExtensionData(definition *portainer.Extension, extensions []portai
|
||||||
definition.Enabled = extension.Enabled
|
definition.Enabled = extension.Enabled
|
||||||
definition.License.Company = extension.License.Company
|
definition.License.Company = extension.License.Company
|
||||||
definition.License.Expiration = extension.License.Expiration
|
definition.License.Expiration = extension.License.Expiration
|
||||||
|
definition.License.Valid = extension.License.Valid
|
||||||
|
|
||||||
definitionVersion := semver.New(definition.Version)
|
definitionVersion := semver.New(definition.Version)
|
||||||
extensionVersion := semver.New(extension.Version)
|
extensionVersion := semver.New(extension.Version)
|
||||||
|
|
|
@ -503,6 +503,7 @@ type (
|
||||||
LicenseKey string `json:"LicenseKey,omitempty"`
|
LicenseKey string `json:"LicenseKey,omitempty"`
|
||||||
Company string `json:"Company,omitempty"`
|
Company string `json:"Company,omitempty"`
|
||||||
Expiration string `json:"Expiration,omitempty"`
|
Expiration string `json:"Expiration,omitempty"`
|
||||||
|
Valid bool `json:"Valid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLIService represents a service for managing CLI
|
// CLIService represents a service for managing CLI
|
||||||
|
@ -780,7 +781,7 @@ const (
|
||||||
// APIVersion is the version number of the Portainer API
|
// APIVersion is the version number of the Portainer API
|
||||||
APIVersion = "1.20.0"
|
APIVersion = "1.20.0"
|
||||||
// DBVersion is the version number of the Portainer database
|
// DBVersion is the version number of the Portainer database
|
||||||
DBVersion = 16
|
DBVersion = 17
|
||||||
// AssetsServerURL represents the URL of the Portainer asset server
|
// AssetsServerURL represents the URL of the Portainer asset server
|
||||||
AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com"
|
AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com"
|
||||||
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
|
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<span class="label label-primary" ng-if="!$ctrl.model.Enabled && !$ctrl.model.Available">coming soon</span>
|
<span class="label label-primary" ng-if="!$ctrl.model.Enabled && !$ctrl.model.Available">coming soon</span>
|
||||||
<span class="label label-warning" ng-if="!$ctrl.model.Enabled && $ctrl.model.Deal">deal</span>
|
<span class="label label-warning" ng-if="!$ctrl.model.Enabled && $ctrl.model.Deal && !$ctrl.model.License.Expiration">deal</span>
|
||||||
<span class="label label-danger" ng-if="$ctrl.model.Enabled && $ctrl.model.Expired">expired</span>
|
<span class="label label-danger" ng-if="!$ctrl.model.Enabled && $ctrl.model.License.Expiration && !$ctrl.model.License.Valid">expired</span>
|
||||||
<span class="label label-success" ng-if="$ctrl.model.Enabled && !$ctrl.model.Expired">enabled</span>
|
<span class="label label-success" ng-if="$ctrl.model.Enabled && $ctrl.model.License.Valid">enabled</span>
|
||||||
<span class="label label-primary" ng-if="$ctrl.model.Enabled && $ctrl.model.UpdateAvailable && !$ctrl.model.Expired">update available</span>
|
<span class="label label-primary" ng-if="$ctrl.model.Enabled && $ctrl.model.License.Valid && $ctrl.model.UpdateAvailable">update available</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- !blocklist-item-line1 -->
|
<!-- !blocklist-item-line1 -->
|
||||||
|
|
|
@ -3,7 +3,6 @@ angular.module('portainer.app')
|
||||||
function($state) {
|
function($state) {
|
||||||
|
|
||||||
var ctrl = this;
|
var ctrl = this;
|
||||||
ctrl.$onInit = $onInit;
|
|
||||||
ctrl.goToExtensionView = goToExtensionView;
|
ctrl.goToExtensionView = goToExtensionView;
|
||||||
|
|
||||||
function goToExtensionView() {
|
function goToExtensionView() {
|
||||||
|
@ -11,10 +10,4 @@ angular.module('portainer.app')
|
||||||
$state.go('portainer.extensions.extension', { id: ctrl.model.Id });
|
$state.go('portainer.extensions.extension', { id: ctrl.model.Id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function $onInit() {
|
|
||||||
if (ctrl.currentDate === ctrl.model.License.Expiration) {
|
|
||||||
ctrl.model.Expired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="blocklist-item-box">
|
<div class="blocklist-item-box">
|
||||||
<!-- extension-image -->
|
<!-- extension-image -->
|
||||||
<span class="blocklist-item-logo">
|
<span class="blocklist-item-logo">
|
||||||
<img class="blocklist-item-logo" src="images/support_{{ $ctrl.model.Id }}.png" />
|
<img class="blocklist-item-logo" ng-src="images/support_{{ $ctrl.model.Id }}.png" />
|
||||||
</span>
|
</span>
|
||||||
<!-- !extension-image -->
|
<!-- !extension-image -->
|
||||||
<!-- extension-details -->
|
<!-- extension-details -->
|
||||||
|
@ -15,11 +15,6 @@
|
||||||
{{ $ctrl.model.Name }}
|
{{ $ctrl.model.Name }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
|
||||||
<span class="label label-danger" ng-if="$ctrl.model.Enabled && $ctrl.model.Expired">expired</span>
|
|
||||||
<span class="label label-success" ng-if="$ctrl.model.Enabled && !$ctrl.model.Expired">enabled</span>
|
|
||||||
<span class="label label-primary" ng-if="$ctrl.model.Enabled && $ctrl.model.UpdateAvailable && !$ctrl.model.Expired">update available</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- !blocklist-item-line1 -->
|
<!-- !blocklist-item-line1 -->
|
||||||
<!-- blocklist-item-line2 -->
|
<!-- blocklist-item-line2 -->
|
||||||
|
@ -29,9 +24,6 @@
|
||||||
{{ $ctrl.model.ShortDescription }}
|
{{ $ctrl.model.ShortDescription }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="$ctrl.model.License.Company">
|
|
||||||
<span class="small text-muted">Licensed to {{ $ctrl.model.License.Company }} - Expires on {{ $ctrl.model.License.Expiration }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- !blocklist-item-line2 -->
|
<!-- !blocklist-item-line2 -->
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -36,7 +36,9 @@
|
||||||
<div class="form-group" style="margin-left: 40px;">
|
<div class="form-group" style="margin-left: 40px;">
|
||||||
|
|
||||||
<div style="font-size: 125%; border-bottom: 2px solid #2d3e63; padding-bottom: 5px;">
|
<div style="font-size: 125%; border-bottom: 2px solid #2d3e63; padding-bottom: 5px;">
|
||||||
{{ extension.Enabled ? 'Enabled' : extension.Price }}
|
<span ng-if="extension.Enabled">Enabled</span>
|
||||||
|
<span ng-if="!extension.Enabled && extension.License.Expiration && !extension.License.Valid">Expired</span>
|
||||||
|
<span ng-if="!extension.Enabled && !extension.License.Expiration">{{ extension.Price }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="small text-muted col-sm-12" style="margin: 15px 0 15px 0;" ng-if="!extension.Enabled">
|
<div class="small text-muted col-sm-12" style="margin: 15px 0 15px 0;" ng-if="!extension.Enabled">
|
||||||
|
|
Loading…
Reference in New Issue