Compare commits

...

897 Commits

Author SHA1 Message Date
Hui
b21e88bb3c compose and swarm stack updates 2021-06-08 17:24:30 +12:00
wheresolivia
ea6df891c3 Merge pull request #5014 from portainer/feat/EE-445/resourcepool-namespace
feat(k8s): replace resourcepool with namespace EE-445
2021-06-02 11:30:20 +12:00
Chaim Lev-Ari
230f8fddc3 fix(kube): replace remaining resource pool texts 2021-06-01 11:56:47 +03:00
Chaim Lev-Ari
6734f0ab74 feat(k8s): replace resource pool with name space 2021-06-01 11:52:05 +03:00
Chaim Lev-Ari
3e60167aeb feat(k8s/applications): default to isolated application 2021-06-01 11:52:05 +03:00
Chaim Lev-Ari
8a4902f15a feat(k8s/applications): rephrase descriptions 2021-06-01 11:52:05 +03:00
yi-portainer
dde0467b89 Merge branch 'release/2.5' into develop 2021-05-28 10:16:38 +12:00
wheresolivia
a2a197b14b Merge pull request #5033 from portainer/fix/CE-575/type-downgrade-error
fix(portainer): Fix the typo in the downgrade error message
2021-05-27 16:46:48 +12:00
cong meng
ee403ca32a fix(image) Confirmation modal on builder output view EE-816 (#5114)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-05-27 13:52:02 +12:00
fhanportainer
d7fcfee2a2 fix(templates): checking windows endpoint and template properties. (#5108)
* fix(templates): checking windows endpoint and template properties.

* fix(templates): removed debug code.

* fix(templates): fixed type issue in custom template.
2021-05-27 08:56:13 +12:00
cong meng
3018801fc0 fix(image) Confirmation modal on builder output view EE-816 (#5107)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-05-26 17:11:32 +12:00
fhanportainer
6bfbf58cdb fix(template): fixed disabled deploy button EE-812 (#5105) 2021-05-25 18:55:50 +02:00
dbuduev
3568fe9e52 feat(git) git clone improvements [EE-451] (#5070) 2021-05-24 17:27:07 +12:00
yi-portainer
2270de73ee Merge branch 'release/2.5' into develop 2021-05-24 08:53:10 +12:00
Chaim Lev-Ari
819faa3948 fix(k8s/proxy): proxy healthz request to k8s api (#5090) 2021-05-21 00:20:08 +02:00
wheresolivia
ef8794c2b9 Merge pull request #5079 from portainer/fix/EE-769/code-editor-prompt-on-change
fix(stacks): check for editor change before setting as dirty
2021-05-20 18:44:46 +12:00
Felix Han
5618794927 fix(k8s-config): check for config editor change before setting as dirty 2021-05-20 11:46:17 +12:00
Felix Han
47d462f085 fix(web-editor): check for editor change before setting as dirty. 2021-05-20 10:22:07 +12:00
zees-dev
0114766d50 Merge pull request #5086 from portainer/revert-5084-feat/EE-341/EE-777/oauth-memberships-teaser
Revert "feat(oauth/team-memberships): EE oauth team memberships teaser"
2021-05-20 10:21:11 +12:00
Stéphane Busso
2b94aa5aa6 Revert "feat(oauth/team-memberships): EE oauth team memberships teaser" 2021-05-20 10:03:59 +12:00
cong meng
746e738f1d Merge pull request #5084 from portainer/feat/EE-341/EE-777/oauth-memberships-teaser
feat(oauth/team-memberships): EE oauth team memberships teaser
2021-05-20 09:21:10 +12:00
zees-dev
29f5008c5f EE oauth team memberships feature teaser 2021-05-19 16:15:46 +12:00
Felix Han
e54d99fd3d fix(stacks): remove line breaks in web editors value 2021-05-19 12:09:11 +12:00
Chaim Lev-Ari
b3784792fe fix(stacks): show containers only for standalone (#5080) 2021-05-18 23:06:04 +02:00
Chaim Lev-Ari
87e7d8ada8 fix(stacks): check for editor change before setting as dirty 2021-05-18 14:08:23 +03:00
yi-portainer
af03d91e39 Merge branch 'release/2.5' into develop 2021-05-18 17:02:31 +12:00
yi-portainer
71635834c7 * update portainer version to 2.5.0
(cherry picked from commit 43702c2516)
2021-05-13 18:32:42 +12:00
yi-portainer
43702c2516 * update portainer version to 2.5.0 2021-05-13 18:30:34 +12:00
Chaim Lev-Ari
a21798f518 fix(docker/containers): show sysctl control (#5051) 2021-05-12 02:29:35 +02:00
dbuduev
3641158daf fix: docker-compose use custom config.json to access private images (#5058)
cherry-picking commit a6b289c9.

Co-authored-by: Dmitry Salakhov <to@dimasalakhov.com>
2021-05-11 23:05:00 +02:00
Chaim Lev-Ari
0ac6274712 fix(docker/services): create a service webhook (#5052) 2021-05-11 10:59:42 +12:00
Chaim Lev-Ari
886d6764be fix(docker): set image pulls as valid if failed fetching (#5055) 2021-05-11 09:24:29 +12:00
Chaim Lev-Ari
39e24ec93f fix(docker): set image pulls as valid if failed fetching (#5007) 2021-05-07 15:38:58 +12:00
Chaim Lev-Ari
b7980f1b60 fix(k8s/ingress): remove only selected ingress (#5035)
* fix(k8s/ingress): remove only selected ingress

* fix(k8s/ingress): remove ingress from namespace
2021-05-07 09:49:56 +12:00
Maxime Bajeux
ce04944ce6 fix(portainer): Fix the type in the downgrade error message 2021-05-05 11:44:00 +02:00
Hui
564bea7575 fix(ACI): ACI UAC breaks when redeploying container with same name asone already existing EE-645 (#5030)
* add existing continer instance checking logic

* modify response status code and err message

* return json instead of plain text for err msg

* Update api/http/proxy/factory/azure/containergroup.go

* Update api/http/proxy/factory/azure/containergroup.go

* Update api/http/proxy/factory/azure/containergroup.go

Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
2021-05-05 20:26:31 +12:00
Chaim Lev-Ari
dcc77e50e5 fix(docker/images): show image selector advanced mode (#5032) 2021-05-05 20:16:59 +12:00
Stéphane Busso
317ebe2bfc Revert "feat(edge) EE-596 Update the version of agent to 2.4.0 in agent deploy command on the adding edge screen (#5021)" (#5031)
This reverts commit 7e2ce3ffc2.
2021-05-05 16:24:20 +12:00
zees-dev
daabce2b8f Merge pull request #4406 from ricmatsui/feat1654-colorize-logs
feat(log-viewer): add ansi color support for logs
2021-05-03 09:25:24 +12:00
cong meng
7e2ce3ffc2 feat(edge) EE-596 Update the version of agent to 2.4.0 in agent deploy command on the adding edge screen (#5021)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-04-29 16:25:09 +12:00
Alice Groux
d99358ea8e feat(k8s/container): realtime metrics (#4416)
* feat(k8s/container): metrics layout

* feat(k8s/container): memory graph

* feat(k8s/container): cpu usage percent

* feat(k8s/metrics): metrics api validation to enable metrics server

* feat(k8s/pods): update error metrics view

* feat(k8s/container): improve stopRepeater function

* feat(k8s/pods): display empty view instead of empty graphs

* feat(k8s/pods): fix CPU usage

* feat(k8s/configure): fix the metrics server test

* feat(k8s/pod): fix cpu issue

* feat(k8s/pod): fix toaster for non register pods in metrics server

* feat(k8s/service): remove options before 30 secondes for refresh rate

* feat(k8s/pod): fix default value for the refresh rate

* feat(k8s/pod): fix rebase
2021-04-29 13:10:14 +12:00
Alice Groux
befccacc27 feat(k8s/ingress): create multiple ingress network per kubernetes namespace (#4464)
* feat(k8s/ingress): introduce multiple hosts per ingress

* feat(k8s/ingress): host selector in app create/edit

* feat(k8s/ingress): save empty hosts

* feat(k8s/ingress): fix empty host

* feat(k8s/ingress): rename inputs + ensure hostnames unicity + fix remove hostname and routes

* feat(k8s/ingress): fix duplicates hostname validation

* feat(k8s/application): fix rebase

* feat(k8s/resource-pool): fix error messages for ingress (wip)

* fix(k8s/resource-pool): ingress duplicates detection
2021-04-28 05:51:13 +12:00
yi-portainer
ca849e31a1 * update version to 2.4 2021-04-21 12:49:09 +12:00
wheresolivia
335bfb81ba Merge pull request #4965 from portainer/feat(backup)-backup-restore-system
feat(backup): Add backup/restore to the server [EE-386] [EE-378] [CE-452]
2021-04-21 12:16:39 +12:00
wheresolivia
ba2e1d1f60 Merge pull request #4986 from portainer/feat/CE-414/add-UAC-to-ACI
feat(ACI): add UAC to ACI
2021-04-21 11:45:19 +12:00
Ricardo Matsui
a7fc7816d1 Merge branch 'develop' into feat1654-colorize-logs 2021-04-15 22:38:43 -07:00
Felix Han
5b26ef2036 feat(ACI): updated function name 2021-04-14 16:08:49 +12:00
Felix Han
effb0f6272 Merge branch 'feat/CE-414/add-UAC-to-ACI' of https://github.com/portainer/portainer into feat/CE-414/add-UAC-to-ACI 2021-04-14 16:06:16 +12:00
LP B
2f95b449aa Revert "feat(ACI): add UAC to ACI (#4952)" (#4982)
This reverts commit 12cf4a00f0.
2021-04-13 15:56:43 +02:00
fhanportainer
12cf4a00f0 feat(ACI): add UAC to ACI (#4952) 2021-04-13 23:55:11 +12:00
Lukas Grotz
d09ae22ba8 feat(container): add sysctls setting in the container view (#4910)
* feat(container): add sysctls in the container view (#2756)

* feat(container): add setting to restrict sysctl access

* feat(endpoint): move sysctl disable setting to security settings

* feat(container): add sysctls to container edit view

* fix(container) remove unnecessary migration setting

Co-authored-by: Owen Kirby <oskirby@gmail.com>
2021-04-12 19:40:45 +12:00
Chaim Lev-Ari
ac7d819620 style(proxy): fix function name (#4970) 2021-04-09 09:02:48 +12:00
fhanportainer
0aec8fd423 EE-379: add S3 stubs to CE (#4967) 2021-04-08 13:32:59 +12:00
Dmitry Salakhov
8bf662c13a that shouldn't be removed 2021-04-07 16:49:27 +12:00
Dmitry Salakhov
fc9511dc97 UI 2021-04-07 13:21:58 +12:00
Dmitry Salakhov
6d8f5e7479 go 1.13 compatibility 2021-04-07 12:12:19 +12:00
Dmitry Salakhov
a3ec2f8e85 feat(backup): Add backup/restore to the server 2021-04-06 22:08:43 +12:00
Chaim Lev-Ari
c04bbb5775 fix(build): ignore chardet missing sourcemaps (#4760) 2021-04-05 23:12:51 +02:00
Chaim Lev-Ari
20cbeb698d chore(deps): remove grunt-html2js and grunt-karma (#4765)
fix #4764
2021-04-05 23:12:25 +02:00
fhanportainer
e75678dd11 fix(container): fixed pull latest image toggle missing on service update and container recreate modal (#4956) 2021-04-01 10:35:42 +13:00
Felix Han
e3e7e84821 feat(ACI): add UAC to ACI 2021-03-30 10:58:56 +13:00
cong meng
ad2910f3f0 fix(registry): #4371 fix broken GITLAB registry (#4935)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-03-25 11:50:34 +13:00
Chaim Lev-Ari
f5aa6c4dc2 feat(docker): show docker pull rate limits (#4666)
* feat(dockerhub): introduce local status endpoint

* feat(proxy): rewrite request with dockerhub credentials

* feat(endpoint): check env type

* feat(endpoint): check for local endpoint

* feat(docker): introduce client side service to get limits

* feat(container): add info about rate limits in container

* feat(dockerhub): load rate limits just for specific endpoints

* feat(images): show specific dockerhub messages for admin

* feat(service-create): show docker rate limits

* feat(service-edit): show rate limit messages

* fix(images): fix loading of page

* refactor(images): move rate limits check to container

* feat(kubernetes): proxy agent requests

* feat(kubernetes/apps): show pull limits in application creation

* refactor(image-registry): move warning to end of field

* fix(image-registry): show right message for admin

* fix(images): silently fail when loading rate limits

* fix(kube/apps): use new rate limits comp

* fix(images): move rate warning to end

* fix(registry): move search to right place

* fix(service): remove service warning

* fix(endpoints): check if kube endpoint is local
2021-03-24 19:27:32 +01:00
Chaim Lev-Ari
d1a21ef6c1 fix(home): redirect home if edge endpoint is down (#4670)
* fix(home): redirect home if edge endpoint is down

* fix(kubernetes): rephrase error message when endpoint is down

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
2021-03-23 21:38:30 +01:00
Chaim Lev-Ari
c542964073 fix(kuberenetes/deploy): use default resource pool (#4674) 2021-03-22 23:35:17 +01:00
Yi Chen
572b64b68e Merge changes from release 2.2 (#4930)
* fix windows build

* fix(endpoints): show correct values of security settings (#4889)

* fix(app): EndpointProvider fallback on URL EndpointID when no endpoint is selected (#4892)

* fix(templates): App templates not loading with error in browser console (#4895)

* fix(kube/config): show used key warning when needed (#4890)

fix [CE-469]
- recalculate duplcate keys when they are changed
- show used warning on duplicate keys

* fix(k8s): CE-471 variables from configuration showing on environment variables section on application edit screen (#4896)

* fix(k8s): CE-471 variables from configuration showing on environment variables section on application edit screen

* fix(k8s): CE-471 avoid to remove value path of env when patch k8s deployment, as the value path does not exist if env variable has empty value.

Co-authored-by: Simon Meng <simon.meng@portainer.io>

Co-authored-by: Dmitry Salakhov <to@dimasalakhov.com>
Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
Co-authored-by: Maxime Bajeux <max.bajeux@gmail.com>
Co-authored-by: cong meng <mcpacino@gmail.com>
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-03-23 08:58:11 +13:00
Stéphane Busso
239e434522 Add licensing information to contributing document 2021-03-22 15:40:08 +13:00
Stéphane Busso
9f4fe3af9e Link to attributions 2021-03-22 15:35:26 +13:00
Stéphane Busso
014ba40081 Chore: Add Licenses attributions (#4938) 2021-03-22 15:10:57 +13:00
Alice Groux
bca32b02c7 fix(k8s/endpoint): update endpoint URL (#4484)
* fix(k8s/endpoint): update endpoint URL

* fix(endpoints): handle kube agent url

* fix(endpoints): fix handling endpoint urls

Co-authored-by: Chaim Lev-Ari <chiptus@gmail.com>
2021-03-20 23:35:54 +01:00
Alice Groux
a7ed6222b0 feat(app): Prevent web editor related views from being accidentally closed (#4715)
* feat(app): when leaving a view with unsaved changed, a modal prompt the user with a confirmation message

feat(app): when leaving a view with unsaved changes, a modal prompt the user with a confirmation message

* feat(app/web-editor): fix the modal behaviour when editing a stack details

* feat(app/web-editor): add a reusable function confirmWebEditorDiscard in modal service

* feat(docker/stack): fix missing dependency
2021-03-20 22:13:27 +01:00
Chaim Lev-Ari
d0d38990c7 chore(plop): use templates as in style guide (#4916)
* chore(plop): use templates as in style guide

fix [CE-483]

* chore(plop): export component and add to module
2021-03-19 09:03:26 +13:00
Maxime Bajeux
32a9a2e46b Enable the ability to cordon/uncordon/drain nodes (#4723)
* feat(node): Enable the ability to cordon/uncordon/drain nodes

* feat(cluster): check if there is a drain operation somewhere

* feat(kubernetes): allow to cordon, uncordon, drain nodes

* refacto(kubernetes): set a constant for drain label name

* fix(node): Relocate the warning message next to the dropdown and change the information message
2021-03-15 22:36:14 +01:00
Maxime Bajeux
660bc2dadf fix(service): change application owner label in createPayload (#4841) 2021-03-14 22:48:17 +01:00
Dmitry Salakhov
4cbd231a5f fix: normalize stack name only for libcompose (#4862)
* fix: normilize stack name only for libcompose

* fix
2021-03-14 20:08:31 +01:00
cong meng
6d5877ca1c fix(registry): #4371 cannot push to quay.io registry (#4868)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-03-13 12:47:35 +13:00
Chaim Lev-Ari
dbb9a21384 fix(endpoints): use default edge checkin interval if n/a (#4909) 2021-03-11 21:00:05 +01:00
Chaim Lev-Ari
f4dd3067ed chore(deps): install core-js@2 (#4762)
fix #4761
2021-03-07 20:48:52 +01:00
psychowood
3dccc59048 feat(area-endpoints/creation): allow custom Docker socket (#4772) and handle public IP, group and tags for docket sockets (#4798)
* fix(endpoints/creation): hide TLS, make use of PublicIP, Groups, Tags for local Docker endpoint

* feat(endpoints/creation): allow specifying custom Docker socket (#4772)

* feat(endpoints/creation): override default socket path

* fix(endpoints/creation): typo socketPath -> SocketPath
2021-03-05 21:44:17 +01:00
aravind-korada
52d4296c08 feat(home): add node count to endpoint list. (#4793)
* feat(home): add node count to endpoint list.

* feat(home): add node count beside docker version
2021-03-04 16:42:47 +01:00
Maxime Bajeux
36fcbb9e18 feat(stack): prevent stack duplication if name already used (#4740)
* feat(stack): prevent stack duplication if name already used

* refacto(stack): deduplicate functions and rename variables

* refacto(stack): add a generic helper for findDeepAll function

* fix(templates): remove forgotten conflict markers
2021-03-03 14:54:35 +01:00
Dmitry Salakhov
f03cf2a6e4 fix(uac): ignore duplicates, spaces and casing in portainer labels (#4823)
* fix: ignore duplicates, spaces and casing in portainer labels

* cleanup

* fix: rebase error
2021-03-03 11:38:59 +02:00
Chaim Lev-Ari
6c8276c65c fix(service-details): clear volume source when changing type (#4671) 2021-03-02 23:10:34 +01:00
cong meng
c705c04d65 feat(volume) change the way portainer creates NFS4 volumes (#4729) (#4735)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-03-02 02:46:18 +01:00
Chaim Lev-Ari
56344ca7d9 feat(main): introduce description to fatal errors (#4468) 2021-03-01 21:49:57 +01:00
Chaim Lev-Ari
91ff7e4143 feat(edge): show last check in date (#4782)
* feat(k8s): better form validation for configuration keys (#4728) (#4733)

Co-authored-by: Simon Meng <simon.meng@portainer.io>

* feat(home): show edge valid tag

* fix(endpoint): show right heartbeat

* style(endpoints): add some comments

Co-authored-by: cong meng <mcpacino@gmail.com>
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-03-01 13:43:47 +13:00
cong meng
f2faccdb10 feat(k8s): better form validation for configuration keys (#4728) (#4733)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-02-27 01:53:47 +01:00
Alice Groux
ccf6babc02 refactor(app): backport technical changes (#4679)
* refactor(app): backport technical changes

* refactor(app): remove EE only features

* feat(app): small review changes to match EE codebase layout on some files

Co-authored-by: xAt0mZ <baron_l@epitech.eu>
2021-02-26 16:50:33 +01:00
Chaim Lev-Ari
158bdae10e feat(datatable): save text filters in session storage (#4741)
* feat(datatable): save text filters in session storage

* refactor(session): as jsdoc comments
2021-02-25 22:46:34 +01:00
Alice Groux
59faec45ce feat(k8s/application): add the ability to redeploy external application (#4704)
* feat(k8s/application): add the ability to redeploy external application

* feat(k8s/application): remove extra whitespace for pod application
2021-02-25 12:12:17 +01:00
Chaim Lev-Ari
c72d07441d feat(services): hide webhook interface (#4794) 2021-02-24 23:08:22 +01:00
Chaim Lev-Ari
7e7127831d fix(db): skip resource control migration if stack doesn't exist (#4879) 2021-02-25 10:27:49 +13:00
Dmitry Salakhov
3746542c69 Merge pull request #4878 from portainer/fix-windows-build
fix windows build
2021-02-23 23:35:48 +00:00
Dmitry Salakhov
ebe448b602 fix windows build 2021-02-24 12:06:20 +13:00
Eduardo Brito
d84a5b9c67 feat(yaml-inspector): add button to expand/collapse yaml inspector (#4007) (#4828)
* #4007 feat(yaml-inspector): add button to expand/collapse yaml inspector

* feat(yaml-inspector): add button to expand/collapse yaml inspector

Better yamlInspector.html formatting

* feat(yaml-inspector): change name of toggle function

More descriptive name for the function that toggles the expansion of the YAML inspector.
2021-02-23 22:02:36 +01:00
Chaim Lev-Ari
86ad1c6af1 feat(stacks): scope stack names to endpoint (#4520)
* refactor(stack): create unique name function

* refactor(stack): change stack resource control id

* feat(stacks): validate stack unique name in endpoint

* feat(stacks): prevent name collision with external stacks

* refactor(stacks): move resource id util

* refactor(stacks): supply resource id util with name and endpoint

* fix(docker): calculate swarm resource id

* feat(stack): prevent migration if stack name already exist

* feat(authorization): use stackutils
2021-02-23 21:18:05 +01:00
Alice Groux
a62e0496de feat(app/containers): display IP (#4435) 2021-02-23 20:45:37 +01:00
Chaim Lev-Ari
05ba00a8f7 fix(containers): fix layout in small screens (#4854) 2021-02-23 11:18:26 +01:00
yi-portainer
7846fdd801 * update version to 2.2.0 2021-02-23 20:18:40 +13:00
Chaim Lev-Ari
50b57614cf docs(api): document apis with swagger (#4678)
* feat(api): introduce swagger

* feat(api): anottate api

* chore(api): tag endpoints

* chore(api): remove tags

* chore(api): add docs for oauth auth

* chore(api): document create endpoint api

* chore(api): document endpoint inspect and list

* chore(api): document endpoint update and snapshots

* docs(endpointgroups): document groups api

* docs(auth): document auth api

* chore(build): introduce a yarn script to build api docs

* docs(api): document auth

* docs(customtemplates): document customtemplates api

* docs(tags): document api

* docs(api): document the use of token

* docs(dockerhub): document dockerhub api

* docs(edgegroups): document edgegroups api

* docs(edgejobs): document api

* docs(edgestacks): doc api

* docs(http/upload): add security

* docs(api): document edge templates

* docs(edge): document edge jobs

* docs(endpointgroups): change description

* docs(endpoints): document missing apis

* docs(motd): doc api

* docs(registries): doc api

* docs(resourcecontrol): api doc

* docs(role): add swagger docs

* docs(settings): add swagger docs

* docs(api/status): add swagger docs

* docs(api/teammembership): add swagger docs

* docs(api/teams): add swagger docs

* docs(api/templates): add swagger docs

* docs(api/users): add swagger docs

* docs(api/webhooks): add swagger docs

* docs(api/webscokets): add swagger docs

* docs(api/stacks): swagger

* docs(api): fix missing apis

* docs(swagger): regen

* chore(build): remove docs from build

* docs(api): update tags

* docs(api): document tags

* docs(api): add description

* docs(api): rename jwt token

* docs(api): add info about types

* docs(api): document types

* docs(api): update request types annotation

* docs(api): doc registry and resource control

* chore(docs): add snippet

* docs(api): add description to role

* docs(api): add types for settings

* docs(status): add types

* style(swagger): remove documented code

* docs(http/upload): update docs with types

* docs(http/tags): add types

* docs(api/custom_templates): add types

* docs(api/teammembership): add types

* docs(http/teams): add types

* docs(http/stacks): add types

* docs(edge): add types to edgestack

* docs(http/teammembership): remove double returns

* docs(api/user): add types

* docs(http): fixes to make file built

* chore(snippets): add scope to swagger snippet

* chore(deps): install swag

* chore(swagger): remove handler

* docs(api): add description

* docs(api): ignore docs folder

* docs(api): add contributing guidelines

* docs(api): cleanup handler

* chore(deps): require swaggo

* fix(auth): fix typo

* fix(docs): make http ids pascal case

* feat(edge): add ids to http handlers

* fix(docs): add ids

* fix(docs): show correct api version

* chore(deps): remove swaggo dependency

* chore(docs): add install script for swag
2021-02-23 16:21:39 +13:00
Anthony McMahon
90f5a6cd0d Update Custom.md 2021-02-23 15:25:00 +13:00
Anthony McMahon
3fc021826c Update Custom.md 2021-02-23 15:24:45 +13:00
knittl
25c010ec3e #4374 feat(images): Add link to Docker Hub on container creation page (#4413)
Add a button next to the image field when creating a new container, which
takes the user to the Docker Hub search page for this image. Version
identifiers are trimmed from the image name to ensure that matching images
will be found.
2021-02-23 01:45:19 +01:00
Chaim Lev-Ari
20f8d03366 feat(k8s/config): disable edit used config keys (#4754)
* feat(k8s/config): tag used data keys

* feat(k8s/config): disabled edit of used data keys
2021-02-23 12:53:33 +13:00
Maxime Bajeux
c84da11a91 feat(custom-templates): switching a template to standalone makes it disappear in swarm mode (#4829)
* feat(custom-templates): switching a template to standalone makes it disappear in swarm mode

* feat(custom-template): disable deploy button and add an error message

* fix(custom-template): invert variable

* fix(custom-templates): put the warning message below the button
2021-02-23 00:52:18 +01:00
Alice Groux
44b6aaedc8 feat(k8s/application): display all environment variables in edition (#4860) 2021-02-23 11:44:40 +13:00
Stéphane Busso
b9cad8a7ea Display error message if database is for Portainer BE (#4557) 2021-02-22 23:14:52 +01:00
Maxime Bajeux
cc9dd55b5c fix(application): Can't update application with persisted data, after the storage option is disabled on cluster (#4861)
* fix(application): Can't update application with persisted data, after the storage option is disabled on cluster

* refacto(application): Some code extraction requested for better maintenance
2021-02-23 08:05:43 +13:00
Anthony McMahon
93eaccc878 Update Custom.md 2021-02-22 13:54:30 +13:00
Anthony McMahon
0a65204b0f Update Custom.md 2021-02-22 13:25:30 +13:00
Anthony McMahon
c99b412e11 Update Bug_report.md 2021-02-22 13:24:30 +13:00
Alice Groux
3b4afe838c feat(app/endpoint-group): replace the tag dropdown by isteven-multi-select (#4714)
* feat-app/endpoint-group): replace the tag dropdown by isteven-multi-select

* feat(app/endpoint-group): fix the dropdown height

* feat(app/tag-selector): remove the slice on filtered tags and add some style to fix the dropdown height
2021-02-19 23:26:32 +01:00
Robert Rosca
3339ed9509 Update link to template definition docs (#4830) 2021-02-19 22:17:46 +01:00
Chaim Lev-Ari
4a1a46c8c1 fix(snapshot): update snapshot interval (#4789)
* fix(snapshot): update snapshot interval

* style(snapshot): add clarification about clearing signal
2021-02-19 14:19:01 +13:00
Alice Groux
387bbeceba feat(app): sort environment variables (#4815)
* feat(app): sort environment variables

* feat(k8s/application): improve the sorting for the env variables when creating/editing application

* feat(k8s/application): update the removal of the env var

* feat(docker/service): improve the sorting order for env var in service edition view
2021-02-18 14:46:26 +01:00
cong meng
86335a4357 fix(ingress): remove associated ingresses while removing ingress controller (#4722) (#4780)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-02-18 14:52:59 +13:00
Chaim Lev-Ari
590b6f69bf chore(dev): add debug config for vscode (#4756)
* chore(dev): add debug config for vscode

* chore(ide): move vscode configs to an example folder
2021-02-18 09:47:05 +13:00
Chaim Lev-Ari
45afe76bc7 fix(customtemplate): create from file (#4769)
* fix(customtemplate): receive File from api

* fix(customtemplate): return custom template

fix #4384
2021-02-17 16:56:44 +01:00
Chaim Lev-Ari
739dda1318 fix(endpoint): skip tls for kube endpoints (#4788) 2021-02-17 15:39:22 +13:00
Chaim Lev-Ari
9bef81eef6 fix(stack): show correct error message (#4853) 2021-02-16 22:37:27 +01:00
Stéphane Busso
aa25eac951 Bump portainer version to 2.1.1 2021-02-16 18:59:58 +13:00
Stéphane Busso
d5864d78fc Add rebase action (#4857) 2021-02-16 17:23:07 +13:00
Alice Groux
0ac8a45825 feat(app): add type=button on every button with ngf-select (#4783) 2021-02-16 00:43:35 +01:00
Alice Groux
48dbb308ec feat(docker/stack): update content of code editor when switching custom template (#4784) 2021-02-16 00:12:52 +01:00
Chaim Lev-Ari
5c1888bfc6 fix(endpoint): show correct windows agent deploy command (#4795)
* fix(endpoint): show correct windows agent deploy command

* format(endpoint): fix code format

* fix(endpoints): move deploy command to one place
2021-02-15 12:33:21 +13:00
jfadelhaye
bc459b55ae Merge pull request #4766 from portainer/fix/GH/3068-fix-auto-refresh-collapse
fix(docker/services): save the settings of the table for auto refresh
2021-02-14 22:49:52 +01:00
cong meng
f2ec7605c2 fix(edge): invalid command displayed for Edge agent deployment on Docker standalone (#4732) (#4734)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-02-12 16:13:27 +13:00
Alice Groux
81b4672076 feat(docker/services): update the information message about default location of secrets (#4816) 2021-02-12 14:27:02 +13:00
Chaim Lev-Ari
0cfa912d77 feat(kube/app): show image pull policy (#4785)
* feat(kube/app): show image pull policy

* fix(kube/app): remove image pull policy

* feat(kube/apps): show container image pull policy
2021-02-12 13:59:20 +13:00
Neil Cresswell
fc0de913c3 Update README.md 2021-02-12 10:55:25 +13:00
Alice Groux
f7e6ba544e fix(docker/service): enable apply change button when user make change on mounts section (#4645) 2021-02-11 16:38:25 +13:00
cong meng
24b1894a84 feat(authtication): #3580 Rename all usernames to lowercase (#4603)
* feat(authtication): Rename all usernames to lowercase

* feat(authentication): Remove database migration (#3580)

* feat(authentication): Make UserByUsername compare usernames case-insensitively (#3580)

* feat(authentication): validate new username case-insensitively (#3580)

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-02-10 15:29:28 +13:00
Chaim Lev-Ari
46dec01fe3 feat(endpoint): relocate docker security settings (#4657)
* feat(endpoint): migrate security settings to endpoint

* feat(endpoint): check for specific endpoint settings

* feat(endpoint): check security settings

* feat(docker): add config page

* feat(endpoint): save settings page

* feat(endpoints): disable features when not agent

* feat(sidebar): hide docker settings for regular user

* fix(docker): small fixes in configs

* fix(volumes): hide browse button for non admins

* refactor(docker): introduce switch component

* refactor(components/switch): seprate label from switch

* feat(app/components): align switch label

* refactor(app/components): move switch css

* fix(docker/settings): add ngijnect

* feat(endpoints): set default security values

* style(portainer): sort types

* fix(endpoint): rename security heading

* fix(endpoints): update endpoints settings
2021-02-09 21:09:06 +13:00
LP B
e401724d43 fix(k8s/resource-pool): unusable RP access management (#4810) 2021-02-03 18:38:56 +13:00
yi-portainer
d2d7f6fdb9 Squashed commit of the following:
commit e4605d990d
Author: yi-portainer <yi.chen@portainer.io>
Date:   Tue Feb 2 17:42:57 2021 +1300

    * update portainer version

commit 768697157c
Author: LP B <xAt0mZ@users.noreply.github.com>
Date:   Tue Feb 2 05:00:19 2021 +0100

    sec(app): remove unused and vulnerable dependencies (#4801)

commit d3086da139
Author: cong meng <mcpacino@gmail.com>
Date:   Tue Feb 2 15:10:06 2021 +1300

    fix(k8s) trigger port validation while changing protocol (ce#394) (#4804)

    Co-authored-by: Simon Meng <simon.meng@portainer.io>

commit 95894e8047
Author: cong meng <mcpacino@gmail.com>
Date:   Tue Feb 2 15:03:11 2021 +1300

    fix(k8s) parse empty configuration as empty string yaml instead of {} (ce#395) (#4805)

    Co-authored-by: Simon Meng <simon.meng@portainer.io>

commit 81de55fedd
Author: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
Date:   Tue Feb 2 11:12:40 2021 +1300

    * fix missing kubectl download (#4802)

commit 84827b8782
Author: Steven Kang <skan070@gmail.com>
Date:   Sun Jan 31 17:32:30 2021 +1300

    feat(build): introducing buildx for Windows (#4792)

    * feat(build): introducing buildx for Windows

    * feat(build): re-ordered USER

    * feat(build): Fixed Typo

    * feat(build): fixed typo
2021-02-02 22:37:37 +13:00
LP B
b747f5f81e sec(app): remove unused and vulnerable dependencies (#4801) 2021-02-02 17:00:19 +13:00
Yi Chen
afbd353808 Merge windows buildx to develop (#4796)
* feat(build): introducing buildx for Windows

* feat(build): re-ordered USER

* feat(build): Fixed Typo

* feat(build): fixed typo

Co-authored-by: ssbkang <skan070@gmail.com>
2021-01-31 17:46:45 +13:00
alice groux
51d584bb50 fix(docker/services): get datas from local storage when auto refresh is enable 2021-01-27 16:10:49 +01:00
alice groux
36fbaa9026 fix(docker/services): save the settings of the table for auto refresh 2021-01-26 16:04:20 +01:00
Dmitry Salakhov
a71e71f481 feat(compose): add docker-compose wrapper (#4713)
* feat(compose): add docker-compose wrapper

ce-187

* fix(compose): pick compose implementation upon startup

* Add static compose build for linux

* Fix wget

* Fix platofrm specific docker-compose download

* Keep amd64 architecture as download parameter

* Add tmp folder for docker-compose

* fix: line endings

* add proxy server

* logs

* Proxy

* Add lite transport for compose

* Fix local deployment

* refactor: pass proxyManager by ref

* fix: string conversion

* refactor: compose wrapper remove unused code

* fix: tests

* Add edge

* Fix merge issue

* refactor: remove unused code

* Move server to proxy implementation

* Cleanup wrapper and manager

* feat: pass max supported compose syntax version with each endpoint

* fix: pick compose syntax version

* fix: store wrapper version in portainer

* Get and show composeSyntaxMaxVersion at stack creation screen

* Get and show composeSyntaxMaxVersion at stack editor screen

* refactor: proxy server

* Fix used tmp

* Bump docker-compose to 1.28.0

* remove message for docker compose limitation

* fix: markup typo

* Rollback docker compose to 1.27.4

* * attempt to fix the windows build issue

* * attempt to debug grunt issue

* * use console log in grunt file

* fix: try to fix windows build by removing indirect deps from go.mod

* Remove tmp folder

* Remove builder stage

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose

* feat(build/windows): add git for Docker Compose - fixed verbose output

* refactor: renames

* fix(stack): get endpoint by EndpointProvider

* fix(stack): use margin to add space between line instead of using br tag

Co-authored-by: Stéphane Busso <stephane.busso@gmail.com>
Co-authored-by: Simon Meng <simon.meng@portainer.io>
Co-authored-by: yi-portainer <yi.chen@portainer.io>
Co-authored-by: Steven Kang <skan070@gmail.com>
2021-01-26 08:16:53 +13:00
LP B
83f4c5ec0b fix(k8s/app): remove advanced deployment panel from app details view (#4730) 2021-01-25 14:43:54 +13:00
Maxime Bajeux
41308d570d feat(configurations): Review UI/UX configurations (#4691)
* feat(configurations): Review UI/UX configurations

* feat(configurations): fix binary secret value

* fix(frontend): populate data between simple and advanced modes (#4503)

* fix(configuration): parseYaml before create configuration

* fix(configurations): change c to C in ConfigurationOwner

* fix(application): change configuration index to configuration key in the view

* fix(configuration): resolve problem in application create with configuration not overriden.

* fix(configuration): fix bad import in helper

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-01-25 14:14:35 +13:00
Chaim Lev-Ari
46ff8a01bc fix(kubernetes/pods): save note (#4675)
* feat(kubernetes/pods): introduce patch api

* feat(k8s/pods): pod converter

* feat(kubernetes/pods): introduce patch api

* feat(k8s/pod): add annotations only if needed

* fix(k8s/pod): replace class with factory function
2021-01-22 14:08:08 +13:00
yi-portainer
2b257d2785 Squashed commit of the following 2.0.1 release fixes:
commit f90d6b55d6
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Wed Jan 13 00:56:19 2021 +0200

    feat(service): clear source volume when change type (#4627)

    * feat(service): clear source volume when change type

    * feat(service): init volume source to the correct value

commit 1b82b450d7
Author: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
Date:   Thu Jan 7 14:47:32 2021 +1300

    * bump the APIVersion to 2.0.1 (#4688)

commit b78d804881
Author: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
Date:   Wed Dec 30 23:03:43 2020 +1300

    Revert "chore(build): bump Kompose version (#4475)" (#4676)

    This reverts commit 380f106571.

    Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>

commit 51b72c12f9
Author: Anthony Lapenna <anthony.lapenna@portainer.io>
Date:   Wed Dec 23 14:45:32 2020 +1300

    fix(docker/stack-details): do not display editor tab for external stack (#4650)

commit 58c04bdbe3
Author: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
Date:   Tue Dec 22 13:47:11 2020 +1300

    + silently continue when downloading artifacts in windows (#4637)

commit a6320d5222
Author: cong meng <mcpacino@gmail.com>
Date:   Tue Dec 22 13:38:54 2020 +1300

    fix(frontend) unable to retrieve config map error when trying to manage newly created resource pool (ce#180) (#4618)

    * fix(frontend) unable to retrieve config map error when trying to manage newly created resource pool (ce#180)

    * fix(frontend) rephrase comments (#4629)

    Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>

    Co-authored-by: Simon Meng <simon.meng@portainer.io>
    Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
2021-01-21 00:04:15 +13:00
cong meng
da41dbb79a fix(stack): stacks created via API are incorrectly marked as private with no owner (#3721) (#4725)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-01-20 15:19:35 +13:00
Maxime Bajeux
68d42617f2 feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster (#4525)
* feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster

* fix(applications): if there is at least one node the application can schedule on, then do not show the warning
2021-01-20 13:02:18 +13:00
Anthony McMahon
8323e22309 Update issue templates
Adding auto labelling to Bug Report (kind/bug, bug/unconfirmed) and Question (kind/question)
2021-01-20 12:06:25 +13:00
Chaim Lev-Ari
20d4341170 fix(state): check validity of state (#4609) 2021-01-19 11:10:08 +13:00
Chaim Lev-Ari
832cafc933 fix(registries): update password only when not empty (#4669) 2021-01-18 13:59:57 +13:00
cong meng
f3c537ac2c chore(build): bump Kompose version (#4473) (#4724)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-01-18 13:02:16 +13:00
Anthony McMahon
958baf6283 Update README.md 2021-01-18 09:30:17 +13:00
Chaim Lev-Ari
08e392378e chore(app): fail on angular components missing nginject (#4224) 2021-01-17 20:28:09 +13:00
Alice Groux
a2d9734b8b fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable (#4511)
* fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable

* fix(k8s/datatables): reduce size of expand/collapse column
2021-01-17 16:50:22 +13:00
DarkAEther
15aed9fc6f feat(area/kubernetes): show shared access policy in volume details (#4707) 2021-01-17 13:53:32 +13:00
Alice Groux
121d33538d fix(k8s/application): validate load balancer ports inputs (#4426)
* fix(k8s/application): validate load balancer ports inputs

* fix(k8s/application): allow user to only change the protocol on the first port mapping
2021-01-15 14:51:36 +13:00
Olli Janatuinen
7a03351df8 dep(api): Support Docker Stack 3.8 (#4333)
- Linux: Update Docker binary to version 19.03.13
- Windows: Update Docker binary to version 19.03.12
2021-01-15 10:05:33 +13:00
Alice Groux
0c2987893d feat(app/images): in advanced mode, remove tooltip and add an information message (#4528) 2021-01-14 15:04:44 +13:00
Alice Groux
d1eddaa188 feat(app/network): rename restrict external acces to the network label and add a tooltip (#4514) 2021-01-14 12:24:56 +13:00
Anthony Lapenna
d336ada3c2 feat(k8s/application): review application creation warning style (#4613) 2021-01-13 16:13:27 +13:00
Avadhut Tanugade
839198fbff #4424 style(stack-details): shift button position in stack details (#4439) 2021-01-13 12:19:18 +13:00
Chaim Lev-Ari
486ffa5bbd chore(webpack): add source maps (#4471)
* chore(webpack): add source maps

* feat(build): fetch source maps for 3rd party libs
2021-01-13 10:40:09 +13:00
Maxime Bajeux
4cd468ce21 Can't create kubernetes resources with a username longer than 63 characters (#4672)
* fix(kubernetes): truncate username when we create resource

* fix(k8s): remove forbidden characters in owner label
2021-01-12 14:35:59 +13:00
Chaim Lev-Ari
cbd7fdc62e feat(docker/stacks): introduce date info for stacks (#4660)
* feat(docker/stacks): add creation and update dates

* feat(docker/stacks): put ownership column as the last column

* feat(docker/stacks): fix the no stacks message

* refactor(docker/stacks): make external stacks helpers more readable

* feat(docker/stacks): add updated and created by

* feat(docker/stacks): toggle updated column

* refactor(datatable): create column visibility component

Co-authored-by: alice groux <alice.grx@gmail.com>
2021-01-12 12:38:49 +13:00
DarkAEther
b9fe8009dd feat(image-details): Show labels in images datatable (#4287)
* feat(images): show labels in images datatable

* move labels to image details view
2021-01-11 15:35:19 +13:00
Stéphane Busso
6a504e7134 fix(settings): Use default setting if UserSessionTimeout not set (#4521)
* fix(settings): Use default settings if UserSessionTimeout not set

* Update UserSessionTimeout settings in database if set to empty string
2021-01-11 14:44:15 +13:00
Alice Groux
51ba0876a5 feat(k8s/configuration): rename add ingress controller button and changed information text (#4540) 2021-01-11 12:51:46 +13:00
Alice Groux
769e6a4c6c feat(k8s/configuration): add extra information panel when creating a sensitive configuration (#4541) 2021-01-11 11:30:31 +13:00
cong meng
105d1ae519 feat(frontend): de-emphasize internal login when OAuth is enabled (#3065) (#4565)
* feat(frontend): de-emphasize internal login when OAuth is enabled (#3065)

* feat(frontend): change the "Use internal authentication" style to be primary (#3065)

* feat(frontend): resize the login with "provider" button to use a 120% font size (#3065)

* feat(frontend): remove unused css for h1 tag (#3065)

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-01-08 15:30:43 +13:00
cong meng
cf508065ec fix(frontend): application edit page initializes the overridenKeyType of new added configuration key to NONE so that the user can select how to load it (#4548) (#4593)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-01-08 12:51:27 +13:00
itsconquest
eab828279e chore(project): exclude refactors (#4689) 2021-01-08 12:46:57 +13:00
cong meng
d5763a970b fix(frontend): Resource pool 'created' attribute is showing the time you view it at & not actual creation time (#4568) (#4599)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-01-08 12:45:06 +13:00
cong meng
c9f68a4d8f fix(kubernetes): removes kube client cache when edge proxy is removed (#4487) (#4574)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-01-08 11:55:42 +13:00
Alice Groux
7848bcf2f4 feat(k8s/resources-list-view): add advanced deployment panel to resources list view (#4516)
* feat(k8s/resources-list-view): add advanced deployment panel to applications view, configurations view and volumes view

* feat(k8s/resources-list-view): move advanced deployment into a template and use it everywhere
2021-01-08 10:29:17 +13:00
Stéphane Busso
b924347c5b Bump portainer version 2021-01-07 14:03:46 +13:00
Yi Chen
9fbda9fb99 Merge in release fixes to develop (#4687)
* fix(frontend) unable to retrieve config map error when trying to manage newly created resource pool (ce#180) (#4618)

* fix(frontend) unable to retrieve config map error when trying to manage newly created resource pool (ce#180)

* fix(frontend) rephrase comments (#4629)

Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>

Co-authored-by: Simon Meng <simon.meng@portainer.io>
Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>

* + silently continue when downloading artifacts in windows (#4637)

* fix(docker/stack-details): do not display editor tab for external stack (#4650)

* Revert "chore(build): bump Kompose version (#4475)" (#4676)

This reverts commit 380f106571.

Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>

Co-authored-by: cong meng <mcpacino@gmail.com>
Co-authored-by: Simon Meng <simon.meng@portainer.io>
Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
2021-01-07 13:38:01 +13:00
Anthony Lapenna
82f8062784 chore(github): update issue template 2021-01-06 11:31:05 +13:00
knittl
49982eb98a #4411 docs: make build steps for local development more easily discoverable (#4412) 2021-01-06 08:49:50 +13:00
Stéphane Busso
4be3ac470f Merge pull request #4658 from portainer/revert-4475-chore-ce-86-bump-kompose-version
Revert "chore(build): bump Kompose version"
2020-12-24 23:45:53 +13:00
Stéphane Busso
a50ab51bef Revert "chore(build): bump Kompose version (#4475)"
This reverts commit 380f106571.
2020-12-24 12:12:28 +13:00
Yi Chen
7975ef796d Revert "feat(docker/stacks): add creation and update dates (#4418)" (#4606)
This reverts commit bd98b8956a.
2020-12-17 13:33:45 +13:00
xAt0mZ
f8b226a1ef fix(k8s/application): ability to remove naked pods (#4598) 2020-12-17 13:05:31 +13:00
cong meng
342a0d6d22 fix(k8s/application): transform username to be dns compliant (#4595) (#4601)
* fix(k8s/application): transform username to be dns compliant (#4595)

* fix(k8s/application): transform username to be dns compliant for configurations and resource pools(#4595)

* fix(k8s/application): update regex to replace all special characters (#4595)

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2020-12-17 12:20:18 +13:00
Alice Groux
58bf76a58f feat(app/volumes): add confirmation modal before deleting volumes in volumes view and volume view (#4597) 2020-12-16 19:57:31 +13:00
Alice Groux
bd98b8956a feat(docker/stacks): add creation and update dates (#4418)
* feat(docker/stacks): add creation and update dates

* feat(docker/stacks): put ownership column as the last column

* feat(docker/stacks): fix the no stacks message
2020-12-16 16:11:59 +13:00
Alice Groux
4bc958f865 feat(app/logs): add download button on container logs and service logs views (#4529) 2020-12-16 12:30:16 +13:00
aravind-korada
b67c0e870c #4470 fix(stack): fix a display issue with the stack editor tab. (#4543) 2020-12-15 11:42:54 +13:00
Chaim Lev-Ari
067257df2b fix(services): prevent adding volume without source and target (#4538)
* feat(services): check that target mounts are non empty

* feat(services): prevent creating service when no source

* refactor(services): remove ng-form

* fix(services): check that every volume is valid
2020-12-14 16:27:05 +13:00
Alice Groux
5f2f7a87ab feat(app): add a preview for business edition features (#4578)
* feat(app): add a preview for business edition features

* feat(app): open links in new tab + show storage quota section + grey out unavailable providers
2020-12-14 14:31:59 +13:00
cong meng
f656ad7124 fix(frontend): fix incorrect datatable selection count on text filter change (#4474)
Co-authored-by: Simon Meng <simon@mcpacino.tk>
2020-12-14 12:25:00 +13:00
Alice Groux
f681e2d532 feat(endpoint): start Portainer without endpoint (#4460)
* feat(endpoint): start Portainer without endpoint

* feat(endpoint): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-12-14 10:20:35 +13:00
Anthony Lapenna
fdb9bf09de docs(README): update README contribution link (#4587) 2020-12-14 09:18:41 +13:00
Alice Groux
92ad3e788d feat(k8s/configuration): rename create entry file button (#4515) 2020-12-13 21:42:54 +13:00
Alice Groux
bc2f5a3260 feat(k8s/advanced-deployment): update extra information message when kubernetes type is selected (#4542) 2020-12-13 17:54:38 +13:00
Alice Groux
487123491e fix(k8s/application): improve ux for instance count input in creation/edition application (#4498) 2020-12-13 17:22:46 +13:00
cong meng
380f106571 chore(build): bump Kompose version (#4475)
Co-authored-by: Simon Meng <simon@mcpacino.tk>
2020-12-13 16:22:18 +13:00
Alice Groux
341378e783 feat(app/endpoint): add deployment instructions for windows (#4442)
* feat(app/endpoint): add deployment instructions for windows

* feat(app/endpoint): hide instructions for kubernetes via load balancer and kubernetes via node port when windows is selected

* feat(endpoint): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-12-13 15:50:42 +13:00
Alice Groux
b360936454 feat(app/endpoint): edge deployment for windows (#4443)
* feat(app/endpoint): edge deployment for windows

* feat(app/endpoint): hide instructions for kubernetes when windows is selected

* feat(app/endpoint): fix typo

* feat(endpoint): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-12-11 17:40:56 +13:00
Mathieu Cantin
8204d32538 fix(configs): fix error with binary file (#3937) 2020-12-11 09:57:28 +13:00
Maxime Bajeux
60c5ab3eec feat(kube): Add a confirmation modal before deleting one or more application or configuration (#4522) 2020-12-10 20:46:58 +13:00
Anthony Lapenna
20cf948e53 fix(docker/resourcecontrol): fix an issue with resource deletion (#4524) 2020-12-10 20:31:31 +13:00
Alice Groux
45fcb1ad26 fix(k8s/configuration): save the owner when updating the configuration (#4517) 2020-12-10 19:49:25 +13:00
Alice Groux
7398d54ed0 fix(k8s/application): refreshing yaml panel doesn't change the selected panel (#4500) 2020-12-10 19:44:24 +13:00
Alice Groux
faded67deb fix(k8s/node): sort labels (#4417) 2020-12-10 15:57:35 +13:00
Alice Groux
eadd8b36d6 fix(applications/ports-mapping): load balancer link expand only if the item length > 1 (#4495) 2020-12-10 15:27:18 +13:00
cong meng
1ad4623b08 fix(frontend): override configuration keys disappear (#4547) (#4560)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2020-12-10 15:13:02 +13:00
Alice Groux
890bbf4058 fix(k8s/sidebar): accessing cluster setup not expand endpoint sidebar (#4496) 2020-12-10 15:11:45 +13:00
cong meng
865c8d899b fix(frontend): revalidate configuration name when change resource pool (#4553) (#4562)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2020-12-10 14:21:43 +13:00
cong meng
aa5277de2e fix(frontend): cannnot access configuration details view containing binary data (#4503) (#4561)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2020-12-10 13:58:10 +13:00
Anthony Lapenna
9136ba30eb feat(build-system): update pull-dog configuration (#4532)
* feat(build-system): update pull-dog configuration

* feat(build): update pull-dog configuration
2020-12-02 08:27:30 +13:00
Stéphane Busso
3d9c10adf1 Merge pull request #4415 from portainer/feat/GH/4011-pods-as-applications
feat(k8s/applications): exposed naked pods as applications
2020-11-23 14:57:04 +13:00
Alice Groux
0d20988bef fix(rest): remove timeouts for all REST services (#4385) 2020-11-05 20:49:37 +13:00
Anthony Lapenna
1545a42f08 chore(github): update bug report template
Update documentation URLs
2020-11-03 06:16:18 +13:00
Ricardo Matsui
3f9ff8460f fix(log-viewer): fix copy logs and log status 2020-10-28 23:43:53 -07:00
Mulia Nasution
a12f2ee893 Fix typo, change Matamo to Matomo (#4409) 2020-10-28 11:33:23 +13:00
Ricardo Matsui
ae3809cefd fix(log-viewer): fix formatting last line without newline 2020-10-26 16:36:12 -07:00
xAt0mZ
174e28b850 feat(k8s/application): app details for pods 2020-10-26 19:48:38 +01:00
xAt0mZ
3da9751c82 feat(k8s/applications): add pod as new application type for apps list 2020-10-26 19:46:44 +01:00
Ricardo Matsui
8e246c203c feat(log-viewer): add ansi color support for logs 2020-10-24 01:01:09 -07:00
Alice Groux
ccea7cca3d fix(endpoint): remove TLS settings for kubernetes (#4388) 2020-10-21 09:22:42 +13:00
Tim van den Eijnden
43891703c2 fix(endpoints): broken datatable sorting (#4373) 2020-10-20 12:07:24 +13:00
Tim van den Eijnden
74429d6d46 feat(frontend): show endpoint.name in page title (#4363)
* feat(frontend): show endpoint.name in page title

* feat(frontend): show endpoint.name in page title - use rootscope for defaultTitle
2020-10-16 22:28:46 +13:00
S.Hale
bb5c2c2875 fix(readme): fix grammar errors in readme (#4376) 2020-10-16 22:12:06 +13:00
itsconquest
3e82d01894 chore(project): update stalebot message (#4364) 2020-10-12 17:28:41 +13:00
Ranjan Purbey
9e80037e72 style(containers): fix word-break on container details table (#4359)
Co-authored-by: Rajesh Swarna <rajeshswarna123@gmail.com>
Co-authored-by: naveenrayudu <naveenkumar.rayudu@gmail.com>
Co-authored-by: Ranjan Purbey <ranjan.purbey@gmail.com>

Co-authored-by: Rajesh Swarna <rajeshswarna123@gmail.com>
Co-authored-by: naveenrayudu <naveenkumar.rayudu@gmail.com>
2020-10-05 11:00:13 +13:00
panchbhai1969
da29c2b6a5 #3741 fix(datatables): fixes datatable selection count on text filter (#4358) 2020-10-05 10:58:53 +13:00
Neil Cresswell
0ed4d443ee Update README.md 2020-09-30 15:40:20 +13:00
itsconquest
a4fa44f831 chore(testing): cleanup e2e for CE (#4349) 2020-09-29 11:01:49 +13:00
itsconquest
e479e41aee feat(ci): add missing powershell scripts & fix related grunt code (#4345)
* feat(ci): add missing powershell scripts & fix related grunt code

* feat(ci): download binaries direct to dist directory

* feat(ci): correctly pass in binary versions

* feat(ci): fix powershell errors

* feat(ci): fix cmdlet syntax

* feat(ci): fix typo

* feat(ci): fix additonal typo
2020-09-24 19:19:41 +12:00
itsconquest
d4c4c4e895 feat(project): refactor e2e testing (#4341)
* feat(project): refactor e2e testing

* feat(project): remove example text

* feat(project): add missing newlines

Co-authored-by: owner <owner@pop-os.localdomain>
2020-09-23 12:31:19 +12:00
Anthony Lapenna
466bd24648 feat(test/e2e): update image in cypress compose file 2020-09-01 09:28:45 +12:00
itsconquest
2fc60f14e1 docs(README): add privacy info (#4289) 2020-08-31 22:17:03 +12:00
Anthony Lapenna
9300603777 fix(k8s/applications): fix an issue with daemonset in 0/0 state (#4288) 2020-08-31 17:21:25 +12:00
Anthony Lapenna
8dac2df7bf fix(k8s/volumes): fix an issue with the system volume filter not working (#4284) 2020-08-31 17:21:15 +12:00
Anthony Lapenna
90fd5af4b9 fix(core/home): fix a display issue with Edge endpoints 2020-08-28 12:05:22 +12:00
Anthony Lapenna
3ec05accbc Merge tag '2.0.0' into develop
Release 2.0.0
2020-08-27 17:48:21 +12:00
Anthony Lapenna
1bc0c1baa9 Merge branch 'release/2.0.0' 2020-08-27 17:48:15 +12:00
Anthony Lapenna
ce8e245759 chore(version): bump version number 2020-08-27 17:48:02 +12:00
Anthony Lapenna
b91895d618 feat(core/endpoint): minor UI update for Kubernetes Edge endpoints 2020-08-27 17:28:41 +12:00
Anthony Lapenna
0019b22be5 fix(core/home): fix an issue when connecting to an Edge kubernetes endpoint (#4274) 2020-08-27 00:26:21 +12:00
Chaim Lev-Ari
eb0278d230 feat(core/cli): change analytics flag message (#4273)
* feat(cli): remove no-analytics flag default value

* feat(cli): hide no-analytics deprecation message when it's false
2020-08-26 23:58:19 +12:00
Anthony Lapenna
787cf41ee3 feat(k8s/configure): rename metrics server 2020-08-25 22:53:12 +12:00
Chaim Lev-Ari
0ebf0ab199 fix(auth): prevent double transition to logout (#4266)
* fix(auth): prevent double transition to logout

* fix(app): revert

* feat(state-manager): reinitalize on login
2020-08-25 20:08:47 +12:00
Chaim Lev-Ari
6fa450a981 feat(aci): introduce basic form validation (#4268)
* feat(aci): introduce basic form validation

* feat(aci): check every port bindings

* fix(aci): remove name and image warnings
2020-08-25 19:45:06 +12:00
Chaim Lev-Ari
b4f97efb85 fix(rbac): clean leftovers (#4265) 2020-08-25 11:04:51 +12:00
Chaim Lev-Ari
45cada05d5 feat(custom-templates): validate unique template name (#4264)
* feat(custom-template): check for name uniqueness

* feat(custom-templates): check unique name on edit
2020-08-24 14:54:02 +12:00
Chaim Lev-Ari
d5d7b17dc4 feat(stacks): disable creation when editor or template is empty (#4262) 2020-08-24 14:53:27 +12:00
Chaim Lev-Ari
859d26aef6 fix(templates): show error when failing to create stack (#4251) 2020-08-21 19:34:40 +12:00
Anthony Lapenna
fc248c31c7 fix(api/stacks): add authorization checks for start/stop operations (#4248)
* fix(api/stacks): add authorization checks for start/stop operations

* feat(api/stacks): re-order validation checks

* fix(api/stacks): add missing endpoint validation check
2020-08-21 15:16:38 +12:00
Anthony Lapenna
383e19077f feat(core/about): remove about view and add link to website (#4256) 2020-08-21 13:11:34 +12:00
Chaim Lev-Ari
a3b54e1981 fix(api/custom-templates): prevent name collisions on update (#4250) 2020-08-21 10:17:30 +12:00
xAt0mZ
403dbb1245 fix(docker/templates): save custom template filters (#4249) 2020-08-21 10:15:26 +12:00
Chaim Lev-Ari
c48d05449c fix(volumes): set right resource id for volume (#4247) 2020-08-21 00:48:53 +12:00
Chaim Lev-Ari
9fd38a0543 fix(volumes): enable volume browsing (#4233)
* fix(docker): browse an agent volume

* refactor(volumes): get resource id from endpoint
2020-08-21 00:29:46 +12:00
Anthony Lapenna
f8be9bb57a feat(k8s/resource-pool): set ingress hostname as mandatory and remove… (#4244)
* feat(k8s/resource-pool): set ingress hostname as mandatory and remove default backend

* refactor(k8s/resource-pool): use constants

* feat(k8s/configure): add experimental note about traefik
2020-08-20 21:24:12 +12:00
Chaim Lev-Ari
7329ea91ca fix(app): set defaults for select boxes (#4235)
* fix(container): select runtime by default

* fix(network): set default network config

* fix(container): set default network container placeholder

* fix(services): default service mount
2020-08-20 13:02:25 +12:00
xAt0mZ
d850e18ff0 feat(k8s/ingresses): add more granularity to ingress configuration (#4220)
* feat(k8s/configure): separate ingress class name and ingress class type

* feat(k8s/resource-pool): ability to add custom annotations to ingress classes on RP create/edit

* feat(k8s/ingresses): remove 'allow users to use ingress' switch

* feat(k8s/configure): minor UI update

* feat(k8s/resource-pool): minor UI update

* feat(k8s/application): update ingress route form validation

* refactor(k8s/resource-pool): remove console.log statement

* feat(k8s/resource-pool): update ingress annotation placeholders

* feat(k8s/configure): add pattern form validation on ingress class

* fix(k8s/resource-pool): automatically associate ingress class to ingress

* fix(k8s/resource-pool): fix invalid ingress when updating a resource pool

* fix(k8s/resource-pool): update ingress rewrite target annotation value

* feat(k8s/application): ingress form validation

* fix(k8s/application): squash ingress rules with empty host inside a single one

* feat(k8s/resource-pool): ingress host validation

* fix(k8s/resource-pool): rewrite rewrite option and only display it for ingress of type nginx

* feat(k8s/application): do not expose ingress applications over node port

* feat(k8s/application): add specific notice for ingress

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-20 12:51:14 +12:00
Chaim Lev-Ari
68851aada4 fix(containers): persist column settings (#4234)
* feat(containers): remove ip column

* fix(containers): persist column settings
2020-08-19 11:50:16 +12:00
Chaim Lev-Ari
aeb3bf535f fix(aci): create aci endpoint (#4231) 2020-08-19 00:40:02 +12:00
Chaim Lev-Ari
7b77a92a2d fix(endpoints): load endpoints and tags together (#4230) 2020-08-19 00:39:32 +12:00
Chaim Lev-Ari
35fa9d6981 fix(oauth): if username is empty, fail to login (#4232)
* fix(oauth): if username is empty, fail to login

* fix(oauth): return err when failing to find username

* fix(oauth): disable autofill
2020-08-19 00:38:58 +12:00
Chaim Lev-Ari
b3b706d88d fix(core/oauth): select no-team by default (#4227) 2020-08-18 20:59:52 +12:00
Chaim Lev-Ari
297eea5da6 fix(frontend): add missing @ngInject (#4217) 2020-08-17 12:33:57 +12:00
Chaim Lev-Ari
b6fc434291 fix(dashboard): show endpoint tags (#4216)
* fix(dashboard): show endpoint tags

* fix(dashboard): use ctrl
2020-08-17 12:30:02 +12:00
Anthony Lapenna
5c6147c9b9 feat(k8s/configure): review ingress UI/UX 2020-08-17 12:27:06 +12:00
Anthony Lapenna
8c3160d061 feat(core/endpoints): review endpoint creation instructions 2020-08-17 12:13:29 +12:00
Anthony Lapenna
1ef78c0fdf refactor(core/db): refactor instance ID init 2020-08-16 10:54:50 +12:00
Anthony Lapenna
9733d32551 feat(core/support): remove support related API route 2020-08-16 10:45:04 +12:00
Anthony Lapenna
bd0d1c25fa feat(core/support): remove support related views 2020-08-16 10:41:09 +12:00
Anthony Lapenna
b77e39c065 feat(k8s/application): minor UI update 2020-08-16 10:31:43 +12:00
xAt0mZ
8d6f6e306a feat(k8s/application): add placement constraints validation (#4214)
* feat(k8s/application): add constraints validation

* feat(k8s/application): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-16 10:11:56 +12:00
Anthony Lapenna
36bf9c24b9 feat(k8s/resource-pool): review ingress rollup panel UI 2020-08-15 13:39:43 +12:00
Anthony Lapenna
e10cf3e59b feat(core/registries): add information message about registry usage 2020-08-15 13:34:44 +12:00
Anthony Lapenna
46762f3e67 fix(k8s/configurations): fix an issue with configuration ownership 2020-08-15 12:42:04 +12:00
Anthony Lapenna
7ad06b3be5 refactor(k8s/configurations): add refactor note 2020-08-15 12:12:56 +12:00
Anthony Lapenna
877e2baf59 feat(k8s/application): update placement style 2020-08-15 12:09:43 +12:00
Anthony Lapenna
9f0ff5181b feat(k8s/application): update placement rule policy style 2020-08-15 12:05:50 +12:00
Anthony Lapenna
56cda7f260 feat(k8s/application): re-order pod/container columns in application containers table 2020-08-14 17:11:26 +12:00
Anthony Lapenna
449b7888d3 fix(k8s/backend): update stander user cluster roles permissions 2020-08-14 17:08:51 +12:00
Anthony Lapenna
83c3f9ed06 fix(k8s/application): fix an issue with the auto-scaler section 2020-08-14 16:56:56 +12:00
xAt0mZ
52bdcf2e2b feat(k8s/application): add/edit placement preferences/constraints (#4210)
* feat(k8s/application): create application with placement preferences/constraints

* feat(k8s/application): edit application placement preferences/constraints
2020-08-14 11:56:53 +12:00
Chaim Lev-Ari
32bac9ffcc fix(main): terminate server if err returned by instanceId check is not nil (#4209) 2020-08-14 11:41:39 +12:00
Maxime Bajeux
00389a7da9 feat(k8s/application): Support multi-container pods applications (#4208)
* feat(application): Support multi-container pods applications

* feat(application): Support multi-container pods applications

* fix(application): use only one pod in app details and fix logs and console links

* fix(application): show all containers in containers datatable

* fix(application): fix order by pod name

* feat(k8s/application): minor UI update

* feat(k8s/application): minor UI update

* feat(k8s/application): minor UI update

* feat(k8s/application): minor UI update

* feat(k8s/application): minor UI update

* fix(application): fix persisted folders in application details

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-14 11:27:10 +12:00
Anthony Lapenna
fe4a80c7bd feat(k8s/ingress): display unused routes 2020-08-13 18:19:16 +12:00
Anthony Lapenna
6615e354c4 feat(k8s/resource-pool): change default behavior for resource assignment (#4207) 2020-08-13 11:49:34 +12:00
Anthony Lapenna
69e9e566c5 feat(k8s/dashboard): remove the RC banner (#4205) 2020-08-13 11:41:14 +12:00
xAt0mZ
f91d3f1ca3 feat(k8s/applications): expose applications via ingress (#4136)
* feat(k8s/endpoint): expose ingress controllers on endpoints

* feat(k8s/applications): add ability to expose applications over ingress - missing RP and app edits

* feat(k8s/application): add validation for ingress routes

* feat(k8s/resource-pools): edit available ingress classes

* fix(k8s/ingress): var name refactor was partially applied

* feat(kubernetes): double validation on RP edit

* feat(k8s/application): app edit ingress update + formvalidation + UI rework

* feat(k8s/ingress): dictionary for default annotations on ingress creation

* fix(k8s/application): temporary fix + TODO dev notice

* feat(k8s/application): select default ingress of selected resource pool

* feat(k8s/ingress): revert ingressClassName removal

* feat(k8s/ingress): admins can now add an host to ingress in a resource pool

* feat(k8s/resource-pool): list applications using RP ingresses

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* fix(k8s/ingresses): remove host if undefined

* feat(k8s/resource-pool): remove the activate ingresses switch

* fix(k8s/resource-pool): edditing an ingress host was deleting all the routes of the ingress

* feat(k8s/application): prevent app deploy if no ports to publish and publishing type not internal

* feat(k8s/ingress): minor UI update

* fix(k8s/ingress): allow routes without prepending /

* feat(k8s/application): add form validation on ingress route

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-13 11:30:23 +12:00
Chaim Lev-Ari
201c3ac143 fix(auth): don't redirect if route is unauthenticated (#4203)
* fix(auth): don't redirect if route is unauthenticated

* refactor(auth): replace resolve with onEnter

* fix(auth): throw error on init
2020-08-12 20:29:08 +12:00
Anthony Lapenna
2c15dcd1f2 feat(k8s): use instance ID to create unique k8s resources (#4196) 2020-08-12 17:10:28 +12:00
Maxime Bajeux
1bf97426bf feat(k8s/node): Add the ability to apply taints and labels to nodes (#4176)
* feat(node): Add the ability to apply taints and labels to nodes

* feat(k8s/node): minor UI update

* feat(k8s/node): UI update and disable system labels

* feat(k8s/node): minor UI update

* fix(node): fix add first taint

* refacto(node): add KubernetesNodeHelper

* feat(node): add used label to labels

* feat(node): add node update modals

* fix(node): modal when used label changes

* feat(k8s/node): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-12 11:42:55 +12:00
Anthony Lapenna
1f614ee95a feat(core/settings): rename settings sections (#4199) 2020-08-11 22:08:44 +12:00
Chaim Lev-Ari
b4c2e5d235 fix(auth): reject main route if logged out (#4197) 2020-08-11 20:23:14 +12:00
Chaim Lev-Ari
9d18d47194 feat(extensions): remove rbac extension (#4157)
* feat(extensions): remove rbac extension client code

* feat(extensions): remove server rbac code

* remove extensions code

* fix(notifications): remove error

* feat(extensions): remove authorizations service

* feat(rbac): deprecate fields

* fix(portainer): revert change

* fix(bouncer): remove rbac authorization check

* feat(sidebar): remove roles link

* fix(portainer): remove portainer module
2020-08-11 17:41:37 +12:00
Chaim Lev-Ari
8629738e34 fix(auth): switch to log-in when user is not logged in (#4162)
* fix(auth): switch to log-in when user is not logged in

* fix(app): remove analytics injection
2020-08-11 14:46:41 +12:00
Anthony Lapenna
a3925c3371 feat(k8s/sidebar): relocate setup entry as a sub-entry (#4192) 2020-08-11 12:39:01 +12:00
Anthony Lapenna
6720c31aa9 fix(k8s/volume): only show resize modal for used volumes (#4194) 2020-08-11 12:38:41 +12:00
Anthony Lapenna
01d414b578 fix(k8s/node): only show API address field for nodes tagged as API (#4193) 2020-08-11 12:38:19 +12:00
Maxime Bajeux
6d069cc8d6 feat(k8s/volumes): Enhance the used by column for volumes (#4191)
* feat(volumes): Enhance the used by column for volumes

* feat(k8s/volumes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-11 12:22:13 +12:00
Chaim Lev-Ari
a1e3ed7f78 feat(k8s/application): add default values for auto scaler (#4190) 2020-08-11 10:57:20 +12:00
Maxime Bajeux
baaa96f34f feat(k8s/application): Update form validation for environment variables when deploying an application (#4177) 2020-08-11 10:56:54 +12:00
Chaim Lev-Ari
56524ca7d5 fix(nodes): add ngInject to node-select (#4187) 2020-08-11 09:52:29 +12:00
Chaim Lev-Ari
c439bc56ff fix(edge): fix edge views (#4184)
* fix(edge): add ngInject

* feat(edge): init edge job model
2020-08-11 09:51:21 +12:00
Chaim Lev-Ari
134f2f1532 feat(docker/routes): add /docker parent route (#4185) 2020-08-10 23:37:49 +12:00
Anthony Lapenna
b4aca3822d feat(k8s/sidebar): add a setup sidebar entry (#4186) 2020-08-10 22:31:55 +12:00
itsconquest
59cc02137d fix(ux): fix checkbox sizing (#4172)
* fix(UX): scale and align checkboxes

* fix(UX): scale to default browser zoom

* fix(UX): use different browser scale properties

* fix(UX): css rule with fixed height/width
2020-08-10 18:21:03 +12:00
itsconquest
8408484f8b feat(docker/node): change table to div and fix styling (#4173) 2020-08-10 10:59:00 +12:00
itsconquest
c5731e237e fix(docker/container): handle multiple ips with the same port (#4121)
* fix(containers): handle multiple ips with the same port

* fix(containers): fix parsing
2020-08-10 10:27:27 +12:00
Maxime Bajeux
cb1a1e7be5 feat(k8s/resource-pool): add a modal when reducing the quota of an in use RP (#4170)
* feat(resourcepool): Reducing the Quota assigned to a RP

* fix(k8s/resource-pool): fix an issue with hasResourceQuotaBeenReduce condition

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-08 10:46:11 +12:00
Maxime Bajeux
e7a33347c6 fix(k8s/storage): missing endpoint id in storage patch request (#4174) 2020-08-08 10:43:34 +12:00
itsconquest
26ee78e1e7 refactor(UX): fix improper grammar (#4161) 2020-08-07 16:50:56 +12:00
Maxime Bajeux
61f97469ab feat(application): Add the ability to use existing volumes when creating an application (#4044)
* feat(applications): update UI to use existing volumes

* feat(application): Add the ability to use existing volumes when creating an application

* feat(application): Existing persisted folders should default to associated volumes

* feat(application): add form validation to existing volume

* feat(application): remove the ability to use an existing volume with statefulset application

* feat(k8s/applications): minor UI update

* feat(k8s/application): minor UI update

* feat(volume): allow to increase volume size and few other things

* feat(volumes): add the ability to allow volume expansion

* fix(storage): fix the storage patch request

* fix(k8s/applications): remove conflict leftover

* feat(k8s/configure): minor UI update

* feat(k8s/volume): minor UI update

* fix(storage): change few things

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-07 16:40:24 +12:00
itsconquest
b9c2bf487b fix(container-creation): add default/override options (#4119)
* fix(container-creation): add default/override options

* fix(container-creation): allow override with empty string on creation

* fix(container-creation): add tooltip & update placeholder

* fix(container-creation): add warning on duplicate
2020-08-07 14:10:40 +12:00
itsconquest
1b88ca2285 fix(container-creation): handle extraHosts correctly (#4139)
* fix(container-creation): handle extraHosts correctly

* fix(container-creation): refactor for readability
2020-08-07 14:10:08 +12:00
Anthony Lapenna
747fdae269 fix(agent/console): fix an issue with the agent console on Docker environments (#4169) 2020-08-07 14:08:57 +12:00
Maxime Bajeux
b8f8c75380 feat(k8s/resource-pool): prevent admins from making changes to "system" namespaces (#4167) 2020-08-07 12:03:00 +12:00
Maxime Bajeux
d85708f6ea feat(docker/services): Add the ability to edit a service networks (#3957)
* feat(services): update services details view

* feat(services): Add the ability to edit a service networks

* feat(services): show ingress network

* refactor(services): use lodash

* feat(networks): disable sending when updating

* feat(networks): limit size of select

* feat(services): update networks only when network is new

* feat(services): prevent submitting of empty networks

* feat(services): show unique networks

* fix(service): use empty array default for networks

* feat(service): show only swarm networks

* feat(services): show placeholder for network

* feat(services): show spaced select box

* feat(services): show macvlan ip

* feat(service): fetch the network subnet

* feat(services): show empty ip when network is not connected

Co-authored-by: Chaim Lev-Ari <chiptus@gmail.com>
2020-08-07 11:11:47 +12:00
Maxime Bajeux
e4ca58a042 fix(application): hpa breaks application edit (#4166) 2020-08-07 10:55:36 +12:00
Chaim Lev-Ari
2158cc5157 feat(telemetry): replace GA with matomo (#4140)
* feat(core/telemetry): add posthog

* feat(core/telemetry): add posthog

* feat(core/telemetry): add matomo

* feat(core/telemetry): update matomo

* feat(core/telemetry): update matomo

* feat(core/telemetry): update matomo

* feat(telemetry): remove google analytics code

* refactor(telemetry): move matomo code to bundle

* refactor(telemetry): move matomo lib to assets

* refactor(telemetry): depreciate --no-analytics

* feat(settings): introduce a setting to enable telemetry

* fix(cli): fix typo

* feat(settings): allow toggle telemetry from settings

* fix(settings): handle case where AuthenticationMethod is missing

* feat(admin): set telemetry on admin init

* refactor(app); revert file

* refactor(state-manager): move optout to state manager

* feat(telemetry): set matomo url

* feat(core/settings): minor UI update

* feat(core/telemetry): update custom URL

* feat(core/telemetry): add placeholder for privacy policy

* feat(core/telemetry): add privacy policy link

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-07 10:46:25 +12:00
DarkAEther
7aaf9d0eb7 fix(registries): remove trailing slash and protocol in registry URLs (#4131)
* feat(registries) prevent trailing slash

* fix(registries) avoid trailing slash in update registry

* fix(registries) include trailing slash removal notice in tooltips

* fix(registries) remove protocol when updating existing registry

* fix(registries) uniform usage of string replace function for registry update
2020-08-06 09:58:44 +12:00
Chaim Lev-Ari
82064152ec feat(registries): remove registry extension (#4155)
* feat(registries): remove client extension code

* feat(registry): remove server registry code

* refactor(registry): remove extension related code

* feat(extensions): remove registry extension type
2020-08-05 22:23:19 +12:00
Chaim Lev-Ari
7e90bf11b7 fix(datatables): deselect row (#4122)
* fix(datatables): deselect row

* fix(datatables): enable batch select

* fix(registry): select registry items
2020-08-05 22:14:28 +12:00
Chaim Lev-Ari
ff250a202a feat(extensions): remove oauth extension (#4156)
* feat(oauth): remove oauth providers

* feat(extensions): remove references to oauth extension
2020-08-05 22:13:23 +12:00
Chaim Lev-Ari
00f4fe0039 feat(auth): integrate oauth extension (#4152)
* refactor(oauth): move oauth client code

* feat(oauth): move extension code into server code

* feat(oauth): enable oauth without extension

* refactor(oauth): make it easier to remove providers
2020-08-05 20:36:46 +12:00
Maxime Bajeux
148ccd1bc4 feat(node): Show which IP address / port the cluster API is listening on (#4134)
* feat(cluster): add kubernetes endpoint resource

* feat(cluster): add kubernetes endpoint service

* feat(node): Show which IP address / port the cluster API is listening on

* fix(cluster): support multi-master clusters

* fix(cluster): support multi-master clusters

* feat(k8s/cluster): minor UI update

* refactor(k8s/cluster): rename variable

* refactor(k8s/endpoints): refactor KubernetesEndpointsFactory

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-05 12:15:17 +12:00
Maxime Bajeux
6756b04b67 feat(k8s/application): add the ability to set the auto-scale policy of an application (#4118)
* feat(application): add horizontalpodautoscaler creation

* feat(application): Add the ability to set the auto-scale policy of an application

* feat(k8s/application): minor UI update

* fix(application): set api version and prevent to use hpa with global deployment type

* feat(settings): add a switch to enable features based on server metrics

* feat(k8s/applications): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-05 10:08:11 +12:00
Anthony Lapenna
909e1ef02c fix(k8s/user): remove username part from service account (#4147) 2020-08-04 16:01:15 +12:00
Chaim Lev-Ari
bd7d7dcef5 feat(agent): add auto agent platform detection (#4132)
* feat(endpoint): check endpoint type on creation

* feat(edge): check edge endpoint type

* feat(endpoint): send endpoint creation type

* feat(endpoint): pass tls config

* feat(endpoint): show connect errors

* fix(endpoint): set correct endpoint type

* feat(endpoint): support endpoint creation

* style(endpoint): remove todo comment

* feat(endpoint): set protocol for endpoint url

* feat(endpoint): change scheme of url

* fix(endpoint): toggle code block

* feat(edge): report missing agent platform header

* fix(api/endpoints): fix an issue with agent on kubernetes endpoint

* feat(core/endpoints): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-08-04 12:44:17 +12:00
itsconquest
490b7ad26f fix(container-creation): allow resetting to unlimited (#4138)
* fix(container-creation): allow resetting to unlimited

* fix(container-creation): refactor for readability
2020-08-04 11:14:59 +12:00
Chaim Lev-Ari
4d5836138b feat(stacks): add the ability to stop a stack (#4042)
* feat(stacks): add stack status

* feat(stacks): add empty start/stop handlers

* feat(stacks): show start/stop button

* feat(stacks): implement stack stop

* feat(stacks): implement start stack

* feat(stacks): filter by active/inactive stacks

* fix(stacks): update authorizations for stack start/stop

* feat(stacks): assign default status on create

* fix(bolt): fix import

* fix(stacks): show external stacks

* fix(stacks): reload on stop/start

* feat(stacks): confirm before stop
2020-08-04 10:18:53 +12:00
Chaim Lev-Ari
da143a7a22 fix(docker/images): ignore pull image rejection (#4128) 2020-07-31 06:24:34 +12:00
xAt0mZ
4431d748c2 feat(k8s/application): expose tolerations and affinities (#4063)
* feat(k8s/application): expose placement conditions

* feat(k8s/applications): minor UI update

* feat(k8s/application): update message for admin and non admin users

* feat(kubernetes/applications): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-30 10:25:59 +12:00
Chaim Lev-Ari
63bf654d8d fix(serverless/ACI): show container instance title (#4126) 2020-07-30 09:53:57 +12:00
Chaim Lev-Ari
93d8c179f1 feat(containers): enforce disable bind mounts (#4110)
* feat(containers): enforce disable bind mounts

* refactor(docker): move check for endpoint admin to a function

* feat(docker): check if service has bind mounts

* feat(services): allow bind mounts for endpoint admin

* feat(container): enable bind mounts for endpoint admin

* fix(services): fix typo
2020-07-29 21:10:46 +12:00
Chaim Lev-Ari
7539f09f98 feat(containers): disable edit container on security setting restricting regular users (#4111)
* 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-29 14:52:23 +12:00
Chaim Lev-Ari
1a3f77137a feat(settings): introduce setting to disable container caps for non-admins (#4109)
* feat(settings): introduce settings to allow/disable

* feat(settings): update the setting

* feat(docker): prevent user from using caps if disabled

* refactor(stacks): revert file

* style(api): remove portainer ns
2020-07-28 19:08:15 +12:00
Chaim Lev-Ari
fec85c77d6 fix(extensions): load extensions file (#4115) 2020-07-28 09:54:12 +12:00
Chaim Lev-Ari
1ff5708183 fix(datatables): select table items (#4116) 2020-07-28 09:53:21 +12:00
Chaim Lev-Ari
1edf981330 fix(container-creation): preselect network (#4117) 2020-07-28 09:52:54 +12:00
Chaim Lev-Ari
fa9eeaf3b1 feat(settings): introduce disable stack management setting (#4100)
* feat(stacks): add a setting to disable the creation of stacks for non-admin users

* 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

* feat(settings): hide stacks for non admin when settings is set

* 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

* fix(docker): hide stacks when not admin
2020-07-27 19:11:32 +12:00
Chaim Lev-Ari
07efd4bdda feat(settings): add setting to disable device mapping for regular users (#4099)
* feat(settings): add setting to disable device mapping for regular users

* 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

* style(server): sort imports
2020-07-27 09:31:14 +12:00
DarkAEther
2bc6b2dff7 feat(docker/container-creation): sort volumes in container creation view (#4078)
* #3635 fix(containers) sort volumes in container creation view

* fix(3635) sort volumes in container creation view
2020-07-27 09:28:33 +12:00
Anthony Lapenna
0cebe6588a chore(github/stalebot): update stalebot config 2020-07-27 09:24:41 +12:00
Anthony Lapenna
990f3cad88 chore(github/stalebot): update stalebot config 2020-07-27 09:19:33 +12:00
Anthony Lapenna
7e7a8e521b feat(app/package): remove angular-cookies dependency 2020-07-25 11:32:31 +12:00
Chaim Lev-Ari
43bbc14c58 feat(app/package): upgrade angularjs to 1.8 (#4073)
* chore(yarn): upgrade angularjs

* refactor(app): use $onInit instead of initComponent

* feat(app/package): remove angular-cookies dependency

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-25 11:23:44 +12:00
Chaim Lev-Ari
adf33385ce feat(containers): Prevent non-admin users from running containers using the host namespace pid (#4098)
* 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

* feat(database): trigger migration for AllowHostNamespace

* feat(containers): check container creation authorization

Co-authored-by: Maxime Bajeux <max.bajeux@gmail.com>
2020-07-25 11:14:46 +12:00
Anthony Lapenna
e78aaec558 feat(api/bolt): update DBVersion to 25 2020-07-25 11:10:46 +12:00
xAt0mZ
3953acf110 feat(k8s/volumes): introduce storage rollup panel (#4055)
* feat(k8s/applications): storages rollup panel

* feat(k8s/volumes): move storages table to volumes view

* feat(k8s/volumes): minor UI update

* feat(k8s/volumes): remember the selected tab

* feat(api/k8s): update user default policies

* feat(k8s/ui): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-24 14:45:02 +12:00
Chaim Lev-Ari
99db41f96e feat(agent): refactor volumeBrowserService to es6 (#4094) 2020-07-23 19:46:29 +12:00
Chaim Lev-Ari
822c4e117c refactor(agent): refactor hostBrowserService to es6 (#4092)
related to #4071
2020-07-23 19:46:02 +12:00
Chaim Lev-Ari
f761e65167 refactor(agent): refactor agentService to es6 (#4091) 2020-07-23 19:45:47 +12:00
Chaim Lev-Ari
1ef7347f19 refactor(agent): refactor host-broswer to es6 (#4088)
* refactor(host): replace host-browser with es6 class

* refactor(host): replace promises with async

* refactor(hosts): replace delete promise with async

* refactor(host): replace upload file with async

* refactor(host): replace template strings

* fix(host): replace host root

* feat(agent): rename main file
2020-07-23 19:45:23 +12:00
Chaim Lev-Ari
a473d738be refactor(agent): refactor volume browser to es6 (#4086)
* refactor(agent): replace root with index

* refactor(agent): use simple export

* refactor(agent): replace function with class

* refactor(agent): replace promise with async
2020-07-23 19:45:12 +12:00
Chaim Lev-Ari
7eb8d5449a refactor(agent): refactor rest factories to es6 (#4090)
* refactor(agent): replace v1 browse with es6 module

* refactor(agent): refactor agentv1 to es6

* refactor(agent): replace agent factory with es6

* refactor(agent): refactor browse response to es6

* refactor(agent): refactor browse to es6

* refactor(agent): import angular

* refactor(agent): refactor host to es6

* refactor(agent): refactor ping to es6
2020-07-23 19:45:01 +12:00
Chaim Lev-Ari
435f15ec6a refactor(agent): refactor file-uploader to es6 (#4087)
* refactor(host): convert fileUploader to es6

* refactor(agent): rename main file
2020-07-23 19:44:32 +12:00
Chaim Lev-Ari
5abd35d4c1 refactor(agent): refactor pingService to es6 (#4093)
related to #4071
2020-07-23 19:43:37 +12:00
Chaim Lev-Ari
b50497301d refactor(agent): refactor files-datatable to es6 (#4085)
* refactor(host): rename files datatable

* feat(agent): rename main file
2020-07-23 19:43:12 +12:00
Chaim Lev-Ari
4534ccb499 fix(templates): replace templates links (#4083) 2020-07-23 06:41:07 +12:00
Chaim Lev-Ari
6f6bc24efd feat(containers): Ensure users cannot create privileged containers via the API (#3969) (#4077)
* feat(containers): Ensure users cannot create privileged containers via the API

* feat(containers): add rbac check in stack creation

Co-authored-by: Maxime Bajeux <max.bajeux@gmail.com>
2020-07-23 06:38:45 +12:00
Chaim Lev-Ari
4346bf95a7 feat(settings): replace cookies with local storage (#4075)
* fix(datatables): persist state changes

* fix(datatables): persist order

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

* feat(config): use local storage instead of cookies
2020-07-23 06:36:22 +12:00
Chaim Lev-Ari
c9dd6e3851 refactor(agent): replace model with class (#4089) 2020-07-23 06:35:15 +12:00
Chaim Lev-Ari
3a33365133 refactor(agent): node selector (#4084)
* refactor(agent): rename files

* refactor(agent): replace controller with regular export

* refactor(agent): replace function with class

* refactor(agent): replace promise with async

* refactor(agent): rename main file
2020-07-23 06:30:42 +12:00
Chaim Lev-Ari
67069547b8 refactor(agent): replace root file with index (#4096) 2020-07-23 06:29:27 +12:00
Chaim Lev-Ari
6fc923b05b refactor(app): move @babel/polyfill to entry (#4031) 2020-07-22 20:02:39 +12:00
Chaim Lev-Ari
8e7aaa23d5 feat(server): support minimum tls v1.2 (#4076) 2020-07-22 12:16:00 +12:00
Anthony Lapenna
227fbeb1b7 feat(build/windows): update windows dockerfile (#4060)
* feat(build/windows): update windows dockerfile

* feat(build/windows): update windows dockerfile

* feat(build/windows): update windows dockerfile

* feat(build/windows): update windows dockerfile

* feat(build/windows): update windows dockerfile

* feat(build/windows): update Dockerfile
2020-07-21 18:04:55 +12:00
Chaim Lev-Ari
53cddeb283 feat(aci): provide container details page (#4037)
* feat(aci): show basic details

* feat(aci): style container details page

* fix(aci): fix container ip

* feat(aci): provide functions to get single aci resource

* feat(aci): show readable data

* feat(aci): style container instance
2020-07-21 09:08:20 +12:00
Chaim Lev-Ari
4b97cf738e fix(app): use deps injection in router correctly (#4049)
* fix(app): use deps injection in router correctly

* feat(app): guard against using wrong endpoint type

* feat(sidebar): supply endpoint id

* feat(templates): move custom templates to docker
2020-07-21 09:06:37 +12:00
Anthony Lapenna
66a3104805 chore(ci/pull-dog): update pulldog configuration 2020-07-21 08:27:53 +12:00
Chaim Lev-Ari
5a4a10859d feat(aci): remove private network deployment (#4056)
* fix(aci): show error failing container creation

* feat(aci): load network profile list

* feat(aci): allow selection of network profile

* feat(aci): remove public ip toggle

* feat(aci): auto deploy container with public ip

* fix(aci): revert changes
2020-07-20 12:42:30 +12:00
Maxime Bajeux
94676df329 feat(k8s/cluster): Show the cluster leader (#4027)
* feat(cluster): Show the cluster leader

* feat(cluster): Restrict leader label only to admin users

* feat(kubernetes): minor UI update

* feat(endpoint):  move all KubernetesEndpoint related code to a single endpoint sub-folder and change few things

* fix(k8s/cluster): fix conflict leftover

* feat(k8s/cluster): review component leader UX

* refactor(k8s/node): remove useless call to endpoints

* refactor(k8s/endpoint): relocate variable declaration

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-20 10:49:49 +12:00
Maxime Bajeux
f765c63c74 feat(cluster): Show the cluster health by showing the status of the underlying cluster components (#4022)
* feat(cluster): add tabs

* feat(cluster): add cluster status informations to cluster detail view

* feat(cluster): change data display

* feat(cluster): prevent regular users to see cluster health

* feat(kubernetes): reviewed ComponentStatus handling

* refactor(kubernetes): review apiToModel for KubernetesComponentStatus

* refactor(kubernetes): remove unused variable

* refactor(kubernetes): clean hasUnhealthyComponentStatus code

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-17 11:39:16 +12:00
Maxime Bajeux
833abb24cb feat(k8s/applications): Expose application workload type (#4029)
* feat(applications): Expose application workload type

* feat(application): support daemonSet workload type and add default value
2020-07-17 10:00:15 +12:00
Maxime Bajeux
c9e8021fe8 feat(k8s/logs): Add the ability to download application/stack logs (#4046)
* feat(logs): Add the ability to download application/stack logs

* feat(kubernetes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-17 09:59:27 +12:00
Anthony Lapenna
a452599829 Merge branch 'develop' of github.com:portainer/portainer into develop 2020-07-15 14:08:15 +12:00
Anthony Lapenna
b7e1abf89f feat(kubernetes): rename node value Manager to Master 2020-07-15 13:48:16 +12:00
Maxime Bajeux
f71abb5669 feat(volumes): Expose the provisioner associated to a volume (#4030)
* feat(volumes): Expose the provisioner associated to a volume

* feat(volumes): fix import and add default value to provisioner
2020-07-15 10:55:29 +12:00
Chaim Lev-Ari
3c34fbd8f2 refactor(router): show endpoint id in url (#3966)
* refactor(module): provide basic endpoint id url

* fix(stacks): fix route to include endpointId

* fix(stacks): fix stacks urls

* fix(sidebar): fix urls to docker routes

* refactor(app): set endpoint id on change view

* refactor(dashboard): revert to old version

* refactor(sidebar): revert file

* feat(app): wip load endpoint on route change

* feat(home): show error

* feat(app): load endpoint route

* feat(sidebar): show endpoint per provider

* refactor(app): revert

* refactor(app): clean endpoint startup

* feat(edge): check for edge k8s

* refactor(endpoints): move all modules under endpoint route

* refactor(stacks): move stacks route to docker

* refactor(templates): move templates route to docker

* refactor(app): check endpoint when entering docker module

* fix(app): load endpoint when entering endpoints modules

* feat(azure): check endpoint

* feat(kubernetes): check endpoint

* feat(home): show loading state when loading edge

* style(app): revert small changes

* refactor(sidebar): remove refernce to endpointId

* fix(stacks): fix stacks route

* style(docker): sort routes

* feat(app): change route to home if endpoint failed

* fix(services): guard against empty snapshots

* feat(app): show error when failed to load endpoint

* feat(app): reload home route when failing

* refactor(router): replace resolvers with onEnter
2020-07-15 08:46:38 +12:00
xAt0mZ
1b3e2c8f69 feat(kubernetes): add ingress details (#4013)
* feat(kubernetes): add ingress details

* fix(kubernetes): fix broken ingress generated links + ignore IP retrieval/display info on missing LB ingress ip

* refactor(kubernetes): each ingress rule in apps port mappings has now its own row

* feat(kubernetes): remove protocol column and concat it to container port

* feat(kubernetes): edit display of ingress rules in application details

* feat(kubernetes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-15 08:45:19 +12:00
Chaim Lev-Ari
b09b1b1691 feat(aci): show container ip (#4034) 2020-07-14 21:24:29 +12:00
Maxime Bajeux
8b79f2524d feat(kubernetes): Prevent deployment/edition of resources inside a system namespace (#4039)
* feat(kubernetes): Prevent deployment/edition of resources inside a system namespace

* feat(kubernetes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-07-14 17:39:13 +12:00
itsconquest
181a6f4553 fix(container-creation): always rebuild exposed ports (#4024) 2020-07-09 17:08:52 +12:00
Anthony Lapenna
cd475a5338 feat(docker): expose port 8000 in Dockerfile (#4016) 2020-07-08 14:13:35 +12:00
Maxime Bajeux
c778ef6404 feat(networks): Support multiple excluded IPs for MACVLAN networks (#3962)
* feat(networks): Support multiple excluded IPs for MACVLAN networks

* feat(networks): add a generated name

* feat(networks): prevent create macvlan network where exclude ip is the same as gateway

* feat(networks): remove auxaddresses validation on submit

* feat(networks): check exclude ip validation on change

* feat(networks): check form validation on change

* feat(networks): clean checkAuxiliaryAddress function
2020-07-08 13:35:52 +12:00
Anthony Lapenna
08095913a6 fix(api): fix issues with old error declaration 2020-07-08 12:25:37 +12:00
Chaim Lev-Ari
db4a5292be refactor(errors): reorganize errors (#3938)
* refactor(bolt): move ErrObjectNotFound to bolt

* refactor(http): move ErrUnauthorized to http package

* refactor(http): move ErrResourceAccessDenied to http errors

* refactor(http): move security errors to package

* refactor(users): move user errors to users package

* refactor(errors): move single errors to their package

* refactor(schedules): move schedule error to package

* refactor(http): move endpoint error to http package

* refactor(docker): move docker errors to package

* refactor(filesystem): move filesystem errors to package

* refactor(errors): remove portainer.Error

* style(chisel): reorder imports

* fix(stacks): remove portainer.Error
2020-07-08 09:57:52 +12:00
Anthony Lapenna
e82833a363 chore(README): update README 2020-07-08 09:53:10 +12:00
Anthony Lapenna
d4456f81ec feat(endpoint-init): remove Azure ACI and remote Docker options (#4015) 2020-07-07 16:55:02 +12:00
Maxime Bajeux
91981c815c feat(volumes): Ensure a unique identifier for volumes (#3879)
* feat(volumes): Ensure a unique identifier for volumes

* feat(volumes): change few things
2020-07-07 12:01:18 +12:00
Chaim Lev-Ari
53b37ab8c8 feat(custom-templates): introduce custom templates (#3906)
* feat(custom-templates): introduce types

* feat(custom-templates): introduce data layer service

* feat(custom-templates): introduce http handler

* feat(custom-templates): create routes and view stubs

* feat(custom-templates): add create custom template ui

* feat(custom-templates): add json keys

* feat(custom-templates): introduce custom templates list page

* feat(custom-templates): introduce update page

* feat(stack): create template from stack

* feat(stacks): create stack from custom template

* feat(custom-templates): disable edit/delete of templates

* fix(custom-templates): fail update on non admin/owner

* fix(custom-templates): add ng-inject decorator

* chore(plop): revert template

* feat(stacks): remove actions column

* feat(stack): add button to create template from stack

* feat(stacks): add empty state for templates

* feat(custom-templates): show templates in a list

* feat(custom-template): replace table with list

* feat(custom-templates): move create template button

* refactor(custom-templates): introduce more fields

* feat(custom-templates): use stack type when creating template

* feat(custom-templates): use same type as stack

* feat(custom-templates): add edit and delete buttons to template item

* feat(custom-templates): customize stack before deploy

* feat(stack): show template details

* feat(custom-templates): move customize

* feat(custom-templates): create description required

* fix(template): show platform icon

* fix(custom-templates): show spinner when creating stack

* feat(custom-templates): prevent user from edit templates

* feat(custom-templates): use resource control for custom templates

* feat(custom-templates): show created templates

* feat(custom-templates): filter templates by stack type

* fix(custom-templates): create swarm or standalone stack

* feat(stacks): filter templates by type

* feat(resource-control): disable resource control on public

* feat(custom-template): apply access control on edit

* feat(custom-template): add form validation

* feat(stack): disable create custom template from external task

* refactor(custom-templates): create template from file and type

* feat(templates): introduce a file handler that returns template docker file

* feat(template): introduce template duplication

* feat(custom-template): enforce unique template name

* fix(template): rename copy button

* fix(custom-template): clear access control selection between templates

* fix(custom-templates): show required fields

* refactor(filesystem): use a constant for temp path
2020-07-07 11:18:39 +12:00
Chaim Lev-Ari
42aa8ceb00 refactor(edge-compute): enforce es6 good practices (#3961)
* refactor(edge-groups): use es6 imports

* refactor(edge-jobs): es6 imports

* refactor(edge-stacks): use es6 imports

* refactor(edge-compute): use es6 imports in components

* refactor(edge-compute): use named imports
2020-07-06 19:35:13 +12:00
Anthony Lapenna
af6bea5acc feat(kubernetes): introduce kubernetes support (#3987)
* feat(kubernetes): fix duplicate published mode

* feat(kubernetes): group port mappings by applications

* feat(kubernetes): updated UX

* feat(kubernetes): updated UX

* feat(kubernetes): new applications list view

* fix(kubernetes): applications - expand ports on row click

* refactor(kubernetes): applications - replace old view with new

* fix(kubernetes): disable access management for default resource pool

* feat(kubernetes): app creation - limit stacks suggestion to selected resource pool

* feat(kubernetes): do not allow access management on system resource pools

* refactor(kubernetes): refactor services

* create view node detail

* compute node status

* compute resource reservations

* resource reservation progress bar

* create applications node datatable

* fix(kubernetes): fix invalid method name

* feat(kubernetes): minor UI changes

* feat(kubernetes): update application inspect UI

* feat(kubernetes): add the ability to copy load balancer IP

* fix(kubernetes): minor fixes on applications view

* feat(kubernetes): set usage level info on progress bars

* fix(kubernetes): fix an issue with duplicate pagination controls

* fix(kubernetes): fix an issue with unexpandable items

* refacto(kubernetes): clean status and resource computation

* fix(kubernetes): remove a bad line

* feat(kubernetes): update application detail view

* feat(kubernetes): change few things on view

* refacto(kubernetes): Corrections relative to PR #13

* refacto(kubernetes): remove old functions

* feat(kubernetes): add application pod logs

* fix(kubernetes): PR #13

* feat(kubernetes): Enable quotas by default

* feat(kubernetes): allow non admin to have access to ressource pool list/detail view

* feat(kubernetes): UI changes

* fix(kubernetes): fix resource reservation computation in node view

* fix(kubernetes): pods are correctly filter by app name

* fix(kubernetes): nodeapplicationsdatatable is correctly reorder by cpu and memory

* fix(kubernetes): nodeapplications datatable is correctly reorder on reload

* feat(kubernetes): update podService

* refacto(kubernetes): rename nodeInspect as node

* refaceto(kubernetes): use colspan 6 instead of colspan 3

* refacto(kubernetes): use genericdatatablecontroller and make isadmin a binding

* refacto(kubernetes): remove not needed lines

* refacto(kubernetes) extract usageLevelInfo as html filter

* refacto(kubernetes): no line break for params

* refacto(kubernetes): change on node converter and filters

* refacto(kubernetes): remove bad indentations

* feat(kubernetes): add plain text informations about resources limits for non admibn user

* refacto(kubernetes): ES6 format

* refacto(kubernetes): format

* refacto(kubernetes): format

* refacto(kubernetes): add refresh callback for nodeapplicationsdatatable

* refacto(kubernetes): change if else structure

* refactor(kubernetes): files naming and format

* fix(kubernetes): remove checkbox and actions on resourcespools view for non admin

* feat(kubernetes): minor UI update

* fix(kubernetes): bind this on getPodsApplications to allow it to access $async

* fix(kubernetes): bind this on getEvents to allow it to access $async

* fix(kubernetes): format

* feat(kubernetes): minor UI update

* feat(kubernetes): add support for container console

* fix(kubernetes): fix a merge issue

* feat(kubernetes): update container console UI

* fix(api): fix typo

* feat(api): proxy pod websocket to agent

* fix(api): fix websocket pod proxy

* refactor(kubernetes): uniformize k8s merge comments

* refactor(kubernetes): update consoleController

* feat(kubernetes): prevent the removal of the default resource pool (#38)

* feat(kubernetes): show all applications running inside the resource pool (#35)

* add new datatable

* feat(kubernetes): add resource pool applications datatable to resource pool detail view

* refacto(kubernetes): factorise computeResourceReservation

* fix(kubernetes): colspan 6 to colspan 5

* fix(kubernetes): rename resourceReservationHelper into kubernetesResourceReservationHelper

* fix(kubernetes): add await to avoid double diggest cycles

* feat(kubernetes): add link to application name

* fix(kubernetes): change kubernetes-resource-pool-applications-datatable table key

* fix(kubernetes): change wording

* feat(kubernetes): add proper support for persisted folders (#36)

* feat(kubernetes): persistent volume mockups

* feat(kubernetes): persistent volume mockups

* feat(kubernetes): update persisted folders mockups

* feat(kubernetes): endpoint configure storage access policies

* fix(kubernetes): restrict advanced deployment to admin

* refactor(kubernetes): storageclass service / rest / model

* refactor(kubernetes): params/payload/converter pattern for deployments and daemonsets

* feat(kubernetes): statefulset management for applications

* fix(kubernets): associate application and pods

* feat(kubernetes): statefulset support for applications

* refactor(kubernetes): rebase on pportainer/k8s

* fix(kubernetes): app create - invalid targetPort on loadbalancer

* fix(kubernetes): internal services showed as loadbalancer

* fix(kubernetes): service ports creation / parsing

* fix(kubernetes): remove ports on headless services + ensure nodePort is used only for Cluster publishing

* fix(kubernetes): delete headless service on statefulset delete

* fix(kubernetes): statefulset replicas count display

* refactor(kubernetes): rebase on pportainer/k8s

* refactor(kubernetes): cleanup

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* fix(kubernetes): remove mockup routes

* feat(kubernetes): only display applications running on node/in resource pool when there are any

* feat(kubernetes): review resource reservations and leverage requests instead of limits (#40)

* fix(kubernetes): filter resource reservation by app in node view (#48)

* refactor(kubernetes): remove review comment

* chore(version): bump version number

* refactor(kubernetes): remove unused stacks view and components

* feat(kubernetes): update CPU slider step to 0.1 for resource pools (#60)

* feat(kubernetes): round up application CPU values (#61)

* feat(kubernetes): add information about application resource reservat… (#62)

* feat(kubernetes): add information about application resource reservations

* feat(kubernetes): apply kubernetesApplicationCPUValue to application CPU reservation

* refactor(kubernetes): services layer with models/converter/payloads (#64)

* refactor(kubernetes): services layer with models/converter/payloads

* refactor(kubernetes): file rename and comment update

* style(kubernetes): replace strings double quotes with simple quotes

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* fix(kubernetes): filter application by node in node detail view (#69)

* fix(kubernetes): filter applications by node

* fix(kubernetes): remove js error

* refactor(kubernetes): delete resource quota deletion process when deleting a resource pool (#68)

* feat(kubernetes): enforce valid resource reservations and clarify its… (#70)

* feat(kubernetes): enforce valid resource reservations and clarify its usage

* feat(kubernetes): update instance count input behavior

* feat(kubernetes): resource pools labels (#71)

* feat(kubernetes): resource pools labels

* fix(kubernetes): RP/RQ/LR owner label

* feat(kubernetes): confirmation popup on RP delete (#76)

* feat(kubernetes): application labels (#72)

* feat(kubernetes): application labels

* feat(kubernetes): display application owner in details when available

* style(kubernetes): revert StackName column labels

* fix(kubernetes): default displayed StackName

* feat(kubernetes): remove RQ query across cluster (#73)

* refactor(kubernetes): routes as components (#75)

* refactor(kubernetes): routes as components

* refactor(kubernetes): use component  lifecycle hook

* refactor(kubernetes): files naming consistency

* fix(kubernetes): fix invalid component name for cluster view

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): update portaineruser cluster role policy rules (#78)

* refactor(kubernetes): remove unused helper

* fix(kubernetes): fix invalid reload link in cluster view

* feat(kubernetes): add cluster resource reservation (#77)

* feat(kubernetes): add cluster resource reservation

* fix(kubernetes): filter resource reservation with applications

* fix(kubernetes): fix indent

* refacto(kubernetes): extract megabytes value calc as resourceReservationHelper method

* fix(kubernetes): remove unused import

* refacto(kubernetes): add resourcereservation model

* fix(kubernetes): add parenthesis on arrow functions parameters

* refacto(kubernetes): getpods in applicationService getAll

* fix(kubernetes): let to const

* fix(kubernetes): remove unused podservice

* fix(kubernetes): fix computeResourceReservation

* fix(kubernetes): app.pods to app.Pods everywhere and camelcase of this.ResourceReservation

* feat(kubernetes): configurations list view (#74)

* feat(kubernetes): add configuration list view

* feat(kubernetes): add configurations datatable

* feat(kubernetes): add item selection

* feat(kubernetes): allow to remove configuration

* feat(kubernetes): allow non admin user to see configurations

* fix(kubernetes): configurations view as component

* feat(kubernetes): remove stack property for secret and configurations

* fix(kubernetes): update import

* fix(kubernetes): remove secret delete payload

* fix(kubernetes): rename configuration model

* fix(kubernetes): remove configmap delete payload

* fix(Kubernetes): fix configuration getAsync

* fix(kubernetes): extract params as variables

* refacto(kubernetes): extract configurations used lines as helper

* fix(kubernetes): add verification of _.find return value

* fix(kubernetes): fix kubernetes configurations datatable callback

* refacto(Kubernetes): extract find before if

* fix(kubernetes): replace this by KubernetesConfigurationHelper in static method

* fix(Kubernetes): fix getASync

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* review(kubernetes): todo comments (#80)

* feat(kubernetes): minor UI update

* feat(kubernetes): round max cpu value in application creation

* feat(kubernetes): minor UI update

* fix(kubernetes): no-wrap resource reservation bar text (#89)

* docs(kubernetes): add review for formValues to resource conversion (#91)

* feat(kubernetes): configuration creation view (#82)

* feat(kubernetes): create configuration view

* feat(kubernetes): add advanced mode and create entry from file

* fix(kubernetes): fix validation issues

* fix(kubernetes): fix wording

* fix(kubernetes): replace data by stringdata in secret payloads

* fix(kubernetes): rename KubernetesConfigurationEntry to KubernetesConfigurationFormValuesDataEntry

* refacto(kubernetes): add isSimple to formValues and change configuration creation pattern

* fix(kubernetes): fix some bugs

* refacto(kubernetes): renaming

* fix(kubernetes): fix few bugs

* fix(kubernetes): fix few bugs

* review(kubernetes): refactor notices

Co-authored-by: xAt0mZ <baron_l@epitech.eu>

* feat(kubernetes): rename codeclimate file

* feat(kubernetes): re-enable codeclimate

* feat(project): update codeclimate configuration

* feat(project): update codeclimate configuration

* feat(project): update codeclimate configuration

* feat(kubernetes): minor UI update

* feat(project): update codeclimate

* feat(project): update codeclimate configuration

* feat(project): update codeclimate configuration

* feat(kubernetes): configuration details view (#93)

* feat(kubernetes): configuration details view

* fix(kubernetes): fix wording

* fix(kubernetes): fix update button

* fix(kubernetes): line indent

* refacto(kubernetes): remove conversion

* refacto(kubernetes): remove useless line

* refacto(kubernetes): remove useless lines

* fix(kubernetes): revert error handling

* fix(kubernetes): fix wording

* fix(kubernetes): revert line deletion

* refacto(kubernetes): change data mapping

* fix(kubernetes): create before delete

* fix(kubernetes): fix duplicate bug

* feat(kubernetes): configurations in application creation (#92)

* feat(kubernetes): application configuration mockups

* feat(kubernetes): update mockup

* feat(kubernetes): app create - dynamic view for configurations

* feat(kubernetes): app create - configuration support

* refactor(kubernetes): more generic configuration conversion function

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): automatically display first entry in configuration creation

* feat(kubernetes): minor UI update regarding applications and configurations

* feat(kubernetes): update Cluster icon in sidebar

* feat(kubernetes): volumes list view (#112)

* feat(kubernetes): add a feedback panel on main views (#111)

* feat(kubernetes): add a feedback panel on main views

* feat(kubernetes): add feedback panel to volumes view

* fix(kubernetes): isolated volumes showed as unused even when used (#116)

* feat(kubernetes): remove limit range from Portainer (#119)

* limits instead of requests (#121)

* feat(kubernetes): volume details (#117)

* feat(kubernetes): volume details

* fix(kubernetes): yaml not showed

* feat(kubernetes): expandable stacks list (#122)

* feat(kubernetes): expandable stacks list

* feat(kubernetes): minor UI update to stacks datatable

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): uibprogress font color (#129)

* feat(kubernetes): minor UI update to resource reservation component

* feat(kubernetes): automatically select a configuration

* refactor(kubernetes): remove comment

* feat(kubernetes): minor UI update

* feat(kubernetes): add resource links and uniformize view headers (#133)

* feat(kubernetes): prevent removal of system configurations (#128)

* feat(kubernetes): prevent removal of system configurations

* fix(kubernetes): KubernetesNamespaceHelper is not a function

* refacto(kubernetes): change prevent removal pattern

* fix(kubernetes): remove unused dependencies

* fix(kubernetes): fix configuration used label (#123)

* fix(kubernetes): fix used configurations

* fix(kubernetes): remove console log

* feat(kubernetes): rename configuration types (#127)

* refacto(kubernetes): fix wording and use configMap instead of Basic in the code

* feat(kubernetes): prevent the removal of system configuration

* fix(kubernetes): remove feat on bad branch

* fix(kubernetes): rename configuration types

* refacto(kubernetes): use a numeric enum and add a filter to display the text type

* refacto(kubernetes): fix wording and use configMap instead of Basic in the code

* feat(kubernetes): prevent the removal of system configuration

* fix(kubernetes): remove feat on bad branch

* fix(kubernetes): rename configuration types

* refacto(kubernetes): use a numeric enum and add a filter to display the text type

* fix(kubernetes): rename file and not use default in switch case

* feat(kubernetes): update advanced deployment UI/UX (#130)

* feat(kubernetes): update advanced deployment UI/UX

* feat(kubernetes): review HTML tags indentation

* feat(kubernetes): applications stacks delete (#135)

* fix(kubernetes): multinode resources reservations (#118)

* fix(kubernetes): filter pods by node

* fix(kubernetes): fix applications by node filter

* fix(kubernetes): filter pods by node

* Update app/kubernetes/views/cluster/node/nodeController.js

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* feat(kubernetes): limit usage of pod console view (#136)

* feat(kubernetes): add yaml and events to configuration details (#126)

* feat(kubernetes): add yaml and events to configuration details

* fix(kubernetes): fix errors on secret details view

* fix(kubernetes): display only events related to configuration

* fix(kubernetes): fix applications by node filter

* fix(kubernetes): revert commit on bad branch

* refacto(kubernetes): refacto configmap get yaml function

* refacto(kubernetes): add yaml into converter

* feat(kubernetes): improve application details (#141)

* refactor(kubernetes): remove applications retrieval from volume service

* feat(kubernetes): improve application details view

* feat(kubernetes): update kompose binary version (#143)

* feat(kubernetes): update kubectl version (#144)

* refactor(kubernetes): rename portainer system namespace (#145)

* feat(kubernetes): add a loading view indicator (#140)

* feat(kubernetes): add an example of view loading indicator

* refactor(css): remove comment

* feat(kubernetes): updated loading pattern

* feat(kubernetes): add loading indicator for resource pool views

* feat(kubernetes): add loading indicator for deploy view

* feat(kubernetes): add loading view indicator to dashboard

* feat(kubernetes): add loading view indicator to configure view

* feat(kubernetes): add loading indicator to configuration views

* feat(kubernetes): add loading indicator to cluster views

* feat(kubernetes): rebase on k8s branch

* feat(kubernetes): update icon size

* refactor(kubernetes): update indentation and tag format

* feat(kubernetes): backend role validation for stack deployment (#147)

* feat(kubernetes): show applications when volume is used

* feat(kubernetes): set empty value when node is not set

* feat(kubernetes): update configuration UI/UX

* feat(kubernetes): update configuration UX

* fix(kubernetes): Invalid value for a configuration (#139)

* fix(kubernetes): Invalid value for a configuration

* fix(kubernetes): remove auto JSON convertion for configMap ; apply it for RPool Accesses only

* refactor(kubernetes): remove unneeded line

* fix(kubernetes): remove default JSON parsing on configMap API retrieval

Co-authored-by: xAt0mZ <baron_l@epitech.eu>

* feat(kubernetes): applications table in configuration details (#154)

* feat(kubernetes): Add the ability to filter system resources (#142)

* feat(kubernetes): hide system configurations

* feat(kubernetes): Add the ability to filter system resources

* feat(kubernetes): add the ability to hide system resources on volumes

* fix(kubernetes): fix few issue in volumesDatatableController

* fix(kubernetes): fix applications / ports / stacks labels

* feat(kubernetes): add volumes and configurations to dashboard (#152)

* feat(kubernetes): event warning indicator (#150)

* feat(kubernetes): event warning indicator for applications

* refactor(kubernetes): refactor events indicator logic

* feat(kubernetes): add event warning indicator to all resources

* feat(kubernetes): fix missing YAML panel for node (#157)

* feat(kubernetes): revised application details view (#159)

* feat(kubernetes): revised application details view

* refactor(kubernetes): remove comment

* feat(kubernetes): rebase on k8s

* refactor(kubernetes): remove extra line

* feat(kubernetes): update kubernetes beta feedback panel locations (#161)

* feat(kubernetes): stack logs (#160)

* feat(kubernetes): stack logs

* fix(kubernetes): ignore starting pods

* fix(kubernetes): colspan on expandable stack applications table

* feat(kubernetes): add an information message about system resources (#163)

* fix(kubernetes): fix empty panel being display in cluster view (#165)

* fix(kubernetes): Invalid CPU unit for node (#156)

* fix(kubernetes): Invalid CPU unit for node

* fix(kubernetes): Invalid CPU unit for node

* refacto(kubernetes): extract parseCPU function in helper

* refacto(kubernetes): rewrite parseCPU function

* feat(kubernetes): add the kube-node-lease namespace to system namespaces (#177)

* feat(kubernetes): tag system applications on node details view (#175)

* feat(kubernetes): tag system applications on node details view

* fix(kubernetes): remove system resources filter

* feat(kubernetes): review UI/UX around volume size unit (#178)

* feat(kubernetes): updates after review (#174)

* feat(kubernetes): update access user message

* feat(kubernetes): relocate resource pool to a specific form section

* feat(kubernetes): review responsiveness of port mappings

* feat(kubernetes): clarify table settings

* feat(kubernetes): add resource reservation summary message

* feat(kubernetes): review wording (#182)

* feat(kubernetes): application stack edit (#179)

* feat(kubernetes): update UI -- update action missing

* feat(kubernetes): application stack update

* feat(kubernetes): change services stacks

* feat(kubernetes): hide default-tokens + prevent remove (#183)

* feat(kubernetes): hide default-tokens + prevent remove

* feat(kubernetes): do not display unused label for system configurations

* fix(kubernetes): minor fix around showing system configurations

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): rebase on k8s branch (#180)

* fix(kubernetes): prevent the display of system resources in dashboard (#186)

* fix(kubernetes): prevent the display of system resources in dashboard

* fix(kubernetes): prevent the display of frontend filtered resource pools

* feat(kubernetes): support downward API for env vars in application details (#181)

* feat(kubernetes): support downward API for env vars in application details

* refactor(kubernetes): remove comment

* feat(kubernetes): minor UI update

* feat(kubernetes): remove Docker features (#189)

* chore(version): bump version number (#187)

* chore(version): bump version number

* feat(kubernetes): disable update notice

* feat(kubernetes): minor UI update

* feat(kubernetes): minor UI update

* feat(kubernetes): form validation (#170)

* feat(kubernetes): add published node port value check

* feat(kubernetes): add a dns compliant validation

* fix(kubernetes): fix port range validation

* feat(kubernetes): lot of form validation

* feat(kubernetes): add lot of form validation

* feat(kubernetes): persisted folders size validation

* feat(kubernetes): persisted folder path should be unique

* fix(kubernetes): fix createResourcePool button

* fix(kubernetes): change few things

* fix(kubernetes): fix slider memory

* fix(kubernetes): fix duplicates on dynamic field list

* fix(kubernetes): remove bad validation on keys

* feat(kubernetes): minor UI enhancements and validation updates

* feat(kubernetes): minor UI update

* fix(kubernetes):  revert on slider fix

* review(kubernetes): add future changes to do

* fix(kubernetes): add form validation on create application memory slider

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
Co-authored-by: xAt0mZ <baron_l@epitech.eu>

* feat(kubernetes): remove Docker related content

* feat(kubernetes): update build system to remove docker binary install

* fix(kubernetes): fix an issue with missing user settings

* feat(kubernetes): created column for apps and resource pools (#184)

* feat(kubernetes): created column for apps and resource pools

* feat(kubernetes): configurations and volumes owner

* feat(kubernetes): rename datatables columns

* fix(kubernetes): auto detect statefulset headless service name (#196)

* fix(applications): display used configurations (#198)

* feat(kubernetes): app details - display data access policy (#199)

* feat(kubernetes): app details - display data access policy

* feat(kubernetes): tooltip on data access info

* feat(kubernetes): move DAP tooltip to end of line

* feat(kubernetes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* fix(kubernetes): fix an issue when updating the local endpoint (#204)

* fix(kubernetes): add unique key to configuration overriden key path field (#207)

* feat(kubernetes): tag applications as external (#221)

* feat(kubernetes): tag applications as external first approach

* feat(kubernetes): tag applications as external

* feat(kubernetes): Use ibytes as the default volume size unit sent to the Kubernetes API (#222)

* feat(kubernetes): Use ibytes as the default volume size unit sent to the Kubernetes API

* fix(kubernetes): only display b units in list and details views

* feat(kubernetes): add note to application details (#212)

* feat(kubernetes): add note to application details

* fix(kubernetes): remove eslintcache

* feat(kubernetes): update application note UI

* feat(kubernetes): add an update button to the note form when a note is already associated to an app

* feat(kubernetes): fix with UI changes

* fix(kubernetes): change few things

* fix(kubernetes): remove duplicate button

* fix(kubernetes): just use a ternary

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): fix data persistence display for isolated DAP (#223)

* feat(kubernetes): add a quick action to copy application name to clipboard (#225)

* feat(kubernetes): revert useless converter changes (#228)

* feat(kubernetes): edit application view (#200)

* feat(kubernetes): application to formValues conversion

* feat(kubernetes): extract applicationFormValues conversion as converter function

* feat(kubernetes): draft app patch

* feat(kubernetes): patch on all apps services + service service + pvc service

* feat(kubernetes): move name to labels and use UUID as kubernetes Name + patch recreate if necessary

* feat(kubernetes): move user app name to label and use UUID for Kubernetes Name field

* feat(kubernetes): kubernetes service patch mechanism

* feat(kubernetes): application edit

* feat(kubernetes): remove stack edit on app details

* feat(kubernetes): revert app name saving in label - now reuse kubernetes Name field

* feat(kubernetes): remove the ability to edit the DAP

* feat(kubernetes): cancel button on edit view

* feat(kubernetes): remove ability to add/remove persisted folders for SFS edition

* feat(kubernetes): minor UI update and action changes

* feat(kubernetes): minor UI update

* feat(kubernetes): remove ability to edit app volumes sizes + disable update button if no changes are made + codeclimate

* fix(kubernetes): resource reservation sliders in app edit

* fix(kubernetes): patch returned with 422 when trying to create nested objects

* fix(kubernetes): changing app deployment type wasn't working (delete failure)

* style(kubernetes): codeclimate

* fix(kubernetes): app edit - limits sliders max value

* feat(kubernetes): remove prefix on service name as we enforce DNS compliant app names

* fix(kubernetes): edit app formvalues replica based on target replica count and not total pods count

* fix(kubernetes): disable update for RWO on multi replica + delete service when changing app type

* fix(kubernetes): app details running / target pods display

* feat(kubernetes): add partial patch for app details view

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): disable edit capability for external and system apps (#233)

* feat(kubernetes): minor UI update

* fix(kubernetes): edit application issues (#235)

* feat(kubernetes): disable edition of load balancer if it's in pending state

* fix(kubernetes): now able to change from LB to other publishing types

* feat(kuberntes): modal on edit click to inform on potential service interruption

* feat(kubernetes): hide note when empty + add capability to collapse it

* fix(kubernetes): UI/API desync + app update button enabled in some cases where it shouldn't be

* fix(kubernetes): all apps are now using rolling updates with specific conditions

* style(kubernetes): code indent

* fix(kubernetes): disable sync process on endpoint init as current endpoint is not saved in client state

* fix(kubernetes): sliders refresh on app create + app details bad display for sfs running pods

* feat(kubernetes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): bump up kubectl version to v1.18.0

* feat(kubernetes): when refreshing a view, remember currently opened tabs (#226)

* feat(kubernetes): When refreshing a view, remember currently opened tabs

* fix(kubernetes): only persist the current tab inside the actual view

* fix(kubernetes): not working with refresh in view header

* fix(kubernetes): skip error on 404 headless service retrieval if missconfigured in sfs (#242)

* refactor(kubernetes): use KubernetesResourcePoolService instead of KubernetesNamespaceService (#243)

* fix(kubernetes): create service before app to enforce port availability (#239)

* fix(kubernetes): external flag on application ports mappings datatable (#245)

* refactor(kubernetes): remove unused KubernetesResourcePoolHelper (#246)

* refactor(kubernetes): make all *service.getAllAsync functions consistent (#249)

* feat(kubernetes): Tag external applications in the application table of the resource pool details view (#251)

* feat(kubernetes): add ability to redeploy application (#240)

* feat(kubernetes): add ability to redeploy application

* feat(kubernetes): allow redeploy for external apps

* Revert "feat(kubernetes): allow redeploy for external apps"

This reverts commit 093375a7e93c1a07b845ebca1618da034a97fbcd.

* refactor(kubernetes): use KubernetesPodService instead of REST KubernetesPods (#247)

* feat(kubernetes): prevent configuration properties edition (#248)

* feat(kubernetes): prevent configuration properties edition

* feat(kubernetes): Relocate the Data/Actions to a separate panel

* feat(kubernetes): remove unused functions

* feat(kubernetes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* refactor(kubernetes): Simplify the FileReader usage (#254)

* refactor(kubernetes): simplify FileReader usage

* refactor(kubernetes): Simplify FileReader usage

* refactor(kubernetes): rename e as event for readability

* feat(kubernetes): Tag system Configs in the Config details view (#257)

* refactor(kubernetes): Refactor the isFormValid function of multiple controllers (#253)

* refactor(kubernetes): refactor isFormValid functions in configurations

* refactor(kubernetes): refactor isformValid functions in create application

* refactor(kubernetes): remove duplicate lines

* refactor(kubernetes): remove commented line

* feat(kubernetes): Tag external volumes and configs (#250)

* feat(kubernetes): Tag external volumes and configs

* feat(kubernetes): remove .eslintcache

* feat(kubernetes): change few things

* feat(kubernetes): don't tag system configuration as external

* feat(kubernetes): minor UI update

* feat(kubernetes): extract inline css and clean all tags

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* fix(kubernetes): daemon set edit (#258)

* fix(kubernetes): persistent folder unit parsing

* fix(kubernetes): edit daemonset on RWO storage

* fix(kubernetes): external SFS had unlinked volumes (#264)

* feat(kubernetes): prevent to override two different configs on the same filesystem path (#259)

* feat(kubernetes): prevent to override two different configs on the same filesystem path

* feat(kubernetes): The validation should only be triggered across Configurations.

* feat(kubernetes): fix validations issues

* feat(kubernetes): fix form validation

* feat(kubernetes): fix few things

* refactor(kubernetes): Review the code mirror component update for configurations (#260)

* refactor(kubernetes): extract duplicate configuration code into a component

* refactor(kubernetes): fix form validation issues

* refactor(kubernetes): fix missing value

* refactor(kubernetes): remove useless await

* feat(kubernetes): Update the shared access policy configuration for Storage (#263)

* feat(kubernetes): Update the shared access policy configuration for Storage

* Update app/kubernetes/models/storage-class/models.js

* feat(kubernetes): remove ROX references and checks

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
Co-authored-by: xAt0mZ <baron_l@epitech.eu>

* feat(kubernetes): provide the remove/restore UX for environment variables when editing an application (#261)

* feat(kubernetes): Provide the remove/restore UX for environment variables when editing an application

* feat(kubernetes): fix ui issue

* feat(kubernetes): change few things

* fix(kubernetes): Invalid display for exposed ports in accessing the application section (#267)

* feat(kubernetes): application rollback (#269)

* feat(kubernetes): retrieve all versions of a deployment

* feat(kubernetes): application history for all types

* feat(kubernetes): deployment rollback

* feat(kubernetes): daemonset / statefulset rollback

* feat(kubernetes): remove the revision selector and rollback on previous version everytime

* feat(kubernetes): minor UI changes

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): reservations should be computed based on requests instead of limits (#268)

* feat(kubernetes): Reservations should be computed based on requests instead of limits

* feat(kubernetes): use requests instead of limits in application details

* feat(kubernetes): removes unused limits

* feat(kubernetes): Not so useless

* feat(kubernetes): use service selectors to bind apps and services (#270)

* feat(kubernetes): use service selectors to bind apps and services

* Update app/kubernetes/services/statefulSetService.js

* style(kubernetes): remove comment block

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* chore(version): bump version number

* feat(kubernetes): update feedback panel text

* chore(app): add prettier to k8s

* style(app): apply prettier to k8s codebase

* fix(kubernetes): Cannot read property 'port' of undefined (#272)

* fix(kubernetes): Cannot read property 'port' of undefined

* fix(kubernetes): concat app ports outside publishedports loop

* fix(application): fix broken display of the persistence layer (#274)

* chore(kubernetes): fix conflicts

* chore(kubernetes): fix issues related to conflict resolution

* refactor(kubernetes): refactor code related to conflict resolution

* fix(kubernetes): fix a minor issue with assets import

* chore(app): update yarn.lock

* fix(application): ports mapping are now correctly detected (#300)

* fix(build-system): fix missing docker binary download step

* feat(kubernetes): application auto scaling details (#301)

* feat(kubernetes): application auto scaling details

* feat(kubernetes): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(kubernetes): Introduce a "used by" column in the volume list view (#303)

Co-authored-by: xAt0mZ <baron_l@epitech.eu>
Co-authored-by: Maxime Bajeux <max.bajeux@gmail.com>
Co-authored-by: xAt0mZ <xAt0mZ@users.noreply.github.com>
2020-07-06 11:21:03 +12:00
Chaim Lev-Ari
24528ecea8 feat(edge-compute): move host jobs to edge (#3840)
* feat(endpoints): create an associated endpoints selector

* feat(schedules): remove edge specific explanations

* refactor(schedule): replace multi-endpoint-selector

* refactor(schedule): move controller to single file

* refactor(endpoints): remove multi-endpoint-selector

* feat(edge): rename host jobs to edge jobs

* feat(edge-jobs): remove edge warning

* refactor(edge-jobs): move schedule pages to edge

* refactor(edge-jobs): mv views to edgeJobs

* refactor(edge-jobs): rename edge jobs

* refactor(edge-jobs): move services to edge

* refactor(edge-jobs): move tasks datatable

* fix(edge-jobs): fix import

* fix(edge-jobs): use right services

* feat(settings): adjust host management description

* feat(edge-jobs): introduce interfaces and types

* feat(edge-jobs): implement bolt service

* refactor(edge-jobs): replace schedule routes

* refactor(edge-job): replace Schedule service

* refactor(edge-jobs): remove job_script_exec

* refactor(host): remove jobs table

* feat(edge-jobs): replace schedule

* feat(edge-jobs): load file on inspect

* fix(edge-job): parse cron correctly

* feat(edge-jobs): show tasks

* feat(host): rename tooltip

* refactor(host): remove old components

* refactor(main): remove schedule types

* refactor(snapshot): replace job service with snapshot service

* refactor(jobs): remove jobs form and datatable

* feat(edge-jobs): create db migration

* fix(main): start snapshot service with correct interval

* feat(settings): change host tooltip

* feat(edge-jobs): load endpoints

* fix(edge-job): disable form submit when form is invalid

* refactor(edge-compute): use const

* refactor(edge-jobs): use generic controller

* refactor(edge-jobs): replace $scope with controllerAs

* refactor(edge-jobs): replace routes with components

* refactor(edge-jobs): replace functions with classes

* refactor(edge-jobs): use async/await

* refactor(edge-jobs): rename functions

* feat(edge-jobs): introduce beta panel

* feat(edge-jobs): allow single character names

* fix(snapshot): run snapshot in coroutine

* feat(edge-jobs): add logs status

* feat(filesystem): add edge job logs methods

* feat(edge-jobs): intoduce edge jobs tasks api

* feat(edge-jobs): remove schedule task model

* fix(fs): build edge job task file path

* fix(edge-jobs): update task meta

* fix(edge-jobs): return a list of endpoints

* feat(edge-jobs): update logs from agent

* feat(edge-jobs): collect logs

* feat(edge-jobs): rename url

* feat(edge-jobs): refresh to same tab

* feat(edge-jobs): remove old info

* refactor(edge-jobs): rename script path json

* fix(edge-job): save file before adding job

* feat(edge-job): show retrieving logs label

* feat(edge-job): replace cron with 5 places

* refactor(edge-jobs): replace tasks with results

* feat(edge-jobs): add auto refresh until logs are collected

* feat(edge-jobs): fix column size

* feat(edge-job): display editor

* feat(edge-job): add name validation

* feat(edge-job): set default time for 1 hour from now

* feat(edge-job): add validation for cron format

* feat(edge-job): add a note about timezone

* fix(edge-job): replace regex

* fix(edge-job): check for every minute cron

* style(edge-jobs): add reference for cron regex

* refactor(edge-jobs): rename migration name

* refactor(edge-job): rename edge job response

* refactor(snapshot): rename snapshot endpoint method

* refactor(edge-jobs): move tasks handler to edgejobs

* feat(security): introduce a middleware for edge compute operations

* feat(edge-job): use edge compute middleware

* feat(edge-groups): filter http actions based on edge setting

* fix(security): return from edge bouncer if failed

* feat(edge-stacks): filter http actions based on edge setting

* feat(edge-groups): show error when failed to load groups

* refactor(db): remove edge-jobs migration

* refactor(migrator): remove unused dependency

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-06-25 15:25:51 +12:00
xAt0mZ
b6f5d8f90e fix(auth): autofocus on login field (#3955) 2020-06-25 10:24:45 +12:00
Chaim Lev-Ari
ec9055f0e6 fix(endpoints): hide edge endpoint url (#3949) 2020-06-23 17:06:53 +12:00
Chaim Lev-Ari
40f9078d80 style(docker): replace icons for containers and volumes (#3950)
* fix(sidebar): replace icons for containers and volumes

* fix(icons): replace icons for containers and volumes
2020-06-23 11:46:56 +12:00
Chaim Lev-Ari
5760648970 chore(plop): fix controller import in template (#3948) 2020-06-22 19:38:22 +12:00
itsconquest
7bd3d6e44a feat(project): introduce toolkit for containerized dev (#3863)
* feat(project): introduce toolkit for containerized dev

* feat(project): clean up localserver shell cmd

* feat(project): add install of yarn deps to grunt

* feat(project): update gruntfile.js

* Introduce an ARG statement for the GO_VERSION

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-06-17 13:36:11 +12:00
Chaim Lev-Ari
0b6dbec305 refactor(auth): remove login retry with $sanitize (#3923)
* refactor(auth): remove update-password view

* refactor(auth): remove auth retry with $sanitize
2020-06-17 11:41:39 +12:00
Chaim Lev-Ari
7c3b83f6e5 refactor(portainer): introduce internal package (#3924)
* refactor(auth): move auth helpers to internal package

* refactor(edge-compute): move edge helpers to internal package

* refactor(tags): move tags helper to internal package

* style(portainer): sort imports
2020-06-16 19:58:16 +12:00
Chaim Lev-Ari
5d7ba0baba feat(edge-compute): add flag to auto enable Edge compute features (#3922) 2020-06-16 19:55:45 +12:00
Anthony Lapenna
89fb3c8dae feat(pulldog): configure expiry 2020-06-15 13:31:28 +12:00
Chaim Lev-Ari
24888fbbae feat(users): prevent the removal of initial admin account (#3912)
* feat(users): prevent the removal of initial admin account

* feat(users): disabled init admin delete button
2020-06-15 11:48:58 +12:00
xAt0mZ
381e372c4c chore(app): clean and update dependencies (#3917) 2020-06-12 09:06:41 +12:00
Anthony Lapenna
e0c47b644e feat(pulldog): update configuration 2020-06-11 10:51:49 +12:00
xAt0mZ
06911ad2c6 refactor(app): remove all VMWARE_VIC related code (#3914) 2020-06-11 07:59:11 +12:00
Chaim Lev-Ari
b02749f877 feat(auth): add custom user timeout (#3871)
* feat(auth): introduce new timeout constant

* feat(auth): pass timeout from handler

* feat(auth): add timeout selector to auth settings view

* feat(settings): add user session timeout property

* feat(auth): load user session timeout from settings

* fix(settings): use correct time format

* feat(auth): remove no-auth flag

* refactor(auth): move timeout mgmt to jwt service

* refactor(client): remove no-auth checks from client

* refactor(cli): remove defaultNoAuth

* feat(settings): create settings with default user timeout value

* refactor(db): save user session timeout always

* refactor(jwt): return error

* feat(auth): set session timeout in jwt service on update

* feat(auth): add description and time settings

* feat(auth): parse duration

* feat(settings): validate user timeout format

* refactor(settings): remove unneccesary import
2020-06-09 21:55:36 +12:00
Chaim Lev-Ari
b58c2facfe revert(azure): revert removal (#3890)
* Revert "fix(sidebar): show docker sidebar when needed (#3852)"

This reverts commit 59da17dde4.

* Revert "refactor(azure): remove Azure ACI endpoint support (#3803)"

This reverts commit 493de20540.
2020-06-09 14:43:32 +12:00
Chaim Lev-Ari
25ca036070 feat(users): add the ability to rename a user (#3884)
* feat(users): update username in server

* feat(users): add username text field

* fix(users): rename label and change buttons size

* feat(users): change update message

* feat(users): disable submit when not changed

* feat(users): confirm updating username

* feat(users): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-06-09 14:42:40 +12:00
yunfan
7325407f5f fix(endpoint): fix invalid Edge deployment command (#3908) 2020-06-08 16:57:34 +12:00
Anthony Lapenna
f0fafd7537 chore(project): update pull-dog.json 2020-06-06 05:26:10 +12:00
Chaim Lev-Ari
d8d3baf18e refactor(client): move assets folder into app and remove rdash-ui dep (#3883)
* refactor(assets): use rdash css

* chore(build): remove rdash dep

* refactor(client): move assets folder into app
2020-06-05 11:03:13 +12:00
Chaim Lev-Ari
a0ba531fed fix(registries): check same url for gitlab (#3870) 2020-06-04 18:50:02 +12:00
Chaim Lev-Ari
9f4631bb6d feat(edge-compute): add specific edge endpoint checkin interval (#3855)
* feat(endpoint): send custom checkin interval

* feat(endpoint): update edge checkin interval

* feat(endpoint): save checkin interval

* feat(endpoints): create endpoint with checkin interval

* feat(endpoints): change tooltip

* fix(edge-compute): fix typos

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* fix(endpoints): show default interval

* fix(endpoint): rename checkin property

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
2020-06-04 17:35:09 +12:00
Chaim Lev-Ari
766ced7cb1 chore(project): add angular components code snippets (#3649)
* chore(project): add angular components code snippets

* chore(project): add plopjs templates

* feat(project): use class in controller template

* chore(client): rename generators

* chore(vscode): fix controller snippets

* chore(git): ignore only specific files in .vscode

* chore(plop): move generators to app

* chore(plop): fix portainer module

* fix(git): fix gitignore vscode

* chore(vscode): remove symling to code-snippets

* refactor(build): move plop templates to root

* feat(build): add readme for plop
2020-06-04 17:01:31 +12:00
Anthony Lapenna
38066ece33 feat(project): re-introduce pull-dog 2020-06-03 11:50:39 +12:00
Anthony Lapenna
334c015f81 fix(bolt): migrate empty templates URL settings (#3856) 2020-06-03 11:40:04 +12:00
Chaim Lev-Ari
01d8c90348 fix(sidebar): show docker sidebar when needed (#3852) 2020-06-03 11:40:04 +12:00
Anthony Lapenna
c5f78f663a feat(settings): update templates documentation link 2020-06-03 11:40:04 +12:00
Anthony Lapenna
25103f08f9 feat(api): introduce new datastore interface (#3802)
* feat(api): introduce new datastore interface

* refactor(api): refactor http and main layers

* refactor(api): refactor http and bolt layers
2020-06-03 11:40:04 +12:00
Anthony Lapenna
493de20540 refactor(azure): remove Azure ACI endpoint support (#3803)
* feat(templates): remove template management features (#3719)

* feat(api): remove template management features

* feat(templates): remove template management features

* refactor(azure): remove Azure ACI endpoint support
2020-06-03 11:40:04 +12:00
Anthony Lapenna
6b41b5ec5d refactor(api): remove unused error constant 2020-06-03 11:40:04 +12:00
Chaim Lev-Ari
c074a714cf feat(server): remove external endpoint feature (#3837)
* fix(prettier): auto format html files (#3836)

* refactor(main): remove reference to external endpoints

* refactor(cli): remove parsing of external endpoints param

* refactor(portainer): remove types for external endpoints

* refactor(endpoints): remove warning for external endpoints

* refactor(endpoints): remove endpoint management setting

* refactor(endpoints): remove ref to endpoint management

* fix(main): remove endpoint management
2020-06-03 11:40:04 +12:00
Anthony Lapenna
d9665bc939 fix(api): update to template file format for Edge templates 2020-06-03 11:40:04 +12:00
Anthony Lapenna
4fdb0934cb feat(cli): remove the --no-snapshot CLI flag (#3814) 2020-06-03 11:40:04 +12:00
Anthony Lapenna
d202660bb8 feat(project): remove pulldog 2020-06-03 11:40:04 +12:00
Anthony Lapenna
8986e284fd feat(api): bump ldap library version (#3772)
* feat(api): bump ldap library version

* feat(api): fix ldap v3 import
2020-06-03 11:40:04 +12:00
Anthony Lapenna
070be46352 feat(templates): leftovers cleanup (#3762)
* feat(templates): leftovers cleanup

* feat(templates): update CLIFlags structure
2020-06-03 11:40:04 +12:00
Simone Cattaneo
800b357041 fix(api): updated LDAP library to v3 (portainer#3244) (#3386)
Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
2020-06-03 11:40:04 +12:00
Anthony Lapenna
4c4cec73d7 chore(version): bump version number 2020-06-03 11:40:03 +12:00
Maxime Bajeux
54621ced9e feat(templates): support templates versioning (#3729)
* feat(templates): Support templates versioning format

* Update app/portainer/models/template.js

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
2020-06-03 11:38:39 +12:00
Anthony Lapenna
f371dc5402 feat(templates): fix an issue with templates initialization and update settings view 2020-06-03 11:38:39 +12:00
Anthony Lapenna
5563ff60fc feat(templates): remove template management features (#3719)
* feat(api): remove template management features

* feat(templates): remove template management features
2020-06-03 11:38:39 +12:00
Anthony Lapenna
45f93882d0 Merge tag '1.24.0' into develop
Release 1.24.0
2020-05-29 14:34:45 +12:00
Anthony Lapenna
13f712d06d Merge branch 'release/1.24.0' 2020-05-29 14:34:39 +12:00
Anthony Lapenna
bfcdeecac9 chore(version): bump version number 2020-05-29 14:34:32 +12:00
Chaim Lev-Ari
babc509115 fix(yarn): downgrade router version (#3857) 2020-05-25 20:00:33 +12:00
Maxime Bajeux
ecbee3ee3d fix(templates): replace volume input with a select (#3853)
* prevent non admins to create volume which they do not have access

* fix(volumes): replace volume input by a select
2020-05-22 11:35:34 +12:00
Maxime Bajeux
10772a3ecd fix(networks): network without ipv6 should not be expand (#3844) 2020-05-21 12:40:23 +12:00
Chaim Lev-Ari
2260107811 feat(edge-stacks): add refresh to edit stack page (#3835) 2020-05-18 20:05:00 +12:00
Chaim Lev-Ari
42e7db0ae7 fix(prettier): auto format html files (#3836) 2020-05-18 19:08:45 +12:00
Maxime Bajeux
ebac85b462 feat(volumes): add a switch to use CIFS volumes (#3823)
* feat(volumes): add a switch to use CIFS volumes

* feat(volumes): switch between nfs and cifs

* feat(volumes): autofix sharepoint, hide driveroptions and allow to create unnammed volume

* feat(volumes): change cifs version select options

* feat(volumes): change few things
2020-05-15 13:28:51 +12:00
Chaim Lev-Ari
8eac1d2221 feat(edge-compute): add support for Edge stacks (#3827)
* feat(api): introduce Edge group API (#3639)

* feat(edge-groups): add object definition and service definition

* feat(edge-groups): implement bolt layer

* feat(edge-groups): bind service to server

* feat(edge-group): add edge-group create http handler

* feat(edge-groups): add list method to edge group handler

* feat(edge-group): add inspect http handler

* feat(edge-groups): add delete edge-group handler

* feat(edge-groups): add update group handler

* style(db): order by alphabetical order

* fix(edge-groups): rewrite http error messages

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* fix(main): order by alphabetical order

* refactor(edge-group): relocate fetch group

* fix(edge-group): reset tagids/endpoints if dynamic

* refactor(server): order by alphabetical order

* refactor(server): order by alphabetical order

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* Introduce a new setting to enable Edge compute features (#3654)

* feat(edge-compute): add edge compute setting

* feat(edge-compute): add edge compute group to sidebar

* fix(settings): rename settings form group

* fix(settings): align form control

* Edge group associated endpoints (#3659)

* chore(version): bump version number

* chore(version): bump version number

* feat(endpoints): filter by endpoint type (#3646)

* refactor(tags): migrate tags to have association objects

* refactor(tags): refactor tag management (#3628)

* refactor(tags): replace tags with tag ids

* refactor(tags): revert tags to be strings and add tagids

* refactor(tags): enable search by tag in home view

* refactor(tags): show endpoint tags

* refactor(endpoints): expect tagIds on create payload

* refactor(endpoints): expect tagIds on update payload

* refactor(endpoints): replace TagIds to TagIDs

* refactor(endpoints): set endpoint group to get TagIDs

* refactor(endpoints): refactor tag-selector to receive tag-ids

* refactor(endpoints): show tags in multi-endpoint-selector

* chore(tags): revert reformat

* refactor(endpoints): remove unneeded bind

* refactor(endpoints): change param tags to tagids in endpoint create

* refactor(endpoints): remove console.log

* refactor(tags): remove deleted tag from endpoint and endpoint group

* fix(endpoints): show loading label while loading tags

* chore(go): remove obsolete import labels

* chore(db): add db version comment

* fix(db): add tag service to migrator

* refactor(db): add error checks in migrator

* style(db): sort props in alphabetical order

* style(tags): fix typo

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(endpoints): replace tagsMap with tag string representation

* refactor(tags): rewrite tag delete to be more readable

* refactor(home): rearange code to match former style

* refactor(tags): guard against missing model in tag-selector

* refactor(tags): rename vars in tag_delete

* refactor(tags): allow any authenticated user to fetch tag list

* refactor(endpoints): replace controller function with class

* refactor(endpoints): replace function with helper

* refactor(endpoints): replace controller with class

* refactor(tags): revert tags-selector to use 1 way bindings

* refactor(endpoints): load empty tag array instead of nil

* refactor(endpoints): revert default tag ids

* refactor(endpoints): use function in place

* refactor(tags): use lodash

* style(tags): use parens in arrow functions

* fix(tags): remove tag from tag model

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(tags): create tag association when creating tag

* refactor(tags): delete tag association when deleting tag

* refactor(db): handle error in tag association create

* feat(endpoint-group): update tag assoc when creating endpoint group

* feat(endpoint-group): update tag association when updating group

* feat(endpoint-groups): remove group from tag associations

* feat(endpoints): associate endpoint with tag on create

* feat(endpoints): edit tag association when updating endpoint

* fix(tags): fix merge problems

* refactor(tags): remove tag association resource

* fix(db): use regular tags map

* style(tags): reorder props and imports

* refactor(endpoint-groups): replace tag-association with tag

* feat(edge-group): get associated endpoints when fetching

* refactor(tags): refactor algo to update endpoint and group tags

* refactor(edge-group): rename variable

* refactor(tags): move calc of tags to remove to global function

* fix(tags): update tag after adding association

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* fix(edge-groups): associate groups only with edge endpoints (#3667)

* fix(edge-groups): check endpoint type when adding to edge-group

* fix(edge-groups): return only edge endpoints for dynamic groups

* fix(edge-compute): load edge compute setting on public setting (#3665)

* Edge group list (#3644)

* feat(edge-groups): add edge module

* feat(edge-groups):  add edge-group service

* feat(edge-group): add groups list view

* feat(edge-groups): add link to groups in the sidebar

* feat(edge-group): show endpoints count and group type

* feat(edge-group): enable removal of edge groups

* refactor(edge-groups): replace datatable controller with class

* refactor(edge-groups): replace function with class

* fix(edge-groups): sort items by endpoints count and group type

* refactor(edge-groups): use generic datatable-header component

* feat(app): add trace for ui router

* fix(edge-compute): add ng injection to onEnter guard

* fix(edge-compute): add ng injection to onEnter guard

* style(edge-compute): remove space

* refactor(edge-compute): import angular

* fix(app): remove ui router trace

* refactor(product): revert app.js

* fix(edge-compute): remove admin guard from edge routes

* fix(edge-groups): change label of empty datatable

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(edge-groups): rename service

* fix(edge-groups): replace icon in sidebar

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(edge-groups): remove datatable controller

* refactor(edge-groups): move datatable icon to binding

* refactor(edge-groups): use vanilla datatable header

* refactor(datatable): remove datatable header

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(edge): rename edge group to Edge group

* feat(edge-groups): edge group creation view (#3671)

* feat(edge-groups): add create group view

* feat(edge-groups): allow to choose group type

* feat(edge-groups): implement create service handler

* feat(edge-group): filter by edge endpoints

* refactor(edge-groups): rename to camel case

* refactor(edge-groups): replace controller with class

* feat(endpoints): filter endpoints by type

* refactor(edge-groups): remove comments and unneccesary async keyword

* refactor(edge-group): use $async service

* fix(edge-groups): replace view title

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* fix(edge-groups): change icon

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* fix(edge-groups): change icon

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(edge-groups): remove obsolete function

* feat(edge-groups): add empty list messages

* feat(edge-group): add description to group types

* refactor(edge-groups): add finally block

* feat(endpoints): search server in multi-endpoint-selector

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* feat(edge-group) edit view (#3672)

* feat(edge-groups): add edit group view

* refactor(edge-group): replace edit controller with class

* refactor(edge-groups): remove async keyword

* refactor(edge-groups): use $async service

* refactor(edge-group): remove unnecessary functions

* fix(endpoints): group by groups in endpoint-selector

* feat(edge-groups): minor UI update

* fix(edge-groups): provide defaults for edge group (#3682)

* feat(edge-stacks): add basic views and sidebar link (#3689)

* feat(edge-stacks): add mock routes

* feat(edge-stacks): add link to stacks on sidebar

* feat(edge-stacks): add edge stacks view

* feat(edge-stacks): add create view

* feat(edge-stacks): add edit view

* fix(edge-stacks): use class in controller

* feat(edge-stacks): add edge-stacks api (#3688)

* feat(edge-stack): add edge stack types

* feat(edge-stacks): add edge stack service interface

* feat(edge-stacks): implement store

* feat(edge-stacks): bind service to datastore

* feat(edge-stacks): bind service to server

* feat(edge-stack): create basic api

* feat(edge-stack): create stack api

* feat(edge-stacks): update api

* refacotor(edge-stack): rename files

* feat(edge-stack): update endpoint status

* style(edge-stacks): remove comments

* feat(edge-stacks): use edge stacks folder for files

* fix(edge-stacks): replace bucket name

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* fix(edge-stacks): replace unmarshal function

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* fix(edge-stacks): replace edge stacks path

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* chore(git): merge develop to edge compute (#3692)

* feat(support): make support type dynamic (#3621)

* chore(version): bump version number

* chore(version): bump version number

* feat(endpoints): filter by endpoint type (#3646)

* chore(assets): double UI image resolutions for HiDPI displays (#3648)

Fixes #3069

Prevents users seeing blurry logos and other images when using a hidpi
display (like scaled 4k, or a Retina display).

These images have been recreated manually with 2x the original
resolution but should resemble the originals as much as possible.

They have also been run through pngcrush for compression.

* fix(services): enforce minimum replica count of 0 (#3653)

* fix(services): enforce minimum replica count of 0

Fixes #3652

Prevents replica count from being set below zero and causing an error.

* fix(services): enforce replica count is an integer

Prevents users entering decimals in the replica count

* refactor(tags): refactor tag management (#3628)

* refactor(tags): replace tags with tag ids

* refactor(tags): revert tags to be strings and add tagids

* refactor(tags): enable search by tag in home view

* refactor(tags): show endpoint tags

* refactor(endpoints): expect tagIds on create payload

* refactor(endpoints): expect tagIds on update payload

* refactor(endpoints): replace TagIds to TagIDs

* refactor(endpoints): set endpoint group to get TagIDs

* refactor(endpoints): refactor tag-selector to receive tag-ids

* refactor(endpoints): show tags in multi-endpoint-selector

* chore(tags): revert reformat

* refactor(endpoints): remove unneeded bind

* refactor(endpoints): change param tags to tagids in endpoint create

* refactor(endpoints): remove console.log

* refactor(tags): remove deleted tag from endpoint and endpoint group

* fix(endpoints): show loading label while loading tags

* chore(go): remove obsolete import labels

* chore(db): add db version comment

* fix(db): add tag service to migrator

* refactor(db): add error checks in migrator

* style(db): sort props in alphabetical order

* style(tags): fix typo

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(endpoints): replace tagsMap with tag string representation

* refactor(tags): rewrite tag delete to be more readable

* refactor(home): rearange code to match former style

* refactor(tags): guard against missing model in tag-selector

* refactor(tags): rename vars in tag_delete

* refactor(tags): allow any authenticated user to fetch tag list

* refactor(endpoints): replace controller function with class

* refactor(endpoints): replace function with helper

* refactor(endpoints): replace controller with class

* refactor(tags): revert tags-selector to use 1 way bindings

* refactor(endpoints): load empty tag array instead of nil

* refactor(endpoints): revert default tag ids

* refactor(endpoints): use function in place

* refactor(tags): use lodash

* style(tags): use parens in arrow functions

* fix(tags): remove tag from tag model

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* chore(yarn): change start:client to start webpack dev server (#3595)

* chore(yarn): change start:client to start webpack dev server

* Update package.json

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* create tag from tag selector (#3640)

* feat(tags): add button to save tag when doesn't exist

* feat(endpoints): allow the creating of tags in endpoint edit

* feat(groups): allow user to create tags in create group

* feat(groups): allow user to create tags in edit group

* feat(endpoint): allow user to create tags from endpoint create

* feat(tags): allow the creation of a new tag from dropdown

* feat(tag): replace "add" with "create"

* feat(tags): show tags input when not tags

* feat(tags): hide create message when not allowed

* refactor(tags): replace component controller with class

* refactor(tags): replace native methods with lodash

* refactor(tags): remove unused onChangeTags function

* refactor(tags): remove on-change binding

* style(tags): remove white space

* refactor(endpoint-groups): move controller to separate file

* fix(groups): allow admin to create tag in group form

* refactor(endpoints): wrap async function with try catch and $async

* style(tags): wrap arrow function args with parenthesis

* refactor(endpoints): return $async functions

* refactor(tags): throw error in the format Notification expects

* chore(yarn): add start:client script back (#3691)

* feat(endpoints): filter by ids and/or tag ids (#3690)

* feat(endpoints): add filter by tagIds

* refactor(endpoints): change endpoints service to query by tagIds

* fix(endpoints): filter by tags

* feat(endpoints): filter by endpoint groups tags

* feat(endpoints): filter by ids

Co-authored-by: itsconquest <william.conquest@portainer.io>
Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
Co-authored-by: Ben Brooks <ben@bbrks.me>
Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* Chore merge develop to edge compute (#3702)

* feat(support): make support type dynamic (#3621)

* chore(version): bump version number

* chore(version): bump version number

* feat(endpoints): filter by endpoint type (#3646)

* chore(assets): double UI image resolutions for HiDPI displays (#3648)

Fixes #3069

Prevents users seeing blurry logos and other images when using a hidpi
display (like scaled 4k, or a Retina display).

These images have been recreated manually with 2x the original
resolution but should resemble the originals as much as possible.

They have also been run through pngcrush for compression.

* fix(services): enforce minimum replica count of 0 (#3653)

* fix(services): enforce minimum replica count of 0

Fixes #3652

Prevents replica count from being set below zero and causing an error.

* fix(services): enforce replica count is an integer

Prevents users entering decimals in the replica count

* refactor(tags): refactor tag management (#3628)

* refactor(tags): replace tags with tag ids

* refactor(tags): revert tags to be strings and add tagids

* refactor(tags): enable search by tag in home view

* refactor(tags): show endpoint tags

* refactor(endpoints): expect tagIds on create payload

* refactor(endpoints): expect tagIds on update payload

* refactor(endpoints): replace TagIds to TagIDs

* refactor(endpoints): set endpoint group to get TagIDs

* refactor(endpoints): refactor tag-selector to receive tag-ids

* refactor(endpoints): show tags in multi-endpoint-selector

* chore(tags): revert reformat

* refactor(endpoints): remove unneeded bind

* refactor(endpoints): change param tags to tagids in endpoint create

* refactor(endpoints): remove console.log

* refactor(tags): remove deleted tag from endpoint and endpoint group

* fix(endpoints): show loading label while loading tags

* chore(go): remove obsolete import labels

* chore(db): add db version comment

* fix(db): add tag service to migrator

* refactor(db): add error checks in migrator

* style(db): sort props in alphabetical order

* style(tags): fix typo

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(endpoints): replace tagsMap with tag string representation

* refactor(tags): rewrite tag delete to be more readable

* refactor(home): rearange code to match former style

* refactor(tags): guard against missing model in tag-selector

* refactor(tags): rename vars in tag_delete

* refactor(tags): allow any authenticated user to fetch tag list

* refactor(endpoints): replace controller function with class

* refactor(endpoints): replace function with helper

* refactor(endpoints): replace controller with class

* refactor(tags): revert tags-selector to use 1 way bindings

* refactor(endpoints): load empty tag array instead of nil

* refactor(endpoints): revert default tag ids

* refactor(endpoints): use function in place

* refactor(tags): use lodash

* style(tags): use parens in arrow functions

* fix(tags): remove tag from tag model

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* chore(yarn): change start:client to start webpack dev server (#3595)

* chore(yarn): change start:client to start webpack dev server

* Update package.json

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* create tag from tag selector (#3640)

* feat(tags): add button to save tag when doesn't exist

* feat(endpoints): allow the creating of tags in endpoint edit

* feat(groups): allow user to create tags in create group

* feat(groups): allow user to create tags in edit group

* feat(endpoint): allow user to create tags from endpoint create

* feat(tags): allow the creation of a new tag from dropdown

* feat(tag): replace "add" with "create"

* feat(tags): show tags input when not tags

* feat(tags): hide create message when not allowed

* refactor(tags): replace component controller with class

* refactor(tags): replace native methods with lodash

* refactor(tags): remove unused onChangeTags function

* refactor(tags): remove on-change binding

* style(tags): remove white space

* refactor(endpoint-groups): move controller to separate file

* fix(groups): allow admin to create tag in group form

* refactor(endpoints): wrap async function with try catch and $async

* style(tags): wrap arrow function args with parenthesis

* refactor(endpoints): return $async functions

* refactor(tags): throw error in the format Notification expects

* chore(yarn): add start:client script back (#3691)

* feat(endpoints): filter by ids and/or tag ids (#3690)

* feat(endpoints): add filter by tagIds

* refactor(endpoints): change endpoints service to query by tagIds

* fix(endpoints): filter by tags

* feat(endpoints): filter by endpoint groups tags

* feat(endpoints): filter by ids

* refactor(project): sort portainer types and interface definitions (#3694)

* refactor(portainer): sort types

* style(portainer): add comment about role service

* refactor(portainer): sort interface types

* refactor(portainer): sort enums

* Update README.md

* Update README.md

* Update README.md

* chore(project): add prettier for code format (#3645)

* chore(project): install prettier and lint-staged

* chore(project): apply prettier to html too

* chore(project): git ignore eslintcache

* chore(project): add a comment about format script

* chore(prettier): update printWidth

* chore(prettier): remove useTabs option

* chore(prettier): add HTML validation

* refactor(prettier): fix closing tags

* feat(prettier): define angular parser for html templates

* style(prettier): run prettier on codebase

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* chore(prettier): run format on client codebase

Co-authored-by: itsconquest <william.conquest@portainer.io>
Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
Co-authored-by: Ben Brooks <ben@bbrks.me>
Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
Co-authored-by: Neil Cresswell <neil@cresswell.net.nz>

* feat(edge-stacks): create basic edge stack service (#3704)

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* feat(edge-groups): Provide a switch to use AND or OR for tags (#3695)

* feat(edge-groups): add switch to form

* feat(project): add property to EdgeGroup

* feat(edge-groups): save mustHaveAllTags

* feat(edge-groups): fetch associated endpoints (AND and OR)

* feat(edge-groups): add AND selector

* feat(edge-groups): default to AND

* fix(edge-groups): rewrite selector options

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(endpoints): move margin to schedule form

* fix(edge-groups): move the selector to top of group

* refactor(edge-groups): replace partialMatch property

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* feat(edge-stacks): add Edge stack creation view (#3705)

* feat(edge-stacks): basic creation view

* feat(edge-stacks): add group selector

* feat(edge-stack): create edge stack

* fix(code-editor): apply digest cycle after editor is changed

* style(project): reformat constants file

* feat(edge-stacks): add a note about missing edge groups

* fix(edge-stacks): add groups when creating stack from file

* feat(edge-groups): add associated endpoints table (#3710)

* feat(edge-groups): load associated endpoints

* feat(endpoints): add option to filter endpoint by partial match tags

* feat(edge-groups): query endpoints by PartialMatch

* feat(edge-groups): reload endpoints when form changes

* feat(edge-groups): remove columns

* feat(edge-group): remove url column

* refactor(edge-group): remove props

* feat(edge-stacks): add list view (#3713)

* feat(edge-stacks): basic datatable

* feat(edge-stacks): remove stack

* refactor(edge-stacks): convert to class

* refactor(edge-stacks): replace id with stackId

* feat(edge-stacks) edit edge stack view (#3716)

* feat(edge-stack): load file content

* feat(edge-stack): edit view

* feat(edge-stack): enable update stack

* refactor(edge-stacks): move form to component

* feat(edge-stacks): add endpoints status

* feat(edge-stacks): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(edge-groups) prevent deletion of edge group used by an edge stack (#3722)

* feat(edge-groups): show if group belonges to edge stack

* feat(edge-group): protect deletion of used edge group

* feat(edge-groups): diable selection of used group

* feat(edge-groups): add inuse tag (#3739)

* feat(edge-groups): add inuse tag

* Update app/edge/components/groups-datatable/groupsDatatable.html

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* feat(edge-stack): update stack version when stack file is changed (#3746)

* feat(edge-stack): update version when stack file is changed

* refactor(edge-stacks): move update of version to clientside

* feat(edge-groups): replace Edge group endpoint selector (#3738)

* feat(edge-groups): replace selector

* feat(edge-group): add selector in edit form

* feat(edge-groups): show tags in endpoint selector

* feat(edge-groups): show the endpoint group name

* fix(edge-group): remove element from associated endpoints

* feat(edge-groups): add group column

* feat(edge-groups): move endpoints to other column

* fix(groups): disable sort

* refactor(endpoints): toggle backend pagination as a property

* fix(endpoints): show group name in group-association-table

* feat(endpoints): truncate table columns

* fix(endpoints): update group association table colspan

* fix(endpoint-groups): show dash when no tags

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>

* feat(edge-stacks): add api for edge to query stack config (#3748)

* refactor(http): move edge validation to bouncer

* feat(edge-stacks): add api for edge to query stack config

* style(edge-stack): remove parentheses

* Update api/http/security/bouncer.go

* refactor(edge-stacks): move config inspect to endpoints handler

* refactor(endpoints): move stack inspect to edge handler

* style(security): fix typo

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(endpoints): rename file

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

* feat(edge-groups): add dynamic group endpoints table (#3780)

* fix(edge-stacks): update version when updating stack files (#3778)

* feat(edgestacks): change status permission to edge enpoints

* feat(edge-compute): add stack info to edge status inspect (#3764)

* feat(edge-compute): create helper functions

* feat(endpoints): add relation object and service

* feat(db): create endpoint relation migration

* feat(endpoints): create relation when creating endpoint

* feat(endpoints): update relation when updating endpoint

* feat(endpoints): delete relation when deleting endpoint

* feat(endpoint): add stack status to endpoint_status

* feat(edge-stacks): connect new edge stack to endpoint

* refactor(edgestack): return errors.New

* refactor(edgestacks): return error

* refactor(edgegroup): endpoint can be related only if edge endpoint

* feat(endpoints): update relation only when tags or groups were changd

* refactor(tags): change tags functions to set functions

* refactor(edgestack): return a list of endpoints for a list of edgegroups

* feat(edgestacks): update relation when updating stack

* feat(edgestacks): remove relations when deleting edge stack

* feat(edgegroup): update related endpoints

* feat(endpoint-group): update endpoints relations on create

* feat(endpointgroup): add relatd stacks to endpoint when added to group

* feat(endpoint-groups): update relation when group is changed

* feat(endpointgroup): when deleting group, update its endpoints relations

* feat(tags): update related endpoints when deleting tag

* refactor(edge-compute): use pointers

* refactor(endpointgroup): handle unassociated endpoint

* fix(edgestack): show correct stack status

* fix(endpoint): remove deleted endpoint from related tags

* feat(edge-stacks): change acknowledged status color to blue (#3810)

* feat(edge-compute): provide stack name to edge endpoint (#3809)

* feat(edge-groups): when no tags selected show empty list of endpoints (#3811)

* feat(edge-groups): when no tags selected show empty list of endpoints

* fix(edge-group): change empty associated endpoint text

* fix(edge-compute): add missing relations updates (#3817)

* fix(endpoint): remove deleted endpoint from edge group

* fix(tags): remove deleted tag from edge group

* fix(endpoint): remove deleted endpoint from edge stack

* fix(edge-groups): remove clearing of edgeGroup fields

* fix(edge-groups): show dynamic edge groups without tags

* fix(edge-compute): use sequential delete in resources (#3818)

* fix(endpoints): delete endpoints on by one

* fix(tags): remove tags one by one

* fix(groups): remove endpoint groups one by one

* fix(edge-stacks): remove stack one by one

* fix(edge-groups): remove edge group one by one

* fix(edge-stacks): add link to root in breadcrumbs

* style(edge): add empty line after errors

* refactor(tags): remove old function

* refactor(endpoints): revert changes to multi-endpoint-selector

* feat(edge-stacks): support Edge stack templates (#3812)

* feat(edge-compute): fetch templates from url

* feat(edge-stacks): fetch edge templates

* feat(edge-stacks): choose template and save

* feat(edge-stacks): add placeholder to templates select

* feat(edge-templates): show info

* fix(edge-stacks): fix typo

* feat(edge-templates): replace template url

* feat(edge-compute): use custom url if available

* fix(edge-stacks): show error message when failing

* feat(edge-compute): show description in template

* feat(edge-templates): change access to route

* style(edge-compute): change EdgeTemplatesURL description

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
Co-authored-by: itsconquest <william.conquest@portainer.io>
Co-authored-by: Ben Brooks <ben@bbrks.me>
Co-authored-by: Neil Cresswell <neil@cresswell.net.nz>
2020-05-14 14:14:28 +12:00
Anthony Lapenna
8e09b935cd feat(cli): add deprecation warnings (#3826) 2020-05-13 16:21:17 +12:00
Anthony Lapenna
9dcd223134 feat(stacks): prevent external stack removal by a non-administrator user (#3800)
* fix(stacks): prevent external stacks removal by non admin

* feat(stacks): add RBAC checks for external stack removals

Co-authored-by: Maxime Bajeux <max.bajeux@gmail.com>
2020-05-13 15:37:35 +12:00
Anthony Lapenna
29c0584454 fix(api): update restricted volume browsing operation logic (#3798)
* fix(api): prevent a potential panic

* fix(api): update restricted volume browsing operation logic
2020-05-12 16:08:01 +12:00
Anthony Lapenna
5c274f5b0c docs(readme): update README 2020-05-12 10:30:12 +12:00
Maxime Bajeux
b3af91cea3 feat(volumes): Revise the UX for creation of NFS volumes (#3815)
* feat(volumes): Revise the UX for creation of NFS volumes

* feat(volume-creation): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-05-12 10:23:42 +12:00
Anthony Lapenna
c8f55ac896 feat(project): update pull-dog configuration 2020-05-11 11:23:56 +12:00
Anthony Lapenna
659e4486db feat(project): add pull-dog config 2020-05-11 09:31:07 +12:00
pull-dog-user
cc091ee589 feat(project): enable Pull Dog for the repository (#3801)
* Create pull-dog.json

* Create docker-compose.pull-dog.yml

* Update pull-dog.json

* Update docker-compose.pull-dog.yml
2020-05-11 09:25:30 +12:00
Maxime Bajeux
8046fb0438 fix(volumes): add unicity check on volumes (#3779)
* fix(volumes): add unicity check on volumes

* fix(volumes): add header to volume creation request

* fix(volumes): change few things
2020-05-09 09:40:49 +12:00
Chaim Lev-Ari
7fa73d1147 fix(endpoints): show line breaks in edge run commands (#3794) 2020-05-06 17:45:17 +12:00
Shivam Goyal
bfd6cca33f feat(image-build): fix typo (#3787) 2020-05-04 19:54:09 +12:00
Maxime Bajeux
7fe7ce1a0a fix(container-creation): ensure exposedPorts exists (#3770) 2020-04-29 14:33:37 +12:00
Maxime Bajeux
7f0ce61413 feat(networks): remove the ability to create host network (#3771) 2020-04-29 14:05:32 +12:00
Maxime Bajeux
3de533042d feat(networks): add ipv6 support (#3717)
* feat(portainer-core): add ipv6 support

* feat(networks): add few changes

* refacto(networks): write regex once

* fix(networks): fix indentation

* refacto(networks): use foreach instead map and pluralize ipvxconfig

* refacto(networks): pluralize ipvxconfig

* feat(networks): support ipv6 with ports

* feat(networks): add an explicit error message

* fix(networks): hide ipv6 configuration when creating macvlan
2020-04-28 12:34:54 +12:00
Dan Underwood
b2f36a3bbe fix(networking): convert from localhost addresses to 127.0.0.1 (#3411) 2020-04-27 13:54:37 +12:00
Anthony Lapenna
3d5bdab620 feat(project): add alpine based Dockerfile (#3759) 2020-04-27 13:46:12 +12:00
Anthony Lapenna
fee20248ea Update CONTRIBUTING.md 2020-04-21 11:45:01 +12:00
Mariell
f525c8d022 feat(container-creation): add support for --init (#2111) (#3714) 2020-04-15 13:09:42 +12:00
Chaim Lev-Ari
bba622a500 chore(eslint): add rule to sort imports (#3715)
* chore(eslint): add plugin to sort imports

* chore(eslint): sort imports

* chore(eslint): add eslint-config-prettier
2020-04-15 11:46:34 +12:00
Chaim Lev-Ari
cf5056d9c0 chore(project): add prettier for code format (#3645)
* chore(project): install prettier and lint-staged

* chore(project): apply prettier to html too

* chore(project): git ignore eslintcache

* chore(project): add a comment about format script

* chore(prettier): update printWidth

* chore(prettier): remove useTabs option

* chore(prettier): add HTML validation

* refactor(prettier): fix closing tags

* feat(prettier): define angular parser for html templates

* style(prettier): run prettier on codebase

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-04-11 09:54:53 +12:00
Neil Cresswell
6663073be1 Update README.md 2020-04-09 10:01:20 +12:00
Neil Cresswell
18a38d597a Update README.md 2020-04-09 10:00:29 +12:00
Neil Cresswell
aeea88be36 Update README.md 2020-04-09 09:59:14 +12:00
Chaim Lev-Ari
6da38d466b refactor(project): sort portainer types and interface definitions (#3694)
* refactor(portainer): sort types

* style(portainer): add comment about role service

* refactor(portainer): sort interface types

* refactor(portainer): sort enums
2020-04-09 09:26:11 +12:00
Chaim Lev-Ari
2542d30a09 feat(endpoints): filter by ids and/or tag ids (#3690)
* feat(endpoints): add filter by tagIds

* refactor(endpoints): change endpoints service to query by tagIds

* fix(endpoints): filter by tags

* feat(endpoints): filter by endpoint groups tags

* feat(endpoints): filter by ids
2020-04-08 21:14:50 +12:00
Chaim Lev-Ari
df13f3b4cc chore(yarn): add start:client script back (#3691) 2020-04-08 21:03:52 +12:00
Chaim Lev-Ari
db8b3d6e5a create tag from tag selector (#3640)
* feat(tags): add button to save tag when doesn't exist

* feat(endpoints): allow the creating of tags in endpoint edit

* feat(groups): allow user to create tags in create group

* feat(groups): allow user to create tags in edit group

* feat(endpoint): allow user to create tags from endpoint create

* feat(tags): allow the creation of a new tag from dropdown

* feat(tag): replace "add" with "create"

* feat(tags): show tags input when not tags

* feat(tags): hide create message when not allowed

* refactor(tags): replace component controller with class

* refactor(tags): replace native methods with lodash

* refactor(tags): remove unused onChangeTags function

* refactor(tags): remove on-change binding

* style(tags): remove white space

* refactor(endpoint-groups): move controller to separate file

* fix(groups): allow admin to create tag in group form

* refactor(endpoints): wrap async function with try catch and $async

* style(tags): wrap arrow function args with parenthesis

* refactor(endpoints): return $async functions

* refactor(tags): throw error in the format Notification expects
2020-04-08 19:56:24 +12:00
Chaim Lev-Ari
dd6262cf69 chore(yarn): change start:client to start webpack dev server (#3595)
* chore(yarn): change start:client to start webpack dev server

* Update package.json

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
2020-04-06 09:06:59 +12:00
Chaim Lev-Ari
edd86f2506 refactor(tags): refactor tag management (#3628)
* refactor(tags): replace tags with tag ids

* refactor(tags): revert tags to be strings and add tagids

* refactor(tags): enable search by tag in home view

* refactor(tags): show endpoint tags

* refactor(endpoints): expect tagIds on create payload

* refactor(endpoints): expect tagIds on update payload

* refactor(endpoints): replace TagIds to TagIDs

* refactor(endpoints): set endpoint group to get TagIDs

* refactor(endpoints): refactor tag-selector to receive tag-ids

* refactor(endpoints): show tags in multi-endpoint-selector

* chore(tags): revert reformat

* refactor(endpoints): remove unneeded bind

* refactor(endpoints): change param tags to tagids in endpoint create

* refactor(endpoints): remove console.log

* refactor(tags): remove deleted tag from endpoint and endpoint group

* fix(endpoints): show loading label while loading tags

* chore(go): remove obsolete import labels

* chore(db): add db version comment

* fix(db): add tag service to migrator

* refactor(db): add error checks in migrator

* style(db): sort props in alphabetical order

* style(tags): fix typo

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

* refactor(endpoints): replace tagsMap with tag string representation

* refactor(tags): rewrite tag delete to be more readable

* refactor(home): rearange code to match former style

* refactor(tags): guard against missing model in tag-selector

* refactor(tags): rename vars in tag_delete

* refactor(tags): allow any authenticated user to fetch tag list

* refactor(endpoints): replace controller function with class

* refactor(endpoints): replace function with helper

* refactor(endpoints): replace controller with class

* refactor(tags): revert tags-selector to use 1 way bindings

* refactor(endpoints): load empty tag array instead of nil

* refactor(endpoints): revert default tag ids

* refactor(endpoints): use function in place

* refactor(tags): use lodash

* style(tags): use parens in arrow functions

* fix(tags): remove tag from tag model

Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
2020-03-29 22:54:14 +13:00
Ben Brooks
fe89a4fc01 fix(services): enforce minimum replica count of 0 (#3653)
* fix(services): enforce minimum replica count of 0

Fixes #3652

Prevents replica count from being set below zero and causing an error.

* fix(services): enforce replica count is an integer

Prevents users entering decimals in the replica count
2020-03-29 13:25:50 +13:00
Ben Brooks
00bef100ee chore(assets): double UI image resolutions for HiDPI displays (#3648)
Fixes #3069

Prevents users seeing blurry logos and other images when using a hidpi
display (like scaled 4k, or a Retina display).

These images have been recreated manually with 2x the original
resolution but should resemble the originals as much as possible.

They have also been run through pngcrush for compression.
2020-03-27 16:49:20 +13:00
Chaim Lev-Ari
ae7f46c8ef feat(endpoints): filter by endpoint type (#3646) 2020-03-26 18:44:27 +13:00
Anthony Lapenna
78558f9c8e chore(version): bump version number 2020-03-20 11:37:35 +13:00
Anthony Lapenna
5a3caab9c4 Merge tag '1.23.2' into develop
Release 1.23.2
2020-03-20 11:35:49 +13:00
Anthony Lapenna
5396a069f2 Merge branch 'release/1.23.2' 2020-03-20 11:35:43 +13:00
Anthony Lapenna
2a92fcb802 chore(version): bump version number 2020-03-20 11:35:36 +13:00
itsconquest
2c400eb3b4 feat(support): make support type dynamic (#3621) 2020-03-19 09:38:56 +13:00
Anthony Lapenna
a11a348893 fix(containers): do not persist container status filter (#3615) 2020-03-16 14:37:59 +13:00
itsconquest
d022853059 feat(support): add new offerings (#3608)
* feat(support): add new offerings

* feat(support): refactor for simplicity

* feat(support): rename for clarity
2020-03-16 11:35:55 +13:00
xAt0mZ
bfdb4dba12 fix(container-creation): validate runtime property (#3581)
Co-authored-by: linquize <linquize2@yahoo.com>
2020-02-26 16:31:59 +13:00
William
8d7bae0560 fix(dependencies): bump go-winio lib (#3569)
* fix(dependencies): bump go-winio lib

* fix(api): update gomod

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-02-25 18:56:34 +13:00
Anthony Lapenna
e0d83db609 fix(authentication/ldap): fix an issue with authorizations not updated after ldap login (#3577) 2020-02-25 18:54:32 +13:00
Steven Kang
ad5f51964c fix(build): Remove -ErrorAction:SilentlyContinue (#3571) 2020-02-22 18:18:09 +13:00
Anthony Lapenna
9cc8448418 chore(version): bump version number 2020-02-20 09:24:06 +13:00
Anthony Lapenna
b2cc6be007 fix(api): update gomod 2020-02-20 08:20:45 +13:00
Anthony Lapenna
be0b01611f Merge tag '1.23.1' into develop
Release 1.23.1
2020-02-18 13:46:17 +13:00
Anthony Lapenna
bcda7e2d7e Merge branch 'release/1.23.1' 2020-02-18 13:46:10 +13:00
Anthony Lapenna
d0e998ddc4 chore(version): bump version number 2020-02-18 13:46:04 +13:00
William
1f7e5fec4f fix(settings/authentication): patch LDAP connectivity check (#3561) 2020-02-18 12:25:30 +13:00
Anthony Lapenna
d3a625e22f feat(api): update gomod 2020-02-15 09:09:45 +13:00
William
eff1b79a4a fix(networks): patch datatable (#3557) 2020-02-12 20:41:41 +01:00
William
0330b16776 fix(container-create): patch dns (#3556) 2020-02-11 15:23:56 +01:00
William
97a0ea4a31 fix(templates): select volume name not object (#3549) 2020-02-06 09:07:23 +13:00
William
167d4319b5 fix(authentication): frontend changes (#3456)
* fix(authentication): frontend changes

* fix(authentication): suggested changes

* fix(authentication): support AnonymousMode boolean

* feat(authentication): support empty vals + move from scope to formValues

* feat(authentication): allow test of TLS & anon

* feat(authentication): remove unneeded whitespace

* feat(authentication): remove un-needed whitespace

* feat(refactor): rebase + cleanup logic
2020-02-06 09:06:22 +13:00
Mike Church
6f59f130a1 feat(dashboard): add health status to home page and dashboard (#3489)
* feat(dashboard): add health status to home page and dashboard

* fix(dashboard): code review updates, using builtin for substring search
2020-02-05 07:59:29 +13:00
William
cc8d3c8639 refactor(UX): disable/remove uneeded UI elements (#3530)
* refactor(UX): disable/remove uneeded UI elements

* refactor(UX): rm missed th in container network

* refactor(UX): minor formatting improvement
2020-01-31 16:49:59 +01:00
Hugo Hromic
f4c461d7fb feat(settings/ldap): preserve ReaderDN in database if empty in settings payload (#3537)
* Allows to avoid changing any current value when using LDAP Anonymous Mode
2020-01-31 13:17:19 +13:00
William
6c492d2290 fix(UX): prevent task expand on row selection (#3531)
* fix(UX): prevent task expand on row selection

* refactor(UX): apply service expand logic to networks
2020-01-29 14:44:02 +01:00
William
8bea0988dd fix(api): lower Docker client API version for backwards support (#3534) 2020-01-29 17:36:28 +13:00
William
8dda67c8d0 refactor(UX): fix grammar (#3528) 2020-01-27 14:02:04 +13:00
William
7365afa1bb feature(UX): sort dropdowns alphabetically (#3524) 2020-01-25 09:53:48 +13:00
Kirill K
1ef29f2671 feat: add setting to change DNS servers (#3511)
* feat: add setting to change DNS servers

* style: fixing codeclimate warning

Looks like conditional was excessive, it works as expected even without
it.

* style: rename 'DNS Server 1/2' to 'Primary/Secondary DNS Server'

Signed-off-by: Kirill K <kovalev.kirill.a@gmail.com>

* style: rename variables in code to match UI naming

* feat: add tooltips on DNS servers input fields

Signed-off-by: Kirill K <kovalev.kirill.a@gmail.com>

* Revert "feat: add tooltips on DNS servers input fields"

This reverts commit b83ef50825.

* style: secondary DNS placeholder

Signed-off-by: Kirill K <kovalev.kirill.a@gmail.com>
2020-01-24 15:49:28 +01:00
William
fa5bb9b1be feat(stack-creation): add note for 2FA (#3509)
* refactor(stack-creation): add note for 2FA

* Update app/portainer/views/stacks/create/createstack.html

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
2020-01-23 09:17:04 +13:00
Hugo Hromic
2ba195adaa feat(api): implement anonymous mode for LDAP connection (#3460)
* When enabled, ReaderDN and Password will not be used
* Anonymous mode is set to `true` by default on fresh installations
2020-01-22 11:14:07 +13:00
William
9da08bc792 refactor(endpoint-creation): remove unneeded port (#3467)
* refactor(endpoint-creation): remove unneeded port

* refactor(endpoint-creation): remove from clipboard
2020-01-22 09:17:41 +13:00
William
17bc17f638 fix(api): fix an issue with ownership for services and stacks (#3512) 2020-01-21 08:09:30 +13:00
William
efae49d92b chore(project): require Portainer logs (#3508) 2020-01-19 18:46:40 +13:00
Iceyer
58c00401e9 fix: atob convert unicode in config failed (#3415)
atob only support decode ascii char, when this unicode in config, it will show unknown char.
2019-12-09 17:52:02 +01:00
Anthony Lapenna
e9f6861df0 feat(api): add go module support (#3408)
* feat(api): experiment with go mod

* feat(api): experiment with go mod

* feat(api): experiment with go mod

* feat(api): add go module support

* refactor(api): go fmt
2019-12-05 17:02:27 +13:00
Anthony Lapenna
bba13f69ad chore(version): bump version number 2019-12-04 17:07:21 +13:00
Anthony Lapenna
36020dd8bc Merge tag '1.23.0' into develop
Release 1.23.0
2019-12-04 17:05:49 +13:00
Anthony Lapenna
b7eca7ce17 Merge branch 'release/1.23.0' 2019-12-04 17:05:42 +13:00
Anthony Lapenna
2189deb3bd chore(version): bump version number 2019-12-04 17:05:36 +13:00
Anthony Lapenna
29b7eeef5a fix(api): trigger an authorization update after auto-provisioning users (#3428) 2019-12-04 15:32:55 +13:00
Anthony Lapenna
f6cefb3318 fix(api): fix invalid method call for docker_windows proxy 2019-12-04 12:54:11 +13:00
Anthony Lapenna
a42619a442 fix(api): fix invalid extensions.json location 2019-12-04 11:42:43 +13:00
xAt0mZ
1465825988 feat(container): disable recreate/duplicate buttons with RBAC for non admins (#3426) 2019-12-04 10:47:07 +13:00
xAt0mZ
2d576394d0 fix(registry-selector): unique images in auto suggest (#3425) 2019-12-04 10:46:03 +13:00
William
f79dae3e27 feat(about): add analytics notice (#3423) 2019-12-04 08:05:00 +13:00
Anthony Lapenna
badb6ee50f fix(http): update volume browsing validation (#3416) 2019-12-03 10:42:55 +13:00
Anthony Lapenna
c2e1129804 feat(extensions): update offline manifest 2019-12-03 09:50:10 +13:00
Mohab Abd El-Dayem
3b1a8e4bba feat(cli): remove the logging of the hashed password of the admin user (#3328) 2019-11-29 09:23:18 +13:00
William
dd0c80e915 fix(container-creation): preserve aliases if null (#3405)
* fix(container-creation): preserve aliases if null

* Update app/docker/views/containers/create/createContainerController.js
2019-11-29 09:21:23 +13:00
William
5ab63bd151 fix(container-create): patch aliases (#3403)
* fix(container-create): patch aliases

* Update app/docker/views/containers/create/createContainerController.js
2019-11-28 17:38:53 +13:00
xAt0mZ
ea1ca76f70 fix(auth): clean browser cache on logout (#3402) 2019-11-28 12:16:34 +13:00
xAt0mZ
e19bc8abc7 fix(app): registry push-pull features overhaul (#3393)
* feat(registry): registry or direct url selector

* feat(app): push pull container creation

* feat(app): push pull container duplicate

* feat(app): push pull container details recreate

* feat(app): push pull container details commit

* feat(app): push pull images

* feat(app): push pull image tag

* feat(app): push pull image push

* feat(app): push pull image pull

* feat(app): push pull service creation

* feat(app): push pull templates create container

* feat(app): push pull templates create stacks

* feat(app): push pull template edit

* feat(app): push pull service details update

* fix(app): refactor registry selector + registry auto select

* feat(app): remove autocomplete on registry selector

* style(image-registry): reword simple/advanced mode

* Revert "feat(app): remove autocomplete on registry selector"

This reverts commit 97ec2ddd62.

* refactor(registry-selector): reverse registry and image fields

* feat(app): autocomplete on registry selector

* feat(registry-selector): change gitlab registry autocomplete

* feat(registry-selector): autocomplete for dockerhub

* feat(registry-selector): gitlab url based on locked value instead of name

* fix(registry-selector): gitlab registries URL are not modified anymore

* fix(registry-selector): change gitlab image autofill on duplicate

* fix(registry-selector): gitlab registries now only suggest their own images and not all from gitlab

* fix(registry-selector): psuh pull issues with gitlab registries

* fix(registry-selector): dockerhub registry selection on duplicate for dockerhub images

* fix(templates): registry retrieval for template

* feat(images): add autocomplete on image pull panel

* fix(registry-selector): add latest tag when no tag is specified

* fix(registry-selector): latest tag now applied for non gitlab registries
2019-11-28 11:36:39 +13:00
William
61c38534a7 fix(container-creation): preserve network aliases (#3401) 2019-11-28 09:35:14 +13:00
William
7f54584ed6 fix(container-creation): match container_network by id (#3398) 2019-11-27 09:19:22 +13:00
xAt0mZ
1a65dbf85f fix(app): permissions lost for UI on browser refresh (#3354)
* fix(app): permissions lost for UI on browser refresh

* fix(app): permissions retrieval moved to global app resolve
2019-11-26 17:01:39 +13:00
William
a3a83d1d7e fix(container): hide opts when autoremove true (#3397) 2019-11-26 08:08:48 +13:00
KemoNine
a41ca1fd46 feat(container-creation): allow empty value for labels (#2655)
* Allow empty values for labels

* Allow empty labels when creating containers from a template
2019-11-25 13:25:30 +13:00
Anthony Lapenna
130c188717 fix(libcompose): apply same normalize name rule as libcompose on stack name (#3395) 2019-11-24 14:28:07 +13:00
Anthony Lapenna
a85f0058ee feat(extensions): add the ability to upload and enable an extension (#3345)
* feat(extensions): offline mode mockup

* feat(extensions): offline mode mockup

* feat(api): add support for extensionUpload API operation

* feat(extensions): offline extension upload

* feat(api): better support for extensions in offline mode

* feat(extension): update offline description

* feat(api): introduce local extension manifest

* fix(api): fix LocalExtensionManifestFile value

* feat(api): use a 5second timeout for online extension infos

* feat(extensions): add download archive link

* feat(extensions): add support for offline update

* fix(api): fix issues with offline install and online updates of extensions

* fix(extensions): fix extensions link URL

* fix(extension): hide screenshot in offline mode
2019-11-20 18:16:40 +13:00
Anthony Lapenna
8b0eb71d69 feat(api): automatically update extensions at startup (#3349)
* feat(api): automatically update extensions at startup

* feat(api): review updateAndStartExtensions
2019-11-20 18:02:07 +13:00
Anthony Lapenna
1f90a091a8 feat(api): bind extensions stdout and stderr to current process (#3375) 2019-11-20 14:08:16 +13:00
Anthony Lapenna
b8be795505 feat(templates): replace volume selector with typeahead (#3371) 2019-11-20 12:18:27 +13:00
Anthony Lapenna
4239db7b34 fix(api): remove roles associated to access policies after removing RBAC extension (#3373) 2019-11-20 11:58:26 +13:00
Anthony Lapenna
81c0bf0632 fix(api): introduce priority based logic for RBAC roles (#3374)
* fix(api): introduce priority based logic for RBAC roles

* refactor(api): rename method
2019-11-18 21:22:47 +13:00
Anthony Lapenna
9decbce511 chore(version): bump version number 2019-11-14 17:50:11 +13:00
Anthony Lapenna
914b46f813 fix(api): introduce gitlab proxy package 2019-11-13 13:12:55 +13:00
Anthony Lapenna
19d4db13be feat(api): rewrite access control management in Docker (#3337)
* feat(api): decorate Docker resource creation response with resource control

* fix(api): fix a potential resource control conflict between stacks/volumes

* feat(api): generate a default private resource control instead of admin only

* fix(api): fix default RC value

* fix(api): update RC authorizations check to support admin only flag

* refactor(api): relocate access control related methods

* fix(api): fix a potential conflict when fetching RC from database

* refactor(api): refactor access control logic

* refactor(api): remove the concept of DecoratedStack

* feat(api): automatically remove RC when removing a Docker resource

* refactor(api): update filter resource methods documentation

* refactor(api): update proxy package structure

* refactor(api): renamed proxy/misc package

* feat(api): re-introduce ResourceControlDelete operation as admin restricted

* refactor(api): relocate default endpoint authorizations

* feat(api): migrate RBAC data

* feat(app): ResourceControl management refactor

* fix(api): fix access control issue on stack deletion and automatically delete RC

* fix(api): fix stack filtering

* fix(api): fix UpdateResourceControl operation checks

* refactor(api): introduce a NewTransport builder method

* refactor(api): inject endpoint in Docker transport

* refactor(api): introduce Docker client into Docker transport

* refactor(api): refactor http/proxy package

* feat(api): inspect a Docker resource labels during access control validation

* fix(api): only apply automatic resource control creation on success response

* fix(api): fix stack access control check

* fix(api): use StatusCreated instead of StatusOK for automatic resource control creation

* fix(app): resource control fixes

* fix(api): fix an issue preventing administrator to inspect a resource with a RC

* refactor(api): remove useless error return

* refactor(api): document DecorateStacks function

* fix(api): fix invalid resource control type for container deletion

* feat(api): support Docker system networks

* feat(api): update Swagger docs

* refactor(api): rename transport variable

* refactor(api): rename transport variable

* feat(networks): add system tag for system networks

* feat(api): add support for resource control labels

* feat(api): upgrade to DBVersion 22

* refactor(api): refactor access control management in Docker proxy

* refactor(api): re-implement docker proxy taskListOperation

* refactor(api): review parameters declaration

* refactor(api): remove extra blank line

* refactor(api): review method comments

* fix(api): fix invalid ServerAddress property and review method visibility

* feat(api): update error message

* feat(api): update restrictedVolumeBrowserOperation method

* refactor(api): refactor method parameters

* refactor(api): minor refactor

* refactor(api): change Azure transport visibility

* refactor(api): update struct documentation

* refactor(api): update struct documentation

* feat(api): review restrictedResourceOperation method

* refactor(api): remove unused authorization methods

* feat(api): apply RBAC when enabled on stack operations

* fix(api): fix invalid data migration procedure for DBVersion = 22

* fix(app): RC duplicate on private resource

* feat(api): change Docker API version logic for libcompose/client factory

* fix(api): update access denied error message to be Docker API compliant

* fix(api): update volume browsing authorizations data migration

* fix(api): fix an issue with access control in multi-node agent Swarm cluster
2019-11-13 12:41:42 +13:00
xAt0mZ
198e92c734 feat(registry): gitlab support (#3107)
* feat(api): gitlab registry type

* feat(registries): early support for gitlab registries

* feat(app): registry service selector

* feat(registry): gitlab support : list repositories and tags - remove features missing

* feat(registry): gitlab registry remove features

* feat(registry): gitlab switch to registry V2 API for repositories and tags

* feat(api): use development extension binary

* fix(registry): avoid 401 on gitlab retrieve to disconnect the user

* feat(registry): gitlab browse projects without extension

* style(app): code cleaning

* refactor(app): PR review changes + refactor on types

* fix(gitlab): remove gitlab info from registrymanagementconfig and force gitlab type

* style(api): go fmt

* feat(api): update APIVersion and ExtensionDefinitionsURL

* fix(api): fix invalid RM extension URL

* feat(registry): PAT scope help

* feat(registry): defaults on registry creation

* style(registry-creation): update layout and text for Gitlab registry

* feat(registry-creation): update gitlab notice
2019-11-12 16:28:31 +13:00
Anthony Lapenna
03d9d6afbb Revert "fix(api): fix invalid resource control check (#3225)" (#3327)
This reverts commit 1fbe6a12f1.
2019-11-01 17:46:53 +13:00
George Cheng
c559b6b55c fix(container-creation): Fix bad env in container creation (#2996)
Currently we are using RegExp `/\=(.+)/` to catch key-value
of environment variables, which could not match empty-value
environment variables such as `KEY=`.

This commit will change the RegExp to `/\=(.*)/`, which
matches the empty values.
2019-11-01 16:15:33 +13:00
Anthony Lapenna
0175490161 fix(api): data migration to update default Portainer authorizations (#3314) 2019-10-31 12:12:04 +13:00
Anthony Lapenna
310b6b34da fix(api): update user authorizations after team deletion (#3315) 2019-10-31 08:46:50 +13:00
Anthony Lapenna
07db1ca16e feat(test): update e2e to support swarm and CI mode 2019-10-29 12:51:26 +13:00
Anthony Lapenna
36de0aee7b feat(test): update e2e setup 2019-10-29 11:38:38 +13:00
Anthony Lapenna
c6e9d8e616 feat(test): update docker-compose file for cypress e2e testing 2019-10-28 16:51:59 +13:00
Anthony Lapenna
dbef3a0508 feat(test): update cypress projectId 2019-10-28 15:29:32 +13:00
William
91c83eccd2 feat(project): add automated testing with cypress (#3305)
* feat(project): add automated testing with cypress

* feat(project): made suggested edits

* feat(project): add init test

* feat(project): add socket to correct container
2019-10-25 18:53:29 +13:00
William
542b76912a feat(endpoint-details): add edge-key to commands (#3302) 2019-10-24 16:36:24 +02:00
Aaron Korte
53942b741a fix(api): increment stack identifier atomically (#3290) 2019-10-24 11:38:41 +13:00
Mattias Edlund
accca0f2a6 feat(containers): added support for port range mappings when deploying containers (#3194)
* feat(containers): added support for port range mappings when deploying containers

* feat(containers): added placeholders to port publishing input fields

* feat(containers): added a tooltip to the manual network port publishing

* feat(containers): improved the code consistency
2019-10-15 11:13:57 +02:00
xAt0mZ
f67e866e7e feat(registry): inspect repository images (#3121)
* feat(registry): inspect repository images

* fix(registry): tag inspect column sorting
2019-10-14 15:46:33 +02:00
xAt0mZ
2445a5aed5 fix(registry): Performance issues with Registry Manager (#2648)
* fix(registry): fetch datatable details on page/filter/order state change instead of fetching all data on first load

* fix(registry): fetch tags datatable details on state change instead of fetching all data on first load

* fix(registry): add pagination support for tags + loading display on data load

* fix(registry): debounce on text filter to avoid querying transient matching values

* refactor(registry): rebase on latest develop

* feat(registries): background tags and optimisation -- need code cleanup and for-await-of to cancel on page leave

* refactor(registry-management): code cleanup

* feat(registry): most optimized version -- need fix for add/retag

* fix(registry): addTag working without page reload

* fix(registry): retag working without reload

* fix(registry): remove tag working without reload

* fix(registry): remove repository working with latest changes

* fix(registry): disable cache on firefox

* feat(registry): use jquery for all 'most used' manifests requests

* feat(registry): retag with progression + rewrite manifest REST service to jquery

* fix(registry): remove forgotten DI

* fix(registry): pagination on repository details

* refactor(registry): info message + hidding images count until fetch has been done

* fix(registry): fix selection reset deleting selectAll function and not resetting status

* fix(registry): resetSelection was trying to set value on a getter

* fix(registry): tags were dropped when too much tags were impacted by a tag removal

* fix(registry): firefox add tag + progression

* refactor(registry): rewording of elements

* style(registry): add space between buttons and texts in status elements

* fix(registry): cancelling a retag/delete action was not removing the status panel

* fix(registry): tags count of empty repositories

* feat(registry): reload page on action cancel to avoid desync

* feat(registry): uncancellable modal on long operations

* feat(registry): modal now closes on error + modal message improvement

* feat(registries): remove empty repositories from the list

* fix(registry): various bugfixes

* feat(registry): independant timer on async actions + modal fix
2019-10-14 15:45:09 +02:00
xAt0mZ
8a8cef9b20 feat(deps): multiselect library as dependency (#3255) 2019-10-14 15:43:58 +02:00
xAt0mZ
e20a139c5a fix(registry): remove checkboxes on repositories list (#3109) 2019-10-14 15:43:27 +02:00
Tim van den Eijnden
774380fb44 chore(icons): update fontawesome dependency (#3219) 2019-10-14 15:40:19 +02:00
Anthony Lapenna
3632e07654 Merge tag '1.22.1' into develop
Release 1.22.1
2019-10-11 10:40:58 +13:00
Anthony Lapenna
80ad5079f7 Merge branch 'release/1.22.1' 2019-10-11 10:40:52 +13:00
Anthony Lapenna
4fad28590d chore(version): bump version number 2019-10-11 10:40:41 +13:00
Soham Mondal
8de507a15d feat(container-details): add entrypoint to container details view (#3120)
* feat(container-details): add entrypoint to container details view

* feat(container-details): restore file from develop branch to bring back original indentation

* feat(container-details): add entrypoint to container details view
2019-10-10 17:47:25 +13:00
Anthony Lapenna
19810b9f4e fix(build-system): fix build system on CI for Windows (#3250)
* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows

* fix(build-system): fix build system on CI for Windows
2019-10-10 16:29:00 +13:00
Anthony Lapenna
ab2acea463 feat(app): add externally sourced support options (#3249)
* feat(app): add externally sourced support options

* refactor(api): rename struct fields
2019-10-10 10:59:27 +13:00
Anthony Lapenna
521a36e629 fix(api): fix missing default settings for LDAPSettings 2019-10-10 10:28:17 +13:00
Anthony Lapenna
182f3734d0 fix(api): fix an issue with unsupported cron format (#3240)
* fix(api): fix an issue with unsupported cron format

* refactor(api): review migration method
2019-10-08 16:18:32 +13:00
Anthony Lapenna
d717ad947b feat(api): remove cron second support 2019-10-08 14:39:37 +13:00
Anthony Lapenna
9aa52a6975 feat(settings): add new settings to disable volume browser (#3239)
* feat(settings): add new settings to disable volume browser

* feat(api): update setting to be compliant with RBAC

* refactor(api): update method comment

* fix(api): remove volume browsing authorizations by default

* feat(settings): rewrite volume management setting description

* feat(settings): rewrite volume management setting tooltip

* Update app/portainer/views/settings/settings.html

Co-Authored-By: William <william.conquest@portainer.io>
2019-10-08 13:17:58 +13:00
firecyberice
ef4c138e03 fix(authentication): trim the newline character from the password string (#3091) 2019-10-08 11:52:37 +13:00
Anthony Lapenna
68fe5d6906 fix(api): fix invalid restriction on StatusInspectVersion 2019-10-08 11:45:16 +13:00
Anthony Lapenna
b0f48ee3ad feat(app): fix XSS vulnerabilities (#3230) 2019-10-07 16:24:48 +13:00
Anthony Lapenna
2912e78f68 fix(api): add access validation for agent browse requests (#3235)
* fix(api): add access validation for agent browse requests

* fix(api): review query parameter retrieval

* refactor(api): remove useless else case
2019-10-07 16:24:08 +13:00
Anthony Lapenna
fb6f6738d9 fix(api): prevent the use of bind mounts in stacks if setting enabled (#3232) 2019-10-07 16:12:21 +13:00
Anthony Lapenna
f7480c4ad4 feat(api): prevent non administrator users to use admin restricted API endpoints (#3227) 2019-10-07 16:10:51 +13:00
Anthony Lapenna
1fbe6a12f1 fix(api): fix invalid resource control check (#3225) 2019-10-07 16:09:35 +13:00
Anthony Lapenna
b7c38b9569 feat(api): trigger user authorization update when required (#3213)
* refactor(api): remove useless type cast

* feat(api): trigger user authorization update when required

* fix(api): fix missing RegistryService injection
2019-10-07 15:42:01 +13:00
Frans-Jan van Steenbeek
6c996377f5 fix(container-creation): prevent duplicate MAC addresses after edit (#1645) (#2993) 2019-10-03 15:37:34 +13:00
William
81e9484dd3 docs(project): add security info to readme (#3211)
* docs(project): add security info to readme

* docs(project): fix whitespace in previous commit
2019-10-03 13:03:14 +13:00
Anthony Lapenna
3ab0422361 Revert "feat(build-system): bump Docker binary version to 19.03.2 (#3202)" (#3210)
This reverts commit ed70d0fb2b.
2019-10-03 11:23:07 +13:00
Anthony Lapenna
d4fa4d8a52 fix(api): always persist data after initial extension check 2019-09-30 14:03:59 +13:00
Pierre Kisters
ed70d0fb2b feat(build-system): bump Docker binary version to 19.03.2 (#3202) 2019-09-30 10:22:04 +13:00
Anthony Lapenna
ea05d96c73 feat(sidebar): add update notification (#3196)
* feat(sidebar): add update notification

* style(sidebar): update notification color palette

* refactor(api): rollback to latest version

* feat(sidebar): update style

* style(sidebar): fix color override
2019-09-26 08:38:11 +12:00
xAt0mZ
b034a60724 fix(auth): authController full rewrite (#3173)
* fix(auth): authController full rewrite

fixes 2 bugs caused by legacy code

* fix(auth): moving state to cookies for Firefox private browsing

* fix(auth): clean query params on OAuth response
2019-09-25 13:36:24 +12:00
Anthony Lapenna
646038cd0f feat(exec): add DEBUG statement when validating license (#3191) 2019-09-24 17:06:08 +12:00
Anthony Lapenna
42d4e1e11c fix(api): prevent panic in auth when OAuth is enabled (#3179) 2019-09-24 11:03:44 +12:00
Anthony Lapenna
b84fa9db2f feat(build-system): remove VOLUME statement from Windows Dockerfile (#3181) 2019-09-20 16:38:43 +12:00
Anthony Lapenna
7509283072 fix(home): refresh the view on endpoint ping failure (#3161)
* fix(api): remove automatic backend failure for Down endpoints

* fix(home): refresh the view on endpoint ping failure
2019-09-20 16:14:44 +12:00
Anthony Lapenna
1f68aad07f feat(api): prevent endpoint creation with already paired agent (#3159) 2019-09-20 16:14:19 +12:00
Anthony Lapenna
07505fabcc fix(api): remove automatic backend failure for Down endpoints (#3160) 2019-09-20 16:13:58 +12:00
Anthony Lapenna
a5e5983c28 feat(api): only error on ping failure for snapshots (#3177) 2019-09-20 16:13:44 +12:00
Anthony Lapenna
baa64ca927 refactor(api): update scheduler to match new cron lib API (#3157) 2019-09-15 10:47:44 +12:00
William
8e922dbfc6 fix(endpoint-creation): clarify docker endpoint (#3148)
* fix(endpoint-creation): clarify docker endpoint

* fix(endpoint-creation): change default order
2019-09-11 07:24:29 +12:00
Anthony Lapenna
7d76bc89e7 feat(api): relocate authorizations outside of JWT (#3079)
* feat(api): relocate authorizations outside of JWT

* fix(api): update user authorization after enabling the RBAC extension

* feat(api): add PortainerEndpointList operation in the default portainer authorizations

* feat(auth): retrieve authorization from API instead of JWT

* refactor(auth): move permissions retrieval to function

* refactor(api): document authorizations methods
2019-09-10 10:58:26 +12:00
xAt0mZ
7ebb3e62dd fix(services): mounted volumes are now persisted and displayed correctly (#3114) 2019-09-10 10:57:36 +12:00
xAt0mZ
52704e681b feat(services): rollback service capability (#3057)
* feat(services): rollback service capability

* refactor(services): notification reword

Co-Authored-By: William <william.conquest@portainer.io>

* refactor(services): remove TODO comment + add note on rollback capability

* fix(services): service update rpc error version out of sync

* feat(services): confirmation modal on rollback

* feat(services): rpc error no previous spec message
2019-09-10 10:56:57 +12:00
Steven Kang
ec19faaa24 fix(stack): Skip SSL Verification (#3064)
* fix(stack): Skip SSL Verification

* fix(stack): Skip SSL Verification

* fix(stack): move httpsCli into service

* fix(stack): clean-up

* fix(stack): move httpsCli back into the function

* fix(stack): move httpsCli and InstallProtocol back into service

* fix(stack): clean-up debugging

* fix(stack): parameter cleanup

Co-Authored-By: Anthony Lapenna <anthony.lapenna@portainer.io>
2019-09-10 10:56:16 +12:00
Anthony Lapenna
628d4960cc fix(api): fix an issue with RegistryUpdate operation (#3137) 2019-09-10 10:55:27 +12:00
Anthony Lapenna
2b48f1e49a refactor(build-system): clarify build system usage through yarn (#3140)
* refactor(build-system): clarify build system usage through yarn

* refactor(build-system): rename azure devops build scripts
2019-09-09 12:40:22 +12:00
Anthony Lapenna
849ff8cf9b docs(api): document EdgeAgentCheckinInterval parameter for SettingsUpdate 2019-09-02 07:17:41 +12:00
Anthony Lapenna
a90fa857ee docs(api): document Edge agent environment type 2019-08-30 09:30:30 +12:00
Mattias Kågström
c34e83cafd docs(README): fix typo in readme (#3071) 2019-08-14 15:03:47 +02:00
Anthony Brame
ea6cddcfd3 feat(swarmvisualizer): add labels display under node info (#2886)
* feat(swarmvisualizer): add labels display under node info

* feat(swarmvisualizer): fix css

* add toggle to display node labels

* feat(swarmvisualizer): rename filters section + fix display when label has no value

* feat(swarmvisualizer): retrieve state from local storage for node labels display toggle
2019-08-13 17:38:04 +02:00
xAt0mZ
96155ac97f feat(app): debounce on all search fields (#3058) 2019-08-12 16:27:05 +02:00
xAt0mZ
c12ce5a5c7 feat(networks): group networks for swarm endpoints (#3028)
* feat(networks): group networks for swarm endpoints

* fix(networks): display error on networks with 1 sub
2019-08-12 16:26:44 +02:00
xAt0mZ
552c897b3b fix(oauth): okta support (#3051)
* fix(oauth): okta support

* fix(oauth): state to follow OAuth 2 RFC against CSRF
2019-08-12 16:26:06 +02:00
xAt0mZ
24013bc524 fix(datatables): saved orderBy was always overridden by the default one (#3052) 2019-08-12 16:25:35 +02:00
William
3afeb13891 chore(project): adjust stalebot config (#3081) 2019-08-12 10:30:19 +02:00
Anthony Lapenna
e11df28df6 fix(api): fix missing windows dependency 2019-07-28 10:30:12 +12:00
xAt0mZ
a33dbd1e91 fix(oauth): state to follow OAuth 2 RFC against CSRF 2019-07-26 20:05:25 +02:00
xAt0mZ
b537a9ad0d fix(oauth): okta support 2019-07-26 20:05:25 +02:00
Anthony Lapenna
a6692ee526 Merge tag '1.22.0' into develop
Release 1.22.0
2019-07-26 14:02:03 +12:00
Anthony Lapenna
0b2a76d75a Merge branch 'release/1.22.0' 2019-07-26 14:01:58 +12:00
Anthony Lapenna
8cb18f9877 chore(version): bump version number 2019-07-26 14:01:49 +12:00
Anthony Lapenna
448003aaa4 docs(swagger): update Swagger documentation 2019-07-26 11:10:26 +12:00
Anthony Lapenna
12a512f01f feat(edge): introduce support for Edge agent (#3031)
* feat(edge): fix webconsole and agent deployment command

* feat(edge): display agent features when connected to IoT endpoint

* feat(edge): add -e CAP_HOST_MANAGEMENT=1 to agent command

* feat(edge): add -v /:/host and --name portainer_agent_iot to agent command

* style(endpoint-creation): refactor IoT agent to Edge agent

* refactor(api): rename AgentIoTEnvironment to AgentEdgeEnvironment

* refactor(api): rename AgentIoTEnvironment to AgentEdgeEnvironment

* feat(endpoint-creation): update Edge agent deployment instructions

* feat(edge): wip edge

* feat(edge): refactor key creation

* feat(edge): update deployment instructions

* feat(home): update Edge agent endpoint item

* feat(edge): support dynamic ports

* feat(edge): support sleep/wake and snapshots

* feat(edge): support offline mode

* feat(edge): host job support for Edge endpoints

* feat(edge): introduce STANDBY state

* feat(edge): update Edge agent deployment command

* feat(edge): introduce EDGE_ID support

* feat(edge): update default inactivity interval to 5min

* feat(edge): reload Edge schedules after restart

* fix(edge): fix execution of endpoint job against an Edge endpoint

* fix(edge): fix minor issues with scheduling UI/UX

* feat(edge): introduce EdgeSchedule version management

* feat(edge): switch back to REQUIRED state from ACTIVE on error

* refactor(edge): remove comment

* feat(edge): updated tunnel status management

* feat(edge): fix flickering UI when accessing Edge endpoint from home view

* feat(edge): remove STANDBY status

* fix(edge): fix an issue with console and Swarm endpoint

* fix(edge): fix an issue with stack deployment

* fix(edge): reset timer when applying active status

* feat(edge): add background ping for Edge endpoints

* fix(edge): fix infinite loading loop after Edge endpoint connection failure

* fix(home): fix an issue with merge

* feat(api): remove SnapshotRaw from EndpointList response

* feat(api): add pagination for EndpointList operation

* feat(api): rename last_id query parameter to start

* feat(api): implement filter for EndpointList operation

* fix(edge): prevent a pointer issue after removing an active Edge endpoint

* feat(home): front - endpoint backend pagination (#2990)

* feat(home): endpoint pagination with backend

* feat(api): remove default limit value

* fix(endpoints): fix a minor issue with column span

* fix(endpointgroup-create): fix an issue with endpoint group creation

* feat(app): minor loading optimizations

* refactor(api): small refactor of EndpointList operation

* fix(home): fix minor loading text display issue

* refactor(api): document bolt services functions

* feat(home): minor optimization

* fix(api): replace seek with index scanning for EndpointPaginated

* fix(api): fix invalid starting index issue

* fix(api): first implementation of working filter

* fix(home): endpoints list keeps backend pagination when it needs to

* fix(api): endpoint pagination doesn't drop the first item on pages >=2 anymore

* fix(home): UI flickering on page/filter load/change

* feat(auth): login spinner

* feat(api): support searching in associated endpoint group data

* refactor(api): remove unused API endpoint

* refactor(api): remove comment

* refactor(api): refactor proxy manager

* feat(api): declare EndpointList params as optional

* feat(api): support groupID filter on endpoints route

* feat(api): add new API operations endpointGroupAddEndpoint and endpointGroupDeleteEndpoint

* feat(edge): new icon for Edge agent endpoint

* fix(edge): fix missing exec quick action

* fix(edge): add loading indicator when connecting to Edge endpoint

* feat(edge): disable service webhooks for Edge endpoints

* feat(endpoints): backend pagination for endpoints view (#3004)

* feat(edge): dynamic loading for stack migration feature

* feat(edge): wordwrap edge key

* feat(endpoint-groups): backend pagination support for create and edit

* feat(endpoint-groups): debounce on filter for create/edit views

* feat(endpoint-groups): filter assigned on create view

* (endpoint-groups): unassigned endpoints edit view

* refactor(endpoint-groups): code clean

* feat(endpoint-groups): remove message for Unassigned group

* refactor(websocket): minor refactor associated to Edge agent

* feat(endpoint-group): enable backend pagination (#3017)

* feat(api): support groupID filter on endpoints route

* feat(api): add new API operations endpointGroupAddEndpoint and endpointGroupDeleteEndpoint

* feat(endpoint-groups): backend pagination support for create and edit

* feat(endpoint-groups): debounce on filter for create/edit views

* feat(endpoint-groups): filter assigned on create view

* (endpoint-groups): unassigned endpoints edit view

* refactor(endpoint-groups): code clean

* feat(endpoint-groups): remove message for Unassigned group

* refactor(api): endpoint group endpoint association refactor

* refactor(api): rename files and remove comments

* refactor(api): remove usage of utils

* refactor(api): optional parameters

* Merge branch 'feat-endpoint-backend-pagination' into edge

# Conflicts:
#	api/bolt/endpoint/endpoint.go
#	api/http/handler/endpointgroups/endpointgroup_update.go
#	api/http/handler/endpointgroups/handler.go
#	api/http/handler/endpoints/endpoint_list.go
#	app/portainer/services/api/endpointService.js

* fix(api): fix default tunnel server credentials

* feat(api): update endpointListOperation behavior and parameters

* fix(api): fix interface declaration

* feat(edge): support configurable Edge agent checkin interval

* feat(edge): support dynamic tunnel credentials

* feat(edge): update Edge agent deployment commands

* style(edge): update Edge agent settings text

* refactor(edge): remove unused credentials management methods

* feat(edge): associate a remote addr to tunnel credentials

* style(edge): update Edge endpoint icon

* feat(edge): support encrypted tunnel credentials

* fix(edge): fix invalid pointer cast

* feat(bolt): decode endpoints with jsoniter

* feat(edge): persist reverse tunnel keyseed

* refactor(edge): minor refactor

* feat(edge): update chisel library usage

* refactor(endpoint): use controller function

* feat(api): database migration to DBVersion 19

* refactor(api): refactor AddSchedule function

* refactor(schedules): remove comment

* refactor(api): remove comment

* refactor(api): remove comment

* feat(api): tunnel manager now only manage Edge endpoints

* refactor(api): clean-up and clarification of the Edge service

* refactor(api): clean-up and clarification of the Edge service

* fix(api): fix an issue with Edge agent snapshots

* refactor(api): add missing comments

* refactor(api): update constant description

* style(home): remove loading text on error

* feat(endpoint): remove 15s timeout for ping request

* style(home): display information about associated Edge endpoints

* feat(home): redirect to endpoint details on click on unassociated Edge endpoint

* feat(settings): remove 60s Edge poll frequency option
2019-07-26 10:38:07 +12:00
Anthony Lapenna
2252ab9da7 style(app): update app loading text (#3046) 2019-07-26 10:20:38 +12:00
xAt0mZ
7338e5fabd fix(security): bump lodash to 4.17.15 (#3043) 2019-07-26 10:14:18 +12:00
Anthony Lapenna
5b91b1a6c9 feat(api): bump default Docker library timeout to 60s (#3038) 2019-07-24 11:56:31 +12:00
xAt0mZ
66b6a6cbbd fix(app): UI settings persistency (#3025) 2019-07-22 20:10:49 -07:00
xAt0mZ
1089846fd6 fix(datatables): default orderby now applied correctly (#3022) 2019-07-22 20:09:43 -07:00
William
fbcffb7969 chore(project): adjust stalebot config (#3029) 2019-07-22 16:28:39 -07:00
xAt0mZ
2bf125c8cc fix(app): un-needed checkbox in service details view (#2982) 2019-07-22 12:59:29 +02:00
xAt0mZ
9ec83bb065 style(container-stats): clarify network graph is aggregate (#3003) 2019-07-22 12:59:01 +02:00
xAt0mZ
64d382f612 fix(containers): multiple clics on image commit (#3013) 2019-07-22 12:58:22 +02:00
xAt0mZ
4fcd2e8afe style(container-creation): clarify ports mapping (#2995) 2019-07-22 12:55:58 +02:00
William
16234aa0c1 style(users): fix typo/grammar (#3010) 2019-07-22 12:55:40 +02:00
xAt0mZ
03c82cac69 feat(datatables): auto refresh on datatables (#2974)
* feat(datatables): auto refresh on datatables

* feat(datatables): auto refresh implementation on docker related resources
2019-07-22 12:54:59 +02:00
linquize
cc487ae68a fix(registries): can edit registries when --no-auth is set (#2763) 2019-07-20 16:48:59 -07:00
Anthony Lapenna
90d3f3a358 Enable endpoint backend pagination (#2989)
* feat(api): remove SnapshotRaw from EndpointList response

* feat(api): add pagination for EndpointList operation

* feat(api): rename last_id query parameter to start

* feat(api): implement filter for EndpointList operation

* feat(home): front - endpoint backend pagination (#2990)

* feat(home): endpoint pagination with backend

* feat(api): remove default limit value

* fix(endpoints): fix a minor issue with column span

* fix(endpointgroup-create): fix an issue with endpoint group creation

* feat(app): minor loading optimizations

* refactor(api): small refactor of EndpointList operation

* fix(home): fix minor loading text display issue

* refactor(api): document bolt services functions

* feat(home): minor optimization

* fix(api): replace seek with index scanning for EndpointPaginated

* fix(api): fix invalid starting index issue

* fix(api): first implementation of working filter

* fix(home): endpoints list keeps backend pagination when it needs to

* fix(api): endpoint pagination doesn't drop the first item on pages >=2 anymore

* fix(home): UI flickering on page/filter load/change

* feat(api): support searching in associated endpoint group data

* feat(api): declare EndpointList params as optional

* feat(endpoints): backend pagination for endpoints view (#3004)

* feat(endpoint-group): enable backend pagination (#3017)

* feat(api): support groupID filter on endpoints route

* feat(api): add new API operations endpointGroupAddEndpoint and endpointGroupDeleteEndpoint

* feat(endpoint-groups): backend pagination support for create and edit

* feat(endpoint-groups): debounce on filter for create/edit views

* feat(endpoint-groups): filter assigned on create view

* (endpoint-groups): unassigned endpoints edit view

* refactor(endpoint-groups): code clean

* feat(endpoint-groups): remove message for Unassigned group

* refactor(api): endpoint group endpoint association refactor

* refactor(api): rename files and remove comments

* refactor(api): remove usage of utils

* refactor(api): optional parameters

* feat(api): update endpointListOperation behavior and parameters

* refactor(api): remove unused methods associated to EndpointService

* refactor(api): remove unused methods associated to EndpointService

* refactor(api): minor refactor
2019-07-20 16:28:11 -07:00
William
d52a1a870c chore(project): clarify bug template (#3021) 2019-07-18 17:46:25 -07:00
William
0b7500827b chore(project): stalebot integration (#3019)
* chore(project): stalebot integration

* chore(project): put file in correct directory
2019-07-18 16:28:08 -07:00
xAt0mZ
f71a565acc refactor(container): reword notification messages 2019-07-18 17:19:00 +02:00
Anthony Lapenna
92a615d7b6 Revert "fix(api): AddCron fix after library update (#3014)" (#3016)
This reverts commit c432ead45f.
2019-07-16 22:22:57 -07:00
xAt0mZ
c432ead45f fix(api): AddCron fix after library update (#3014) 2019-07-16 21:34:31 -07:00
xAt0mZ
a856053338 fix(containers): multiple clics on image commit button were creating a lot of images 2019-07-15 13:28:30 +02:00
itsconquest
afda5d07bf style(container-stats): clarify network graph is aggregate 2019-07-11 16:52:28 +12:00
xAt0mZ
693182fbd3 feat(auth): login spinner (#2999) 2019-07-11 04:13:03 +12:00
itsconquest
d1fee6f119 style(container-creation): clarify ports mapping 2019-07-10 17:16:43 +12:00
xAt0mZ
4084e7c8ec feat(home): pagination on endpoints list (#2981) 2019-07-05 09:46:59 +12:00
xAt0mZ
f20526d662 fix(app): disable-authorization directive was hiding/showing elements instead of disabling them 2019-07-04 15:36:29 +02:00
xAt0mZ
3d4af7c54f feat(registry): disable browse for quay.io registry (#2971)
* feat(registry): disable browse for quay.io registry

* refactor(registry): browsable urls check done with function
2019-07-03 00:33:46 +02:00
xAt0mZ
1138fd5ab1 fix(datatables): allow selecting range using shift (#344) (#2962)
* fix(datatables): allow selecting range using shift (#344)

* feat(datatables): more intuitive batch select behaviour

* feat(datatables): add overridable function called on selection change

* refactor(datatables): remove custom selectAll on Generic-extending Controllers

* fix(datatables): stored state data retrieval on Generic-extanding datatables controllers

* refactor(datatables): remove code duplication between GenericController and extending controllers
2019-07-02 17:51:17 +02:00
xAt0mZ
6591498ab9 feat(node-details): move engine labels from node-details panel to engine-details panel (#2966) 2019-06-25 11:22:45 +12:00
Anthony Lapenna
7a8a54c96a refactor(api): introduce ExtensionServer constant 2019-06-23 11:36:45 +12:00
Anthony Brame
b3c7c76be2 fix(swarmvisualizer): nodes are now sorted by roles then by hostname (#2885) 2019-06-21 15:50:58 +02:00
Michael Oborne
fb69ffa764 fix(create-container): init ExposedPorts on container creation process (#2933) 2019-06-21 15:48:52 +02:00
Pedro Henrique
96f266adf6 fix(volume-browser): download capability for all types of files (#2954) 2019-06-21 15:44:29 +02:00
William
f3b9668629 fix(endpoints): always reset agent headers when switching state (#2939) 2019-06-18 14:45:49 +02:00
xAt0mZ
71b1da8d32 fix(app): wrapper for UI refresh trigger with async/await (#2945)
* fix(app): wrapper for UI refresh trigger with async/await

* fix(async): $async wrapper now accepts functions with params

* fix(async): $async should return a promise to be chained with ES5 .then() style

* fix(async): $async with multiple params was not working

* refactor(app): wrap all async functions with $async

* docs(async): add link to async wrapper documentation
2019-06-17 16:51:39 +02:00
xAt0mZ
09cf55a7dc fix(build): bump auto-ngtemplate-loader to 2.0.1 to fix windows builds (#2935) 2019-06-14 14:22:49 +02:00
dependabot[bot]
ead160f792 chore(deps): bump js-yaml from 3.10.0 to 3.13.1 (#2930)
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 3.10.0 to 3.13.1.
- [Release notes](https://github.com/nodeca/js-yaml/releases)
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.10.0...3.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2019-06-11 23:41:32 +02:00
xAt0mZ
144e0ae07e refactor(app): move storidge to new 'integrations' module (#2905)
* refactor(app): move storidge to new 'integrations' module

* style(storidge): revert TODO note removal
2019-06-11 23:13:18 +02:00
Anthony Lapenna
67de71a18f docs(api): update swagger documentation 2019-06-11 10:34:54 +12:00
Anthony Lapenna
e5f092058b Merge tag '1.21.0' into develop
Release 1.21.0
2019-06-04 15:51:32 +12:00
Anthony Lapenna
c1433eff0d Merge branch 'release/1.21.0' 2019-06-04 15:51:20 +12:00
Anthony Lapenna
48281df41a chore(version): bump version number 2019-06-04 15:51:11 +12:00
Steven Kang
af08a1b0f6 fix(build-system) - fix Windows build on Azure devops (#2921)
* fix(build-system) - add sac2016 tag

This is to fix the Windows image build, where MS stopped supporting the `latest` tag

* fix(build-system) - use ARG for win multi-arch

* fix(build-system) - use ARG for win multi-arch

* fix(build-system) - delete unused Dockerfile

* fix(build-system) - use ARG for win multi-arch

* fix(build-system) - update GOPATH

* fix(build-system) - update GOPATH and add debug

* Debug

* Debug

* fix(build-system) - add static tag

* fix(build-system) - add static tag

* fix(build-system) - revert GOPATH

* Debug

* Debugging

* Debugging

* Debugging

* Debugging

* fix(build-system) - fix binary location

* fix(build-system) - enable verbose output

* fix(build-system) - Remove relativeTo Options

* fix(build-system) - update options

* fix(build-system) - Remove Debug
2019-06-04 14:05:04 +12:00
Anthony Lapenna
b4c16a1fb4 refactor(api): update ExtensionDefinitionsURL 2019-06-04 13:54:16 +12:00
Anthony Lapenna
d55212e9da docs(api): update Swagger documentation 2019-06-04 11:19:37 +12:00
Anthony Lapenna
50f547a6e7 feat(motd): add the ability to use custom style (#2918)
* feat(motd): rework motd display mechanism for more flexibility on motd content

* feat(api): enhance MOTD

* refactor(api): refactor MOTD related codebase

* feat(motd): hash on message
2019-06-02 18:16:43 +12:00
Anthony Lapenna
1d9166216a feat(app): set anonymizeIp property for GA (#2919) 2019-06-02 18:16:07 +12:00
Steven Kang
d75f2f5d7d fix(build-system) - add sac2016 tag for Windows image (#2909) 2019-05-29 16:57:30 +12:00
baron_l
5388585ef1 fix(app): extensions status retrieval (#2910)
* fix(rbac): extensions status retrieval not interrupted anymore

* refactor(extensions): change endpoint used to retrieve extension activation status
2019-05-29 12:39:30 +12:00
Anthony Lapenna
086d4f1d1c feat(containers): hide the attach quick action by default (#2908)
* feat(containers): hide the attach quick action by default

* fix(containers): add missing display checks for quick actions column

* fix(services): add missing showQuickActionAttach property
2019-05-29 12:15:52 +12:00
baron_l
608fc497a8 fix(app): extensions cache only for rbac (#2904) 2019-05-28 20:41:20 +12:00
Anthony Lapenna
dc3a29ad43 refactor(rbac): update RBAC name to full-length name 2019-05-27 15:48:46 +12:00
Anthony Lapenna
5fda4ff9f8 refactor(api): update role descriptions 2019-05-27 15:47:08 +12:00
Anthony Lapenna
23eaf14f58 style(roles): fix typo 2019-05-27 10:43:03 +12:00
Anthony Lapenna
a2d29df21b fix(rbac): add specific authorization for Storidge management 2019-05-27 10:41:12 +12:00
Anthony Lapenna
4349f5803c fix(api): fix missing default Portainer permissions for users 2019-05-27 09:31:20 +12:00
Anthony Lapenna
407328f9ed fix(api): remove admin restriction for registry proxy endpoint 2019-05-27 09:15:50 +12:00
William
e3eeb32a11 style(container-creation): clarify port mapping (#2899) 2019-05-25 09:56:30 +12:00
baron_l
851607394c feat(integrations): storidge evolution (#2711)
* feat(storidge): update storidge routes

* feat(storidge): add new fields on profile create/edit

* feat(storidge): add drives list and details view

* feat(storidge): add node details / cordon / uncordon / remove

* feat(storidge): add volume and snapshot details

* feat(storidge): add snapshot creation on volume details

* feat(storidge): add rescan drives button

* refactor(storidge): move add / remove / put in / put ouf maintenance buttons for cluster nodes

* style(storidge): change cluster / node icon color based on status

* feat(storidge): profiles can enable snapshots without interval + interval in minutes

* refactor(storidge): split cluster and node status badge filter

* fix(storidge): error on volume IOPS update

* fix(storidge): snapshot can now be created without comments

* feat(storidge): remove snapshots panels when volume snapshots are disabled

* fix(app): paginatedItemLimit now retrieved for datables extending GenericDatatableController

* fix(storidge): addDrive is called with the good parameters

* fix(storidge): update model and views for Storidge v2695

* refactor(storidge): webpack migration

* fix(storidge): display modifications + fix js errors

* feat(storidge): snapshots, profile and nodes evolution

* fix(storidge): values for InterfaceDriver on profile create/edit

* feat(storidge): v5 update without style (profile / statuses / volume)

* fix(storidge): description tables on the same view have now the same fixed offset

* fix(app): override rdash-ui select style

* Revert "fix(app): override rdash-ui select style"

This reverts commit e724833261.

* feat(storidge): wip on update 6

* feat(storidge): update 6

* feat(storidge): update 6

* feat(storidge): update 6

* feat(storidge): update 7 - node details + cluster views

* fix(storidge): update 7 - profiles creation + volume details

* fix(storidge): update 7 - profile create/edit interface type

* feat(storidge): update 8 - add drive

* feat(storidge): update 8 - UI refactors + cluster availability

* fix(storidge): update 8 - revert cluster availability

* feat(storidge): update 8 - node availability on swarm overview

* feat(storidge): cluster condition badge

* fix(storidge): update 9 - move add storage button + api profile filesystem kv to obj

* feat(storidge): update 9 - disable add drive button when action is in progress

* fix(storidge): update 9 - add drive button will now change only for the concerned drive

* fix(storidge): update 10 - disable remove drive button when removal in progress

* fix(api): update Storidge proxy creation process

* refactor(api): update version number

* feat(extensions): fix an issue with Storidge API URL

* feat(storidge): force the use of a manager node
2019-05-25 09:53:10 +12:00
Anthony Lapenna
17765d992e fix(api): fix missing winio dependency 2019-05-24 19:35:15 +12:00
Anthony Lapenna
8057aa45c4 feat(extensions): introduce RBAC extension (#2900) 2019-05-24 18:04:58 +12:00
William
27a0188949 feat(build-system): remove webpack-bundle-analyzer plugin (#2898) 2019-05-24 17:59:44 +12:00
William
c8c8345a43 dep(jquery): update jquery to version 3.4.0 (#2897) 2019-05-24 17:58:25 +12:00
William
8025d4c817 fix(support): enforce minimum purchase amount (#2891)
* fix(support): enforce minimum purchase amount

* Update app/portainer/views/support/product/product.html

Co-Authored-By: Anthony Lapenna <lapenna.anthony@gmail.com>

* fix(support): fix product img tags for webpack
2019-05-24 17:57:27 +12:00
Anthony Lapenna
6be394c2e0 refactor(api): minor refactor to stream.go 2019-05-13 09:20:55 +12:00
Kai
540d3c2c6b feat(api): support utf8 output in websocket endpoints
* change TCPConnToWebsocketConn read function to ReadRune

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Update websocket_exec.go

* Delete Project_Default.xml

* Delete modules.xml

* Delete portainer.iml

* Delete vcs.xml

* Delete workspace.xml

* Delete exec.go

* change TCPConnToWebsocketConn read function to ReadRune

* Apply suggestions from code review

Co-Authored-By: Anthony Lapenna <lapenna.anthony@gmail.com>

* Update stream.go
2019-05-13 08:55:25 +12:00
mrdrogdrog
1af9fb4490 feat(containers): add support docker attach (#2842)
* #592 feat(container-details): split websocket backend code into more files and add attach handler

* #592 feat(container-details): rename console to exec and add attach console

* Revert "#592 feat(container-details): rename console to exec and add attach console"

This reverts commit f2deaee1

* #592 feat(container-details): add attach to containerconsole

* #592 feat(container-details): catch more errors

* #592 feat(container-details): use less vars

* #592 feat(container-details): error message is more verbose

* #592 feat(container-details): go fmt

* #592 feat(container-details): unpack netdial

* #592 feat(container-details): reformat service

* #592 feat(container-details): fix go compiler bugs

* #592 feat(container-details): refactor services

* #592 feat(container-details): fix windows dial

* #592 feat(container-details): gofmt dial_windows.go

* #592 feat(container-details): split console into two views and fix breadcrumbs

* #592 feat(container-details): swap exec and attach action

* #592 feat(container-details): add some warnings

* #592 feat(container-details): refresh view more

* #592 feat(container-details): use less functions for connecting/disconnecting

* #592 feat(container-details): move link replacements into initTerm

* #592 feat(container-details): disable attach/exec button if container is not running

* #592 feat(container-details): fix typo

* #592 feat(container-details): autoconnect attach view

* #592 feat(container-details): fix first draw after attach + reformat code

* #592 feat(container-details): remove init-helper-div

* #592 feat(container-details): console resize code and remove padding

* #592 feat(container-details): swap height and width arguments in container tty resize restcall

* #592 feat(container-details): swap height and width arguments in exec tty resize restcall

* #592 feat(container-details): remove css unit

* #592 feat(container-details): remove loaded state from states object

* #592 feat(container-details): reword Disattach to Detach

* #592 feat(container-details): remove unloaded state from states object

* #592 feat(container-details): remove useless code

* #592 feat(container-details): clearer state-check

* #592 feat(container-details): fixed resize bugs by using xterms col attribute
2019-05-09 14:04:40 +12:00
Steven Kang
dc9a3de88f feat(webhooks): Add Tag Support (#2871)
* feat(webhooks): Add Tag Support

* feat(webhooks): Add Tag Support
2019-05-08 10:41:31 +12:00
baron_l
7b3ef7f1a2 fix(node-details): engine labels were displayed as Objects (#2858) 2019-05-02 08:37:24 +12:00
William
80c5052b55 style(users): fix typo/grammar (#2848) 2019-04-23 12:22:54 +12:00
William
845f4e912b fix(secret-creation): prevent assignment of label with no name (#2838) 2019-04-19 09:18:40 +12:00
Anthony Lapenna
e5fd61044a feat(project): update issue templates to include a note about support policy 2019-04-12 10:00:18 +12:00
Anthony Lapenna
c3066d7f3f docs(README): add a note about support policy 2019-04-12 09:57:56 +12:00
William
8a7a73fe84 fix(notifications): fix create user errormessage (#2820) 2019-04-08 15:48:43 +12:00
William
0f8de0a039 fix(build-system): fix lodash imports (#2818) 2019-04-08 12:35:02 +12:00
William
e4a81df42e fix(group-access): fix authorize/deny all buttons behavior (#2813) 2019-04-08 09:28:57 +12:00
William
c39807e86c fix(team): fix invalid team leader count (#2811) 2019-04-05 14:49:08 +13:00
Chaim Lev-Ari
45113a7ff4 refactor(app): introduce webpack and babel (#2407)
* feat(agent): add new host page

* feat(agent): convert volume-browser to files-datatable

* fix(agent): browse folders in file-datatable

* feat(engine-details): replace engine view with host view

* feat(engine-details): remove old panels

* feat(engine-details): add basic engine-details-panel component

* feat(engine-details): pass details to the different components

* feat(engine-details): replace host-view with host-overview

* feat(engine-details): add commaseperated filter

* feat(engine-details): add host-view container component

* feat(engine-details): add host-details component

* feat(engine-details): build host details object

* feat(engine-details): format engine version

* feat(engine-details): get details for one node

* feat(engine-details): pass is-agent from view

* feat(engine-details): replace old node view with a new component

* feat(engine-details): add swarm-node-details component

* feat(engine-details): remove isSwarm binding

* feat(engine-details): remove node-details and include in parent

* feat(engine-details): add labels-table component

* feat(engine-details): add update node service

* feat(engine-details): add update label functionality

* style(engine-details): remove whitespaces

* feat(engine-details): remove old node page

* feat(engine-details): pass is agent to host details

* feat(host-details): hide missing info

* feat(host-details): update node availability

* style(host-details): remove obsolete event object

* feat(host-details): fix labels not sending

* feat(host-details): remove flags for hiding data

* feat(host-details): create mock call to server for agent host info

* style(host-details): fix spelling mistake in filter's name

* feat(host-details): get info from agent

* feat(host-details): hide engine labels when empty

* feat(node-details): move labels table and save button

* feat(host-info): add different urls for refresh

* feat(host-details): show disk/devices info for agent

* feat(host-view): add loading indicator to devices-panel

* feat(host-details): add loading indicator to disks panel

* feat(agent): fix browse volume

* feat(agent): browse files

* feat(agent): enable rename

* feat(agent): download file

* fix(agent): download file from root

* feat(agent): delete file

* style(agent): remove whitespaces

* fix(agent): fix link on node browser

* feat(agent): basic file uploader

* feat(agent): add basic file upload

* fix(volume-browser): move volume id to query params

* feat(node-browser): moved uploader into browser

* feat(node-browser): add upload spinner

* feat(agent): browse files relative to root

* feat(build): add webpack build config

* feat(build): add missing imports

* feat(webpack): add missing imports

* feat(build): enable eslint on build

* feat(build): add webpack notifier

* feat(build): clean terminal on build

* feat(build): import all globals

* feat(build): add angular import

* feat(build): fix styles

* feat(build): load favicons

* feat(build): load css before script

* feat(webpack): split vendors css and js to a different bundle

* feat(webpack): import angular in all files

* feat(webpack): remove eslint global config

* feat(webpack): add webpack clean dist

* feat(webpack): fix styling issues

* refactor(webpack): remove empty controllers

* refactor(webpack): optimize moment

* refactor(webpack): add bundle analyzer

* feat(webpack): add babel

* refactor(webpack): optimize lodash

* refactor(toastr): update toastr

* feat(webpack): create basic production and dev config

* fix(webpack): fix production config

* fix(webpack): fix html templates url

* refactor(webpack): remove angular imports

* refactor(webpack): remove more angular imports

* refactor(webpack): return angular to entry file

* style(webpack): remove comments from config

* fix(hosts): remove browse button

* fix(webpack): import lodash

* fix(webpack): import missing htmls

* feat(webpack): reduce lodash size

* feat(webpack): config grunt to use webpack

* feat(webpack): add postcss

* chore(codeclimate): use eslint-5 channel

* feat(deps): upgrade from lodash to lodash-es

* fix(webpack): fix bug with lodash

* chore(build): add build client script

* fix(webpack): fix missing jsyaml reference

* refactor(webpack): seperate builds of img files

* chore(build): add a way to check times of webpack build

* feat(webpack): add dev server

* fix(webpack): fix css output name

* chore(webpack): optimize images

* chore(webpack): add node env

* fix(build): copy templates on release

* chore(webpack): set env NODE_ENV

* feat(webpack): set NODE_ENV on production builds

* fix(extensions): set image path

* refactor(css): move vendor css to js import

* style(app): remove whitespaces

* fix(build-system): allow DevOps pipeline to leverage webpack (#2670)

* Update devopsbuild task to use webpack & remove AppVeyor environment var

* Added -Force to replace the existing dist folder

* Removed Test-Path

* dep(build-system): add angularjs-annotate to webpack + fix on imports

* Merge branch 'develop' into webpack

* refactor(app): webpack aliases for imports + async / await dep + start refactor

* style(extensions): use develop version of the view

* fix(app): fix several issues introduced by webpack migration

* fix(webpack): fix ng-include not loading templates with webpack

* Fix Windows CI with Webpack (#2782)

* fix(configs): refactor broke configs creation and list views

* fix(build-system): update build_binary_devops for Windows
2019-03-21 18:46:49 +13:00
Anthony Lapenna
14845a4a53 refactor(api): refactor base import path (#2788)
* refactor(api): refactor base import path

* fix(build-system): update build_binary_devops

* fix(build-system): fix build_binary_devops for linux

* fix(build-system): fix build_binary_devops for Windows
2019-03-21 14:20:14 +13:00
Steven Kang
0c7d69eb17 fix(build-system): fix an issue with Windows build in CI (#2780)
* Change GOPATH outside of the current project path

* Fix ENV Variable

* Fix locations

* Fix dist directory location
2019-03-15 19:08:35 +13:00
William
3b8f982dbd fix(build-system): Install docker binary when on macOS (#2775) 2019-03-13 14:58:04 +13:00
Fabian Grutschus
dbab524e5d style(containers): change order of container action buttons to match ordering of quick action buttons (#2772) 2019-03-13 11:10:15 +13:00
Anthony Lapenna
1618388e39 refactor(app): minor refactor (#2773) 2019-03-13 11:09:53 +13:00
William
ac4af41317 fix(services): Do not display quick actions in a regular swarm environment (#2769) 2019-03-11 16:48:00 +13:00
linquize
ce6cb837f9 refactor(api): fix lint issues (#2760) 2019-03-11 12:50:10 +13:00
Anthony Lapenna
9967ae5994 Merge tag '1.20.2' into develop
Release 1.20.2
2019-03-05 17:34:33 +13:00
Anthony Lapenna
a171e540c5 Merge branch 'release/1.20.2' 2019-03-05 17:34:28 +13:00
Anthony Lapenna
cb858f0412 chore(version): bump version number 2019-03-05 17:34:19 +13:00
Anthony Lapenna
82078a8d8f style(extensions): update extensions information panel 2019-03-05 16:09:03 +13:00
Anthony Lapenna
2b31f489d9 feat(api): add support for an externally fetched title for motd (#2755)
* feat(api): add support for an externally fetched title for motd

* refactor(api): gofmt motd.go

* refactor(api): update go comment
2019-03-05 16:05:15 +13:00
Anthony Lapenna
e2a17480af Merge branch 'develop' of github.com:portainer/portainer into develop 2019-03-04 13:48:01 +13:00
Anthony Lapenna
0670079566 feat(api): update ExtensionDefinitionsURL 2019-03-04 13:46:27 +13:00
Anthony Lapenna
5ca9501540 dep(api): update docker binary version to 18.09.3 (#2749) 2019-03-01 14:45:36 +13:00
Anthony Lapenna
415c1759d1 Merge branch 'oath-poc' into develop 2019-03-01 14:16:04 +13:00
Anthony Lapenna
db0091b46d feat(api): revert extension URLs to correct one 2019-03-01 13:58:55 +13:00
linquize
42529cc5ea feat(volumes): show volume creation date (#2745) 2019-03-01 11:59:11 +13:00
Anthony Lapenna
60fbfeba23 fix(oauth): fix settings displaying issue for custom OAuth configuration 2019-03-01 11:24:47 +13:00
Anthony Lapenna
f5091ce5fb fix(auth): fix invalid condition to display OAuth login button 2019-03-01 10:58:18 +13:00
Anthony Lapenna
58962de20e Merge branch 'develop' into oath-poc 2019-03-01 09:42:38 +13:00
Anthony Lapenna
1eb7e6bacc fix(auth): rollback changes introduced via #2591 (#2747) 2019-02-28 11:38:02 +13:00
Anthony Lapenna
130baddea0 fix(api): fix an issue when removing non local administrators 2019-02-25 18:54:21 +13:00
Tim van den Eijnden
9cbf1f34a7 feat(networks): prevent removal of predefined networks (#2697)
* fix(networks): disable removing predefined networks (#1838)

*  fix(networks): disable select all for predefined networks (#1838)

* fix(networks): do not allow delete in network-details & use constant (#1838)
2019-02-25 14:25:48 +13:00
linquize
c152d3f62e fix(stacks): update web editor to set tab key to insert spaces (#2735) 2019-02-25 14:19:53 +13:00
linquize
da44f14e07 fix(auth): prevent redirect parameter to use state portainer.auth (#2701) 2019-02-25 13:57:11 +13:00
Anthony Lapenna
49516e2c3f style(oauth): update Azure UI elements 2019-02-25 13:38:27 +13:00
Anthony Lapenna
9c4c782a90 style(container-creation): review auto remove element position 2019-02-25 13:09:09 +13:00
baron_l
7aa6a30614 feat(registry-manager): allow regular users to use the registry browse feature (#2664)
* feat(registries): registries accessibility to all authorized people and not only admins

* feat(registry): dockerhub settings for admin only

* feat(registry): remove registry config access for non admin users

* feat(api): use AuthenticatedAccess policy instead of RestrictedAccess for extensionList operation

* refactor(api): minor update to security package

* refactor(api): revert unexporting function changes

* refactor(api): apply gofmt
2019-02-25 13:02:49 +13:00
linquize
99e50370bd feat(container-creation): support auto remove option (docker run --rm) (#2684) 2019-02-25 09:48:31 +13:00
Anthony Lapenna
dc2a8cf1f4 feat(oauth): update OAuth configuration UX 2019-02-21 14:02:25 +13:00
Anthony Lapenna
b9ac3d4286 feat(oauth): fix the double refresh issue 2019-02-21 11:09:57 +13:00
Anthony Lapenna
6711e6c969 feat(oauth): update configuration override UX 2019-02-21 10:30:09 +13:00
Anthony Lapenna
4a5fa211a7 feat(account): display a warning message in the account view 2019-02-20 13:57:13 +13:00
Anthony Lapenna
d510d23408 feat(oauth): improve Azure OAuth support 2019-02-20 13:53:25 +13:00
Anthony Lapenna
ce9e009e22 feat(oauth): update UI/UX 2019-02-19 14:38:42 +13:00
Anthony Lapenna
9918c1260b feat(oauth): update authentication panel with OAuth provider details 2019-02-19 09:54:02 +13:00
Anthony Lapenna
e325ad10dd fix(oauth): fix an UX issue when updating microsoft oauth settings 2019-02-18 16:18:48 +13:00
Anthony Lapenna
73f20b5157 refactor(oauth): remove console log statement 2019-02-18 15:21:34 +13:00
Anthony Lapenna
b6f04c5e0d fix(oauth): fix missing scopes for microsoft provider 2019-02-18 15:21:06 +13:00
Anthony Lapenna
2ef8c0b33e fix(app): rewrite URLHelper to avoid an issue with minification 2019-02-18 15:08:54 +13:00
Anthony Lapenna
7643f8d08c feat(oauth): dev build supporting Oauth extension 2019-02-18 14:46:34 +13:00
Anthony Lapenna
086bad2956 Merge branch 'develop' into oath-poc 2019-02-18 09:58:51 +13:00
Anthony Lapenna
d5dfc889bb docs(README): remove gitter badges 2019-02-18 09:51:20 +13:00
Montana Flynn
ef926dce33 docs(README): update logo src (#2719)
The current logo src is 404: https://portainer.io/images/logo_alt.png

The repo already includes the logo: https://github.com/portainer/portainer/blob/develop/assets/images/logo_alt.png?raw=true
2019-02-18 09:49:34 +13:00
Anthony Lapenna
d768e72a21 feat(oauth): add support for default team 2019-02-17 19:01:42 +13:00
Anthony Lapenna
78e2aaf7d4 feat(oauth): update OAuth UX 2019-02-17 17:01:36 +13:00
Anthony Lapenna
17cf374c30 Merge branch 'develop' into oath-poc 2019-02-17 16:39:23 +13:00
Nathan Baum
165096bef0 refactor(api): fix a typo (#2712)
Just a trivial spelling error.
2019-02-15 09:12:53 +13:00
Anthony Lapenna
de76ba4e67 feat(oauth): update OAuth UX 2019-02-14 15:58:45 +13:00
linquize
b1e048e218 feat(build-system): prefix some dependencies with "semver:" (#2690)
This makes both npm and yarn to work
2019-02-14 12:13:48 +13:00
linquize
8f32d58fae fix(templates): redirect to home if endpoint not yet selected #2709 (#2710) 2019-02-14 12:08:46 +13:00
Anthony Lapenna
16226b1202 Merge branch 'oath-poc' of github.com:portainer/pportainer into oath-poc 2019-02-13 10:01:06 +13:00
baron_l
8f568c8699 style(oauth): oauth loading + oauth config rework 2019-02-08 16:07:16 +01:00
Anthony Lapenna
af34b99cd4 Merge branch 'develop' into oath-poc 2019-02-08 13:32:53 +13:00
baron_l
2755527d28 feat(oauth): default team for user on oauth settings 2019-02-07 19:32:02 +01:00
baron_l
4d8133f696 feat(oauth): spinner on code evaluation after sucessfull oauth 2019-02-07 15:07:10 +01:00
Anthony Lapenna
fdc11dbe3a feat(build-system): update build system (#2682) 2019-02-07 12:00:47 +13:00
Anthony Lapenna
508352f4ea Merge branch 'develop' into oath-poc 2019-02-04 09:19:12 +13:00
Daniel Cardoza
9b6b6e09ae fix(endpoints): correct agent stack download url (#2667)
* 2584 fix(endpoints): correct agent stack download url

The directions for installing the agent stack from the endpoints
view used an old url. Update to the new url.

* Drop the portainer- prefix for the download path and filename

Co-Authored-By: dang3r <danielpcardoza@gmail.com>
2019-02-04 09:06:07 +13:00
Anthony Lapenna
899cd5f279 fix(home): fix an issue when trying to connect to an Azure ACI endpoint (#2671) 2019-02-04 09:04:52 +13:00
Anthony Lapenna
2eec8b75d0 Merge tag '1.20.1' into develop
Release 1.20.1
2019-01-31 13:15:28 +13:00
Anthony Lapenna
048c74a0dc Merge branch 'release/1.20.1' 2019-01-31 13:15:23 +13:00
Anthony Lapenna
6b1c476b63 chore(version): bump version number 2019-01-31 13:15:18 +13:00
Anthony Lapenna
c5b5f80bea docs(README): update build badge 2019-01-31 12:02:12 +13:00
Anthony Lapenna
cea2c60b55 refactor(build-system): fix lint issues 2019-01-31 11:38:27 +13:00
Steven Kang
576f369152 feat(build-system): introduce Azure DevOps support (#2666) 2019-01-31 11:37:16 +13:00
Anthony Lapenna
fca4f619b5 fix(api): re-use previous password when ldap settings update use empty password (#2659) 2019-01-30 14:53:14 +13:00
Chaim Lev Ari
90281fd7f0 feat(oauth): add providers to providers-selector 2019-01-25 10:57:40 +02:00
Chaim Lev Ari
c1939f6070 feature(oauth): add provider selector 2019-01-25 10:46:17 +02:00
Chaim Lev Ari
50c604ee4c fix(auth): use the right function to oauth validate 2019-01-25 10:44:31 +02:00
Chaim Lev Ari
41ded64037 Revert "refactor(auth): extract oauth login mechanism to service"
This reverts commit 0a439b3893.
2019-01-25 10:37:23 +02:00
baron_l
801336336f fix(registry-manager): add repositories pagination support (#2641)
* fix(registry-management): add support for repositories list with multiple requests

* refactor(registry-management): change regex usage to a reusable interceptor function

* refactor(registry-management): change interceptor to transformResponse function
2019-01-24 13:38:36 +13:00
Anthony Lapenna
90a0998502 feat(templates): add sonatype nexus 3 template 2019-01-23 16:05:07 +13:00
Anthony Lapenna
1a4dff536d fix(container-creation): fix an issue with command parsing (#2642)
* fix(container-creation): fix an issue with command parsing

* refactor(container-creation): remove indentation update
2019-01-23 12:25:42 +13:00
Chaim Lev-Ari
f772cd31cb feat(auth): preserve url when redirected to login (#2591)
* feat(auth): preserve url when redirected to login

* feat(auth): add redirect also to unauthenticated flow

* style(app): remove style changes from files

* fix(app): remove reference to otpLogin

* style(auth): remove semicolon
2019-01-23 12:22:56 +13:00
Chaim Lev-Ari
8160fe4717 feat(app): redirect to home if no endpoint is set (#2601)
* refactor(stacks): set newstack state as a child state of stacks

* fix(docker): add check on docker states for endpoint

* refactor(app): remove redirect notification
2019-01-23 12:21:48 +13:00
Anthony Lapenna
86c60807cd feat(endpoint-creation): fix invalid link (#2644) 2019-01-23 12:18:18 +13:00
baron_l
c1f2d90997 fix(container-creation): fix missing capabilities on duplicate (#2635) 2019-01-23 09:28:44 +13:00
Chaim Lev Ari
3699b794eb feat(oauth): add providers selectors 2019-01-18 12:14:12 +02:00
Chaim Lev Ari
69252a8377 refactour(auth): move information body to each setting 2019-01-18 12:08:18 +02:00
Chaim Lev Ari
193e7eb3f8 refactor(oauth): remove separation of strings 2019-01-18 11:53:44 +02:00
Chaim Lev Ari
de5f6086d0 refactor(oauth): return parse content error 2019-01-18 11:51:41 +02:00
Chaim Lev Ari
46e8f10aea refactor(ouath): use oauth2 library to get token 2019-01-18 10:56:16 +02:00
Chaim Lev Ari
60040e90d0 refactor(oauth): move build url logic to service 2019-01-18 10:24:42 +02:00
Chaim Lev Ari
c5c06b307a refactor(oauth): rename authenticate function 2019-01-18 10:15:02 +02:00
Chaim Lev Ari
c28274667d refactor(oauth): use oauth2 to generate login url 2019-01-18 10:13:33 +02:00
Anthony Lapenna
54163e3b92 fix(extensions): fix an issue with extensions with expired licenses (#2628)
* fix(extensions): fix an issue with extensions with expired licenses

* fix(api): fix invalid log call

* fix(api): allow to re-enable an extension
2019-01-18 10:00:18 +13:00
Chaim Lev-Ari
62eb47b3cb fix(container-creation): revert container state if creation failed (#2565)
* fix(container): rename old container only if exist

* fix(container): remove new container only if created

* style(container): fix typo

Co-Authored-By: chiptus <chiptus@users.noreply.github.com>
2019-01-18 08:59:43 +13:00
Anthony Lapenna
808eb7d341 dep(bootstrap): update bootstrap version to 3.4.0 (#2632) 2019-01-18 08:51:12 +13:00
hiyao
a33eca4bbb fix(registry-manager): fix an issue when removing all tags of a repository (#2545)
* fix repository reload got error in remove tags

When I remove all tags, removeTags() will reload and do initView() again, but data.tags response null, that trigger data.tags.length got error.

* Revert "fix repository reload got error in remove tags"

This reverts commit 5d9b1778ef.

* fix(registry-management): change response repository tags type to array by force

* feat(registry-management): redirect to repositories page when no tag in the repository after delete tags
2019-01-18 08:01:47 +13:00
baron_l
50e77d2bf1 fix(network-details): displaying all subnets and gateways on network details (#2629) 2019-01-17 11:39:15 +13:00
DevHugo
50a3b08209 feat(app): add driver name in the volume selector for container/service creation (#2534)
* Feat(containers): add driver name in the volume selector

* Feat(services): add driver name in the volume selector
2019-01-17 11:28:40 +13:00
Chaim Lev Ari
0a439b3893 refactor(auth): extract oauth login mechanism to service 2019-01-16 18:57:15 +02:00
Chaim Lev Ari
0d4e1d00f0 refactor(login): move oauth button to right 2019-01-16 18:00:01 +02:00
Chaim Lev Ari
b09f491f62 style(auth): remove comments and change error 2019-01-16 17:53:10 +02:00
Chaim Lev Ari
dc067b3308 refactor(http): remove old oauth handler 2019-01-16 17:41:56 +02:00
Chaim Lev Ari
b121f975fa refactor(settings): remove duplicate settings 2019-01-16 17:38:07 +02:00
Chaim Lev Ari
3f44925d7e fix(auth): fix typo - missing function 2019-01-16 17:37:50 +02:00
Chaim Lev Ari
80d570861d refactor(auth): move public settings into view model 2019-01-16 17:34:12 +02:00
Chaim Lev Ari
317bd53e43 Merge branch 'oath-poc' of github.com:portainer/pportainer into oath-poc 2019-01-16 17:26:29 +02:00
Chaim Lev Ari
24f066716b refactor(auth): expose only the login url 2019-01-16 17:25:16 +02:00
Chaim Lev Ari
4cbde7bb0d refactor(auth): move oauth handler under auth 2019-01-16 17:24:58 +02:00
Chaim Lev Ari
f6bdc5c2b3 refactor(auth): move oauth handler code to its own file 2019-01-16 17:01:38 +02:00
Anthony Lapenna
c650fe56c2 fix(auth): fix typos
Co-Authored-By: chiptus <chiptus@users.noreply.github.com>
2019-01-16 16:53:24 +02:00
Anthony Lapenna
fc8938e871 fix(auth): change oauth error type
Co-Authored-By: chiptus <chiptus@users.noreply.github.com>
2019-01-16 16:50:19 +02:00
Anthony Lapenna
44b7e0fdca fix(auth): change error type
Co-Authored-By: chiptus <chiptus@users.noreply.github.com>
2019-01-16 16:49:33 +02:00
Chaim Lev-Ari
fe63b4a156 fix(container-creation): populate logger config from existing container (#2602)
* refactor(container): change map function to lodash

* style(container): add semicolon
2019-01-16 13:34:28 +13:00
Chaim Lev-Ari
42365a52b1 feat(container-details): change network identifier to name (#2623) 2019-01-16 08:05:55 +13:00
Mark Stansberry
d6aafceba8 docs(api): update swagger definitions 2019-01-16 08:04:47 +13:00
baron_l
c7983d8993 fix(app): remove endpoint status update on 502/503 http return
* refactor(app): removing unused dep and function
2019-01-16 07:58:35 +13:00
Anthony Lapenna
34667bd3b3 fix(network-creation): force overlay network creation on manager node (#2622)
* fix(network-creation): force overlay network creation on manager node

* fix(app): fix function override

* fix(app): use portainerAgentManagerOperation in interceptor
2019-01-15 12:10:29 +13:00
Anthony Lapenna
3a3577754e fix(home): only display group name if available (#2621) 2019-01-15 08:52:26 +13:00
Anthony Lapenna
bed49c37e4 fix(teams): remove name sanitization when creating a team (#2619) 2019-01-14 17:27:55 +13:00
Anthony Lapenna
dedc02cc8d docs(api): fix invalid example value for AutoCreateUsers property (#2618) 2019-01-14 16:50:53 +13:00
Chaim Lev Ari
17ac3e5ed1 refactor(oauth): move enpoint constant to extension 2019-01-03 13:36:17 +02:00
Chaim Lev Ari
25620c5008 refactor(auth): refactor get url params 2019-01-02 20:49:25 +02:00
Chaim Lev Ari
9bebe9dee7 refactor(auth): move user setter into function 2019-01-02 20:01:23 +02:00
Chaim Lev Ari
81e3ace232 fix(auth): fix oauh enabled function 2019-01-02 20:01:06 +02:00
Chaim Lev Ari
15b6941872 refactor(oauth): move oauth rest service to extension 2019-01-02 20:00:41 +02:00
Chaim Lev Ari
7aaa9e58e9 refactor(auth): move oauth info to component 2019-01-02 16:24:10 +02:00
Chaim Lev Ari
515daf6dba refactor(auth): exprt oauth settings into extension 2019-01-02 16:21:36 +02:00
Chaim Lev Ari
0a1643bbcf style(auth): remove added spaces 2019-01-02 16:01:10 +02:00
Chaim Lev Ari
38f24683a6 refactor(auth): remove empty $q.deffered 2019-01-02 15:59:38 +02:00
Chaim Lev Ari
7494101a4d refactor(auth): refactor auth controller 2019-01-02 15:56:08 +02:00
Chaim Lev Ari
996319d299 feat(auth): don't clear client secret on update 2018-12-30 18:39:16 +02:00
Chaim Lev Ari
2ee6f2780b refactor(oauth): add debug logs 2018-12-30 18:25:30 +02:00
Chaim Lev Ari
241a701eca feat(oauth): merge pr from https://github.com/portainer/portainer/pull/2515 2018-12-30 18:02:22 +02:00
Anthony Lapenna
463b379876 docs(README): remove broken badges and links 2018-12-27 09:03:13 +01:00
Chaim Lev-Ari
f2cd33e831 feat(container-creation): call stopAndRename after pullImage (#2564)
* refactor(container): remove bind of function
2018-12-21 00:37:35 +09:00
Anthony Lapenna
6b05a35881 fix(api): set a default value for potentially empty snapshot interval (#2543) 2018-12-12 21:16:44 +13:00
Anthony Lapenna
6648c0bbe7 Merge tag '1.20.0' into develop
Release 1.20.0
2018-12-12 17:03:36 +13:00
Anthony Lapenna
dbda568481 Merge branch 'release/1.20.0' 2018-12-12 17:03:31 +13:00
Anthony Lapenna
189d131105 chore(version): bump version number 2018-12-12 17:03:25 +13:00
Anthony Lapenna
1384359baf fix(api): fix snapshot hanging 2018-12-12 17:00:15 +13:00
Anthony Lapenna
6c26cf1f39 style(support): update support pricing 2018-12-12 16:03:20 +13:00
Anthony Lapenna
8780b0a901 feat(api): update extension path on Windows arch 2018-12-12 14:19:58 +13:00
Anthony Lapenna
f5ada3085e fix(api): fix an issue with schedule update 2018-12-12 14:11:40 +13:00
Anthony Lapenna
acc5218c16 fix(api): fix snapshot schedule loading 2018-12-12 12:31:55 +13:00
Anthony Lapenna
8a186b4024 feat(api): update DigitalSignatureService (#2539) 2018-12-12 11:19:23 +13:00
Anthony Lapenna
5c2e714e69 style(extensions): minor update to extension UX/UI (#2538)
* style(extensions): update extension icons

* style(extensions): style update

* feat(extensions): update extension UX

* style(extensions): update extension style

* style(extension-details): update screenshot default size

* style(extensions): update overview diagram image

* refactor(support): fix support URLs
2018-12-12 10:28:21 +13:00
Anthony Lapenna
f222b3cb1a feat(templates): update logo URLs 2018-12-12 09:47:28 +13:00
Anthony Lapenna
e440ba53cb feat(api): migrate template data logo URLs (#2537) 2018-12-12 09:46:05 +13:00
Anthony Lapenna
17d85fdc15 fix(registry-creation): fix registry creation request being fired twice on firefox 2018-12-10 21:56:07 +13:00
Anthony Lapenna
42a357f863 fix(support-details): fix a js error 2018-12-09 17:03:26 +13:00
Anthony Lapenna
6fd5ddc802 feat(extensions): introduce extension support (#2527)
* wip

* wip: missing repository & tags removal

* feat(registry): private registry management

* style(plugin-details): update view

* wip

* wip

* wip

* feat(plugins): add license info

* feat(plugins): browse feature preview

* feat(registry-configure): add the ability to configure registry management

* style(app): update text in app

* feat(plugins): add plugin version number

* feat(plugins): wip plugin upgrade process

* feat(plugins): wip plugin upgrade

* feat(plugins): add the ability to update a plugin

* feat(plugins): init plugins at startup time

* feat(plugins): add the ability to remove a plugin

* feat(plugins): update to latest plugin definitions

* feat(plugins): introduce plugin-tooltip component

* refactor(app): relocate plugin files to app/plugins

* feat(plugins): introduce PluginDefinitionsURL constant

* feat(plugins): update the flags used by the plugins

* feat(plugins): wip

* feat(plugins): display a label when a plugin has expired

* wip

* feat(registry-creation): update registry creation logic

* refactor(registry-creation): change name/ids for inputs

* feat(api): pass registry type to management configuration

* feat(api): unstrip /v2 in regsitry proxy

* docs(api): add TODO

* feat(store): mockup-1

* feat(store): mockup 2

* feat(store): mockup 2

* feat(store): update mockup-2

* feat(app): add unauthenticated event check

* update gruntfile

* style(support): update support views

* style(support): update product views

* refactor(extensions): refactor plugins to extensions

* feat(extensions): add a deal property

* feat(extensions): introduce ExtensionManager

* style(extensions): update extension details style

* feat(extensions): display license/company when enabling extension

* feat(extensions): update extensions views

* feat(extensions): use ProductId defined in extension schema

* style(app): remove padding left for form section title elements

* style(support): use per host model

* refactor(extensions): multiple refactors related to extensions mecanism

* feat(extensions): update tls file path for registry extension

* feat(extensions): update registry management configuration

* feat(extensions): send license in header to extension proxy

* fix(proxy): fix invalid default loopback address

* feat(extensions): add header X-RegistryManagement-ForceNew for specific operations

* feat(extensions): add the ability to display screenshots

* feat(extensions): center screenshots

* style(extensions): tune style

* feat(extensions-details): open full screen image on click (#2517)

* feat(extension-details): show magnifying glass on images

* feat(extensions): support extension logo

* feat(extensions): update support logos

* refactor(lint): fix lint issues
2018-12-09 16:49:27 +13:00
Anthony Lapenna
f5dc663879 fix(build-system): revert appveyor integration
* Revert "fix(build-system): fix local build system after appveyor introduction (#2528)"

This reverts commit 79c24ced96.

* Revert "feat(build-system): add support for AppVeyor CI (#2449)"

This reverts commit 65979709e9.
2018-12-09 16:32:12 +13:00
Anthony Lapenna
79c24ced96 fix(build-system): fix local build system after appveyor introduction (#2528) 2018-12-09 16:08:36 +13:00
Steven Kang
65979709e9 feat(build-system): add support for AppVeyor CI (#2449) 2018-12-07 16:19:58 +13:00
Olli Janatuinen
2541f4daea feat(UX): persist search criterias (#2425)
* feat(ui): persist search criteria

* fix(ui): trying make templates search working correctly

* fix(ui): corrected search persistance on home and templates

* fix(ui): corrected javascript errors
2018-12-07 08:54:34 +13:00
baron_l
1a94158f77 * feat(UX): schedule creation UX overhaul (#2485)
* feat(api): add a new Recurring property on Schedule

* feat(schedules): date to cron convert + recurring flag

* feat(schedules): update angularjs-datetime-picker from v1 to v2

* chore(app): use minified dependency for angularjs-datetime-picker

* chore(vendor): rollback version of angularjs-datetime-picker

* * feat(ux): replace datepicker for schedule creation/details

* feat(container-stats): add refresh rate of 1 and 3 seconds (#2493)

* fix(templates): set var to default value if no value selected (#2323)

* fix(templates): set preset to true iff var type is preset

* fix(templates): add env var value when changing type

* feat(security): shutdown instance after 5minutes if no admin account created (#2500)

* feat(security): skip admin check if --no-auth

* fix(security): change error message

* fix(vendor): use datepicker minified version

* feat(schedule-creation): replace angular-datetime-picker

* feat(schedule): parse cron to datetime

* fix(schedule): fix zero based months
2018-12-07 08:53:23 +13:00
Anthony Lapenna
9e1800e2ec style(settings): update host management tooltip 2018-12-06 14:01:49 +13:00
baron_l
a9b107dbb5 feat(app): add the capability to enable/disable host management features (#2472)
* feat(settings): add the capability to enable/disable the host management features

* feat(settings): remove the validation of EnableHostManagementFeatures in frontend

* feat(api): disable schedules API when HostManagementFeatures is false + DB migration

* style(settings): update host management settings tooltip

* refacot(schedules): update DBVersion to 15
2018-12-06 11:36:25 +13:00
Chaim Lev-Ari
101bb41587 feat(security): shutdown instance after 5minutes if no admin account created (#2500)
* feat(security): skip admin check if --no-auth

* fix(security): change error message
2018-12-04 16:50:41 +13:00
Chaim Lev-Ari
acce5e0023 fix(templates): set var to default value if no value selected (#2323)
* fix(templates): set preset to true iff var type is preset

* 

* fix(templates): add env var value when changing type
2018-12-04 09:52:59 +13:00
linquize
5fa4403d20 feat(container-stats): add refresh rate of 1 and 3 seconds (#2493) 2018-12-03 21:49:02 +13:00
Anthony Lapenna
dc9a878f4b chore(docker): update docker binary version to 18.09.0 (#2510) 2018-12-03 12:10:55 +13:00
baron_l
969f70edeb fix(image-upload): uploading a tar with multiple images wont display an error anymore (#2503) 2018-11-29 07:00:58 +13:00
baron_l
c778e79004 fix(container-console): close the console when selected shell does not exist inside the container (#2502) 2018-11-29 06:57:36 +13:00
Chaim Lev-Ari
34b886d690 chore(build-system): add start and start:server scripts (#2495) 2018-11-27 10:05:13 +13:00
Andreas Roussos
b809177147 feat(dashboard): use plural form only when required
* fix(endpoint-item): use plural form only when required

* refactor(endpoint-item): use clearer patterns

* refactor(dashboard): use clearer patterns
2018-11-25 09:46:13 +13:00
baron_l
52788029ed feat(container-details): add visual feedback when creating image from container (#2487) 2018-11-24 11:11:58 +13:00
Anthony Lapenna
d510bbbcfd feat(api): filter LDAP password from settings response (#2488) 2018-11-24 08:40:56 +13:00
Olli Janatuinen
17d63ae3ca chore(dependencies): updated xterm to 3.8.0 version (#2452) 2018-11-23 22:00:30 +13:00
baron_l
5e49f934b9 fix(containers-stats): accessing a down container stats wont display a js error anymore (#2484) 2018-11-23 21:44:34 +13:00
Anthony Lapenna
d03fd5805a feat(api): support AGENT_SECRET environment variable (#2486) 2018-11-23 11:46:51 +13:00
baron_l
fe8dfee69a feat(home): display each endpoint URL (#2471) 2018-11-19 19:07:38 +13:00
baron_l
488dc5f9db fix(network-creation): macvlan availability for standalone endpoints (#2441) 2018-11-16 13:26:56 +13:00
Anthony Lapenna
0ef25a4cbd fix(schedules): add schedule name validation and remove endpoint name prefix (#2470) 2018-11-14 16:10:49 +13:00
Anthony Lapenna
94d3d7bde2 feat(motd): relocate motd file URL and always return 200 (#2466) 2018-11-14 12:20:33 +13:00
Christer Warén
40e0c3879c style(dashboard): change blocklist-item border color (#2465)
Changing blocklist-item border color to more confortable color that makes UI look more consistence
2018-11-14 10:01:36 +13:00
baron_l
d455ab3fc7 feat(endpoints): enhance offline browsing (#2454)
* feat(api): rewrite error response when trying to query a down endpoint

* feat(interceptors): adding custom backend return code on offline fastfail
2018-11-13 16:08:12 +13:00
Anthony Lapenna
0825d05546 feat(endpoints): improve offline banner UX (#2462)
* feat(endpoints): add the last snapshot timestamp in offline banner

* feat(endpoints): add the ability to refresh a snapshot in the offline banner
2018-11-13 16:02:49 +13:00
Anthony Lapenna
cf370f6a4c refactor(endpoints): remove time.Sleep call 2018-11-13 15:19:29 +13:00
Anthony Lapenna
381ab81fdd fix(endpoints): ensure endpoint is up to date after snapshot (#2460)
* feat(snapshots): fix a potential concurrency issue with endpoint snapshots

* fix(endpoints): ensure endpoint is up to date after snapshot
2018-11-13 15:18:38 +13:00
Anthony Lapenna
64c29f7402 feat(schedules): add the ability to list tasks from snapshots (#2458)
* feat(schedules): add the ability to list tasks from snapshots

* feat(schedules): update schedules

* refactor(schedules): fix linting issue
2018-11-13 14:39:26 +13:00
Anthony Lapenna
a2d9f591a7 feat(schedules): add retry policy to script schedules (#2445) 2018-11-09 15:22:08 +13:00
Anthony Lapenna
e7ab057c81 feat(sidebar): add a new Scheduler top entry 2018-11-08 14:09:21 +13:00
Yassir Hannoun
309620545c fix(container-stat): fix cpu/mem charts on Windows containers
* Fixing the CPU and Memory charts on Windows containers

* Fixing the CPU and Memory charts on Windows containers
2018-11-08 13:31:33 +13:00
Dmitriy Larionov
55b50c2a49 feat(container-creation): allow escaped quotes in command field (#2419) 2018-11-08 09:53:19 +13:00
Anthony Lapenna
807c830db0 feat(schedules): add the ability to update a schedule script (#2438) 2018-11-07 17:19:10 +13:00
Anthony Lapenna
695c28d4f8 fix(host): fix a typo in job history clear notification 2018-11-07 16:06:27 +13:00
Anthony Lapenna
4740375ba5 feat(schedules): add schedules UI (#2414)
* feat(schedules): add schedules UI mockups

* feat(schedules): update controller pattern

* feat(schedules): leverages API

* feat(schedules): add the ability create/edit a script execution job schedule

* feat(schedules): add form validation and details about cron expression
2018-11-07 11:59:21 +13:00
Anthony Lapenna
7d32a6619d feat(api): add created property for schedules (#2435) 2018-11-07 09:22:30 +13:00
Anthony Lapenna
110fcc46a6 feat(api): revamp scheduling to introduce system schedules (#2433)
* feat(api): revamp scheduling to introduce system schedules

* fix(api): fix linting issues

* fix(api): fix lint issues

* refactor(api): fix lint issues
2018-11-06 22:49:48 +13:00
Chaim Lev-Ari
dbbea0a20f feat(schedules): add the schedule API
* feat(jobs): add job service interface

* feat(jobs): create job execution api

* style(jobs): remove comment

* feat(jobs): add bindings

* feat(jobs): validate payload different cases

* refactor(jobs): rename endpointJob method

* refactor(jobs): return original error

* feat(jobs): pull image before creating container

* feat(jobs): run jobs with sh

* style(jobs): remove comment

* refactor(jobs): change error names

* feat(jobs): sync pull image

* fix(jobs): close image reader after error check

* style(jobs): remove comment and add docs

* refactor(jobs): inline script command

* fix(jobs): handle pul image error

* refactor(jobs): handle image pull output

* fix(docker): set http client timeout to 100s

* feat(api): create schedule type

* feat(agent): add basic schedule api

* feat(schedules): add schedule service in bolt

* feat(schedule): add schedule service to handler

* feat(schedule): add and list schedules from db

* feat(agent): get schedule from db

* feat(schedule): update schedule in db

* feat(agent): delete schedule

* fix(bolt): remove sync method from scheduleService

* feat(schedules): save/delete script in fs

* feat(schedules): schedules cron service implementation

* feat(schedule): integrate handler with cron

* feat(schedules): schedules API overhaul

* refactor(project): remove .idea folder

* fix(schedules): fix script task execute call

* refactor(schedules): refactor/fix golint issues

* refactor(schedules): update SnapshotTask documentation

* refactor(schedules): validate image name in ScheduleCreate operation
2018-11-06 09:58:15 +13:00
Anthony Lapenna
e94d6ad6b2 docs(swagger): update EndpointCreate operation 2018-11-01 07:32:41 +13:00
Jan Jansen
78bf374548 feat(ux): normalize quick actions buttons (#2389)
* feat(ux): normalize quick actions buttons

Fixes #2013

* fix(ux): fix wrong naming of variable
2018-10-31 15:50:38 +13:00
pc
8df64031e8 feat(log-viewer): change line count default to 100 and add a since parameter (#2377)
* chore(log-viewer): add the ability to use`since` parameter #1942

https://github.com/portainer/portainer/issues/1942#issuecomment-430246378

* chore(log-viewer): change lineCount to 100 #1942

https://github.com/portainer/portainer/issues/1942#issuecomment-430246378

* fix(log-viewer): js syntax typo for `;` and `'`

forget to lint the code, reported by codeclimate

* fix(log-viewer): use mementjs to format timestamp

1. use moment lib instead of define a function in filter.js(not the right place for this function, removed)
2. set sinceTimestamp init value to `24 hours ago`, as we just need to focus on the relative latest logs after the log-viewer loading, not all the logs(to speedup the process)
3. use moment().unix() to convert the `sinceTimestamp`  to local unix timestamp(not utc)

* chore(log-viewer): add the ability to select the datetime for `since`

* chore(log-viewer): add the ability to fetch logs from specific time
2018-10-29 17:49:35 +13:00
baron_l
a61654a35d feat(endpoints): add the ability to browse offline endpoints (#2253)
* feat(back): saved data in snapshot

* feat(endpoints): adding interceptors to retrieve saved data on offline endpoints

* feat(endpoints): offline dashboard working - need tests on offline views

* refactor(endpoints): interceptors cleaning and saving/loading offline endpoints data in/from localstorage

* feat(endpoints): browsing offline endpoints

* feat(endpoints): removing all the link in offline mode - sidebar not working when switching between off and on modes w/ stateManager logic

* feat(endpoints): endpoint status detection in real time

* fix(endpoints): offline swarm endpoint are not accessible anymore

* fix(endpoints): refactor message + disable offline browsing for an endpoint when no snapshot is available for it

* fix(endpoints): adding timeout and enabling loading bar for offline requests

* fix(endpoints): trying to access a down endpoint wont remove sidebar items if it fails

* feat(endpoints): disable checkboxes on offline views for offline mode

* feat(endpoints): updating endpoint status when detecting a change

* refactor(host): moved offline status panel from engine view to new host view

* fix(endpoints): missing endpoint update on ping from home view

* fix(api): rework EndpointUpdate operation

* refactor(offline): moved endpoint status to EndpointProvider and refactor the status-changed detection

* fix(offline): moved status detection to callback on views -> prevent displaying the offline message when endpoint is back online on view change

* fix(offline): offline message is now displayed online when browsing an offline endpoint

* fix(offline): sidebar updates correctly on endpoint status change

* fix(offline): offline panel not displayed and hidden on online mode

* refactor(offline): rework of OfflineMode management

* refactor(offline): extract information-panel for offlineMode into a component

* refactor(offline): remove redundant binding of informationPanel + endpointStatusInterceptor patter as service

* refactor(interceptors): moved interceptors pattern to service pattern

* feat(stacks): prevent inspection of a stack in offline mode

* feat(host): hide devices/disk panels in offline mode

* feat(host): disable browse action in offline mode

* refactor(home): remove comments
2018-10-28 22:27:06 +13:00
baron_l
354fda31f1 feat(jobs): add the ability to run a job on a target endpoint #2374
* feat(jobs): adding the ability to run scripts on endpoints

fix(job): click on containerId in JobsDatatable redirects to container's logs
refactor(job): remove the jobs datatable settings + texts changes on JobCreation view
fix(jobs): jobs payloads are now following API rules and case
feat(jobs): adding the capability to run scripts on hosts

* feat(jobs): adding the ability to purge jobs containers

* refactor(job): apply review changes

* feat(job-creation): store image name in local storage

* feat(host): disable job exec link in non-agent Swarm setup

* feat(host): only display execute job in agent setups or standalone

* feat(job): job execution overhaul

* docs(swagger): update EndpointJob documentation
2018-10-28 19:06:50 +13:00
Mark Stansberry
6ab510e5cb docs(api): update swagger related files to support swagger-codegen (#2404)
* Linting updates to api/swagger.yaml

* Security updates to api/swagger.yml

* Add api/swagger_config.json for swagger-codegen

* Add swagger_config.json packageVersion to match swagger.yml
2018-10-28 16:05:54 +13:00
Damian Czaja
7e6c647e93 feat(container-creation): add the ability to override the logging driver (#2384) 2018-10-28 16:00:56 +13:00
Yassir Hannoun
07c1e1bc3e feat(container-stats): display cache in memory usage chart (#2383) 2018-10-28 15:45:02 +13:00
Ricardo Cardona Ramirez
fe6ca042f3 feat(ux): Alphabetically sort configs and secrets in service details/creation (#2396)
* fix(sorting): Alphabetically sort configs in service details select box
* fix(sorting): Alphabetically sort configs and secrets  for service creation
2018-10-28 15:39:09 +13:00
Chaim Lev-Ari
9813099aa4 feat(app): toggle features based on agent API version (#2378)
* feat(agent): get agent's version from ping

* feat(agent): add version to api url

* feat(agent): query agent with api version

* feat(agent): rename agent api version name on state

* feat(agent): disable feature based on agent's api version

* style(agent): rename ping rest service + remove whitespaces

* style(state): remove whitespace

* style(agent): add whitespace

* fix(agent): remove check for error status 403

* refactor(agent): rename ping file name

* refactor(agent): move old services to v1 folder

* refactor(agent): turn ping service to usual pattern

* refactor(agent): change version to a global variable

* refactor(agent): move ping to version2

* refactor(agent): restore ping to use root ping

* fix(volumes): add volumeID to browse api path

* feat(volume): add upload button to volume browser
2018-10-26 16:16:29 +13:00
Yassir Hannoun
cca378b2e8 docs(README): fix semaphore badge 2018-10-24 08:55:30 +13:00
Anthony Lapenna
b5dfaff292 refactor(app): refactor unauthenticated state management (#2393)
* refactor(app): refactor Authentication service

* refactor(app): refactor unauthenticated state management
2018-10-23 17:28:59 +13:00
Anthony Lapenna
4f9a8180f9 docs(swagger): document the endpoint job execution (#2392) 2018-10-23 11:59:43 +13:00
Anthony Lapenna
14d2bf4ebb refactor(api): fix typo (#2391)
* refactor(api): fix typo

* refactor(api): remove newline
2018-10-23 10:07:39 +13:00
Chaim Lev-Ari
65291c68e9 feat(jobs): add the job execution API
* feat(jobs): add job service interface

* feat(jobs): create job execution api

* style(jobs): remove comment

* feat(jobs): add bindings

* feat(jobs): validate payload different cases

* refactor(jobs): rename endpointJob method

* refactor(jobs): return original error

* feat(jobs): pull image before creating container

* feat(jobs): run jobs with sh

* style(jobs): remove comment

* refactor(jobs): change error names

* feat(jobs): sync pull image

* fix(jobs): close image reader after error check

* style(jobs): remove comment and add docs

* refactor(jobs): inline script command

* fix(jobs): handle pul image error

* refactor(jobs): handle image pull output

* fix(docker): set http client timeout to 100s

* fix(client): remove timeout from http client
2018-10-23 10:03:30 +13:00
Yassir Hannoun
719299d75b fix(container-stat) : exclude cache from the Memory Usage chart to avoid misinterpret… (#2371) 2018-10-18 10:00:45 +13:00
1576 changed files with 107228 additions and 35640 deletions

13
.babelrc Normal file
View File

@@ -0,0 +1,13 @@
{
"plugins": ["lodash", "angularjs-annotate"],
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"useBuiltIns": "entry",
"corejs": "2"
}
]
]
}

View File

@@ -1,61 +1,44 @@
version: "2"
checks:
argument-count:
enabled: true
config:
threshold: 4
enabled: false
complex-logic:
enabled: true
config:
threshold: 4
enabled: false
file-lines:
enabled: true
config:
threshold: 300
enabled: false
method-complexity:
enabled: false
method-count:
enabled: true
config:
threshold: 20
enabled: false
method-lines:
enabled: true
config:
threshold: 50
enabled: false
nested-control-flow:
enabled: true
config:
threshold: 4
enabled: false
return-statements:
enabled: false
similar-code:
enabled: true
config:
threshold: #language-specific defaults. overrides affect all languages.
enabled: false
identical-code:
enabled: true
config:
threshold: #language-specific defaults. overrides affect all languages.
enabled: false
plugins:
gofmt:
enabled: true
golint:
enabled: true
govet:
enabled: true
csslint:
enabled: true
duplication:
enabled: true
config:
languages:
javascript:
mass_threshold: 80
eslint:
enabled: true
channel: "eslint-5"
config:
config: .eslintrc.yml
fixme:
enabled: true
exclude_patterns:
- assets/
- build/
- dist/
- distribution/
- node_modules
- test/
- webpack/
- gruntfile.js
- webpack.config.js
- api/
- "!app/kubernetes/**"
- .github/
- .tmp/

3
.eslintignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules/
dist/
test/

View File

@@ -1,287 +1,28 @@
env:
browser: true
jquery: true
node: true
es6: true
globals:
angular: true
# globals:
# angular: true
# $: true
# _: true
# moment: true
# filesize: true
# splitargs: true
extends:
- 'eslint:recommended'
- prettier
plugins:
- import
parserOptions:
ecmaVersion: 2018
sourceType: module
ecmaFeatures:
modules: true
# http://eslint.org/docs/rules/
rules:
# Possible Errors
no-await-in-loop: off
no-cond-assign: error
no-console: off
no-constant-condition: error
no-control-regex: error
no-debugger: error
no-dupe-args: error
no-dupe-keys: error
no-duplicate-case: error
no-empty-character-class: error
no-empty: error
no-ex-assign: error
no-extra-boolean-cast: error
no-extra-parens: off
no-extra-semi: error
no-func-assign: error
no-inner-declarations:
- error
- functions
no-invalid-regexp: error
no-irregular-whitespace: error
no-negated-in-lhs: error
no-obj-calls: error
no-prototype-builtins: off
no-regex-spaces: error
no-sparse-arrays: error
no-template-curly-in-string: off
no-unexpected-multiline: error
no-unreachable: error
no-unsafe-finally: off
no-unsafe-negation: off
use-isnan: error
valid-jsdoc: off
valid-typeof: error
# Best Practices
accessor-pairs: error
array-callback-return: off
block-scoped-var: off
class-methods-use-this: off
complexity:
- error
- 6
consistent-return: off
curly: off
default-case: off
dot-location: off
dot-notation: off
eqeqeq: error
guard-for-in: error
no-alert: error
no-caller: error
no-case-declarations: error
no-div-regex: error
no-else-return: off
no-empty-function: off
no-empty-pattern: error
no-eq-null: error
no-eval: error
no-extend-native: error
no-extra-bind: error
no-extra-label: off
no-fallthrough: error
no-floating-decimal: off
no-global-assign: off
no-implicit-coercion: off
no-implied-eval: error
no-invalid-this: off
no-iterator: error
no-labels:
- error
- allowLoop: true
allowSwitch: true
no-lone-blocks: error
no-loop-func: error
no-magic-number: off
no-multi-spaces: off
no-multi-str: off
no-native-reassign: error
no-new-func: error
no-new-wrappers: error
no-new: error
no-octal-escape: error
no-octal: error
no-param-reassign: off
no-proto: error
no-redeclare: error
no-restricted-properties: off
no-return-assign: error
no-return-await: off
no-script-url: error
no-self-assign: off
no-self-compare: error
no-sequences: off
no-throw-literal: off
no-unmodified-loop-condition: off
no-unused-expressions: error
no-unused-labels: off
no-useless-call: error
no-useless-concat: error
no-control-regex: off
no-empty: warn
no-empty-function: warn
no-useless-escape: off
no-useless-return: off
no-void: error
no-warning-comments: off
no-with: error
prefer-promise-reject-errors: off
radix: error
require-await: off
vars-on-top: off
wrap-iife: error
yoda: off
# Strict
strict: off
# Variables
init-declarations: off
no-catch-shadow: error
no-delete-var: error
no-label-var: error
no-restricted-globals: off
no-shadow-restricted-names: error
no-shadow: off
no-undef-init: error
no-undef: off
no-undefined: off
no-unused-vars:
- warn
-
vars: local
no-use-before-define: off
# Node.js and CommonJS
callback-return: error
global-require: error
handle-callback-err: error
no-mixed-requires: off
no-new-require: off
no-path-concat: error
no-process-env: off
no-process-exit: error
no-restricted-modules: off
no-sync: off
# Stylistic Issues
array-bracket-spacing: off
block-spacing: off
brace-style: off
camelcase: off
capitalized-comments: off
comma-dangle:
- error
- never
comma-spacing: off
comma-style: off
computed-property-spacing: off
consistent-this: off
eol-last: off
func-call-spacing: off
func-name-matching: off
func-names: off
func-style: off
id-length: off
id-match: off
indent: off
jsx-quotes: off
key-spacing: off
keyword-spacing: off
line-comment-position: off
linebreak-style:
- error
- unix
lines-around-comment: off
lines-around-directive: off
max-depth: off
max-len: off
max-nested-callbacks: off
max-params: off
max-statements-per-line: off
max-statements:
- error
- 30
multiline-ternary: off
new-cap: off
new-parens: off
newline-after-var: off
newline-before-return: off
newline-per-chained-call: off
no-array-constructor: off
no-bitwise: off
no-continue: off
no-inline-comments: off
no-lonely-if: off
no-mixed-operators: off
no-mixed-spaces-and-tabs: off
no-multi-assign: off
no-multiple-empty-lines: off
no-negated-condition: off
no-nested-ternary: off
no-new-object: off
no-plusplus: off
no-restricted-syntax: off
no-spaced-func: off
no-tabs: off
no-ternary: off
no-trailing-spaces: off
no-underscore-dangle: off
no-unneeded-ternary: off
object-curly-newline: off
object-curly-spacing: off
object-property-newline: off
one-var-declaration-per-line: off
one-var: off
operator-assignment: off
operator-linebreak: off
padded-blocks: off
quote-props: off
quotes:
- error
- single
require-jsdoc: off
semi-spacing: off
semi:
- error
- always
sort-keys: off
sort-vars: off
space-before-blocks: off
space-before-function-paren: off
space-in-parens: off
space-infix-ops: off
space-unary-ops: off
spaced-comment: off
template-tag-spacing: off
unicode-bom: off
wrap-regex: off
# ECMAScript 6
arrow-body-style: off
arrow-parens: off
arrow-spacing: off
constructor-super: off
generator-star-spacing: off
no-class-assign: off
no-confusing-arrow: off
no-const-assign: off
no-dupe-class-members: off
no-duplicate-imports: off
no-new-symbol: off
no-restricted-imports: off
no-this-before-super: off
no-useless-computed-key: off
no-useless-constructor: off
no-useless-rename: off
no-var: off
object-shorthand: off
prefer-arrow-callback: off
prefer-const: off
prefer-destructuring: off
prefer-numeric-literals: off
prefer-rest-params: off
prefer-reflect: off
prefer-spread: off
prefer-template: off
require-yield: off
rest-spread-spacing: off
sort-imports: off
symbol-description: off
template-curly-spacing: off
yield-star-spacing: off
import/order: error

View File

@@ -1,47 +1,55 @@
---
name: Bug report
about: Create a bug report
---
<!--
Thanks for reporting a bug for Portainer !
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/ or gitter https://gitter.im/portainer/Lobby.
Before opening a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
Also, be sure to check our FAQ and documentation first: https://portainer.readthedocs.io
-->
**Bug description**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
Briefly describe what you were expecting.
**Steps to reproduce the issue:**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Technical details:**
* Portainer version:
* Docker version (managed by Portainer):
* Platform (windows/linux):
* Command used to start Portainer (`docker run -p 9000:9000 portainer/portainer`):
* Browser:
**Additional context**
Add any other context about the problem here.
---
name: Bug report
about: Create a bug report
title: ''
labels: bug/need-confirmation, kind/bug
assignees: ''
---
<!--
Thanks for reporting a bug for Portainer !
You can find more information about Portainer support framework policy here: https://www.portainer.io/2019/04/portainer-support-policy/
Do you need help or have a question? Come chat with us on Slack http://portainer.slack.com/
Before opening a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
Also, be sure to check our FAQ and documentation first: https://documentation.portainer.io/
-->
**Bug description**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Portainer Logs**
Provide the logs of your Portainer container or Service.
You can see how [here](https://documentation.portainer.io/archive/1.23.2/faq/#how-do-i-get-the-logs-from-portainer)
**Steps to reproduce the issue:**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Technical details:**
- Portainer version:
- Docker version (managed by Portainer):
- Kubernetes version (managed by Portainer):
- Platform (windows/linux):
- Command used to start Portainer (`docker run -p 9000:9000 portainer/portainer`):
- Browser:
- Use Case (delete as appropriate): Using Portainer at Home, Using Portainer in a Commerical setup.
- Have you reviewed our technical documentation and knowledge base? Yes/No
**Additional context**
Add any other context about the problem here.

View File

@@ -1,15 +1,25 @@
---
name: Question
about: Ask us a question about Portainer usage or deployment
---
<!--
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/ or gitter https://gitter.im/portainer/Lobby.
Also, be sure to check our FAQ and documentation first: https://portainer.readthedocs.io
-->
**Question**:
How can I deploy Portainer on... ?
---
name: Question
about: Ask us a question about Portainer usage or deployment
title: ''
labels: ''
assignees: ''
---
Before you start, we need a little bit more information from you:
Use Case (delete as appropriate): Using Portainer at Home, Using Portainer in a Commerical setup.
Have you reviewed our technical documentation and knowledge base? Yes/No
<!--
You can find more information about Portainer support framework policy here: https://old.portainer.io/2019/04/portainer-support-policy/
Do you need help or have a question? Come chat with us on Slack http://portainer.slack.com/
Also, be sure to check our FAQ and documentation first: https://documentation.portainer.io/
-->
**Question**:
How can I deploy Portainer on... ?

View File

@@ -1,31 +1,34 @@
---
name: Feature request
about: Suggest a feature/enhancement that should be added in Portainer
---
<!--
Thanks for opening a feature request for Portainer !
Do you need help or have a question? Come chat with us on Slack http://portainer.io/slack/ or gitter https://gitter.im/portainer/Lobby.
Before opening a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
Also, be sure to check our FAQ and documentation first: https://portainer.readthedocs.io
-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
---
name: Feature request
about: Suggest a feature/enhancement that should be added in Portainer
title: ''
labels: ''
assignees: ''
---
<!--
Thanks for opening a feature request for Portainer !
Do you need help or have a question? Come chat with us on Slack http://portainer.slack.com/
Before opening a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.
Also, be sure to check our FAQ and documentation first: https://documentation.portainer.io/
-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

54
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
# Config for Stalebot, limited to only `issues`
only: issues
# Issues config
issues:
daysUntilStale: 60
daysUntilClose: 7
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Issues with these labels will never be considered stale
exemptLabels:
- kind/enhancement
- kind/question
- kind/style
- kind/workaround
- kind/refactor
- bug/need-confirmation
- bug/confirmed
- status/discuss
# Only issues with all of these labels are checked if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: true
# Label to use when marking an issue as stale
staleLabel: status/stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been marked as stale as it has not had recent activity,
it will be closed if no further activity occurs in the next 7 days.
If you believe that it has been incorrectly labelled as stale,
leave a comment and the label will be removed.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Since no further activity has appeared on this issue it will be closed.
If you believe that it has been incorrectly closed, leave a comment
mentioning `ametdoohan`, `balasu` or `keverv` and one of our staff will then review the issue.
Note - If it is an old bug report, make sure that it is reproduceable in the
latest version of Portainer as it may have already been fixed.

19
.github/workflows/rebase.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Automatic Rebase
on:
issue_comment:
types: [created]
jobs:
rebase:
name: Rebase
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase')
runs-on: ubuntu-latest
steps:
- name: Checkout the latest code
uses: actions/checkout@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0 # otherwise, you will fail to push refs to dest repo
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

10
.gitignore vendored
View File

@@ -4,4 +4,12 @@ dist
portainer-checksum.txt
api/cmd/portainer/portainer*
.tmp
.vscode
**/.vscode/settings.json
**/.vscode/tasks.json
.eslintcache
__debug_bin
api/docs
.idea
.env

13
.prettierrc Normal file
View File

@@ -0,0 +1,13 @@
{
"printWidth": 180,
"singleQuote": true,
"htmlWhitespaceSensitivity": "strict",
"overrides": [
{
"files": ["*.html"],
"options": {
"parser": "angular"
}
}
]
}

View File

@@ -0,0 +1,19 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceRoot}/api/cmd/portainer/main.go",
"cwd": "${workspaceRoot}",
"env": {},
"showLog": true,
"args": ["--data", "${env:HOME}/portainer-data", "--assets", "${workspaceRoot}/dist"]
}
]
}

View File

@@ -0,0 +1,167 @@
{
// Place your portainer workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
// "Print to console": {
// "scope": "javascript,typescript",
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Component": {
"scope": "javascript",
"prefix": "mycomponent",
"description": "Dummy Angularjs Component",
"body": [
"import angular from 'angular';",
"import controller from './${TM_FILENAME_BASE}Controller'",
"",
"angular.module('portainer.${TM_DIRECTORY/.*\\/app\\/([^\\/]*)(\\/.*)?$/$1/}').component('$TM_FILENAME_BASE', {",
" templateUrl: './$TM_FILENAME_BASE.html',",
" controller,",
"});",
""
]
},
"Controller": {
"scope": "javascript",
"prefix": "mycontroller",
"body": [
"class ${TM_FILENAME_BASE/(.*)/${1:/capitalize}/} {",
"\t/* @ngInject */",
"\tconstructor($0) {",
"\t}",
"}",
"",
"export default ${TM_FILENAME_BASE/(.*)/${1:/capitalize}/};"
],
"description": "Dummy ES6+ controller"
},
"Service": {
"scope": "javascript",
"prefix": "myservice",
"description": "Dummy ES6+ service",
"body": [
"import angular from 'angular';",
"import PortainerError from 'Portainer/error';",
"",
"class $1 {",
" /* @ngInject */",
" constructor(\\$async, $0) {",
" this.\\$async = \\$async;",
"",
" this.getAsync = this.getAsync.bind(this);",
" this.getAllAsync = this.getAllAsync.bind(this);",
" this.createAsync = this.createAsync.bind(this);",
" this.updateAsync = this.updateAsync.bind(this);",
" this.deleteAsync = this.deleteAsync.bind(this);",
" }",
"",
" /**",
" * GET",
" */",
" async getAsync() {",
" try {",
"",
" } catch (err) {",
" throw new PortainerError('', err);",
" }",
" }",
"",
" async getAllAsync() {",
" try {",
"",
" } catch (err) {",
" throw new PortainerError('', err);",
" }",
" }",
"",
" get() {",
" if () {",
" return this.\\$async(this.getAsync);",
" }",
" return this.\\$async(this.getAllAsync);",
" }",
"",
" /**",
" * CREATE",
" */",
" async createAsync() {",
" try {",
"",
" } catch (err) {",
" throw new PortainerError('', err);",
" }",
" }",
"",
" create() {",
" return this.\\$async(this.createAsync);",
" }",
"",
" /**",
" * UPDATE",
" */",
" async updateAsync() {",
" try {",
"",
" } catch (err) {",
" throw new PortainerError('', err);",
" }",
" }",
"",
" update() {",
" return this.\\$async(this.updateAsync);",
" }",
"",
" /**",
" * DELETE",
" */",
" async deleteAsync() {",
" try {",
"",
" } catch (err) {",
" throw new PortainerError('', err);",
" }",
" }",
"",
" delete() {",
" return this.\\$async(this.deleteAsync);",
" }",
"}",
"",
"export default $1;",
"angular.module('portainer.${TM_DIRECTORY/.*\\/app\\/([^\\/]*)(\\/.*)?$/$1/}').service('$1', $1);"
]
},
"swagger-api-doc": {
"prefix": "swapi",
"scope": "go",
"description": "Snippet for a api doc",
"body": [
"// @id ",
"// @summary ",
"// @description ",
"// @description **Access policy**: ",
"// @tags ",
"// @security jwt",
"// @accept json",
"// @produce json",
"// @param id path int true \"identifier\"",
"// @param body body Object true \"details\"",
"// @success 200 {object} portainer. \"Success\"",
"// @success 204 \"Success\"",
"// @failure 400 \"Invalid request\"",
"// @failure 403 \"Permission denied\"",
"// @failure 404 \" not found\"",
"// @failure 500 \"Server error\"",
"// @router /{id} [get]"
]
}
}

32
ATTRIBUTIONS.md Normal file
View File

@@ -0,0 +1,32 @@
# Open Source License Attribution
This application uses Open Source components. You can find the source
code of their open source projects along with license information below.
We acknowledge and are grateful to these developers for their contributions
to open source.
### [angular-json-tree](https://github.com/awendland/angular-json-tree)
by [Alex Wendland](https://github.com/awendland) is licensed under [CC BY 4.0 License](https://creativecommons.org/licenses/by/4.0/)
### [caniuse-db](https://github.com/Fyrd/caniuse)
by [caniuse.com](caniuse.com) is licensed under [CC BY 4.0 License](https://creativecommons.org/licenses/by/4.0/)
### [caniuse-lite](https://github.com/ben-eb/caniuse-lite)
by [caniuse.com](caniuse.com) is licensed under [CC BY 4.0 License](https://creativecommons.org/licenses/by/4.0/)
### [spdx-exceptions](https://github.com/jslicense/spdx-exceptions.json)
by Kyle Mitchell using [SPDX](https://spdx.dev/) from Linux Foundation licensed under [CC BY 3.0 License](https://creativecommons.org/licenses/by/3.0/)
### [fontawesome-free](https://github.com/FortAwesome/Font-Awesome) Icons
by [Fort Awesome](https://fortawesome.com/) is licensed under [CC BY 4.0 License](https://creativecommons.org/licenses/by/4.0/)
Portainer also contains the following code, which is licensed under the [MIT license](https://opensource.org/licenses/MIT):
UI For Docker: Copyright (c) 2013-2016 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com), Anthony Lapenna (portainer.io)
rdash-angular: Copyright (c) [2014][elliot hesp]

View File

@@ -6,30 +6,16 @@ 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`.
## Issues open to contribution
Want to contribute but don't know where to start?
Some of the open issues are labeled with prefix `exp/`, this is used to mark them as available for contributors to work on. All of these have an attributed difficulty level:
* **beginner**: a task that should be accessible with users not familiar with the codebase
* **intermediate**: a task that require some understanding of the project codebase or some experience in
either AngularJS or Golang
* **advanced**: a task that require a deep understanding of the project codebase
You can use Github filters to list these issues:
* beginner labeled issues: https://github.com/portainer/portainer/labels/exp%2Fbeginner
* intermediate labeled issues: https://github.com/portainer/portainer/labels/exp%2Fintermediate
* advanced labeled issues: https://github.com/portainer/portainer/labels/exp%2Fadvanced
Want to contribute but don't know where to start? Have a look at the issues labeled with the `good first issue` label: https://github.com/portainer/portainer/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22
## Commit Message Format
@@ -51,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
@@ -71,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
@@ -88,3 +74,62 @@ Our contribution process is described below. Some of the steps can be visualized
The feature request process is similar to the bug report process but has an extra functional validation before the technical validation as well as a documentation validation before the testing phase.
![portainer_featurerequest_workflow](https://user-images.githubusercontent.com/5485061/45727229-5ad39f00-bbf5-11e8-9550-16ba66c50615.png)
## Build Portainer locally
Ensure you have Docker, Node.js, yarn, and Golang installed in the correct versions.
Install dependencies with yarn:
```sh
$ yarn
```
Then build and run the project:
```sh
$ yarn start
```
Portainer can now be accessed at <http://localhost:9000>.
Find more detailed steps at <https://documentation.portainer.io/contributing/instructions/>.
## Adding api docs
When adding a new resource (or a route handler), we should add a new tag to api/http/handler/handler.go#L136 like this:
```
// @tag.name <Name of resource>
// @tag.description a short description
```
When adding a new route to an existing handler use the following as a template (you can use `swapi` snippet if you're using vscode):
```
// @id
// @summary
// @description
// @description **Access policy**:
// @tags
// @security jwt
// @accept json
// @produce json
// @param id path int true "identifier"
// @param body body Object true "details"
// @success 200 {object} portainer. "Success"
// @success 204 "Success"
// @failure 400 "Invalid request"
// @failure 403 "Permission denied"
// @failure 404 " not found"
// @failure 500 "Server error"
// @router /{id} [get]
```
explanation about each line can be found (here)[https://github.com/swaggo/swag#api-operation]
## Licensing
See the [LICENSE](https://github.com/portainer/portainer/blob/develop/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.

View File

@@ -1,27 +1,19 @@
<p align="center">
<img title="portainer" src='https://portainer.io/images/logo_alt.png' />
<img title="portainer" src='https://github.com/portainer/portainer/blob/develop/app/assets/images/logo_alt.png?raw=true' />
</p>
[![Docker Pulls](https://img.shields.io/docker/pulls/portainer/portainer.svg)](https://hub.docker.com/r/portainer/portainer/)
[![Microbadger](https://images.microbadger.com/badges/image/portainer/portainer.svg)](http://microbadger.com/images/portainer/portainer "Image size")
[![Documentation Status](https://readthedocs.org/projects/portainer/badge/?version=stable)](http://portainer.readthedocs.io/en/stable/?badge=stable)
[![Build Status](https://semaphoreci.com/api/v1/portainer/portainer/branches/develop/badge.svg)](https://semaphoreci.com/portainer/portainer)
[![Microbadger](https://images.microbadger.com/badges/image/portainer/portainer.svg)](http://microbadger.com/images/portainer/portainer 'Image size')
[![Build Status](https://portainer.visualstudio.com/Portainer%20CI/_apis/build/status/Portainer%20CI?branchName=develop)](https://portainer.visualstudio.com/Portainer%20CI/_build/latest?definitionId=3&branchName=develop)
[![Code Climate](https://codeclimate.com/github/portainer/portainer/badges/gpa.svg)](https://codeclimate.com/github/portainer/portainer)
[![Slack](https://portainer.io/slack/badge.svg)](https://portainer.io/slack/)
[![Gitter](https://badges.gitter.im/portainer/Lobby.svg)](https://gitter.im/portainer/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHXZJQNJQ36H6)
**_Portainer_** is a lightweight management UI which allows you to **easily** manage your different Docker environments (Docker hosts or Swarm clusters).
**_Portainer_** is meant to be as **simple** to deploy as it is to use. It consists of a single container that can run on any Docker engine (can be deployed as Linux container or a Windows native container).
**_Portainer_** allows you to manage your Docker containers, images, volumes, networks and more ! It is compatible with the *standalone Docker* engine and with *Docker Swarm mode*.
**_Portainer_** is meant to be as **simple** to deploy as it is to use. It consists of a single container that can run on any Docker engine (can be deployed as Linux container or a Windows native container, supports other platforms too).
**_Portainer_** allows you to manage all your Docker resources (containers, images, volumes, networks and more!) It is compatible with the _standalone Docker_ engine and with _Docker Swarm mode_.
## Demo
<img src="https://portainer.io/images/screenshots/portainer.gif" width="77%"/>
You can try out the public demo instance: http://demo.portainer.io/ (login with the username **admin** and the password **tryportainer**).
Please note that the public demo cluster is **reset every 15min**.
@@ -32,42 +24,45 @@ Alternatively, you can deploy a copy of the demo stack inside a [play-with-docke
- Sign in with your [Docker ID](https://docs.docker.com/docker-id)
- Follow [these](https://github.com/portainer/portainer-demo/blob/master/play-with-docker/docker-stack.yml#L5-L8) steps.
Unlike the public demo, the playground sessions are deleted after 4 hours. Apart from that, all the settings are same, including default credentials.
Unlike the public demo, the playground sessions are deleted after 4 hours. Apart from that, all the settings are the same, including default credentials.
## Getting started
* [Deploy Portainer](https://portainer.readthedocs.io/en/latest/deployment.html)
* [Documentation](https://portainer.readthedocs.io)
- [Deploy Portainer](https://documentation.portainer.io/quickstart/)
- [Documentation](https://documentation.portainer.io)
- [Building Portainer](https://documentation.portainer.io/contributing/instructions/)
## Getting help
* Issues: https://github.com/portainer/portainer/issues
* FAQ: https://portainer.readthedocs.io/en/latest/faq.html
* Slack (chat): https://portainer.io/slack/
* Gitter (chat): https://gitter.im/portainer/Lobby
For FORMAL Support, please purchase a support subscription from here: https://www.portainer.io/products/portainer-business
For community support: You can find more information about Portainer's community support framework policy here: https://www.portainer.io/products/community-edition/customer-success
- Issues: https://github.com/portainer/portainer/issues
- FAQ: https://documentation.portainer.io
- Slack (chat): https://portainer.io/slack/
## Reporting bugs and contributing
* Want to report a bug or request a feature? Please open [an issue](https://github.com/portainer/portainer/issues/new).
* Want to help us build **_portainer_**? Follow our [contribution guidelines](https://portainer.readthedocs.io/en/latest/contribute.html) to build it locally and make a pull request. We need all the help we can get!
- Want to report a bug or request a feature? Please open [an issue](https://github.com/portainer/portainer/issues/new).
- Want to help us build **_portainer_**? Follow our [contribution guidelines](https://documentation.portainer.io/contributing/instructions/) to build it locally and make a pull request. We need all the help we can get!
## Security
- Here at Portainer, we believe in [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure) of security issues. If you have found a security issue, please report it to <security@portainer.io>.
## Privacy
**To make sure we focus our development effort in the right places we need to know which features get used most often. To give us this information we use [Matomo Analytics](https://matomo.org/), which is hosted in Germany and is fully GDPR compliant.**
When Portainer first starts, you are given the option to DISABLE analytics. If you **don't** choose to disable it, we collect anonymous usage as per [our privacy policy](https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/). **Please note**, there is no personally identifiable information sent or stored at any time and we only use the data to help us improve Portainer.
## Limitations
**_Portainer_** has full support for the following Docker versions:
* Docker 1.10 to the latest version
* Standalone Docker Swarm >= 1.2.3 _(**NOTE:** Use of Standalone Docker Swarm is being discouraged since the introduction of built-in Swarm Mode in Docker. While older versions of Portainer had support for Standalone Docker Swarm, Portainer 1.17.0 and newer **do not** support it. However, the built-in Swarm Mode of Docker is fully supported.)_
Partial support for the following Docker versions (some features may not be available):
* Docker 1.9
Portainer supports "Current - 2 docker versions only. Prior versions may operate, however these are not supported.
## Licensing
Portainer is licensed under the zlib license. See [LICENSE](./LICENSE) for reference.
Portainer also contains the following code, which is licensed under the [MIT license](https://opensource.org/licenses/MIT):
UI For Docker: Copyright (c) 2013-2016 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com), Anthony Lapenna (portainer.io)
rdash-angular: Copyright (c) [2014] [Elliot Hesp]
Portainer also contains code from open source projects. See [ATTRIBUTIONS.md](./ATTRIBUTIONS.md) for a list.

View File

@@ -0,0 +1,69 @@
package adminmonitor
import (
"context"
"log"
"time"
portainer "github.com/portainer/portainer/api"
)
var logFatalf = log.Fatalf
type Monitor struct {
timeout time.Duration
datastore portainer.DataStore
shutdownCtx context.Context
cancellationFunc context.CancelFunc
}
// New creates a monitor that when started will wait for the timeout duration and then shutdown the application unless it has been initialized.
func New(timeout time.Duration, datastore portainer.DataStore, shutdownCtx context.Context) *Monitor {
return &Monitor{
timeout: timeout,
datastore: datastore,
shutdownCtx: shutdownCtx,
}
}
// Starts starts the monitor. Active monitor could be stopped or shuttted down by cancelling the shutdown context.
func (m *Monitor) Start() {
cancellationCtx, cancellationFunc := context.WithCancel(context.Background())
m.cancellationFunc = cancellationFunc
go func() {
log.Println("[DEBUG] [internal,init] [message: start initialization monitor ]")
select {
case <-time.After(m.timeout):
initialized, err := m.WasInitialized()
if err != nil {
logFatalf("%s", err)
}
if !initialized {
logFatalf("[FATAL] [internal,init] No administrator account was created in %f mins. Shutting down the Portainer instance for security reasons", m.timeout.Minutes())
}
case <-cancellationCtx.Done():
log.Println("[DEBUG] [internal,init] [message: canceling initialization monitor]")
case <-m.shutdownCtx.Done():
log.Println("[DEBUG] [internal,init] [message: shutting down initialization monitor]")
}
}()
}
// Stop stops monitor. Safe to call even if monitor wasn't started.
func (m *Monitor) Stop() {
if m.cancellationFunc == nil {
return
}
m.cancellationFunc()
m.cancellationFunc = nil
}
// WasInitialized is a system initialization check
func (m *Monitor) WasInitialized() (bool, error) {
users, err := m.datastore.User().UsersByRole(portainer.AdministratorRole)
if err != nil {
return false, err
}
return len(users) > 0, nil
}

View File

@@ -0,0 +1,50 @@
package adminmonitor
import (
"context"
"testing"
"time"
portainer "github.com/portainer/portainer/api"
i "github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert"
)
func Test_stopWithoutStarting(t *testing.T) {
monitor := New(1*time.Minute, nil, nil)
monitor.Stop()
}
func Test_stopCouldBeCalledMultipleTimes(t *testing.T) {
monitor := New(1*time.Minute, nil, nil)
monitor.Stop()
monitor.Stop()
}
func Test_canStopStartedMonitor(t *testing.T) {
monitor := New(1*time.Minute, nil, context.Background())
monitor.Start()
assert.NotNil(t, monitor.cancellationFunc, "cancellation function is missing in started monitor")
monitor.Stop()
assert.Nil(t, monitor.cancellationFunc, "cancellation function should absent in stopped monitor")
}
func Test_start_shouldFatalAfterTimeout_ifNotInitialized(t *testing.T) {
timeout := 10 * time.Millisecond
datastore := i.NewDatastore(i.WithUsers([]portainer.User{}))
var fataled bool
origLogFatalf := logFatalf
logFatalf = func(s string, v ...interface{}) { fataled = true }
defer func() {
logFatalf = origLogFatalf
}()
monitor := New(timeout, datastore, context.Background())
monitor.Start()
<-time.After(2 * timeout)
assert.True(t, fataled, "monitor should been timeout and fatal")
}

53
api/api-description.md Normal file
View File

@@ -0,0 +1,53 @@
Portainer API is an HTTP API served by Portainer. It is used by the Portainer UI and everything you can do with the UI can be done using the HTTP API.
Examples are available at https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8
You can find out more about Portainer at [http://portainer.io](http://portainer.io) and get some support on [Slack](http://portainer.io/slack/).
# Authentication
Most of the API endpoints require to be authenticated as well as some level of authorization to be used.
Portainer API uses JSON Web Token to manage authentication and thus requires you to provide a token in the **Authorization** header of each request
with the **Bearer** authentication mechanism.
Example:
```
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsImV4cCI6MTQ5OTM3NjE1NH0.NJ6vE8FY1WG6jsRQzfMqeatJ4vh2TWAeeYfDhP71YEE
```
# Security
Each API endpoint has an associated access policy, it is documented in the description of each endpoint.
Different access policies are available:
- Public access
- Authenticated access
- Restricted access
- Administrator access
### Public access
No authentication is required to access the endpoints with this access policy.
### Authenticated access
Authentication is required to access the endpoints with this access policy.
### Restricted access
Authentication is required to access the endpoints with this access policy.
Extra-checks might be added to ensure access to the resource is granted. Returned data might also be filtered.
### Administrator access
Authentication as well as an administrator role are required to access the endpoints with this access policy.
# Execute Docker requests
Portainer **DO NOT** expose specific endpoints to manage your Docker resources (create a container, remove a volume, etc...).
Instead, it acts as a reverse-proxy to the Docker HTTP API. This means that you can execute Docker requests **via** the Portainer HTTP API.
To do so, you can use the `/endpoints/{id}/docker` Portainer API endpoint (which is not documented below due to Swagger limitations). This endpoint has a restricted access policy so you still need to be authenticated to be able to query this endpoint. Any query on this endpoint will be proxied to the Docker API of the associated endpoint (requests and responses objects are the same as documented in the Docker API).
**NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8).

View File

@@ -7,13 +7,13 @@ import (
// TarFileInBuffer will create a tar archive containing a single file named via fileName and using the content
// specified in fileContent. Returns the archive as a byte array.
func TarFileInBuffer(fileContent []byte, fileName string) ([]byte, error) {
func TarFileInBuffer(fileContent []byte, fileName string, mode int64) ([]byte, error) {
var buffer bytes.Buffer
tarWriter := tar.NewWriter(&buffer)
header := &tar.Header{
Name: fileName,
Mode: 0600,
Mode: mode,
Size: int64(len(fileContent)),
}

119
api/archive/targz.go Normal file
View File

@@ -0,0 +1,119 @@
package archive
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// TarGzDir creates a tar.gz archive and returns it's path.
// abosolutePath should be an absolute path to a directory.
// Archive name will be <directoryName>.tar.gz and will be placed next to the directory.
func TarGzDir(absolutePath string) (string, error) {
targzPath := filepath.Join(absolutePath, fmt.Sprintf("%s.tar.gz", filepath.Base(absolutePath)))
outFile, err := os.Create(targzPath)
if err != nil {
return "", err
}
defer outFile.Close()
zipWriter := gzip.NewWriter(outFile)
defer zipWriter.Close()
tarWriter := tar.NewWriter(zipWriter)
defer tarWriter.Close()
err = filepath.Walk(absolutePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == targzPath {
return nil // skip archive file
}
pathInArchive := filepath.Clean(strings.TrimPrefix(path, absolutePath))
if pathInArchive == "" {
return nil // skip root dir
}
return addToArchive(tarWriter, pathInArchive, path, info)
})
return targzPath, err
}
func addToArchive(tarWriter *tar.Writer, pathInArchive string, path string, info os.FileInfo) error {
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
header.Name = pathInArchive // use relative paths in archive
err = tarWriter.WriteHeader(header)
if err != nil {
return err
}
if info.IsDir() {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
_, err = io.Copy(tarWriter, file)
return err
}
// ExtractTarGz reads a .tar.gz archive from the reader and extracts it into outputDirPath directory
func ExtractTarGz(r io.Reader, outputDirPath string) error {
zipReader, err := gzip.NewReader(r)
if err != nil {
return err
}
defer zipReader.Close()
tarReader := tar.NewReader(zipReader)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
switch header.Typeflag {
case tar.TypeDir:
// skip, dir will be created with a file
case tar.TypeReg:
p := filepath.Clean(filepath.Join(outputDirPath, header.Name))
if err := os.MkdirAll(filepath.Dir(p), 0744); err != nil {
return fmt.Errorf("Failed to extract dir %s", filepath.Dir(p))
}
outFile, err := os.Create(p)
if err != nil {
return fmt.Errorf("Failed to create file %s", header.Name)
}
if _, err := io.Copy(outFile, tarReader); err != nil {
return fmt.Errorf("Failed to extract file %s", header.Name)
}
outFile.Close()
default:
return fmt.Errorf("Tar: uknown type: %v in %s",
header.Typeflag,
header.Name)
}
}
return nil
}

99
api/archive/targz_test.go Normal file
View File

@@ -0,0 +1,99 @@
package archive
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/ioutils"
"github.com/stretchr/testify/assert"
)
func listFiles(dir string) []string {
items := make([]string, 0)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if path == dir {
return nil
}
items = append(items, path)
return nil
})
return items
}
func Test_shouldCreateArhive(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
content := []byte("content")
ioutil.WriteFile(path.Join(tmpdir, "outer"), content, 0600)
os.MkdirAll(path.Join(tmpdir, "dir"), 0700)
ioutil.WriteFile(path.Join(tmpdir, "dir", ".dotfile"), content, 0600)
ioutil.WriteFile(path.Join(tmpdir, "dir", "inner"), content, 0600)
gzPath, err := TarGzDir(tmpdir)
assert.Nil(t, err)
assert.Equal(t, filepath.Join(tmpdir, fmt.Sprintf("%s.tar.gz", filepath.Base(tmpdir))), gzPath)
extractionDir, _ := ioutils.TempDir("", "extract")
defer os.RemoveAll(extractionDir)
cmd := exec.Command("tar", "-xzf", gzPath, "-C", extractionDir)
err = cmd.Run()
if err != nil {
t.Fatal("Failed to extract archive: ", err)
}
extractedFiles := listFiles(extractionDir)
wasExtracted := func(p string) {
fullpath := path.Join(extractionDir, p)
assert.Contains(t, extractedFiles, fullpath)
copyContent, _ := ioutil.ReadFile(fullpath)
assert.Equal(t, content, copyContent)
}
wasExtracted("outer")
wasExtracted("dir/inner")
wasExtracted("dir/.dotfile")
}
func Test_shouldCreateArhiveXXXXX(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
content := []byte("content")
ioutil.WriteFile(path.Join(tmpdir, "outer"), content, 0600)
os.MkdirAll(path.Join(tmpdir, "dir"), 0700)
ioutil.WriteFile(path.Join(tmpdir, "dir", ".dotfile"), content, 0600)
ioutil.WriteFile(path.Join(tmpdir, "dir", "inner"), content, 0600)
gzPath, err := TarGzDir(tmpdir)
assert.Nil(t, err)
assert.Equal(t, filepath.Join(tmpdir, fmt.Sprintf("%s.tar.gz", filepath.Base(tmpdir))), gzPath)
extractionDir, _ := ioutils.TempDir("", "extract")
defer os.RemoveAll(extractionDir)
r, _ := os.Open(gzPath)
ExtractTarGz(r, extractionDir)
if err != nil {
t.Fatal("Failed to extract archive: ", err)
}
extractedFiles := listFiles(extractionDir)
wasExtracted := func(p string) {
fullpath := path.Join(extractionDir, p)
assert.Contains(t, extractedFiles, fullpath)
copyContent, _ := ioutil.ReadFile(fullpath)
assert.Equal(t, content, copyContent)
}
wasExtracted("outer")
wasExtracted("dir/inner")
wasExtracted("dir/.dotfile")
}

BIN
api/archive/testdata/sample_archive.zip vendored Normal file

Binary file not shown.

114
api/archive/zip.go Normal file
View File

@@ -0,0 +1,114 @@
package archive
import (
"archive/zip"
"bytes"
"fmt"
"github.com/pkg/errors"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// UnzipArchive will unzip an archive from bytes into the dest destination folder on disk
func UnzipArchive(archiveData []byte, dest string) error {
zipReader, err := zip.NewReader(bytes.NewReader(archiveData), int64(len(archiveData)))
if err != nil {
return err
}
for _, zipFile := range zipReader.File {
err := extractFileFromArchive(zipFile, dest)
if err != nil {
return err
}
}
return nil
}
func extractFileFromArchive(file *zip.File, dest string) error {
f, err := file.Open()
if err != nil {
return err
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return err
}
fpath := filepath.Join(dest, file.Name)
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return err
}
_, err = io.Copy(outFile, bytes.NewReader(data))
if err != nil {
return err
}
return outFile.Close()
}
// UnzipFile will decompress a zip archive, moving all files and folders
// within the zip file (parameter 1) to an output directory (parameter 2).
func UnzipFile(src string, dest string) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer r.Close()
for _, f := range r.File {
p := filepath.Join(dest, f.Name)
// Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
if !strings.HasPrefix(p, filepath.Clean(dest)+string(os.PathSeparator)) {
return fmt.Errorf("%s: illegal file path", p)
}
if f.FileInfo().IsDir() {
// Make Folder
os.MkdirAll(p, os.ModePerm)
continue
}
err = unzipFile(f, p)
if err != nil {
return err
}
}
return nil
}
func unzipFile(f *zip.File, p string) error {
// Make File
if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
return errors.Wrapf(err, "unzipFile: can't make a path %s", p)
}
outFile, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return errors.Wrapf(err, "unzipFile: can't create file %s", p)
}
defer outFile.Close()
rc, err := f.Open()
if err != nil {
return errors.Wrapf(err, "unzipFile: can't open zip file %s in the archive", f.Name)
}
defer rc.Close()
_, err = io.Copy(outFile, rc)
if err != nil {
return errors.Wrapf(err, "unzipFile: can't copy an archived file content")
}
return nil
}

32
api/archive/zip_test.go Normal file
View File

@@ -0,0 +1,32 @@
package archive
import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestUnzipFile(t *testing.T) {
dir, err := ioutil.TempDir("", "unzip-test-")
assert.NoError(t, err)
defer os.RemoveAll(dir)
/*
Archive structure.
├── 0
│ ├── 1
│ │ └── 2.txt
│ └── 1.txt
└── 0.txt
*/
err = UnzipFile("./testdata/sample_archive.zip", dir)
assert.NoError(t, err)
archiveDir := dir + "/sample_archive"
assert.FileExists(t, filepath.Join(archiveDir, "0.txt"))
assert.FileExists(t, filepath.Join(archiveDir, "0", "1.txt"))
assert.FileExists(t, filepath.Join(archiveDir, "0", "1", "2.txt"))
}

83
api/backup/backup.go Normal file
View File

@@ -0,0 +1,83 @@
package backup
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/pkg/errors"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/archive"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/http/offlinegate"
)
const rwxr__r__ os.FileMode = 0744
var filesToBackup = []string{"compose", "config.json", "custom_templates", "edge_jobs", "edge_stacks", "extensions", "portainer.key", "portainer.pub", "tls"}
// Creates a tar.gz system archive and encrypts it if password is not empty. Returns a path to the archive file.
func CreateBackupArchive(password string, gate *offlinegate.OfflineGate, datastore portainer.DataStore, filestorePath string) (string, error) {
unlock := gate.Lock()
defer unlock()
backupDirPath := filepath.Join(filestorePath, "backup", time.Now().Format("2006-01-02_15-04-05"))
if err := os.MkdirAll(backupDirPath, rwxr__r__); err != nil {
return "", errors.Wrap(err, "Failed to create backup dir")
}
if err := backupDb(backupDirPath, datastore); err != nil {
return "", errors.Wrap(err, "Failed to backup database")
}
for _, filename := range filesToBackup {
err := copyPath(filepath.Join(filestorePath, filename), backupDirPath)
if err != nil {
return "", errors.Wrap(err, "Failed to create backup file")
}
}
archivePath, err := archive.TarGzDir(backupDirPath)
if err != nil {
return "", errors.Wrap(err, "Failed to make an archive")
}
if password != "" {
archivePath, err = encrypt(archivePath, password)
if err != nil {
return "", errors.Wrap(err, "Failed to encrypt backup with the password")
}
}
return archivePath, nil
}
func backupDb(backupDirPath string, datastore portainer.DataStore) error {
backupWriter, err := os.Create(filepath.Join(backupDirPath, "portainer.db"))
if err != nil {
return err
}
if err = datastore.BackupTo(backupWriter); err != nil {
return err
}
return backupWriter.Close()
}
func encrypt(path string, passphrase string) (string, error) {
in, err := os.Open(path)
if err != nil {
return "", err
}
defer in.Close()
outFileName := fmt.Sprintf("%s.encrypted", path)
out, err := os.Create(outFileName)
if err != nil {
return "", err
}
err = crypto.AesEncrypt(in, out, []byte(passphrase))
return outFileName, err
}

68
api/backup/copy.go Normal file
View File

@@ -0,0 +1,68 @@
package backup
import (
"errors"
"io"
"os"
"path/filepath"
"strings"
)
func copyPath(path string, toDir string) error {
info, err := os.Stat(path)
if err != nil && errors.Is(err, os.ErrNotExist) {
// skip copy if file does not exist
return nil
}
if !info.IsDir() {
destination := filepath.Join(toDir, info.Name())
return copyFile(path, destination)
}
return copyDir(path, toDir)
}
func copyDir(fromDir, toDir string) error {
cleanedSourcePath := filepath.Clean(fromDir)
parentDirectory := filepath.Dir(cleanedSourcePath)
err := filepath.Walk(cleanedSourcePath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
destination := filepath.Join(toDir, strings.TrimPrefix(path, parentDirectory))
if info.IsDir() {
return nil // skip directory creations
}
if info.Mode()&os.ModeSymlink != 0 { // entry is a symlink
return nil // don't copy symlinks
}
return copyFile(path, destination)
})
return err
}
// copies regular a file from src to dst
func copyFile(src, dst string) error {
from, err := os.Open(src)
if err != nil {
return err
}
defer from.Close()
// has to include 'execute' bit, otherwise fails. MkdirAll follows `mkdir -m` restrictions
if err := os.MkdirAll(filepath.Dir(dst), 0744); err != nil {
return err
}
to, err := os.Create(dst)
if err != nil {
return err
}
defer to.Close()
_, err = io.Copy(to, from)
return err
}

105
api/backup/copy_test.go Normal file
View File

@@ -0,0 +1,105 @@
package backup
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/ioutils"
"github.com/stretchr/testify/assert"
)
func listFiles(dir string) []string {
items := make([]string, 0)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if path == dir {
return nil
}
items = append(items, path)
return nil
})
return items
}
func contains(t *testing.T, list []string, path string) {
assert.Contains(t, list, path)
copyContent, _ := ioutil.ReadFile(path)
assert.Equal(t, "content\n", string(copyContent))
}
func Test_copyFile_returnsError_whenSourceDoesNotExist(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
err := copyFile("does-not-exist", tmpdir)
assert.NotNil(t, err)
}
func Test_copyFile_shouldMakeAbackup(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
content := []byte("content")
ioutil.WriteFile(path.Join(tmpdir, "origin"), content, 0600)
err := copyFile(path.Join(tmpdir, "origin"), path.Join(tmpdir, "copy"))
assert.Nil(t, err)
copyContent, _ := ioutil.ReadFile(path.Join(tmpdir, "copy"))
assert.Equal(t, content, copyContent)
}
func Test_copyDir_shouldCopyAllFilesAndDirectories(t *testing.T) {
destination, _ := ioutils.TempDir("", "destination")
defer os.RemoveAll(destination)
err := copyDir("./test_assets/copy_test", destination)
assert.Nil(t, err)
createdFiles := listFiles(destination)
contains(t, createdFiles, filepath.Join(destination, "copy_test", "outer"))
contains(t, createdFiles, filepath.Join(destination, "copy_test", "dir", ".dotfile"))
contains(t, createdFiles, filepath.Join(destination, "copy_test", "dir", "inner"))
}
func Test_backupPath_shouldSkipWhenNotExist(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
err := copyPath("does-not-exists", tmpdir)
assert.Nil(t, err)
assert.Empty(t, listFiles(tmpdir))
}
func Test_backupPath_shouldCopyFile(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
content := []byte("content")
ioutil.WriteFile(path.Join(tmpdir, "file"), content, 0600)
os.MkdirAll(path.Join(tmpdir, "backup"), 0700)
err := copyPath(path.Join(tmpdir, "file"), path.Join(tmpdir, "backup"))
assert.Nil(t, err)
copyContent, err := ioutil.ReadFile(path.Join(tmpdir, "backup", "file"))
assert.Nil(t, err)
assert.Equal(t, content, copyContent)
}
func Test_backupPath_shouldCopyDir(t *testing.T) {
destination, _ := ioutils.TempDir("", "destination")
defer os.RemoveAll(destination)
err := copyPath("./test_assets/copy_test", destination)
assert.Nil(t, err)
createdFiles := listFiles(destination)
contains(t, createdFiles, filepath.Join(destination, "copy_test", "outer"))
contains(t, createdFiles, filepath.Join(destination, "copy_test", "dir", ".dotfile"))
contains(t, createdFiles, filepath.Join(destination, "copy_test", "dir", "inner"))
}

68
api/backup/restore.go Normal file
View File

@@ -0,0 +1,68 @@
package backup
import (
"context"
"io"
"os"
"path/filepath"
"time"
"github.com/pkg/errors"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/archive"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/http/offlinegate"
)
var filesToRestore = append(filesToBackup, "portainer.db")
// Restores system state from backup archive, will trigger system shutdown, when finished.
func RestoreArchive(archive io.Reader, password string, filestorePath string, gate *offlinegate.OfflineGate, datastore portainer.DataStore, shutdownTrigger context.CancelFunc) error {
var err error
if password != "" {
archive, err = decrypt(archive, password)
if err != nil {
return errors.Wrap(err, "failed to decrypt the archive")
}
}
restorePath := filepath.Join(filestorePath, "restore", time.Now().Format("20060102150405"))
defer os.RemoveAll(filepath.Dir(restorePath))
err = extractArchive(archive, restorePath)
if err != nil {
return errors.Wrap(err, "cannot extract files from the archive. Please ensure the password is correct and try again")
}
unlock := gate.Lock()
defer unlock()
if err = datastore.Close(); err != nil {
return errors.Wrap(err, "Failed to stop db")
}
if err = restoreFiles(restorePath, filestorePath); err != nil {
return errors.Wrap(err, "failed to restore the system state")
}
shutdownTrigger()
return nil
}
func decrypt(r io.Reader, password string) (io.Reader, error) {
return crypto.AesDecrypt(r, []byte(password))
}
func extractArchive(r io.Reader, destinationDirPath string) error {
return archive.ExtractTarGz(r, destinationDirPath)
}
func restoreFiles(srcDir string, destinationDir string) error {
for _, filename := range filesToRestore {
err := copyPath(filepath.Join(srcDir, filename), destinationDir)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1 @@
content

View File

@@ -0,0 +1 @@
content

View File

@@ -0,0 +1 @@
content

View File

@@ -0,0 +1,96 @@
package customtemplate
import (
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "customtemplates"
)
// Service represents a service for managing custom template data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// CustomTemplates return an array containing all the custom templates.
func (service *Service) CustomTemplates() ([]portainer.CustomTemplate, error) {
var customTemplates = make([]portainer.CustomTemplate, 0)
err := service.connection.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 customTemplate portainer.CustomTemplate
err := internal.UnmarshalObjectWithJsoniter(v, &customTemplate)
if err != nil {
return err
}
customTemplates = append(customTemplates, customTemplate)
}
return nil
})
return customTemplates, err
}
// CustomTemplate returns an custom template by ID.
func (service *Service) CustomTemplate(ID portainer.CustomTemplateID) (*portainer.CustomTemplate, error) {
var customTemplate portainer.CustomTemplate
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &customTemplate)
if err != nil {
return nil, err
}
return &customTemplate, nil
}
// UpdateCustomTemplate updates an custom template.
func (service *Service) UpdateCustomTemplate(ID portainer.CustomTemplateID, customTemplate *portainer.CustomTemplate) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.connection, BucketName, identifier, customTemplate)
}
// DeleteCustomTemplate deletes an custom template.
func (service *Service) DeleteCustomTemplate(ID portainer.CustomTemplateID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// CreateCustomTemplate assign an ID to a new custom template and saves it.
func (service *Service) CreateCustomTemplate(customTemplate *portainer.CustomTemplate) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
data, err := internal.MarshalObject(customTemplate)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(customTemplate.ID)), data)
})
}
// GetNextIdentifier returns the next identifier for a custom template.
func (service *Service) GetNextIdentifier() int {
return internal.GetNextIdentifier(service.connection, BucketName)
}

View File

@@ -1,27 +1,39 @@
package bolt
import (
"io"
"log"
"path"
"time"
"github.com/boltdb/bolt"
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/dockerhub"
"github.com/portainer/portainer/bolt/endpoint"
"github.com/portainer/portainer/bolt/endpointgroup"
"github.com/portainer/portainer/bolt/migrator"
"github.com/portainer/portainer/bolt/registry"
"github.com/portainer/portainer/bolt/resourcecontrol"
"github.com/portainer/portainer/bolt/settings"
"github.com/portainer/portainer/bolt/stack"
"github.com/portainer/portainer/bolt/tag"
"github.com/portainer/portainer/bolt/team"
"github.com/portainer/portainer/bolt/teammembership"
"github.com/portainer/portainer/bolt/template"
"github.com/portainer/portainer/bolt/user"
"github.com/portainer/portainer/bolt/version"
"github.com/portainer/portainer/bolt/webhook"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/customtemplate"
"github.com/portainer/portainer/api/bolt/dockerhub"
"github.com/portainer/portainer/api/bolt/edgegroup"
"github.com/portainer/portainer/api/bolt/edgejob"
"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/errors"
"github.com/portainer/portainer/api/bolt/extension"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/portainer/portainer/api/bolt/migrator"
"github.com/portainer/portainer/api/bolt/registry"
"github.com/portainer/portainer/api/bolt/resourcecontrol"
"github.com/portainer/portainer/api/bolt/role"
"github.com/portainer/portainer/api/bolt/schedule"
"github.com/portainer/portainer/api/bolt/settings"
"github.com/portainer/portainer/api/bolt/stack"
"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/user"
"github.com/portainer/portainer/api/bolt/version"
"github.com/portainer/portainer/api/bolt/webhook"
"github.com/portainer/portainer/api/internal/authorization"
)
const (
@@ -31,24 +43,40 @@ const (
// Store defines the implementation of portainer.DataStore using
// BoltDB as the storage system.
type Store struct {
path string
db *bolt.DB
checkForDataMigration bool
fileService portainer.FileService
DockerHubService *dockerhub.Service
EndpointGroupService *endpointgroup.Service
EndpointService *endpoint.Service
RegistryService *registry.Service
ResourceControlService *resourcecontrol.Service
SettingsService *settings.Service
StackService *stack.Service
TagService *tag.Service
TeamMembershipService *teammembership.Service
TeamService *team.Service
TemplateService *template.Service
UserService *user.Service
VersionService *version.Service
WebhookService *webhook.Service
path string
connection *internal.DbConnection
isNew bool
fileService portainer.FileService
CustomTemplateService *customtemplate.Service
DockerHubService *dockerhub.Service
EdgeGroupService *edgegroup.Service
EdgeJobService *edgejob.Service
EdgeStackService *edgestack.Service
EndpointGroupService *endpointgroup.Service
EndpointService *endpoint.Service
EndpointRelationService *endpointrelation.Service
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
TunnelServerService *tunnelserver.Service
UserService *user.Service
VersionService *version.Service
WebhookService *webhook.Service
}
func (store *Store) edition() portainer.SoftwareEdition {
edition, err := store.VersionService.Edition()
if err == errors.ErrObjectNotFound {
edition = portainer.PortainerCE
}
return edition
}
// NewStore initializes a new Store and the associated services
@@ -56,6 +84,8 @@ func NewStore(storePath string, fileService portainer.FileService) (*Store, erro
store := &Store{
path: storePath,
fileService: fileService,
isNew: true,
connection: &internal.DbConnection{},
}
databasePath := path.Join(storePath, databaseFileName)
@@ -64,10 +94,8 @@ func NewStore(storePath string, fileService portainer.FileService) (*Store, erro
return nil, err
}
if !databaseFileExists {
store.checkForDataMigration = false
} else {
store.checkForDataMigration = true
if databaseFileExists {
store.isNew = false
}
return store, nil
@@ -80,50 +108,43 @@ func (store *Store) Open() error {
if err != nil {
return err
}
store.db = db
store.connection.DB = db
return store.initServices()
}
// Init creates the default data set.
func (store *Store) Init() error {
groups, err := store.EndpointGroupService.EndpointGroups()
if err != nil {
return err
// Close closes the BoltDB database.
func (store *Store) Close() error {
if store.connection.DB != nil {
return store.connection.Close()
}
if len(groups) == 0 {
unassignedGroup := &portainer.EndpointGroup{
Name: "Unassigned",
Description: "Unassigned endpoints",
Labels: []portainer.Pair{},
AuthorizedUsers: []portainer.UserID{},
AuthorizedTeams: []portainer.TeamID{},
Tags: []string{},
}
return store.EndpointGroupService.CreateEndpointGroup(unassignedGroup)
}
return nil
}
// Close closes the BoltDB database.
func (store *Store) Close() error {
if store.db != nil {
return store.db.Close()
// 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
}
// CheckCurrentEdition checks if current edition is community edition
func (store *Store) CheckCurrentEdition() error {
if store.edition() != portainer.PortainerCE {
return errors.ErrWrongDBEdition
}
return nil
}
// MigrateData automatically migrate the data based on the DBVersion.
func (store *Store) MigrateData() error {
if !store.checkForDataMigration {
// This process is only triggered on an existing database, not if the database was just created.
// if force is true, then migrate regardless.
func (store *Store) MigrateData(force bool) error {
if store.isNew && !force {
return store.VersionService.StoreDBVersion(portainer.DBVersion)
}
version, err := store.VersionService.DBVersion()
if err == portainer.ErrObjectNotFound {
if err == errors.ErrObjectNotFound {
version = 0
} else if err != nil {
return err
@@ -131,16 +152,24 @@ func (store *Store) MigrateData() error {
if version < portainer.DBVersion {
migratorParams := &migrator.Parameters{
DB: store.db,
DatabaseVersion: version,
EndpointGroupService: store.EndpointGroupService,
EndpointService: store.EndpointService,
ResourceControlService: store.ResourceControlService,
SettingsService: store.SettingsService,
StackService: store.StackService,
UserService: store.UserService,
VersionService: store.VersionService,
FileService: store.fileService,
DB: store.connection.DB,
DatabaseVersion: version,
EndpointGroupService: store.EndpointGroupService,
EndpointService: store.EndpointService,
EndpointRelationService: store.EndpointRelationService,
ExtensionService: store.ExtensionService,
RegistryService: store.RegistryService,
ResourceControlService: store.ResourceControlService,
RoleService: store.RoleService,
ScheduleService: store.ScheduleService,
SettingsService: store.SettingsService,
StackService: store.StackService,
TagService: store.TagService,
TeamMembershipService: store.TeamMembershipService,
UserService: store.UserService,
VersionService: store.VersionService,
FileService: store.fileService,
AuthorizationService: authorization.NewService(store),
}
migrator := migrator.NewMigrator(migratorParams)
@@ -155,90 +184,11 @@ func (store *Store) MigrateData() error {
return nil
}
func (store *Store) initServices() error {
dockerhubService, err := dockerhub.NewService(store.db)
if err != nil {
// BackupTo backs up db to a provided writer.
// It does hot backup and doesn't block other database reads and writes
func (store *Store) BackupTo(w io.Writer) error {
return store.connection.View(func(tx *bolt.Tx) error {
_, err := tx.WriteTo(w)
return err
}
store.DockerHubService = dockerhubService
endpointgroupService, err := endpointgroup.NewService(store.db)
if err != nil {
return err
}
store.EndpointGroupService = endpointgroupService
endpointService, err := endpoint.NewService(store.db)
if err != nil {
return err
}
store.EndpointService = endpointService
registryService, err := registry.NewService(store.db)
if err != nil {
return err
}
store.RegistryService = registryService
resourcecontrolService, err := resourcecontrol.NewService(store.db)
if err != nil {
return err
}
store.ResourceControlService = resourcecontrolService
settingsService, err := settings.NewService(store.db)
if err != nil {
return err
}
store.SettingsService = settingsService
stackService, err := stack.NewService(store.db)
if err != nil {
return err
}
store.StackService = stackService
tagService, err := tag.NewService(store.db)
if err != nil {
return err
}
store.TagService = tagService
teammembershipService, err := teammembership.NewService(store.db)
if err != nil {
return err
}
store.TeamMembershipService = teammembershipService
teamService, err := team.NewService(store.db)
if err != nil {
return err
}
store.TeamService = teamService
templateService, err := template.NewService(store.db)
if err != nil {
return err
}
store.TemplateService = templateService
userService, err := user.NewService(store.db)
if err != nil {
return err
}
store.UserService = userService
versionService, err := version.NewService(store.db)
if err != nil {
return err
}
store.VersionService = versionService
webhookService, err := webhook.NewService(store.db)
if err != nil {
return err
}
store.WebhookService = webhookService
return nil
})
}

View File

@@ -1,10 +1,8 @@
package dockerhub
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
@@ -15,18 +13,18 @@ const (
// Service represents a service for managing Dockerhub data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +32,7 @@ func NewService(db *bolt.DB) (*Service, error) {
func (service *Service) DockerHub() (*portainer.DockerHub, error) {
var dockerhub portainer.DockerHub
err := internal.GetObject(service.db, BucketName, []byte(dockerHubKey), &dockerhub)
err := internal.GetObject(service.connection, BucketName, []byte(dockerHubKey), &dockerhub)
if err != nil {
return nil, err
}
@@ -44,5 +42,5 @@ func (service *Service) DockerHub() (*portainer.DockerHub, error) {
// UpdateDockerHub updates a DockerHub object.
func (service *Service) UpdateDockerHub(dockerhub *portainer.DockerHub) error {
return internal.UpdateObject(service.db, BucketName, []byte(dockerHubKey), dockerhub)
return internal.UpdateObject(service.connection, BucketName, []byte(dockerHubKey), dockerhub)
}

View File

@@ -0,0 +1,94 @@
package edgegroup
import (
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "edgegroups"
)
// Service represents a service for managing Edge group data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// EdgeGroups return an array containing all the Edge groups.
func (service *Service) EdgeGroups() ([]portainer.EdgeGroup, error) {
var groups = make([]portainer.EdgeGroup, 0)
err := service.connection.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 group portainer.EdgeGroup
err := internal.UnmarshalObjectWithJsoniter(v, &group)
if err != nil {
return err
}
groups = append(groups, group)
}
return nil
})
return groups, err
}
// EdgeGroup returns an Edge group by ID.
func (service *Service) EdgeGroup(ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) {
var group portainer.EdgeGroup
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &group)
if err != nil {
return nil, err
}
return &group, nil
}
// UpdateEdgeGroup updates an Edge group.
func (service *Service) UpdateEdgeGroup(ID portainer.EdgeGroupID, group *portainer.EdgeGroup) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.connection, BucketName, identifier, group)
}
// DeleteEdgeGroup deletes an Edge group.
func (service *Service) DeleteEdgeGroup(ID portainer.EdgeGroupID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// CreateEdgeGroup assign an ID to a new Edge group and saves it.
func (service *Service) CreateEdgeGroup(group *portainer.EdgeGroup) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
group.ID = portainer.EdgeGroupID(id)
data, err := internal.MarshalObject(group)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(group.ID)), data)
})
}

101
api/bolt/edgejob/edgejob.go Normal file
View File

@@ -0,0 +1,101 @@
package edgejob
import (
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "edgejobs"
)
// Service represents a service for managing edge jobs data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// EdgeJobs returns a list of Edge jobs
func (service *Service) EdgeJobs() ([]portainer.EdgeJob, error) {
var edgeJobs = make([]portainer.EdgeJob, 0)
err := service.connection.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 edgeJob portainer.EdgeJob
err := internal.UnmarshalObject(v, &edgeJob)
if err != nil {
return err
}
edgeJobs = append(edgeJobs, edgeJob)
}
return nil
})
return edgeJobs, err
}
// EdgeJob returns an Edge job by ID
func (service *Service) EdgeJob(ID portainer.EdgeJobID) (*portainer.EdgeJob, error) {
var edgeJob portainer.EdgeJob
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &edgeJob)
if err != nil {
return nil, err
}
return &edgeJob, nil
}
// CreateEdgeJob creates a new Edge job
func (service *Service) CreateEdgeJob(edgeJob *portainer.EdgeJob) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
if edgeJob.ID == 0 {
id, _ := bucket.NextSequence()
edgeJob.ID = portainer.EdgeJobID(id)
}
data, err := internal.MarshalObject(edgeJob)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(edgeJob.ID)), data)
})
}
// UpdateEdgeJob updates an Edge job by ID
func (service *Service) UpdateEdgeJob(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.connection, BucketName, identifier, edgeJob)
}
// DeleteEdgeJob deletes an Edge job
func (service *Service) DeleteEdgeJob(ID portainer.EdgeJobID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// GetNextIdentifier returns the next identifier for an endpoint.
func (service *Service) GetNextIdentifier() int {
return internal.GetNextIdentifier(service.connection, BucketName)
}

View File

@@ -0,0 +1,101 @@
package edgestack
import (
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "edge_stack"
)
// Service represents a service for managing Edge stack data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// EdgeStacks returns an array containing all edge stacks
func (service *Service) EdgeStacks() ([]portainer.EdgeStack, error) {
var stacks = make([]portainer.EdgeStack, 0)
err := service.connection.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 stack portainer.EdgeStack
err := internal.UnmarshalObject(v, &stack)
if err != nil {
return err
}
stacks = append(stacks, stack)
}
return nil
})
return stacks, err
}
// EdgeStack returns an Edge stack by ID.
func (service *Service) EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStack, error) {
var stack portainer.EdgeStack
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &stack)
if err != nil {
return nil, err
}
return &stack, nil
}
// CreateEdgeStack assign an ID to a new Edge stack and saves it.
func (service *Service) CreateEdgeStack(edgeStack *portainer.EdgeStack) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
if edgeStack.ID == 0 {
id, _ := bucket.NextSequence()
edgeStack.ID = portainer.EdgeStackID(id)
}
data, err := internal.MarshalObject(edgeStack)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(edgeStack.ID)), data)
})
}
// UpdateEdgeStack updates an Edge stack.
func (service *Service) UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.connection, BucketName, identifier, edgeStack)
}
// DeleteEdgeStack deletes an Edge stack.
func (service *Service) DeleteEdgeStack(ID portainer.EdgeStackID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// GetNextIdentifier returns the next identifier for an endpoint.
func (service *Service) GetNextIdentifier() int {
return internal.GetNextIdentifier(service.connection, BucketName)
}

View File

@@ -1,10 +1,9 @@
package endpoint
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
@@ -14,18 +13,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +33,7 @@ func (service *Service) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint,
var endpoint portainer.Endpoint
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &endpoint)
err := internal.GetObject(service.connection, BucketName, identifier, &endpoint)
if err != nil {
return nil, err
}
@@ -45,26 +44,26 @@ func (service *Service) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint,
// UpdateEndpoint updates an endpoint.
func (service *Service) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, endpoint)
return internal.UpdateObject(service.connection, BucketName, identifier, endpoint)
}
// DeleteEndpoint deletes an endpoint.
func (service *Service) DeleteEndpoint(ID portainer.EndpointID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// Endpoints return an array containing all the endpoints.
func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
var endpoints = make([]portainer.Endpoint, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.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 endpoint portainer.Endpoint
err := internal.UnmarshalObject(v, &endpoint)
err := internal.UnmarshalObjectWithJsoniter(v, &endpoint)
if err != nil {
return err
}
@@ -79,7 +78,7 @@ func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
// CreateEndpoint assign an ID to a new endpoint and saves it.
func (service *Service) CreateEndpoint(endpoint *portainer.Endpoint) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
// We manually manage sequences for endpoints
@@ -99,12 +98,12 @@ func (service *Service) CreateEndpoint(endpoint *portainer.Endpoint) error {
// GetNextIdentifier returns the next identifier for an endpoint.
func (service *Service) GetNextIdentifier() int {
return internal.GetNextIdentifier(service.db, BucketName)
return internal.GetNextIdentifier(service.connection, BucketName)
}
// Synchronize creates, updates and deletes endpoints inside a single transaction.
func (service *Service) Synchronize(toCreate, toUpdate, toDelete []*portainer.Endpoint) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
for _, endpoint := range toCreate {

View File

@@ -1,8 +1,8 @@
package endpointgroup
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +14,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +34,7 @@ func (service *Service) EndpointGroup(ID portainer.EndpointGroupID) (*portainer.
var endpointGroup portainer.EndpointGroup
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &endpointGroup)
err := internal.GetObject(service.connection, BucketName, identifier, &endpointGroup)
if err != nil {
return nil, err
}
@@ -45,20 +45,20 @@ func (service *Service) EndpointGroup(ID portainer.EndpointGroupID) (*portainer.
// UpdateEndpointGroup updates an endpoint group.
func (service *Service) UpdateEndpointGroup(ID portainer.EndpointGroupID, endpointGroup *portainer.EndpointGroup) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, endpointGroup)
return internal.UpdateObject(service.connection, BucketName, identifier, endpointGroup)
}
// DeleteEndpointGroup deletes an endpoint group.
func (service *Service) DeleteEndpointGroup(ID portainer.EndpointGroupID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// EndpointGroups return an array containing all the endpoint groups.
func (service *Service) EndpointGroups() ([]portainer.EndpointGroup, error) {
var endpointGroups = make([]portainer.EndpointGroup, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -79,7 +79,7 @@ func (service *Service) EndpointGroups() ([]portainer.EndpointGroup, error) {
// CreateEndpointGroup assign an ID to a new endpoint group and saves it.
func (service *Service) CreateEndpointGroup(endpointGroup *portainer.EndpointGroup) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()

View File

@@ -0,0 +1,68 @@
package endpointrelation
import (
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "endpoint_relations"
)
// Service represents a service for managing endpoint relation data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// EndpointRelation returns a Endpoint relation object by EndpointID
func (service *Service) EndpointRelation(endpointID portainer.EndpointID) (*portainer.EndpointRelation, error) {
var endpointRelation portainer.EndpointRelation
identifier := internal.Itob(int(endpointID))
err := internal.GetObject(service.connection, BucketName, identifier, &endpointRelation)
if err != nil {
return nil, err
}
return &endpointRelation, nil
}
// CreateEndpointRelation saves endpointRelation
func (service *Service) CreateEndpointRelation(endpointRelation *portainer.EndpointRelation) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
data, err := internal.MarshalObject(endpointRelation)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(endpointRelation.EndpointID)), data)
})
}
// UpdateEndpointRelation updates an Endpoint relation object
func (service *Service) UpdateEndpointRelation(EndpointID portainer.EndpointID, endpointRelation *portainer.EndpointRelation) error {
identifier := internal.Itob(int(EndpointID))
return internal.UpdateObject(service.connection, BucketName, identifier, endpointRelation)
}
// DeleteEndpointRelation deletes an Endpoint relation object
func (service *Service) DeleteEndpointRelation(EndpointID portainer.EndpointID) error {
identifier := internal.Itob(int(EndpointID))
return internal.DeleteObject(service.connection, BucketName, identifier)
}

View File

@@ -0,0 +1,8 @@
package errors
import "errors"
var (
ErrObjectNotFound = errors.New("Object not found inside the database")
ErrWrongDBEdition = errors.New("The Portainer database is set for Portainer Business Edition, please follow the instructions in our documentation to downgrade it: https://documentation.portainer.io/v2.0-be/downgrade/be-to-ce/")
)

View File

@@ -0,0 +1,86 @@
package extension
import (
portainer "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 = "extension"
)
// Service represents a service for managing endpoint data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// Extension returns a extension by ID
func (service *Service) Extension(ID portainer.ExtensionID) (*portainer.Extension, error) {
var extension portainer.Extension
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &extension)
if err != nil {
return nil, err
}
return &extension, nil
}
// Extensions return an array containing all the extensions.
func (service *Service) Extensions() ([]portainer.Extension, error) {
var extensions = make([]portainer.Extension, 0)
err := service.connection.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 extension portainer.Extension
err := internal.UnmarshalObject(v, &extension)
if err != nil {
return err
}
extensions = append(extensions, extension)
}
return nil
})
return extensions, err
}
// Persist persists a extension inside the database.
func (service *Service) Persist(extension *portainer.Extension) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
data, err := internal.MarshalObject(extension)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(extension.ID)), data)
})
}
// DeleteExtension deletes a Extension.
func (service *Service) DeleteExtension(ID portainer.ExtensionID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.connection, BucketName, identifier)
}

96
api/bolt/init.go Normal file
View File

@@ -0,0 +1,96 @@
package bolt
import (
"github.com/gofrs/uuid"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
)
// Init creates the default data set.
func (store *Store) Init() error {
instanceID, err := store.VersionService.InstanceID()
if err == errors.ErrObjectNotFound {
uid, err := uuid.NewV4()
if err != nil {
return err
}
instanceID = uid.String()
err = store.VersionService.StoreInstanceID(instanceID)
if err != nil {
return err
}
} else if err != nil {
return err
}
_, err = store.SettingsService.Settings()
if err == errors.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{},
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
TemplatesURL: portainer.DefaultTemplatesURL,
UserSessionTimeout: portainer.DefaultUserSessionTimeout,
}
err = store.SettingsService.UpdateSettings(defaultSettings)
if err != nil {
return err
}
} else if err != nil {
return err
}
_, err = store.DockerHubService.DockerHub()
if err == errors.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
}
if len(groups) == 0 {
unassignedGroup := &portainer.EndpointGroup{
Name: "Unassigned",
Description: "Unassigned endpoints",
Labels: []portainer.Pair{},
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
TagIDs: []portainer.TagID{},
}
err = store.EndpointGroupService.CreateEndpointGroup(unassignedGroup)
if err != nil {
return err
}
}
return nil
}

View File

@@ -4,9 +4,13 @@ import (
"encoding/binary"
"github.com/boltdb/bolt"
"github.com/portainer/portainer"
"github.com/portainer/portainer/api/bolt/errors"
)
type DbConnection struct {
*bolt.DB
}
// Itob returns an 8-byte big endian representation of v.
// This function is typically used for encoding integer IDs to byte slices
// so that they can be used as BoltDB keys.
@@ -17,8 +21,8 @@ func Itob(v int) []byte {
}
// CreateBucket is a generic function used to create a bucket inside a bolt database.
func CreateBucket(db *bolt.DB, bucketName string) error {
return db.Update(func(tx *bolt.Tx) error {
func CreateBucket(connection *DbConnection, bucketName string) error {
return connection.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(bucketName))
if err != nil {
return err
@@ -28,15 +32,15 @@ func CreateBucket(db *bolt.DB, bucketName string) error {
}
// GetObject is a generic function used to retrieve an unmarshalled object from a bolt database.
func GetObject(db *bolt.DB, bucketName string, key []byte, object interface{}) error {
func GetObject(connection *DbConnection, bucketName string, key []byte, object interface{}) error {
var data []byte
err := db.View(func(tx *bolt.Tx) error {
err := connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
value := bucket.Get(key)
if value == nil {
return portainer.ErrObjectNotFound
return errors.ErrObjectNotFound
}
data = make([]byte, len(value))
@@ -52,8 +56,8 @@ func GetObject(db *bolt.DB, bucketName string, key []byte, object interface{}) e
}
// UpdateObject is a generic function used to update an object inside a bolt database.
func UpdateObject(db *bolt.DB, bucketName string, key []byte, object interface{}) error {
return db.Update(func(tx *bolt.Tx) error {
func UpdateObject(connection *DbConnection, bucketName string, key []byte, object interface{}) error {
return connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
data, err := MarshalObject(object)
@@ -71,24 +75,26 @@ func UpdateObject(db *bolt.DB, bucketName string, key []byte, object interface{}
}
// DeleteObject is a generic function used to delete an object inside a bolt database.
func DeleteObject(db *bolt.DB, bucketName string, key []byte) error {
return db.Update(func(tx *bolt.Tx) error {
func DeleteObject(connection *DbConnection, bucketName string, key []byte) error {
return connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
return bucket.Delete(key)
})
}
// GetNextIdentifier is a generic function that returns the specified bucket identifier incremented by 1.
func GetNextIdentifier(db *bolt.DB, bucketName string) int {
func GetNextIdentifier(connection *DbConnection, bucketName string) int {
var identifier int
db.View(func(tx *bolt.Tx) error {
connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
id := bucket.Sequence()
id, err := bucket.NextSequence()
if err != nil {
return err
}
identifier = int(id)
return nil
})
identifier++
return identifier
}

View File

@@ -2,6 +2,8 @@ package internal
import (
"encoding/json"
jsoniter "github.com/json-iterator/go"
)
// MarshalObject encodes an object to binary format
@@ -13,3 +15,11 @@ func MarshalObject(object interface{}) ([]byte, error) {
func UnmarshalObject(data []byte, object interface{}) error {
return json.Unmarshal(data, object)
}
// UnmarshalObjectWithJsoniter decodes an object from binary data
// using the jsoniter library. It is mainly used to accelerate endpoint
// decoding at the moment.
func UnmarshalObjectWithJsoniter(data []byte, object interface{}) error {
var jsoni = jsoniter.ConfigCompatibleWithStandardLibrary
return jsoni.Unmarshal(data, &object)
}

View File

@@ -2,8 +2,9 @@ package migrator
import (
"github.com/boltdb/bolt"
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/user"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/bolt/user"
)
func (m *Migrator) updateAdminUserToDBVersion1() error {
@@ -22,7 +23,7 @@ func (m *Migrator) updateAdminUserToDBVersion1() error {
if err != nil {
return err
}
} else if err != nil && err != portainer.ErrObjectNotFound {
} else if err != nil && err != errors.ErrObjectNotFound {
return err
}
return nil

View File

@@ -2,8 +2,8 @@ package migrator
import (
"github.com/boltdb/bolt"
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
func (m *Migrator) updateResourceControlsToDBVersion2() error {

View File

@@ -1,6 +1,6 @@
package migrator
import "github.com/portainer/portainer"
import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion11() error {
legacyEndpoints, err := m.endpointService.Endpoints()

View File

@@ -5,9 +5,9 @@ import (
"strings"
"github.com/boltdb/bolt"
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
"github.com/portainer/portainer/bolt/stack"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/portainer/portainer/api/bolt/stack"
)
func (m *Migrator) updateEndpointsToVersion12() error {

View File

@@ -1,6 +1,6 @@
package migrator
import "github.com/portainer/portainer"
import "github.com/portainer/portainer/api"
func (m *Migrator) updateSettingsToVersion13() error {
legacySettings, err := m.settingsService.Settings()

View File

@@ -0,0 +1,16 @@
package migrator
func (m *Migrator) updateSettingsToDBVersion15() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
legacySettings.EnableHostManagementFeatures = false
return m.settingsService.UpdateSettings(legacySettings)
}
func (m *Migrator) updateTemplatesToVersion15() error {
// Removed with the entire template management layer, part of https://github.com/portainer/portainer/issues/3707
return nil
}

View File

@@ -0,0 +1,14 @@
package migrator
func (m *Migrator) updateSettingsToDBVersion16() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
if legacySettings.SnapshotInterval == "" {
legacySettings.SnapshotInterval = "5m"
}
return m.settingsService.UpdateSettings(legacySettings)
}

View File

@@ -0,0 +1,19 @@
package migrator
func (m *Migrator) updateExtensionsToDBVersion17() error {
legacyExtensions, err := m.extensionService.Extensions()
if err != nil {
return err
}
for _, extension := range legacyExtensions {
extension.License.Valid = true
err = m.extensionService.Persist(&extension)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,125 @@
package migrator
import (
portainer "github.com/portainer/portainer/api"
)
func (m *Migrator) updateUsersToDBVersion18() error {
legacyUsers, err := m.userService.Users()
if err != nil {
return err
}
for _, user := range legacyUsers {
user.PortainerAuthorizations = map[portainer.Authorization]bool{
portainer.OperationPortainerDockerHubInspect: true,
portainer.OperationPortainerEndpointGroupList: true,
portainer.OperationPortainerEndpointList: true,
portainer.OperationPortainerEndpointInspect: true,
portainer.OperationPortainerEndpointExtensionAdd: true,
portainer.OperationPortainerEndpointExtensionRemove: true,
portainer.OperationPortainerExtensionList: true,
portainer.OperationPortainerMOTD: true,
portainer.OperationPortainerRegistryList: true,
portainer.OperationPortainerRegistryInspect: true,
portainer.OperationPortainerTeamList: true,
portainer.OperationPortainerTemplateList: true,
portainer.OperationPortainerTemplateInspect: true,
portainer.OperationPortainerUserList: true,
portainer.OperationPortainerUserMemberships: true,
}
err = m.userService.UpdateUser(user.ID, &user)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateEndpointsToDBVersion18() error {
legacyEndpoints, err := m.endpointService.Endpoints()
if err != nil {
return err
}
for _, endpoint := range legacyEndpoints {
endpoint.UserAccessPolicies = make(portainer.UserAccessPolicies)
for _, userID := range endpoint.AuthorizedUsers {
endpoint.UserAccessPolicies[userID] = portainer.AccessPolicy{
RoleID: 4,
}
}
endpoint.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
for _, teamID := range endpoint.AuthorizedTeams {
endpoint.TeamAccessPolicies[teamID] = portainer.AccessPolicy{
RoleID: 4,
}
}
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
legacyEndpointGroups, err := m.endpointGroupService.EndpointGroups()
if err != nil {
return err
}
for _, endpointGroup := range legacyEndpointGroups {
endpointGroup.UserAccessPolicies = make(portainer.UserAccessPolicies)
for _, userID := range endpointGroup.AuthorizedUsers {
endpointGroup.UserAccessPolicies[userID] = portainer.AccessPolicy{
RoleID: 4,
}
}
endpointGroup.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
for _, teamID := range endpointGroup.AuthorizedTeams {
endpointGroup.TeamAccessPolicies[teamID] = portainer.AccessPolicy{
RoleID: 4,
}
}
err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateRegistriesToDBVersion18() error {
legacyRegistries, err := m.registryService.Registries()
if err != nil {
return err
}
for _, registry := range legacyRegistries {
registry.UserAccessPolicies = make(portainer.UserAccessPolicies)
for _, userID := range registry.AuthorizedUsers {
registry.UserAccessPolicies[userID] = portainer.AccessPolicy{}
}
registry.TeamAccessPolicies = make(portainer.TeamAccessPolicies)
for _, teamID := range registry.AuthorizedTeams {
registry.TeamAccessPolicies[teamID] = portainer.AccessPolicy{}
}
err = m.registryService.UpdateRegistry(registry.ID, &registry)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,16 @@
package migrator
import portainer "github.com/portainer/portainer/api"
func (m *Migrator) updateSettingsToDBVersion19() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
if legacySettings.EdgeAgentCheckinInterval == 0 {
legacySettings.EdgeAgentCheckinInterval = portainer.DefaultEdgeAgentCheckinIntervalInSeconds
}
return m.settingsService.UpdateSettings(legacySettings)
}

View File

@@ -0,0 +1,57 @@
package migrator
import (
"strings"
)
const scheduleScriptExecutionJobType = 1
func (m *Migrator) updateUsersToDBVersion20() error {
return m.authorizationService.UpdateUsersAuthorizations()
}
func (m *Migrator) updateSettingsToDBVersion20() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
legacySettings.AllowVolumeBrowserForRegularUsers = false
return m.settingsService.UpdateSettings(legacySettings)
}
func (m *Migrator) updateSchedulesToDBVersion20() error {
legacySchedules, err := m.scheduleService.Schedules()
if err != nil {
return err
}
for _, schedule := range legacySchedules {
if schedule.JobType == scheduleScriptExecutionJobType {
if schedule.CronExpression == "0 0 * * *" {
schedule.CronExpression = "0 * * * *"
} else if schedule.CronExpression == "0 0 0/2 * *" {
schedule.CronExpression = "0 */2 * * *"
} else if schedule.CronExpression == "0 0 0 * *" {
schedule.CronExpression = "0 0 * * *"
} else {
revisedCronExpression := strings.Split(schedule.CronExpression, " ")
if len(revisedCronExpression) == 5 {
continue
}
revisedCronExpression = revisedCronExpression[1:]
schedule.CronExpression = strings.Join(revisedCronExpression, " ")
}
err := m.scheduleService.UpdateSchedule(schedule.ID, &schedule)
if err != nil {
return err
}
}
}
return nil
}

View File

@@ -1,6 +1,6 @@
package migrator
import "github.com/portainer/portainer"
import "github.com/portainer/portainer/api"
func (m *Migrator) updateSettingsToDBVersion3() error {
legacySettings, err := m.settingsService.Settings()

View File

@@ -0,0 +1,85 @@
package migrator
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/internal/authorization"
)
func (m *Migrator) updateResourceControlsToDBVersion22() error {
legacyResourceControls, err := m.resourceControlService.ResourceControls()
if err != nil {
return err
}
for _, resourceControl := range legacyResourceControls {
resourceControl.AdministratorsOnly = false
err := m.resourceControlService.UpdateResourceControl(resourceControl.ID, &resourceControl)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
legacyUsers, err := m.userService.Users()
if err != nil {
return err
}
settings, err := m.settingsService.Settings()
if err != nil {
return err
}
for _, user := range legacyUsers {
user.PortainerAuthorizations = authorization.DefaultPortainerAuthorizations()
err = m.userService.UpdateUser(user.ID, &user)
if err != nil {
return err
}
}
endpointAdministratorRole, err := m.roleService.Role(portainer.RoleID(1))
if err != nil {
return err
}
endpointAdministratorRole.Priority = 1
endpointAdministratorRole.Authorizations = authorization.DefaultEndpointAuthorizationsForEndpointAdministratorRole()
err = m.roleService.UpdateRole(endpointAdministratorRole.ID, endpointAdministratorRole)
helpDeskRole, err := m.roleService.Role(portainer.RoleID(2))
if err != nil {
return err
}
helpDeskRole.Priority = 2
helpDeskRole.Authorizations = authorization.DefaultEndpointAuthorizationsForHelpDeskRole(settings.AllowVolumeBrowserForRegularUsers)
err = m.roleService.UpdateRole(helpDeskRole.ID, helpDeskRole)
standardUserRole, err := m.roleService.Role(portainer.RoleID(3))
if err != nil {
return err
}
standardUserRole.Priority = 3
standardUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForStandardUserRole(settings.AllowVolumeBrowserForRegularUsers)
err = m.roleService.UpdateRole(standardUserRole.ID, standardUserRole)
readOnlyUserRole, err := m.roleService.Role(portainer.RoleID(4))
if err != nil {
return err
}
readOnlyUserRole.Priority = 4
readOnlyUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForReadOnlyUserRole(settings.AllowVolumeBrowserForRegularUsers)
err = m.roleService.UpdateRole(readOnlyUserRole.ID, readOnlyUserRole)
if err != nil {
return err
}
return m.authorizationService.UpdateUsersAuthorizations()
}

View File

@@ -0,0 +1,92 @@
package migrator
import "github.com/portainer/portainer/api"
func (m *Migrator) updateTagsToDBVersion23() error {
tags, err := m.tagService.Tags()
if err != nil {
return err
}
for _, tag := range tags {
tag.EndpointGroups = make(map[portainer.EndpointGroupID]bool)
tag.Endpoints = make(map[portainer.EndpointID]bool)
err = m.tagService.UpdateTag(tag.ID, &tag)
if err != nil {
return err
}
}
return nil
}
func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
tags, err := m.tagService.Tags()
if err != nil {
return err
}
tagsNameMap := make(map[string]portainer.Tag)
for _, tag := range tags {
tagsNameMap[tag.Name] = tag
}
endpoints, err := m.endpointService.Endpoints()
if err != nil {
return err
}
for _, endpoint := range endpoints {
endpointTags := make([]portainer.TagID, 0)
for _, tagName := range endpoint.Tags {
tag, ok := tagsNameMap[tagName]
if ok {
endpointTags = append(endpointTags, tag.ID)
tag.Endpoints[endpoint.ID] = true
}
}
endpoint.TagIDs = endpointTags
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
relation := &portainer.EndpointRelation{
EndpointID: endpoint.ID,
EdgeStacks: map[portainer.EdgeStackID]bool{},
}
err = m.endpointRelationService.CreateEndpointRelation(relation)
if err != nil {
return err
}
}
endpointGroups, err := m.endpointGroupService.EndpointGroups()
if err != nil {
return err
}
for _, endpointGroup := range endpointGroups {
endpointGroupTags := make([]portainer.TagID, 0)
for _, tagName := range endpointGroup.Tags {
tag, ok := tagsNameMap[tagName]
if ok {
endpointGroupTags = append(endpointGroupTags, tag.ID)
tag.EndpointGroups[endpointGroup.ID] = true
}
}
endpointGroup.TagIDs = endpointGroupTags
err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
if err != nil {
return err
}
}
for _, tag := range tagsNameMap {
err = m.tagService.UpdateTag(tag.ID, &tag)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,34 @@
package migrator
import portainer "github.com/portainer/portainer/api"
func (m *Migrator) updateSettingsToDB24() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
legacySettings.AllowHostNamespaceForRegularUsers = true
legacySettings.AllowDeviceMappingForRegularUsers = true
legacySettings.AllowStackManagementForRegularUsers = true
return m.settingsService.UpdateSettings(legacySettings)
}
func (m *Migrator) updateStacksToDB24() error {
stacks, err := m.stackService.Stacks()
if err != nil {
return err
}
for idx := range stacks {
stack := &stacks[idx]
stack.Status = portainer.StackStatusActive
err := m.stackService.UpdateStack(stack.ID, stack)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,23 @@
package migrator
import (
"github.com/portainer/portainer/api"
)
func (m *Migrator) updateSettingsToDB25() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
return err
}
if legacySettings.TemplatesURL == "" {
legacySettings.TemplatesURL = portainer.DefaultTemplatesURL
}
legacySettings.UserSessionTimeout = portainer.DefaultUserSessionTimeout
legacySettings.EnableTelemetry = true
legacySettings.AllowContainerCapabilitiesForRegularUsers = true
return m.settingsService.UpdateSettings(legacySettings)
}

View File

@@ -0,0 +1,51 @@
package migrator
import (
portainer "github.com/portainer/portainer/api"
)
func (m *Migrator) updateEndpointSettingsToDB25() error {
settings, err := m.settingsService.Settings()
if err != nil {
return err
}
endpoints, err := m.endpointService.Endpoints()
if err != nil {
return err
}
for i := range endpoints {
endpoint := endpoints[i]
securitySettings := portainer.EndpointSecuritySettings{}
if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment ||
endpoint.Type == portainer.AgentOnDockerEnvironment ||
endpoint.Type == portainer.DockerEnvironment {
securitySettings = portainer.EndpointSecuritySettings{
AllowBindMountsForRegularUsers: settings.AllowBindMountsForRegularUsers,
AllowContainerCapabilitiesForRegularUsers: settings.AllowContainerCapabilitiesForRegularUsers,
AllowDeviceMappingForRegularUsers: settings.AllowDeviceMappingForRegularUsers,
AllowHostNamespaceForRegularUsers: settings.AllowHostNamespaceForRegularUsers,
AllowPrivilegedModeForRegularUsers: settings.AllowPrivilegedModeForRegularUsers,
AllowStackManagementForRegularUsers: settings.AllowStackManagementForRegularUsers,
}
if endpoint.Type == portainer.AgentOnDockerEnvironment || endpoint.Type == portainer.EdgeAgentOnDockerEnvironment {
securitySettings.AllowVolumeBrowserForRegularUsers = settings.AllowVolumeBrowserForRegularUsers
securitySettings.EnableHostManagementFeatures = settings.EnableHostManagementFeatures
}
}
endpoint.SecuritySettings = securitySettings
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,40 @@
package migrator
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/internal/stackutils"
)
func (m *Migrator) updateStackResourceControlToDB27() error {
resourceControls, err := m.resourceControlService.ResourceControls()
if err != nil {
return err
}
for _, resource := range resourceControls {
if resource.Type != portainer.StackResourceControl {
continue
}
stackName := resource.ResourceID
stack, err := m.stackService.StackByName(stackName)
if err != nil {
if err == errors.ErrObjectNotFound {
continue
}
return err
}
resource.ResourceID = stackutils.ResourceControlID(stack.EndpointID, stack.Name)
err = m.resourceControlService.UpdateResourceControl(resource.ID, &resource)
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,6 +1,6 @@
package migrator
import "github.com/portainer/portainer"
import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToDBVersion4() error {
legacyEndpoints, err := m.endpointService.Endpoints()

View File

@@ -1,6 +1,6 @@
package migrator
import "github.com/portainer/portainer"
import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion8() error {
legacyEndpoints, err := m.endpointService.Endpoints()

View File

@@ -1,6 +1,6 @@
package migrator
import "github.com/portainer/portainer"
import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion9() error {
legacyEndpoints, err := m.endpointService.Endpoints()

View File

@@ -1,6 +1,6 @@
package migrator
import "github.com/portainer/portainer"
import "github.com/portainer/portainer/api"
func (m *Migrator) updateEndpointsToVersion10() error {
legacyEndpoints, err := m.endpointService.Endpoints()

View File

@@ -2,65 +2,96 @@ package migrator
import (
"github.com/boltdb/bolt"
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/endpoint"
"github.com/portainer/portainer/bolt/endpointgroup"
"github.com/portainer/portainer/bolt/resourcecontrol"
"github.com/portainer/portainer/bolt/settings"
"github.com/portainer/portainer/bolt/stack"
"github.com/portainer/portainer/bolt/user"
"github.com/portainer/portainer/bolt/version"
portainer "github.com/portainer/portainer/api"
"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/registry"
"github.com/portainer/portainer/api/bolt/resourcecontrol"
"github.com/portainer/portainer/api/bolt/role"
"github.com/portainer/portainer/api/bolt/schedule"
"github.com/portainer/portainer/api/bolt/settings"
"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/user"
"github.com/portainer/portainer/api/bolt/version"
"github.com/portainer/portainer/api/internal/authorization"
)
type (
// Migrator defines a service to migrate data after a Portainer version update.
Migrator struct {
currentDBVersion int
db *bolt.DB
endpointGroupService *endpointgroup.Service
endpointService *endpoint.Service
resourceControlService *resourcecontrol.Service
settingsService *settings.Service
stackService *stack.Service
userService *user.Service
versionService *version.Service
fileService portainer.FileService
currentDBVersion int
db *bolt.DB
endpointGroupService *endpointgroup.Service
endpointService *endpoint.Service
endpointRelationService *endpointrelation.Service
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
userService *user.Service
versionService *version.Service
fileService portainer.FileService
authorizationService *authorization.Service
}
// Parameters represents the required parameters to create a new Migrator instance.
Parameters struct {
DB *bolt.DB
DatabaseVersion int
EndpointGroupService *endpointgroup.Service
EndpointService *endpoint.Service
ResourceControlService *resourcecontrol.Service
SettingsService *settings.Service
StackService *stack.Service
UserService *user.Service
VersionService *version.Service
FileService portainer.FileService
DB *bolt.DB
DatabaseVersion int
EndpointGroupService *endpointgroup.Service
EndpointService *endpoint.Service
EndpointRelationService *endpointrelation.Service
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
UserService *user.Service
VersionService *version.Service
FileService portainer.FileService
AuthorizationService *authorization.Service
}
)
// NewMigrator creates a new Migrator.
func NewMigrator(parameters *Parameters) *Migrator {
return &Migrator{
db: parameters.DB,
currentDBVersion: parameters.DatabaseVersion,
endpointGroupService: parameters.EndpointGroupService,
endpointService: parameters.EndpointService,
resourceControlService: parameters.ResourceControlService,
settingsService: parameters.SettingsService,
stackService: parameters.StackService,
userService: parameters.UserService,
versionService: parameters.VersionService,
fileService: parameters.FileService,
db: parameters.DB,
currentDBVersion: parameters.DatabaseVersion,
endpointGroupService: parameters.EndpointGroupService,
endpointService: parameters.EndpointService,
endpointRelationService: parameters.EndpointRelationService,
extensionService: parameters.ExtensionService,
registryService: parameters.RegistryService,
resourceControlService: parameters.ResourceControlService,
roleService: parameters.RoleService,
scheduleService: parameters.ScheduleService,
settingsService: parameters.SettingsService,
tagService: parameters.TagService,
teamMembershipService: parameters.TeamMembershipService,
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()
@@ -186,5 +217,146 @@ func (m *Migrator) Migrate() error {
}
}
// Portainer 1.20.0
if m.currentDBVersion < 15 {
err := m.updateSettingsToDBVersion15()
if err != nil {
return err
}
err = m.updateTemplatesToVersion15()
if err != nil {
return err
}
}
if m.currentDBVersion < 16 {
err := m.updateSettingsToDBVersion16()
if err != nil {
return err
}
}
// Portainer 1.20.1
if m.currentDBVersion < 17 {
err := m.updateExtensionsToDBVersion17()
if err != nil {
return err
}
}
// Portainer 1.21.0
if m.currentDBVersion < 18 {
err := m.updateUsersToDBVersion18()
if err != nil {
return err
}
err = m.updateEndpointsToDBVersion18()
if err != nil {
return err
}
err = m.updateEndpointGroupsToDBVersion18()
if err != nil {
return err
}
err = m.updateRegistriesToDBVersion18()
if err != nil {
return err
}
}
// Portainer 1.22.0
if m.currentDBVersion < 19 {
err := m.updateSettingsToDBVersion19()
if err != nil {
return err
}
}
// Portainer 1.22.1
if m.currentDBVersion < 20 {
err := m.updateUsersToDBVersion20()
if err != nil {
return err
}
err = m.updateSettingsToDBVersion20()
if err != nil {
return err
}
err = m.updateSchedulesToDBVersion20()
if err != nil {
return err
}
}
// Portainer 1.23.0
// DBVersion 21 is missing as it was shipped as via hotfix 1.22.2
if m.currentDBVersion < 22 {
err := m.updateResourceControlsToDBVersion22()
if err != nil {
return err
}
err = m.updateUsersAndRolesToDBVersion22()
if err != nil {
return err
}
}
// Portainer 1.24.0
if m.currentDBVersion < 23 {
err := m.updateTagsToDBVersion23()
if err != nil {
return err
}
err = m.updateEndpointsAndEndpointGroupsToDBVersion23()
if err != nil {
return err
}
}
// Portainer 1.24.1
if m.currentDBVersion < 24 {
err := m.updateSettingsToDB24()
if err != nil {
return err
}
}
// Portainer 2.0.0
if m.currentDBVersion < 25 {
err := m.updateSettingsToDB25()
if err != nil {
return err
}
err = m.updateStacksToDB24()
if err != nil {
return err
}
}
// Portainer 2.1.0
if m.currentDBVersion < 26 {
err := m.updateEndpointSettingsToDB25()
if err != nil {
return err
}
}
// Portainer 2.2.0
if m.currentDBVersion < 27 {
err := m.updateStackResourceControlToDB27()
if err != nil {
return err
}
}
return m.versionService.StoreDBVersion(portainer.DBVersion)
}

View File

@@ -1,8 +1,8 @@
package registry
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +14,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +34,7 @@ func (service *Service) Registry(ID portainer.RegistryID) (*portainer.Registry,
var registry portainer.Registry
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &registry)
err := internal.GetObject(service.connection, BucketName, identifier, &registry)
if err != nil {
return nil, err
}
@@ -46,7 +46,7 @@ func (service *Service) Registry(ID portainer.RegistryID) (*portainer.Registry,
func (service *Service) Registries() ([]portainer.Registry, error) {
var registries = make([]portainer.Registry, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -67,7 +67,7 @@ func (service *Service) Registries() ([]portainer.Registry, error) {
// CreateRegistry creates a new registry.
func (service *Service) CreateRegistry(registry *portainer.Registry) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
@@ -85,11 +85,11 @@ func (service *Service) CreateRegistry(registry *portainer.Registry) error {
// UpdateRegistry updates an registry.
func (service *Service) UpdateRegistry(ID portainer.RegistryID, registry *portainer.Registry) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, registry)
return internal.UpdateObject(service.connection, BucketName, identifier, registry)
}
// DeleteRegistry deletes an registry.
func (service *Service) DeleteRegistry(ID portainer.RegistryID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}

View File

@@ -1,8 +1,8 @@
package resourcecontrol
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +14,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +34,7 @@ func (service *Service) ResourceControl(ID portainer.ResourceControlID) (*portai
var resourceControl portainer.ResourceControl
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &resourceControl)
err := internal.GetObject(service.connection, BucketName, identifier, &resourceControl)
if err != nil {
return nil, err
}
@@ -42,12 +42,13 @@ func (service *Service) ResourceControl(ID portainer.ResourceControlID) (*portai
return &resourceControl, nil
}
// ResourceControlByResourceID returns a ResourceControl object by checking if the resourceID is equal
// to the main ResourceID or in SubResourceIDs
func (service *Service) ResourceControlByResourceID(resourceID string) (*portainer.ResourceControl, error) {
// ResourceControlByResourceIDAndType returns a ResourceControl object by checking if the resourceID is equal
// to the main ResourceID or in SubResourceIDs. It also performs a check on the resource type. Return nil
// if no ResourceControl was found.
func (service *Service) ResourceControlByResourceIDAndType(resourceID string, resourceType portainer.ResourceControlType) (*portainer.ResourceControl, error) {
var resourceControl *portainer.ResourceControl
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -58,7 +59,7 @@ func (service *Service) ResourceControlByResourceID(resourceID string) (*portain
return err
}
if rc.ResourceID == resourceID {
if rc.ResourceID == resourceID && rc.Type == resourceType {
resourceControl = &rc
break
}
@@ -71,10 +72,6 @@ func (service *Service) ResourceControlByResourceID(resourceID string) (*portain
}
}
if resourceControl == nil {
return portainer.ErrObjectNotFound
}
return nil
})
@@ -85,7 +82,7 @@ func (service *Service) ResourceControlByResourceID(resourceID string) (*portain
func (service *Service) ResourceControls() ([]portainer.ResourceControl, error) {
var rcs = make([]portainer.ResourceControl, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -106,7 +103,7 @@ func (service *Service) ResourceControls() ([]portainer.ResourceControl, error)
// CreateResourceControl creates a new ResourceControl object
func (service *Service) CreateResourceControl(resourceControl *portainer.ResourceControl) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
@@ -124,11 +121,11 @@ func (service *Service) CreateResourceControl(resourceControl *portainer.Resourc
// UpdateResourceControl saves a ResourceControl object.
func (service *Service) UpdateResourceControl(ID portainer.ResourceControlID, resourceControl *portainer.ResourceControl) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, resourceControl)
return internal.UpdateObject(service.connection, BucketName, identifier, resourceControl)
}
// DeleteResourceControl deletes a ResourceControl object by ID
func (service *Service) DeleteResourceControl(ID portainer.ResourceControlID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}

89
api/bolt/role/role.go Normal file
View File

@@ -0,0 +1,89 @@
package role
import (
portainer "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 = "roles"
)
// Service represents a service for managing endpoint data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// Role returns a Role by ID
func (service *Service) Role(ID portainer.RoleID) (*portainer.Role, error) {
var set portainer.Role
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &set)
if err != nil {
return nil, err
}
return &set, nil
}
// Roles return an array containing all the sets.
func (service *Service) Roles() ([]portainer.Role, error) {
var sets = make([]portainer.Role, 0)
err := service.connection.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 set portainer.Role
err := internal.UnmarshalObject(v, &set)
if err != nil {
return err
}
sets = append(sets, set)
}
return nil
})
return sets, err
}
// CreateRole creates a new Role.
func (service *Service) CreateRole(role *portainer.Role) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
role.ID = portainer.RoleID(id)
data, err := internal.MarshalObject(role)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(role.ID)), data)
})
}
// UpdateRole updates a role.
func (service *Service) UpdateRole(ID portainer.RoleID, role *portainer.Role) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.connection, BucketName, identifier, role)
}

View File

@@ -0,0 +1,129 @@
package schedule
import (
portainer "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 = "schedules"
)
// Service represents a service for managing schedule data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// Schedule returns a schedule by ID.
func (service *Service) Schedule(ID portainer.ScheduleID) (*portainer.Schedule, error) {
var schedule portainer.Schedule
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &schedule)
if err != nil {
return nil, err
}
return &schedule, nil
}
// UpdateSchedule updates a schedule.
func (service *Service) UpdateSchedule(ID portainer.ScheduleID, schedule *portainer.Schedule) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.connection, BucketName, identifier, schedule)
}
// DeleteSchedule deletes a schedule.
func (service *Service) DeleteSchedule(ID portainer.ScheduleID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// Schedules return a array containing all the schedules.
func (service *Service) Schedules() ([]portainer.Schedule, error) {
var schedules = make([]portainer.Schedule, 0)
err := service.connection.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 schedule portainer.Schedule
err := internal.UnmarshalObject(v, &schedule)
if err != nil {
return err
}
schedules = append(schedules, schedule)
}
return nil
})
return schedules, err
}
// SchedulesByJobType return a array containing all the schedules
// with the specified JobType.
func (service *Service) SchedulesByJobType(jobType portainer.JobType) ([]portainer.Schedule, error) {
var schedules = make([]portainer.Schedule, 0)
err := service.connection.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 schedule portainer.Schedule
err := internal.UnmarshalObject(v, &schedule)
if err != nil {
return err
}
if schedule.JobType == jobType {
schedules = append(schedules, schedule)
}
}
return nil
})
return schedules, err
}
// CreateSchedule assign an ID to a new schedule and saves it.
func (service *Service) CreateSchedule(schedule *portainer.Schedule) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
// We manually manage sequences for schedules
err := bucket.SetSequence(uint64(schedule.ID))
if err != nil {
return err
}
data, err := internal.MarshalObject(schedule)
if err != nil {
return err
}
return bucket.Put(internal.Itob(int(schedule.ID)), data)
})
}
// GetNextIdentifier returns the next identifier for a schedule.
func (service *Service) GetNextIdentifier() int {
return internal.GetNextIdentifier(service.connection, BucketName)
}

263
api/bolt/services.go Normal file
View File

@@ -0,0 +1,263 @@
package bolt
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/customtemplate"
"github.com/portainer/portainer/api/bolt/dockerhub"
"github.com/portainer/portainer/api/bolt/edgegroup"
"github.com/portainer/portainer/api/bolt/edgejob"
"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/registry"
"github.com/portainer/portainer/api/bolt/resourcecontrol"
"github.com/portainer/portainer/api/bolt/role"
"github.com/portainer/portainer/api/bolt/schedule"
"github.com/portainer/portainer/api/bolt/settings"
"github.com/portainer/portainer/api/bolt/stack"
"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/user"
"github.com/portainer/portainer/api/bolt/version"
"github.com/portainer/portainer/api/bolt/webhook"
)
func (store *Store) initServices() error {
authorizationsetService, err := role.NewService(store.connection)
if err != nil {
return err
}
store.RoleService = authorizationsetService
customTemplateService, err := customtemplate.NewService(store.connection)
if err != nil {
return err
}
store.CustomTemplateService = customTemplateService
dockerhubService, err := dockerhub.NewService(store.connection)
if err != nil {
return err
}
store.DockerHubService = dockerhubService
edgeStackService, err := edgestack.NewService(store.connection)
if err != nil {
return err
}
store.EdgeStackService = edgeStackService
edgeGroupService, err := edgegroup.NewService(store.connection)
if err != nil {
return err
}
store.EdgeGroupService = edgeGroupService
edgeJobService, err := edgejob.NewService(store.connection)
if err != nil {
return err
}
store.EdgeJobService = edgeJobService
endpointgroupService, err := endpointgroup.NewService(store.connection)
if err != nil {
return err
}
store.EndpointGroupService = endpointgroupService
endpointService, err := endpoint.NewService(store.connection)
if err != nil {
return err
}
store.EndpointService = endpointService
endpointRelationService, err := endpointrelation.NewService(store.connection)
if err != nil {
return err
}
store.EndpointRelationService = endpointRelationService
extensionService, err := extension.NewService(store.connection)
if err != nil {
return err
}
store.ExtensionService = extensionService
registryService, err := registry.NewService(store.connection)
if err != nil {
return err
}
store.RegistryService = registryService
resourcecontrolService, err := resourcecontrol.NewService(store.connection)
if err != nil {
return err
}
store.ResourceControlService = resourcecontrolService
settingsService, err := settings.NewService(store.connection)
if err != nil {
return err
}
store.SettingsService = settingsService
stackService, err := stack.NewService(store.connection)
if err != nil {
return err
}
store.StackService = stackService
tagService, err := tag.NewService(store.connection)
if err != nil {
return err
}
store.TagService = tagService
teammembershipService, err := teammembership.NewService(store.connection)
if err != nil {
return err
}
store.TeamMembershipService = teammembershipService
teamService, err := team.NewService(store.connection)
if err != nil {
return err
}
store.TeamService = teamService
tunnelServerService, err := tunnelserver.NewService(store.connection)
if err != nil {
return err
}
store.TunnelServerService = tunnelServerService
userService, err := user.NewService(store.connection)
if err != nil {
return err
}
store.UserService = userService
versionService, err := version.NewService(store.connection)
if err != nil {
return err
}
store.VersionService = versionService
webhookService, err := webhook.NewService(store.connection)
if err != nil {
return err
}
store.WebhookService = webhookService
scheduleService, err := schedule.NewService(store.connection)
if err != nil {
return err
}
store.ScheduleService = scheduleService
return nil
}
// CustomTemplate gives access to the CustomTemplate data management layer
func (store *Store) CustomTemplate() portainer.CustomTemplateService {
return store.CustomTemplateService
}
// 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
}
// EdgeJob gives access to the EdgeJob data management layer
func (store *Store) EdgeJob() portainer.EdgeJobService {
return store.EdgeJobService
}
// 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
}
// 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
}
// 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

@@ -1,10 +1,8 @@
package settings
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
"github.com/boltdb/bolt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
@@ -15,18 +13,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +32,7 @@ func NewService(db *bolt.DB) (*Service, error) {
func (service *Service) Settings() (*portainer.Settings, error) {
var settings portainer.Settings
err := internal.GetObject(service.db, BucketName, []byte(settingsKey), &settings)
err := internal.GetObject(service.connection, BucketName, []byte(settingsKey), &settings)
if err != nil {
return nil, err
}
@@ -44,5 +42,5 @@ func (service *Service) Settings() (*portainer.Settings, error) {
// UpdateSettings persists a Settings object.
func (service *Service) UpdateSettings(settings *portainer.Settings) error {
return internal.UpdateObject(service.db, BucketName, []byte(settingsKey), settings)
return internal.UpdateObject(service.connection, BucketName, []byte(settingsKey), settings)
}

View File

@@ -1,8 +1,9 @@
package stack
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +15,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +35,7 @@ func (service *Service) Stack(ID portainer.StackID) (*portainer.Stack, error) {
var stack portainer.Stack
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &stack)
err := internal.GetObject(service.connection, BucketName, identifier, &stack)
if err != nil {
return nil, err
}
@@ -46,7 +47,7 @@ func (service *Service) Stack(ID portainer.StackID) (*portainer.Stack, error) {
func (service *Service) StackByName(name string) (*portainer.Stack, error) {
var stack *portainer.Stack
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -64,7 +65,7 @@ func (service *Service) StackByName(name string) (*portainer.Stack, error) {
}
if stack == nil {
return portainer.ErrObjectNotFound
return errors.ErrObjectNotFound
}
return nil
@@ -77,7 +78,7 @@ func (service *Service) StackByName(name string) (*portainer.Stack, error) {
func (service *Service) Stacks() ([]portainer.Stack, error) {
var stacks = make([]portainer.Stack, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -98,12 +99,12 @@ func (service *Service) Stacks() ([]portainer.Stack, error) {
// GetNextIdentifier returns the next identifier for a stack.
func (service *Service) GetNextIdentifier() int {
return internal.GetNextIdentifier(service.db, BucketName)
return internal.GetNextIdentifier(service.connection, BucketName)
}
// CreateStack creates a new stack.
func (service *Service) CreateStack(stack *portainer.Stack) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
// We manually manage sequences for stacks
@@ -124,11 +125,16 @@ func (service *Service) CreateStack(stack *portainer.Stack) error {
// UpdateStack updates a stack.
func (service *Service) UpdateStack(ID portainer.StackID, stack *portainer.Stack) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, stack)
return internal.UpdateObject(service.connection, BucketName, identifier, stack)
}
// DeleteStack deletes a stack.
func (service *Service) DeleteStack(ID portainer.StackID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// StackByWebhookID returns a stack via searching with webhook ID
func (service *Service) StackByWebhookID(id string) (*portainer.Stack, error) {
return nil, errors.ErrObjectNotFound
}

View File

@@ -1,8 +1,8 @@
package tag
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +14,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -33,7 +33,7 @@ func NewService(db *bolt.DB) (*Service, error) {
func (service *Service) Tags() ([]portainer.Tag, error) {
var tags = make([]portainer.Tag, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -52,9 +52,22 @@ func (service *Service) Tags() ([]portainer.Tag, error) {
return tags, err
}
// Tag returns a tag by ID.
func (service *Service) Tag(ID portainer.TagID) (*portainer.Tag, error) {
var tag portainer.Tag
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.connection, BucketName, identifier, &tag)
if err != nil {
return nil, err
}
return &tag, nil
}
// CreateTag creates a new tag.
func (service *Service) CreateTag(tag *portainer.Tag) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
@@ -69,8 +82,14 @@ func (service *Service) CreateTag(tag *portainer.Tag) error {
})
}
// UpdateTag updates a tag.
func (service *Service) UpdateTag(ID portainer.TagID, tag *portainer.Tag) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.connection, BucketName, identifier, tag)
}
// DeleteTag deletes a tag.
func (service *Service) DeleteTag(ID portainer.TagID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}

View File

@@ -1,8 +1,11 @@
package team
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
"strings"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +17,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +37,7 @@ func (service *Service) Team(ID portainer.TeamID) (*portainer.Team, error) {
var team portainer.Team
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &team)
err := internal.GetObject(service.connection, BucketName, identifier, &team)
if err != nil {
return nil, err
}
@@ -46,7 +49,7 @@ func (service *Service) Team(ID portainer.TeamID) (*portainer.Team, error) {
func (service *Service) TeamByName(name string) (*portainer.Team, error) {
var team *portainer.Team
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -57,14 +60,14 @@ func (service *Service) TeamByName(name string) (*portainer.Team, error) {
return err
}
if t.Name == name {
if strings.EqualFold(t.Name, name) {
team = &t
break
}
}
if team == nil {
return portainer.ErrObjectNotFound
return errors.ErrObjectNotFound
}
return nil
@@ -77,7 +80,7 @@ func (service *Service) TeamByName(name string) (*portainer.Team, error) {
func (service *Service) Teams() ([]portainer.Team, error) {
var teams = make([]portainer.Team, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -99,12 +102,12 @@ func (service *Service) Teams() ([]portainer.Team, error) {
// UpdateTeam saves a Team.
func (service *Service) UpdateTeam(ID portainer.TeamID, team *portainer.Team) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, team)
return internal.UpdateObject(service.connection, BucketName, identifier, team)
}
// CreateTeam creates a new Team.
func (service *Service) CreateTeam(team *portainer.Team) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
@@ -122,5 +125,5 @@ func (service *Service) CreateTeam(team *portainer.Team) error {
// DeleteTeam deletes a Team.
func (service *Service) DeleteTeam(ID portainer.TeamID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}

View File

@@ -1,8 +1,8 @@
package teammembership
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +14,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +34,7 @@ func (service *Service) TeamMembership(ID portainer.TeamMembershipID) (*portaine
var membership portainer.TeamMembership
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &membership)
err := internal.GetObject(service.connection, BucketName, identifier, &membership)
if err != nil {
return nil, err
}
@@ -46,7 +46,7 @@ func (service *Service) TeamMembership(ID portainer.TeamMembershipID) (*portaine
func (service *Service) TeamMemberships() ([]portainer.TeamMembership, error) {
var memberships = make([]portainer.TeamMembership, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -69,7 +69,7 @@ func (service *Service) TeamMemberships() ([]portainer.TeamMembership, error) {
func (service *Service) TeamMembershipsByUserID(userID portainer.UserID) ([]portainer.TeamMembership, error) {
var memberships = make([]portainer.TeamMembership, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -95,7 +95,7 @@ func (service *Service) TeamMembershipsByUserID(userID portainer.UserID) ([]port
func (service *Service) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]portainer.TeamMembership, error) {
var memberships = make([]portainer.TeamMembership, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -120,12 +120,12 @@ func (service *Service) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]port
// UpdateTeamMembership saves a TeamMembership object.
func (service *Service) UpdateTeamMembership(ID portainer.TeamMembershipID, membership *portainer.TeamMembership) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, membership)
return internal.UpdateObject(service.connection, BucketName, identifier, membership)
}
// CreateTeamMembership creates a new TeamMembership object.
func (service *Service) CreateTeamMembership(membership *portainer.TeamMembership) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
@@ -143,12 +143,12 @@ func (service *Service) CreateTeamMembership(membership *portainer.TeamMembershi
// DeleteTeamMembership deletes a TeamMembership object.
func (service *Service) DeleteTeamMembership(ID portainer.TeamMembershipID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// DeleteTeamMembershipByUserID deletes all the TeamMembership object associated to a UserID.
func (service *Service) DeleteTeamMembershipByUserID(userID portainer.UserID) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -173,7 +173,7 @@ func (service *Service) DeleteTeamMembershipByUserID(userID portainer.UserID) er
// DeleteTeamMembershipByTeamID deletes all the TeamMembership object associated to a TeamID.
func (service *Service) DeleteTeamMembershipByTeamID(teamID portainer.TeamID) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()

View File

@@ -1,95 +0,0 @@
package template
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/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

@@ -0,0 +1,46 @@
package tunnelserver
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "tunnel_server"
infoKey = "INFO"
)
// Service represents a service for managing endpoint data.
type Service struct {
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
}
// Info retrieve the TunnelServerInfo object.
func (service *Service) Info() (*portainer.TunnelServerInfo, error) {
var info portainer.TunnelServerInfo
err := internal.GetObject(service.connection, BucketName, []byte(infoKey), &info)
if err != nil {
return nil, err
}
return &info, nil
}
// UpdateInfo persists a TunnelServerInfo object.
func (service *Service) UpdateInfo(settings *portainer.TunnelServerInfo) error {
return internal.UpdateObject(service.connection, BucketName, []byte(infoKey), settings)
}

View File

@@ -1,8 +1,11 @@
package user
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
"strings"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +17,18 @@ const (
// Service represents a service for managing endpoint data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -34,7 +37,7 @@ func (service *Service) User(ID portainer.UserID) (*portainer.User, error) {
var user portainer.User
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &user)
err := internal.GetObject(service.connection, BucketName, identifier, &user)
if err != nil {
return nil, err
}
@@ -46,7 +49,9 @@ func (service *Service) User(ID portainer.UserID) (*portainer.User, error) {
func (service *Service) UserByUsername(username string) (*portainer.User, error) {
var user *portainer.User
err := service.db.View(func(tx *bolt.Tx) error {
username = strings.ToLower(username)
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -57,14 +62,14 @@ func (service *Service) UserByUsername(username string) (*portainer.User, error)
return err
}
if u.Username == username {
if strings.EqualFold(u.Username, username) {
user = &u
break
}
}
if user == nil {
return portainer.ErrObjectNotFound
return errors.ErrObjectNotFound
}
return nil
})
@@ -76,7 +81,7 @@ func (service *Service) UserByUsername(username string) (*portainer.User, error)
func (service *Service) Users() ([]portainer.User, error) {
var users = make([]portainer.User, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -98,7 +103,7 @@ func (service *Service) Users() ([]portainer.User, error) {
// UsersByRole return an array containing all the users with the specified role.
func (service *Service) UsersByRole(role portainer.UserRole) ([]portainer.User, error) {
var users = make([]portainer.User, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -122,16 +127,18 @@ func (service *Service) UsersByRole(role portainer.UserRole) ([]portainer.User,
// UpdateUser saves a user.
func (service *Service) UpdateUser(ID portainer.UserID, user *portainer.User) error {
identifier := internal.Itob(int(ID))
return internal.UpdateObject(service.db, BucketName, identifier, user)
user.Username = strings.ToLower(user.Username)
return internal.UpdateObject(service.connection, BucketName, identifier, user)
}
// CreateUser creates a new user.
func (service *Service) CreateUser(user *portainer.User) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()
user.ID = portainer.UserID(id)
user.Username = strings.ToLower(user.Username)
data, err := internal.MarshalObject(user)
if err != nil {
@@ -145,5 +152,5 @@ func (service *Service) CreateUser(user *portainer.User) error {
// DeleteUser deletes a user.
func (service *Service) DeleteUser(ID portainer.UserID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}

View File

@@ -4,30 +4,33 @@ import (
"strconv"
"github.com/boltdb/bolt"
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/bolt/internal"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "version"
versionKey = "DB_VERSION"
BucketName = "version"
versionKey = "DB_VERSION"
instanceKey = "INSTANCE_ID"
editionKey = "EDITION"
)
// Service represents a service to manage stored versions.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -35,12 +38,12 @@ func NewService(db *bolt.DB) (*Service, error) {
func (service *Service) DBVersion() (int, error) {
var data []byte
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
value := bucket.Get([]byte(versionKey))
if value == nil {
return portainer.ErrObjectNotFound
return errors.ErrObjectNotFound
}
data = make([]byte, len(value))
@@ -55,12 +58,94 @@ func (service *Service) DBVersion() (int, error) {
return strconv.Atoi(string(data))
}
// Edition retrieves the stored portainer edition.
func (service *Service) Edition() (portainer.SoftwareEdition, error) {
editionData, err := service.getKey(editionKey)
if err != nil {
return 0, err
}
edition, err := strconv.Atoi(string(editionData))
if err != nil {
return 0, err
}
return portainer.SoftwareEdition(edition), nil
}
// StoreDBVersion store the database version.
func (service *Service) StoreDBVersion(version int) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
data := []byte(strconv.Itoa(version))
return bucket.Put([]byte(versionKey), data)
})
}
// InstanceID retrieves the stored instance ID.
func (service *Service) InstanceID() (string, error) {
var data []byte
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
value := bucket.Get([]byte(instanceKey))
if value == nil {
return errors.ErrObjectNotFound
}
data = make([]byte, len(value))
copy(data, value)
return nil
})
if err != nil {
return "", err
}
return string(data), nil
}
// StoreInstanceID store the instance ID.
func (service *Service) StoreInstanceID(ID string) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
data := []byte(ID)
return bucket.Put([]byte(instanceKey), data)
})
}
func (service *Service) getKey(key string) ([]byte, error) {
var data []byte
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
value := bucket.Get([]byte(key))
if value == nil {
return errors.ErrObjectNotFound
}
data = make([]byte, len(value))
copy(data, value)
return nil
})
if err != nil {
return nil, err
}
return data, nil
}
func (service *Service) setKey(key string, value string) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
data := []byte(value)
return bucket.Put([]byte(key), data)
})
}

View File

@@ -1,8 +1,9 @@
package webhook
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt/internal"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/bolt/internal"
"github.com/boltdb/bolt"
)
@@ -14,18 +15,18 @@ const (
// Service represents a service for managing webhook data.
type Service struct {
db *bolt.DB
connection *internal.DbConnection
}
// NewService creates a new instance of a service.
func NewService(db *bolt.DB) (*Service, error) {
err := internal.CreateBucket(db, BucketName)
func NewService(connection *internal.DbConnection) (*Service, error) {
err := internal.CreateBucket(connection, BucketName)
if err != nil {
return nil, err
}
return &Service{
db: db,
connection: connection,
}, nil
}
@@ -33,7 +34,7 @@ func NewService(db *bolt.DB) (*Service, error) {
func (service *Service) Webhooks() ([]portainer.Webhook, error) {
var webhooks = make([]portainer.Webhook, 0)
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -57,7 +58,7 @@ func (service *Service) Webhook(ID portainer.WebhookID) (*portainer.Webhook, err
var webhook portainer.Webhook
identifier := internal.Itob(int(ID))
err := internal.GetObject(service.db, BucketName, identifier, &webhook)
err := internal.GetObject(service.connection, BucketName, identifier, &webhook)
if err != nil {
return nil, err
}
@@ -69,7 +70,7 @@ func (service *Service) Webhook(ID portainer.WebhookID) (*portainer.Webhook, err
func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, error) {
var webhook *portainer.Webhook
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -87,7 +88,7 @@ func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, erro
}
if webhook == nil {
return portainer.ErrObjectNotFound
return errors.ErrObjectNotFound
}
return nil
@@ -100,7 +101,7 @@ func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, erro
func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error) {
var webhook *portainer.Webhook
err := service.db.View(func(tx *bolt.Tx) error {
err := service.connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
cursor := bucket.Cursor()
@@ -118,7 +119,7 @@ func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error)
}
if webhook == nil {
return portainer.ErrObjectNotFound
return errors.ErrObjectNotFound
}
return nil
@@ -130,12 +131,12 @@ func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error)
// DeleteWebhook deletes a webhook.
func (service *Service) DeleteWebhook(ID portainer.WebhookID) error {
identifier := internal.Itob(int(ID))
return internal.DeleteObject(service.db, BucketName, identifier)
return internal.DeleteObject(service.connection, BucketName, identifier)
}
// CreateWebhook assign an ID to a new webhook and saves it.
func (service *Service) CreateWebhook(webhook *portainer.Webhook) error {
return service.db.Update(func(tx *bolt.Tx) error {
return service.connection.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(BucketName))
id, _ := bucket.NextSequence()

24
api/chisel/key.go Normal file
View File

@@ -0,0 +1,24 @@
package chisel
import (
"encoding/base64"
"fmt"
"strconv"
"strings"
)
// GenerateEdgeKey will generate a key that can be used by an Edge agent to register with a Portainer instance.
// The key represents the following data in this particular format:
// portainer_instance_url|tunnel_server_addr|tunnel_server_fingerprint|endpoint_ID
// The key returned by this function is a base64 encoded version of the data.
func (service *Service) GenerateEdgeKey(url, host string, endpointIdentifier int) string {
keyInformation := []string{
url,
fmt.Sprintf("%s:%s", host, service.serverPort),
service.serverFingerprint,
strconv.Itoa(endpointIdentifier),
}
key := strings.Join(keyInformation, "|")
return base64.RawStdEncoding.EncodeToString([]byte(key))
}

47
api/chisel/schedules.go Normal file
View File

@@ -0,0 +1,47 @@
package chisel
import (
"strconv"
portainer "github.com/portainer/portainer/api"
)
// AddEdgeJob register an EdgeJob inside the tunnel details associated to an endpoint.
func (service *Service) AddEdgeJob(endpointID portainer.EndpointID, edgeJob *portainer.EdgeJob) {
tunnel := service.GetTunnelDetails(endpointID)
existingJobIndex := -1
for idx, existingJob := range tunnel.Jobs {
if existingJob.ID == edgeJob.ID {
existingJobIndex = idx
break
}
}
if existingJobIndex == -1 {
tunnel.Jobs = append(tunnel.Jobs, *edgeJob)
} else {
tunnel.Jobs[existingJobIndex] = *edgeJob
}
key := strconv.Itoa(int(endpointID))
service.tunnelDetailsMap.Set(key, tunnel)
}
// RemoveEdgeJob will remove the specified Edge job from each tunnel it was registered with.
func (service *Service) RemoveEdgeJob(edgeJobID portainer.EdgeJobID) {
for item := range service.tunnelDetailsMap.IterBuffered() {
tunnelDetails := item.Val.(*portainer.TunnelDetails)
updatedJobs := make([]portainer.EdgeJob, 0)
for _, edgeJob := range tunnelDetails.Jobs {
if edgeJob.ID == edgeJobID {
continue
}
updatedJobs = append(updatedJobs, edgeJob)
}
tunnelDetails.Jobs = updatedJobs
service.tunnelDetailsMap.Set(item.Key, tunnelDetails)
}
}

199
api/chisel/service.go Normal file
View File

@@ -0,0 +1,199 @@
package chisel
import (
"context"
"fmt"
"log"
"strconv"
"time"
"github.com/dchest/uniuri"
chserver "github.com/jpillora/chisel/server"
cmap "github.com/orcaman/concurrent-map"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
)
const (
tunnelCleanupInterval = 10 * time.Second
requiredTimeout = 15 * time.Second
activeTimeout = 4*time.Minute + 30*time.Second
)
// Service represents a service to manage the state of multiple reverse tunnels.
// 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
snapshotService portainer.SnapshotService
chiselServer *chserver.Server
shutdownCtx context.Context
}
// NewService returns a pointer to a new instance of Service
func NewService(dataStore portainer.DataStore, shutdownCtx context.Context) *Service {
return &Service{
tunnelDetailsMap: cmap.New(),
dataStore: dataStore,
shutdownCtx: shutdownCtx,
}
}
// StartTunnelServer starts a tunnel server on the specified addr and port.
// It uses a seed to generate a new private/public key pair. If the seed cannot
// be found inside the database, it will generate a new one randomly and persist it.
// It starts the tunnel status verification process in the background.
// The snapshotter is used in the tunnel status verification process.
func (service *Service) StartTunnelServer(addr, port string, snapshotService portainer.SnapshotService) error {
keySeed, err := service.retrievePrivateKeySeed()
if err != nil {
return err
}
config := &chserver.Config{
Reverse: true,
KeySeed: keySeed,
}
chiselServer, err := chserver.NewServer(config)
if err != nil {
return err
}
service.serverFingerprint = chiselServer.GetFingerprint()
service.serverPort = port
err = chiselServer.Start(addr, port)
if err != nil {
return err
}
service.chiselServer = chiselServer
// TODO: work-around Chisel default behavior.
// By default, Chisel will allow anyone to connect if no user exists.
username, password := generateRandomCredentials()
err = service.chiselServer.AddUser(username, password, "127.0.0.1")
if err != nil {
return err
}
service.snapshotService = snapshotService
go service.startTunnelVerificationLoop()
return nil
}
// StopTunnelServer stops tunnel http server
func (service *Service) StopTunnelServer() error {
return service.chiselServer.Close()
}
func (service *Service) retrievePrivateKeySeed() (string, error) {
var serverInfo *portainer.TunnelServerInfo
serverInfo, err := service.dataStore.TunnelServer().Info()
if err == errors.ErrObjectNotFound {
keySeed := uniuri.NewLen(16)
serverInfo = &portainer.TunnelServerInfo{
PrivateKeySeed: keySeed,
}
err := service.dataStore.TunnelServer().UpdateInfo(serverInfo)
if err != nil {
return "", err
}
} else if err != nil {
return "", err
}
return serverInfo.PrivateKeySeed, nil
}
func (service *Service) startTunnelVerificationLoop() {
log.Printf("[DEBUG] [chisel, monitoring] [check_interval_seconds: %f] [message: starting tunnel management process]", tunnelCleanupInterval.Seconds())
ticker := time.NewTicker(tunnelCleanupInterval)
for {
select {
case <-ticker.C:
service.checkTunnels()
case <-service.shutdownCtx.Done():
log.Println("[DEBUG] Shutting down tunnel service")
if err := service.StopTunnelServer(); err != nil {
log.Printf("Stopped tunnel service: %s", err)
}
ticker.Stop()
return
}
}
}
func (service *Service) checkTunnels() {
for item := range service.tunnelDetailsMap.IterBuffered() {
tunnel := item.Val.(*portainer.TunnelDetails)
if tunnel.LastActivity.IsZero() || tunnel.Status == portainer.EdgeAgentIdle {
continue
}
elapsed := time.Since(tunnel.LastActivity)
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %s] [status: %s] [status_time_seconds: %f] [message: endpoint tunnel monitoring]", item.Key, tunnel.Status, elapsed.Seconds())
if tunnel.Status == portainer.EdgeAgentManagementRequired && elapsed.Seconds() < requiredTimeout.Seconds() {
continue
} else if tunnel.Status == portainer.EdgeAgentManagementRequired && elapsed.Seconds() > requiredTimeout.Seconds() {
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %s] [status: %s] [status_time_seconds: %f] [timeout_seconds: %f] [message: REQUIRED state timeout exceeded]", item.Key, tunnel.Status, elapsed.Seconds(), requiredTimeout.Seconds())
}
if tunnel.Status == portainer.EdgeAgentActive && elapsed.Seconds() < activeTimeout.Seconds() {
continue
} else if tunnel.Status == portainer.EdgeAgentActive && elapsed.Seconds() > activeTimeout.Seconds() {
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %s] [status: %s] [status_time_seconds: %f] [timeout_seconds: %f] [message: ACTIVE state timeout exceeded]", item.Key, tunnel.Status, elapsed.Seconds(), activeTimeout.Seconds())
endpointID, err := strconv.Atoi(item.Key)
if err != nil {
log.Printf("[ERROR] [chisel,snapshot,conversion] Invalid endpoint identifier (id: %s): %s", item.Key, err)
}
err = service.snapshotEnvironment(portainer.EndpointID(endpointID), tunnel.Port)
if err != nil {
log.Printf("[ERROR] [snapshot] Unable to snapshot Edge endpoint (id: %s): %s", item.Key, err)
}
}
if len(tunnel.Jobs) > 0 {
endpointID, err := strconv.Atoi(item.Key)
if err != nil {
log.Printf("[ERROR] [chisel,conversion] Invalid endpoint identifier (id: %s): %s", item.Key, err)
continue
}
service.SetTunnelStatusToIdle(portainer.EndpointID(endpointID))
} else {
service.tunnelDetailsMap.Remove(item.Key)
}
}
}
func (service *Service) snapshotEnvironment(endpointID portainer.EndpointID, tunnelPort int) error {
endpoint, err := service.dataStore.Endpoint().Endpoint(endpointID)
if err != nil {
return err
}
endpointURL := endpoint.URL
endpoint.URL = fmt.Sprintf("tcp://127.0.0.1:%d", tunnelPort)
err = service.snapshotService.SnapshotEndpoint(endpoint)
if err != nil {
return err
}
endpoint.URL = endpointURL
return service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
}

144
api/chisel/tunnel.go Normal file
View File

@@ -0,0 +1,144 @@
package chisel
import (
"encoding/base64"
"fmt"
"math/rand"
"strconv"
"strings"
"time"
"github.com/portainer/libcrypto"
"github.com/dchest/uniuri"
portainer "github.com/portainer/portainer/api"
)
const (
minAvailablePort = 49152
maxAvailablePort = 65535
)
// getUnusedPort is used to generate an unused random port in the dynamic port range.
// Dynamic ports (also called private ports) are 49152 to 65535.
func (service *Service) getUnusedPort() int {
port := randomInt(minAvailablePort, maxAvailablePort)
for item := range service.tunnelDetailsMap.IterBuffered() {
tunnel := item.Val.(*portainer.TunnelDetails)
if tunnel.Port == port {
return service.getUnusedPort()
}
}
return port
}
func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// GetTunnelDetails returns information about the tunnel associated to an endpoint.
func (service *Service) GetTunnelDetails(endpointID portainer.EndpointID) *portainer.TunnelDetails {
key := strconv.Itoa(int(endpointID))
if item, ok := service.tunnelDetailsMap.Get(key); ok {
tunnelDetails := item.(*portainer.TunnelDetails)
return tunnelDetails
}
jobs := make([]portainer.EdgeJob, 0)
return &portainer.TunnelDetails{
Status: portainer.EdgeAgentIdle,
Port: 0,
Jobs: jobs,
Credentials: "",
}
}
// SetTunnelStatusToActive update the status of the tunnel associated to the specified endpoint.
// It sets the status to ACTIVE.
func (service *Service) SetTunnelStatusToActive(endpointID portainer.EndpointID) {
tunnel := service.GetTunnelDetails(endpointID)
tunnel.Status = portainer.EdgeAgentActive
tunnel.Credentials = ""
tunnel.LastActivity = time.Now()
key := strconv.Itoa(int(endpointID))
service.tunnelDetailsMap.Set(key, tunnel)
}
// SetTunnelStatusToIdle update the status of the tunnel associated to the specified endpoint.
// It sets the status to IDLE.
// It removes any existing credentials associated to the tunnel.
func (service *Service) SetTunnelStatusToIdle(endpointID portainer.EndpointID) {
tunnel := service.GetTunnelDetails(endpointID)
tunnel.Status = portainer.EdgeAgentIdle
tunnel.Port = 0
tunnel.LastActivity = time.Now()
credentials := tunnel.Credentials
if credentials != "" {
tunnel.Credentials = ""
service.chiselServer.DeleteUser(strings.Split(credentials, ":")[0])
}
key := strconv.Itoa(int(endpointID))
service.tunnelDetailsMap.Set(key, tunnel)
}
// SetTunnelStatusToRequired update the status of the tunnel associated to the specified endpoint.
// It sets the status to REQUIRED.
// If no port is currently associated to the tunnel, it will associate a random unused port to the tunnel
// and generate temporary credentials that can be used to establish a reverse tunnel on that port.
// Credentials are encrypted using the Edge ID associated to the endpoint.
func (service *Service) SetTunnelStatusToRequired(endpointID portainer.EndpointID) error {
tunnel := service.GetTunnelDetails(endpointID)
if tunnel.Port == 0 {
endpoint, err := service.dataStore.Endpoint().Endpoint(endpointID)
if err != nil {
return err
}
tunnel.Status = portainer.EdgeAgentManagementRequired
tunnel.Port = service.getUnusedPort()
tunnel.LastActivity = time.Now()
username, password := generateRandomCredentials()
authorizedRemote := fmt.Sprintf("^R:0.0.0.0:%d$", tunnel.Port)
err = service.chiselServer.AddUser(username, password, authorizedRemote)
if err != nil {
return err
}
credentials, err := encryptCredentials(username, password, endpoint.EdgeID)
if err != nil {
return err
}
tunnel.Credentials = credentials
key := strconv.Itoa(int(endpointID))
service.tunnelDetailsMap.Set(key, tunnel)
}
return nil
}
func generateRandomCredentials() (string, string) {
username := uniuri.NewLen(8)
password := uniuri.NewLen(8)
return username, password
}
func encryptCredentials(username, password, key string) (string, error) {
credentials := fmt.Sprintf("%s:%s", username, password)
encryptedCredentials, err := libcrypto.Encrypt([]byte(credentials), []byte(key))
if err != nil {
return "", err
}
return base64.RawStdEncoding.EncodeToString(encryptedCredentials), nil
}

View File

@@ -1,9 +1,11 @@
package cli
import (
"errors"
"log"
"time"
"github.com/portainer/portainer"
"github.com/portainer/portainer/api"
"os"
"path/filepath"
@@ -15,16 +17,11 @@ import (
// Service implements the CLIService interface
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")
var (
errInvalidEndpointProtocol = errors.New("Invalid endpoint protocol: Portainer only supports unix://, npipe:// or tcp://")
errSocketOrNamedPipeNotFound = errors.New("Unable to locate Unix socket or named pipe")
errInvalidSnapshotInterval = errors.New("Invalid snapshot interval")
errAdminPassExcludeAdminPassFile = errors.New("Cannot use --admin-password with --admin-password-file")
)
// ParseFlags parse the CLI flags and return a portainer.Flags struct
@@ -32,30 +29,28 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
kingpin.Version(version)
flags := &portainer.CLIFlags{
Addr: kingpin.Flag("bind", "Address and port to serve Portainer").Default(defaultBindAddress).Short('p').String(),
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").String(),
NoAuth: kingpin.Flag("no-auth", "Disable authentication").Default(defaultNoAuth).Bool(),
NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app").Default(defaultNoAnalytics).Bool(),
TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(),
TLSSkipVerify: kingpin.Flag("tlsskipverify", "Disable TLS server verification").Default(defaultTLSSkipVerify).Bool(),
TLSCacert: kingpin.Flag("tlscacert", "Path to the CA").Default(defaultTLSCACertPath).String(),
TLSCert: kingpin.Flag("tlscert", "Path to the TLS certificate file").Default(defaultTLSCertPath).String(),
TLSKey: kingpin.Flag("tlskey", "Path to the TLS key").Default(defaultTLSKeyPath).String(),
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").Default(defaultSyncInterval).String(),
Snapshot: kingpin.Flag("snapshot", "Start a background job to create endpoint snapshots").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 templates (app) definitions on the filesystem").Default(defaultTemplateFile).String(),
Addr: kingpin.Flag("bind", "Address and port to serve Portainer").Default(defaultBindAddress).Short('p').String(),
TunnelAddr: kingpin.Flag("tunnel-addr", "Address to serve the tunnel server").Default(defaultTunnelServerAddress).String(),
TunnelPort: kingpin.Flag("tunnel-port", "Port to serve the tunnel server").Default(defaultTunnelServerPort).String(),
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(),
EnableEdgeComputeFeatures: kingpin.Flag("edge-compute", "Enable Edge Compute features").Bool(),
NoAnalytics: kingpin.Flag("no-analytics", "Disable Analytics in app (deprecated)").Bool(),
TLS: kingpin.Flag("tlsverify", "TLS support").Default(defaultTLS).Bool(),
TLSSkipVerify: kingpin.Flag("tlsskipverify", "Disable TLS server verification").Default(defaultTLSSkipVerify).Bool(),
TLSCacert: kingpin.Flag("tlscacert", "Path to the CA").Default(defaultTLSCACertPath).String(),
TLSCert: kingpin.Flag("tlscert", "Path to the TLS certificate file").Default(defaultTLSCertPath).String(),
TLSKey: kingpin.Flag("tlskey", "Path to the TLS key").Default(defaultTLSKeyPath).String(),
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(),
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(),
}
kingpin.Parse()
@@ -74,26 +69,9 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
// ValidateFlags validates the values of the flags.
func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
if *flags.EndpointURL != "" && *flags.ExternalEndpoints != "" {
return errEndpointExcludeExternal
}
displayDeprecationWarnings(flags)
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)
err := validateEndpointURL(*flags.EndpointURL)
if err != nil {
return err
}
@@ -103,10 +81,6 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
return err
}
if *flags.NoAuth && (*flags.AdminPassword != "" || *flags.AdminPasswordFile != "") {
return errNoAuthExcludeAdminPassword
}
if *flags.AdminPassword != "" && *flags.AdminPasswordFile != "" {
return errAdminPassExcludeAdminPassFile
}
@@ -114,6 +88,12 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
return nil
}
func displayDeprecationWarnings(flags *portainer.CLIFlags) {
if *flags.NoAnalytics {
log.Println("Warning: The --no-analytics flag has been kept to allow migration of instances running a previous version of Portainer with this flag enabled, to version 2.0 where enabling this flag will have no effect.")
}
}
func validateEndpointURL(endpointURL string) error {
if endpointURL != "" {
if !strings.HasPrefix(endpointURL, "unix://") && !strings.HasPrefix(endpointURL, "tcp://") && !strings.HasPrefix(endpointURL, "npipe://") {
@@ -134,38 +114,6 @@ 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

@@ -3,21 +3,18 @@
package cli
const (
defaultBindAddress = ":9000"
defaultDataDirectory = "/data"
defaultAssetsDirectory = "./"
defaultNoAuth = "false"
defaultNoAnalytics = "false"
defaultTLS = "false"
defaultTLSSkipVerify = "false"
defaultTLSCACertPath = "/certs/ca.pem"
defaultTLSCertPath = "/certs/cert.pem"
defaultTLSKeyPath = "/certs/key.pem"
defaultSSL = "false"
defaultSSLCertPath = "/certs/portainer.crt"
defaultSSLKeyPath = "/certs/portainer.key"
defaultSyncInterval = "60s"
defaultSnapshot = "true"
defaultSnapshotInterval = "5m"
defaultTemplateFile = "/templates.json"
defaultBindAddress = ":9000"
defaultTunnelServerAddress = "0.0.0.0"
defaultTunnelServerPort = "8000"
defaultDataDirectory = "/data"
defaultAssetsDirectory = "./"
defaultTLS = "false"
defaultTLSSkipVerify = "false"
defaultTLSCACertPath = "/certs/ca.pem"
defaultTLSCertPath = "/certs/cert.pem"
defaultTLSKeyPath = "/certs/key.pem"
defaultSSL = "false"
defaultSSLCertPath = "/certs/portainer.crt"
defaultSSLKeyPath = "/certs/portainer.key"
defaultSnapshotInterval = "5m"
)

View File

@@ -1,21 +1,18 @@
package cli
const (
defaultBindAddress = ":9000"
defaultDataDirectory = "C:\\data"
defaultAssetsDirectory = "./"
defaultNoAuth = "false"
defaultNoAnalytics = "false"
defaultTLS = "false"
defaultTLSSkipVerify = "false"
defaultTLSCACertPath = "C:\\certs\\ca.pem"
defaultTLSCertPath = "C:\\certs\\cert.pem"
defaultTLSKeyPath = "C:\\certs\\key.pem"
defaultSSL = "false"
defaultSSLCertPath = "C:\\certs\\portainer.crt"
defaultSSLKeyPath = "C:\\certs\\portainer.key"
defaultSyncInterval = "60s"
defaultSnapshot = "true"
defaultSnapshotInterval = "5m"
defaultTemplateFile = "/templates.json"
defaultBindAddress = ":9000"
defaultTunnelServerAddress = "0.0.0.0"
defaultTunnelServerPort = "8000"
defaultDataDirectory = "C:\\data"
defaultAssetsDirectory = "./"
defaultTLS = "false"
defaultTLSSkipVerify = "false"
defaultTLSCACertPath = "C:\\certs\\ca.pem"
defaultTLSCertPath = "C:\\certs\\cert.pem"
defaultTLSKeyPath = "C:\\certs\\key.pem"
defaultSSL = "false"
defaultSSLCertPath = "C:\\certs\\portainer.crt"
defaultSSLKeyPath = "C:\\certs\\portainer.key"
defaultSnapshotInterval = "5m"
)

View File

@@ -1,7 +1,7 @@
package cli
import (
"github.com/portainer/portainer"
"github.com/portainer/portainer/api"
"fmt"
"gopkg.in/alecthomas/kingpin.v2"

View File

@@ -1,37 +1,45 @@
package main // import "github.com/portainer/portainer"
package main
import (
"encoding/json"
"context"
"log"
"os"
"strings"
"github.com/portainer/portainer"
"github.com/portainer/portainer/bolt"
"github.com/portainer/portainer/cli"
"github.com/portainer/portainer/cron"
"github.com/portainer/portainer/crypto"
"github.com/portainer/portainer/docker"
"github.com/portainer/portainer/exec"
"github.com/portainer/portainer/filesystem"
"github.com/portainer/portainer/git"
"github.com/portainer/portainer/http"
"github.com/portainer/portainer/http/client"
"github.com/portainer/portainer/jwt"
"github.com/portainer/portainer/ldap"
"github.com/portainer/portainer/libcompose"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt"
"github.com/portainer/portainer/api/chisel"
"github.com/portainer/portainer/api/cli"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/docker"
"log"
"github.com/portainer/portainer/api/exec"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/git"
"github.com/portainer/portainer/api/http"
"github.com/portainer/portainer/api/http/client"
"github.com/portainer/portainer/api/http/proxy"
kubeproxy "github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/snapshot"
"github.com/portainer/portainer/api/jwt"
"github.com/portainer/portainer/api/kubernetes"
kubecli "github.com/portainer/portainer/api/kubernetes/cli"
"github.com/portainer/portainer/api/ldap"
"github.com/portainer/portainer/api/libcompose"
"github.com/portainer/portainer/api/oauth"
)
func initCLI() *portainer.CLIFlags {
var cli portainer.CLIService = &cli.Service{}
flags, err := cli.ParseFlags(portainer.APIVersion)
var cliService portainer.CLIService = &cli.Service{}
flags, err := cliService.ParseFlags(portainer.APIVersion)
if err != nil {
log.Fatal(err)
log.Fatalf("failed parsing flags: %v", err)
}
err = cli.ValidateFlags(flags)
err = cliService.ValidateFlags(flags)
if err != nil {
log.Fatal(err)
log.Fatalf("failed validating flags:%v", err)
}
return flags
}
@@ -39,55 +47,71 @@ func initCLI() *portainer.CLIFlags {
func initFileService(dataStorePath string) portainer.FileService {
fileService, err := filesystem.NewService(dataStorePath, "")
if err != nil {
log.Fatal(err)
log.Fatalf("failed creating file service: %v", err)
}
return fileService
}
func initStore(dataStorePath string, fileService portainer.FileService) *bolt.Store {
func initDataStore(dataStorePath string, fileService portainer.FileService) portainer.DataStore {
store, err := bolt.NewStore(dataStorePath, fileService)
if err != nil {
log.Fatal(err)
log.Fatalf("failed creating data store: %v", err)
}
err = store.Open()
if err != nil {
log.Fatal(err)
log.Fatalf("failed opening store: %v", err)
}
err = store.Init()
if err != nil {
log.Fatal(err)
log.Fatalf("failed initializing data store: %v", err)
}
err = store.MigrateData()
err = store.MigrateData(false)
if err != nil {
log.Fatal(err)
log.Fatalf("failed migration: %v", err)
}
return store
}
func initComposeStackManager(dataStorePath string) portainer.ComposeStackManager {
return libcompose.NewComposeStackManager(dataStorePath)
}
func initSwarmStackManager(assetsPath string, dataStorePath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService) (portainer.SwarmStackManager, error) {
return exec.NewSwarmStackManager(assetsPath, dataStorePath, signatureService, fileService)
}
func initJWTService(authenticationEnabled bool) portainer.JWTService {
if authenticationEnabled {
jwtService, err := jwt.NewService()
if err != nil {
log.Fatal(err)
}
return jwtService
func initComposeStackManager(assetsPath string, dataStorePath string, reverseTunnelService portainer.ReverseTunnelService, proxyManager *proxy.Manager) portainer.ComposeStackManager {
composeWrapper, err := exec.NewComposeStackManager(assetsPath, dataStorePath, proxyManager)
if err != nil {
log.Printf("[INFO] [main,compose] [message: falling-back to libcompose] [error: %s]", err)
return libcompose.NewComposeStackManager(dataStorePath, reverseTunnelService)
}
return nil
return composeWrapper
}
func initSwarmStackManager(assetsPath string, dataStorePath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) (portainer.SwarmStackManager, error) {
return exec.NewSwarmStackManager(assetsPath, dataStorePath, signatureService, fileService, reverseTunnelService)
}
func initKubernetesDeployer(assetsPath string) portainer.KubernetesDeployer {
return exec.NewKubernetesDeployer(assetsPath)
}
func initJWTService(dataStore portainer.DataStore) (portainer.JWTService, error) {
settings, err := dataStore.Settings().Settings()
if err != nil {
return nil, err
}
if settings.UserSessionTimeout == "" {
settings.UserSessionTimeout = portainer.DefaultUserSessionTimeout
dataStore.Settings().UpdateSettings(settings)
}
jwtService, err := jwt.NewService(settings.UserSessionTimeout)
if err != nil {
return nil, err
}
return jwtService, nil
}
func initDigitalSignatureService() portainer.DigitalSignatureService {
return &crypto.ECDSAService{}
return crypto.NewECDSAService(os.Getenv("AGENT_SECRET"))
}
func initCryptoService() portainer.CryptoService {
@@ -98,149 +122,60 @@ func initLDAPService() portainer.LDAPService {
return &ldap.Service{}
}
func initOAuthService() portainer.OAuthService {
return oauth.NewService()
}
func initGitService() portainer.GitService {
return &git.Service{}
return git.NewService()
}
func initClientFactory(signatureService portainer.DigitalSignatureService) *docker.ClientFactory {
return docker.NewClientFactory(signatureService)
func initDockerClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService) *docker.ClientFactory {
return docker.NewClientFactory(signatureService, reverseTunnelService)
}
func initSnapshotter(clientFactory *docker.ClientFactory) portainer.Snapshotter {
return docker.NewSnapshotter(clientFactory)
func initKubernetesClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, instanceID string) *kubecli.ClientFactory {
return kubecli.NewClientFactory(signatureService, reverseTunnelService, instanceID)
}
func initJobScheduler(endpointService portainer.EndpointService, snapshotter portainer.Snapshotter, flags *portainer.CLIFlags) (portainer.JobScheduler, error) {
jobScheduler := cron.NewJobScheduler(endpointService, snapshotter)
func initSnapshotService(snapshotInterval string, dataStore portainer.DataStore, dockerClientFactory *docker.ClientFactory, kubernetesClientFactory *kubecli.ClientFactory, shutdownCtx context.Context) (portainer.SnapshotService, error) {
dockerSnapshotter := docker.NewSnapshotter(dockerClientFactory)
kubernetesSnapshotter := kubernetes.NewSnapshotter(kubernetesClientFactory)
if *flags.ExternalEndpoints != "" {
log.Println("Using external endpoint definition. Endpoint management via the API will be disabled.")
err := jobScheduler.ScheduleEndpointSyncJob(*flags.ExternalEndpoints, *flags.SyncInterval)
if err != nil {
return nil, err
}
snapshotService, err := snapshot.NewService(snapshotInterval, dataStore, dockerSnapshotter, kubernetesSnapshotter, shutdownCtx)
if err != nil {
return nil, err
}
if *flags.Snapshot {
err := jobScheduler.ScheduleSnapshotJob(*flags.SnapshotInterval)
if err != nil {
return nil, err
}
}
return jobScheduler, nil
return snapshotService, nil
}
func initStatus(endpointManagement, snapshot bool, flags *portainer.CLIFlags) *portainer.Status {
func initStatus(flags *portainer.CLIFlags) *portainer.Status {
return &portainer.Status{
Analytics: !*flags.NoAnalytics,
Authentication: !*flags.NoAuth,
EndpointManagement: endpointManagement,
Snapshot: snapshot,
Version: portainer.APIVersion,
Version: portainer.APIVersion,
}
}
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{
AutoCreateUsers: true,
TLSConfig: portainer.TLSConfiguration{},
SearchSettings: []portainer.LDAPSearchSettings{
portainer.LDAPSearchSettings{},
},
GroupSearchSettings: []portainer.LDAPGroupSearchSettings{
portainer.LDAPGroupSearchSettings{},
},
},
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
SnapshotInterval: *flags.SnapshotInterval,
}
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()
func updateSettingsFromFlags(dataStore portainer.DataStore, flags *portainer.CLIFlags) error {
settings, err := dataStore.Settings().Settings()
if err != nil {
return err
}
if len(existingTemplates) != 0 {
log.Printf("Templates already registered inside the database. Skipping template import.")
return nil
settings.LogoURL = *flags.Logo
settings.SnapshotInterval = *flags.SnapshotInterval
settings.EnableEdgeComputeFeatures = *flags.EnableEdgeComputeFeatures
settings.EnableTelemetry = true
if *flags.Templates != "" {
settings.TemplatesURL = *flags.Templates
}
templatesJSON, err := fileService.GetFileContent(templateFile)
if err != nil {
log.Println("Unable to retrieve template definitions via filesystem")
return err
if *flags.Labels != nil {
settings.BlackListedLabels = *flags.Labels
}
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]
return dataStore.Settings().UpdateSettings(settings)
}
func loadAndParseKeyPair(fileService portainer.FileService, signatureService portainer.DigitalSignatureService) error {
@@ -263,7 +198,7 @@ func generateAndStoreKeyPair(fileService portainer.FileService, signatureService
func initKeyPair(fileService portainer.FileService, signatureService portainer.DigitalSignatureService) error {
existingKeyPair, err := fileService.KeyPairFilesExist()
if err != nil {
log.Fatal(err)
log.Fatalf("failed checking for existing key pair: %v", err)
}
if existingKeyPair {
@@ -272,7 +207,7 @@ func initKeyPair(fileService portainer.FileService, signatureService portainer.D
return generateAndStoreKeyPair(fileService, signatureService)
}
func createTLSSecuredEndpoint(flags *portainer.CLIFlags, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore portainer.DataStore, snapshotService portainer.SnapshotService) error {
tlsConfiguration := portainer.TLSConfiguration{
TLS: *flags.TLS,
TLSSkipVerify: *flags.TLSSkipVerify,
@@ -286,20 +221,33 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, endpointService portain
tlsConfiguration.TLS = true
}
endpointID := endpointService.GetNextIdentifier()
endpointID := dataStore.Endpoint().GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: "primary",
URL: *flags.EndpointURL,
GroupID: portainer.EndpointGroupID(1),
Type: portainer.DockerEnvironment,
TLSConfig: tlsConfiguration,
AuthorizedUsers: []portainer.UserID{},
AuthorizedTeams: []portainer.TeamID{},
Extensions: []portainer.EndpointExtension{},
Tags: []string{},
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{},
ID: portainer.EndpointID(endpointID),
Name: "primary",
URL: *flags.EndpointURL,
GroupID: portainer.EndpointGroupID(1),
Type: portainer.DockerEnvironment,
TLSConfig: tlsConfiguration,
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{},
TagIDs: []portainer.TagID{},
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.DockerSnapshot{},
Kubernetes: portainer.KubernetesDefault(),
SecuritySettings: portainer.EndpointSecuritySettings{
AllowVolumeBrowserForRegularUsers: false,
EnableHostManagementFeatures: false,
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowHostNamespaceForRegularUsers: true,
AllowContainerCapabilitiesForRegularUsers: true,
AllowDeviceMappingForRegularUsers: true,
AllowStackManagementForRegularUsers: true,
},
}
if strings.HasPrefix(endpoint.URL, "tcp://") {
@@ -318,10 +266,15 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, endpointService portain
}
}
return snapshotAndPersistEndpoint(endpoint, endpointService, snapshotter)
err := snapshotService.SnapshotEndpoint(endpoint)
if err != nil {
log.Printf("http error: endpoint snapshot error (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
}
return dataStore.Endpoint().CreateEndpoint(endpoint)
}
func createUnsecuredEndpoint(endpointURL string, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
func createUnsecuredEndpoint(endpointURL string, dataStore portainer.DataStore, snapshotService portainer.SnapshotService) error {
if strings.HasPrefix(endpointURL, "tcp://") {
_, err := client.ExecutePingOperation(endpointURL, nil)
if err != nil {
@@ -329,45 +282,49 @@ func createUnsecuredEndpoint(endpointURL string, endpointService portainer.Endpo
}
}
endpointID := endpointService.GetNextIdentifier()
endpointID := dataStore.Endpoint().GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: "primary",
URL: endpointURL,
GroupID: portainer.EndpointGroupID(1),
Type: portainer.DockerEnvironment,
TLSConfig: portainer.TLSConfiguration{},
AuthorizedUsers: []portainer.UserID{},
AuthorizedTeams: []portainer.TeamID{},
Extensions: []portainer.EndpointExtension{},
Tags: []string{},
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{},
ID: portainer.EndpointID(endpointID),
Name: "primary",
URL: endpointURL,
GroupID: portainer.EndpointGroupID(1),
Type: portainer.DockerEnvironment,
TLSConfig: portainer.TLSConfiguration{},
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{},
TagIDs: []portainer.TagID{},
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.DockerSnapshot{},
Kubernetes: portainer.KubernetesDefault(),
SecuritySettings: portainer.EndpointSecuritySettings{
AllowVolumeBrowserForRegularUsers: false,
EnableHostManagementFeatures: false,
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowHostNamespaceForRegularUsers: true,
AllowContainerCapabilitiesForRegularUsers: true,
AllowDeviceMappingForRegularUsers: true,
AllowStackManagementForRegularUsers: true,
},
}
return snapshotAndPersistEndpoint(endpoint, endpointService, snapshotter)
}
func snapshotAndPersistEndpoint(endpoint *portainer.Endpoint, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
snapshot, err := snapshotter.CreateSnapshot(endpoint)
endpoint.Status = portainer.EndpointStatusUp
err := snapshotService.SnapshotEndpoint(endpoint)
if err != nil {
log.Printf("http error: endpoint snapshot error (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
}
if snapshot != nil {
endpoint.Snapshots = []portainer.Snapshot{*snapshot}
}
return endpointService.CreateEndpoint(endpoint)
return dataStore.Endpoint().CreateEndpoint(endpoint)
}
func initEndpoint(flags *portainer.CLIFlags, endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) error {
func initEndpoint(flags *portainer.CLIFlags, dataStore portainer.DataStore, snapshotService portainer.SnapshotService) error {
if *flags.EndpointURL == "" {
return nil
}
endpoints, err := endpointService.Endpoints()
endpoints, err := dataStore.Endpoint().Endpoints()
if err != nil {
return err
}
@@ -378,153 +335,165 @@ func initEndpoint(flags *portainer.CLIFlags, endpointService portainer.EndpointS
}
if *flags.TLS || *flags.TLSSkipVerify {
return createTLSSecuredEndpoint(flags, endpointService, snapshotter)
return createTLSSecuredEndpoint(flags, dataStore, snapshotService)
}
return createUnsecuredEndpoint(*flags.EndpointURL, endpointService, snapshotter)
return createUnsecuredEndpoint(*flags.EndpointURL, dataStore, snapshotService)
}
func main() {
flags := initCLI()
func buildServer(flags *portainer.CLIFlags) portainer.Server {
shutdownCtx, shutdownTrigger := context.WithCancel(context.Background())
fileService := initFileService(*flags.Data)
store := initStore(*flags.Data, fileService)
defer store.Close()
dataStore := initDataStore(*flags.Data, fileService)
jwtService := initJWTService(!*flags.NoAuth)
if err := dataStore.CheckCurrentEdition(); err != nil {
log.Fatal(err)
}
jwtService, err := initJWTService(dataStore)
if err != nil {
log.Fatalf("failed initializing JWT service: %v", err)
}
ldapService := initLDAPService()
oauthService := initOAuthService()
gitService := initGitService()
cryptoService := initCryptoService()
digitalSignatureService := initDigitalSignatureService()
err := initKeyPair(fileService, digitalSignatureService)
err = initKeyPair(fileService, digitalSignatureService)
if err != nil {
log.Fatal(err)
log.Fatalf("failed initializing key pai: %v", err)
}
clientFactory := initClientFactory(digitalSignatureService)
reverseTunnelService := chisel.NewService(dataStore, shutdownCtx)
snapshotter := initSnapshotter(clientFactory)
jobScheduler, err := initJobScheduler(store.EndpointService, snapshotter, flags)
instanceID, err := dataStore.Version().InstanceID()
if err != nil {
log.Fatal(err)
log.Fatalf("failed getting instance id: %v", err)
}
jobScheduler.Start()
dockerClientFactory := initDockerClientFactory(digitalSignatureService, reverseTunnelService)
kubernetesClientFactory := initKubernetesClientFactory(digitalSignatureService, reverseTunnelService, instanceID)
endpointManagement := true
if *flags.ExternalEndpoints != "" {
endpointManagement = false
snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, shutdownCtx)
if err != nil {
log.Fatalf("failed initializing snapshot service: %v", err)
}
snapshotService.Start()
swarmStackManager, err := initSwarmStackManager(*flags.Assets, *flags.Data, digitalSignatureService, fileService, reverseTunnelService)
if err != nil {
log.Fatalf("failed initializing swarm stack manager: %v", err)
}
kubernetesTokenCacheManager := kubeproxy.NewTokenCacheManager()
proxyManager := proxy.NewManager(dataStore, digitalSignatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager)
composeStackManager := initComposeStackManager(*flags.Assets, *flags.Data, reverseTunnelService, proxyManager)
kubernetesDeployer := initKubernetesDeployer(*flags.Assets)
if dataStore.IsNew() {
err = updateSettingsFromFlags(dataStore, flags)
if err != nil {
log.Fatalf("failed updating settings from flags: %v", err)
}
}
swarmStackManager, err := initSwarmStackManager(*flags.Assets, *flags.Data, digitalSignatureService, fileService)
err = edge.LoadEdgeJobs(dataStore, reverseTunnelService)
if err != nil {
log.Fatal(err)
log.Fatalf("failed loading edge jobs from database: %v", err)
}
composeStackManager := initComposeStackManager(*flags.Data)
applicationStatus := initStatus(flags)
err = initTemplates(store.TemplateService, fileService, *flags.Templates, *flags.TemplateFile)
err = initEndpoint(flags, dataStore, snapshotService)
if err != nil {
log.Fatal(err)
}
err = initSettings(store.SettingsService, flags)
if err != nil {
log.Fatal(err)
}
err = initDockerHub(store.DockerHubService)
if err != nil {
log.Fatal(err)
}
applicationStatus := initStatus(endpointManagement, *flags.Snapshot, flags)
err = initEndpoint(flags, store.EndpointService, snapshotter)
if err != nil {
log.Fatal(err)
log.Fatalf("failed initializing endpoint: %v", err)
}
adminPasswordHash := ""
if *flags.AdminPasswordFile != "" {
content, err := fileService.GetFileContent(*flags.AdminPasswordFile)
if err != nil {
log.Fatal(err)
log.Fatalf("failed getting admin password file: %v", err)
}
adminPasswordHash, err = cryptoService.Hash(string(content))
adminPasswordHash, err = cryptoService.Hash(strings.TrimSuffix(string(content), "\n"))
if err != nil {
log.Fatal(err)
log.Fatalf("failed hashing admin password: %v", err)
}
} else if *flags.AdminPassword != "" {
adminPasswordHash = *flags.AdminPassword
}
if adminPasswordHash != "" {
users, err := store.UserService.UsersByRole(portainer.AdministratorRole)
users, err := dataStore.User().UsersByRole(portainer.AdministratorRole)
if err != nil {
log.Fatal(err)
log.Fatalf("failed getting admin user: %v", err)
}
if len(users) == 0 {
log.Printf("Creating admin user with password hash %s", adminPasswordHash)
log.Println("Created admin user with the given password.")
user := &portainer.User{
Username: "admin",
Role: portainer.AdministratorRole,
Password: adminPasswordHash,
}
err := store.UserService.CreateUser(user)
err := dataStore.User().CreateUser(user)
if err != nil {
log.Fatal(err)
log.Fatalf("failed creating admin user: %v", err)
}
} else {
log.Println("Instance already has an administrator user defined. Skipping admin password related flags.")
}
}
var server portainer.Server = &http.Server{
Status: applicationStatus,
BindAddress: *flags.Addr,
AssetsPath: *flags.Assets,
AuthDisabled: *flags.NoAuth,
EndpointManagement: endpointManagement,
UserService: store.UserService,
TeamService: store.TeamService,
TeamMembershipService: store.TeamMembershipService,
EndpointService: store.EndpointService,
EndpointGroupService: store.EndpointGroupService,
ResourceControlService: store.ResourceControlService,
SettingsService: store.SettingsService,
RegistryService: store.RegistryService,
DockerHubService: store.DockerHubService,
StackService: store.StackService,
TagService: store.TagService,
TemplateService: store.TemplateService,
WebhookService: store.WebhookService,
SwarmStackManager: swarmStackManager,
ComposeStackManager: composeStackManager,
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,
err = reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotService)
if err != nil {
log.Fatalf("failed starting license service: %s", err)
}
log.Printf("Starting Portainer %s on %s", portainer.APIVersion, *flags.Addr)
err = server.Start()
if err != nil {
log.Fatal(err)
return &http.Server{
ReverseTunnelService: reverseTunnelService,
Status: applicationStatus,
BindAddress: *flags.Addr,
AssetsPath: *flags.Assets,
DataStore: dataStore,
SwarmStackManager: swarmStackManager,
ComposeStackManager: composeStackManager,
KubernetesDeployer: kubernetesDeployer,
CryptoService: cryptoService,
JWTService: jwtService,
FileService: fileService,
LDAPService: ldapService,
OAuthService: oauthService,
GitService: gitService,
ProxyManager: proxyManager,
KubernetesTokenCacheManager: kubernetesTokenCacheManager,
SignatureService: digitalSignatureService,
SnapshotService: snapshotService,
SSL: *flags.SSL,
SSLCert: *flags.SSLCert,
SSLKey: *flags.SSLKey,
DockerClientFactory: dockerClientFactory,
KubernetesClientFactory: kubernetesClientFactory,
ShutdownCtx: shutdownCtx,
ShutdownTrigger: shutdownTrigger,
}
}
func main() {
flags := initCLI()
for {
server := buildServer(flags)
log.Printf("Starting Portainer %s on %s\n", portainer.APIVersion, *flags.Addr)
err := server.Start()
log.Printf("Http server exited: %s\n", err)
}
}

View File

@@ -1,60 +0,0 @@
package cron
import (
"log"
"github.com/portainer/portainer"
)
type (
endpointSnapshotJob struct {
endpointService portainer.EndpointService
snapshotter portainer.Snapshotter
}
)
func newEndpointSnapshotJob(endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) endpointSnapshotJob {
return endpointSnapshotJob{
endpointService: endpointService,
snapshotter: snapshotter,
}
}
func (job endpointSnapshotJob) Snapshot() error {
endpoints, err := job.endpointService.Endpoints()
if err != nil {
return err
}
for _, endpoint := range endpoints {
if endpoint.Type == portainer.AzureEnvironment {
continue
}
snapshot, err := job.snapshotter.CreateSnapshot(&endpoint)
endpoint.Status = portainer.EndpointStatusUp
if err != nil {
log.Printf("cron error: endpoint snapshot error (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
endpoint.Status = portainer.EndpointStatusDown
}
if snapshot != nil {
endpoint.Snapshots = []portainer.Snapshot{*snapshot}
}
err = job.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
if err != nil {
return err
}
}
return nil
}
func (job endpointSnapshotJob) Run() {
err := job.Snapshot()
if err != nil {
log.Printf("cron error: snapshot job error (err=%s)\n", err)
}
}

View File

@@ -1,206 +0,0 @@
package cron
import (
"encoding/json"
"io/ioutil"
"log"
"strings"
"github.com/portainer/portainer"
)
type (
endpointSyncJob struct {
endpointService portainer.EndpointService
endpointFilePath string
}
synchronization struct {
endpointsToCreate []*portainer.Endpoint
endpointsToUpdate []*portainer.Endpoint
endpointsToDelete []*portainer.Endpoint
}
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"`
}
)
const (
// ErrEmptyEndpointArray is an error raised when the external endpoint source array is empty.
ErrEmptyEndpointArray = portainer.Error("External endpoint source is empty")
)
func newEndpointSyncJob(endpointFilePath string, endpointService portainer.EndpointService) endpointSyncJob {
return endpointSyncJob{
endpointService: endpointService,
endpointFilePath: endpointFilePath,
}
}
func endpointSyncError(err error) bool {
if err != nil {
log.Printf("cron error: synchronization job error (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
}
// TMP: endpointSyncJob method to access logger, should be generic
func (job endpointSyncJob) 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,
}
}
func (job endpointSyncJob) Sync() error {
data, err := ioutil.ReadFile(job.endpointFilePath)
if endpointSyncError(err) {
return err
}
var fileEndpoints []fileEndpoint
err = json.Unmarshal(data, &fileEndpoints)
if endpointSyncError(err) {
return err
}
if len(fileEndpoints) == 0 {
return ErrEmptyEndpointArray
}
storedEndpoints, err := job.endpointService.Endpoints()
if endpointSyncError(err) {
return err
}
convertedFileEndpoints := convertFileEndpoints(fileEndpoints)
sync := job.prepareSyncData(storedEndpoints, convertedFileEndpoints)
if sync.requireSync() {
err = job.endpointService.Synchronize(sync.endpointsToCreate, sync.endpointsToUpdate, sync.endpointsToDelete)
if endpointSyncError(err) {
return err
}
log.Printf("Endpoint synchronization ended. [created: %v] [updated: %v] [deleted: %v]", len(sync.endpointsToCreate), len(sync.endpointsToUpdate), len(sync.endpointsToDelete))
}
return nil
}
func (job endpointSyncJob) Run() {
log.Println("cron: synchronization job started")
err := job.Sync()
endpointSyncError(err)
}

View File

@@ -1,82 +0,0 @@
package cron
import (
"log"
"github.com/portainer/portainer"
"github.com/robfig/cron"
)
// JobScheduler represents a service for managing crons.
type JobScheduler struct {
cron *cron.Cron
endpointService portainer.EndpointService
snapshotter portainer.Snapshotter
endpointFilePath string
endpointSyncInterval string
}
// NewJobScheduler initializes a new service.
func NewJobScheduler(endpointService portainer.EndpointService, snapshotter portainer.Snapshotter) *JobScheduler {
return &JobScheduler{
cron: cron.New(),
endpointService: endpointService,
snapshotter: snapshotter,
}
}
// ScheduleEndpointSyncJob schedules a cron job to synchronize the endpoints from a file
func (scheduler *JobScheduler) ScheduleEndpointSyncJob(endpointFilePath string, interval string) error {
scheduler.endpointFilePath = endpointFilePath
scheduler.endpointSyncInterval = interval
job := newEndpointSyncJob(endpointFilePath, scheduler.endpointService)
err := job.Sync()
if err != nil {
return err
}
return scheduler.cron.AddJob("@every "+interval, job)
}
// ScheduleSnapshotJob schedules a cron job to create endpoint snapshots
func (scheduler *JobScheduler) ScheduleSnapshotJob(interval string) error {
job := newEndpointSnapshotJob(scheduler.endpointService, scheduler.snapshotter)
go job.Snapshot()
return scheduler.cron.AddJob("@every "+interval, job)
}
// UpdateSnapshotJob will update the schedules to match the new snapshot interval
func (scheduler *JobScheduler) UpdateSnapshotJob(interval string) {
// TODO: the cron library do not support removing/updating schedules.
// As a work-around we need to re-create the cron and reschedule the jobs.
// We should update the library.
jobs := scheduler.cron.Entries()
scheduler.cron.Stop()
scheduler.cron = cron.New()
for _, job := range jobs {
switch job.Job.(type) {
case endpointSnapshotJob:
scheduler.ScheduleSnapshotJob(interval)
case endpointSyncJob:
scheduler.ScheduleEndpointSyncJob(scheduler.endpointFilePath, scheduler.endpointSyncInterval)
default:
log.Println("Unsupported job")
}
}
scheduler.cron.Start()
}
// Start starts the scheduled jobs
func (scheduler *JobScheduler) Start() {
if len(scheduler.cron.Entries()) > 0 {
scheduler.cron.Start()
}
}

70
api/crypto/aes.go Normal file
View File

@@ -0,0 +1,70 @@
package crypto
import (
"crypto/aes"
"crypto/cipher"
"io"
"golang.org/x/crypto/scrypt"
)
// NOTE: has to go with what is considered to be a simplistic in that it omits any
// authentication of the encrypted data.
// Person with better knowledge is welcomed to improve it.
// sourced from https://golang.org/src/crypto/cipher/example_test.go
var emptySalt []byte = make([]byte, 0, 0)
// AesEncrypt reads from input, encrypts with AES-256 and writes to the output.
// passphrase is used to generate an encryption key.
func AesEncrypt(input io.Reader, output io.Writer, passphrase []byte) error {
// making a 32 bytes key that would correspond to AES-256
// don't necessarily need a salt, so just kept in empty
key, err := scrypt.Key(passphrase, emptySalt, 32768, 8, 1, 32)
if err != nil {
return err
}
block, err := aes.NewCipher(key)
if err != nil {
return err
}
// If the key is unique for each ciphertext, then it's ok to use a zero
// IV.
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
writer := &cipher.StreamWriter{S: stream, W: output}
// Copy the input to the output, encrypting as we go.
if _, err := io.Copy(writer, input); err != nil {
return err
}
return nil
}
// AesDecrypt reads from input, decrypts with AES-256 and returns the reader to a read decrypted content from.
// passphrase is used to generate an encryption key.
func AesDecrypt(input io.Reader, passphrase []byte) (io.Reader, error) {
// making a 32 bytes key that would correspond to AES-256
// don't necessarily need a salt, so just kept in empty
key, err := scrypt.Key(passphrase, emptySalt, 32768, 8, 1, 32)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// If the key is unique for each ciphertext, then it's ok to use a zero
// IV.
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
reader := &cipher.StreamReader{S: stream, R: input}
return reader, nil
}

132
api/crypto/aes_test.go Normal file
View File

@@ -0,0 +1,132 @@
package crypto
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/ioutils"
"github.com/stretchr/testify/assert"
)
func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "encrypt")
defer os.RemoveAll(tmpdir)
var (
originFilePath = filepath.Join(tmpdir, "origin")
encryptedFilePath = filepath.Join(tmpdir, "encrypted")
decryptedFilePath = filepath.Join(tmpdir, "decrypted")
)
content := []byte("content")
ioutil.WriteFile(originFilePath, content, 0600)
originFile, _ := os.Open(originFilePath)
defer originFile.Close()
encryptedFileWriter, _ := os.Create(encryptedFilePath)
defer encryptedFileWriter.Close()
err := AesEncrypt(originFile, encryptedFileWriter, []byte("passphrase"))
assert.Nil(t, err, "Failed to encrypt a file")
encryptedContent, err := ioutil.ReadFile(encryptedFilePath)
assert.Nil(t, err, "Couldn't read encrypted file")
assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted")
encryptedFileReader, _ := os.Open(encryptedFilePath)
defer encryptedFileReader.Close()
decryptedFileWriter, _ := os.Create(decryptedFilePath)
defer decryptedFileWriter.Close()
decryptedReader, err := AesDecrypt(encryptedFileReader, []byte("passphrase"))
assert.Nil(t, err, "Failed to decrypt file")
io.Copy(decryptedFileWriter, decryptedReader)
decryptedContent, _ := ioutil.ReadFile(decryptedFilePath)
assert.Equal(t, content, decryptedContent, "Original and decrypted content should match")
}
func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "encrypt")
defer os.RemoveAll(tmpdir)
var (
originFilePath = filepath.Join(tmpdir, "origin")
encryptedFilePath = filepath.Join(tmpdir, "encrypted")
decryptedFilePath = filepath.Join(tmpdir, "decrypted")
)
content := []byte("content")
ioutil.WriteFile(originFilePath, content, 0600)
originFile, _ := os.Open(originFilePath)
defer originFile.Close()
encryptedFileWriter, _ := os.Create(encryptedFilePath)
defer encryptedFileWriter.Close()
err := AesEncrypt(originFile, encryptedFileWriter, []byte(""))
assert.Nil(t, err, "Failed to encrypt a file")
encryptedContent, err := ioutil.ReadFile(encryptedFilePath)
assert.Nil(t, err, "Couldn't read encrypted file")
assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted")
encryptedFileReader, _ := os.Open(encryptedFilePath)
defer encryptedFileReader.Close()
decryptedFileWriter, _ := os.Create(decryptedFilePath)
defer decryptedFileWriter.Close()
decryptedReader, err := AesDecrypt(encryptedFileReader, []byte(""))
assert.Nil(t, err, "Failed to decrypt file")
io.Copy(decryptedFileWriter, decryptedReader)
decryptedContent, _ := ioutil.ReadFile(decryptedFilePath)
assert.Equal(t, content, decryptedContent, "Original and decrypted content should match")
}
func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "encrypt")
defer os.RemoveAll(tmpdir)
var (
originFilePath = filepath.Join(tmpdir, "origin")
encryptedFilePath = filepath.Join(tmpdir, "encrypted")
decryptedFilePath = filepath.Join(tmpdir, "decrypted")
)
content := []byte("content")
ioutil.WriteFile(originFilePath, content, 0600)
originFile, _ := os.Open(originFilePath)
defer originFile.Close()
encryptedFileWriter, _ := os.Create(encryptedFilePath)
defer encryptedFileWriter.Close()
err := AesEncrypt(originFile, encryptedFileWriter, []byte("passphrase"))
assert.Nil(t, err, "Failed to encrypt a file")
encryptedContent, err := ioutil.ReadFile(encryptedFilePath)
assert.Nil(t, err, "Couldn't read encrypted file")
assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted")
encryptedFileReader, _ := os.Open(encryptedFilePath)
defer encryptedFileReader.Close()
decryptedFileWriter, _ := os.Create(decryptedFilePath)
defer decryptedFileWriter.Close()
decryptedReader, err := AesDecrypt(encryptedFileReader, []byte("garbage"))
assert.Nil(t, err, "Should allow to decrypt with wrong passphrase")
io.Copy(decryptedFileWriter, decryptedReader)
decryptedContent, _ := ioutil.ReadFile(decryptedFilePath)
assert.NotEqual(t, content, decryptedContent, "Original and decrypted content should NOT match")
}

View File

@@ -8,6 +8,8 @@ import (
"encoding/base64"
"encoding/hex"
"math/big"
"github.com/portainer/libcrypto"
)
const (
@@ -26,6 +28,15 @@ type ECDSAService struct {
privateKey *ecdsa.PrivateKey
publicKey *ecdsa.PublicKey
encodedPubKey string
secret string
}
// NewECDSAService returns a pointer to a ECDSAService.
// An optional secret can be specified
func NewECDSAService(secret string) *ECDSAService {
return &ECDSAService{
secret: secret,
}
}
// EncodedPublicKey returns the encoded version of the public that can be used
@@ -91,12 +102,18 @@ func (service *ECDSAService) GenerateKeyPair() ([]byte, []byte, error) {
return private, public, nil
}
// Sign creates a signature from a message.
// It automatically hash the message using MD5 and creates a signature from
// CreateSignature creates a digital signature.
// It automatically hash a specific message using MD5 and creates a signature from
// that hash.
// If a secret is associated to the service, it will be used instead of the specified
// message.
// It then encodes the generated signature in base64.
func (service *ECDSAService) Sign(message string) (string, error) {
hash := HashFromBytes([]byte(message))
func (service *ECDSAService) CreateSignature(message string) (string, error) {
if service.secret != "" {
message = service.secret
}
hash := libcrypto.HashFromBytes([]byte(message))
r := big.NewInt(0)
s := big.NewInt(0)

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