mirror of https://github.com/portainer/portainer
feat(api): automatically update extensions at startup (#3349)
* feat(api): automatically update extensions at startup * feat(api): review updateAndStartExtensionspull/3392/head
parent
1f90a091a8
commit
8b0eb71d69
|
@ -489,26 +489,11 @@ func initJobService(dockerClientFactory *docker.ClientFactory) portainer.JobServ
|
||||||
func initExtensionManager(fileService portainer.FileService, extensionService portainer.ExtensionService) (portainer.ExtensionManager, error) {
|
func initExtensionManager(fileService portainer.FileService, extensionService portainer.ExtensionService) (portainer.ExtensionManager, error) {
|
||||||
extensionManager := exec.NewExtensionManager(fileService, extensionService)
|
extensionManager := exec.NewExtensionManager(fileService, extensionService)
|
||||||
|
|
||||||
extensions, err := extensionService.Extensions()
|
err := extensionManager.StartExtensions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, extension := range extensions {
|
|
||||||
err := extensionManager.EnableExtension(&extension, extension.License.LicenseKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Unable to enable extension: %s [extension: %s]", err.Error(), extension.Name)
|
|
||||||
extension.Enabled = false
|
|
||||||
extension.License.Valid = false
|
|
||||||
}
|
|
||||||
|
|
||||||
err = extensionService.Persist(&extension)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return extensionManager, nil
|
return extensionManager, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/go-semver/semver"
|
||||||
|
|
||||||
"github.com/orcaman/concurrent-map"
|
"github.com/orcaman/concurrent-map"
|
||||||
"github.com/portainer/portainer/api"
|
"github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/http/client"
|
"github.com/portainer/portainer/api/http/client"
|
||||||
|
@ -146,6 +148,61 @@ func (manager *ExtensionManager) DisableExtension(extension *portainer.Extension
|
||||||
return manager.fileService.RemoveDirectory(extensionBinaryPath)
|
return manager.fileService.RemoveDirectory(extensionBinaryPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartExtensions will retrieve the extensions definitions from the Internet and check if a new version of each
|
||||||
|
// extension is available. If so, it will automatically install the new version of the extension. If no update is
|
||||||
|
// available it will simply start the extension.
|
||||||
|
// The purpose of this function is to be ran at startup, as such most of the error handling won't block the program execution
|
||||||
|
// and will log warning messages instead.
|
||||||
|
func (manager *ExtensionManager) StartExtensions() error {
|
||||||
|
extensions, err := manager.extensionService.Extensions()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
definitions, err := manager.FetchExtensionDefinitions()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARN] [exec,extensions] [message: unable to retrieve extension information from Internet. Skipping extensions update check.] [err: %s]", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return manager.updateAndStartExtensions(extensions, definitions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *ExtensionManager) updateAndStartExtensions(extensions []portainer.Extension, definitions []portainer.Extension) error {
|
||||||
|
for _, definition := range definitions {
|
||||||
|
for _, extension := range extensions {
|
||||||
|
if extension.ID == definition.ID {
|
||||||
|
definitionVersion := semver.New(definition.Version)
|
||||||
|
extensionVersion := semver.New(extension.Version)
|
||||||
|
|
||||||
|
if extensionVersion.LessThan(*definitionVersion) {
|
||||||
|
log.Printf("[INFO] [exec,extensions] [message: new version detected, updating extension] [extension: %s] [current_version: %s] [available_version: %s]", extension.Name, extension.Version, definition.Version)
|
||||||
|
err := manager.UpdateExtension(&extension, definition.Version)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARN] [exec,extensions] [message: unable to update extension automatically] [extension: %s] [current_version: %s] [available_version: %s] [err: %s]", extension.Name, extension.Version, definition.Version, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := manager.EnableExtension(&extension, extension.License.LicenseKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARN] [exec,extensions] [message: unable to start extension] [extension: %s] [err: %s]", extension.Name, err)
|
||||||
|
extension.Enabled = false
|
||||||
|
extension.License.Valid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := manager.extensionService.Persist(&extension)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateExtension will download the new extension binary from the official Portainer assets
|
// UpdateExtension will download the new extension binary from the official Portainer assets
|
||||||
// server, disable the previous extension via DisableExtension, trigger a license check
|
// server, disable the previous extension via DisableExtension, trigger a license check
|
||||||
// and then start the extension process and add it to the processes map
|
// and then start the extension process and add it to the processes map
|
||||||
|
|
|
@ -894,6 +894,7 @@ type (
|
||||||
EnableExtension(extension *Extension, licenseKey string) error
|
EnableExtension(extension *Extension, licenseKey string) error
|
||||||
DisableExtension(extension *Extension) error
|
DisableExtension(extension *Extension) error
|
||||||
UpdateExtension(extension *Extension, version string) error
|
UpdateExtension(extension *Extension, version string) error
|
||||||
|
StartExtensions() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReverseTunnelService represensts a service used to manage reverse tunnel connections.
|
// ReverseTunnelService represensts a service used to manage reverse tunnel connections.
|
||||||
|
|
Loading…
Reference in New Issue