THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

feat(api): automatically update extensions at startup (#3349)

* feat(api): automatically update extensions at startup

* feat(api): review updateAndStartExtensions
tags/1.23.0^2
Anthony Lapenna GitHub 3 months ago
parent
commit
8b0eb71d69
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 16 deletions
  1. +1
    -16
      api/cmd/portainer/main.go
  2. +57
    -0
      api/exec/extension.go
  3. +1
    -0
      api/portainer.go

+ 1
- 16
api/cmd/portainer/main.go View File

@@ -489,26 +489,11 @@ func initJobService(dockerClientFactory *docker.ClientFactory) portainer.JobServ
func initExtensionManager(fileService portainer.FileService, extensionService portainer.ExtensionService) (portainer.ExtensionManager, error) {
extensionManager := exec.NewExtensionManager(fileService, extensionService)

extensions, err := extensionService.Extensions()
err := extensionManager.StartExtensions()
if err != nil {
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
}



+ 57
- 0
api/exec/extension.go View File

@@ -13,6 +13,8 @@ import (
"strings"
"time"

"github.com/coreos/go-semver/semver"

"github.com/orcaman/concurrent-map"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/client"
@@ -146,6 +148,61 @@ func (manager *ExtensionManager) DisableExtension(extension *portainer.Extension
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
// server, disable the previous extension via DisableExtension, trigger a license check
// and then start the extension process and add it to the processes map


+ 1
- 0
api/portainer.go View File

@@ -894,6 +894,7 @@ type (
EnableExtension(extension *Extension, licenseKey string) error
DisableExtension(extension *Extension) error
UpdateExtension(extension *Extension, version string) error
StartExtensions() error
}

// ReverseTunnelService represensts a service used to manage reverse tunnel connections.


Loading…
Cancel
Save