Compare commits

..

69 Commits

Author SHA1 Message Date
Anthony Lapenna
df3886fd25 Merge tag '1.24.1' into demo
Release 1.24.1
2020-07-23 11:43:41 +12:00
Anthony Lapenna
f347d97daf chore(version): bump version number 2020-07-23 10:28:34 +12:00
Anthony Lapenna
d5cee5b8b1 feat(core/extensions): add the ability to update a license (#4081)
* feat(core/extensions): add the ability to update a license

* feat(core/extensions): trigger data upgrade if extension is not enabled yet

* feat(core/extensions): trigger data upgrade if extension is not enabled yet

* feat(core/extensions): trigger data upgrade if extension is not enabled yet

* feat(core/extensions): trigger data upgrade if extension is not enabled yet
2020-07-22 21:13:51 +12:00
Anthony Lapenna
4da6824bc7 feat(database): review database migration (#4054) 2020-07-17 17:04:32 +12:00
Chaim Lev-Ari
80b6b6e300 fix(registries): filter gitlab repos without tags (#4048) 2020-07-16 20:57:52 +12:00
Anthony Lapenna
484dab5932 feat(database): trigger missing database migration for AllowHostNamespaceForRegularUsers setting (#4035) 2020-07-13 22:27:22 +12:00
Chaim Lev-Ari
f8bd075ce4 feat(containers): disable edit container on security setting restricting regular users (#4033)
* feat(settings): add info about container edit disable

* feat(settings): set security settings

* feat(containers): hide recreate button when setting is enabled

* feat(settings): rephrase security notice

* fix(settings): save allowHostNamespaceForRegularUsers to state
2020-07-13 22:26:23 +12:00
Chaim Lev-Ari
cd58c16b4e feat(settings): hide stacks for non admin when settings is set (#4025)
* refactor(settings): replace disableDeviceMapping with allow

* feat(dashboard): hide stacks if settings disabled and non admin

* refactor(sidebar): check if user is endpoint admin

* feat(settings): set the default value for stack management

* feat(settings): rename field label

* fix(sidebar): refresh show stacks state
2020-07-13 18:36:47 +12:00
Chaim Lev-Ari
5ebb03cb4e feat(settings): add setting to disable device mapping for regular users (#4017)
* feat(settings): introduce device mapping service

* feat(containers): hide devices field when setting is on

* feat(containers): prevent passing of devices when not allowed

* feat(stacks): prevent non admin from device mapping

* feat(stacks): disallow swarm stack creation for user

* refactor(settings): replace disableDeviceMapping with allow

* fix(stacks): remove check for disable device mappings from swarm

* feat(settings): rename field to disable

* feat(settings): supply default value for disableDeviceMapping

* feat(container): check for endpoint admin
2020-07-13 16:32:56 +12:00
Chaim Lev-Ari
dffcd3fdfd feat(settings): replace cookies with local storage (#3979)
* feat(cookies): use secured cookies in frontend

* fix(datatables): persist state changes

* fix(datatables): persist order

* feat(sidebar): use local storage to store toggle state

* feat(config): use local storage instead of cookies
2020-07-10 11:51:31 +12:00
Chaim Lev-Ari
3f7687e78a feat(server): support minimum tls v1.2 (#4019)
* feat(crypto): use tls 1.2

* feat(crypto): use secure cipher suites

* feat(server): accept tls1.2 connections

* refactor(crypto): create base tls config

* refactor(server): use basic tls config

* fix(server): remove unused import

* refactor(crypto): rename tls conf factory
2020-07-10 11:48:01 +12:00
Maxime Bajeux
0f58ece899 feat(containers): prevent non-admin users from running containers using the host namespace pid (#3970)
* feat(containers): Prevent non-admin users from running containers using the host namespace pid

* feat(containers): add rbac check for swarm stack too

* feat(containers): remove forgotten conflict

* feat(containers): init EnableHostNamespaceUse to true and return 403 on forbidden action

* feat(containers): change enableHostNamespaceUse to restrictHostNamespaceUse in html

* feat(settings): rename EnableHostNamespaceUse to AllowHostNamespaceForRegularUsers
2020-07-08 09:48:34 +12:00
Chaim Lev-Ari
b0ad212858 fix(registries): hide zero tags repositories (#3985) 2020-07-07 10:59:33 +12:00
Chaim Lev-Ari
7eb2fd3424 feat(stacks): add a setting to disable the creation of stacks for non-admin users (#3932)
* feat(settings): introduce a setting to prevent non-admin from stack creation

* feat(settings): update stack creation setting

* feat(settings): fail stack creation if user is non admin

* fix(settings): save preventStackCreation setting to state

* feat(stacks): disable add button when settings is enabled

* format(stacks): remove line

* feat(stacks): setting to hide stacks from users

* feat(settings): rename disable stacks setting

* refactor(settings): rename setting to disableStackManagementForRegularUsers
2020-07-01 09:34:43 +12:00
Maxime Bajeux
4c0d8ce732 feat(containers): Ensure users cannot create privileged containers via the API (#3969)
* feat(containers): Ensure users cannot create privileged containers via the API

* feat(containers): add rbac check in stack creation
2020-06-30 17:13:37 +12:00
Anthony Lapenna
e1cc4bc9a1 chore(version): bump version number 2020-06-16 17:22:51 +12:00
Anthony Lapenna
f2682d82b6 merge branch master into demo 2020-06-05 11:55:22 +12:00
Anthony Lapenna
d3576fe8e6 merge branch master into demo 2020-06-05 11:55:14 +12:00
Anthony Lapenna
f34d27df0f Merge branch 'master' into demo 2020-03-20 11:43:39 +13:00
Anthony Lapenna
f228b28639 Merge branch 'master' into demo 2020-02-18 13:49:34 +13:00
Anthony Lapenna
d7b4e4aba1 Merge branch 'master' into demo 2019-12-04 17:30:43 +13:00
Anthony Lapenna
7234c443e8 Merge branch 'develop' into demo 2019-10-11 12:17:32 +13:00
Anthony Lapenna
5b844fd40a fix(http): allow endpoint update operation on demo environment 2019-07-26 15:25:13 +12:00
Anthony Lapenna
7f2ef8fb06 Merge branch 'develop' into demo
# Conflicts:
#	api/cmd/portainer/main.go
2019-07-26 14:43:10 +12:00
Anthony Lapenna
9f817749c1 Merge branch 'develop' into demo 2019-06-04 16:12:31 +12:00
Anthony Lapenna
36198b57a5 Merge branch 'develop' into demo 2019-03-05 18:48:33 +13:00
Anthony Lapenna
70f9e37eab fix(api): fix missing endpoint identifier 2019-02-14 11:37:54 +13:00
Anthony Lapenna
24f69f0185 Merge branch 'develop' into demo 2019-01-31 15:23:48 +13:00
Anthony Lapenna
4a2e9a892d fix(demo): fix invalid demo data and trigger snapshot at startup 2018-12-12 20:17:53 +13:00
Anthony Lapenna
1e4bedde4b Merge branch 'develop' into demo 2018-12-12 17:59:46 +13:00
Anthony Lapenna
9db07e7f4e Merge branch 'develop' into demo 2018-12-12 14:52:50 +13:00
Anthony Lapenna
d4b8d9947d Merge branch 'master' into demo 2018-09-15 19:30:58 +08:00
Anthony Lapenna
9194ddcd03 Merge branch 'develop' into demo 2018-07-28 20:35:14 +02:00
Anthony Lapenna
e1edf37770 Merge branch 'develop' into demo
Conflicts:
	app/portainer/views/settings/settings.html
2018-07-27 18:03:52 +02:00
Anthony Lapenna
82b73c06b4 Merge branch 'develop' into demo 2018-06-25 16:44:43 +03:00
Anthony Lapenna
ae286998ab Merge branch 'develop' into demo
Conflicts:
	api/cmd/portainer/main.go
	api/http/handler/endpoint.go
	api/http/handler/user.go
2018-06-21 16:47:13 +03:00
Anthony Lapenna
ffc3ed67e2 Merge branch 'develop' into demo
Conflicts:
	api/cmd/portainer/main.go
2018-05-21 13:45:45 +02:00
Anthony Lapenna
6252be4a08 Merge branch 'develop' into demo
Conflicts:
	api/cmd/portainer/main.go
	app/portainer/views/settings/settings.html
2018-05-10 18:14:48 +02:00
Anthony Lapenna
75481d928e Merge branch 'develop' into demo 2018-04-02 08:57:02 +10:00
Anthony Lapenna
d65d65803f Merge branch 'develop' into demo 2018-03-11 20:30:52 +10:00
Anthony Lapenna
6a8fc253bd fix(demo): fix demo init data 2018-03-03 10:31:09 +10:00
Anthony Lapenna
1fa8921c11 Merge branch 'develop' into demo 2018-03-03 10:16:10 +10:00
Anthony Lapenna
ba3892aebf Merge branch 'develop' into demo 2018-02-08 10:22:47 +01:00
Anthony Lapenna
27fc700d6f Merge branch 'develop' into demo 2018-01-21 18:29:26 +01:00
Anthony Lapenna
3187cb0ada feat(api): add demo.portainer.io as the Public URL of the endpoint 2018-01-16 15:53:49 +01:00
Anthony Lapenna
b462a15921 Merge branch 'develop' into demo 2017-12-11 17:29:33 +01:00
Anthony Lapenna
11a646a076 Merge branch 'develop' into demo
Conflicts:
	app/components/endpoints/endpoints.html
	app/components/users/users.html
2017-12-10 11:32:26 +01:00
Anthony Lapenna
4c1edaf251 Merge branch 'develop' into demo 2017-11-26 11:18:14 +01:00
Anthony Lapenna
b45c4f8bea Merge branch 'develop' into demo
Conflicts:
	app/components/settings/settings.html
2017-11-13 12:07:24 +01:00
Anthony Lapenna
968f070b0b Merge branch 'develop' into demo 2017-11-08 09:33:45 +01:00
Anthony Lapenna
e679483ffd Merge branch 'develop' into demo 2017-10-15 20:50:36 +02:00
Anthony Lapenna
494ef6c392 Merge branch 'develop' into 'demo' 2017-10-15 20:27:59 +02:00
Anthony Lapenna
7951c1d150 Merge branch 'develop' into demo 2017-09-28 19:03:22 +02:00
Anthony Lapenna
0083bdb6a2 Merge branch 'master' into demo 2017-09-21 17:49:21 +02:00
Anthony Lapenna
e65881223d Merge branch 'develop' into 'demo' 2017-09-20 16:15:23 +02:00
Anthony Lapenna
f0c9058568 Merge branch 'develop' into 'demo' 2017-08-13 21:03:39 +02:00
Anthony Lapenna
afd654e4a8 Merge branch 'develop' into demo 2017-07-17 16:01:00 +02:00
Anthony Lapenna
0ef116d6b5 Merge branch 'develop' into demo 2017-07-13 18:19:14 +02:00
Anthony Lapenna
ee64a53782 Merge branch 'develop' into demo 2017-06-29 16:41:40 +02:00
Anthony Lapenna
4a0c899df5 Merge branch 'develop' into 'demo' 2017-06-20 13:24:29 +02:00
Anthony Lapenna
840603bb8c feat(settings): prevent the display of the URL field for custom templates 2017-06-05 09:56:04 +02:00
Anthony Lapenna
5fe79621a6 feat(settings): set a default value for custom templates 2017-06-05 09:51:01 +02:00
Anthony Lapenna
d3b9822105 Merge branch 'develop' into 'demo' 2017-06-05 09:45:06 +02:00
Anthony Lapenna
9163bcae25 Merge branch 'develop' into demo 2017-05-25 12:40:11 +02:00
Anthony Lapenna
17dad6e36f fix(api): prevent the deletion of the admin user 2017-05-23 21:51:30 +02:00
Anthony Lapenna
75b836453a merge branch 'develop' into demo 2017-05-23 21:06:50 +02:00
Anthony Lapenna
b8866d487f Merge branch 'develop' into demo 2017-04-06 11:02:27 +02:00
Anthony Lapenna
dfb3b3aaa1 Merge branch 'develop' into demo 2017-03-28 15:38:24 +02:00
Anthony Lapenna
09fdc9781c feat(demo): add demo restrictions 2017-03-14 08:47:57 +01:00
300 changed files with 6117 additions and 1404 deletions

View File

@@ -6,10 +6,10 @@ Some basic conventions for contributing to this project.
Please make sure that there aren't existing pull requests attempting to address the issue mentioned. Likewise, please check for issues related to update, as someone else may be working on the issue in a branch or fork.
* Please open a discussion in a new issue / existing issue to talk about the changes you'd like to bring
* Develop in a topic branch, not master/develop
- Please open a discussion in a new issue / existing issue to talk about the changes you'd like to bring
- Develop in a topic branch, not master/develop
When creating a new branch, prefix it with the *type* of the change (see section **Commit Message Format** below), the associated opened issue number, a dash and some text describing the issue (using dash as a separator).
When creating a new branch, prefix it with the _type_ of the change (see section **Commit Message Format** below), the associated opened issue number, a dash and some text describing the issue (using dash as a separator).
For example, if you work on a bugfix for the issue #361, you could name the branch `fix361-template-selection`.
@@ -37,14 +37,14 @@ Lines should not exceed 100 characters. This allows the message to be easier to
Must be one of the following:
* **feat**: A new feature
* **fix**: A bug fix
* **docs**: Documentation only changes
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation only changes
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug or adds a feature
* **test**: Adding missing tests
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
- **refactor**: A code change that neither fixes a bug or adds a feature
- **test**: Adding missing tests
- **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
generation
### Scope
@@ -57,9 +57,9 @@ You can use the **area** label tag associated on the issue here (for `area/conta
The subject contains succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize first letter
- no dot (.) at the end
## Contribution process

View File

@@ -3,13 +3,34 @@ package portainer
// AuthorizationService represents a service used to
// update authorizations associated to a user or team.
type AuthorizationService struct {
dataStore DataStore
endpointService EndpointService
endpointGroupService EndpointGroupService
registryService RegistryService
roleService RoleService
teamMembershipService TeamMembershipService
userService UserService
}
// AuthorizationServiceParameters are the required parameters
// used to create a new AuthorizationService.
type AuthorizationServiceParameters struct {
EndpointService EndpointService
EndpointGroupService EndpointGroupService
RegistryService RegistryService
RoleService RoleService
TeamMembershipService TeamMembershipService
UserService UserService
}
// NewAuthorizationService returns a point to a new AuthorizationService instance.
func NewAuthorizationService(dataStore DataStore) *AuthorizationService {
func NewAuthorizationService(parameters *AuthorizationServiceParameters) *AuthorizationService {
return &AuthorizationService{
dataStore: dataStore,
endpointService: parameters.EndpointService,
endpointGroupService: parameters.EndpointGroupService,
registryService: parameters.RegistryService,
roleService: parameters.RoleService,
teamMembershipService: parameters.TeamMembershipService,
userService: parameters.UserService,
}
}
@@ -428,7 +449,7 @@ func DefaultPortainerAuthorizations() Authorizations {
// the authorizations will be dropped for the each role. If removeAuthorizations is set to false, the authorizations
// will be reset based for each role.
func (service AuthorizationService) UpdateVolumeBrowsingAuthorizations(remove bool) error {
roles, err := service.dataStore.Role().Roles()
roles, err := service.roleService.Roles()
if err != nil {
return err
}
@@ -438,7 +459,7 @@ func (service AuthorizationService) UpdateVolumeBrowsingAuthorizations(remove bo
if role.ID != RoleID(1) {
updateRoleVolumeBrowsingAuthorizations(&role, remove)
err := service.dataStore.Role().UpdateRole(role.ID, &role)
err := service.roleService.UpdateRole(role.ID, &role)
if err != nil {
return err
}
@@ -471,7 +492,7 @@ func updateRoleVolumeBrowsingAuthorizations(role *Role, removeAuthorizations boo
// RemoveTeamAccessPolicies will remove all existing access policies associated to the specified team
func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) error {
endpoints, err := service.dataStore.Endpoint().Endpoints()
endpoints, err := service.endpointService.Endpoints()
if err != nil {
return err
}
@@ -481,7 +502,7 @@ func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) err
if policyTeamID == teamID {
delete(endpoint.TeamAccessPolicies, policyTeamID)
err := service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint)
err := service.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
@@ -491,7 +512,7 @@ func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) err
}
}
endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := service.endpointGroupService.EndpointGroups()
if err != nil {
return err
}
@@ -501,7 +522,7 @@ func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) err
if policyTeamID == teamID {
delete(endpointGroup.TeamAccessPolicies, policyTeamID)
err := service.dataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
err := service.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
@@ -511,7 +532,7 @@ func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) err
}
}
registries, err := service.dataStore.Registry().Registries()
registries, err := service.registryService.Registries()
if err != nil {
return err
}
@@ -521,7 +542,7 @@ func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) err
if policyTeamID == teamID {
delete(registry.TeamAccessPolicies, policyTeamID)
err := service.dataStore.Registry().UpdateRegistry(registry.ID, &registry)
err := service.registryService.UpdateRegistry(registry.ID, &registry)
if err != nil {
return err
}
@@ -536,7 +557,7 @@ func (service *AuthorizationService) RemoveTeamAccessPolicies(teamID TeamID) err
// RemoveUserAccessPolicies will remove all existing access policies associated to the specified user
func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) error {
endpoints, err := service.dataStore.Endpoint().Endpoints()
endpoints, err := service.endpointService.Endpoints()
if err != nil {
return err
}
@@ -546,7 +567,7 @@ func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) err
if policyUserID == userID {
delete(endpoint.UserAccessPolicies, policyUserID)
err := service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint)
err := service.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
@@ -556,7 +577,7 @@ func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) err
}
}
endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := service.endpointGroupService.EndpointGroups()
if err != nil {
return err
}
@@ -566,7 +587,7 @@ func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) err
if policyUserID == userID {
delete(endpointGroup.UserAccessPolicies, policyUserID)
err := service.dataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
err := service.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
@@ -576,7 +597,7 @@ func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) err
}
}
registries, err := service.dataStore.Registry().Registries()
registries, err := service.registryService.Registries()
if err != nil {
return err
}
@@ -586,7 +607,7 @@ func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) err
if policyUserID == userID {
delete(registry.UserAccessPolicies, policyUserID)
err := service.dataStore.Registry().UpdateRegistry(registry.ID, &registry)
err := service.registryService.UpdateRegistry(registry.ID, &registry)
if err != nil {
return err
}
@@ -601,7 +622,7 @@ func (service *AuthorizationService) RemoveUserAccessPolicies(userID UserID) err
// UpdateUsersAuthorizations will trigger an update of the authorizations for all the users.
func (service *AuthorizationService) UpdateUsersAuthorizations() error {
users, err := service.dataStore.User().Users()
users, err := service.userService.Users()
if err != nil {
return err
}
@@ -617,7 +638,7 @@ func (service *AuthorizationService) UpdateUsersAuthorizations() error {
}
func (service *AuthorizationService) updateUserAuthorizations(userID UserID) error {
user, err := service.dataStore.User().User(userID)
user, err := service.userService.User(userID)
if err != nil {
return err
}
@@ -629,7 +650,7 @@ func (service *AuthorizationService) updateUserAuthorizations(userID UserID) err
user.EndpointAuthorizations = endpointAuthorizations
return service.dataStore.User().UpdateUser(userID, user)
return service.userService.UpdateUser(userID, user)
}
func (service *AuthorizationService) getAuthorizations(user *User) (EndpointAuthorizations, error) {
@@ -638,22 +659,22 @@ func (service *AuthorizationService) getAuthorizations(user *User) (EndpointAuth
return endpointAuthorizations, nil
}
userMemberships, err := service.dataStore.TeamMembership().TeamMembershipsByUserID(user.ID)
userMemberships, err := service.teamMembershipService.TeamMembershipsByUserID(user.ID)
if err != nil {
return endpointAuthorizations, err
}
endpoints, err := service.dataStore.Endpoint().Endpoints()
endpoints, err := service.endpointService.Endpoints()
if err != nil {
return endpointAuthorizations, err
}
endpointGroups, err := service.dataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := service.endpointGroupService.EndpointGroups()
if err != nil {
return endpointAuthorizations, err
}
roles, err := service.dataStore.Role().Roles()
roles, err := service.roleService.Roles()
if err != nil {
return endpointAuthorizations, err
}

View File

@@ -5,14 +5,16 @@ import (
"path"
"time"
"github.com/portainer/portainer/api/bolt/edgegroup"
"github.com/portainer/portainer/api/bolt/edgestack"
"github.com/portainer/portainer/api/bolt/endpointrelation"
"github.com/portainer/portainer/api/bolt/tunnelserver"
"github.com/boltdb/bolt"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/dockerhub"
"github.com/portainer/portainer/api/bolt/edgegroup"
"github.com/portainer/portainer/api/bolt/edgestack"
"github.com/portainer/portainer/api/bolt/endpoint"
"github.com/portainer/portainer/api/bolt/endpointgroup"
"github.com/portainer/portainer/api/bolt/endpointrelation"
"github.com/portainer/portainer/api/bolt/extension"
"github.com/portainer/portainer/api/bolt/migrator"
"github.com/portainer/portainer/api/bolt/registry"
@@ -24,7 +26,7 @@ import (
"github.com/portainer/portainer/api/bolt/tag"
"github.com/portainer/portainer/api/bolt/team"
"github.com/portainer/portainer/api/bolt/teammembership"
"github.com/portainer/portainer/api/bolt/tunnelserver"
"github.com/portainer/portainer/api/bolt/template"
"github.com/portainer/portainer/api/bolt/user"
"github.com/portainer/portainer/api/bolt/version"
"github.com/portainer/portainer/api/bolt/webhook"
@@ -39,8 +41,9 @@ const (
type Store struct {
path string
db *bolt.DB
isNew bool
checkForDataMigration bool
fileService portainer.FileService
RoleService *role.Service
DockerHubService *dockerhub.Service
EdgeGroupService *edgegroup.Service
EdgeStackService *edgestack.Service
@@ -50,17 +53,17 @@ type Store struct {
ExtensionService *extension.Service
RegistryService *registry.Service
ResourceControlService *resourcecontrol.Service
RoleService *role.Service
ScheduleService *schedule.Service
SettingsService *settings.Service
StackService *stack.Service
TagService *tag.Service
TeamMembershipService *teammembership.Service
TeamService *team.Service
TemplateService *template.Service
TunnelServerService *tunnelserver.Service
UserService *user.Service
VersionService *version.Service
WebhookService *webhook.Service
ScheduleService *schedule.Service
}
// NewStore initializes a new Store and the associated services
@@ -68,7 +71,6 @@ func NewStore(storePath string, fileService portainer.FileService) (*Store, erro
store := &Store{
path: storePath,
fileService: fileService,
isNew: true,
}
databasePath := path.Join(storePath, databaseFileName)
@@ -77,8 +79,10 @@ func NewStore(storePath string, fileService portainer.FileService) (*Store, erro
return nil, err
}
if databaseFileExists {
store.isNew = false
if !databaseFileExists {
store.checkForDataMigration = false
} else {
store.checkForDataMigration = true
}
return store, nil
@@ -104,16 +108,9 @@ func (store *Store) Close() error {
return nil
}
// IsNew returns true if the database was just created and false if it is re-using
// existing data.
func (store *Store) IsNew() bool {
return store.isNew
}
// MigrateData automatically migrate the data based on the DBVersion.
// This process is only triggered on an existing database, not if the database was just created.
func (store *Store) MigrateData() error {
if store.isNew {
if !store.checkForDataMigration {
return store.VersionService.StoreDBVersion(portainer.DBVersion)
}
@@ -140,10 +137,10 @@ func (store *Store) MigrateData() error {
StackService: store.StackService,
TagService: store.TagService,
TeamMembershipService: store.TeamMembershipService,
TemplateService: store.TemplateService,
UserService: store.UserService,
VersionService: store.VersionService,
FileService: store.fileService,
AuthorizationService: portainer.NewAuthorizationService(store),
}
migrator := migrator.NewMigrator(migratorParams)
@@ -249,6 +246,12 @@ func (store *Store) initServices() error {
}
store.TeamService = teamService
templateService, err := template.NewService(store.db)
if err != nil {
return err
}
store.TemplateService = templateService
tunnelServerService, err := tunnelserver.NewService(store.db)
if err != nil {
return err
@@ -281,103 +284,3 @@ func (store *Store) initServices() error {
return nil
}
// DockerHub gives access to the DockerHub data management layer
func (store *Store) DockerHub() portainer.DockerHubService {
return store.DockerHubService
}
// EdgeGroup gives access to the EdgeGroup data management layer
func (store *Store) EdgeGroup() portainer.EdgeGroupService {
return store.EdgeGroupService
}
// EdgeStack gives access to the EdgeStack data management layer
func (store *Store) EdgeStack() portainer.EdgeStackService {
return store.EdgeStackService
}
// Endpoint gives access to the Endpoint data management layer
func (store *Store) Endpoint() portainer.EndpointService {
return store.EndpointService
}
// EndpointGroup gives access to the EndpointGroup data management layer
func (store *Store) EndpointGroup() portainer.EndpointGroupService {
return store.EndpointGroupService
}
// EndpointRelation gives access to the EndpointRelation data management layer
func (store *Store) EndpointRelation() portainer.EndpointRelationService {
return store.EndpointRelationService
}
// Extension gives access to the Extension data management layer
func (store *Store) Extension() portainer.ExtensionService {
return store.ExtensionService
}
// Registry gives access to the Registry data management layer
func (store *Store) Registry() portainer.RegistryService {
return store.RegistryService
}
// ResourceControl gives access to the ResourceControl data management layer
func (store *Store) ResourceControl() portainer.ResourceControlService {
return store.ResourceControlService
}
// Role gives access to the Role data management layer
func (store *Store) Role() portainer.RoleService {
return store.RoleService
}
// Schedule gives access to the Schedule data management layer
func (store *Store) Schedule() portainer.ScheduleService {
return store.ScheduleService
}
// Settings gives access to the Settings data management layer
func (store *Store) Settings() portainer.SettingsService {
return store.SettingsService
}
// Stack gives access to the Stack data management layer
func (store *Store) Stack() portainer.StackService {
return store.StackService
}
// Tag gives access to the Tag data management layer
func (store *Store) Tag() portainer.TagService {
return store.TagService
}
// TeamMembership gives access to the TeamMembership data management layer
func (store *Store) TeamMembership() portainer.TeamMembershipService {
return store.TeamMembershipService
}
// Team gives access to the Team data management layer
func (store *Store) Team() portainer.TeamService {
return store.TeamService
}
// TunnelServer gives access to the TunnelServer data management layer
func (store *Store) TunnelServer() portainer.TunnelServerService {
return store.TunnelServerService
}
// User gives access to the User data management layer
func (store *Store) User() portainer.UserService {
return store.UserService
}
// Version gives access to the Version data management layer
func (store *Store) Version() portainer.VersionService {
return store.VersionService
}
// Webhook gives access to the Webhook data management layer
func (store *Store) Webhook() portainer.WebhookService {
return store.WebhookService
}

View File

@@ -4,55 +4,6 @@ import portainer "github.com/portainer/portainer/api"
// Init creates the default data set.
func (store *Store) Init() error {
_, err := store.SettingsService.Settings()
if err == portainer.ErrObjectNotFound {
defaultSettings := &portainer.Settings{
AuthenticationMethod: portainer.AuthenticationInternal,
BlackListedLabels: make([]portainer.Pair, 0),
LDAPSettings: portainer.LDAPSettings{
AnonymousMode: true,
AutoCreateUsers: true,
TLSConfig: portainer.TLSConfiguration{},
SearchSettings: []portainer.LDAPSearchSettings{
portainer.LDAPSearchSettings{},
},
GroupSearchSettings: []portainer.LDAPGroupSearchSettings{
portainer.LDAPGroupSearchSettings{},
},
},
OAuthSettings: portainer.OAuthSettings{},
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowVolumeBrowserForRegularUsers: false,
EnableHostManagementFeatures: false,
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
TemplatesURL: portainer.DefaultTemplatesURL,
}
err = store.SettingsService.UpdateSettings(defaultSettings)
if err != nil {
return err
}
} else if err != nil {
return err
}
_, err = store.DockerHubService.DockerHub()
if err == portainer.ErrObjectNotFound {
defaultDockerHub := &portainer.DockerHub{
Authentication: false,
Username: "",
Password: "",
}
err := store.DockerHubService.UpdateDockerHub(defaultDockerHub)
if err != nil {
return err
}
} else if err != nil {
return err
}
groups, err := store.EndpointGroupService.EndpointGroups()
if err != nil {
return err

View File

@@ -1,5 +1,11 @@
package migrator
import (
"strings"
"github.com/portainer/portainer/api"
)
func (m *Migrator) updateSettingsToDBVersion15() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
@@ -11,6 +17,19 @@ func (m *Migrator) updateSettingsToDBVersion15() error {
}
func (m *Migrator) updateTemplatesToVersion15() error {
// Removed with the entire template management layer, part of https://github.com/portainer/portainer/issues/3707
legacyTemplates, err := m.templateService.Templates()
if err != nil {
return err
}
for _, template := range legacyTemplates {
template.Logo = strings.Replace(template.Logo, "https://portainer.io/images", portainer.AssetsServerURL, -1)
err = m.templateService.UpdateTemplate(template.ID, &template)
if err != nil {
return err
}
}
return nil
}

View File

@@ -7,7 +7,17 @@ import (
)
func (m *Migrator) updateUsersToDBVersion20() error {
return m.authorizationService.UpdateUsersAuthorizations()
authorizationServiceParameters := &portainer.AuthorizationServiceParameters{
EndpointService: m.endpointService,
EndpointGroupService: m.endpointGroupService,
RegistryService: m.registryService,
RoleService: m.roleService,
TeamMembershipService: m.teamMembershipService,
UserService: m.userService,
}
authorizationService := portainer.NewAuthorizationService(authorizationServiceParameters)
return authorizationService.UpdateUsersAuthorizations()
}
func (m *Migrator) updateSettingsToDBVersion20() error {

View File

@@ -74,9 +74,16 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
readOnlyUserRole.Authorizations = portainer.DefaultEndpointAuthorizationsForReadOnlyUserRole(settings.AllowVolumeBrowserForRegularUsers)
err = m.roleService.UpdateRole(readOnlyUserRole.ID, readOnlyUserRole)
if err != nil {
return err
authorizationServiceParameters := &portainer.AuthorizationServiceParameters{
EndpointService: m.endpointService,
EndpointGroupService: m.endpointGroupService,
RegistryService: m.registryService,
RoleService: m.roleService,
TeamMembershipService: m.teamMembershipService,
UserService: m.userService,
}
return m.authorizationService.UpdateUsersAuthorizations()
authorizationService := portainer.NewAuthorizationService(authorizationServiceParameters)
return authorizationService.UpdateUsersAuthorizations()
}

View File

@@ -1,6 +1,8 @@
package migrator
import "github.com/portainer/portainer/api"
import (
"github.com/portainer/portainer/api"
)
func (m *Migrator) updateTagsToDBVersion23() error {
tags, err := m.tagService.Tags()

View File

@@ -1,18 +1,14 @@
package migrator
import portainer "github.com/portainer/portainer/api"
func (m *Migrator) updateSettingsToDB24() error {
func (m *Migrator) updateSettingsToDBVersion24() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
if legacySettings.TemplatesURL == "" {
legacySettings.TemplatesURL = portainer.DefaultTemplatesURL
legacySettings.AllowDeviceMappingForRegularUsers = true
legacySettings.AllowStackManagementForRegularUsers = true
legacySettings.AllowHostNamespaceForRegularUsers = true
return m.settingsService.UpdateSettings(legacySettings)
}
return nil
return m.settingsService.UpdateSettings(legacySettings)
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/portainer/portainer/api/bolt/stack"
"github.com/portainer/portainer/api/bolt/tag"
"github.com/portainer/portainer/api/bolt/teammembership"
"github.com/portainer/portainer/api/bolt/template"
"github.com/portainer/portainer/api/bolt/user"
"github.com/portainer/portainer/api/bolt/version"
)
@@ -36,10 +37,10 @@ type (
stackService *stack.Service
tagService *tag.Service
teamMembershipService *teammembership.Service
templateService *template.Service
userService *user.Service
versionService *version.Service
fileService portainer.FileService
authorizationService *portainer.AuthorizationService
}
// Parameters represents the required parameters to create a new Migrator instance.
@@ -58,10 +59,10 @@ type (
StackService *stack.Service
TagService *tag.Service
TeamMembershipService *teammembership.Service
TemplateService *template.Service
UserService *user.Service
VersionService *version.Service
FileService portainer.FileService
AuthorizationService *portainer.AuthorizationService
}
)
@@ -81,16 +82,17 @@ func NewMigrator(parameters *Parameters) *Migrator {
settingsService: parameters.SettingsService,
tagService: parameters.TagService,
teamMembershipService: parameters.TeamMembershipService,
templateService: parameters.TemplateService,
stackService: parameters.StackService,
userService: parameters.UserService,
versionService: parameters.VersionService,
fileService: parameters.FileService,
authorizationService: parameters.AuthorizationService,
}
}
// Migrate checks the database version and migrate the existing data to the most recent data model.
func (m *Migrator) Migrate() error {
// Portainer < 1.12
if m.currentDBVersion < 1 {
err := m.updateAdminUserToDBVersion1()
@@ -320,9 +322,9 @@ func (m *Migrator) Migrate() error {
}
}
// Portainer 2.0
// Portainer 1.24.1
if m.currentDBVersion < 24 {
err := m.updateSettingsToDB24()
err := m.updateSettingsToDBVersion24()
if err != nil {
return err
}

View File

@@ -0,0 +1,95 @@
package template
import (
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "templates"
)
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
}, nil
}
// Templates return an array containing all the templates.
func (service *Service) Templates() ([]portainer.Template, error) {
var templates = make([]portainer.Template, 0)
err := service.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
var template portainer.Template
err := internal.UnmarshalObject(v, &template)
if err != nil {
return err
}
templates = append(templates, template)
}
return nil
})
return templates, err
}
// Template returns a template by ID.
func (service *Service) Template(ID portainer.TemplateID) (*portainer.Template, error) {
var template portainer.Template
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &template)
if err != nil {
return nil, err
}
return &template, nil
}
// CreateTemplate creates a new template.
func (service *Service) CreateTemplate(template *portainer.Template) error {
return service.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
template.ID = portainer.TemplateID(id)
data, err := internal.MarshalObject(template)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(template.ID)), data)
})
}
// UpdateTemplate saves a template.
func (service *Service) UpdateTemplate(ID portainer.TemplateID, template *portainer.Template) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, template)
}
// DeleteTemplate deletes a template.
func (service *Service) DeleteTemplate(ID portainer.TemplateID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
}

View File

@@ -24,19 +24,21 @@ const (
// It is used to start a reverse tunnel server and to manage the connection status of each tunnel
// connected to the tunnel server.
type Service struct {
serverFingerprint string
serverPort string
tunnelDetailsMap cmap.ConcurrentMap
dataStore portainer.DataStore
snapshotter portainer.Snapshotter
chiselServer *chserver.Server
serverFingerprint string
serverPort string
tunnelDetailsMap cmap.ConcurrentMap
endpointService portainer.EndpointService
tunnelServerService portainer.TunnelServerService
snapshotter portainer.Snapshotter
chiselServer *chserver.Server
}
// NewService returns a pointer to a new instance of Service
func NewService(dataStore portainer.DataStore) *Service {
func NewService(endpointService portainer.EndpointService, tunnelServerService portainer.TunnelServerService) *Service {
return &Service{
tunnelDetailsMap: cmap.New(),
dataStore: dataStore,
tunnelDetailsMap: cmap.New(),
endpointService: endpointService,
tunnelServerService: tunnelServerService,
}
}
@@ -87,7 +89,7 @@ func (service *Service) StartTunnelServer(addr, port string, snapshotter portain
func (service *Service) retrievePrivateKeySeed() (string, error) {
var serverInfo *portainer.TunnelServerInfo
serverInfo, err := service.dataStore.TunnelServer().Info()
serverInfo, err := service.tunnelServerService.Info()
if err == portainer.ErrObjectNotFound {
keySeed := uniuri.NewLen(16)
@@ -95,7 +97,7 @@ func (service *Service) retrievePrivateKeySeed() (string, error) {
PrivateKeySeed: keySeed,
}
err := service.dataStore.TunnelServer().UpdateInfo(serverInfo)
err := service.tunnelServerService.UpdateInfo(serverInfo)
if err != nil {
return "", err
}
@@ -171,7 +173,7 @@ func (service *Service) checkTunnels() {
}
func (service *Service) snapshotEnvironment(endpointID portainer.EndpointID, tunnelPort int) error {
endpoint, err := service.dataStore.Endpoint().Endpoint(endpointID)
endpoint, err := service.endpointService.Endpoint(portainer.EndpointID(endpointID))
if err != nil {
return err
}
@@ -185,5 +187,5 @@ func (service *Service) snapshotEnvironment(endpointID portainer.EndpointID, tun
endpoint.Snapshots = []portainer.Snapshot{*snapshot}
endpoint.URL = endpointURL
return service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
return service.endpointService.UpdateEndpoint(endpoint.ID, endpoint)
}

View File

@@ -97,7 +97,7 @@ func (service *Service) SetTunnelStatusToRequired(endpointID portainer.EndpointI
tunnel := service.GetTunnelDetails(endpointID)
if tunnel.Port == 0 {
endpoint, err := service.dataStore.Endpoint().Endpoint(endpointID)
endpoint, err := service.endpointService.Endpoint(endpointID)
if err != nil {
return err
}

View File

@@ -19,7 +19,11 @@ type Service struct{}
const (
errInvalidEndpointProtocol = portainer.Error("Invalid endpoint protocol: Portainer only supports unix://, npipe:// or tcp://")
errSocketOrNamedPipeNotFound = portainer.Error("Unable to locate Unix socket or named pipe")
errEndpointsFileNotFound = portainer.Error("Unable to locate external endpoints file")
errTemplateFileNotFound = portainer.Error("Unable to locate template file on disk")
errInvalidSyncInterval = portainer.Error("Invalid synchronization interval")
errInvalidSnapshotInterval = portainer.Error("Invalid snapshot interval")
errEndpointExcludeExternal = portainer.Error("Cannot use the -H flag mutually with --external-endpoints")
errNoAuthExcludeAdminPassword = portainer.Error("Cannot use --no-auth with --admin-password or --admin-password-file")
errAdminPassExcludeAdminPassFile = portainer.Error("Cannot use --admin-password with --admin-password-file")
)
@@ -35,6 +39,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
Assets: kingpin.Flag("assets", "Path to the assets").Default(defaultAssetsDirectory).Short('a').String(),
Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(),
EndpointURL: kingpin.Flag("host", "Endpoint URL").Short('H').String(),
ExternalEndpoints: kingpin.Flag("external-endpoints", "Path to a file defining available endpoints (deprecated)").String(),
NoAuth: kingpin.Flag("no-auth", "Disable authentication (deprecated)").Default(defaultNoAuth).Bool(),
NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app").Default(defaultNoAnalytics).Bool(),
TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(),
@@ -45,12 +50,15 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
SSL: kingpin.Flag("ssl", "Secure Portainer instance using SSL").Default(defaultSSL).Bool(),
SSLCert: kingpin.Flag("sslcert", "Path to the SSL certificate used to secure the Portainer instance").Default(defaultSSLCertPath).String(),
SSLKey: kingpin.Flag("sslkey", "Path to the SSL key used to secure the Portainer instance").Default(defaultSSLKeyPath).String(),
SyncInterval: kingpin.Flag("sync-interval", "Duration between each synchronization via the external endpoints source (deprecated)").Default(defaultSyncInterval).String(),
Snapshot: kingpin.Flag("snapshot", "Start a background job to create endpoint snapshots (deprecated)").Default(defaultSnapshot).Bool(),
SnapshotInterval: kingpin.Flag("snapshot-interval", "Duration between each endpoint snapshot job").Default(defaultSnapshotInterval).String(),
AdminPassword: kingpin.Flag("admin-password", "Hashed admin password").String(),
AdminPasswordFile: kingpin.Flag("admin-password-file", "Path to the file containing the password for the admin user").String(),
Labels: pairs(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l')),
Logo: kingpin.Flag("logo", "URL for the logo displayed in the UI").String(),
Templates: kingpin.Flag("templates", "URL to the templates definitions.").Short('t').String(),
TemplateFile: kingpin.Flag("template-file", "Path to the App templates definitions on the filesystem (deprecated)").Default(defaultTemplateFile).String(),
}
kingpin.Parse()
@@ -71,7 +79,26 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
displayDeprecationWarnings(flags)
err := validateEndpointURL(*flags.EndpointURL)
if *flags.EndpointURL != "" && *flags.ExternalEndpoints != "" {
return errEndpointExcludeExternal
}
err := validateTemplateFile(*flags.TemplateFile)
if err != nil {
return err
}
err = validateEndpointURL(*flags.EndpointURL)
if err != nil {
return err
}
err = validateExternalEndpoints(*flags.ExternalEndpoints)
if err != nil {
return err
}
err = validateSyncInterval(*flags.SyncInterval)
if err != nil {
return err
}
@@ -93,9 +120,25 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
}
func displayDeprecationWarnings(flags *portainer.CLIFlags) {
if *flags.ExternalEndpoints != "" {
log.Println("Warning: the --external-endpoint flag is deprecated and will likely be removed in a future version of Portainer.")
}
if *flags.SyncInterval != defaultSyncInterval {
log.Println("Warning: the --sync-interval flag is deprecated and will likely be removed in a future version of Portainer.")
}
if *flags.NoAuth {
log.Println("Warning: the --no-auth flag is deprecated and will likely be removed in a future version of Portainer.")
}
if !*flags.Snapshot {
log.Println("Warning: the --no-snapshot flag is deprecated and will likely be removed in a future version of Portainer.")
}
if *flags.TemplateFile != "" {
log.Println("Warning: the --template-file flag is deprecated and will likely be removed in a future version of Portainer.")
}
}
func validateEndpointURL(endpointURL string) error {
@@ -118,6 +161,38 @@ func validateEndpointURL(endpointURL string) error {
return nil
}
func validateExternalEndpoints(externalEndpoints string) error {
if externalEndpoints != "" {
if _, err := os.Stat(externalEndpoints); err != nil {
if os.IsNotExist(err) {
return errEndpointsFileNotFound
}
return err
}
}
return nil
}
func validateTemplateFile(templateFile string) error {
if _, err := os.Stat(templateFile); err != nil {
if os.IsNotExist(err) {
return errTemplateFileNotFound
}
return err
}
return nil
}
func validateSyncInterval(syncInterval string) error {
if syncInterval != defaultSyncInterval {
_, err := time.ParseDuration(syncInterval)
if err != nil {
return errInvalidSyncInterval
}
}
return nil
}
func validateSnapshotInterval(snapshotInterval string) error {
if snapshotInterval != defaultSnapshotInterval {
_, err := time.ParseDuration(snapshotInterval)

View File

@@ -18,5 +18,8 @@ const (
defaultSSL = "false"
defaultSSLCertPath = "/certs/portainer.crt"
defaultSSLKeyPath = "/certs/portainer.key"
defaultSyncInterval = "60s"
defaultSnapshot = "true"
defaultSnapshotInterval = "5m"
defaultTemplateFile = "/templates.json"
)

View File

@@ -16,5 +16,8 @@ const (
defaultSSL = "false"
defaultSSLCertPath = "C:\\certs\\portainer.crt"
defaultSSLKeyPath = "C:\\certs\\portainer.key"
defaultSyncInterval = "60s"
defaultSnapshot = "true"
defaultSnapshotInterval = "5m"
defaultTemplateFile = "/templates.json"
)

View File

@@ -1,6 +1,7 @@
package main
import (
"encoding/json"
"log"
"os"
"strings"
@@ -8,7 +9,7 @@ import (
"github.com/portainer/portainer/api/chisel"
"github.com/portainer/portainer/api"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt"
"github.com/portainer/portainer/api/cli"
"github.com/portainer/portainer/api/cron"
@@ -25,13 +26,13 @@ import (
)
func initCLI() *portainer.CLIFlags {
var cliService portainer.CLIService = &cli.Service{}
flags, err := cliService.ParseFlags(portainer.APIVersion)
var cli portainer.CLIService = &cli.Service{}
flags, err := cli.ParseFlags(portainer.APIVersion)
if err != nil {
log.Fatal(err)
}
err = cliService.ValidateFlags(flags)
err = cli.ValidateFlags(flags)
if err != nil {
log.Fatal(err)
}
@@ -46,7 +47,7 @@ func initFileService(dataStorePath string) portainer.FileService {
return fileService
}
func initDataStore(dataStorePath string, fileService portainer.FileService) portainer.DataStore {
func initStore(dataStorePath string, fileService portainer.FileService) *bolt.Store {
store, err := bolt.NewStore(dataStorePath, fileService)
if err != nil {
log.Fatal(err)
@@ -69,6 +70,45 @@ func initDataStore(dataStorePath string, fileService portainer.FileService) port
return store
}
func initDemoData(store *bolt.Store, cryptoService portainer.CryptoService) error {
password, err := cryptoService.Hash("tryportainer")
if err != nil {
return err
}
admin := &portainer.User{
Username: "admin",
Password: password,
Role: portainer.AdministratorRole,
}
err = store.UserService.CreateUser(admin)
if err != nil {
return err
}
localEndpoint := &portainer.Endpoint{
ID: portainer.EndpointID(1),
Name: "local",
URL: "unix:///var/run/docker.sock",
PublicURL: "demo.portainer.io",
TLSConfig: portainer.TLSConfiguration{
TLS: false,
},
AuthorizedUsers: []portainer.UserID{},
AuthorizedTeams: []portainer.TeamID{},
Extensions: []portainer.EndpointExtension{},
Tags: []string{},
}
err = store.EndpointService.CreateEndpoint(localEndpoint)
if err != nil {
return err
}
return nil
}
func initComposeStackManager(dataStorePath string, reverseTunnelService portainer.ReverseTunnelService) portainer.ComposeStackManager {
return libcompose.NewComposeStackManager(dataStorePath, reverseTunnelService)
}
@@ -116,13 +156,13 @@ func initJobScheduler() portainer.JobScheduler {
return cron.NewJobScheduler()
}
func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter portainer.Snapshotter, dataStore portainer.DataStore) error {
settings, err := dataStore.Settings().Settings()
func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter portainer.Snapshotter, scheduleService portainer.ScheduleService, endpointService portainer.EndpointService, settingsService portainer.SettingsService) error {
settings, err := settingsService.Settings()
if err != nil {
return err
}
schedules, err := dataStore.Schedule().SchedulesByJobType(portainer.SnapshotJobType)
schedules, err := scheduleService.SchedulesByJobType(portainer.SnapshotJobType)
if err != nil {
return err
}
@@ -131,7 +171,7 @@ func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter
if len(schedules) == 0 {
snapshotJob := &portainer.SnapshotJob{}
snapshotSchedule = &portainer.Schedule{
ID: portainer.ScheduleID(dataStore.Schedule().GetNextIdentifier()),
ID: portainer.ScheduleID(scheduleService.GetNextIdentifier()),
Name: "system_snapshot",
CronExpression: "@every " + settings.SnapshotInterval,
Recurring: true,
@@ -143,7 +183,7 @@ func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter
snapshotSchedule = &schedules[0]
}
snapshotJobContext := cron.NewSnapshotJobContext(dataStore, snapshotter)
snapshotJobContext := cron.NewSnapshotJobContext(endpointService, snapshotter)
snapshotJobRunner := cron.NewSnapshotJobRunner(snapshotSchedule, snapshotJobContext)
err = jobScheduler.ScheduleJob(snapshotJobRunner)
@@ -152,13 +192,52 @@ func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter
}
if len(schedules) == 0 {
return dataStore.Schedule().CreateSchedule(snapshotSchedule)
return scheduleService.CreateSchedule(snapshotSchedule)
}
return nil
}
func loadSchedulesFromDatabase(jobScheduler portainer.JobScheduler, jobService portainer.JobService, dataStore portainer.DataStore, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) error {
schedules, err := dataStore.Schedule().Schedules()
func loadEndpointSyncSystemSchedule(jobScheduler portainer.JobScheduler, scheduleService portainer.ScheduleService, endpointService portainer.EndpointService, flags *portainer.CLIFlags) error {
if *flags.ExternalEndpoints == "" {
return nil
}
log.Println("Using external endpoint definition. Endpoint management via the API will be disabled.")
schedules, err := scheduleService.SchedulesByJobType(portainer.EndpointSyncJobType)
if err != nil {
return err
}
if len(schedules) != 0 {
return nil
}
endpointSyncJob := &portainer.EndpointSyncJob{}
endpointSyncSchedule := &portainer.Schedule{
ID: portainer.ScheduleID(scheduleService.GetNextIdentifier()),
Name: "system_endpointsync",
CronExpression: "@every " + *flags.SyncInterval,
Recurring: true,
JobType: portainer.EndpointSyncJobType,
EndpointSyncJob: endpointSyncJob,
Created: time.Now().Unix(),
}
endpointSyncJobContext := cron.NewEndpointSyncJobContext(endpointService, *flags.ExternalEndpoints)
endpointSyncJobRunner := cron.NewEndpointSyncJobRunner(endpointSyncSchedule, endpointSyncJobContext)
err = jobScheduler.ScheduleJob(endpointSyncJobRunner)
if err != nil {
return err
}
return scheduleService.CreateSchedule(endpointSyncSchedule)
}
func loadSchedulesFromDatabase(jobScheduler portainer.JobScheduler, jobService portainer.JobService, scheduleService portainer.ScheduleService, endpointService portainer.EndpointService, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) error {
schedules, err := scheduleService.Schedules()
if err != nil {
return err
}
@@ -166,7 +245,7 @@ func loadSchedulesFromDatabase(jobScheduler portainer.JobScheduler, jobService p
for _, schedule := range schedules {
if schedule.JobType == portainer.ScriptExecutionJobType {
jobContext := cron.NewScriptExecutionJobContext(jobService, dataStore, fileService)
jobContext := cron.NewScriptExecutionJobContext(jobService, endpointService, fileService)
jobRunner := cron.NewScriptExecutionJobRunner(&schedule, jobContext)
err = jobScheduler.ScheduleJob(jobRunner)
@@ -186,32 +265,124 @@ func loadSchedulesFromDatabase(jobScheduler portainer.JobScheduler, jobService p
return nil
}
func initStatus(flags *portainer.CLIFlags) *portainer.Status {
func initStatus(endpointManagement, snapshot bool, flags *portainer.CLIFlags) *portainer.Status {
return &portainer.Status{
Analytics: !*flags.NoAnalytics,
Authentication: !*flags.NoAuth,
Version: portainer.APIVersion,
Analytics: !*flags.NoAnalytics,
Authentication: !*flags.NoAuth,
EndpointManagement: endpointManagement,
Snapshot: snapshot,
Version: portainer.APIVersion,
}
}
func updateSettingsFromFlags(dataStore portainer.DataStore, flags *portainer.CLIFlags) error {
settings, err := dataStore.Settings().Settings()
func initDockerHub(dockerHubService portainer.DockerHubService) error {
_, err := dockerHubService.DockerHub()
if err == portainer.ErrObjectNotFound {
dockerhub := &portainer.DockerHub{
Authentication: false,
Username: "",
Password: "",
}
return dockerHubService.UpdateDockerHub(dockerhub)
} else if err != nil {
return err
}
return nil
}
func initSettings(settingsService portainer.SettingsService, flags *portainer.CLIFlags) error {
_, err := settingsService.Settings()
if err == portainer.ErrObjectNotFound {
settings := &portainer.Settings{
LogoURL: *flags.Logo,
AuthenticationMethod: portainer.AuthenticationInternal,
LDAPSettings: portainer.LDAPSettings{
AnonymousMode: true,
AutoCreateUsers: true,
TLSConfig: portainer.TLSConfiguration{},
SearchSettings: []portainer.LDAPSearchSettings{
portainer.LDAPSearchSettings{},
},
GroupSearchSettings: []portainer.LDAPGroupSearchSettings{
portainer.LDAPGroupSearchSettings{},
},
},
OAuthSettings: portainer.OAuthSettings{},
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowVolumeBrowserForRegularUsers: false,
AllowDeviceMappingForRegularUsers: true,
AllowStackManagementForRegularUsers: true,
EnableHostManagementFeatures: false,
AllowHostNamespaceForRegularUsers: true,
SnapshotInterval: *flags.SnapshotInterval,
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
}
if *flags.Templates != "" {
settings.TemplatesURL = *flags.Templates
}
if *flags.Labels != nil {
settings.BlackListedLabels = *flags.Labels
} else {
settings.BlackListedLabels = make([]portainer.Pair, 0)
}
return settingsService.UpdateSettings(settings)
} else if err != nil {
return err
}
return nil
}
func initTemplates(templateService portainer.TemplateService, fileService portainer.FileService, templateURL, templateFile string) error {
if templateURL != "" {
log.Printf("Portainer started with the --templates flag. Using external templates, template management will be disabled.")
return nil
}
existingTemplates, err := templateService.Templates()
if err != nil {
return err
}
settings.LogoURL = *flags.Logo
settings.SnapshotInterval = *flags.SnapshotInterval
if *flags.Templates != "" {
settings.TemplatesURL = *flags.Templates
if len(existingTemplates) != 0 {
log.Printf("Templates already registered inside the database. Skipping template import.")
return nil
}
if *flags.Labels != nil {
settings.BlackListedLabels = *flags.Labels
templatesJSON, err := fileService.GetFileContent(templateFile)
if err != nil {
log.Println("Unable to retrieve template definitions via filesystem")
return err
}
return dataStore.Settings().UpdateSettings(settings)
var templates []portainer.Template
err = json.Unmarshal(templatesJSON, &templates)
if err != nil {
log.Println("Unable to parse templates file. Please review your template definition file.")
return err
}
for _, template := range templates {
err := templateService.CreateTemplate(&template)
if err != nil {
return err
}
}
return nil
}
func retrieveFirstEndpointFromDatabase(endpointService portainer.EndpointService) *portainer.Endpoint {
endpoints, err := endpointService.Endpoints()
if err != nil {
log.Fatal(err)
}
return &endpoints[0]
}
func loadAndParseKeyPair(fileService portainer.FileService, signatureService portainer.DigitalSignatureService) error {
@@ -243,7 +414,7 @@ func initKeyPair(fileService portainer.FileService, signatureService portainer.D
return generateAndStoreKeyPair(fileService, signatureService)
}
func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore portainer.DataStore, snapshotter portainer.Snapshotter) error {
func createTLSSecuredEndpoint(flags *portainer.CLIFlags, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
tlsConfiguration := portainer.TLSConfiguration{
TLS: *flags.TLS,
TLSSkipVerify: *flags.TLSSkipVerify,
@@ -257,7 +428,7 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore portainer.Dat
tlsConfiguration.TLS = true
}
endpointID := dataStore.Endpoint().GetNextIdentifier()
endpointID := endpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: "primary",
@@ -289,10 +460,10 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore portainer.Dat
}
}
return snapshotAndPersistEndpoint(endpoint, dataStore, snapshotter)
return snapshotAndPersistEndpoint(endpoint, endpointService, snapshotter)
}
func createUnsecuredEndpoint(endpointURL string, dataStore portainer.DataStore, snapshotter portainer.Snapshotter) error {
func createUnsecuredEndpoint(endpointURL string, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
if strings.HasPrefix(endpointURL, "tcp://") {
_, err := client.ExecutePingOperation(endpointURL, nil)
if err != nil {
@@ -300,7 +471,7 @@ func createUnsecuredEndpoint(endpointURL string, dataStore portainer.DataStore,
}
}
endpointID := dataStore.Endpoint().GetNextIdentifier()
endpointID := endpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: "primary",
@@ -316,10 +487,10 @@ func createUnsecuredEndpoint(endpointURL string, dataStore portainer.DataStore,
Snapshots: []portainer.Snapshot{},
}
return snapshotAndPersistEndpoint(endpoint, dataStore, snapshotter)
return snapshotAndPersistEndpoint(endpoint, endpointService, snapshotter)
}
func snapshotAndPersistEndpoint(endpoint *portainer.Endpoint, dataStore portainer.DataStore, snapshotter portainer.Snapshotter) error {
func snapshotAndPersistEndpoint(endpoint *portainer.Endpoint, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
snapshot, err := snapshotter.CreateSnapshot(endpoint)
endpoint.Status = portainer.EndpointStatusUp
if err != nil {
@@ -330,15 +501,15 @@ func snapshotAndPersistEndpoint(endpoint *portainer.Endpoint, dataStore portaine
endpoint.Snapshots = []portainer.Snapshot{*snapshot}
}
return dataStore.Endpoint().CreateEndpoint(endpoint)
return endpointService.CreateEndpoint(endpoint)
}
func initEndpoint(flags *portainer.CLIFlags, dataStore portainer.DataStore, snapshotter portainer.Snapshotter) error {
func initEndpoint(flags *portainer.CLIFlags, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
if *flags.EndpointURL == "" {
return nil
}
endpoints, err := dataStore.Endpoint().Endpoints()
endpoints, err := endpointService.Endpoints()
if err != nil {
return err
}
@@ -349,17 +520,17 @@ func initEndpoint(flags *portainer.CLIFlags, dataStore portainer.DataStore, snap
}
if *flags.TLS || *flags.TLSSkipVerify {
return createTLSSecuredEndpoint(flags, dataStore, snapshotter)
return createTLSSecuredEndpoint(flags, endpointService, snapshotter)
}
return createUnsecuredEndpoint(*flags.EndpointURL, dataStore, snapshotter)
return createUnsecuredEndpoint(*flags.EndpointURL, endpointService, snapshotter)
}
func initJobService(dockerClientFactory *docker.ClientFactory) portainer.JobService {
return docker.NewJobService(dockerClientFactory)
}
func initExtensionManager(fileService portainer.FileService, dataStore portainer.DataStore) (portainer.ExtensionManager, error) {
extensionManager := exec.NewExtensionManager(fileService, dataStore)
func initExtensionManager(fileService portainer.FileService, extensionService portainer.ExtensionService) (portainer.ExtensionManager, error) {
extensionManager := exec.NewExtensionManager(fileService, extensionService)
err := extensionManager.StartExtensions()
if err != nil {
@@ -369,11 +540,11 @@ func initExtensionManager(fileService portainer.FileService, dataStore portainer
return extensionManager, nil
}
func terminateIfNoAdminCreated(dataStore portainer.DataStore) {
func terminateIfNoAdminCreated(userService portainer.UserService) {
timer1 := time.NewTimer(5 * time.Minute)
<-timer1.C
users, err := dataStore.User().UsersByRole(portainer.AdministratorRole)
users, err := userService.UsersByRole(portainer.AdministratorRole)
if err != nil {
log.Fatal(err)
}
@@ -389,8 +560,8 @@ func main() {
fileService := initFileService(*flags.Data)
dataStore := initDataStore(*flags.Data, fileService)
defer dataStore.Close()
store := initStore(*flags.Data, fileService)
defer store.Close()
jwtService := initJWTService(!*flags.NoAuth)
@@ -400,6 +571,8 @@ func main() {
cryptoService := initCryptoService()
initDemoData(store, cryptoService)
digitalSignatureService := initDigitalSignatureService()
err := initKeyPair(fileService, digitalSignatureService)
@@ -407,12 +580,12 @@ func main() {
log.Fatal(err)
}
extensionManager, err := initExtensionManager(fileService, dataStore)
extensionManager, err := initExtensionManager(fileService, store.ExtensionService)
if err != nil {
log.Fatal(err)
}
reverseTunnelService := chisel.NewService(dataStore)
reverseTunnelService := chisel.NewService(store.EndpointService, store.TunnelServerService)
clientFactory := initClientFactory(digitalSignatureService, reverseTunnelService)
@@ -420,6 +593,11 @@ func main() {
snapshotter := initSnapshotter(clientFactory)
endpointManagement := true
if *flags.ExternalEndpoints != "" {
endpointManagement = false
}
swarmStackManager, err := initSwarmStackManager(*flags.Assets, *flags.Data, digitalSignatureService, fileService, reverseTunnelService)
if err != nil {
log.Fatal(err)
@@ -427,30 +605,45 @@ func main() {
composeStackManager := initComposeStackManager(*flags.Data, reverseTunnelService)
if dataStore.IsNew() {
err = updateSettingsFromFlags(dataStore, flags)
err = initTemplates(store.TemplateService, fileService, *flags.Templates, *flags.TemplateFile)
if err != nil {
log.Fatal(err)
}
err = initSettings(store.SettingsService, flags)
if err != nil {
log.Fatal(err)
}
jobScheduler := initJobScheduler()
err = loadSchedulesFromDatabase(jobScheduler, jobService, store.ScheduleService, store.EndpointService, fileService, reverseTunnelService)
if err != nil {
log.Fatal(err)
}
err = loadEndpointSyncSystemSchedule(jobScheduler, store.ScheduleService, store.EndpointService, flags)
if err != nil {
log.Fatal(err)
}
if *flags.Snapshot {
err = loadSnapshotSystemSchedule(jobScheduler, snapshotter, store.ScheduleService, store.EndpointService, store.SettingsService)
if err != nil {
log.Fatal(err)
}
}
jobScheduler := initJobScheduler()
err = loadSchedulesFromDatabase(jobScheduler, jobService, dataStore, fileService, reverseTunnelService)
if err != nil {
log.Fatal(err)
}
err = loadSnapshotSystemSchedule(jobScheduler, snapshotter, dataStore)
if err != nil {
log.Fatal(err)
}
jobScheduler.Start()
applicationStatus := initStatus(flags)
err = initDockerHub(store.DockerHubService)
if err != nil {
log.Fatal(err)
}
err = initEndpoint(flags, dataStore, snapshotter)
applicationStatus := initStatus(endpointManagement, *flags.Snapshot, flags)
err = initEndpoint(flags, store.EndpointService, snapshotter)
if err != nil {
log.Fatal(err)
}
@@ -470,7 +663,7 @@ func main() {
}
if adminPasswordHash != "" {
users, err := dataStore.User().UsersByRole(portainer.AdministratorRole)
users, err := store.UserService.UsersByRole(portainer.AdministratorRole)
if err != nil {
log.Fatal(err)
}
@@ -483,7 +676,7 @@ func main() {
Password: adminPasswordHash,
PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(),
}
err := dataStore.User().CreateUser(user)
err := store.UserService.CreateUser(user)
if err != nil {
log.Fatal(err)
}
@@ -493,7 +686,7 @@ func main() {
}
if !*flags.NoAuth {
go terminateIfNoAdminCreated(dataStore)
go terminateIfNoAdminCreated(store.UserService)
}
err = reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotter)
@@ -502,28 +695,47 @@ func main() {
}
var server portainer.Server = &http.Server{
ReverseTunnelService: reverseTunnelService,
Status: applicationStatus,
BindAddress: *flags.Addr,
AssetsPath: *flags.Assets,
AuthDisabled: *flags.NoAuth,
DataStore: dataStore,
SwarmStackManager: swarmStackManager,
ComposeStackManager: composeStackManager,
ExtensionManager: extensionManager,
CryptoService: cryptoService,
JWTService: jwtService,
FileService: fileService,
LDAPService: ldapService,
GitService: gitService,
SignatureService: digitalSignatureService,
JobScheduler: jobScheduler,
Snapshotter: snapshotter,
SSL: *flags.SSL,
SSLCert: *flags.SSLCert,
SSLKey: *flags.SSLKey,
DockerClientFactory: clientFactory,
JobService: jobService,
ReverseTunnelService: reverseTunnelService,
Status: applicationStatus,
BindAddress: *flags.Addr,
AssetsPath: *flags.Assets,
AuthDisabled: *flags.NoAuth,
EndpointManagement: endpointManagement,
RoleService: store.RoleService,
UserService: store.UserService,
TeamService: store.TeamService,
TeamMembershipService: store.TeamMembershipService,
EdgeGroupService: store.EdgeGroupService,
EdgeStackService: store.EdgeStackService,
EndpointService: store.EndpointService,
EndpointGroupService: store.EndpointGroupService,
EndpointRelationService: store.EndpointRelationService,
ExtensionService: store.ExtensionService,
ResourceControlService: store.ResourceControlService,
SettingsService: store.SettingsService,
RegistryService: store.RegistryService,
DockerHubService: store.DockerHubService,
StackService: store.StackService,
ScheduleService: store.ScheduleService,
TagService: store.TagService,
TemplateService: store.TemplateService,
WebhookService: store.WebhookService,
SwarmStackManager: swarmStackManager,
ComposeStackManager: composeStackManager,
ExtensionManager: extensionManager,
CryptoService: cryptoService,
JWTService: jwtService,
FileService: fileService,
LDAPService: ldapService,
GitService: gitService,
SignatureService: digitalSignatureService,
JobScheduler: jobScheduler,
Snapshotter: snapshotter,
SSL: *flags.SSL,
SSLCert: *flags.SSLCert,
SSLKey: *flags.SSLKey,
DockerClientFactory: clientFactory,
JobService: jobService,
}
log.Printf("Starting Portainer %s on %s", portainer.APIVersion, *flags.Addr)

View File

@@ -0,0 +1,214 @@
package cron
import (
"encoding/json"
"io/ioutil"
"log"
"strings"
"github.com/portainer/portainer/api"
)
// EndpointSyncJobRunner is used to run a EndpointSyncJob
type EndpointSyncJobRunner struct {
schedule *portainer.Schedule
context *EndpointSyncJobContext
}
// EndpointSyncJobContext represents the context of execution of a EndpointSyncJob
type EndpointSyncJobContext struct {
endpointService portainer.EndpointService
endpointFilePath string
}
// NewEndpointSyncJobContext returns a new context that can be used to execute a EndpointSyncJob
func NewEndpointSyncJobContext(endpointService portainer.EndpointService, endpointFilePath string) *EndpointSyncJobContext {
return &EndpointSyncJobContext{
endpointService: endpointService,
endpointFilePath: endpointFilePath,
}
}
// NewEndpointSyncJobRunner returns a new runner that can be scheduled
func NewEndpointSyncJobRunner(schedule *portainer.Schedule, context *EndpointSyncJobContext) *EndpointSyncJobRunner {
return &EndpointSyncJobRunner{
schedule: schedule,
context: context,
}
}
type synchronization struct {
endpointsToCreate []*portainer.Endpoint
endpointsToUpdate []*portainer.Endpoint
endpointsToDelete []*portainer.Endpoint
}
type fileEndpoint struct {
Name string `json:"Name"`
URL string `json:"URL"`
TLS bool `json:"TLS,omitempty"`
TLSSkipVerify bool `json:"TLSSkipVerify,omitempty"`
TLSCACert string `json:"TLSCACert,omitempty"`
TLSCert string `json:"TLSCert,omitempty"`
TLSKey string `json:"TLSKey,omitempty"`
}
// GetSchedule returns the schedule associated to the runner
func (runner *EndpointSyncJobRunner) GetSchedule() *portainer.Schedule {
return runner.schedule
}
// Run triggers the execution of the endpoint synchronization process.
func (runner *EndpointSyncJobRunner) Run() {
data, err := ioutil.ReadFile(runner.context.endpointFilePath)
if endpointSyncError(err) {
return
}
var fileEndpoints []fileEndpoint
err = json.Unmarshal(data, &fileEndpoints)
if endpointSyncError(err) {
return
}
if len(fileEndpoints) == 0 {
log.Println("background job error (endpoint synchronization). External endpoint source is empty")
return
}
storedEndpoints, err := runner.context.endpointService.Endpoints()
if endpointSyncError(err) {
return
}
convertedFileEndpoints := convertFileEndpoints(fileEndpoints)
sync := prepareSyncData(storedEndpoints, convertedFileEndpoints)
if sync.requireSync() {
err = runner.context.endpointService.Synchronize(sync.endpointsToCreate, sync.endpointsToUpdate, sync.endpointsToDelete)
if endpointSyncError(err) {
return
}
log.Printf("Endpoint synchronization ended. [created: %v] [updated: %v] [deleted: %v]", len(sync.endpointsToCreate), len(sync.endpointsToUpdate), len(sync.endpointsToDelete))
}
}
func endpointSyncError(err error) bool {
if err != nil {
log.Printf("background job error (endpoint synchronization). Unable to synchronize endpoints (err=%s)\n", err)
return true
}
return false
}
func isValidEndpoint(endpoint *portainer.Endpoint) bool {
if endpoint.Name != "" && endpoint.URL != "" {
if !strings.HasPrefix(endpoint.URL, "unix://") && !strings.HasPrefix(endpoint.URL, "tcp://") {
return false
}
return true
}
return false
}
func convertFileEndpoints(fileEndpoints []fileEndpoint) []portainer.Endpoint {
convertedEndpoints := make([]portainer.Endpoint, 0)
for _, e := range fileEndpoints {
endpoint := portainer.Endpoint{
Name: e.Name,
URL: e.URL,
TLSConfig: portainer.TLSConfiguration{},
}
if e.TLS {
endpoint.TLSConfig.TLS = true
endpoint.TLSConfig.TLSSkipVerify = e.TLSSkipVerify
endpoint.TLSConfig.TLSCACertPath = e.TLSCACert
endpoint.TLSConfig.TLSCertPath = e.TLSCert
endpoint.TLSConfig.TLSKeyPath = e.TLSKey
}
convertedEndpoints = append(convertedEndpoints, endpoint)
}
return convertedEndpoints
}
func endpointExists(endpoint *portainer.Endpoint, endpoints []portainer.Endpoint) int {
for idx, v := range endpoints {
if endpoint.Name == v.Name && isValidEndpoint(&v) {
return idx
}
}
return -1
}
func mergeEndpointIfRequired(original, updated *portainer.Endpoint) *portainer.Endpoint {
var endpoint *portainer.Endpoint
if original.URL != updated.URL || original.TLSConfig.TLS != updated.TLSConfig.TLS ||
(updated.TLSConfig.TLS && original.TLSConfig.TLSSkipVerify != updated.TLSConfig.TLSSkipVerify) ||
(updated.TLSConfig.TLS && original.TLSConfig.TLSCACertPath != updated.TLSConfig.TLSCACertPath) ||
(updated.TLSConfig.TLS && original.TLSConfig.TLSCertPath != updated.TLSConfig.TLSCertPath) ||
(updated.TLSConfig.TLS && original.TLSConfig.TLSKeyPath != updated.TLSConfig.TLSKeyPath) {
endpoint = original
endpoint.URL = updated.URL
if updated.TLSConfig.TLS {
endpoint.TLSConfig.TLS = true
endpoint.TLSConfig.TLSSkipVerify = updated.TLSConfig.TLSSkipVerify
endpoint.TLSConfig.TLSCACertPath = updated.TLSConfig.TLSCACertPath
endpoint.TLSConfig.TLSCertPath = updated.TLSConfig.TLSCertPath
endpoint.TLSConfig.TLSKeyPath = updated.TLSConfig.TLSKeyPath
} else {
endpoint.TLSConfig.TLS = false
endpoint.TLSConfig.TLSSkipVerify = false
endpoint.TLSConfig.TLSCACertPath = ""
endpoint.TLSConfig.TLSCertPath = ""
endpoint.TLSConfig.TLSKeyPath = ""
}
}
return endpoint
}
func (sync synchronization) requireSync() bool {
if len(sync.endpointsToCreate) != 0 || len(sync.endpointsToUpdate) != 0 || len(sync.endpointsToDelete) != 0 {
return true
}
return false
}
func prepareSyncData(storedEndpoints, fileEndpoints []portainer.Endpoint) *synchronization {
endpointsToCreate := make([]*portainer.Endpoint, 0)
endpointsToUpdate := make([]*portainer.Endpoint, 0)
endpointsToDelete := make([]*portainer.Endpoint, 0)
for idx := range storedEndpoints {
fidx := endpointExists(&storedEndpoints[idx], fileEndpoints)
if fidx != -1 {
endpoint := mergeEndpointIfRequired(&storedEndpoints[idx], &fileEndpoints[fidx])
if endpoint != nil {
log.Printf("New definition for a stored endpoint found in file, updating database. [name: %v] [url: %v]\n", endpoint.Name, endpoint.URL)
endpointsToUpdate = append(endpointsToUpdate, endpoint)
}
} else {
log.Printf("Stored endpoint not found in file (definition might be invalid), removing from database. [name: %v] [url: %v]", storedEndpoints[idx].Name, storedEndpoints[idx].URL)
endpointsToDelete = append(endpointsToDelete, &storedEndpoints[idx])
}
}
for idx, endpoint := range fileEndpoints {
if !isValidEndpoint(&endpoint) {
log.Printf("Invalid file endpoint definition, skipping. [name: %v] [url: %v]", endpoint.Name, endpoint.URL)
continue
}
sidx := endpointExists(&fileEndpoints[idx], storedEndpoints)
if sidx == -1 {
log.Printf("File endpoint not found in database, adding to database. [name: %v] [url: %v]", fileEndpoints[idx].Name, fileEndpoints[idx].URL)
endpointsToCreate = append(endpointsToCreate, &fileEndpoints[idx])
}
}
return &synchronization{
endpointsToCreate: endpointsToCreate,
endpointsToUpdate: endpointsToUpdate,
endpointsToDelete: endpointsToDelete,
}
}

View File

@@ -16,17 +16,17 @@ type ScriptExecutionJobRunner struct {
// ScriptExecutionJobContext represents the context of execution of a ScriptExecutionJob
type ScriptExecutionJobContext struct {
dataStore portainer.DataStore
jobService portainer.JobService
fileService portainer.FileService
jobService portainer.JobService
endpointService portainer.EndpointService
fileService portainer.FileService
}
// NewScriptExecutionJobContext returns a new context that can be used to execute a ScriptExecutionJob
func NewScriptExecutionJobContext(jobService portainer.JobService, dataStore portainer.DataStore, fileService portainer.FileService) *ScriptExecutionJobContext {
func NewScriptExecutionJobContext(jobService portainer.JobService, endpointService portainer.EndpointService, fileService portainer.FileService) *ScriptExecutionJobContext {
return &ScriptExecutionJobContext{
jobService: jobService,
dataStore: dataStore,
fileService: fileService,
jobService: jobService,
endpointService: endpointService,
fileService: fileService,
}
}
@@ -56,7 +56,7 @@ func (runner *ScriptExecutionJobRunner) Run() {
targets := make([]*portainer.Endpoint, 0)
for _, endpointID := range runner.schedule.ScriptExecutionJob.Endpoints {
endpoint, err := runner.context.dataStore.Endpoint().Endpoint(endpointID)
endpoint, err := runner.context.endpointService.Endpoint(endpointID)
if err != nil {
log.Printf("scheduled job error (script execution). Unable to retrieve information about endpoint (id=%d) (err=%s)\n", endpointID, err)
return

View File

@@ -14,15 +14,15 @@ type SnapshotJobRunner struct {
// SnapshotJobContext represents the context of execution of a SnapshotJob
type SnapshotJobContext struct {
dataStore portainer.DataStore
snapshotter portainer.Snapshotter
endpointService portainer.EndpointService
snapshotter portainer.Snapshotter
}
// NewSnapshotJobContext returns a new context that can be used to execute a SnapshotJob
func NewSnapshotJobContext(dataStore portainer.DataStore, snapshotter portainer.Snapshotter) *SnapshotJobContext {
func NewSnapshotJobContext(endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) *SnapshotJobContext {
return &SnapshotJobContext{
dataStore: dataStore,
snapshotter: snapshotter,
endpointService: endpointService,
snapshotter: snapshotter,
}
}
@@ -46,20 +46,20 @@ func (runner *SnapshotJobRunner) GetSchedule() *portainer.Schedule {
// retrieve the latest version of the endpoint right after a snapshot.
func (runner *SnapshotJobRunner) Run() {
go func() {
endpoints, err := runner.context.dataStore.Endpoint().Endpoints()
endpoints, err := runner.context.endpointService.Endpoints()
if err != nil {
log.Printf("background schedule error (endpoint snapshot). Unable to retrieve endpoint list (err=%s)\n", err)
return
}
for _, endpoint := range endpoints {
if endpoint.Type == portainer.EdgeAgentEnvironment {
if endpoint.Type == portainer.AzureEnvironment || endpoint.Type == portainer.EdgeAgentEnvironment {
continue
}
snapshot, snapshotError := runner.context.snapshotter.CreateSnapshot(&endpoint)
latestEndpointReference, err := runner.context.dataStore.Endpoint().Endpoint(endpoint.ID)
latestEndpointReference, err := runner.context.endpointService.Endpoint(endpoint.ID)
if latestEndpointReference == nil {
log.Printf("background schedule error (endpoint snapshot). Endpoint not found inside the database anymore (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
continue
@@ -75,7 +75,7 @@ func (runner *SnapshotJobRunner) Run() {
latestEndpointReference.Snapshots = []portainer.Snapshot{*snapshot}
}
err = runner.context.dataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
err = runner.context.endpointService.UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
if err != nil {
log.Printf("background schedule error (endpoint snapshot). Unable to update endpoint (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
return

View File

@@ -6,6 +6,24 @@ import (
"io/ioutil"
)
// CreateTLSConfiguration creates a basic tls.Config to be used by servers with recommended TLS settings
func CreateServerTLSConfiguration() *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
}
}
// CreateTLSConfigurationFromBytes initializes a tls.Config using a CA certificate, a certificate and a key
// loaded from memory.
func CreateTLSConfigurationFromBytes(caCert, cert, key []byte, skipClientVerification, skipServerVerification bool) (*tls.Config, error) {

View File

@@ -35,7 +35,9 @@ func NewClientFactory(signatureService portainer.DigitalSignatureService, revers
// a specific endpoint configuration. The nodeName parameter can be used
// with an agent enabled endpoint to target a specific node in an agent cluster.
func (factory *ClientFactory) CreateClient(endpoint *portainer.Endpoint, nodeName string) (*client.Client, error) {
if endpoint.Type == portainer.AgentOnDockerEnvironment {
if endpoint.Type == portainer.AzureEnvironment {
return nil, unsupportedEnvironmentType
} else if endpoint.Type == portainer.AgentOnDockerEnvironment {
return createAgentClient(endpoint, factory.signatureService, nodeName)
} else if endpoint.Type == portainer.EdgeAgentEnvironment {
return createEdgeClient(endpoint, factory.reverseTunnelService, nodeName)

View File

@@ -39,6 +39,11 @@ const (
ErrEndpointAccessDenied = Error("Access denied to endpoint")
)
// Azure environment errors
const (
ErrAzureInvalidCredentials = Error("Invalid Azure credentials")
)
// Endpoint group errors.
const (
ErrCannotRemoveDefaultGroup = Error("Cannot remove the default endpoint group")
@@ -84,6 +89,11 @@ const (
ErrUndefinedTLSFileType = Error("Undefined TLS file type")
)
// Demo errors.
const (
ErrNotAvailableInDemo = Error("This feature is not available in the demo version of Portainer")
)
// Extension errors.
const (
ErrExtensionAlreadyEnabled = Error("This extension is already enabled")

View File

@@ -34,17 +34,17 @@ var extensionBinaryMap = map[portainer.ExtensionID]string{
// ExtensionManager represents a service used to
// manage extension processes.
type ExtensionManager struct {
processes cmap.ConcurrentMap
fileService portainer.FileService
dataStore portainer.DataStore
processes cmap.ConcurrentMap
fileService portainer.FileService
extensionService portainer.ExtensionService
}
// NewExtensionManager returns a pointer to an ExtensionManager
func NewExtensionManager(fileService portainer.FileService, dataStore portainer.DataStore) *ExtensionManager {
func NewExtensionManager(fileService portainer.FileService, extensionService portainer.ExtensionService) *ExtensionManager {
return &ExtensionManager{
processes: cmap.New(),
fileService: fileService,
dataStore: dataStore,
processes: cmap.New(),
fileService: fileService,
extensionService: extensionService,
}
}
@@ -188,7 +188,7 @@ func (manager *ExtensionManager) DisableExtension(extension *portainer.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.dataStore.Extension().Extensions()
extensions, err := manager.extensionService.Extensions()
if err != nil {
return err
}
@@ -224,7 +224,7 @@ func (manager *ExtensionManager) updateAndStartExtensions(extensions []portainer
}
}
err := manager.dataStore.Extension().Persist(&extension)
err := manager.extensionService.Persist(&extension)
if err != nil {
return err
}

View File

@@ -13,7 +13,6 @@ require (
github.com/docker/cli v0.0.0-20191126203649-54d085b857e9
github.com/docker/docker v0.0.0-00010101000000-000000000000
github.com/g07cha/defender v0.0.0-20180505193036-5665c627c814
github.com/go-ldap/ldap/v3 v3.1.8
github.com/gofrs/uuid v3.2.0+incompatible
github.com/gorilla/mux v1.7.3
github.com/gorilla/securecookie v1.1.1
@@ -31,7 +30,11 @@ require (
github.com/robfig/cron/v3 v3.0.0
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/asn1-ber.v1 v1.0.0-00010101000000-000000000000 // indirect
gopkg.in/ldap.v2 v2.5.1
gopkg.in/src-d/go-git.v4 v4.13.1
)
replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.20200204220554-5f6d6f3f2203
replace gopkg.in/asn1-ber.v1 => github.com/go-asn1-ber/asn1-ber v1.3.1

View File

@@ -50,8 +50,12 @@ github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BU
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/engine v1.4.2-0.20191127222017-3152f9436292 h1:qQ7mw+CVWpRj5DWBL4CVHtBbGQdlPCj4j1evDh0ethw=
github.com/docker/engine v1.4.2-0.20191127222017-3152f9436292/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
github.com/docker/engine v1.4.2-0.20200204220554-5f6d6f3f2203 h1:QeBh8wW8pIZKlXxlMOQ8hSCMdJA+2Z/bD/iDyCAS8XU=
github.com/docker/engine v1.4.2-0.20200204220554-5f6d6f3f2203/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
github.com/docker/engine v1.13.1 h1:Cks33UT9YBW5Xyc3MtGDq2IPgqfJtJ+qkFaxc2b0Euc=
github.com/docker/engine v1.13.1/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA=
@@ -73,8 +77,6 @@ github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev
github.com/go-asn1-ber/asn1-ber v1.3.1 h1:gvPdv/Hr++TRFCl0UbPFHC54P9N9jgsRPnmnr419Uck=
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap/v3 v3.1.8 h1:5vU/2jOh9HqprwXp8aF915s9p6Z8wmbSEVF7/gdTFhM=
github.com/go-ldap/ldap/v3 v3.1.8/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -138,6 +140,8 @@ github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3Zk
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microsoft/go-winio v0.4.8 h1:N4SmTFXUK7/jnn/UG/gm2mrHiYu9LVGvtsvULyody/c=
github.com/microsoft/go-winio v0.4.8/go.mod h1:kcIxxtKZE55DEncT/EOvFiygPobhUWpSDqDb47poQOU=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
@@ -171,7 +175,6 @@ github.com/portainer/libcrypto v0.0.0-20190723020515-23ebe86ab2c2 h1:0PfgGLys9yH
github.com/portainer/libcrypto v0.0.0-20190723020515-23ebe86ab2c2/go.mod h1:/wIeGwJOMYc1JplE/OvYMO5korce39HddIfI8VKGyAM=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33 h1:H8HR2dHdBf8HANSkUyVw4o8+4tegGcd+zyKZ3e599II=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33/go.mod h1:Y2TfgviWI4rT2qaOTHr+hq6MdKIE5YjgQAu7qwptTV0=
github.com/portainer/portainer v0.10.1 h1:I8K345CjGWfUGsVA8c8/gqamwLCC6CIAjxZXSklAFq0=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
@@ -244,6 +247,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -263,6 +268,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU=
gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=

View File

@@ -2,9 +2,12 @@ package client
import (
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"time"
@@ -16,6 +19,55 @@ const (
defaultHTTPTimeout = 5
)
// HTTPClient represents a client to send HTTP requests.
type HTTPClient struct {
*http.Client
}
// NewHTTPClient is used to build a new HTTPClient.
func NewHTTPClient() *HTTPClient {
return &HTTPClient{
&http.Client{
Timeout: time.Second * time.Duration(defaultHTTPTimeout),
},
}
}
// AzureAuthenticationResponse represents an Azure API authentication response.
type AzureAuthenticationResponse struct {
AccessToken string `json:"access_token"`
ExpiresOn string `json:"expires_on"`
}
// ExecuteAzureAuthenticationRequest is used to execute an authentication request
// against the Azure API. It re-uses the same http.Client.
func (client *HTTPClient) ExecuteAzureAuthenticationRequest(credentials *portainer.AzureCredentials) (*AzureAuthenticationResponse, error) {
loginURL := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/token", credentials.TenantID)
params := url.Values{
"grant_type": {"client_credentials"},
"client_id": {credentials.ApplicationID},
"client_secret": {credentials.AuthenticationKey},
"resource": {"https://management.azure.com/"},
}
response, err := client.PostForm(loginURL, params)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, portainer.ErrAzureInvalidCredentials
}
var token AzureAuthenticationResponse
err = json.NewDecoder(response.Body).Decode(&token)
if err != nil {
return nil, err
}
return &token, nil
}
// Get executes a simple HTTP GET to the specified URL and returns
// the content of the response body. Timeout can be specified via the timeout parameter,
// will default to defaultHTTPTimeout if set to 0.

View File

@@ -42,12 +42,12 @@ func (handler *Handler) authenticate(w http.ResponseWriter, r *http.Request) *ht
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
settings, err := handler.DataStore.Settings().Settings()
settings, err := handler.SettingsService.Settings()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
}
u, err := handler.DataStore.User().UserByUsername(payload.Username)
u, err := handler.UserService.UserByUsername(payload.Username)
if err != nil && err != portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve a user with the specified username from the database", err}
}
@@ -108,7 +108,7 @@ func (handler *Handler) authenticateLDAPAndCreateUser(w http.ResponseWriter, use
PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(),
}
err = handler.DataStore.User().CreateUser(user)
err = handler.UserService.CreateUser(user)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist user inside the database", err}
}
@@ -146,7 +146,7 @@ func (handler *Handler) persistAndWriteToken(w http.ResponseWriter, tokenData *p
}
func (handler *Handler) addUserIntoTeams(user *portainer.User, settings *portainer.LDAPSettings) error {
teams, err := handler.DataStore.Team().Teams()
teams, err := handler.TeamService.Teams()
if err != nil {
return err
}
@@ -156,7 +156,7 @@ func (handler *Handler) addUserIntoTeams(user *portainer.User, settings *portain
return err
}
userMemberships, err := handler.DataStore.TeamMembership().TeamMembershipsByUserID(user.ID)
userMemberships, err := handler.TeamMembershipService.TeamMembershipsByUserID(user.ID)
if err != nil {
return err
}
@@ -174,7 +174,7 @@ func (handler *Handler) addUserIntoTeams(user *portainer.User, settings *portain
Role: portainer.TeamMember,
}
err := handler.DataStore.TeamMembership().CreateTeamMembership(membership)
err := handler.TeamMembershipService.CreateTeamMembership(membership)
if err != nil {
return err
}

View File

@@ -78,7 +78,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
settings, err := handler.DataStore.Settings().Settings()
settings, err := handler.SettingsService.Settings()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
}
@@ -87,7 +87,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
return &httperror.HandlerError{http.StatusForbidden, "OAuth authentication is not enabled", portainer.Error("OAuth authentication is not enabled")}
}
extension, err := handler.DataStore.Extension().Extension(portainer.OAuthAuthenticationExtension)
extension, err := handler.ExtensionService.Extension(portainer.OAuthAuthenticationExtension)
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Oauth authentication extension is not enabled", err}
} else if err != nil {
@@ -100,7 +100,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to authenticate through OAuth", portainer.ErrUnauthorized}
}
user, err := handler.DataStore.User().UserByUsername(username)
user, err := handler.UserService.UserByUsername(username)
if err != nil && err != portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve a user with the specified username from the database", err}
}
@@ -116,7 +116,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
PortainerAuthorizations: portainer.DefaultPortainerAuthorizations(),
}
err = handler.DataStore.User().CreateUser(user)
err = handler.UserService.CreateUser(user)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist user inside the database", err}
}
@@ -128,7 +128,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
Role: portainer.TeamMember,
}
err = handler.DataStore.TeamMembership().CreateTeamMembership(membership)
err = handler.TeamMembershipService.CreateTeamMembership(membership)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist team membership inside the database", err}
}

View File

@@ -11,6 +11,8 @@ import (
)
const (
// ErrInvalidCredentials is an error raised when credentials for a user are invalid
ErrInvalidCredentials = portainer.Error("Invalid credentials")
// ErrAuthDisabled is an error raised when trying to access the authentication endpoints
// when the server has been started with the --no-auth flag
ErrAuthDisabled = portainer.Error("Authentication is disabled")
@@ -19,13 +21,20 @@ const (
// Handler is the HTTP handler used to handle authentication operations.
type Handler struct {
*mux.Router
authDisabled bool
DataStore portainer.DataStore
CryptoService portainer.CryptoService
JWTService portainer.JWTService
LDAPService portainer.LDAPService
ProxyManager *proxy.Manager
AuthorizationService *portainer.AuthorizationService
authDisabled bool
UserService portainer.UserService
CryptoService portainer.CryptoService
JWTService portainer.JWTService
LDAPService portainer.LDAPService
SettingsService portainer.SettingsService
TeamService portainer.TeamService
TeamMembershipService portainer.TeamMembershipService
ExtensionService portainer.ExtensionService
EndpointService portainer.EndpointService
EndpointGroupService portainer.EndpointGroupService
RoleService portainer.RoleService
ProxyManager *proxy.Manager
AuthorizationService *portainer.AuthorizationService
}
// NewHandler creates a handler to manage authentication operations.

View File

@@ -9,7 +9,7 @@ import (
// GET request on /api/dockerhub
func (handler *Handler) dockerhubInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
dockerhub, err := handler.DataStore.DockerHub().DockerHub()
dockerhub, err := handler.DockerHubService.DockerHub()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve DockerHub details from the database", err}
}

View File

@@ -43,7 +43,7 @@ func (handler *Handler) dockerhubUpdate(w http.ResponseWriter, r *http.Request)
dockerhub.Password = payload.Password
}
err = handler.DataStore.DockerHub().UpdateDockerHub(dockerhub)
err = handler.DockerHubService.UpdateDockerHub(dockerhub)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the Dockerhub changes inside the database", err}
}

View File

@@ -16,7 +16,7 @@ func hideFields(dockerHub *portainer.DockerHub) {
// Handler is the HTTP handler used to handle DockerHub operations.
type Handler struct {
*mux.Router
DataStore portainer.DataStore
DockerHubService portainer.DockerHubService
}
// NewHandler creates a handler to manage Dockerhub operations.

View File

@@ -11,7 +11,7 @@ func (handler *Handler) getEndpointsByTags(tagIDs []portainer.TagID, partialMatc
return []portainer.EndpointID{}, nil
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return nil, err
}
@@ -20,7 +20,7 @@ func (handler *Handler) getEndpointsByTags(tagIDs []portainer.TagID, partialMatc
tags := []portainer.Tag{}
for _, tagID := range tagIDs {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return nil, err
}

View File

@@ -38,7 +38,7 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve Edge groups from the database", err}
}
@@ -62,7 +62,7 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request)
} else {
endpointIDs := []portainer.EndpointID{}
for _, endpointID := range payload.Endpoints {
endpoint, err := handler.DataStore.Endpoint().Endpoint(endpointID)
endpoint, err := handler.EndpointService.Endpoint(endpointID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint from the database", err}
}
@@ -74,7 +74,7 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request)
edgeGroup.Endpoints = endpointIDs
}
err = handler.DataStore.EdgeGroup().CreateEdgeGroup(edgeGroup)
err = handler.EdgeGroupService.CreateEdgeGroup(edgeGroup)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the Edge group inside the database", err}
}

View File

@@ -15,14 +15,14 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid Edge group identifier route variable", err}
}
_, err = handler.DataStore.EdgeGroup().EdgeGroup(portainer.EdgeGroupID(edgeGroupID))
_, err = handler.EdgeGroupService.EdgeGroup(portainer.EdgeGroupID(edgeGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge group with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge group with the specified identifier inside the database", err}
}
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve Edge stacks from the database", err}
}
@@ -35,7 +35,7 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request)
}
}
err = handler.DataStore.EdgeGroup().DeleteEdgeGroup(portainer.EdgeGroupID(edgeGroupID))
err = handler.EdgeGroupService.DeleteEdgeGroup(portainer.EdgeGroupID(edgeGroupID))
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove the Edge group from the database", err}
}

View File

@@ -15,7 +15,7 @@ func (handler *Handler) edgeGroupInspect(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid Edge group identifier route variable", err}
}
edgeGroup, err := handler.DataStore.EdgeGroup().EdgeGroup(portainer.EdgeGroupID(edgeGroupID))
edgeGroup, err := handler.EdgeGroupService.EdgeGroup(portainer.EdgeGroupID(edgeGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge group with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -14,12 +14,12 @@ type decoratedEdgeGroup struct {
}
func (handler *Handler) edgeGroupList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve Edge groups from the database", err}
}
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve Edge stacks from the database", err}
}

View File

@@ -43,7 +43,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
edgeGroup, err := handler.DataStore.EdgeGroup().EdgeGroup(portainer.EdgeGroupID(edgeGroupID))
edgeGroup, err := handler.EdgeGroupService.EdgeGroup(portainer.EdgeGroupID(edgeGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge group with the specified identifier inside the database", err}
} else if err != nil {
@@ -51,7 +51,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
}
if payload.Name != "" {
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve Edge groups from the database", err}
}
@@ -63,12 +63,12 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
edgeGroup.Name = payload.Name
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from database", err}
}
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from database", err}
}
@@ -81,7 +81,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
} else {
endpointIDs := []portainer.EndpointID{}
for _, endpointID := range payload.Endpoints {
endpoint, err := handler.DataStore.Endpoint().Endpoint(endpointID)
endpoint, err := handler.EndpointService.Endpoint(endpointID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint from the database", err}
}
@@ -97,7 +97,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
edgeGroup.PartialMatch = *payload.PartialMatch
}
err = handler.DataStore.EdgeGroup().UpdateEdgeGroup(edgeGroup.ID, edgeGroup)
err = handler.EdgeGroupService.UpdateEdgeGroup(edgeGroup.ID, edgeGroup)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist Edge group changes inside the database", err}
}
@@ -116,27 +116,27 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
}
func (handler *Handler) updateEndpoint(endpointID portainer.EndpointID) error {
relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpointID)
relation, err := handler.EndpointRelationService.EndpointRelation(endpointID)
if err != nil {
return err
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(endpointID)
endpoint, err := handler.EndpointService.Endpoint(endpointID)
if err != nil {
return err
}
endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(endpoint.GroupID)
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(endpoint.GroupID)
if err != nil {
return err
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return err
}
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return err
}
@@ -150,5 +150,5 @@ func (handler *Handler) updateEndpoint(endpointID portainer.EndpointID) error {
relation.EdgeStacks = edgeStackSet
return handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpoint.ID, relation)
return handler.EndpointRelationService.UpdateEndpointRelation(endpoint.ID, relation)
}

View File

@@ -12,7 +12,12 @@ import (
// Handler is the HTTP handler used to handle endpoint group operations.
type Handler struct {
*mux.Router
DataStore portainer.DataStore
EdgeGroupService portainer.EdgeGroupService
EdgeStackService portainer.EdgeStackService
EndpointService portainer.EndpointService
EndpointGroupService portainer.EndpointGroupService
EndpointRelationService portainer.EndpointRelationService
TagService portainer.TagService
}
// NewHandler creates a handler to manage endpoint group operations.

View File

@@ -27,17 +27,17 @@ func (handler *Handler) edgeStackCreate(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create Edge stack", err}
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from database", err}
}
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from database", err}
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from database", err}
}
@@ -45,14 +45,14 @@ func (handler *Handler) edgeStackCreate(w http.ResponseWriter, r *http.Request)
relatedEndpoints, err := portainer.EdgeStackRelatedEndpoints(edgeStack.EdgeGroups, endpoints, endpointGroups, edgeGroups)
for _, endpointID := range relatedEndpoints {
relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpointID)
relation, err := handler.EndpointRelationService.EndpointRelation(endpointID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find endpoint relation in database", err}
}
relation.EdgeStacks[edgeStack.ID] = true
err = handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpointID, relation)
err = handler.EndpointRelationService.UpdateEndpointRelation(endpointID, relation)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint relation in database", err}
}
@@ -104,7 +104,7 @@ func (handler *Handler) createSwarmStackFromFileContent(r *http.Request) (*porta
return nil, err
}
stackID := handler.DataStore.EdgeStack().GetNextIdentifier()
stackID := handler.EdgeStackService.GetNextIdentifier()
stack := &portainer.EdgeStack{
ID: portainer.EdgeStackID(stackID),
Name: payload.Name,
@@ -122,7 +122,7 @@ func (handler *Handler) createSwarmStackFromFileContent(r *http.Request) (*porta
}
stack.ProjectPath = projectPath
err = handler.DataStore.EdgeStack().CreateEdgeStack(stack)
err = handler.EdgeStackService.CreateEdgeStack(stack)
if err != nil {
return nil, err
}
@@ -172,7 +172,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(r *http.Request) (*por
return nil, err
}
stackID := handler.DataStore.EdgeStack().GetNextIdentifier()
stackID := handler.EdgeStackService.GetNextIdentifier()
stack := &portainer.EdgeStack{
ID: portainer.EdgeStackID(stackID),
Name: payload.Name,
@@ -200,7 +200,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(r *http.Request) (*por
return nil, err
}
err = handler.DataStore.EdgeStack().CreateEdgeStack(stack)
err = handler.EdgeStackService.CreateEdgeStack(stack)
if err != nil {
return nil, err
}
@@ -248,7 +248,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(r *http.Request) (*portai
return nil, err
}
stackID := handler.DataStore.EdgeStack().GetNextIdentifier()
stackID := handler.EdgeStackService.GetNextIdentifier()
stack := &portainer.EdgeStack{
ID: portainer.EdgeStackID(stackID),
Name: payload.Name,
@@ -266,7 +266,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(r *http.Request) (*portai
}
stack.ProjectPath = projectPath
err = handler.DataStore.EdgeStack().CreateEdgeStack(stack)
err = handler.EdgeStackService.CreateEdgeStack(stack)
if err != nil {
return nil, err
}
@@ -275,7 +275,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(r *http.Request) (*portai
}
func (handler *Handler) validateUniqueName(name string) error {
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return err
}

View File

@@ -15,29 +15,29 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid edge stack identifier route variable", err}
}
edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID))
edgeStack, err := handler.EdgeStackService.EdgeStack(portainer.EdgeStackID(edgeStackID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an edge stack with the specified identifier inside the database", err}
}
err = handler.DataStore.EdgeStack().DeleteEdgeStack(portainer.EdgeStackID(edgeStackID))
err = handler.EdgeStackService.DeleteEdgeStack(portainer.EdgeStackID(edgeStackID))
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove the edge stack from the database", err}
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from database", err}
}
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from database", err}
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from database", err}
}
@@ -45,14 +45,14 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request)
relatedEndpoints, err := portainer.EdgeStackRelatedEndpoints(edgeStack.EdgeGroups, endpoints, endpointGroups, edgeGroups)
for _, endpointID := range relatedEndpoints {
relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpointID)
relation, err := handler.EndpointRelationService.EndpointRelation(endpointID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find endpoint relation in database", err}
}
delete(relation.EdgeStacks, edgeStack.ID)
err = handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpointID, relation)
err = handler.EndpointRelationService.UpdateEndpointRelation(endpointID, relation)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint relation in database", err}
}

View File

@@ -21,7 +21,7 @@ func (handler *Handler) edgeStackFile(w http.ResponseWriter, r *http.Request) *h
return &httperror.HandlerError{http.StatusBadRequest, "Invalid edge stack identifier route variable", err}
}
stack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(stackID))
stack, err := handler.EdgeStackService.EdgeStack(portainer.EdgeStackID(stackID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -15,7 +15,7 @@ func (handler *Handler) edgeStackInspect(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid edge stack identifier route variable", err}
}
edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID))
edgeStack, err := handler.EdgeStackService.EdgeStack(portainer.EdgeStackID(edgeStackID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -8,7 +8,7 @@ import (
)
func (handler *Handler) edgeStackList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stacks from the database", err}
}

View File

@@ -35,7 +35,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
return &httperror.HandlerError{http.StatusBadRequest, "Invalid stack identifier route variable", err}
}
stack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(stackID))
stack, err := handler.EdgeStackService.EdgeStack(portainer.EdgeStackID(stackID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err}
} else if err != nil {
@@ -48,7 +48,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(*payload.EndpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(*payload.EndpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -66,7 +66,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
EndpointID: *payload.EndpointID,
}
err = handler.DataStore.EdgeStack().UpdateEdgeStack(stack.ID, stack)
err = handler.EdgeStackService.UpdateEdgeStack(stack.ID, stack)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the stack changes inside the database", err}
}

View File

@@ -34,7 +34,7 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid stack identifier route variable", err}
}
stack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(stackID))
stack, err := handler.EdgeStackService.EdgeStack(portainer.EdgeStackID(stackID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err}
} else if err != nil {
@@ -48,17 +48,17 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
}
if payload.EdgeGroups != nil {
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from database", err}
}
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from database", err}
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from database", err}
}
@@ -84,14 +84,14 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
}
for endpointID := range endpointsToRemove {
relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpointID)
relation, err := handler.EndpointRelationService.EndpointRelation(endpointID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find endpoint relation in database", err}
}
delete(relation.EdgeStacks, stack.ID)
err = handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpointID, relation)
err = handler.EndpointRelationService.UpdateEndpointRelation(endpointID, relation)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint relation in database", err}
}
@@ -105,14 +105,14 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
}
for endpointID := range endpointsToAdd {
relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpointID)
relation, err := handler.EndpointRelationService.EndpointRelation(endpointID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find endpoint relation in database", err}
}
relation.EdgeStacks[stack.ID] = true
err = handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpointID, relation)
err = handler.EndpointRelationService.UpdateEndpointRelation(endpointID, relation)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint relation in database", err}
}
@@ -137,7 +137,7 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
stack.Status = map[portainer.EndpointID]portainer.EdgeStackStatus{}
}
err = handler.DataStore.EdgeStack().UpdateEdgeStack(stack.ID, stack)
err = handler.EdgeStackService.UpdateEdgeStack(stack.ID, stack)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the stack changes inside the database", err}
}

View File

@@ -12,10 +12,14 @@ import (
// Handler is the HTTP handler used to handle endpoint group operations.
type Handler struct {
*mux.Router
requestBouncer *security.RequestBouncer
DataStore portainer.DataStore
FileService portainer.FileService
GitService portainer.GitService
requestBouncer *security.RequestBouncer
EdgeGroupService portainer.EdgeGroupService
EdgeStackService portainer.EdgeStackService
EndpointService portainer.EndpointService
EndpointGroupService portainer.EndpointGroupService
EndpointRelationService portainer.EndpointRelationService
FileService portainer.FileService
GitService portainer.GitService
}
// NewHandler creates a handler to manage endpoint group operations.

View File

@@ -2,6 +2,7 @@ package edgetemplates
import (
"encoding/json"
"log"
"net/http"
httperror "github.com/portainer/libhttp/error"
@@ -10,39 +11,35 @@ import (
"github.com/portainer/portainer/api/http/client"
)
type templateFileFormat struct {
Version string `json:"version"`
Templates []portainer.Template `json:"templates"`
}
// GET request on /api/edgetemplates
func (handler *Handler) edgeTemplateList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
settings, err := handler.DataStore.Settings().Settings()
settings, err := handler.SettingsService.Settings()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
}
url := portainer.DefaultTemplatesURL
url := portainer.EdgeTemplatesURL
if settings.TemplatesURL != "" {
url = settings.TemplatesURL
}
var templateData []byte
templateData, err = client.Get(url, 10)
templateData, err = client.Get(url, 0)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve external templates", err}
}
var templateFile templateFileFormat
var templates []portainer.Template
err = json.Unmarshal(templateData, &templateFile)
err = json.Unmarshal(templateData, &templates)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to parse template file", err}
log.Printf("[DEBUG] [http,edge,templates] [failed parsing edge templates] [body: %s]", templateData)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to parse external templates", err}
}
filteredTemplates := make([]portainer.Template, 0)
filteredTemplates := []portainer.Template{}
for _, template := range templateFile.Templates {
for _, template := range templates {
if template.Type == portainer.EdgeStackTemplate {
filteredTemplates = append(filteredTemplates, template)
}

View File

@@ -13,8 +13,8 @@ import (
// Handler is the HTTP handler used to handle edge endpoint operations.
type Handler struct {
*mux.Router
requestBouncer *security.RequestBouncer
DataStore portainer.DataStore
requestBouncer *security.RequestBouncer
SettingsService portainer.SettingsService
}
// NewHandler creates a handler to manage endpoint operations.

View File

@@ -23,7 +23,7 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -40,7 +40,7 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
return &httperror.HandlerError{http.StatusBadRequest, "Invalid edge stack identifier route variable", err}
}
edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID))
edgeStack, err := handler.EdgeStackService.EdgeStack(portainer.EdgeStackID(edgeStackID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -13,9 +13,10 @@ import (
// Handler is the HTTP handler used to handle edge endpoint operations.
type Handler struct {
*mux.Router
requestBouncer *security.RequestBouncer
DataStore portainer.DataStore
FileService portainer.FileService
requestBouncer *security.RequestBouncer
EndpointService portainer.EndpointService
EdgeStackService portainer.EdgeStackService
FileService portainer.FileService
}
// NewHandler creates a handler to manage endpoint operations.

View File

@@ -43,12 +43,12 @@ func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Reque
TagIDs: payload.TagIDs,
}
err = handler.DataStore.EndpointGroup().CreateEndpointGroup(endpointGroup)
err = handler.EndpointGroupService.CreateEndpointGroup(endpointGroup)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the endpoint group inside the database", err}
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}
}
@@ -58,7 +58,7 @@ func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Reque
if endpoint.ID == id {
endpoint.GroupID = endpointGroup.ID
err := handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint)
err := handler.EndpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update endpoint", err}
}
@@ -74,14 +74,14 @@ func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Reque
}
for _, tagID := range endpointGroup.TagIDs {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve tag from the database", err}
}
tag.EndpointGroups[endpointGroup.ID] = true
err = handler.DataStore.Tag().UpdateTag(tagID, tag)
err = handler.TagService.UpdateTag(tagID, tag)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist tag changes inside the database", err}
}

View File

@@ -20,19 +20,19 @@ func (handler *Handler) endpointGroupDelete(w http.ResponseWriter, r *http.Reque
return &httperror.HandlerError{http.StatusForbidden, "Unable to remove the default 'Unassigned' group", portainer.ErrCannotRemoveDefaultGroup}
}
endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err}
}
err = handler.DataStore.EndpointGroup().DeleteEndpointGroup(portainer.EndpointGroupID(endpointGroupID))
err = handler.EndpointGroupService.DeleteEndpointGroup(portainer.EndpointGroupID(endpointGroupID))
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove the endpoint group from the database", err}
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}
}
@@ -42,7 +42,7 @@ func (handler *Handler) endpointGroupDelete(w http.ResponseWriter, r *http.Reque
if endpoint.GroupID == portainer.EndpointGroupID(endpointGroupID) {
updateAuthorizations = true
endpoint.GroupID = portainer.EndpointGroupID(1)
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint)
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update endpoint", err}
}
@@ -62,14 +62,14 @@ func (handler *Handler) endpointGroupDelete(w http.ResponseWriter, r *http.Reque
}
for _, tagID := range endpointGroup.TagIDs {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve tag from the database", err}
}
delete(tag.EndpointGroups, endpointGroup.ID)
err = handler.DataStore.Tag().UpdateTag(tagID, tag)
err = handler.TagService.UpdateTag(tagID, tag)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist tag changes inside the database", err}
}

View File

@@ -21,14 +21,14 @@ func (handler *Handler) endpointGroupAddEndpoint(w http.ResponseWriter, r *http.
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -37,7 +37,7 @@ func (handler *Handler) endpointGroupAddEndpoint(w http.ResponseWriter, r *http.
endpoint.GroupID = endpointGroup.ID
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}

View File

@@ -21,14 +21,14 @@ func (handler *Handler) endpointGroupDeleteEndpoint(w http.ResponseWriter, r *ht
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
_, err = handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
_, err = handler.EndpointGroupService.EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -37,7 +37,7 @@ func (handler *Handler) endpointGroupDeleteEndpoint(w http.ResponseWriter, r *ht
endpoint.GroupID = portainer.EndpointGroupID(1)
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}

View File

@@ -16,7 +16,7 @@ func (handler *Handler) endpointGroupInspect(w http.ResponseWriter, r *http.Requ
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint group identifier route variable", err}
}
endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -10,7 +10,7 @@ import (
// GET request on /api/endpoint_groups
func (handler *Handler) endpointGroupList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from the database", err}
}

View File

@@ -35,7 +35,7 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(portainer.EndpointGroupID(endpointGroupID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err}
} else if err != nil {
@@ -62,12 +62,12 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
removeTags := portainer.TagDifference(endpointGroupTagSet, payloadTagSet)
for tagID := range removeTags {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a tag inside the database", err}
}
delete(tag.EndpointGroups, endpointGroup.ID)
err = handler.DataStore.Tag().UpdateTag(tag.ID, tag)
err = handler.TagService.UpdateTag(tag.ID, tag)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist tag changes inside the database", err}
}
@@ -75,14 +75,14 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
endpointGroup.TagIDs = payload.TagIDs
for _, tagID := range payload.TagIDs {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a tag inside the database", err}
}
tag.EndpointGroups[endpointGroup.ID] = true
err = handler.DataStore.Tag().UpdateTag(tag.ID, tag)
err = handler.TagService.UpdateTag(tag.ID, tag)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist tag changes inside the database", err}
}
@@ -101,7 +101,7 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
updateAuthorizations = true
}
err = handler.DataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, endpointGroup)
err = handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, endpointGroup)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint group changes inside the database", err}
}
@@ -114,7 +114,7 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
}
if tagsChanged {
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}

View File

@@ -8,7 +8,7 @@ func (handler *Handler) updateEndpointRelations(endpoint *portainer.Endpoint, en
}
if endpointGroup == nil {
unassignedGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(1))
unassignedGroup, err := handler.EndpointGroupService.EndpointGroup(portainer.EndpointGroupID(1))
if err != nil {
return err
}
@@ -16,17 +16,17 @@ func (handler *Handler) updateEndpointRelations(endpoint *portainer.Endpoint, en
endpointGroup = unassignedGroup
}
endpointRelation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpoint.ID)
endpointRelation, err := handler.EndpointRelationService.EndpointRelation(endpoint.ID)
if err != nil {
return err
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return err
}
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return err
}
@@ -38,5 +38,5 @@ func (handler *Handler) updateEndpointRelations(endpoint *portainer.Endpoint, en
}
endpointRelation.EdgeStacks = stacksSet
return handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpoint.ID, endpointRelation)
return handler.EndpointRelationService.UpdateEndpointRelation(endpoint.ID, endpointRelation)
}

View File

@@ -12,8 +12,13 @@ import (
// Handler is the HTTP handler used to handle endpoint group operations.
type Handler struct {
*mux.Router
DataStore portainer.DataStore
AuthorizationService *portainer.AuthorizationService
AuthorizationService *portainer.AuthorizationService
EdgeGroupService portainer.EdgeGroupService
EdgeStackService portainer.EdgeStackService
EndpointService portainer.EndpointService
EndpointGroupService portainer.EndpointGroupService
EndpointRelationService portainer.EndpointRelationService
TagService portainer.TagService
}
// NewHandler creates a handler to manage endpoint group operations.

View File

@@ -11,8 +11,9 @@ import (
// Handler is the HTTP handler used to proxy requests to external APIs.
type Handler struct {
*mux.Router
DataStore portainer.DataStore
requestBouncer *security.RequestBouncer
EndpointService portainer.EndpointService
SettingsService portainer.SettingsService
ProxyManager *proxy.Manager
ReverseTunnelService portainer.ReverseTunnelService
}
@@ -23,6 +24,8 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
Router: mux.NewRouter(),
requestBouncer: bouncer,
}
h.PathPrefix("/{id}/azure").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToAzureAPI)))
h.PathPrefix("/{id}/docker").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToDockerAPI)))
h.PathPrefix("/{id}/storidge").Handler(

View File

@@ -0,0 +1,43 @@
package endpointproxy
import (
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/portainer/api"
"net/http"
)
func (handler *Handler) proxyRequestsToAzureAPI(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
}
err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint, false)
if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
}
var proxy http.Handler
proxy = handler.ProxyManager.GetEndpointProxy(endpoint)
if proxy == nil {
proxy, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create proxy", err}
}
}
id := strconv.Itoa(endpointID)
http.StripPrefix("/"+id+"/azure", proxy).ServeHTTP(w, r)
return nil
}

View File

@@ -18,7 +18,7 @@ func (handler *Handler) proxyRequestsToDockerAPI(w http.ResponseWriter, r *http.
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -44,7 +44,7 @@ func (handler *Handler) proxyRequestsToDockerAPI(w http.ResponseWriter, r *http.
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update tunnel status", err}
}
settings, err := handler.DataStore.Settings().Settings()
settings, err := handler.SettingsService.Settings()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
}

View File

@@ -18,7 +18,7 @@ func (handler *Handler) proxyRequestsToStoridgeAPI(w http.ResponseWriter, r *htt
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -18,18 +18,21 @@ import (
)
type endpointCreatePayload struct {
Name string
URL string
EndpointType int
PublicURL string
GroupID int
TLS bool
TLSSkipVerify bool
TLSSkipClientVerify bool
TLSCACertFile []byte
TLSCertFile []byte
TLSKeyFile []byte
TagIDs []portainer.TagID
Name string
URL string
EndpointType int
PublicURL string
GroupID int
TLS bool
TLSSkipVerify bool
TLSSkipClientVerify bool
TLSCACertFile []byte
TLSCertFile []byte
TLSKeyFile []byte
AzureApplicationID string
AzureTenantID string
AzureAuthenticationKey string
TagIDs []portainer.TagID
}
func (payload *endpointCreatePayload) Validate(r *http.Request) error {
@@ -41,7 +44,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
endpointType, err := request.RetrieveNumericMultiPartFormValue(r, "EndpointType", false)
if err != nil || endpointType == 0 {
return portainer.Error("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment) or 4 (Edge Agent environment)")
return portainer.Error("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge Agent environment)")
}
payload.EndpointType = endpointType
@@ -93,20 +96,45 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
}
}
endpointURL, err := request.RetrieveMultiPartFormValue(r, "URL", true)
if err != nil {
return portainer.Error("Invalid endpoint URL")
}
payload.URL = endpointURL
switch portainer.EndpointType(payload.EndpointType) {
case portainer.AzureEnvironment:
azureApplicationID, err := request.RetrieveMultiPartFormValue(r, "AzureApplicationID", false)
if err != nil {
return portainer.Error("Invalid Azure application ID")
}
payload.AzureApplicationID = azureApplicationID
publicURL, _ := request.RetrieveMultiPartFormValue(r, "PublicURL", true)
payload.PublicURL = publicURL
azureTenantID, err := request.RetrieveMultiPartFormValue(r, "AzureTenantID", false)
if err != nil {
return portainer.Error("Invalid Azure tenant ID")
}
payload.AzureTenantID = azureTenantID
azureAuthenticationKey, err := request.RetrieveMultiPartFormValue(r, "AzureAuthenticationKey", false)
if err != nil {
return portainer.Error("Invalid Azure authentication key")
}
payload.AzureAuthenticationKey = azureAuthenticationKey
default:
url, err := request.RetrieveMultiPartFormValue(r, "URL", true)
if err != nil {
return portainer.Error("Invalid endpoint URL")
}
payload.URL = url
publicURL, _ := request.RetrieveMultiPartFormValue(r, "PublicURL", true)
payload.PublicURL = publicURL
}
return nil
}
// POST request on /api/endpoints
func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
if !handler.authorizeEndpointManagement {
return &httperror.HandlerError{http.StatusServiceUnavailable, "Endpoint management is disabled", ErrEndpointManagementDisabled}
}
payload := &endpointCreatePayload{}
err := payload.Validate(r)
if err != nil {
@@ -118,17 +146,17 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *
return endpointCreationError
}
endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(endpoint.GroupID)
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(endpoint.GroupID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group inside the database", err}
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from the database", err}
}
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stacks from the database", err}
}
@@ -145,7 +173,7 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *
}
}
err = handler.DataStore.EndpointRelation().CreateEndpointRelation(relationObject)
err = handler.EndpointRelationService.CreateEndpointRelation(relationObject)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the relation object inside the database", err}
}
@@ -154,7 +182,9 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *
}
func (handler *Handler) createEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
if portainer.EndpointType(payload.EndpointType) == portainer.EdgeAgentEnvironment {
if portainer.EndpointType(payload.EndpointType) == portainer.AzureEnvironment {
return handler.createAzureEndpoint(payload)
} else if portainer.EndpointType(payload.EndpointType) == portainer.EdgeAgentEnvironment {
return handler.createEdgeAgentEndpoint(payload)
}
@@ -164,9 +194,47 @@ func (handler *Handler) createEndpoint(payload *endpointCreatePayload) (*portain
return handler.createUnsecuredEndpoint(payload)
}
func (handler *Handler) createAzureEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
credentials := portainer.AzureCredentials{
ApplicationID: payload.AzureApplicationID,
TenantID: payload.AzureTenantID,
AuthenticationKey: payload.AzureAuthenticationKey,
}
httpClient := client.NewHTTPClient()
_, err := httpClient.ExecuteAzureAuthenticationRequest(&credentials)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to authenticate against Azure", err}
}
endpointID := handler.EndpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: payload.Name,
URL: "https://management.azure.com",
Type: portainer.AzureEnvironment,
GroupID: portainer.EndpointGroupID(payload.GroupID),
PublicURL: payload.PublicURL,
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{},
AzureCredentials: credentials,
TagIDs: payload.TagIDs,
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{},
}
err = handler.saveEndpointAndUpdateAuthorizations(endpoint)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "An error occured while trying to create the endpoint", err}
}
return endpoint, nil
}
func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
endpointType := portainer.EdgeAgentEnvironment
endpointID := handler.DataStore.Endpoint().GetNextIdentifier()
endpointID := handler.EndpointService.GetNextIdentifier()
portainerURL, err := url.Parse(payload.URL)
if err != nil {
@@ -228,7 +296,7 @@ func (handler *Handler) createUnsecuredEndpoint(payload *endpointCreatePayload)
}
}
endpointID := handler.DataStore.Endpoint().GetNextIdentifier()
endpointID := handler.EndpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: payload.Name,
@@ -271,7 +339,7 @@ func (handler *Handler) createTLSSecuredEndpoint(payload *endpointCreatePayload)
endpointType = portainer.AgentOnDockerEnvironment
}
endpointID := handler.DataStore.Endpoint().GetNextIdentifier()
endpointID := handler.EndpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: payload.Name,
@@ -327,12 +395,12 @@ func (handler *Handler) snapshotAndPersistEndpoint(endpoint *portainer.Endpoint)
}
func (handler *Handler) saveEndpointAndUpdateAuthorizations(endpoint *portainer.Endpoint) error {
err := handler.DataStore.Endpoint().CreateEndpoint(endpoint)
err := handler.EndpointService.CreateEndpoint(endpoint)
if err != nil {
return err
}
group, err := handler.DataStore.EndpointGroup().EndpointGroup(endpoint.GroupID)
group, err := handler.EndpointGroupService.EndpointGroup(endpoint.GroupID)
if err != nil {
return err
}
@@ -342,14 +410,14 @@ func (handler *Handler) saveEndpointAndUpdateAuthorizations(endpoint *portainer.
}
for _, tagID := range endpoint.TagIDs {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return err
}
tag.Endpoints[endpoint.ID] = true
err = handler.DataStore.Tag().UpdateTag(tagID, tag)
err = handler.TagService.UpdateTag(tagID, tag)
if err != nil {
return err
}

View File

@@ -12,12 +12,20 @@ import (
// DELETE request on /api/endpoints/:id
func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
if !handler.authorizeEndpointManagement {
return &httperror.HandlerError{http.StatusServiceUnavailable, "Endpoint management is disabled", ErrEndpointManagementDisabled}
}
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if endpointID == 1 {
return &httperror.HandlerError{http.StatusForbidden, "This feature is not available in the demo version of Portainer", portainer.ErrNotAvailableInDemo}
}
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -32,7 +40,7 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
}
}
err = handler.DataStore.Endpoint().DeleteEndpoint(portainer.EndpointID(endpointID))
err = handler.EndpointService.DeleteEndpoint(portainer.EndpointID(endpointID))
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove endpoint from the database", err}
}
@@ -46,26 +54,26 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
}
}
err = handler.DataStore.EndpointRelation().DeleteEndpointRelation(endpoint.ID)
err = handler.EndpointRelationService.DeleteEndpointRelation(endpoint.ID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove endpoint relation from the database", err}
}
for _, tagID := range endpoint.TagIDs {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find tag inside the database", err}
}
delete(tag.Endpoints, endpoint.ID)
err = handler.DataStore.Tag().UpdateTag(tagID, tag)
err = handler.TagService.UpdateTag(tagID, tag)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist tag relation inside the database", err}
}
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from the database", err}
}
@@ -75,14 +83,14 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
endpointIdx := findEndpointIndex(edgeGroup.Endpoints, endpoint.ID)
if endpointIdx != -1 {
edgeGroup.Endpoints = removeElement(edgeGroup.Endpoints, endpointIdx)
err = handler.DataStore.EdgeGroup().UpdateEdgeGroup(edgeGroup.ID, edgeGroup)
err = handler.EdgeGroupService.UpdateEdgeGroup(edgeGroup.ID, edgeGroup)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update edge group", err}
}
}
}
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stacks from the database", err}
}
@@ -91,7 +99,7 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
edgeStack := &edgeStacks[idx]
if _, ok := edgeStack.Status[endpoint.ID]; ok {
delete(edgeStack.Status, endpoint.ID)
err = handler.DataStore.EdgeStack().UpdateEdgeStack(edgeStack.ID, edgeStack)
err = handler.EdgeStackService.UpdateEdgeStack(edgeStack.ID, edgeStack)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update edge stack", err}
}

View File

@@ -34,7 +34,7 @@ func (handler *Handler) endpointExtensionAdd(w http.ResponseWriter, r *http.Requ
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -66,7 +66,7 @@ func (handler *Handler) endpointExtensionAdd(w http.ResponseWriter, r *http.Requ
endpoint.Extensions = append(endpoint.Extensions, *extension)
}
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}

View File

@@ -18,7 +18,7 @@ func (handler *Handler) endpointExtensionRemove(w http.ResponseWriter, r *http.R
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -36,7 +36,7 @@ func (handler *Handler) endpointExtensionRemove(w http.ResponseWriter, r *http.R
}
}
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}

View File

@@ -16,7 +16,7 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -63,7 +63,7 @@ func (handler *Handler) endpointJob(w http.ResponseWriter, r *http.Request) *htt
nodeName, _ := request.RetrieveQueryParameter(r, "nodeName", true)
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -38,12 +38,12 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht
var endpointIDs []portainer.EndpointID
request.RetrieveJSONQueryParameter(r, "endpointIds", &endpointIDs, true)
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoint groups from the database", err}
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}
}
@@ -64,7 +64,7 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht
}
if search != "" {
tags, err := handler.DataStore.Tag().Tags()
tags, err := handler.TagService.Tags()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve tags from the database", err}
}

View File

@@ -16,16 +16,20 @@ func (handler *Handler) endpointSnapshot(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
}
if endpoint.Type == portainer.AzureEnvironment {
return &httperror.HandlerError{http.StatusBadRequest, "Snapshots not supported for Azure endpoints", err}
}
snapshot, snapshotError := handler.Snapshotter.CreateSnapshot(endpoint)
latestEndpointReference, err := handler.DataStore.Endpoint().Endpoint(endpoint.ID)
latestEndpointReference, err := handler.EndpointService.Endpoint(endpoint.ID)
if latestEndpointReference == nil {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
}
@@ -39,7 +43,7 @@ func (handler *Handler) endpointSnapshot(w http.ResponseWriter, r *http.Request)
latestEndpointReference.Snapshots = []portainer.Snapshot{*snapshot}
}
err = handler.DataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
err = handler.EndpointService.UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}

View File

@@ -11,15 +11,19 @@ import (
// POST request on /api/endpoints/snapshot
func (handler *Handler) endpointSnapshots(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err}
}
for _, endpoint := range endpoints {
if endpoint.Type == portainer.AzureEnvironment {
continue
}
snapshot, snapshotError := handler.Snapshotter.CreateSnapshot(&endpoint)
latestEndpointReference, err := handler.DataStore.Endpoint().Endpoint(endpoint.ID)
latestEndpointReference, err := handler.EndpointService.Endpoint(endpoint.ID)
if latestEndpointReference == nil {
log.Printf("background schedule error (endpoint snapshot). Endpoint not found inside the database anymore (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
continue
@@ -35,7 +39,7 @@ func (handler *Handler) endpointSnapshots(w http.ResponseWriter, r *http.Request
latestEndpointReference.Snapshots = []portainer.Snapshot{*snapshot}
}
err = handler.DataStore.Endpoint().UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
err = handler.EndpointService.UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}

View File

@@ -30,7 +30,7 @@ func (handler *Handler) endpointStatusInspect(w http.ResponseWriter, r *http.Req
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -47,13 +47,13 @@ func (handler *Handler) endpointStatusInspect(w http.ResponseWriter, r *http.Req
endpoint.EdgeID = edgeIdentifier
err := handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
err := handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to Unable to persist endpoint changes inside the database", err}
}
}
settings, err := handler.DataStore.Settings().Settings()
settings, err := handler.SettingsService.Settings()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
}
@@ -72,14 +72,14 @@ func (handler *Handler) endpointStatusInspect(w http.ResponseWriter, r *http.Req
handler.ReverseTunnelService.SetTunnelStatusToActive(endpoint.ID)
}
relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpoint.ID)
relation, err := handler.EndpointRelationService.EndpointRelation(endpoint.ID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve relation object from the database", err}
}
edgeStacksStatus := []stackStatusResponse{}
for stackID := range relation.EdgeStacks {
stack, err := handler.DataStore.EdgeStack().EdgeStack(stackID)
stack, err := handler.EdgeStackService.EdgeStack(stackID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stack from the database", err}
}

View File

@@ -9,20 +9,24 @@ import (
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/client"
)
type endpointUpdatePayload struct {
Name *string
URL *string
PublicURL *string
GroupID *int
TLS *bool
TLSSkipVerify *bool
TLSSkipClientVerify *bool
Status *int
TagIDs []portainer.TagID
UserAccessPolicies portainer.UserAccessPolicies
TeamAccessPolicies portainer.TeamAccessPolicies
Name *string
URL *string
PublicURL *string
GroupID *int
TLS *bool
TLSSkipVerify *bool
TLSSkipClientVerify *bool
Status *int
AzureApplicationID *string
AzureTenantID *string
AzureAuthenticationKey *string
TagIDs []portainer.TagID
UserAccessPolicies portainer.UserAccessPolicies
TeamAccessPolicies portainer.TeamAccessPolicies
}
func (payload *endpointUpdatePayload) Validate(r *http.Request) error {
@@ -31,6 +35,10 @@ func (payload *endpointUpdatePayload) Validate(r *http.Request) error {
// PUT request on /api/endpoints/:id
func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
if !handler.authorizeEndpointManagement {
return &httperror.HandlerError{http.StatusServiceUnavailable, "Endpoint management is disabled", ErrEndpointManagementDisabled}
}
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
@@ -42,7 +50,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
@@ -80,13 +88,13 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
removeTags := portainer.TagDifference(endpointTagSet, payloadTagSet)
for tagID := range removeTags {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a tag inside the database", err}
}
delete(tag.Endpoints, endpoint.ID)
err = handler.DataStore.Tag().UpdateTag(tag.ID, tag)
err = handler.TagService.UpdateTag(tag.ID, tag)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist tag changes inside the database", err}
}
@@ -94,14 +102,14 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
endpoint.TagIDs = payload.TagIDs
for _, tagID := range payload.TagIDs {
tag, err := handler.DataStore.Tag().Tag(tagID)
tag, err := handler.TagService.Tag(tagID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a tag inside the database", err}
}
tag.Endpoints[endpoint.ID] = true
err = handler.DataStore.Tag().UpdateTag(tag.ID, tag)
err = handler.TagService.UpdateTag(tag.ID, tag)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist tag changes inside the database", err}
}
@@ -133,6 +141,26 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
}
}
if endpoint.Type == portainer.AzureEnvironment {
credentials := endpoint.AzureCredentials
if payload.AzureApplicationID != nil {
credentials.ApplicationID = *payload.AzureApplicationID
}
if payload.AzureTenantID != nil {
credentials.TenantID = *payload.AzureTenantID
}
if payload.AzureAuthenticationKey != nil {
credentials.AuthenticationKey = *payload.AzureAuthenticationKey
}
httpClient := client.NewHTTPClient()
_, authErr := httpClient.ExecuteAzureAuthenticationRequest(&credentials)
if authErr != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to authenticate against Azure", authErr}
}
endpoint.AzureCredentials = credentials
}
if payload.TLS != nil {
folder := strconv.Itoa(endpointID)
@@ -177,14 +205,14 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
}
}
if payload.URL != nil || payload.TLS != nil {
if payload.URL != nil || payload.TLS != nil || endpoint.Type == portainer.AzureEnvironment {
_, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to register HTTP proxy for the endpoint", err}
}
}
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
err = handler.EndpointService.UpdateEndpoint(endpoint.ID, endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint changes inside the database", err}
}
@@ -197,22 +225,22 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
}
if endpoint.Type == portainer.EdgeAgentEnvironment && (groupIDChanged || tagsChanged) {
relation, err := handler.DataStore.EndpointRelation().EndpointRelation(endpoint.ID)
relation, err := handler.EndpointRelationService.EndpointRelation(endpoint.ID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find endpoint relation inside the database", err}
}
endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(endpoint.GroupID)
endpointGroup, err := handler.EndpointGroupService.EndpointGroup(endpoint.GroupID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find endpoint group inside the database", err}
}
edgeGroups, err := handler.DataStore.EdgeGroup().EdgeGroups()
edgeGroups, err := handler.EdgeGroupService.EdgeGroups()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge groups from the database", err}
}
edgeStacks, err := handler.DataStore.EdgeStack().EdgeStacks()
edgeStacks, err := handler.EdgeStackService.EdgeStacks()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve edge stacks from the database", err}
}
@@ -226,7 +254,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
relation.EdgeStacks = edgeStackSet
err = handler.DataStore.EndpointRelation().UpdateEndpointRelation(endpoint.ID, relation)
err = handler.EndpointRelationService.UpdateEndpointRelation(endpoint.ID, relation)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist endpoint relation changes inside the database", err}
}

View File

@@ -11,7 +11,14 @@ import (
"github.com/gorilla/mux"
)
const (
// ErrEndpointManagementDisabled is an error raised when trying to access the endpoints management endpoints
// when the server has been started with the --external-endpoints flag
ErrEndpointManagementDisabled = portainer.Error("Endpoint management is disabled")
)
func hideFields(endpoint *portainer.Endpoint) {
endpoint.AzureCredentials = portainer.AzureCredentials{}
if len(endpoint.Snapshots) > 0 {
endpoint.Snapshots[0].SnapshotRaw = portainer.SnapshotRaw{}
}
@@ -20,21 +27,29 @@ func hideFields(endpoint *portainer.Endpoint) {
// Handler is the HTTP handler used to handle endpoint operations.
type Handler struct {
*mux.Router
requestBouncer *security.RequestBouncer
DataStore portainer.DataStore
AuthorizationService *portainer.AuthorizationService
FileService portainer.FileService
JobService portainer.JobService
ProxyManager *proxy.Manager
ReverseTunnelService portainer.ReverseTunnelService
Snapshotter portainer.Snapshotter
authorizeEndpointManagement bool
requestBouncer *security.RequestBouncer
AuthorizationService *portainer.AuthorizationService
EdgeGroupService portainer.EdgeGroupService
EdgeStackService portainer.EdgeStackService
EndpointService portainer.EndpointService
EndpointGroupService portainer.EndpointGroupService
EndpointRelationService portainer.EndpointRelationService
FileService portainer.FileService
JobService portainer.JobService
ProxyManager *proxy.Manager
ReverseTunnelService portainer.ReverseTunnelService
SettingsService portainer.SettingsService
Snapshotter portainer.Snapshotter
TagService portainer.TagService
}
// NewHandler creates a handler to manage endpoint operations.
func NewHandler(bouncer *security.RequestBouncer) *Handler {
func NewHandler(bouncer *security.RequestBouncer, authorizeEndpointManagement bool) *Handler {
h := &Handler{
Router: mux.NewRouter(),
requestBouncer: bouncer,
Router: mux.NewRouter(),
authorizeEndpointManagement: authorizeEndpointManagement,
requestBouncer: bouncer,
}
h.Handle("/endpoints",

View File

@@ -17,7 +17,7 @@ func updateTeamAccessPolicyToReadOnlyRole(policies portainer.TeamAccessPolicies,
}
func (handler *Handler) upgradeRBACData() error {
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return err
}
@@ -31,13 +31,13 @@ func (handler *Handler) upgradeRBACData() error {
updateTeamAccessPolicyToReadOnlyRole(endpointGroup.TeamAccessPolicies, key)
}
err := handler.DataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
err := handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return err
}
@@ -51,7 +51,7 @@ func (handler *Handler) upgradeRBACData() error {
updateTeamAccessPolicyToReadOnlyRole(endpoint.TeamAccessPolicies, key)
}
err := handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint)
err := handler.EndpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
@@ -73,7 +73,7 @@ func updateTeamAccessPolicyToNoRole(policies portainer.TeamAccessPolicies, key p
}
func (handler *Handler) downgradeRBACData() error {
endpointGroups, err := handler.DataStore.EndpointGroup().EndpointGroups()
endpointGroups, err := handler.EndpointGroupService.EndpointGroups()
if err != nil {
return err
}
@@ -87,13 +87,13 @@ func (handler *Handler) downgradeRBACData() error {
updateTeamAccessPolicyToNoRole(endpointGroup.TeamAccessPolicies, key)
}
err := handler.DataStore.EndpointGroup().UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
err := handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
}
endpoints, err := handler.DataStore.Endpoint().Endpoints()
endpoints, err := handler.EndpointService.Endpoints()
if err != nil {
return err
}
@@ -107,7 +107,7 @@ func (handler *Handler) downgradeRBACData() error {
updateTeamAccessPolicyToNoRole(endpoint.TeamAccessPolicies, key)
}
err := handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, &endpoint)
err := handler.EndpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}

View File

@@ -36,21 +36,26 @@ func (handler *Handler) extensionCreate(w http.ResponseWriter, r *http.Request)
}
extensionID := portainer.ExtensionID(extensionIdentifier)
extensions, err := handler.DataStore.Extension().Extensions()
extensions, err := handler.ExtensionService.Extensions()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extensions status from the database", err}
}
for _, existingExtension := range extensions {
if existingExtension.ID == extensionID && existingExtension.Enabled {
return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", portainer.ErrExtensionAlreadyEnabled}
}
}
extension := &portainer.Extension{
ID: extensionID,
}
for _, existingExtension := range extensions {
if existingExtension.ID == extensionID && (existingExtension.Enabled || !existingExtension.License.Valid) {
if existingExtension.License.LicenseKey == payload.License {
return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", portainer.ErrExtensionAlreadyEnabled}
}
_ = handler.ExtensionManager.DisableExtension(&existingExtension)
extension.Enabled = true
}
}
extensionDefinitions, err := handler.ExtensionManager.FetchExtensionDefinitions()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extension definitions", err}
@@ -68,16 +73,15 @@ func (handler *Handler) extensionCreate(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to enable extension", err}
}
extension.Enabled = true
if extension.ID == portainer.RBACExtension {
if extension.ID == portainer.RBACExtension && !extension.Enabled {
err = handler.upgradeRBACData()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "An error occured during database update", err}
}
}
err = handler.DataStore.Extension().Persist(extension)
extension.Enabled = true
err = handler.ExtensionService.Persist(extension)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist extension status inside the database", err}
}

View File

@@ -17,7 +17,7 @@ func (handler *Handler) extensionDelete(w http.ResponseWriter, r *http.Request)
}
extensionID := portainer.ExtensionID(extensionIdentifier)
extension, err := handler.DataStore.Extension().Extension(extensionID)
extension, err := handler.ExtensionService.Extension(extensionID)
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a extension with the specified identifier inside the database", err}
} else if err != nil {
@@ -36,7 +36,7 @@ func (handler *Handler) extensionDelete(w http.ResponseWriter, r *http.Request)
}
}
err = handler.DataStore.Extension().DeleteExtension(extensionID)
err = handler.ExtensionService.DeleteExtension(extensionID)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to delete the extension from the database", err}
}

View File

@@ -25,7 +25,7 @@ func (handler *Handler) extensionInspect(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extensions informations", err}
}
localExtension, err := handler.DataStore.Extension().Extension(extensionID)
localExtension, err := handler.ExtensionService.Extension(extensionID)
if err != nil && err != portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extension information from the database", err}
}

View File

@@ -12,7 +12,7 @@ import (
func (handler *Handler) extensionList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
fetchManifestInformation, _ := request.RetrieveBooleanQueryParameter(r, "store", true)
extensions, err := handler.DataStore.Extension().Extensions()
extensions, err := handler.ExtensionService.Extensions()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extensions from the database", err}
}

View File

@@ -35,7 +35,7 @@ func (handler *Handler) extensionUpdate(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
extension, err := handler.DataStore.Extension().Extension(extensionID)
extension, err := handler.ExtensionService.Extension(extensionID)
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a extension with the specified identifier inside the database", err}
} else if err != nil {
@@ -47,7 +47,7 @@ func (handler *Handler) extensionUpdate(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update extension", err}
}
err = handler.DataStore.Extension().Persist(extension)
err = handler.ExtensionService.Persist(extension)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist extension status inside the database", err}
}

View File

@@ -46,10 +46,21 @@ func (handler *Handler) extensionUpload(w http.ResponseWriter, r *http.Request)
}
extensionID := portainer.ExtensionID(extensionIdentifier)
extensions, err := handler.ExtensionService.Extensions()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extensions status from the database", err}
}
extension := &portainer.Extension{
ID: extensionID,
}
for _, existingExtension := range extensions {
if existingExtension.ID == extensionID && (existingExtension.Enabled || !existingExtension.License.Valid) {
extension.Enabled = true
}
}
_ = handler.ExtensionManager.DisableExtension(extension)
err = handler.ExtensionManager.InstallExtension(extension, payload.License, payload.ArchiveFileName, payload.ExtensionArchive)
@@ -57,16 +68,16 @@ func (handler *Handler) extensionUpload(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to install extension", err}
}
extension.Enabled = true
if extension.ID == portainer.RBACExtension {
if extension.ID == portainer.RBACExtension && !extension.Enabled {
err = handler.upgradeRBACData()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "An error occured during database update", err}
}
}
err = handler.DataStore.Extension().Persist(extension)
extension.Enabled = true
err = handler.ExtensionService.Persist(extension)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist extension status inside the database", err}
}

View File

@@ -14,8 +14,11 @@ import (
// Handler is the HTTP handler used to handle extension operations.
type Handler struct {
*mux.Router
DataStore portainer.DataStore
ExtensionService portainer.ExtensionService
ExtensionManager portainer.ExtensionManager
EndpointGroupService portainer.EndpointGroupService
EndpointService portainer.EndpointService
RegistryService portainer.RegistryService
AuthorizationService *portainer.AuthorizationService
}

View File

@@ -90,6 +90,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/storidge/"):
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/azure/"):
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/edge/"):
http.StripPrefix("/api/endpoints", h.EndpointEdgeHandler).ServeHTTP(w, r)
default:

View File

@@ -18,10 +18,11 @@ func hideFields(registry *portainer.Registry) {
// Handler is the HTTP handler used to handle registry operations.
type Handler struct {
*mux.Router
requestBouncer *security.RequestBouncer
DataStore portainer.DataStore
FileService portainer.FileService
ProxyManager *proxy.Manager
requestBouncer *security.RequestBouncer
RegistryService portainer.RegistryService
ExtensionService portainer.ExtensionService
FileService portainer.FileService
ProxyManager *proxy.Manager
}
// NewHandler creates a handler to manage registry operations.

View File

@@ -17,7 +17,7 @@ func (handler *Handler) proxyRequestsToRegistryAPI(w http.ResponseWriter, r *htt
return &httperror.HandlerError{http.StatusBadRequest, "Invalid registry identifier route variable", err}
}
registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID))
registry, err := handler.RegistryService.Registry(portainer.RegistryID(registryID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err}
} else if err != nil {
@@ -29,7 +29,7 @@ func (handler *Handler) proxyRequestsToRegistryAPI(w http.ResponseWriter, r *htt
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", portainer.ErrEndpointAccessDenied}
}
extension, err := handler.DataStore.Extension().Extension(portainer.RegistryManagementExtension)
extension, err := handler.ExtensionService.Extension(portainer.RegistryManagementExtension)
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Registry management extension is not enabled", err}
} else if err != nil {

View File

@@ -17,7 +17,7 @@ func (handler *Handler) proxyRequestsToGitlabAPIWithRegistry(w http.ResponseWrit
return &httperror.HandlerError{http.StatusBadRequest, "Invalid registry identifier route variable", err}
}
registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID))
registry, err := handler.RegistryService.Registry(portainer.RegistryID(registryID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err}
} else if err != nil {
@@ -29,7 +29,7 @@ func (handler *Handler) proxyRequestsToGitlabAPIWithRegistry(w http.ResponseWrit
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", portainer.ErrEndpointAccessDenied}
}
extension, err := handler.DataStore.Extension().Extension(portainer.RegistryManagementExtension)
extension, err := handler.ExtensionService.Extension(portainer.RegistryManagementExtension)
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Registry management extension is not enabled", err}
} else if err != nil {

View File

@@ -78,7 +78,7 @@ func (handler *Handler) registryConfigure(w http.ResponseWriter, r *http.Request
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID))
registry, err := handler.RegistryService.Registry(portainer.RegistryID(registryID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err}
} else if err != nil {
@@ -128,7 +128,7 @@ func (handler *Handler) registryConfigure(w http.ResponseWriter, r *http.Request
}
}
err = handler.DataStore.Registry().UpdateRegistry(registry.ID, registry)
err = handler.RegistryService.UpdateRegistry(registry.ID, registry)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist registry changes inside the database", err}
}

View File

@@ -55,7 +55,7 @@ func (handler *Handler) registryCreate(w http.ResponseWriter, r *http.Request) *
Gitlab: payload.Gitlab,
}
err = handler.DataStore.Registry().CreateRegistry(registry)
err = handler.RegistryService.CreateRegistry(registry)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the registry inside the database", err}
}

View File

@@ -16,14 +16,14 @@ func (handler *Handler) registryDelete(w http.ResponseWriter, r *http.Request) *
return &httperror.HandlerError{http.StatusBadRequest, "Invalid registry identifier route variable", err}
}
_, err = handler.DataStore.Registry().Registry(portainer.RegistryID(registryID))
_, err = handler.RegistryService.Registry(portainer.RegistryID(registryID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a registry with the specified identifier inside the database", err}
}
err = handler.DataStore.Registry().DeleteRegistry(portainer.RegistryID(registryID))
err = handler.RegistryService.DeleteRegistry(portainer.RegistryID(registryID))
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove the registry from the database", err}
}

View File

@@ -16,7 +16,7 @@ func (handler *Handler) registryInspect(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusBadRequest, "Invalid registry identifier route variable", err}
}
registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID))
registry, err := handler.RegistryService.Registry(portainer.RegistryID(registryID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err}
} else if err != nil {

View File

@@ -10,7 +10,7 @@ import (
// GET request on /api/registries
func (handler *Handler) registryList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
registries, err := handler.DataStore.Registry().Registries()
registries, err := handler.RegistryService.Registries()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve registries from the database", err}
}

View File

@@ -36,7 +36,7 @@ func (handler *Handler) registryUpdate(w http.ResponseWriter, r *http.Request) *
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID))
registry, err := handler.RegistryService.Registry(portainer.RegistryID(registryID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err}
} else if err != nil {
@@ -48,7 +48,7 @@ func (handler *Handler) registryUpdate(w http.ResponseWriter, r *http.Request) *
}
if payload.URL != nil {
registries, err := handler.DataStore.Registry().Registries()
registries, err := handler.RegistryService.Registries()
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve registries from the database", err}
}
@@ -88,7 +88,7 @@ func (handler *Handler) registryUpdate(w http.ResponseWriter, r *http.Request) *
registry.TeamAccessPolicies = payload.TeamAccessPolicies
}
err = handler.DataStore.Registry().UpdateRegistry(registry.ID, registry)
err = handler.RegistryService.UpdateRegistry(registry.ID, registry)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist registry changes inside the database", err}
}

View File

@@ -12,7 +12,7 @@ import (
// Handler is the HTTP handler used to handle resource control operations.
type Handler struct {
*mux.Router
DataStore portainer.DataStore
ResourceControlService portainer.ResourceControlService
}
// NewHandler creates a handler to manage resource control operations.

View File

@@ -68,7 +68,7 @@ func (handler *Handler) resourceControlCreate(w http.ResponseWriter, r *http.Req
return &httperror.HandlerError{http.StatusBadRequest, "Invalid type value. Value must be one of: container, service, volume, network, secret, stack or config", portainer.ErrInvalidResourceControlType}
}
rc, err := handler.DataStore.ResourceControl().ResourceControlByResourceIDAndType(payload.ResourceID, resourceControlType)
rc, err := handler.ResourceControlService.ResourceControlByResourceIDAndType(payload.ResourceID, resourceControlType)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve resource controls from the database", err}
}
@@ -104,7 +104,7 @@ func (handler *Handler) resourceControlCreate(w http.ResponseWriter, r *http.Req
TeamAccesses: teamAccesses,
}
err = handler.DataStore.ResourceControl().CreateResourceControl(&resourceControl)
err = handler.ResourceControlService.CreateResourceControl(&resourceControl)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the resource control inside the database", err}
}

View File

@@ -16,14 +16,14 @@ func (handler *Handler) resourceControlDelete(w http.ResponseWriter, r *http.Req
return &httperror.HandlerError{http.StatusBadRequest, "Invalid resource control identifier route variable", err}
}
_, err = handler.DataStore.ResourceControl().ResourceControl(portainer.ResourceControlID(resourceControlID))
_, err = handler.ResourceControlService.ResourceControl(portainer.ResourceControlID(resourceControlID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a resource control with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a resource control with with the specified identifier inside the database", err}
}
err = handler.DataStore.ResourceControl().DeleteResourceControl(portainer.ResourceControlID(resourceControlID))
err = handler.ResourceControlService.DeleteResourceControl(portainer.ResourceControlID(resourceControlID))
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove the resource control from the database", err}
}

View File

@@ -42,7 +42,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req
return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
}
resourceControl, err := handler.DataStore.ResourceControl().ResourceControl(portainer.ResourceControlID(resourceControlID))
resourceControl, err := handler.ResourceControlService.ResourceControl(portainer.ResourceControlID(resourceControlID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find a resource control with the specified identifier inside the database", err}
} else if err != nil {
@@ -85,7 +85,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the resource control", portainer.ErrResourceAccessDenied}
}
err = handler.DataStore.ResourceControl().UpdateResourceControl(resourceControl.ID, resourceControl)
err = handler.ResourceControlService.UpdateResourceControl(resourceControl.ID, resourceControl)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist resource control changes inside the database", err}
}

View File

@@ -12,7 +12,7 @@ import (
// Handler is the HTTP handler used to handle role operations.
type Handler struct {
*mux.Router
DataStore portainer.DataStore
RoleService portainer.RoleService
}
// NewHandler creates a handler to manage role operations.

Some files were not shown because too many files have changed in this diff Show More