diff --git a/README.md b/README.md index 3a8035de2..65117ef76 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Docker Pulls](https://img.shields.io/docker/pulls/portainer/portainer.svg)](https://hub.docker.com/r/portainer/portainer/) [![Microbadger](https://images.microbadger.com/badges/image/portainer/portainer.svg)](http://microbadger.com/images/portainer/portainer "Image size") [![Documentation Status](https://readthedocs.org/projects/portainer/badge/?version=stable)](http://portainer.readthedocs.io/en/stable/?badge=stable) -[![Build Status](https://semaphoreci.com/api/v1/portainer/portainer-ci/branches/develop/badge.svg)](https://semaphoreci.com/portainer/portainer-ci) +[![Build Status](https://portainer.visualstudio.com/Portainer%20CI/_apis/build/status/Portainer%20CI?branchName=develop)](https://portainer.visualstudio.com/Portainer%20CI/_build/latest?definitionId=3&branchName=develop) [![Code Climate](https://codeclimate.com/github/portainer/portainer/badges/gpa.svg)](https://codeclimate.com/github/portainer/portainer) [![Gitter](https://badges.gitter.im/portainer/Lobby.svg)](https://gitter.im/portainer/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHXZJQNJQ36H6) diff --git a/api/bolt/datastore.go b/api/bolt/datastore.go index 0e94ad157..85273c23b 100644 --- a/api/bolt/datastore.go +++ b/api/bolt/datastore.go @@ -139,6 +139,7 @@ func (store *Store) MigrateData() error { DatabaseVersion: version, EndpointGroupService: store.EndpointGroupService, EndpointService: store.EndpointService, + ExtensionService: store.ExtensionService, ResourceControlService: store.ResourceControlService, SettingsService: store.SettingsService, StackService: store.StackService, diff --git a/api/bolt/migrator/migrate_dbversion16.go b/api/bolt/migrator/migrate_dbversion16.go new file mode 100644 index 000000000..4464a87ce --- /dev/null +++ b/api/bolt/migrator/migrate_dbversion16.go @@ -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 +} diff --git a/api/bolt/migrator/migrator.go b/api/bolt/migrator/migrator.go index 6ec80dcc9..ccee735ff 100644 --- a/api/bolt/migrator/migrator.go +++ b/api/bolt/migrator/migrator.go @@ -5,6 +5,7 @@ import ( "github.com/portainer/portainer" "github.com/portainer/portainer/bolt/endpoint" "github.com/portainer/portainer/bolt/endpointgroup" + "github.com/portainer/portainer/bolt/extension" "github.com/portainer/portainer/bolt/resourcecontrol" "github.com/portainer/portainer/bolt/settings" "github.com/portainer/portainer/bolt/stack" @@ -20,6 +21,7 @@ type ( db *bolt.DB endpointGroupService *endpointgroup.Service endpointService *endpoint.Service + extensionService *extension.Service resourceControlService *resourcecontrol.Service settingsService *settings.Service stackService *stack.Service @@ -35,6 +37,7 @@ type ( DatabaseVersion int EndpointGroupService *endpointgroup.Service EndpointService *endpoint.Service + ExtensionService *extension.Service ResourceControlService *resourcecontrol.Service SettingsService *settings.Service StackService *stack.Service @@ -52,6 +55,7 @@ func NewMigrator(parameters *Parameters) *Migrator { currentDBVersion: parameters.DatabaseVersion, endpointGroupService: parameters.EndpointGroupService, endpointService: parameters.EndpointService, + extensionService: parameters.ExtensionService, resourceControlService: parameters.ResourceControlService, settingsService: parameters.SettingsService, templateService: parameters.TemplateService, @@ -210,5 +214,13 @@ func (m *Migrator) Migrate() error { } } + // Portainer 1.20.1 + if m.currentDBVersion < 17 { + err := m.updateExtensionsToDBVersion17() + if err != nil { + return err + } + } + return m.versionService.StoreDBVersion(portainer.DBVersion) } diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index 83f85aa8f..24b5c7097 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -492,7 +492,10 @@ func initExtensionManager(fileService portainer.FileService, extensionService po for _, extension := range extensions { err := extensionManager.EnableExtension(&extension, extension.License.LicenseKey) 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) } } diff --git a/api/exec/extension.go b/api/exec/extension.go index 20cf2eca8..cb58ecad6 100644 --- a/api/exec/extension.go +++ b/api/exec/extension.go @@ -113,6 +113,7 @@ func (manager *ExtensionManager) EnableExtension(extension *portainer.Extension, LicenseKey: licenseKey, Company: licenseDetails[0], Expiration: licenseDetails[1], + Valid: true, } extension.Version = licenseDetails[2] diff --git a/api/http/handler/extensions/extension_create.go b/api/http/handler/extensions/extension_create.go index b0ce72406..22d146f6e 100644 --- a/api/http/handler/extensions/extension_create.go +++ b/api/http/handler/extensions/extension_create.go @@ -42,7 +42,7 @@ func (handler *Handler) extensionCreate(w http.ResponseWriter, r *http.Request) } 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} } } diff --git a/api/http/handler/extensions/extension_list.go b/api/http/handler/extensions/extension_list.go index 392822528..68d26a7e7 100644 --- a/api/http/handler/extensions/extension_list.go +++ b/api/http/handler/extensions/extension_list.go @@ -42,6 +42,7 @@ func associateExtensionData(definition *portainer.Extension, extensions []portai definition.Enabled = extension.Enabled definition.License.Company = extension.License.Company definition.License.Expiration = extension.License.Expiration + definition.License.Valid = extension.License.Valid definitionVersion := semver.New(definition.Version) extensionVersion := semver.New(extension.Version) diff --git a/api/http/handler/settings/settings_update.go b/api/http/handler/settings/settings_update.go index b4ddb9e96..21b9daa99 100644 --- a/api/http/handler/settings/settings_update.go +++ b/api/http/handler/settings/settings_update.go @@ -67,7 +67,12 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) * } if payload.LDAPSettings != nil { + ldapPassword := settings.LDAPSettings.Password + if payload.LDAPSettings.Password != "" { + ldapPassword = payload.LDAPSettings.Password + } settings.LDAPSettings = *payload.LDAPSettings + settings.LDAPSettings.Password = ldapPassword } if payload.OAuthSettings != nil { diff --git a/api/portainer.go b/api/portainer.go index 63f886f0f..6f71dc176 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -517,6 +517,7 @@ type ( LicenseKey string `json:"LicenseKey,omitempty"` Company string `json:"Company,omitempty"` Expiration string `json:"Expiration,omitempty"` + Valid bool `json:"Valid,omitempty"` } // CLIService represents a service for managing CLI @@ -799,9 +800,9 @@ type ( const ( // APIVersion is the version number of the Portainer API - APIVersion = "1.20.0" + APIVersion = "1.20.1" // DBVersion is the version number of the Portainer database - DBVersion = 16 + DBVersion = 17 // AssetsServerURL represents the URL of the Portainer asset server AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com" // MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved diff --git a/api/swagger.yaml b/api/swagger.yaml index d8a3ad48a..7f5339d2e 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -54,7 +54,7 @@ info: **NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8). - version: "1.20.0" + version: "1.20.1" title: "Portainer API" contact: email: "info@portainer.io" @@ -525,7 +525,7 @@ paths: **Access policy**: administrator operationId: "EndpointJob" consumes: - - "application/json" + - "multipart/form-data" produces: - "application/json" security: @@ -1434,7 +1434,7 @@ paths: **Access policy**: restricted operationId: "StackCreate" consumes: - - "application/json" + - "multipart/form-data" produces: - "application/json" security: @@ -2733,7 +2733,7 @@ paths: - "application/json" security: - jwt: [] - parameters: + parameters: [] responses: 200: description: "Success" @@ -3018,7 +3018,7 @@ definitions: description: "Is analytics enabled" Version: type: "string" - example: "1.20.0" + example: "1.20.1" description: "Portainer API version" PublicSettingsInspectResponse: type: "object" @@ -3146,7 +3146,7 @@ definitions: $ref: "#/definitions/LDAPGroupSearchSettings" AutoCreateUsers: type: "boolean" - example: "true" + example: true description: "Automatically provision users and assign them to matching LDAP group names" Settings: @@ -3606,6 +3606,7 @@ definitions: - "Authentication" - "Name" - "Password" + - "Type" - "URL" - "Username" properties: @@ -3613,6 +3614,10 @@ definitions: type: "string" example: "my-registry" description: "Name that will be used to identify this registry" + Type: + type: "integer" + example: 1 + description: "Registry Type. Valid values are: 1 (Quay.io), 2 (Azure container registry) or 3 (custom registry)" URL: type: "string" example: "registry.mydomain.tld:2375" @@ -4037,7 +4042,7 @@ definitions: description: "A list of categories associated to the template" items: type: "string" - exampe: "database" + example: "database" registry: type: "string" example: "quay.io" @@ -4133,7 +4138,7 @@ definitions: description: "A list of categories associated to the template" items: type: "string" - exampe: "database" + example: "database" registry: type: "string" example: "quay.io" @@ -4233,7 +4238,7 @@ definitions: description: "A list of categories associated to the template" items: type: "string" - exampe: "database" + example: "database" registry: type: "string" example: "quay.io" diff --git a/api/swagger_config.json b/api/swagger_config.json index beee114ea..cdd9e1115 100644 --- a/api/swagger_config.json +++ b/api/swagger_config.json @@ -1,5 +1,5 @@ { "packageName": "portainer", - "packageVersion": "1.20.0", + "packageVersion": "1.20.1", "projectName": "portainer" } diff --git a/app/app.js b/app/app.js index 021f4d58a..8a05b219b 100644 --- a/app/app.js +++ b/app/app.js @@ -30,7 +30,7 @@ function ($rootScope, $state, Authentication, authManager, StateManager, Endpoin }; $transitions.onBefore({ to: 'docker.**' }, function() { - HttpRequestHelper.resetAgentTargetQueue(); + HttpRequestHelper.resetAgentHeaders(); }); }]); @@ -45,7 +45,7 @@ function initAuthentication(authManager, Authentication, $rootScope, $state) { // to have more controls on which URL should trigger the unauthenticated state. $rootScope.$on('unauthenticated', function (event, data) { if (!_.includes(data.config.url, '/v2/')) { - $state.go('portainer.auth', {error: 'Your session has expired'}); + $state.go('portainer.auth', {error: 'Your session has expired', redirect: $state.current.name}); } }); } diff --git a/app/config.js b/app/config.js index cd21e1f08..268b39288 100644 --- a/app/config.js +++ b/app/config.js @@ -27,6 +27,9 @@ angular.module('portainer') request: function(config) { if (config.url.indexOf('/docker/') > -1) { config.headers['X-PortainerAgent-Target'] = HttpRequestHelper.portainerAgentTargetHeader(); + if (HttpRequestHelper.portainerAgentManagerOperation()) { + config.headers['X-PortainerAgent-ManagerOperation'] = '1'; + } } return config; } diff --git a/app/docker/__module.js b/app/docker/__module.js index 9bd99a7cd..d36f0df8c 100644 --- a/app/docker/__module.js +++ b/app/docker/__module.js @@ -5,7 +5,17 @@ angular.module('portainer.docker', ['portainer.app']) var docker = { name: 'docker', parent: 'root', - abstract: true + abstract: true, + resolve: { + endpointID: ['EndpointProvider', '$state', + function (EndpointProvider, $state) { + var id = EndpointProvider.endpointID(); + if (!id) { + return $state.go('portainer.home'); + } + } + ] + } }; var configs = { diff --git a/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html b/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html index a79fdc698..bb8a5d4c6 100644 --- a/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html +++ b/app/docker/components/datatables/container-networks-datatable/containerNetworksDatatable.html @@ -38,12 +38,12 @@
- curl -L https://portainer.io/download/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent
+ curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent
Copy
diff --git a/app/portainer/views/extensions/inspect/extension.html b/app/portainer/views/extensions/inspect/extension.html
index 0a83740d9..df59e2966 100644
--- a/app/portainer/views/extensions/inspect/extension.html
+++ b/app/portainer/views/extensions/inspect/extension.html
@@ -36,7 +36,9 @@